diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index 4800d1ad64..75a9037b0a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -47,6 +47,7 @@ import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.ResponseCodeException; import org.jackhuang.hmcl.util.platform.*; +import org.jackhuang.hmcl.util.platform.windows.WinReg; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import org.jackhuang.hmcl.util.versioning.VersionNumber; @@ -86,6 +87,7 @@ public final class LauncherHelper { private boolean showLogs; private QuickPlayOption quickPlayOption; private boolean disableOfflineSkin = false; + private boolean modifiedGpuReg = false; public LauncherHelper(Profile profile, Account account, String selectedVersion) { this.profile = Objects.requireNonNull(profile); @@ -232,6 +234,30 @@ private void launch0() { launchOptionsBuilder.setQuickPlayOption(quickPlayOption); } + if (config().isWindowsHighPerformance()) { + try { + WinReg reg = WinReg.INSTANCE; + if (reg != null) { + Object current = reg.queryValue( + WinReg.HKEY.HKEY_CURRENT_USER, + "Software\\Microsoft\\DirectX\\UserGpuPreferences", + FileUtils.getAbsolutePath(javaVersionRef.get().getBinary()) + ); + if (!(current instanceof String)) { + reg.setValue( + WinReg.HKEY.HKEY_CURRENT_USER, + "Software\\Microsoft\\DirectX\\UserGpuPreferences", + FileUtils.getAbsolutePath(javaVersionRef.get().getBinary()), + "GpuPreference=2;" + ); + modifiedGpuReg = true; + } + } + } catch (Exception e) { + LOG.warning("Failed to apply high performance GPU preference", e); + } + } + LaunchOptions launchOptions = launchOptionsBuilder.create(); LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModsDirectory(selectedVersion), 10)); @@ -875,6 +901,21 @@ public void run() { } private void finishLaunch() { + if (modifiedGpuReg) { + try { + Thread.sleep(5000L); + } catch (InterruptedException ignored) { + } + try { + WinReg.INSTANCE.deleteValue( + WinReg.HKEY.HKEY_CURRENT_USER, + "Software\\Microsoft\\DirectX\\UserGpuPreferences", + process.getProcess().info().command().orElseThrow() + ); + } catch (Exception e) { + LOG.warning("Failed to revert high performance GPU preference", e); + } + } switch (launcherVisibility) { case HIDE_AND_REOPEN: runLater(() -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java index 0a845929b5..cc713ef9e5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java @@ -704,6 +704,21 @@ public void setDisableAutoGameOptions(boolean disableAutoGameOptions) { this.disableAutoGameOptions.set(disableAutoGameOptions); } + @SerializedName("windowsHighPerformance") + private final BooleanProperty windowsHighPerformance = new SimpleBooleanProperty(false); + + public BooleanProperty windowsHighPerformanceProperty() { + return windowsHighPerformance; + } + + public boolean isWindowsHighPerformance() { + return windowsHighPerformance.get(); + } + + public void setWindowsHighPerformance(boolean windowsHighPerformance) { + this.windowsHighPerformance.set(windowsHighPerformance); + } + @SerializedName("allowAutoAgent") private final BooleanProperty allowAutoAgent = new SimpleBooleanProperty(false); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 2281016c35..a849e9fa12 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -260,6 +260,15 @@ else if (locale.isSameLanguage(currentLocale)) settingsPane.getContent().add(disableAutoGameOptionsPane); } + { + LineToggleButton windowsHighPerformance = new LineToggleButton(); + windowsHighPerformance.setTitle(i18n("settings.launcher.gpu_preferences")); + windowsHighPerformance.setSubtitle(i18n("settings.advanced.windows_only")); + windowsHighPerformance.selectedProperty().bindBidirectional(config().windowsHighPerformanceProperty()); + + settingsPane.getContent().add(windowsHighPerformance); + } + { LineToggleButton allowAutoAgentPane = new LineToggleButton(); allowAutoAgentPane.setTitle(i18n("settings.launcher.allow_auto_agent")); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index a2b324057d..56b91fa60d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1429,6 +1429,7 @@ settings.advanced.unsupported_system_options=Settings not applicable to the curr settings.advanced.use_native_glfw=Use System GLFW settings.advanced.use_native_openal=Use System OpenAL settings.advanced.linux_freebsd_only=Linux/FreeBSD Only +settings.advanced.windows_only=Windows Only settings.advanced.workaround=Workaround settings.advanced.workaround.warning=Workaround options are intended only for advanced users. Tweaking with these options may crash the game. Unless you know what you are doing, please do not edit these options. settings.advanced.wrapper_launcher=Wrapper Command @@ -1487,6 +1488,7 @@ settings.launcher.font.anti_aliasing.auto=Auto settings.launcher.font.anti_aliasing.gray=Grayscale settings.launcher.font.anti_aliasing.lcd=Sub-pixel settings.launcher.general=General +settings.launcher.gpu_preferences=Launch games with high-performance GPU settings.launcher.language=Language settings.launcher.launcher_log.export=Export Launcher Logs settings.launcher.launcher_log.export.failed=Failed to export logs. diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 96ab768a83..73036ac3e8 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1220,6 +1220,7 @@ settings.advanced.unsupported_system_options=不適用於目前系統的選項 settings.advanced.use_native_glfw=使用系統 GLFW settings.advanced.use_native_openal=使用系統 OpenAL settings.advanced.linux_freebsd_only=僅限 Linux/FreeBSD +settings.advanced.windows_only=僅限 Windows settings.advanced.workaround=除錯選項 settings.advanced.workaround.warning=除錯選項僅提供給專業玩家使用。修改除錯選項可能會導致遊戲無法啟動。除非你知道你在做什麼,否則請不要修改這些選項。 settings.advanced.wrapper_launcher=前置指令 @@ -1276,6 +1277,7 @@ settings.launcher.font.anti_aliasing.auto=自動 settings.launcher.font.anti_aliasing.gray=灰階 settings.launcher.font.anti_aliasing.lcd=子像素 settings.launcher.general=一般 +settings.launcher.gpu_preferences=使用高效能 GPU 啟動遊戲 settings.launcher.language=語言 settings.launcher.launcher_log.export=匯出啟動器日誌 settings.launcher.launcher_log.export.failed=無法匯出日誌。 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index d29478876f..1fcf5a079c 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1225,6 +1225,7 @@ settings.advanced.unsupported_system_options=不适用于当前系统的选项 settings.advanced.use_native_glfw=使用系统 GLFW settings.advanced.use_native_openal=使用系统 OpenAL settings.advanced.linux_freebsd_only=仅 Linux/FreeBSD +settings.advanced.windows_only=仅 Windows settings.advanced.workaround=调试选项 settings.advanced.workaround.warning=调试选项仅提供给专业玩家使用。调试选项可能会导致游戏无法启动。除非你知道你在做什么,否则请不要修改这些选项! settings.advanced.wrapper_launcher=包装命令 @@ -1281,6 +1282,7 @@ settings.launcher.font.anti_aliasing.auto=自动 settings.launcher.font.anti_aliasing.gray=灰度 settings.launcher.font.anti_aliasing.lcd=子像素 settings.launcher.general=通用 +settings.launcher.gpu_preferences=使用高性能 GPU 启动游戏 settings.launcher.language=语言 settings.launcher.launcher_log.export=导出启动器日志 settings.launcher.launcher_log.export.failed=无法导出日志 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/Advapi32.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/Advapi32.java index 274826cd69..fe463e639b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/Advapi32.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/Advapi32.java @@ -56,4 +56,22 @@ int RegEnumKeyExW(Pointer hKey, int dwIndex, IntByReference lpReserved, Pointer lpClass, IntByReference lpcchClass, Pointer lpftLastWriteTime); + + /** + * @see RegCreateKeyExW function + */ + int RegCreateKeyExW(Pointer hKey, WString lpSubKey, int Reserved, WString lpClass, + int dwOptions, int samDesired, Pointer lpSecurityAttributes, + PointerByReference phkResult, IntByReference lpdwDisposition); + + /** + * @see RegSetValueExW function + */ + int RegSetValueExW(Pointer hKey, WString lpValueName, int Reserved, int dwType, + Pointer lpData, int cbData); + + /** + * @see RegDeleteValueW function + */ + int RegDeleteValueW(Pointer hKey, WString lpValueName); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinConstants.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinConstants.java index 01888d0308..d080830ed3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinConstants.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinConstants.java @@ -53,6 +53,8 @@ public interface WinConstants { // https://learn.microsoft.com/windows/win32/sysinfo/registry-key-security-and-access-rights int KEY_QUERY_VALUE = 0x0001; int KEY_ENUMERATE_SUB_KEYS = 0x0008; + int KEY_WRITE = 0x20006; + int KEY_SET_VALUE = 0x0002; int KEY_READ = 0x20019; // https://learn.microsoft.com/windows/win32/sysinfo/registry-value-types @@ -100,4 +102,6 @@ public interface WinConstants { // https://learn.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; + // https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regcreatekeyexw + int REG_OPTION_NON_VOLATILE = 0x0000; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinReg.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinReg.java index 0067160500..5c4d56d3c1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinReg.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinReg.java @@ -88,6 +88,10 @@ public List querySubKeys(HKEY root, String key) { return list; } + public abstract boolean setValue(HKEY root, String key, String valueName, String value); + + public abstract boolean deleteValue(HKEY root, String key, String valueName); + private static final class JNAWinReg extends WinReg { private final Advapi32 advapi32; @@ -245,6 +249,63 @@ public List querySubKeyNames(HKEY root, String key) { return Collections.emptyList(); } + + @Override + public boolean setValue(HKEY root, String key, String valueName, String value) { + PointerByReference phkKey = new PointerByReference(); + int status = advapi32.RegCreateKeyExW( + root.toPointer(), + new WString(key), + 0, + null, + WinConstants.REG_OPTION_NON_VOLATILE, + WinConstants.KEY_WRITE, + null, + phkKey, + null + ); + + if (status != WinConstants.ERROR_SUCCESS) { + return false; + } + + Pointer hkey = phkKey.getValue(); + try { + byte[] data = (value + "\0").getBytes(StandardCharsets.UTF_16LE); + try (Memory mem = new Memory(data.length)) { + mem.write(0, data, 0, data.length); + status = advapi32.RegSetValueExW( + hkey, + new WString(valueName), + 0, + WinConstants.REG_SZ, + mem, + data.length + ); + } + return status == WinConstants.ERROR_SUCCESS; + } finally { + advapi32.RegCloseKey(hkey); + } + } + + @Override + public boolean deleteValue(HKEY root, String key, String valueName) { + PointerByReference phkKey = new PointerByReference(); + int status = advapi32.RegOpenKeyExW(root.toPointer(), new WString(key), 0, WinConstants.KEY_SET_VALUE, phkKey); + + if (status != WinConstants.ERROR_SUCCESS) { + return false; + } + + Pointer hkey = phkKey.getValue(); + try { + status = advapi32.RegDeleteValueW(hkey, new WString(valueName)); + return status == WinConstants.ERROR_SUCCESS; + } finally { + advapi32.RegCloseKey(hkey); + } + } } }