mirror of
				https://github.com/N64Recomp/N64ModernRuntime.git
				synced 2025-10-30 08:02:29 +00:00 
			
		
		
		
	Compare commits
	
		
			4 commits
		
	
	
		
			48a769d071
			...
			8f1e1789ec
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 8f1e1789ec | ||
|   | df7e820d8c | ||
|   | 8b5800233a | ||
|   | 8719abe217 | 
					 6 changed files with 254 additions and 134 deletions
				
			
		|  | @ -73,12 +73,21 @@ extern "C" void osGetCount_recomp(uint8_t * rdram, recomp_context * ctx) { | ||||||
|     ctx->r2 = osGetCount(); |     ctx->r2 = osGetCount(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | extern "C" void osSetCount_recomp(uint8_t * rdram, recomp_context * ctx) { | ||||||
|  |     osSetCount(ctx->r4); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| extern "C" void osGetTime_recomp(uint8_t * rdram, recomp_context * ctx) { | extern "C" void osGetTime_recomp(uint8_t * rdram, recomp_context * ctx) { | ||||||
|     uint64_t total_count = osGetTime(); |     uint64_t total_count = osGetTime(); | ||||||
|     ctx->r2 = (int32_t)(total_count >> 32); |     ctx->r2 = (int32_t)(total_count >> 32); | ||||||
|     ctx->r3 = (int32_t)(total_count >> 0); |     ctx->r3 = (int32_t)(total_count >> 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | extern "C" void osSetTime_recomp(uint8_t * rdram, recomp_context * ctx) { | ||||||
|  |     uint64_t t = ((uint64_t)(ctx->r4) << 32) | ((ctx->r5) & 0xFFFFFFFFu); | ||||||
|  |     osSetTime(t); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| extern "C" void osSetTimer_recomp(uint8_t * rdram, recomp_context * ctx) { | extern "C" void osSetTimer_recomp(uint8_t * rdram, recomp_context * ctx) { | ||||||
|     uint64_t countdown = ((uint64_t)(ctx->r6) << 32) | ((ctx->r7) & 0xFFFFFFFFu); |     uint64_t countdown = ((uint64_t)(ctx->r6) << 32) | ((ctx->r7) & 0xFFFFFFFFu); | ||||||
|     uint64_t interval = load_doubleword(rdram, ctx->r29, 0x10); |     uint64_t interval = load_doubleword(rdram, ctx->r29, 0x10); | ||||||
|  |  | ||||||
|  | @ -22,6 +22,18 @@ extern "C" void osViRepeatLine_recomp(uint8_t* rdram, recomp_context* ctx) { | ||||||
|     osViRepeatLine(_arg<0, u8>(rdram, ctx)); |     osViRepeatLine(_arg<0, u8>(rdram, ctx)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | extern "C" void osViGetCurrentLine_recomp(uint8_t* rdram, recomp_context* ctx) { | ||||||
|  |     ctx->r2 = (gpr)osViGetCurrentLine(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern "C" void osViGetCurrentField_recomp(uint8_t* rdram, recomp_context* ctx) { | ||||||
|  |     ctx->r2 = (gpr)osViGetCurrentField(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern "C" void osViGetStatus_recomp(uint8_t* rdram, recomp_context* ctx) { | ||||||
|  |     ctx->r2 = (gpr)osViGetStatus(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| extern "C" void osViSetSpecialFeatures_recomp(uint8_t* rdram, recomp_context* ctx) { | extern "C" void osViSetSpecialFeatures_recomp(uint8_t* rdram, recomp_context* ctx) { | ||||||
|     osViSetSpecialFeatures((uint32_t)ctx->r4); |     osViSetSpecialFeatures((uint32_t)ctx->r4); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -28,6 +28,23 @@ struct SDL_Window; | ||||||
| 
 | 
 | ||||||
| namespace ultramodern { | namespace ultramodern { | ||||||
|     namespace renderer { |     namespace renderer { | ||||||
|  |         struct ViRegs { | ||||||
|  |             unsigned int VI_STATUS_REG; | ||||||
|  |             unsigned int VI_ORIGIN_REG; | ||||||
|  |             unsigned int VI_WIDTH_REG; | ||||||
|  |             unsigned int VI_INTR_REG; | ||||||
|  |             unsigned int VI_V_CURRENT_LINE_REG; | ||||||
|  |             unsigned int VI_TIMING_REG; | ||||||
|  |             unsigned int VI_V_SYNC_REG; | ||||||
|  |             unsigned int VI_H_SYNC_REG; | ||||||
|  |             unsigned int VI_LEAP_REG; | ||||||
|  |             unsigned int VI_H_START_REG; | ||||||
|  |             unsigned int VI_V_START_REG; | ||||||
|  |             unsigned int VI_V_BURST_REG; | ||||||
|  |             unsigned int VI_X_SCALE_REG; | ||||||
|  |             unsigned int VI_Y_SCALE_REG; | ||||||
|  |         }; | ||||||
|  |         ViRegs* get_vi_regs(); | ||||||
| 
 | 
 | ||||||
| #if defined(_WIN32) | #if defined(_WIN32) | ||||||
|         // Native HWND handle to the target window.
 |         // Native HWND handle to the target window.
 | ||||||
|  | @ -67,7 +84,7 @@ namespace ultramodern { | ||||||
| 
 | 
 | ||||||
|                 virtual void enable_instant_present() = 0; |                 virtual void enable_instant_present() = 0; | ||||||
|                 virtual void send_dl(const OSTask* task) = 0; |                 virtual void send_dl(const OSTask* task) = 0; | ||||||
|                 virtual void update_screen(uint32_t vi_origin) = 0; |                 virtual void update_screen() = 0; | ||||||
|                 virtual void shutdown() = 0; |                 virtual void shutdown() = 0; | ||||||
|                 virtual uint32_t get_display_framerate() const = 0; |                 virtual uint32_t get_display_framerate() const = 0; | ||||||
|                 virtual float get_resolution_scale() const = 0; |                 virtual float get_resolution_scale() const = 0; | ||||||
|  |  | ||||||
|  | @ -44,6 +44,8 @@ typedef uint8_t u8; | ||||||
| #  ifdef __cplusplus | #  ifdef __cplusplus | ||||||
| #    define NULLPTR (PTR(void))0 | #    define NULLPTR (PTR(void))0 | ||||||
| #  endif | #  endif | ||||||
|  | #  define PHYS_TO_K1(x)	((x)|0xA0000000) | ||||||
|  | #  define IO_READ(addr) (*(volatile uint32_t*)PHYS_TO_K1(addr)) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifndef NULL | #ifndef NULL | ||||||
|  | @ -285,12 +287,17 @@ void osViSetMode(RDRAM_ARG PTR(OSViMode)); | ||||||
| void osViSetSpecialFeatures(uint32_t func); | void osViSetSpecialFeatures(uint32_t func); | ||||||
| void osViBlack(uint8_t active); | void osViBlack(uint8_t active); | ||||||
| void osViRepeatLine(uint8_t active); | void osViRepeatLine(uint8_t active); | ||||||
|  | u32 osViGetCurrentLine(); | ||||||
|  | u32 osViGetCurrentField(); | ||||||
|  | u32 osViGetStatus(); | ||||||
| void osViSetXScale(float scale); | void osViSetXScale(float scale); | ||||||
| void osViSetYScale(float scale); | void osViSetYScale(float scale); | ||||||
| PTR(void) osViGetNextFramebuffer(); | PTR(void) osViGetNextFramebuffer(); | ||||||
| PTR(void) osViGetCurrentFramebuffer(); | PTR(void) osViGetCurrentFramebuffer(); | ||||||
| u32 osGetCount(); | u32 osGetCount(); | ||||||
|  | void osSetCount(u32 count); | ||||||
| OSTime osGetTime(); | OSTime osGetTime(); | ||||||
|  | void osSetTime(OSTime t); | ||||||
| int osSetTimer(RDRAM_ARG PTR(OSTimer) timer, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg); | int osSetTimer(RDRAM_ARG PTR(OSTimer) timer, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg); | ||||||
| int osStopTimer(RDRAM_ARG PTR(OSTimer) timer); | int osStopTimer(RDRAM_ARG PTR(OSTimer) timer); | ||||||
| u32 osVirtualToPhysical(PTR(void) addr); | u32 osVirtualToPhysical(PTR(void) addr); | ||||||
|  |  | ||||||
|  | @ -27,23 +27,84 @@ struct SpTaskAction { | ||||||
|     OSTask task; |     OSTask task; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct SwapBuffersAction { | struct ScreenUpdateAction { | ||||||
|     uint32_t origin; |     ultramodern::renderer::ViRegs regs; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct UpdateConfigAction { | struct UpdateConfigAction { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| using Action = std::variant<SpTaskAction, SwapBuffersAction, UpdateConfigAction>; | using Action = std::variant<SpTaskAction, ScreenUpdateAction, UpdateConfigAction>; | ||||||
|  | 
 | ||||||
|  | struct ViState { | ||||||
|  |     const OSViMode* mode; | ||||||
|  |     PTR(void) framebuffer; | ||||||
|  |     PTR(OSMesg) mq; | ||||||
|  |     OSMesg msg; | ||||||
|  |     uint32_t state; | ||||||
|  |     uint32_t control; | ||||||
|  |     int retrace_count = 1; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define VI_STATE_BLACK 0x20 | ||||||
|  | #define VI_STATE_REPEATLINE 0x40 | ||||||
| 
 | 
 | ||||||
| static struct { | static struct { | ||||||
|     struct { |     struct { | ||||||
|         std::thread thread; |         std::thread thread; | ||||||
|         PTR(OSMesgQueue) mq = NULLPTR; |         int cur_state; | ||||||
|         PTR(void) current_buffer = NULLPTR; |         int field; | ||||||
|         PTR(void) next_buffer = NULLPTR; |         ViState states[2]; | ||||||
|         OSMesg msg = (OSMesg)0; |         ultramodern::renderer::ViRegs regs; | ||||||
|         int retrace_count = 1; |         ultramodern::renderer::ViRegs update_screen_regs; | ||||||
|  | 
 | ||||||
|  |         ViState* get_next_state() { | ||||||
|  |             return &states[cur_state ^ 1]; | ||||||
|  |         } | ||||||
|  |         ViState* get_cur_state() { | ||||||
|  |             return &states[cur_state]; | ||||||
|  |         } | ||||||
|  |         void update_vi() { | ||||||
|  |             ViState* next_state = get_next_state(); | ||||||
|  |             const OSViMode* next_mode = next_state->mode; | ||||||
|  |             const OSViCommonRegs* common_regs = &next_mode->comRegs; | ||||||
|  |             const OSViFieldRegs* field_regs = &next_mode->fldRegs[field]; | ||||||
|  |             PTR(void) framebuffer = osVirtualToPhysical(next_state->framebuffer); | ||||||
|  |             PTR(void) origin = framebuffer + field_regs->origin; | ||||||
|  | 
 | ||||||
|  |             // Process the VI state flags.
 | ||||||
|  |             uint32_t hStart = common_regs->hStart; | ||||||
|  |             if (next_state->state & VI_STATE_BLACK) { | ||||||
|  |                 hStart = 0; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             uint32_t yScale = field_regs->yScale; | ||||||
|  |             if (next_state->state & VI_STATE_REPEATLINE) { | ||||||
|  |                 yScale = 0; | ||||||
|  |                 origin = framebuffer; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // TODO implement osViFade
 | ||||||
|  | 
 | ||||||
|  |             // Update VI registers.
 | ||||||
|  |             regs.VI_ORIGIN_REG = origin; | ||||||
|  |             regs.VI_WIDTH_REG = common_regs->width; | ||||||
|  |             regs.VI_TIMING_REG = common_regs->burst; | ||||||
|  |             regs.VI_V_SYNC_REG = common_regs->vSync; | ||||||
|  |             regs.VI_H_SYNC_REG = common_regs->hSync; | ||||||
|  |             regs.VI_LEAP_REG = common_regs->leap; | ||||||
|  |             regs.VI_H_START_REG = hStart; | ||||||
|  |             regs.VI_V_START_REG = field_regs->vStart; // TODO implement osViExtendVStart
 | ||||||
|  |             regs.VI_V_BURST_REG = field_regs->vBurst; | ||||||
|  |             regs.VI_INTR_REG = field_regs->vIntr; | ||||||
|  |             regs.VI_X_SCALE_REG = common_regs->xScale; // TODO implement osViSetXScale
 | ||||||
|  |             regs.VI_Y_SCALE_REG = yScale; // TODO implement osViSetYScale
 | ||||||
|  |             regs.VI_STATUS_REG = next_state->control; | ||||||
|  |              | ||||||
|  |             // Swap VI states.
 | ||||||
|  |             cur_state ^= 1; | ||||||
|  |             *get_next_state() = *get_cur_state(); | ||||||
|  |         } | ||||||
|     } vi; |     } vi; | ||||||
|     struct { |     struct { | ||||||
|         std::thread gfx_thread; |         std::thread gfx_thread; | ||||||
|  | @ -71,8 +132,11 @@ static struct { | ||||||
|     moodycamel::ConcurrentQueue<OSThread*> deleted_threads{}; |     moodycamel::ConcurrentQueue<OSThread*> deleted_threads{}; | ||||||
| } events_context{}; | } events_context{}; | ||||||
| 
 | 
 | ||||||
|  | ultramodern::renderer::ViRegs* ultramodern::renderer::get_vi_regs() { | ||||||
|  |     return &events_context.vi.update_screen_regs; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, OSMesg msg) { | extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, OSMesg msg) { | ||||||
|     OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_); |  | ||||||
|     std::lock_guard lock{ events_context.message_mutex }; |     std::lock_guard lock{ events_context.message_mutex }; | ||||||
| 
 | 
 | ||||||
|     switch (event_id) { |     switch (event_id) { | ||||||
|  | @ -96,9 +160,10 @@ extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, | ||||||
| 
 | 
 | ||||||
| extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 retrace_count) { | extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 retrace_count) { | ||||||
|     std::lock_guard lock{ events_context.message_mutex }; |     std::lock_guard lock{ events_context.message_mutex }; | ||||||
|     events_context.vi.mq = mq_; |     ViState* next_state = events_context.vi.get_next_state(); | ||||||
|     events_context.vi.msg = msg; |     next_state->mq = mq_; | ||||||
|     events_context.vi.retrace_count = retrace_count; |     next_state->msg = msg; | ||||||
|  |     next_state->retrace_count = retrace_count; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint64_t total_vis = 0; | uint64_t total_vis = 0; | ||||||
|  | @ -107,7 +172,7 @@ uint64_t total_vis = 0; | ||||||
| extern std::atomic_bool exited; | extern std::atomic_bool exited; | ||||||
| extern moodycamel::LightweightSemaphore graphics_shutdown_ready; | extern moodycamel::LightweightSemaphore graphics_shutdown_ready; | ||||||
| 
 | 
 | ||||||
| void set_dummy_vi(); | void set_dummy_vi(bool odd); | ||||||
| 
 | 
 | ||||||
| void vi_thread_func() { | void vi_thread_func() { | ||||||
|     ultramodern::set_native_thread_name("VI Thread"); |     ultramodern::set_native_thread_name("VI Thread"); | ||||||
|  | @ -116,7 +181,7 @@ void vi_thread_func() { | ||||||
|     ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Critical); |     ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Critical); | ||||||
|     using namespace std::chrono_literals; |     using namespace std::chrono_literals; | ||||||
| 
 | 
 | ||||||
|     int remaining_retraces = events_context.vi.retrace_count; |     int remaining_retraces = 1; | ||||||
| 
 | 
 | ||||||
|     while (!exited) { |     while (!exited) { | ||||||
|         // Determine the next VI time (more accurate than adding 16ms each VI interrupt)
 |         // Determine the next VI time (more accurate than adding 16ms each VI interrupt)
 | ||||||
|  | @ -135,39 +200,42 @@ void vi_thread_func() { | ||||||
|             next = std::chrono::high_resolution_clock::now(); |             next = std::chrono::high_resolution_clock::now(); | ||||||
|         } |         } | ||||||
|         ultramodern::sleep_until(next); |         ultramodern::sleep_until(next); | ||||||
|  |         auto time_now = ultramodern::time_since_start(); | ||||||
|         // Calculate how many VIs have passed
 |         // Calculate how many VIs have passed
 | ||||||
|         uint64_t new_total_vis = (ultramodern::time_since_start() * (60 * ultramodern::get_speed_multiplier()) / 1000ms) + 1; |         uint64_t new_total_vis = (time_now * (60 * ultramodern::get_speed_multiplier()) / 1000ms) + 1; | ||||||
|         if (new_total_vis > total_vis + 1) { |         if (new_total_vis > total_vis + 1) { | ||||||
|             //printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1);
 |             //printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1);
 | ||||||
|         } |         } | ||||||
|         total_vis = new_total_vis; |         total_vis = new_total_vis; | ||||||
| 
 | 
 | ||||||
|         remaining_retraces--; |         // If the game hasn't started yet, set a dummy VI mode and origin.
 | ||||||
|  |         if (!ultramodern::is_game_started()) { | ||||||
|  |             static bool odd = false; | ||||||
|  |             set_dummy_vi(odd); | ||||||
|  |             odd = !odd; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         { |         // Queue a screen update for the graphics thread with the current VI register state.
 | ||||||
|             std::lock_guard lock{ events_context.message_mutex }; |         // Doing this before the VI update is equivalent to updating the screen after the previous frame's scanout finished.
 | ||||||
|  |         events_context.action_queue.enqueue(ScreenUpdateAction{ events_context.vi.regs }); | ||||||
|  | 
 | ||||||
|  |         // Update VI registers and swap VI modes.
 | ||||||
|  |         events_context.vi.update_vi(); | ||||||
|  | 
 | ||||||
|  |         // If the game has started, handle sending VI and AI events.
 | ||||||
|  |         if (ultramodern::is_game_started()) { | ||||||
|  |             remaining_retraces--; | ||||||
|  |              | ||||||
|             uint8_t* rdram = events_context.rdram; |             uint8_t* rdram = events_context.rdram; | ||||||
|  |             std::lock_guard lock{ events_context.message_mutex }; | ||||||
|  |             ViState* cur_state = events_context.vi.get_cur_state(); | ||||||
|             if (remaining_retraces == 0) { |             if (remaining_retraces == 0) { | ||||||
|                 remaining_retraces = events_context.vi.retrace_count; |                 if (cur_state->mq != NULLPTR) { | ||||||
| 
 |                     if (osSendMesg(PASS_RDRAM cur_state->mq, cur_state->msg, OS_MESG_NOBLOCK) == -1) { | ||||||
|                 if (ultramodern::is_game_started()) { |                         //printf("Game skipped a VI frame!\n");
 | ||||||
|                     if (events_context.vi.mq != NULLPTR) { |  | ||||||
|                         if (osSendMesg(PASS_RDRAM events_context.vi.mq, events_context.vi.msg, OS_MESG_NOBLOCK) == -1) { |  | ||||||
|                             //printf("Game skipped a VI frame!\n");
 |  | ||||||
|                         } |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 else { |                 remaining_retraces = cur_state->retrace_count; | ||||||
|                     set_dummy_vi(); |  | ||||||
|                     static bool swap = false; |  | ||||||
|                     uint32_t vi_origin = 0x400 + 0x280; // Skip initial RDRAM contents and add the usual origin offset
 |  | ||||||
|                     // Offset by one FB every other frame so RT64 continues drawing
 |  | ||||||
|                     if (swap) { |  | ||||||
|                         vi_origin += 0x25800; |  | ||||||
|                     } |  | ||||||
|                     osViSwapBuffer(rdram, vi_origin); |  | ||||||
|                     swap = !swap; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             if (events_context.ai.mq != NULLPTR) { |             if (events_context.ai.mq != NULLPTR) { | ||||||
|                 if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) { |                 if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) { | ||||||
|  | @ -298,19 +366,20 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re | ||||||
|                 sp_complete(); |                 sp_complete(); | ||||||
|                 ultramodern::measure_input_latency(); |                 ultramodern::measure_input_latency(); | ||||||
| 
 | 
 | ||||||
|                 auto renderer_start = std::chrono::high_resolution_clock::now(); |                 [[maybe_unused]] auto renderer_start = std::chrono::high_resolution_clock::now(); | ||||||
|                 renderer_context->send_dl(&task_action->task); |                 renderer_context->send_dl(&task_action->task); | ||||||
|                 auto renderer_end = std::chrono::high_resolution_clock::now(); |                 [[maybe_unused]] auto renderer_end = std::chrono::high_resolution_clock::now(); | ||||||
|                 dp_complete(); |                 dp_complete(); | ||||||
|                 // printf("Renderer ProcessDList time: %d us\n", static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(renderer_end - renderer_start).count()));
 |                 // printf("Renderer ProcessDList time: %d us\n", static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(renderer_end - renderer_start).count()));
 | ||||||
|             } |             } | ||||||
|             else if (const auto* swap_action = std::get_if<SwapBuffersAction>(&action)) { |             else if (const auto* screen_update_action = std::get_if<ScreenUpdateAction>(&action)) { | ||||||
|                 events_context.vi.current_buffer = events_context.vi.next_buffer; |                 events_context.vi.update_screen_regs = screen_update_action->regs; | ||||||
|                 renderer_context->update_screen(swap_action->origin); |                 renderer_context->update_screen(); | ||||||
|                 display_refresh_rate = renderer_context->get_display_framerate(); |                 display_refresh_rate = renderer_context->get_display_framerate(); | ||||||
|                 resolution_scale = renderer_context->get_resolution_scale(); |                 resolution_scale = renderer_context->get_resolution_scale(); | ||||||
|             } |             } | ||||||
|             else if (const auto* config_action = std::get_if<UpdateConfigAction>(&action)) { |             else if (const auto* config_action = std::get_if<UpdateConfigAction>(&action)) { | ||||||
|  |                 (void)config_action; | ||||||
|                 auto new_config = ultramodern::renderer::get_graphics_config(); |                 auto new_config = ultramodern::renderer::get_graphics_config(); | ||||||
|                 if (renderer_context->update_config(old_config, new_config)) { |                 if (renderer_context->update_config(old_config, new_config)) { | ||||||
|                     old_config = new_config; |                     old_config = new_config; | ||||||
|  | @ -323,79 +392,6 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re | ||||||
|     renderer_context->shutdown(); |     renderer_context->shutdown(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern unsigned int VI_STATUS_REG; |  | ||||||
| extern unsigned int VI_ORIGIN_REG; |  | ||||||
| extern unsigned int VI_WIDTH_REG; |  | ||||||
| extern unsigned int VI_INTR_REG; |  | ||||||
| extern unsigned int VI_V_CURRENT_LINE_REG; |  | ||||||
| extern unsigned int VI_TIMING_REG; |  | ||||||
| extern unsigned int VI_V_SYNC_REG; |  | ||||||
| extern unsigned int VI_H_SYNC_REG; |  | ||||||
| extern unsigned int VI_LEAP_REG; |  | ||||||
| extern unsigned int VI_H_START_REG; |  | ||||||
| extern unsigned int VI_V_START_REG; |  | ||||||
| extern unsigned int VI_V_BURST_REG; |  | ||||||
| extern unsigned int VI_X_SCALE_REG; |  | ||||||
| extern unsigned int VI_Y_SCALE_REG; |  | ||||||
| 
 |  | ||||||
| #define VI_STATE_BLACK 0x20 |  | ||||||
| #define VI_STATE_REPEATLINE 0x40 |  | ||||||
| 
 |  | ||||||
| uint32_t hstart = 0; |  | ||||||
| uint32_t vi_origin_offset = 320 * sizeof(uint16_t); |  | ||||||
| static uint16_t vi_state = 0; |  | ||||||
| 
 |  | ||||||
| void set_dummy_vi() { |  | ||||||
|     VI_STATUS_REG = 0x311E; |  | ||||||
|     VI_WIDTH_REG = 0x140; |  | ||||||
|     VI_V_SYNC_REG = 0x20D; |  | ||||||
|     VI_H_SYNC_REG = 0xC15; |  | ||||||
|     VI_LEAP_REG = 0x0C150C15; |  | ||||||
|     hstart = 0x006C02EC; |  | ||||||
|     VI_X_SCALE_REG = 0x200; |  | ||||||
|     VI_V_CURRENT_LINE_REG = 0x0; |  | ||||||
|     vi_origin_offset = 0x280; |  | ||||||
|     VI_Y_SCALE_REG = 0x400; |  | ||||||
|     VI_V_START_REG = 0x2501FF; |  | ||||||
|     VI_V_BURST_REG = 0xE0204; |  | ||||||
|     VI_INTR_REG = 0x2; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) { |  | ||||||
|     VI_H_START_REG = hstart; |  | ||||||
|     if (vi_state & VI_STATE_BLACK) { |  | ||||||
|         VI_H_START_REG = 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (vi_state & VI_STATE_REPEATLINE) { |  | ||||||
|         VI_Y_SCALE_REG = 0; |  | ||||||
|         VI_ORIGIN_REG = osVirtualToPhysical(frameBufPtr); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     events_context.vi.next_buffer = frameBufPtr; |  | ||||||
|     events_context.action_queue.enqueue(SwapBuffersAction{ osVirtualToPhysical(frameBufPtr) + vi_origin_offset }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) { |  | ||||||
|     OSViMode* mode = TO_PTR(OSViMode, mode_); |  | ||||||
|     VI_STATUS_REG = mode->comRegs.ctrl; |  | ||||||
|     VI_WIDTH_REG = mode->comRegs.width; |  | ||||||
|     // burst
 |  | ||||||
|     VI_V_SYNC_REG = mode->comRegs.vSync; |  | ||||||
|     VI_H_SYNC_REG = mode->comRegs.hSync; |  | ||||||
|     VI_LEAP_REG = mode->comRegs.leap; |  | ||||||
|     hstart = mode->comRegs.hStart; |  | ||||||
|     VI_X_SCALE_REG = mode->comRegs.xScale; |  | ||||||
|     VI_V_CURRENT_LINE_REG = mode->comRegs.vCurrent; |  | ||||||
| 
 |  | ||||||
|     // TODO swap these every VI to account for fields changing
 |  | ||||||
|     vi_origin_offset = mode->fldRegs[0].origin; |  | ||||||
|     VI_Y_SCALE_REG = mode->fldRegs[0].yScale; |  | ||||||
|     VI_V_START_REG = mode->fldRegs[0].vStart; |  | ||||||
|     VI_V_BURST_REG = mode->fldRegs[0].vBurst; |  | ||||||
|     VI_INTR_REG = mode->fldRegs[0].vIntr; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #define VI_CTRL_TYPE_16             0x00002 | #define VI_CTRL_TYPE_16             0x00002 | ||||||
| #define VI_CTRL_TYPE_32             0x00003 | #define VI_CTRL_TYPE_32             0x00003 | ||||||
| #define VI_CTRL_GAMMA_DITHER_ON     0x00004 | #define VI_CTRL_GAMMA_DITHER_ON     0x00004 | ||||||
|  | @ -412,6 +408,54 @@ extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) { | ||||||
| #define VI_CTRL_PIXEL_ADV_3         0x03000 | #define VI_CTRL_PIXEL_ADV_3         0x03000 | ||||||
| #define VI_CTRL_DITHER_FILTER_ON    0x10000 | #define VI_CTRL_DITHER_FILTER_ON    0x10000 | ||||||
| 
 | 
 | ||||||
|  | static const OSViMode dummy_mode = []() { | ||||||
|  |     OSViMode ret{}; | ||||||
|  | 
 | ||||||
|  |     ret.type = 2; | ||||||
|  |     ret.comRegs.ctrl = VI_CTRL_TYPE_16 | VI_CTRL_GAMMA_DITHER_ON | VI_CTRL_GAMMA_ON | VI_CTRL_DIVOT_ON | VI_CTRL_ANTIALIAS_MODE_1 | VI_CTRL_PIXEL_ADV_3; | ||||||
|  |     ret.comRegs.width = 0x140; | ||||||
|  |     ret.comRegs.burst = 0x03E52239; | ||||||
|  |     ret.comRegs.vSync = 0x20D; | ||||||
|  |     ret.comRegs.hSync = 0xC15; | ||||||
|  |     ret.comRegs.leap = 0x0C150C15; | ||||||
|  |     ret.comRegs.hStart = 0x006C02EC; | ||||||
|  |     ret.comRegs.xScale = 0x200; | ||||||
|  |     ret.comRegs.vCurrent = 0x0; | ||||||
|  | 
 | ||||||
|  |     for (int field = 0; field < 2; field++) { | ||||||
|  |         ret.fldRegs[field].origin = 0x280; | ||||||
|  |         ret.fldRegs[field].yScale = 0x400; | ||||||
|  |         ret.fldRegs[field].vStart = 0x2501FF; | ||||||
|  |         ret.fldRegs[field].vBurst = 0xE0204; | ||||||
|  |         ret.fldRegs[field].vIntr = 0x2; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | }(); | ||||||
|  | 
 | ||||||
|  | void set_dummy_vi(bool odd) { | ||||||
|  |     ViState* next_state = events_context.vi.get_next_state(); | ||||||
|  |     next_state->mode = &dummy_mode; | ||||||
|  |     // Set up a dummy framebuffer.
 | ||||||
|  |     next_state->framebuffer = 0x80700000; | ||||||
|  |     if (odd) { | ||||||
|  |         next_state->framebuffer += 0x25800; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) { | ||||||
|  |     std::lock_guard lock{ events_context.message_mutex }; | ||||||
|  |     events_context.vi.get_next_state()->framebuffer = frameBufPtr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) { | ||||||
|  |     std::lock_guard lock{ events_context.message_mutex }; | ||||||
|  |     OSViMode* mode = TO_PTR(OSViMode, mode_); | ||||||
|  |     ViState* next_state = events_context.vi.get_next_state(); | ||||||
|  |     next_state->mode = mode; | ||||||
|  |     next_state->control = next_state->mode->comRegs.ctrl; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #define OS_VI_GAMMA_ON          0x0001 | #define OS_VI_GAMMA_ON          0x0001 | ||||||
| #define OS_VI_GAMMA_OFF         0x0002 | #define OS_VI_GAMMA_OFF         0x0002 | ||||||
| #define OS_VI_GAMMA_DITHER_ON   0x0004 | #define OS_VI_GAMMA_DITHER_ON   0x0004 | ||||||
|  | @ -422,57 +466,78 @@ extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) { | ||||||
| #define OS_VI_DITHER_FILTER_OFF 0x0080 | #define OS_VI_DITHER_FILTER_OFF 0x0080 | ||||||
| 
 | 
 | ||||||
| extern "C" void osViSetSpecialFeatures(uint32_t func) { | extern "C" void osViSetSpecialFeatures(uint32_t func) { | ||||||
|  |     std::lock_guard lock{ events_context.message_mutex }; | ||||||
|  |     ViState* next_state = events_context.vi.get_next_state(); | ||||||
|  |     uint32_t* control_out = &next_state->control; | ||||||
|     if ((func & OS_VI_GAMMA_ON) != 0) { |     if ((func & OS_VI_GAMMA_ON) != 0) { | ||||||
|         VI_STATUS_REG |= VI_CTRL_GAMMA_ON; |         *control_out |= VI_CTRL_GAMMA_ON; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ((func & OS_VI_GAMMA_OFF) != 0) { |     if ((func & OS_VI_GAMMA_OFF) != 0) { | ||||||
|         VI_STATUS_REG &= ~VI_CTRL_GAMMA_ON; |         *control_out &= ~VI_CTRL_GAMMA_ON; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ((func & OS_VI_GAMMA_DITHER_ON) != 0) { |     if ((func & OS_VI_GAMMA_DITHER_ON) != 0) { | ||||||
|         VI_STATUS_REG |= VI_CTRL_GAMMA_DITHER_ON; |         *control_out |= VI_CTRL_GAMMA_DITHER_ON; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ((func & OS_VI_GAMMA_DITHER_OFF) != 0) { |     if ((func & OS_VI_GAMMA_DITHER_OFF) != 0) { | ||||||
|         VI_STATUS_REG &= ~VI_CTRL_GAMMA_DITHER_ON; |         *control_out &= ~VI_CTRL_GAMMA_DITHER_ON; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ((func & OS_VI_DIVOT_ON) != 0) { |     if ((func & OS_VI_DIVOT_ON) != 0) { | ||||||
|         VI_STATUS_REG |= VI_CTRL_DIVOT_ON; |         *control_out |= VI_CTRL_DIVOT_ON; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ((func & OS_VI_DIVOT_OFF) != 0) { |     if ((func & OS_VI_DIVOT_OFF) != 0) { | ||||||
|         VI_STATUS_REG &= ~VI_CTRL_DIVOT_ON; |         *control_out &= ~VI_CTRL_DIVOT_ON; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ((func & OS_VI_DITHER_FILTER_ON) != 0) { |     if ((func & OS_VI_DITHER_FILTER_ON) != 0) { | ||||||
|         VI_STATUS_REG |= VI_CTRL_DITHER_FILTER_ON; |         *control_out |= VI_CTRL_DITHER_FILTER_ON; | ||||||
|         VI_STATUS_REG &= ~VI_CTRL_ANTIALIAS_MASK; |         *control_out &= ~VI_CTRL_ANTIALIAS_MASK; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ((func & OS_VI_DITHER_FILTER_OFF) != 0) { |     if ((func & OS_VI_DITHER_FILTER_OFF) != 0) { | ||||||
|         VI_STATUS_REG &= ~VI_CTRL_DITHER_FILTER_ON; |         *control_out &= ~VI_CTRL_DITHER_FILTER_ON; | ||||||
|         //VI_STATUS_REG |= __osViNext->modep->comRegs.ctrl & VI_CTRL_ANTIALIAS_MASK;
 |         *control_out |= next_state->mode->comRegs.ctrl & VI_CTRL_ANTIALIAS_MASK; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern "C" void osViBlack(uint8_t active) { | extern "C" void osViBlack(uint8_t active) { | ||||||
|  |     std::lock_guard lock{ events_context.message_mutex }; | ||||||
|  |     ViState* next_state = events_context.vi.get_next_state(); | ||||||
|  |     uint32_t* state_out = &next_state->state; | ||||||
|     if (active) { |     if (active) { | ||||||
|         vi_state |= VI_STATE_BLACK; |         *state_out |= VI_STATE_BLACK; | ||||||
|     } else { |     } else { | ||||||
|         vi_state &= ~VI_STATE_BLACK; |         *state_out &= ~VI_STATE_BLACK; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern "C" void osViRepeatLine(uint8_t active) { | extern "C" void osViRepeatLine(uint8_t active) { | ||||||
|  |     std::lock_guard lock{ events_context.message_mutex }; | ||||||
|  |     ViState* next_state = events_context.vi.get_next_state(); | ||||||
|  |     uint32_t* state_out = &next_state->state; | ||||||
|     if (active) { |     if (active) { | ||||||
|         vi_state |= VI_STATE_REPEATLINE; |         *state_out |= VI_STATE_REPEATLINE; | ||||||
|     } else { |     } else { | ||||||
|         vi_state &= ~VI_STATE_REPEATLINE; |         *state_out &= ~VI_STATE_REPEATLINE; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | extern "C" u32 osViGetCurrentLine() { | ||||||
|  |     return IO_READ(VI_V_CURRENT_LINE_REG); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern "C" u32 osViGetCurrentField() { | ||||||
|  |     return IO_READ(VI_V_CURRENT_LINE_REG) & 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern "C" u32 osViGetStatus() { | ||||||
|  |     return IO_READ(VI_STATUS_REG); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| extern "C" void osViSetXScale(float scale) { | extern "C" void osViSetXScale(float scale) { | ||||||
|     if (scale != 1.0f) { |     if (scale != 1.0f) { | ||||||
|         assert(false); |         assert(false); | ||||||
|  | @ -486,11 +551,11 @@ extern "C" void osViSetYScale(float scale) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern "C" PTR(void) osViGetNextFramebuffer() { | extern "C" PTR(void) osViGetNextFramebuffer() { | ||||||
|     return events_context.vi.next_buffer; |     return events_context.vi.get_next_state()->framebuffer; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern "C" PTR(void) osViGetCurrentFramebuffer() { | extern "C" PTR(void) osViGetCurrentFramebuffer() { | ||||||
|     return events_context.vi.current_buffer; |     return events_context.vi.get_cur_state()->framebuffer; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ultramodern::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) { | void ultramodern::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) { | ||||||
|  | @ -523,7 +588,7 @@ void ultramodern::init_events(RDRAM_ARG ultramodern::renderer::WindowHandle wind | ||||||
|     task_thread_ready.wait(); |     task_thread_ready.wait(); | ||||||
| 
 | 
 | ||||||
|     ultramodern::renderer::SetupResult setup_result = renderer_setup_result.load(); |     ultramodern::renderer::SetupResult setup_result = renderer_setup_result.load(); | ||||||
|     if (renderer_setup_result != ultramodern::renderer::SetupResult::Success) { |     if (setup_result != ultramodern::renderer::SetupResult::Success) { | ||||||
|         auto show_renderer_error = [](const std::string& msg) { |         auto show_renderer_error = [](const std::string& msg) { | ||||||
|             std::string error_msg = "An error has been encountered on startup: " + msg; |             std::string error_msg = "An error has been encountered on startup: " + msg; | ||||||
| 
 | 
 | ||||||
|  | @ -531,7 +596,7 @@ void ultramodern::init_events(RDRAM_ARG ultramodern::renderer::WindowHandle wind | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         const std::string driver_os_suffix = "\nPlease make sure your GPU drivers and your OS are up to date."; |         const std::string driver_os_suffix = "\nPlease make sure your GPU drivers and your OS are up to date."; | ||||||
|         switch (renderer_setup_result) { |         switch (setup_result) { | ||||||
|             case ultramodern::renderer::SetupResult::Success: |             case ultramodern::renderer::SetupResult::Success: | ||||||
|                 break; |                 break; | ||||||
|             case ultramodern::renderer::SetupResult::DynamicLibrariesNotFound: |             case ultramodern::renderer::SetupResult::DynamicLibrariesNotFound: | ||||||
|  |  | ||||||
|  | @ -13,6 +13,8 @@ | ||||||
| 
 | 
 | ||||||
| // Start time for the program
 | // Start time for the program
 | ||||||
| static std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now(); | static std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now(); | ||||||
|  | // Offset of the duration since program start used to calculate the value for osGetTime. 
 | ||||||
|  | static int64_t ostime_offset = 0; | ||||||
| // Game speed multiplier (1 means no speedup)
 | // Game speed multiplier (1 means no speedup)
 | ||||||
| constexpr uint32_t speed_multiplier = 1; | constexpr uint32_t speed_multiplier = 1; | ||||||
| // N64 CPU counter ticks per millisecond
 | // N64 CPU counter ticks per millisecond
 | ||||||
|  | @ -162,12 +164,20 @@ extern "C" u32 osGetCount() { | ||||||
|     return (uint32_t)total_count; |     return (uint32_t)total_count; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | extern "C" void osSetCount(u32 count) { | ||||||
|  |     assert(false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| extern "C" OSTime osGetTime() { | extern "C" OSTime osGetTime() { | ||||||
|     uint64_t total_count = time_now(); |     uint64_t total_count = time_now() - ostime_offset; | ||||||
| 
 | 
 | ||||||
|     return total_count; |     return total_count; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | extern "C" void osSetTime(OSTime t) { | ||||||
|  |     ostime_offset = time_now() - t; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| extern "C" int osSetTimer(RDRAM_ARG PTR(OSTimer) t_, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg) { | extern "C" int osSetTimer(RDRAM_ARG PTR(OSTimer) t_, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg) { | ||||||
|     OSTimer* t = TO_PTR(OSTimer, t_); |     OSTimer* t = TO_PTR(OSTimer, t_); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue