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);
+ }
+ }
}
}