Start setting up user callbacks system

This commit is contained in:
Angie 2024-05-20 23:39:11 -04:00 committed by angie
parent 6dce8c2fc2
commit 0df06daf44
6 changed files with 76 additions and 36 deletions

View file

@ -43,11 +43,6 @@ target_include_directories(ultramodern PRIVATE
"${PROJECT_SOURCE_DIR}/../rt64/src/contrib/nativefiledialog-extended/src/include" "${PROJECT_SOURCE_DIR}/../rt64/src/contrib/nativefiledialog-extended/src/include"
) )
# TODO: remove when librecomp is untangled from ultramodern
target_include_directories(ultramodern PRIVATE
"${CMAKE_SOURCE_DIR}/librecomp/include"
)
if (WIN32) if (WIN32)
include(FetchContent) include(FetchContent)
# Fetch SDL2 on windows # Fetch SDL2 on windows

View file

@ -1,12 +0,0 @@
#ifndef __ULTRAMODERN_RECOMP_UI__
#define __ULTRAMODERN_RECOMP_UI__
namespace recomp {
// Currently those functions are expected to be provided by the consumer of this library.
// TODO: Change these functions to a callback registering system
void destroy_ui();
void update_supported_options();
}
#endif

View file

@ -0,0 +1,29 @@
#ifndef __USER_CALLBACKS_HPP__
#define __USER_CALLBACKS_HPP__
namespace ultramodern {
struct UserCallbacks {
// TODO: Do we want those functions to take a generic `void *arg` for user data?
// TODO: Consider renaming some functions to something more general,
// like `update_rumble` -> `update_controller`
void (*update_rumble)();
void (*update_supported_options)();
// TODO: Since we have a destroy_ui we could provide an init_ui?
// void (*init_ui)();
void (*destroy_ui)();
/**
* Show an OS dialog with the given `msg`.
* `msg` is non-NULL.
*/
void (*message_box)(const char* msg);
};
void register_user_callbacks(UserCallbacks& callbacks);
const UserCallbacks& get_user_callbacks();
};
#endif

View file

