Add inspector with hooks for SDL2.

This commit is contained in:
Dario 2024-08-10 17:56:33 -03:00
parent 1812bc03c3
commit 33fd9fd88d
7 changed files with 179 additions and 119 deletions

View File

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

View File

@ -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
@ -240,4 +242,18 @@ namespace RT64 {
return ImGui_ImplWin32_WndProcHandler(swapChain->getWindow(), msg, wParam, lParam);
}
#endif
bool Inspector::handleSdlEvent(SDL_Event *event) {
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;
}
};

View File

@ -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 {
@ -36,5 +37,6 @@ namespace RT64 {
# ifdef _WIN32
bool handleMessage(UINT msg, WPARAM wParam, LPARAM lParam);
# endif
bool handleSdlEvent(SDL_Event *event);
};
};

View File

@ -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<std::mutex> lock(presentQueue->inspectorMutex);
if (presentQueue->inspector == nullptr) {
presentQueue->inspector = std::make_unique<Inspector>(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,79 @@ namespace RT64 {
return false;
}
#endif
bool Application::sdlEventFilter(SDL_Event *event) {
if (userConfig.developerMode && (presentQueue != nullptr) && (state != nullptr) && !FileDialog::isOpen) {
const std::lock_guard<std::mutex> 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;
}
}
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<std::mutex> lock(presentQueue->inspectorMutex);
if (presentQueue->inspector == nullptr) {
presentQueue->inspector = std::make_unique<Inspector>(device.get(), swapChain.get(), createdGraphicsAPI);
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

View File

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

View File

@ -51,16 +51,24 @@ namespace RT64 {
assert(listener != nullptr);
this->listener = listener;
windowHandle = window;
# ifdef _WIN32
windowHandle = window;
usingSdl = SDL_WasInit(SDL_INIT_VIDEO);
if (listener->usesWindowMessageFilter()) {
assert(HookedApplicationWindow == nullptr);
assert(threadId != 0);
windowHook = SetWindowsHookEx(WH_GETMESSAGE, &windowHookCallback, NULL, threadId);
HookedApplicationWindow = this;
if (usingSdl) {
// 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.
}
# ifdef _WIN32
else {
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 +79,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,9 +116,8 @@ 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);
SDL_SysWMinfo wmInfo;
assert(sdlWindow && "Failed to open window with SDL");
@ -128,10 +135,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 +308,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 +329,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<ApplicationWindow *>(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 && usingSdl) {
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<ApplicationWindow *>(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;
}
}
};

View File

@ -9,34 +9,29 @@
#include <Windows.h>
#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
Listener *listener;
uint32_t refreshRate = 0;
bool fullScreen;
@ -44,6 +39,15 @@ namespace RT64 {
bool usingSdl;
int32_t windowLeft = INT32_MAX;
int32_t windowTop = INT32_MAX;
SDL_EventFilter sdlEventFilterStored = nullptr;
void *sdlEventFilterUserdata = nullptr;
bool sdlEventFilterInstalled = false;
# ifdef _WIN32
HHOOK windowHook;
HMENU windowMenu;
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
};
};