From e764c5cd4ede53e9610d09b6194437a9e08f1321 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Sat, 25 Feb 2023 00:22:10 -0600 Subject: [PATCH] GSRunner: macOS support --- CMakeLists.txt | 6 +- cmake/BuildParameters.cmake | 2 +- common/CocoaTools.h | 11 +++ common/CocoaTools.mm | 84 +++++++++++++++++++++ pcsx2-gsrunner/Main.cpp | 45 ++++++++++- pcsx2/CMakeLists.txt | 9 ++- pcsx2/GS/Renderers/Metal/GSMTLDeviceInfo.mm | 9 +++ 7 files changed, 161 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4036241b47..d18b7d12a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,10 +62,12 @@ endif() # gsrunner if(ENABLE_GSRUNNER) - if (NOT WIN32) - message(WARNING "GSRunner is only supported on Windows and may not build on your system") + 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) endif() #------------------------------------------------------------------------------- diff --git a/cmake/BuildParameters.cmake b/cmake/BuildParameters.cmake index 67504a9c0e..e551edae2a 100644 --- a/cmake/BuildParameters.cmake +++ b/cmake/BuildParameters.cmake @@ -7,7 +7,7 @@ include(GNUInstallDirs) # Misc option #------------------------------------------------------------------------------- option(ENABLE_TESTS "Enables building the unit tests" ON) -option(ENABLE_GSRUNNER "Enables building the GSRunner" OFF) +option(ENABLE_GSRUNNER "Enables building the GSRunner by default. It can still be built with `make pcsx2-gsrunner` otherwise." OFF) option(LTO_PCSX2_CORE "Enable LTO/IPO/LTCG on the subset of pcsx2 that benefits most from it but not anything else") option(USE_VTUNE "Plug VTUNE to profile GS JIT.") option(PACKAGE_MODE "Use this option to ease packaging of PCSX2 (developer/distribution option)") diff --git a/common/CocoaTools.h b/common/CocoaTools.h index 8da50f8a2a..50f0600275 100644 --- a/common/CocoaTools.h +++ b/common/CocoaTools.h @@ -31,6 +31,17 @@ namespace CocoaTools bool DelayedLaunch(std::string_view file); /// Open a Finder window to the given URL bool ShowInFinder(std::string_view file); + + /// Create a window + void* CreateWindow(std::string_view title, uint32_t width, uint32_t height); + /// Destroy a window + void DestroyWindow(void* window); + /// Make a WindowInfo from the given window + void GetWindowInfoFromWindow(WindowInfo* wi, void* window); + /// Run cocoa event loop + void RunCocoaEventLoop(bool wait_forever = false); + /// Posts an event to the main telling `RunCocoaEventLoop(true)` to exit + void StopMainThreadEventLoop(); } #endif // __APPLE__ diff --git a/common/CocoaTools.mm b/common/CocoaTools.mm index adbe803b80..68bbad2239 100644 --- a/common/CocoaTools.mm +++ b/common/CocoaTools.mm @@ -226,3 +226,87 @@ bool CocoaTools::ShowInFinder(std::string_view file) return [[NSWorkspace sharedWorkspace] selectFile:NSStringFromStringView(file) inFileViewerRootedAtPath:nil]; } + +// MARK: - GSRunner + +void* CocoaTools::CreateWindow(std::string_view title, u32 width, u32 height) +{ + if (!NSApp) + { + [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + [NSApp finishLaunching]; + } + constexpr NSWindowStyleMask style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; + NSScreen* mainScreen = [NSScreen mainScreen]; + // Center the window on the screen, because why not + NSRect screenFrame = [mainScreen frame]; + NSRect viewFrame = screenFrame; + viewFrame.size = NSMakeSize(width, height); + viewFrame.origin.x += (screenFrame.size.width - viewFrame.size.width) / 2; + viewFrame.origin.y += (screenFrame.size.height - viewFrame.size.height) / 2; + NSWindow* window = [[NSWindow alloc] + initWithContentRect:viewFrame + styleMask:style + backing:NSBackingStoreBuffered + defer:NO]; + [window setTitle:NSStringFromStringView(title)]; + [window makeKeyAndOrderFront:window]; + return (__bridge_retained void*)window; +} + +void CocoaTools::DestroyWindow(void* window) +{ + (void)(__bridge_transfer NSWindow*)window; +} + +void CocoaTools::GetWindowInfoFromWindow(WindowInfo* wi, void* cf_window) +{ + if (cf_window) + { + NSWindow* window = (__bridge NSWindow*)cf_window; + float scale = [window backingScaleFactor]; + NSView* view = [window contentView]; + NSRect dims = [view frame]; + wi->type = WindowInfo::Type::MacOS; + wi->window_handle = (__bridge void*)view; + wi->surface_width = dims.size.width * scale; + wi->surface_height = dims.size.height * scale; + wi->surface_scale = scale; + } + else + { + wi->type = WindowInfo::Type::Surfaceless; + } +} + +static constexpr short STOP_EVENT_LOOP = 0x100; + +void CocoaTools::RunCocoaEventLoop(bool forever) +{ + NSDate* end = forever ? [NSDate distantFuture] : [NSDate distantPast]; + while (true) + { @autoreleasepool { + NSEvent* ev = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:end + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (!ev || ([ev type] == NSEventTypeApplicationDefined && [ev subtype] == STOP_EVENT_LOOP)) + break; + [NSApp sendEvent:ev]; + }} +} + +void CocoaTools::StopMainThreadEventLoop() +{ @autoreleasepool { + NSEvent* ev = [NSEvent otherEventWithType:NSEventTypeApplicationDefined + location:{} + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:STOP_EVENT_LOOP + data1:0 + data2:0]; + [NSApp postEvent:ev atStart:NO]; +}} diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp index 8eccc828e9..5ee16b1f28 100644 --- a/pcsx2-gsrunner/Main.cpp +++ b/pcsx2-gsrunner/Main.cpp @@ -16,6 +16,7 @@ #include "fmt/format.h" #include "common/Assertions.h" +#include "common/CocoaTools.h" #include "common/Console.h" #include "common/CrashHandler.h" #include "common/FileSystem.h" @@ -1031,4 +1032,46 @@ int wmain(int argc, wchar_t** argv) return real_main(argc, u8_argptrs.data()); } -#endif // _WIN32 +#elif defined(__APPLE__) + +static void* s_window; +static WindowInfo s_wi; + +bool GSRunner::CreatePlatformWindow() +{ + pxAssertRel(!s_window, "Tried to create window when there already was one!"); + s_window = CocoaTools::CreateWindow("PCSX2 GS Runner", WINDOW_WIDTH, WINDOW_HEIGHT); + CocoaTools::GetWindowInfoFromWindow(&s_wi, s_window); + PumpPlatformMessages(); + return s_window; +} + +void GSRunner::DestroyPlatformWindow() +{ + if (s_window) { + CocoaTools::DestroyWindow(s_window); + s_window = nullptr; + } +} + +std::optional GSRunner::GetPlatformWindowInfo() +{ + WindowInfo wi; + if (s_window) + wi = s_wi; + else + wi.type = WindowInfo::Type::Surfaceless; + return wi; +} + +void GSRunner::PumpPlatformMessages(bool forever) +{ + CocoaTools::RunCocoaEventLoop(forever); +} + +void GSRunner::StopPlatformMessagePump() +{ + CocoaTools::StopMainThreadEventLoop(); +} + +#endif // _WIN32 / __APPLE__ diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 3beb2a4c23..2efa784dc2 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -1237,9 +1237,16 @@ fixup_file_properties(PCSX2) force_include_last(PCSX2_FLAGS "/(usr|local)/include/?$") if (APPLE) + find_library(APPKIT_LIBRARY AppKit) + find_library(IOKIT_LIBRARY IOKit) find_library(METAL_LIBRARY Metal) find_library(QUARTZCORE_LIBRARY QuartzCore) - target_link_libraries(PCSX2_FLAGS INTERFACE ${METAL_LIBRARY} ${QUARTZCORE_LIBRARY}) + target_link_libraries(PCSX2_FLAGS INTERFACE + ${APPKIT_LIBRARY} + ${IOKIT_LIBRARY} + ${METAL_LIBRARY} + ${QUARTZCORE_LIBRARY} + ) endif() set_property(GLOBAL PROPERTY PCSX2_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/pcsx2/GS/Renderers/Metal/GSMTLDeviceInfo.mm b/pcsx2/GS/Renderers/Metal/GSMTLDeviceInfo.mm index c55d3c09e8..c165072158 100644 --- a/pcsx2/GS/Renderers/Metal/GSMTLDeviceInfo.mm +++ b/pcsx2/GS/Renderers/Metal/GSMTLDeviceInfo.mm @@ -4,12 +4,19 @@ #include "GSMTLDeviceInfo.h" #include "GS/GS.h" #include "common/Console.h" +#include "common/Path.h" #ifdef __APPLE__ static id loadMainLibrary(id dev, NSString* name) { NSString* path = [[NSBundle mainBundle] pathForResource:name ofType:@"metallib"]; + if (!path) + { + std::string ssname = std::string([name UTF8String]) + ".metallib"; + std::string sspath = Path::Combine(EmuFolders::Resources, ssname); + path = [[NSString alloc] initWithBytes:sspath.data() length:sspath.length() encoding:NSUTF8StringEncoding]; + } return path ? [dev newLibraryWithFile:path error:nullptr] : nullptr; } @@ -24,6 +31,8 @@ static MRCOwned> loadMainLibrary(id dev) if (@available(macOS 10.14, iOS 12.0, *)) if (id lib = loadMainLibrary(dev, @"Metal21")) return MRCTransfer(lib); + if (id lib = loadMainLibrary(dev, @"default")) + return MRCTransfer(lib); return MRCTransfer([dev newDefaultLibrary]); }