diff --git a/librecomp/CMakeLists.txt b/librecomp/CMakeLists.txt index 4db1505..fcc3337 100644 --- a/librecomp/CMakeLists.txt +++ b/librecomp/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(librecomp STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/dp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/eep.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/euc-jp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/extensions.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/files.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/flash.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/heap.cpp" diff --git a/librecomp/src/extensions.cpp b/librecomp/src/extensions.cpp new file mode 100644 index 0000000..33174c5 --- /dev/null +++ b/librecomp/src/extensions.cpp @@ -0,0 +1,11 @@ +#include "recomp.h" +#include "ultramodern/extensions.h" +#include "librecomp/helpers.hpp" + +extern "C" void osExQueueDisplaylistEvent_recomp(uint8_t* rdram, recomp_context* ctx) { + PTR(OSMesgQueue) mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx); + OSMesg mesg = _arg<1, OSMesg>(rdram, ctx); + PTR(void) displaylist = _arg<2, PTR(void)>(rdram, ctx); + u32 event_type = _arg<3, u32>(rdram, ctx); + osExQueueDisplaylistEvent(mq, mesg, displaylist, event_type); +} diff --git a/ultramodern/CMakeLists.txt b/ultramodern/CMakeLists.txt index 90ac35a..5593172 100644 --- a/ultramodern/CMakeLists.txt +++ b/ultramodern/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(ultramodern STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/audio.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/error_handling.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/events.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/extensions.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/input.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/mesgqueue.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/misc_ultra.cpp" diff --git a/ultramodern/include/ultramodern/extensions.h b/ultramodern/include/ultramodern/extensions.h new file mode 100644 index 0000000..0b9095b --- /dev/null +++ b/ultramodern/include/ultramodern/extensions.h @@ -0,0 +1,48 @@ +#ifndef __ULTRAMODERN_EXTENSIONS_H__ +#define __ULTRAMODERN_EXTENSIONS_H__ + +#if defined(mips) // Patch compilation +#include +#else +#include "ultramodern/ultra64.h" +#endif + +#ifndef PTR +#define PTR(x) x* +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + // Triggers when a displaylist has been submitted to the renderer. + OS_EX_DISPLAYLIST_EVENT_SUBMITTED, + // Triggers when a displaylist has been fully parsed by the renderer. This includes any referenced data, such + // as vertices, matrices, and textures. + OS_EX_DISPLAYLIST_EVENT_PARSED, + // Triggers when rendering of a displaylist has been completed by the renderer. This only includes the + // rendering pass that produces an image in RAM, not the high res output images that get presented to the user. + OS_EX_DISPLAYLIST_EVENT_COMPLETED +} DisplaylistEventType; + +// Queues a one-time message to be sent the next time the given event type occurs for the given displaylist. +// This allows easier detection of displaylist events without needing to patch a game's scheduler. +// The event will be cleared after it occurs. +// event_type must be a member of the DisplaylistEventType enum. +void osExQueueDisplaylistEvent(PTR(OSMesgQueue) mq, OSMesg mesg, PTR(void) displaylist, u32 event_type); + +#ifdef __cplusplus +} + +namespace ultramodern { + namespace extensions { + void on_displaylist_submitted(PTR(u64) displaylist); + void on_displaylist_parsed(PTR(u64) displaylist); + void on_displaylist_completed(PTR(u64) displaylist); + } +} + +#endif + +#endif diff --git a/ultramodern/src/events.cpp b/ultramodern/src/events.cpp index 2aeb93a..1cd7907 100644 --- a/ultramodern/src/events.cpp +++ b/ultramodern/src/events.cpp @@ -13,6 +13,7 @@ #include "ultramodern/ultra64.h" #include "ultramodern/ultramodern.hpp" +#include "ultramodern/extensions.h" #include "ultramodern/rsp.hpp" #include "ultramodern/renderer_context.hpp" @@ -364,10 +365,17 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re sp_complete(); ultramodern::measure_input_latency(); + PTR(u64) displaylist = task_action->task.t.data_ptr; + ultramodern::extensions::on_displaylist_submitted(displaylist); + [[maybe_unused]] auto renderer_start = std::chrono::high_resolution_clock::now(); renderer_context->send_dl(&task_action->task); [[maybe_unused]] auto renderer_end = std::chrono::high_resolution_clock::now(); + dp_complete(); + // TODO hook the parsed event up to the actual parsing point when a callback is added to RT64. + ultramodern::extensions::on_displaylist_parsed(displaylist); + ultramodern::extensions::on_displaylist_completed(displaylist); // printf("Renderer ProcessDList time: %d us\n", static_cast(std::chrono::duration_cast(renderer_end - renderer_start).count())); } else if (const auto* screen_update_action = std::get_if(&action)) { diff --git a/ultramodern/src/extensions.cpp b/ultramodern/src/extensions.cpp new file mode 100644 index 0000000..b0f4dff --- /dev/null +++ b/ultramodern/src/extensions.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "ultramodern/extensions.h" +#include "ultramodern/ultramodern.hpp" + +struct DLEvent { + PTR(OSMesgQueue) mq; + OSMesg mesg; + PTR(void) displaylist; + u32 event_type; +}; + +struct { + struct { + std::mutex dl_event_mutex; + std::vector pending_events; + } dl_events; +} extension_state; + +extern "C" void osExQueueDisplaylistEvent(PTR(OSMesgQueue) mq, OSMesg mesg, PTR(void) displaylist, u32 event_type) { + std::lock_guard lock{ extension_state.dl_events.dl_event_mutex }; + + assert( + event_type == OS_EX_DISPLAYLIST_EVENT_SUBMITTED || + event_type == OS_EX_DISPLAYLIST_EVENT_PARSED || + event_type == OS_EX_DISPLAYLIST_EVENT_COMPLETED); + + extension_state.dl_events.pending_events.emplace_back( + DLEvent{ mq, mesg, displaylist, event_type } + ); +} + +void dispatch_displaylist_events(PTR(void) displaylist, u32 event_type) { + std::lock_guard lock{ extension_state.dl_events.dl_event_mutex }; + + // Check every pending DL event to see if they match this displaylist and event type. + for (auto iter = extension_state.dl_events.pending_events.begin(); iter != extension_state.dl_events.pending_events.end(); ) { + if (iter->displaylist == displaylist && iter->event_type == event_type) { + // Send the provided message to the corresponding message queue for this event, then remove this event from the queue. + ultramodern::enqueue_external_message(iter->mq, iter->mesg, false, true); + iter = extension_state.dl_events.pending_events.erase(iter); + } + else { + ++iter; + } + } +} + +void ultramodern::extensions::on_displaylist_submitted(PTR(void) displaylist) { + dispatch_displaylist_events(displaylist, OS_EX_DISPLAYLIST_EVENT_SUBMITTED); +} + +void ultramodern::extensions::on_displaylist_parsed(PTR(void) displaylist) { + dispatch_displaylist_events(displaylist, OS_EX_DISPLAYLIST_EVENT_PARSED); +} + +void ultramodern::extensions::on_displaylist_completed(PTR(void) displaylist) { + dispatch_displaylist_events(displaylist, OS_EX_DISPLAYLIST_EVENT_COMPLETED); +}