diff --git a/CMakeLists.txt b/CMakeLists.txt index d18b7d12a9..63387a52cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,9 +62,6 @@ endif() # gsrunner if(ENABLE_GSRUNNER) - if (NOT WIN32 AND NOT APPLE) - message(WARNING "GSRunner is only supported on Windows and macOS and may not build on your system") - endif() add_subdirectory(pcsx2-gsrunner) else() add_subdirectory(pcsx2-gsrunner EXCLUDE_FROM_ALL) diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp index cbc2fb1a97..9242821f4b 100644 --- a/pcsx2-gsrunner/Main.cpp +++ b/pcsx2-gsrunner/Main.cpp @@ -47,6 +47,15 @@ #include "svnrev.h" +// Down here because X11 has a lot of defines that can conflict +#if defined(__linux__) +#include +#include +#include +#include +#include +#endif + namespace GSRunner { static void InitializeConsole(); @@ -1086,4 +1095,118 @@ void GSRunner::StopPlatformMessagePump() CocoaTools::StopMainThreadEventLoop(); } +#elif defined(__linux__) +static Display* s_display = nullptr; +static Window s_window = None; +static WindowInfo s_wi; +static std::atomic s_shutdown_requested{false}; + +bool GSRunner::CreatePlatformWindow() +{ + pxAssertRel(!s_display && s_window == None, "Tried to create window when there already was one!"); + + s_display = XOpenDisplay(nullptr); + if (!s_display) + { + Console.Error("Failed to open X11 display"); + return false; + } + + int screen = DefaultScreen(s_display); + Window root = RootWindow(s_display, screen); + + s_window = XCreateSimpleWindow(s_display, root, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 1, + BlackPixel(s_display, screen), WhitePixel(s_display, screen)); + + if (s_window == None) + { + Console.Error("Failed to create X11 window"); + XCloseDisplay(s_display); + s_display = nullptr; + return false; + } + + XStoreName(s_display, s_window, "PCSX2 GS Runner"); + XSelectInput(s_display, s_window, StructureNotifyMask); + XMapWindow(s_display, s_window); + + s_wi.type = WindowInfo::Type::X11; + s_wi.display_connection = s_display; + s_wi.window_handle = reinterpret_cast(s_window); + s_wi.surface_width = WINDOW_WIDTH; + s_wi.surface_height = WINDOW_HEIGHT; + s_wi.surface_scale = 1.0f; + + XFlush(s_display); + PumpPlatformMessages(); + return true; +} + +void GSRunner::DestroyPlatformWindow() +{ + if (s_display && s_window != None) + { + XDestroyWindow(s_display, s_window); + s_window = None; + } + + if (s_display) + { + XCloseDisplay(s_display); + s_display = nullptr; + } +} + +std::optional GSRunner::GetPlatformWindowInfo() +{ + WindowInfo wi; + if (s_display && s_window != None) + wi = s_wi; + else + wi.type = WindowInfo::Type::Surfaceless; + return wi; +} + +void GSRunner::PumpPlatformMessages(bool forever) +{ + if (!s_display) + return; + + do + { + while (XPending(s_display) > 0) + { + XEvent event; + XNextEvent(s_display, &event); + + switch (event.type) + { + case ConfigureNotify: + { + const XConfigureEvent& configure = event.xconfigure; + s_wi.surface_width = static_cast(configure.width); + s_wi.surface_height = static_cast(configure.height); + break; + } + case DestroyNotify: + return; + default: + break; + } + } + + if (s_shutdown_requested.load()) + return; + + if (forever) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } while (forever && !s_shutdown_requested.load()); +} + +void GSRunner::StopPlatformMessagePump() +{ + s_shutdown_requested.store(true); +} #endif // _WIN32 / __APPLE__ diff --git a/pcsx2-gsrunner/test_run_dumps.py b/pcsx2-gsrunner/test_run_dumps.py index b61db398fc..09debbd46a 100644 --- a/pcsx2-gsrunner/test_run_dumps.py +++ b/pcsx2-gsrunner/test_run_dumps.py @@ -73,7 +73,6 @@ def run_regression_test(runner, dumpdir, renderer, upscale, renderhacks, paralle #print("Running '%s'" % (" ".join(args))) subprocess.run(args, env=environ, stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL, creationflags=creationflags) - def run_regression_tests(runner, gsdir, dumpdir, renderer, upscale, renderhacks, parallel=1): paths = glob.glob(gsdir + "/*.*", recursive=True) gamepaths = list(filter(lambda x: get_gs_name(x) is not None, paths)) @@ -104,12 +103,12 @@ def run_regression_tests(runner, gsdir, dumpdir, renderer, upscale, renderhacks, if __name__ == "__main__": parser = argparse.ArgumentParser(description="Generate frame dump images for regression tests") - parser.add_argument("-runner", action="store", required=True, help="Path to PCSX2 GS runner") - parser.add_argument("-gsdir", action="store", required=True, help="Directory containing GS dumps") - parser.add_argument("-dumpdir", action="store", required=True, help="Base directory to dump frames to") - parser.add_argument("-renderer", action="store", required=False, help="Renderer to use") + parser.add_argument("-runner", action="store", required=True, type=str.strip, help="Path to PCSX2 GS runner") + parser.add_argument("-gsdir", action="store", required=True, type=str.strip, help="Directory containing GS dumps") + parser.add_argument("-dumpdir", action="store", required=True, type=str.strip, help="Base directory to dump frames to") + parser.add_argument("-renderer", action="store", required=False, type=str.strip, help="Renderer to use") parser.add_argument("-upscale", action="store", type=float, default=1, help="Upscaling multiplier to use") - parser.add_argument("-renderhacks", action="store", required=False, help="Enable HW Rendering hacks") + parser.add_argument("-renderhacks", action="store", required=False, type=str.strip, help="Enable HW Rendering hacks") parser.add_argument("-parallel", action="store", type=int, default=1, help="Number of processes to run") args = parser.parse_args()