macos: Patch QMetalLayer.setNeedsDisplayInRect at runtime to avoid freezing on recent Qt

This commit is contained in:
OpenSauce04 2025-09-03 02:03:17 +01:00 committed by OpenSauce
parent bf03cac9e1
commit 16b8a78571
5 changed files with 80 additions and 2 deletions

View File

@ -60,6 +60,10 @@ if (ENABLE_QT AND UNIX AND NOT APPLE)
target_link_libraries(citra_meta PRIVATE Qt6::DBus gamemode) target_link_libraries(citra_meta PRIVATE Qt6::DBus gamemode)
endif() endif()
if (ENABLE_QT AND APPLE)
target_link_libraries(citra_meta PRIVATE Qt6::GuiPrivate)
endif()
if (ENABLE_QT AND USE_DISCORD_PRESENCE) if (ENABLE_QT AND USE_DISCORD_PRESENCE)
target_link_libraries(citra_meta PRIVATE discord-rpc) target_link_libraries(citra_meta PRIVATE discord-rpc)
endif() endif()

View File

@ -172,12 +172,12 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
multiplayer/state.h multiplayer/state.h
multiplayer/validation.h multiplayer/validation.h
precompiled_headers.h precompiled_headers.h
qt_image_interface.cpp
qt_image_interface.h
uisettings.cpp uisettings.cpp
uisettings.h uisettings.h
user_data_migration.cpp user_data_migration.cpp
user_data_migration.h user_data_migration.h
qt_image_interface.cpp
qt_image_interface.h
util/clickable_label.cpp util/clickable_label.cpp
util/clickable_label.h util/clickable_label.h
util/graphics_device_info.cpp util/graphics_device_info.cpp
@ -190,6 +190,13 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
util/util.h util/util.h
) )
if (APPLE)
target_sources(citra_qt PUBLIC
qt_swizzle.h
qt_swizzle.mm
)
endif()
file(GLOB COMPAT_LIST file(GLOB COMPAT_LIST
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
@ -275,6 +282,10 @@ if (NOT WIN32)
target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
endif() endif()
if (APPLE)
target_link_libraries(citra_qt PRIVATE Qt6::GuiPrivate)
endif()
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)
target_link_libraries(citra_qt PRIVATE Qt6::DBus gamemode) target_link_libraries(citra_qt PRIVATE Qt6::DBus gamemode)
endif() endif()

View File

@ -69,6 +69,7 @@
#include "citra_qt/movie/movie_record_dialog.h" #include "citra_qt/movie/movie_record_dialog.h"
#include "citra_qt/multiplayer/state.h" #include "citra_qt/multiplayer/state.h"
#include "citra_qt/qt_image_interface.h" #include "citra_qt/qt_image_interface.h"
#include "citra_qt/qt_swizzle.h"
#include "citra_qt/uisettings.h" #include "citra_qt/uisettings.h"
#include "common/play_time_manager.h" #include "common/play_time_manager.h"
#ifdef ENABLE_QT_UPDATE_CHECKER #ifdef ENABLE_QT_UPDATE_CHECKER
@ -4112,6 +4113,11 @@ static Qt::HighDpiScaleFactorRoundingPolicy GetHighDpiRoundingPolicy() {
} }
void LaunchQtFrontend(int argc, char* argv[]) { void LaunchQtFrontend(int argc, char* argv[]) {
#ifdef __APPLE__
// Ensure that the linker doesn't optimize qt_swizzle.mm out of existence.
QtSwizzle::Dummy();
#endif
Common::DetachedTasks detached_tasks; Common::DetachedTasks detached_tasks;
#if MICROPROFILE_ENABLED #if MICROPROFILE_ENABLED

View File

@ -0,0 +1,9 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
namespace QtSwizzle {
void Dummy();
} // namespace QtSwizzle

View File

@ -0,0 +1,48 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#import <QtGui/private/qmetallayer_p.h>
#import <objc/runtime.h>
namespace QtSwizzle {
void Dummy() {
// Call this anywhere to make sure that qt_swizzle.mm is linked.
// noop
}
} // namespace QtSwizzle
@implementation QMetalLayer (AzaharPatch)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class targetClass = [self class];
// Get the original and swizzled methods
Method originalMethod =
class_getInstanceMethod(targetClass, @selector(setNeedsDisplayInRect:));
Method swizzledMethod =
class_getInstanceMethod(targetClass, @selector(swizzled_setNeedsDisplayInRect:));
// Swap the implementations
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
- (void)swizzled_setNeedsDisplayInRect:(CGRect)rect {
constexpr auto tooBig = 1e10; // Arbitrary large number
// Check for problematic huge rectangles
if ((!self.needsDisplay) && (rect.size.width > tooBig || rect.size.height > tooBig ||
rect.origin.x < -tooBig || rect.origin.y < -tooBig)) {
return;
}
// Call the original implementation
[self swizzled_setNeedsDisplayInRect:rect];
}
@end