Skip to content

Commit e8d7aec

Browse files
authored
Implement displaylist event extension functions for easier game scheduling modifications (#128)
* Implement displaylist event extension functions for easier game scheduling modifications * Fix build on clang 15
1 parent dced99c commit e8d7aec

File tree

6 files changed

+129
-0
lines changed

6 files changed

+129
-0
lines changed

librecomp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_library(librecomp STATIC
1212
"${CMAKE_CURRENT_SOURCE_DIR}/src/dp.cpp"
1313
"${CMAKE_CURRENT_SOURCE_DIR}/src/eep.cpp"
1414
"${CMAKE_CURRENT_SOURCE_DIR}/src/euc-jp.cpp"
15+
"${CMAKE_CURRENT_SOURCE_DIR}/src/extensions.cpp"
1516
"${CMAKE_CURRENT_SOURCE_DIR}/src/files.cpp"
1617
"${CMAKE_CURRENT_SOURCE_DIR}/src/flash.cpp"
1718
"${CMAKE_CURRENT_SOURCE_DIR}/src/heap.cpp"

librecomp/src/extensions.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include "recomp.h"
2+
#include "ultramodern/extensions.h"
3+
#include "librecomp/helpers.hpp"
4+
5+
extern "C" void osExQueueDisplaylistEvent_recomp(uint8_t* rdram, recomp_context* ctx) {
6+
PTR(OSMesgQueue) mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx);
7+
OSMesg mesg = _arg<1, OSMesg>(rdram, ctx);
8+
PTR(void) displaylist = _arg<2, PTR(void)>(rdram, ctx);
9+
u32 event_type = _arg<3, u32>(rdram, ctx);
10+
osExQueueDisplaylistEvent(mq, mesg, displaylist, event_type);
11+
}

ultramodern/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_library(ultramodern STATIC
99
"${CMAKE_CURRENT_SOURCE_DIR}/src/audio.cpp"
1010
"${CMAKE_CURRENT_SOURCE_DIR}/src/error_handling.cpp"
1111
"${CMAKE_CURRENT_SOURCE_DIR}/src/events.cpp"
12+
"${CMAKE_CURRENT_SOURCE_DIR}/src/extensions.cpp"
1213
"${CMAKE_CURRENT_SOURCE_DIR}/src/input.cpp"
1314
"${CMAKE_CURRENT_SOURCE_DIR}/src/mesgqueue.cpp"
1415
"${CMAKE_CURRENT_SOURCE_DIR}/src/misc_ultra.cpp"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#ifndef __ULTRAMODERN_EXTENSIONS_H__
2+
#define __ULTRAMODERN_EXTENSIONS_H__
3+
4+
#if defined(mips) // Patch compilation
5+
#include <ultra64.h>
6+
#else
7+
#include "ultramodern/ultra64.h"
8+
#endif
9+
10+
#ifndef PTR
11+
#define PTR(x) x*
12+
#endif
13+
14+
#ifdef __cplusplus
15+
extern "C" {
16+
#endif
17+
18+
typedef enum {
19+
// Triggers when a displaylist has been submitted to the renderer.
20+
OS_EX_DISPLAYLIST_EVENT_SUBMITTED,
21+
// Triggers when a displaylist has been fully parsed by the renderer. This includes any referenced data, such
22+
// as vertices, matrices, and textures.
23+
OS_EX_DISPLAYLIST_EVENT_PARSED,
24+
// Triggers when rendering of a displaylist has been completed by the renderer. This only includes the
25+
// rendering pass that produces an image in RAM, not the high res output images that get presented to the user.
26+
OS_EX_DISPLAYLIST_EVENT_COMPLETED
27+
} DisplaylistEventType;
28+
29+
// Queues a one-time message to be sent the next time the given event type occurs for the given displaylist.
30+
// This allows easier detection of displaylist events without needing to patch a game's scheduler.
31+
// The event will be cleared after it occurs.
32+
// event_type must be a member of the DisplaylistEventType enum.
33+
void osExQueueDisplaylistEvent(PTR(OSMesgQueue) mq, OSMesg mesg, PTR(void) displaylist, u32 event_type);
34+
35+
#ifdef __cplusplus
36+
}
37+
38+
namespace ultramodern {
39+
namespace extensions {
40+
void on_displaylist_submitted(PTR(u64) displaylist);
41+
void on_displaylist_parsed(PTR(u64) displaylist);
42+
void on_displaylist_completed(PTR(u64) displaylist);
43+
}
44+
}
45+
46+
#endif
47+
48+
#endif

ultramodern/src/events.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "ultramodern/ultra64.h"
1515
#include "ultramodern/ultramodern.hpp"
16+
#include "ultramodern/extensions.h"
1617

1718
#include "ultramodern/rsp.hpp"
1819
#include "ultramodern/renderer_context.hpp"
@@ -364,10 +365,17 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
364365
sp_complete();
365366
ultramodern::measure_input_latency();
366367

368+
PTR(u64) displaylist = task_action->task.t.data_ptr;
369+
ultramodern::extensions::on_displaylist_submitted(displaylist);
370+
367371
[[maybe_unused]] auto renderer_start = std::chrono::high_resolution_clock::now();
368372
renderer_context->send_dl(&task_action->task);
369373
[[maybe_unused]] auto renderer_end = std::chrono::high_resolution_clock::now();
374+
370375
dp_complete();
376+
// TODO hook the parsed event up to the actual parsing point when a callback is added to RT64.
377+
ultramodern::extensions::on_displaylist_parsed(displaylist);
378+
ultramodern::extensions::on_displaylist_completed(displaylist);
371379
// printf("Renderer ProcessDList time: %d us\n", static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(renderer_end - renderer_start).count()));
372380
}
373381
else if (const auto* screen_update_action = std::get_if<ScreenUpdateAction>(&action)) {

ultramodern/src/extensions.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <mutex>
2+
#include <vector>
3+
4+
#include "ultramodern/extensions.h"
5+
#include "ultramodern/ultramodern.hpp"
6+
7+
struct DLEvent {
8+
PTR(OSMesgQueue) mq;
9+
OSMesg mesg;
10+
PTR(void) displaylist;
11+
u32 event_type;
12+
};
13+
14+
struct {
15+
struct {
16+
std::mutex dl_event_mutex;
17+
std::vector<DLEvent> pending_events;
18+
} dl_events;
19+
} extension_state;
20+
21+
extern "C" void osExQueueDisplaylistEvent(PTR(OSMesgQueue) mq, OSMesg mesg, PTR(void) displaylist, u32 event_type) {
22+
std::lock_guard lock{ extension_state.dl_events.dl_event_mutex };
23+
24+
assert(
25+
event_type == OS_EX_DISPLAYLIST_EVENT_SUBMITTED ||
26+
event_type == OS_EX_DISPLAYLIST_EVENT_PARSED ||
27+
event_type == OS_EX_DISPLAYLIST_EVENT_COMPLETED);
28+
29+
extension_state.dl_events.pending_events.emplace_back(
30+
DLEvent{ mq, mesg, displaylist, event_type }
31+
);
32+
}
33+
34+
void dispatch_displaylist_events(PTR(void) displaylist, u32 event_type) {
35+
std::lock_guard lock{ extension_state.dl_events.dl_event_mutex };
36+
37+
// Check every pending DL event to see if they match this displaylist and event type.
38+
for (auto iter = extension_state.dl_events.pending_events.begin(); iter != extension_state.dl_events.pending_events.end(); ) {
39+
if (iter->displaylist == displaylist && iter->event_type == event_type) {
40+
// Send the provided message to the corresponding message queue for this event, then remove this event from the queue.
41+
ultramodern::enqueue_external_message(iter->mq, iter->mesg, false, true);
42+
iter = extension_state.dl_events.pending_events.erase(iter);
43+
}
44+
else {
45+
++iter;
46+
}
47+
}
48+
}
49+
50+
void ultramodern::extensions::on_displaylist_submitted(PTR(void) displaylist) {
51+
dispatch_displaylist_events(displaylist, OS_EX_DISPLAYLIST_EVENT_SUBMITTED);
52+
}
53+
54+
void ultramodern::extensions::on_displaylist_parsed(PTR(void) displaylist) {
55+
dispatch_displaylist_events(displaylist, OS_EX_DISPLAYLIST_EVENT_PARSED);
56+
}
57+
58+
void ultramodern::extensions::on_displaylist_completed(PTR(void) displaylist) {
59+
dispatch_displaylist_events(displaylist, OS_EX_DISPLAYLIST_EVENT_COMPLETED);
60+
}

0 commit comments

Comments
 (0)