Add inspector with hooks for SDL2. (#75)

* Add inspector with hooks for SDL2.

* Fix build errors.

* Add the remaining SDL2 implementation that was required.
This commit is contained in:
Darío 2024-08-10 19:41:29 -03:00 committed by GitHub
parent 1812bc03c3
commit 88c618c1f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 227 additions and 153 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
@ -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;
}
};

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 {
@ -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<RenderDescriptorSet> descriptorSet;
std::unique_ptr<VulkanContext> 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);
};
};

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,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<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;
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<std::mutex> lock(presentQueue->inspectorMutex);
if (presentQueue->inspector == nullptr) {
presentQueue->inspector = std::make_unique<Inspector>(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

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

@ -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<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 && (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<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,41 +9,45 @@
#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
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
};
};