diff --git a/librecomp/src/flash.cpp b/librecomp/src/flash.cpp index b9fd333..47f860a 100644 --- a/librecomp/src/flash.cpp +++ b/librecomp/src/flash.cpp @@ -154,7 +154,7 @@ extern "C" void osFlashWriteBuffer_recomp(uint8_t * rdram, recomp_context * ctx) } // Send the message indicating write completion - osSendMesg(PASS_RDRAM mq, 0, OS_MESG_NOBLOCK); + ultramodern::enqueue_external_message(mq, 0, false, true); ctx->r2 = 0; } @@ -193,7 +193,7 @@ extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) { save_read(PASS_RDRAM dramAddr, offset, count); // Send the message indicating read completion - osSendMesg(PASS_RDRAM mq, 0, OS_MESG_NOBLOCK); + ultramodern::enqueue_external_message(mq, 0, false, true); ctx->r2 = 0; } diff --git a/librecomp/src/pi.cpp b/librecomp/src/pi.cpp index d7ebd53..62656ad 100644 --- a/librecomp/src/pi.cpp +++ b/librecomp/src/pi.cpp @@ -275,7 +275,7 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_ recomp::do_rom_read(rdram, rdram_address, physical_addr, size); // Send a message to the mq to indicate that the transfer completed - osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK); + ultramodern::enqueue_external_message(mq, 0, false, true); } else if (physical_addr >= recomp::sram_base) { if (!recomp::sram_allowed()) { ultramodern::error_handling::message_box("Attempted to use SRAM saving with other save type"); @@ -285,7 +285,7 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_ save_read(rdram, rdram_address, physical_addr - recomp::sram_base, size); // Send a message to the mq to indicate that the transfer completed - osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK); + ultramodern::enqueue_external_message(mq, 0, false, true); } else { fprintf(stderr, "[WARN] PI DMA read from unknown region, phys address 0x%08X\n", physical_addr); } @@ -302,7 +302,7 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_ save_write(rdram, rdram_address, physical_addr - recomp::sram_base, size); // Send a message to the mq to indicate that the transfer completed - osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK); + ultramodern::enqueue_external_message(mq, 0, false, true); } else { fprintf(stderr, "[WARN] PI DMA write to unknown region, phys address 0x%08X\n", physical_addr); } diff --git a/ultramodern/include/ultramodern/ultramodern.hpp b/ultramodern/include/ultramodern/ultramodern.hpp index db45ee4..3b28af3 100644 --- a/ultramodern/include/ultramodern/ultramodern.hpp +++ b/ultramodern/include/ultramodern/ultramodern.hpp @@ -52,6 +52,7 @@ bool thread_queue_empty(RDRAM_ARG PTR(PTR(OSThread)) queue); PTR(OSThread) thread_queue_peek(RDRAM_ARG PTR(PTR(OSThread)) queue); // Message queues. +void enqueue_external_message(PTR(OSMesgQueue) mq, OSMesg msg, bool jam, bool requeue_if_blocked); void wait_for_external_message(RDRAM_ARG1); void wait_for_external_message_timed(RDRAM_ARG1, u32 millis); @@ -77,7 +78,7 @@ PTR(OSThread) this_thread(); void set_main_thread(); bool is_game_thread(); void submit_rsp_task(RDRAM_ARG PTR(OSTask) task); -void send_si_message(RDRAM_ARG1); +void send_si_message(); uint32_t get_speed_multiplier(); // Time diff --git a/ultramodern/src/events.cpp b/ultramodern/src/events.cpp index 46f63f1..2aeb93a 100644 --- a/ultramodern/src/events.cpp +++ b/ultramodern/src/events.cpp @@ -226,21 +226,19 @@ void vi_thread_func() { if (ultramodern::is_game_started()) { remaining_retraces--; - 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 (cur_state->mq != NULLPTR) { - if (osSendMesg(PASS_RDRAM cur_state->mq, cur_state->msg, OS_MESG_NOBLOCK) == -1) { - //printf("Game skipped a VI frame!\n"); - } + // Send a message to the VI queue, and do not set it to be requeued if the queue was full. + // The worst case scenario is that the game misses a VI message and has to wait a little longer for the next. + ultramodern::enqueue_external_message(cur_state->mq, cur_state->msg, false, false); } remaining_retraces = cur_state->retrace_count; } if (events_context.ai.mq != NULLPTR) { - if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) { - //printf("Game skipped a AI frame!\n"); - } + // Send a message to the VI queue, and do not set it to be requeued if the queue was full for the same reason as the VI message above. + ultramodern::enqueue_external_message(events_context.ai.mq, events_context.ai.msg, false, false); } } @@ -253,13 +251,13 @@ void vi_thread_func() { void sp_complete() { uint8_t* rdram = events_context.rdram; std::lock_guard lock{ events_context.message_mutex }; - osSendMesg(PASS_RDRAM events_context.sp.mq, events_context.sp.msg, OS_MESG_NOBLOCK); + ultramodern::enqueue_external_message(events_context.sp.mq, events_context.sp.msg, false, true); } void dp_complete() { uint8_t* rdram = events_context.rdram; std::lock_guard lock{ events_context.message_mutex }; - osSendMesg(PASS_RDRAM events_context.dp.mq, events_context.dp.msg, OS_MESG_NOBLOCK); + ultramodern::enqueue_external_message(events_context.dp.mq, events_context.dp.msg, false, true); } void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready) { @@ -559,8 +557,8 @@ void ultramodern::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) { } } -void ultramodern::send_si_message(RDRAM_ARG1) { - osSendMesg(PASS_RDRAM events_context.si.mq, events_context.si.msg, OS_MESG_NOBLOCK); +void ultramodern::send_si_message() { + ultramodern::enqueue_external_message(events_context.si.mq, events_context.si.msg, false, true); } void ultramodern::init_events(RDRAM_ARG ultramodern::renderer::WindowHandle window_handle) { diff --git a/ultramodern/src/input.cpp b/ultramodern/src/input.cpp index 03a7c6b..c48db1b 100644 --- a/ultramodern/src/input.cpp +++ b/ultramodern/src/input.cpp @@ -98,7 +98,7 @@ extern "C" s32 osContReset(RDRAM_ARG PTR(OSMesgQueue) mq, PTR(OSContStatus) data } extern "C" s32 osContStartQuery(RDRAM_ARG PTR(OSMesgQueue) mq) { - ultramodern::send_si_message(PASS_RDRAM1); + ultramodern::send_si_message(); return 0; } @@ -109,7 +109,7 @@ extern "C" s32 osContStartReadData(RDRAM_ARG PTR(OSMesgQueue) mq) { } update_poll_time(); - ultramodern::send_si_message(rdram); + ultramodern::send_si_message(); return 0; } diff --git a/ultramodern/src/mesgqueue.cpp b/ultramodern/src/mesgqueue.cpp index 6f38e02..961de3d 100644 --- a/ultramodern/src/mesgqueue.cpp +++ b/ultramodern/src/mesgqueue.cpp @@ -9,33 +9,44 @@ struct QueuedMessage { PTR(OSMesgQueue) mq; OSMesg mesg; bool jam; + bool requeue_if_blocked; }; static moodycamel::BlockingConcurrentQueue external_messages {}; -void enqueue_external_message(PTR(OSMesgQueue) mq, OSMesg msg, bool jam) { - external_messages.enqueue({mq, msg, jam}); +void ultramodern::enqueue_external_message(PTR(OSMesgQueue) mq, OSMesg msg, bool jam, bool requeue_if_blocked) { + external_messages.enqueue({mq, msg, jam, requeue_if_blocked}); } bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block); void dequeue_external_messages(RDRAM_ARG1) { QueuedMessage to_send; + std::vector requeued_messages{}; while (external_messages.try_dequeue(to_send)) { - do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false); + if (!do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false) && to_send.requeue_if_blocked) { + requeued_messages.push_back(to_send); + } + } + for (QueuedMessage& cur_mesg : requeued_messages) { + external_messages.enqueue(cur_mesg); } } void ultramodern::wait_for_external_message(RDRAM_ARG1) { QueuedMessage to_send; external_messages.wait_dequeue(to_send); - do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false); + if (!do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false) && to_send.requeue_if_blocked) { + external_messages.enqueue(to_send); + } } -void ultramodern::wait_for_external_message_timed(RDRAM_ARG1, u32 millis) { +void ultramodern::wait_for_external_message_timed(RDRAM_ARG u32 millis) { QueuedMessage to_send; if (external_messages.wait_dequeue_timed(to_send, std::chrono::milliseconds{millis})) { - do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false); + if (!do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false) && to_send.requeue_if_blocked) { + external_messages.enqueue(to_send); + } } } @@ -138,7 +149,7 @@ extern "C" s32 osSendMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) // Don't directly send to the message queue if this isn't a game thread to avoid contention. if (!ultramodern::is_game_thread()) { - enqueue_external_message(mq_, msg, jam); + ultramodern::enqueue_external_message(mq_, msg, jam, false); return 0; } @@ -160,7 +171,7 @@ extern "C" s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) // Don't directly send to the message queue if this isn't a game thread to avoid contention. if (!ultramodern::is_game_thread()) { - enqueue_external_message(mq_, msg, jam); + ultramodern::enqueue_external_message(mq_, msg, jam, false); return 0; } diff --git a/ultramodern/src/timer.cpp b/ultramodern/src/timer.cpp index d36ffcc..276301b 100644 --- a/ultramodern/src/timer.cpp +++ b/ultramodern/src/timer.cpp @@ -130,7 +130,7 @@ void timer_thread(RDRAM_ARG1) { } else { // Waiting for the timer completed, so send the timer's message to its message queue - osSendMesg(PASS_RDRAM cur_timer->mq, cur_timer->msg, OS_MESG_NOBLOCK); + ultramodern::enqueue_external_message(cur_timer->mq, cur_timer->msg, false, true); // If the timer has a specified interval then reload it with that value if (cur_timer->interval != 0) { cur_timer->timestamp = cur_timer->interval + time_now();