@ -15,11 +15,7 @@
#include "ultramodern.hpp" #include "ultramodern.hpp"
#include "config.hpp" #include "config.hpp"
#include "rt64_layer.h" #include "rt64_layer.h"
#include "recomp_ui.h" #include "user_callbacks.hpp"
#include "recomp.h"
#include "recomp_game.h"
#include "recomp_input.h"
#include "rsp.h"
struct SpTaskAction { struct SpTaskAction {
OSTask task; OSTask task;
@ -116,8 +112,9 @@ void vi_thread_func() {
// the game to generate new audio and gfx lists. // the game to generate new audio and gfx lists.
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 = events_context.vi.retrace_count;
auto& user_callbacks = ultramodern::get_user_callbacks();
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)
@ -176,9 +173,10 @@ void vi_thread_func() {
} }
} }
} }
// TODO move recomp code out of ultramodern. if (user_callbacks.update_rumble != nullptr) {
recomp::update_rumble(); user_callbacks.update_rumble();
}
} }
} }
@ -198,9 +196,11 @@ uint8_t dmem[0x1000];
uint16_t rspReciprocals[512]; uint16_t rspReciprocals[512];
uint16_t rspInverseSquareRoots[512]; uint16_t rspInverseSquareRoots[512];
#if 0
using RspUcodeFunc = RspExitReason(uint8_t* rdram); using RspUcodeFunc = RspExitReason(uint8_t* rdram);
extern RspUcodeFunc njpgdspMain; extern RspUcodeFunc njpgdspMain;
extern RspUcodeFunc aspMain; extern RspUcodeFunc aspMain;
#endif
// From Ares emulator. For license details, see rsp_vu.h // From Ares emulator. For license details, see rsp_vu.h
void rsp_constants_init() { void rsp_constants_init() {
@ -219,7 +219,7 @@ void rsp_constants_init() {
rspInverseSquareRoots[index] = u16(b >> 1); rspInverseSquareRoots[index] = u16(b >> 1);
} }
} }
#if 0
// Runs a recompiled RSP microcode // Runs a recompiled RSP microcode
void run_rsp_microcode(uint8_t* rdram, const OSTask* task, RspUcodeFunc* ucode_func) { void run_rsp_microcode(uint8_t* rdram, const OSTask* task, RspUcodeFunc* ucode_func) {
// Load the OSTask into DMEM // Load the OSTask into DMEM
@ -231,7 +231,7 @@ void run_rsp_microcode(uint8_t* rdram, const OSTask* task, RspUcodeFunc* ucode_f
// Ensure that the ucode exited correctly // Ensure that the ucode exited correctly
assert(exit_reason == RspExitReason::Broke); assert(exit_reason == RspExitReason::Broke);
} }
#endif
void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready) { void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready) {
ultramodern::set_native_thread_name("SP Task Thread"); ultramodern::set_native_thread_name("SP Task Thread");
@ -251,10 +251,14 @@ void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_r
// Run the correct function based on the task type // Run the correct function based on the task type
if (task->t.type == M_AUDTASK) { if (task->t.type == M_AUDTASK) {
#if 0
run_rsp_microcode(rdram, task, aspMain); run_rsp_microcode(rdram, task, aspMain);
#endif
} }
else if (task->t.type == M_NJPEGTASK) { else if (task->t.type == M_NJPEGTASK) {
#if 0
run_rsp_microcode(rdram, task, njpgdspMain); run_rsp_microcode(rdram, task, njpgdspMain);
#endif
} }
else { else {
fprintf(stderr, "Unknown task type: %" PRIu32 "\n", task->t.type); fprintf(stderr, "Unknown task type: %" PRIu32 "\n", task->t.type);
@ -315,6 +319,8 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
ultramodern::RT64Context rt64{rdram, window_handle, cur_config.load().developer_mode}; ultramodern::RT64Context rt64{rdram, window_handle, cur_config.load().developer_mode};
auto& user_callbacks = ultramodern::get_user_callbacks();
if (!rt64.valid()) { if (!rt64.valid()) {
// TODO move recomp code out of ultramodern. // TODO move recomp code out of ultramodern.
rt64_setup_result.store(rt64.get_setup_result()); rt64_setup_result.store(rt64.get_setup_result());
@ -323,8 +329,9 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
return; return;
} }
// TODO move recomp code out of ultramodern. if (user_callbacks.update_supported_options != nullptr) {
recomp::update_supported_options(); user_callbacks.update_supported_options();
}
rsp_constants_init(); rsp_constants_init();
@ -372,8 +379,10 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
} }
} }
} }
// TODO move recomp code out of ultramodern.
recomp::destroy_ui(); if (user_callbacks.destroy_ui != nullptr) {
user_callbacks.destroy_ui();
}
rt64.shutdown(); rt64.shutdown();
} }
@ -571,7 +580,7 @@ void ultramodern::init_events(RDRAM_ARG ultramodern::WindowHandle window_handle)
events_context.rdram = rdram; events_context.rdram = rdram;
events_context.sp.gfx_thread = std::thread{ gfx_thread_func, rdram, &gfx_thread_ready, window_handle }; events_context.sp.gfx_thread = std::thread{ gfx_thread_func, rdram, &gfx_thread_ready, window_handle };
events_context.sp.task_thread = std::thread{ task_thread_func, rdram, &task_thread_ready }; events_context.sp.task_thread = std::thread{ task_thread_func, rdram, &task_thread_ready };
// Wait for the two sp threads to be ready before continuing to prevent the game from // Wait for the two sp threads to be ready before continuing to prevent the game from
// running before we're able to handle RSP tasks. // running before we're able to handle RSP tasks.
gfx_thread_ready.wait(); gfx_thread_ready.wait();
@ -580,9 +589,18 @@ void ultramodern::init_events(RDRAM_ARG ultramodern::WindowHandle window_handle)
ultramodern::RT64SetupResult setup_result = rt64_setup_result.load(); ultramodern::RT64SetupResult setup_result = rt64_setup_result.load();
if (rt64_setup_result != ultramodern::RT64SetupResult::Success) { if (rt64_setup_result != ultramodern::RT64SetupResult::Success) {
auto show_rt64_error = [](const std::string& msg) { auto show_rt64_error = [](const std::string& msg) {
// TODO move recomp code out of ultramodern (message boxes). auto& user_callbacks = ultramodern::get_user_callbacks();
recomp::message_box(("An error has been encountered on startup: " + msg).c_str()); std::string error_msg = "An error has been encountered on startup: " + msg;
// We print the message to stderr since the user may not have provided a message_box callback
// TODO: is fprintf ok? or do we prefer using something more C++'ish?
fprintf(stderr, "%s\n", error_msg.c_str());
if (user_callbacks.message_box != nullptr) {
user_callbacks.message_box(error_msg.c_str());
}
}; };
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 (rt64_setup_result) { switch (rt64_setup_result) {
case ultramodern::RT64SetupResult::DynamicLibrariesNotFound: case ultramodern::RT64SetupResult::DynamicLibrariesNotFound:

View file

@ -4,7 +4,6 @@
#include "ultra64.h" #include "ultra64.h"
#include "ultramodern.hpp" #include "ultramodern.hpp"
#include "recomp.h"
struct QueuedMessage { struct QueuedMessage {
PTR(OSMesgQueue) mq; PTR(OSMesgQueue) mq;

View file

@ -0,0 +1,11 @@
#include "ultramodern/user_callbacks.hpp"
static ultramodern::UserCallbacks s_user_callbacks {};
void ultramodern::register_user_callbacks(UserCallbacks& callbacks) {
s_user_callbacks = callbacks;
}
const ultramodern::UserCallbacks& ultramodern::get_user_callbacks() {
return s_user_callbacks;
}