From 88c618c1f8d8089f94e78537e49e0d77798fc0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo?= Date: Sat, 10 Aug 2024 19:41:29 -0300 Subject: [PATCH] Add inspector with hooks for SDL2. (#75) * Add inspector with hooks for SDL2. * Fix build errors. * Add the remaining SDL2 implementation that was required. --- CMakeLists.txt | 1 + src/gui/rt64_inspector.cpp | 70 +++++++++++----- src/gui/rt64_inspector.h | 7 +- src/hle/rt64_application.cpp | 115 ++++++++++++++++++-------- src/hle/rt64_application.h | 11 ++- src/hle/rt64_application_window.cpp | 124 ++++++++++++---------------- src/hle/rt64_application_window.h | 52 ++++++------ 7 files changed, 227 insertions(+), 153 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8aac0c2..00a9276 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -333,6 +333,7 @@ set (SOURCES "${PROJECT_SOURCE_DIR}/src/contrib/imgui/imgui_draw.cpp" "${PROJECT_SOURCE_DIR}/src/contrib/imgui/imgui_tables.cpp" "${PROJECT_SOURCE_DIR}/src/contrib/imgui/imgui_widgets.cpp" + "${PROJECT_SOURCE_DIR}/src/contrib/imgui/backends/imgui_impl_sdl2.cpp" "${PROJECT_SOURCE_DIR}/src/contrib/imgui/backends/imgui_impl_vulkan.cpp" "${PROJECT_SOURCE_DIR}/src/contrib/im3d/im3d.cpp" "${PROJECT_SOURCE_DIR}/src/contrib/implot/implot.cpp" diff --git a/src/gui/rt64_inspector.cpp b/src/gui/rt64_inspector.cpp index bbd29d6..7318103 100644 --- a/src/gui/rt64_inspector.cpp +++ b/src/gui/rt64_inspector.cpp @@ -12,6 +12,12 @@ #include "imgui/imgui.h" #include "implot/implot.h" +// Volk must be included before the ImGui Vulkan backend. +#include "vulkan/rt64_vulkan.h" + +#include "imgui/backends/imgui_impl_sdl2.h" +#include "imgui/backends/imgui_impl_vulkan.h" + #if defined(_WIN32) # include "imgui/backends/imgui_impl_dx12.h" # include "imgui/backends/imgui_impl_win32.h" @@ -22,10 +28,6 @@ # include "d3d12/rt64_d3d12.h" #endif -// Volk must be included before the ImGui Vulkan backend. -#include "vulkan/rt64_vulkan.h" -#include "imgui/backends/imgui_impl_vulkan.h" - static std::string IniFilenameUTF8; #ifdef _WIN32 @@ -59,13 +61,14 @@ namespace RT64 { // Inspector - Inspector::Inspector(RenderDevice *device, const RenderSwapChain *swapChain, UserConfiguration::GraphicsAPI graphicsAPI) { + Inspector::Inspector(RenderDevice *device, const RenderSwapChain *swapChain, UserConfiguration::GraphicsAPI graphicsAPI, SDL_Window *sdlWindow) { assert(device != nullptr); assert(swapChain != nullptr); this->device = device; this->swapChain = swapChain; this->graphicsAPI = graphicsAPI; + this->sdlWindow = sdlWindow; IMGUI_CHECKVERSION(); @@ -73,10 +76,15 @@ namespace RT64 { ImPlot::CreateContext(); ImGui::StyleColorsDark(); -# ifdef _WIN32 - RenderWindow renderWindow = swapChain->getWindow(); - ImGui_ImplWin32_Init(renderWindow); -# endif + if (sdlWindow != nullptr) { + ImGui_ImplSDL2_InitForOther(sdlWindow); + } + else { +# ifdef _WIN32 + RenderWindow renderWindow = swapChain->getWindow(); + ImGui_ImplWin32_Init(renderWindow); +# endif + } switch (graphicsAPI) { case UserConfiguration::GraphicsAPI::D3D12: { @@ -153,11 +161,14 @@ namespace RT64 { break; } -# ifdef _WIN32 - ImGui_ImplWin32_Shutdown(); -# else - assert(false && "Unimplemented."); -# endif + if (sdlWindow != nullptr) { + ImGui_ImplSDL2_Shutdown(); + } + else { +# ifdef _WIN32 + ImGui_ImplWin32_Shutdown(); +# endif + } ImPlot::DestroyContext(); ImGui::DestroyContext(); @@ -173,12 +184,15 @@ namespace RT64 { assert(worker != nullptr); frameMutex.lock(); - -# ifdef _WIN32 - ImGui_ImplWin32_NewFrame(); -# else - assert(false && "Unimplemented."); -# endif + + if (sdlWindow != nullptr) { + ImGui_ImplSDL2_NewFrame(); + } + else { +# ifdef _WIN32 + ImGui_ImplWin32_NewFrame(); +# endif + } switch (graphicsAPI) { case UserConfiguration::GraphicsAPI::D3D12: { @@ -240,4 +254,20 @@ namespace RT64 { return ImGui_ImplWin32_WndProcHandler(swapChain->getWindow(), msg, wParam, lParam); } #endif + + bool Inspector::handleSdlEvent(SDL_Event *event) { + assert((sdlWindow != nullptr) && "SDL Events shouldn't be handled if SDL was not used."); + + bool processed = ImGui_ImplSDL2_ProcessEvent(event); + if (processed) { + if ((event->type == SDL_KEYDOWN) || (event->type == SDL_KEYUP)) { + return ImGui::GetIO().WantCaptureKeyboard; + } + else if ((event->type == SDL_MOUSEMOTION) || (event->type == SDL_MOUSEBUTTONDOWN) || (event->type == SDL_MOUSEBUTTONUP) || (event->type == SDL_MOUSEWHEEL)) { + return ImGui::GetIO().WantCaptureMouse; + } + } + + return false; + } }; \ No newline at end of file diff --git a/src/gui/rt64_inspector.h b/src/gui/rt64_inspector.h index 595d37e..52b2249 100644 --- a/src/gui/rt64_inspector.h +++ b/src/gui/rt64_inspector.h @@ -14,6 +14,7 @@ #include "common/rt64_common.h" #include "common/rt64_user_configuration.h" #include "render/rt64_render_worker.h" +#include "hle/rt64_application_window.h" #include "rhi/rt64_render_interface.h" namespace RT64 { @@ -22,12 +23,13 @@ namespace RT64 { struct Inspector { RenderDevice *device = nullptr; const RenderSwapChain *swapChain = nullptr; + UserConfiguration::GraphicsAPI graphicsAPI; + SDL_Window *sdlWindow = nullptr; std::unique_ptr descriptorSet; std::unique_ptr vulkanContext; - UserConfiguration::GraphicsAPI graphicsAPI; std::mutex frameMutex; - Inspector(RenderDevice *device, const RenderSwapChain *swapChain, UserConfiguration::GraphicsAPI graphicsAPI); + Inspector(RenderDevice *device, const RenderSwapChain *swapChain, UserConfiguration::GraphicsAPI graphicsAPI, SDL_Window *sdlWindow); ~Inspector(); void setIniPath(const std::filesystem::path &path); void newFrame(RenderWorker *worker); @@ -36,5 +38,6 @@ namespace RT64 { # ifdef _WIN32 bool handleMessage(UINT msg, WPARAM wParam, LPARAM lParam); # endif + bool handleSdlEvent(SDL_Event *event); }; }; \ No newline at end of file diff --git a/src/hle/rt64_application.cpp b/src/hle/rt64_application.cpp index 40f10a4..cc8f5f2 100644 --- a/src/hle/rt64_application.cpp +++ b/src/hle/rt64_application.cpp @@ -390,6 +390,7 @@ namespace RT64 { } void Application::updateScreen() { + appWindow->sdlCheckFilterInstallation(); screenApiProfiler.logAndRestart(); state->updateScreen(core.decodeVI(), false); } @@ -448,43 +449,20 @@ namespace RT64 { switch (message) { case WM_KEYDOWN: { switch (wParam) { - case VK_F1: { - if (userConfig.developerMode) { - const std::lock_guard lock(presentQueue->inspectorMutex); - if (presentQueue->inspector == nullptr) { - presentQueue->inspector = std::make_unique(device.get(), swapChain.get(), createdGraphicsAPI); - if (!userPaths.isEmpty()) { - presentQueue->inspector->setIniPath(userPaths.imguiPath); - } - - freeCamClearQueued = true; - appWindow->blockSdlKeyboard(); - } - else if (presentQueue->inspector != nullptr) { - presentQueue->inspector.reset(nullptr); - appWindow->unblockSdlKeyboard(); - } - } - else { - fprintf(stdout, "Inspector is not available: developer mode is not enabled in the configuration.\n"); - } - + case VK_F1: + processDeveloperShortcut(DeveloperShortcut::Inspector); return true; - } - case VK_F2: { - workloadQueue->rtEnabled = !workloadQueue->rtEnabled; + case VK_F2: + processDeveloperShortcut(DeveloperShortcut::RayTracing); return true; - } - case VK_F3: { - presentQueue->viewRDRAM = !presentQueue->viewRDRAM; + case VK_F3: + processDeveloperShortcut(DeveloperShortcut::ViewRDRAM); return true; - } - case VK_F4: { - textureCache->textureMap.replacementMapEnabled = !textureCache->textureMap.replacementMapEnabled; + case VK_F4: + processDeveloperShortcut(DeveloperShortcut::Replacements); return true; - } default: - // Ignore key. + // Unknown shortcut. break; } } @@ -492,11 +470,82 @@ namespace RT64 { return false; } +#endif + + bool Application::sdlEventFilter(SDL_Event *event) { + if (userConfig.developerMode && (presentQueue != nullptr) && (state != nullptr) && !FileDialog::isOpen) { + const std::lock_guard lock(presentQueue->inspectorMutex); + if ((presentQueue->inspector != nullptr) && presentQueue->inspector->handleSdlEvent(event)) { + return true; + } + } + + if (event->type == SDL_KEYDOWN) { + switch (event->key.keysym.scancode) { + case SDL_SCANCODE_F1: + processDeveloperShortcut(DeveloperShortcut::Inspector); + return true; + case SDL_SCANCODE_F2: + processDeveloperShortcut(DeveloperShortcut::RayTracing); + return true; + case SDL_SCANCODE_F3: + processDeveloperShortcut(DeveloperShortcut::ViewRDRAM); + return true; + case SDL_SCANCODE_F4: + processDeveloperShortcut(DeveloperShortcut::Replacements); + return true; + default: + // Don't filter the key event. + break; + } + } + + return false; + } bool Application::usesWindowMessageFilter() { return userConfig.developerMode; } -#endif + + void Application::processDeveloperShortcut(DeveloperShortcut developerShortcut) { + switch (developerShortcut) { + case DeveloperShortcut::Inspector: { + if (userConfig.developerMode) { + const std::lock_guard lock(presentQueue->inspectorMutex); + if (presentQueue->inspector == nullptr) { + presentQueue->inspector = std::make_unique(device.get(), swapChain.get(), createdGraphicsAPI, appWindow->sdlWindow); + if (!userPaths.isEmpty()) { + presentQueue->inspector->setIniPath(userPaths.imguiPath); + } + + freeCamClearQueued = true; + } + else if (presentQueue->inspector != nullptr) { + presentQueue->inspector.reset(nullptr); + } + } + else { + fprintf(stdout, "Inspector is not available: developer mode is not enabled in the configuration.\n"); + } + + break; + } + case DeveloperShortcut::RayTracing: { + workloadQueue->rtEnabled = !workloadQueue->rtEnabled; + break; + } + case DeveloperShortcut::ViewRDRAM: { + presentQueue->viewRDRAM = !presentQueue->viewRDRAM; + break; + } + case DeveloperShortcut::Replacements: { + textureCache->textureMap.replacementMapEnabled = !textureCache->textureMap.replacementMapEnabled; + break; + } + default: + break; + }; + } void Application::end() { # if SCRIPT_ENABLED diff --git a/src/hle/rt64_application.h b/src/hle/rt64_application.h index 7445f95..2ee2114 100644 --- a/src/hle/rt64_application.h +++ b/src/hle/rt64_application.h @@ -51,6 +51,13 @@ namespace RT64 { GraphicsDeviceNotFound }; + enum class DeveloperShortcut { + Inspector, + RayTracing, + ViewRDRAM, + Replacements + }; + struct Core { RenderWindow window; uint8_t *HEADER; @@ -159,8 +166,10 @@ namespace RT64 { bool checkDirectoryCreated(const std::filesystem::path &path); # ifdef _WIN32 bool windowMessageFilter(unsigned int message, WPARAM wParam, LPARAM lParam) override; - bool usesWindowMessageFilter() override; # endif + bool sdlEventFilter(SDL_Event *event) override; + bool usesWindowMessageFilter() override; + void processDeveloperShortcut(DeveloperShortcut developerShortcut); void updateUserConfig(bool discardFBs); void updateEmulatorConfig(); void updateEnhancementConfig(); diff --git a/src/hle/rt64_application_window.cpp b/src/hle/rt64_application_window.cpp index 45861f8..58788aa 100644 --- a/src/hle/rt64_application_window.cpp +++ b/src/hle/rt64_application_window.cpp @@ -23,16 +23,7 @@ namespace RT64 { ApplicationWindow *ApplicationWindow::HookedApplicationWindow = nullptr; ApplicationWindow::ApplicationWindow() { - windowHandle = {}; - sdlEventFilterUserdata = nullptr; - sdlEventFilterStored = false; - fullScreen = false; - lastMaximizedState = false; -# ifdef _WIN32 - windowHook = nullptr; - windowMenu = nullptr; -# endif - usingSdl = false; + // Empty. } ApplicationWindow::~ApplicationWindow() { @@ -51,16 +42,27 @@ namespace RT64 { assert(listener != nullptr); this->listener = listener; - windowHandle = window; -# ifdef _WIN32 + windowHandle = window; + if (listener->usesWindowMessageFilter()) { - assert(HookedApplicationWindow == nullptr); - assert(threadId != 0); - windowHook = SetWindowsHookEx(WH_GETMESSAGE, &windowHookCallback, NULL, threadId); - HookedApplicationWindow = this; + if ((sdlWindow == nullptr) && SDL_WasInit(SDL_INIT_VIDEO)) { + // We'd normally install the event filter here, but Mupen does not set its own event filter + // until much later. Instead, we delegate this to the first time a screen update is sent. + // FIXME: We attempt to get the first window created by SDL2. This can be improved later + // by actually passing the SDL_Window handle through as a parameter. + sdlWindow = SDL_GetWindowFromID(1); + } + + if (sdlWindow == nullptr) { +# ifdef _WIN32 + assert(HookedApplicationWindow == nullptr); + assert(threadId != 0); + windowHook = SetWindowsHookEx(WH_GETMESSAGE, &windowHookCallback, NULL, threadId); + HookedApplicationWindow = this; +# endif + } } -# endif } void ApplicationWindow::setup(const char *windowTitle, Listener *listener) { @@ -71,7 +73,7 @@ namespace RT64 { const int Height = 720; struct { uint32_t left, top, width, height; - } bounds {}; + } bounds{}; # if defined(_WIN32) SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); @@ -108,12 +110,13 @@ namespace RT64 { # else static_assert(false && "Unimplemented"); # endif - + // Create window. -#ifdef RT64_SDL_WINDOW - SDL_Window *sdlWindow = SDL_CreateWindow(windowTitle, bounds.left, bounds.top, bounds.width, bounds.height, SDL_WINDOW_RESIZABLE); + sdlWindow = SDL_CreateWindow(windowTitle, bounds.left, bounds.top, bounds.width, bounds.height, SDL_WINDOW_RESIZABLE); + assert((sdlWindow != nullptr) && "Failed to open window with SDL"); + + // Get native window handles from the window. SDL_SysWMinfo wmInfo; - assert(sdlWindow && "Failed to open window with SDL"); SDL_VERSION(&wmInfo.version); SDL_GetWindowWMInfo(sdlWindow, &wmInfo); # if defined(_WIN32) @@ -128,10 +131,6 @@ namespace RT64 { # else static_assert(false && "Unimplemented"); # endif - usingSdl = true; -#else - static_assert(false && "Unimplemented"); -#endif # ifdef _WIN32 setup(windowHandle, listener, GetCurrentThreadId()); @@ -305,7 +304,7 @@ namespace RT64 { } } -# ifdef _WIN32 +#ifdef _WIN32 void ApplicationWindow::windowMessage(UINT message, WPARAM wParam, LPARAM lParam) { if (listener->windowMessageFilter(message, wParam, lParam)) { return; @@ -326,47 +325,34 @@ namespace RT64 { return CallNextHookEx(NULL, nCode, wParam, lParam); } -# endif - -#ifdef RT64_SDL_WINDOW - void ApplicationWindow::blockSdlKeyboard() { - if (!usingSdl) { - return; - } - - sdlEventFilterStored = SDL_GetEventFilter(&sdlEventFilter, &sdlEventFilterUserdata); - if (sdlEventFilterStored) { - SDL_SetEventFilter(&ApplicationWindow::sdlEventKeyboardFilter, this); - } - } - - void ApplicationWindow::unblockSdlKeyboard() { - if (!usingSdl) { - return; - } - - if (sdlEventFilterStored) { - SDL_SetEventFilter(sdlEventFilter, sdlEventFilterUserdata); - sdlEventFilterStored = false; - } - } - - int ApplicationWindow::sdlEventKeyboardFilter(void *userdata, SDL_Event *event) { - ApplicationWindow *appWindow = reinterpret_cast(userdata); - - switch (event->type) { - // Ignore all keyboard events. - case SDL_KEYDOWN: - case SDL_KEYUP: - return 0; - // Pass through to the original event filter. - default: - return appWindow->sdlEventFilter(userdata, event); - } - } - - -#else - static_assert(false && "Unimplemented"); #endif + + void ApplicationWindow::sdlCheckFilterInstallation() { + if (!sdlEventFilterInstalled && (sdlWindow != nullptr)) { + if (!SDL_GetEventFilter(&sdlEventFilterStored, &sdlEventFilterUserdata)) { + sdlEventFilterStored = nullptr; + sdlEventFilterUserdata = nullptr; + } + + SDL_SetEventFilter(&ApplicationWindow::sdlEventFilter, this); + sdlEventFilterInstalled = true; + } + } + + int ApplicationWindow::sdlEventFilter(void *userdata, SDL_Event *event) { + // Run it through the listener's event filter. If it's processed by the listener, the event should be filtered. + ApplicationWindow *appWindow = reinterpret_cast(userdata); + if (appWindow->listener->sdlEventFilter(event)) { + return 0; + } + + // Pass to the event filter that was stored if it exists. Let the original filter determine the result. + if (appWindow->sdlEventFilterStored != nullptr) { + return appWindow->sdlEventFilterStored(appWindow->sdlEventFilterUserdata, event); + } + // The event should not be filtered. + else { + return 1; + } + } }; diff --git a/src/hle/rt64_application_window.h b/src/hle/rt64_application_window.h index 88e319b..dc3f19c 100644 --- a/src/hle/rt64_application_window.h +++ b/src/hle/rt64_application_window.h @@ -9,41 +9,45 @@ #include #endif -#define RT64_SDL_WINDOW - -#ifdef RT64_SDL_WINDOW -# include "SDL_events.h" -# include "SDL_system.h" -# include "SDL_syswm.h" -# include "SDL_video.h" -#endif +#include "SDL.h" +#include "SDL_events.h" +#include "SDL_system.h" +#include "SDL_syswm.h" +#include "SDL_video.h" namespace RT64 { struct ApplicationWindow { static ApplicationWindow *HookedApplicationWindow; struct Listener { + virtual bool usesWindowMessageFilter() = 0; + // Return true if the listener should accept and handle the message. // Return false otherwise for the default message handler to take over. + virtual bool sdlEventFilter(SDL_Event *event) = 0; + # ifdef _WIN32 virtual bool windowMessageFilter(unsigned int message, WPARAM wParam, LPARAM lParam) = 0; - virtual bool usesWindowMessageFilter() = 0; # endif }; - RenderWindow windowHandle; -# ifdef _WIN32 - HHOOK windowHook; - HMENU windowMenu; - RECT lastWindowRect; -# endif + RenderWindow windowHandle = {}; Listener *listener; uint32_t refreshRate = 0; - bool fullScreen; - bool lastMaximizedState; - bool usingSdl; + bool fullScreen = false; + bool lastMaximizedState = false; int32_t windowLeft = INT32_MAX; int32_t windowTop = INT32_MAX; + SDL_Window *sdlWindow = nullptr; + SDL_EventFilter sdlEventFilterStored = nullptr; + void *sdlEventFilterUserdata = nullptr; + bool sdlEventFilterInstalled = false; + +# ifdef _WIN32 + HHOOK windowHook = nullptr; + HMENU windowMenu = nullptr; + RECT lastWindowRect = {}; +# endif ApplicationWindow(); ~ApplicationWindow(); @@ -54,20 +58,12 @@ namespace RT64 { void detectRefreshRate(); uint32_t getRefreshRate() const; bool detectWindowMoved(); + void sdlCheckFilterInstallation(); + static int sdlEventFilter(void *userdata, SDL_Event *event); # ifdef _WIN32 void windowMessage(UINT message, WPARAM wParam, LPARAM lParam); static LRESULT windowHookCallback(int nCode, WPARAM wParam, LPARAM lParam); # endif - -# ifdef RT64_SDL_WINDOW - SDL_EventFilter sdlEventFilter; - void *sdlEventFilterUserdata; - bool sdlEventFilterStored; - - void blockSdlKeyboard(); - void unblockSdlKeyboard(); - static int sdlEventKeyboardFilter(void *userdata, SDL_Event *event); -# endif }; }; \ No newline at end of file