Implement displaylist event extension functions for easier game scheduling modifications (#128)
Some checks failed
validate / ubuntu (arm64, Debug) (push) Has been cancelled
validate / ubuntu (arm64, Release) (push) Has been cancelled
validate / ubuntu (x64, Debug) (push) Has been cancelled
validate / ubuntu (x64, Release) (push) Has been cancelled
validate / windows (x64, Debug) (push) Has been cancelled
validate / windows (x64, Release) (push) Has been cancelled
validate / macos (arm64, Debug) (push) Has been cancelled
validate / macos (arm64, Release) (push) Has been cancelled
validate / macos (x64, Debug) (push) Has been cancelled
validate / macos (x64, Release) (push) Has been cancelled

* Implement displaylist event extension functions for easier game scheduling modifications

* Fix build on clang 15
This commit is contained in:
Wiseguy 2025-12-12 02:11:14 -05:00 committed by GitHub
parent dced99c0b8
commit e8d7aec1bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 129 additions and 0 deletions

View file

@ -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"

View file

@ -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);
}

View file

@ -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"

View file

@ -0,0 +1,48 @@
#ifndef __ULTRAMODERN_EXTENSIONS_H__
#define __ULTRAMODERN_EXTENSIONS_H__
#if defined(mips) // Patch compilation
#include <ultra64.h>
#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

View file

@ -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<u32>(std::chrono::duration_cast<std::chrono::microseconds>(renderer_end - renderer_start).count()));
}
else if (const auto* screen_update_action = std::get_if<ScreenUpdateAction>(&action)) {

View file

@ -0,0 +1,60 @@
#include <mutex>
#include <vector>
#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<DLEvent> 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);
}