From 346e298eb12d0e38de368042713384e5e2edde8e Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 4 Nov 2023 12:45:22 -0400 Subject: [PATCH] Moved window event handling out of gfx thread so resizing doesn't freeze gameplay, added full document hot reloading for UI --- include/rt64_layer.h | 2 +- portultra/audio.cpp | 6 ++-- portultra/events.cpp | 19 ----------- portultra/multilibultra.hpp | 11 ++++--- portultra/timer.cpp | 10 +++--- src/cont.cpp | 6 ++-- src/main/main.cpp | 16 ++------- src/recomp.cpp | 52 ++++++++++++++++------------- src/rt64_layer.cpp | 2 +- src/ui.cpp | 66 ++++++++++++++++++++++++++++++------- 10 files changed, 104 insertions(+), 86 deletions(-) diff --git a/include/rt64_layer.h b/include/rt64_layer.h index ae17119..51c0d00 100644 --- a/include/rt64_layer.h +++ b/include/rt64_layer.h @@ -64,7 +64,7 @@ typedef struct { //DLLEXPORT void (CALL *PumpEvents)(void) = nullptr; #if defined(_WIN32) -extern "C" int InitiateGFXWindows(GFX_INFO Gfx_Info, HWND hwnd); +extern "C" int InitiateGFXWindows(GFX_INFO Gfx_Info, HWND hwnd, DWORD threadId); #elif defined(__ANDROID__) static_assert(false && "Unimplemented"); #elif defined(__linux__) diff --git a/portultra/audio.cpp b/portultra/audio.cpp index dd51c04..ba2949a 100644 --- a/portultra/audio.cpp +++ b/portultra/audio.cpp @@ -6,10 +6,8 @@ static uint32_t sample_rate = 48000; static Multilibultra::audio_callbacks_t audio_callbacks; -void Multilibultra::set_audio_callbacks(const audio_callbacks_t* callbacks) { - if (callbacks != nullptr) { - audio_callbacks = *callbacks; - } +void set_audio_callbacks(const Multilibultra::audio_callbacks_t& callbacks) { + audio_callbacks = callbacks; } void Multilibultra::init_audio() { diff --git a/portultra/events.cpp b/portultra/events.cpp index 0c6a2a3..c1fb9b0 100644 --- a/portultra/events.cpp +++ b/portultra/events.cpp @@ -236,14 +236,6 @@ void task_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_rea } } -static Multilibultra::gfx_callbacks_t gfx_callbacks; - -void Multilibultra::set_gfx_callbacks(const gfx_callbacks_t* callbacks) { - if (callbacks != nullptr) { - gfx_callbacks = *callbacks; - } -} - void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_ready, Multilibultra::WindowHandle window_handle) { using namespace std::chrono_literals; Multilibultra::gfx_callbacks_t::gfx_data_t gfx_data{}; @@ -251,14 +243,6 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read Multilibultra::set_native_thread_name("Gfx Thread"); Multilibultra::set_native_thread_priority(Multilibultra::ThreadPriority::Normal); - if (gfx_callbacks.create_gfx != nullptr) { - gfx_data = gfx_callbacks.create_gfx(); - } - - if (gfx_callbacks.create_window != nullptr) { - window_handle = gfx_callbacks.create_window(gfx_data); - } - RT64Init(rom, rdram, window_handle); rsp_constants_init(); @@ -285,9 +269,6 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read RT64UpdateScreen(swap_action->origin); } } - if (gfx_callbacks.update_gfx != nullptr) { - gfx_callbacks.update_gfx(nullptr); - } } } diff --git a/portultra/multilibultra.hpp b/portultra/multilibultra.hpp index c2d4091..dc8b51e 100644 --- a/portultra/multilibultra.hpp +++ b/portultra/multilibultra.hpp @@ -30,13 +30,18 @@ namespace Multilibultra { #if defined(_WIN32) // Native HWND handle to the target window. - using WindowHandle = HWND; + struct WindowHandle { + HWND window; + DWORD thread_id = (DWORD)-1; + auto operator<=>(const WindowHandle&) const = default; + }; #elif defined(__ANDROID__) using WindowHandle = ANativeWindow*; #elif defined(__linux__) struct WindowHandle { Display* display; Window window; + auto operator<=>(const WindowHandle&) const = default; }; #endif @@ -101,14 +106,12 @@ struct audio_callbacks_t { get_samples_remaining_t* get_frames_remaining; set_frequency_t* set_frequency; }; -void set_audio_callbacks(const audio_callbacks_t* callbacks); // Input struct input_callbacks_t { using get_input_t = void(uint16_t*, float*, float*); get_input_t* get_input; }; -void set_input_callbacks(const input_callbacks_t* callback); struct gfx_callbacks_t { using gfx_data_t = void*; @@ -119,7 +122,7 @@ struct gfx_callbacks_t { create_window_t* create_window; update_gfx_t* update_gfx; }; -void set_gfx_callbacks(const gfx_callbacks_t* callbacks); +void start(WindowHandle window_handle, const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks); class preemption_guard { public: diff --git a/portultra/timer.cpp b/portultra/timer.cpp index dce0c99..15d1d63 100644 --- a/portultra/timer.cpp +++ b/portultra/timer.cpp @@ -7,7 +7,7 @@ #include "multilibultra.hpp" // Start time for the program -static std::chrono::system_clock::time_point start = std::chrono::system_clock::now(); +static std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now(); // Game speed multiplier (1 means no speedup) constexpr uint32_t speed_multiplier = 1; // N64 CPU counter ticks per millisecond @@ -52,11 +52,11 @@ std::chrono::microseconds ticks_to_duration(uint64_t ticks) { } std::chrono::system_clock::time_point ticks_to_timepoint(uint64_t ticks) { - return start + ticks_to_duration(ticks); + return start_time + ticks_to_duration(ticks); } uint64_t time_now() { - return duration_to_ticks(std::chrono::system_clock::now() - start); + return duration_to_ticks(std::chrono::system_clock::now() - start_time); } void timer_thread(RDRAM_ARG1) { @@ -142,11 +142,11 @@ uint32_t Multilibultra::get_speed_multiplier() { } std::chrono::system_clock::time_point Multilibultra::get_start() { - return start; + return start_time; } std::chrono::system_clock::duration Multilibultra::time_since_start() { - return std::chrono::system_clock::now() - start; + return std::chrono::system_clock::now() - start_time; } extern "C" u32 osGetCount() { diff --git a/src/cont.cpp b/src/cont.cpp index 86ca632..75cdfcd 100644 --- a/src/cont.cpp +++ b/src/cont.cpp @@ -3,10 +3,8 @@ static Multilibultra::input_callbacks_t input_callbacks; -void Multilibultra::set_input_callbacks(const input_callbacks_t* callbacks) { - if (callbacks != nullptr) { - input_callbacks = *callbacks; - } +void set_input_callbacks(const Multilibultra::input_callbacks_t& callbacks) { + input_callbacks = callbacks; } static int max_controllers = 0; diff --git a/src/main/main.cpp b/src/main/main.cpp index 04c26b6..fd556a5 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -164,7 +164,7 @@ Multilibultra::WindowHandle create_window(Multilibultra::gfx_callbacks_t::gfx_da SDL_GetWindowWMInfo(window, &wmInfo); #if defined(_WIN32) - return wmInfo.info.win.window; + return Multilibultra::WindowHandle{ wmInfo.info.win.window, GetCurrentThreadId() }; #elif defined(__ANDROID__) static_assert(false && "Unimplemented"); #elif defined(__linux__) @@ -353,19 +353,7 @@ int main(int argc, char** argv) { .get_input = get_input, }; - //create_gfx(); - //void* window_handle = create_window(nullptr); - - Multilibultra::set_gfx_callbacks(&gfx_callbacks); - start(Multilibultra::WindowHandle{}, &audio_callbacks, &input_callbacks); - - // Do nothing forever - while (1) { - using namespace std::chrono_literals; - std::this_thread::sleep_for(10ms); - //update_gfx(nullptr); - //std::this_thread::sleep_for(1ms); - } + Multilibultra::start({}, audio_callbacks, input_callbacks, gfx_callbacks); return EXIT_SUCCESS; } diff --git a/src/recomp.cpp b/src/recomp.cpp index bddebb2..958b97c 100644 --- a/src/recomp.cpp +++ b/src/recomp.cpp @@ -187,31 +187,31 @@ EXPORT extern "C" void init() { // return DefWindowProc(hwnd, uMsg, wParam, lParam); // } -/*EXPORT extern "C"*/ void start(Multilibultra::WindowHandle window_handle, const Multilibultra::audio_callbacks_t* audio_callbacks, const Multilibultra::input_callbacks_t* input_callbacks) { - Multilibultra::set_audio_callbacks(audio_callbacks); - Multilibultra::set_input_callbacks(input_callbacks); +static Multilibultra::gfx_callbacks_t gfx_callbacks; - //// Register window class. - //WNDCLASS wc; - //memset(&wc, 0, sizeof(WNDCLASS)); - //wc.lpfnWndProc = WindowProc; - //wc.hInstance = GetModuleHandle(0); - //wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); - //wc.lpszClassName = "RT64Sample"; - //RegisterClass(&wc); +void set_audio_callbacks(const Multilibultra::audio_callbacks_t& callbacks); +void set_input_callbacks(const Multilibultra::input_callbacks_t& callback); - //// Create window. - //const int Width = 1280; - //const int Height = 720; - //RECT rect; - //UINT dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE; - //rect.left = (GetSystemMetrics(SM_CXSCREEN) - Width) / 2; - //rect.top = (GetSystemMetrics(SM_CYSCREEN) - Height) / 2; - //rect.right = rect.left + Width; - //rect.bottom = rect.top + Height; - //AdjustWindowRectEx(&rect, dwStyle, 0, 0); +void Multilibultra::start(WindowHandle window_handle, const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks_) { + set_audio_callbacks(audio_callbacks); + set_input_callbacks(input_callbacks); - //HWND hwnd = CreateWindow(wc.lpszClassName, "Recomp", dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0, 0, wc.hInstance, NULL); + gfx_callbacks_t gfx_callbacks = gfx_callbacks_; + + gfx_callbacks_t::gfx_data_t gfx_data{}; + + if (gfx_callbacks.create_gfx) { + gfx_data = gfx_callbacks.create_gfx(); + } + + if (window_handle == WindowHandle{}) { + if (gfx_callbacks.create_window) { + window_handle = gfx_callbacks.create_window(gfx_data); + } + else { + assert(false && "No create_window callback provided"); + } + } std::thread game_thread{[](Multilibultra::WindowHandle window_handle) { debug_printf("[Recomp] Starting\n"); @@ -225,5 +225,11 @@ EXPORT extern "C" void init() { debug_printf("[Recomp] Quitting\n"); }, window_handle}; - game_thread.detach(); + while (true) { + using namespace std::chrono_literals; + std::this_thread::sleep_for(1ms); + if (gfx_callbacks.update_gfx != nullptr) { + gfx_callbacks.update_gfx(nullptr); + } + } } diff --git a/src/rt64_layer.cpp b/src/rt64_layer.cpp index 672ee41..2d9be82 100644 --- a/src/rt64_layer.cpp +++ b/src/rt64_layer.cpp @@ -100,7 +100,7 @@ void RT64Init(uint8_t* rom, uint8_t* rdram, Multilibultra::WindowHandle window_h gfx_info.RDRAM_SIZE = &RDRAM_SIZE; #if defined(_WIN32) - InitiateGFXWindows(gfx_info, window_handle); + InitiateGFXWindows(gfx_info, window_handle.window, window_handle.thread_id); #elif defined(__ANDROID__) static_assert(false && "Unimplemented"); #elif defined(__linux__) diff --git a/src/ui.cpp b/src/ui.cpp index 2f5d045..5ed983d 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -38,6 +38,7 @@ struct UIRenderContext { RT64::RenderInterface* interface; RT64::RenderDevice* device; + Rml::ElementDocument* document; }; // TODO deduplicate from rt64_common.h @@ -99,6 +100,8 @@ T from_bytes_le(const char* input) { return *reinterpret_cast(input); } +void load_document(); + class RmlRenderInterface_RT64 : public Rml::RenderInterface { static constexpr uint32_t per_frame_descriptor_set = 0; static constexpr uint32_t per_draw_descriptor_set = 1; @@ -516,7 +519,7 @@ public: mvp_ = projection_mtx_ * transform_; } - void start(RT64::RenderCommandList* list, uint32_t image_width, uint32_t image_height) { + void start(RT64::RenderCommandList* list, uint32_t image_width, uint32_t image_height, bool reload_style) { list_ = list; list_->setPipeline(pipeline_.get()); list_->setGraphicsPipelineLayout(layout_.get()); @@ -528,6 +531,10 @@ public: // Clear out any stale buffers from the last command list. stale_buffers_.clear(); + if (reload_style) { + load_document(); + } + // Reset and map the upload buffer. upload_buffer_bytes_used_ = 0; upload_buffer_mapped_data_ = reinterpret_cast(upload_buffer_->map()); @@ -556,6 +563,18 @@ struct { // TODO make this not be global extern SDL_Window* window; +void load_document() { + if (UIContext.render.document) { + UIContext.render.document->Close(); + // Documents are owned by RmlUi, so we don't have anything to free here. + UIContext.render.document = nullptr; + } + UIContext.render.document = UIContext.rml.context->LoadDocument("assets/demo.rml"); + if (UIContext.render.document) { + UIContext.render.document->Show(); + } +} + void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) { printf("RT64 hook init\n"); @@ -598,10 +617,7 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) { } } - if (Rml::ElementDocument* document = UIContext.rml.context->LoadDocument("assets/demo.rml")) { - document->Show(); - } - + load_document(); } void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_chain_texture) { @@ -613,13 +629,41 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_ // TODO process SDL events - int width, height; - SDL_GetWindowSizeInPixels(window, &width, &height); + int num_keys; + const Uint8* key_state = SDL_GetKeyboardState(&num_keys); - UIContext.rml.render_interface->start(command_list, width, height); - UIContext.rml.context->Update(); - UIContext.rml.context->Render(); - UIContext.rml.render_interface->end(command_list); + static bool was_reload_held = false; + bool is_reload_held = key_state[SDL_SCANCODE_F11] != 0; + bool reload_sheets = is_reload_held && !was_reload_held; + was_reload_held = is_reload_held; + + static bool menu_open = false; + static bool was_toggle_menu_held = false; + bool is_toggle_menu_held = key_state[SDL_SCANCODE_M] != 0; + if (is_toggle_menu_held && !was_toggle_menu_held) { + menu_open = !menu_open; + } + was_toggle_menu_held = is_toggle_menu_held; + + if (menu_open) { + int width, height; + SDL_GetWindowSizeInPixels(window, &width, &height); + + UIContext.rml.render_interface->start(command_list, width, height, reload_sheets); + + static int prev_width = 0; + static int prev_height = 0; + + if (prev_width != width || prev_height != height) { + UIContext.rml.context->SetDimensions({ width, height }); + } + prev_width = width; + prev_height = height; + + UIContext.rml.context->Update(); + UIContext.rml.context->Render(); + UIContext.rml.render_interface->end(command_list); + } } void deinit_hook() {