feat: [Windows] 支持自动使用高性能显卡 (#5420)

resolves #4639
This commit is contained in:
辞庐
2026-08-01 22:14:34 +08:00
committed by GitHub
parent a50e327f70
commit 612998a98f
9 changed files with 138 additions and 0 deletions
@@ -50,6 +50,7 @@ import org.jackhuang.hmcl.util.i18n.I18n;
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;
import org.jetbrains.annotations.Nullable;
@@ -242,6 +243,36 @@ public final class LauncherHelper {
launchOptionsBuilder.setQuickPlayOption(quickPlayOption);
}
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && setting.getInheritable(GameSettings::highPerformanceProperty)) {
@Nullable String javaPathGpuReg = FileUtils.getAbsolutePath(javaVersionRef.get().getBinary());
try {
WinReg reg = WinReg.INSTANCE;
if (reg != null) {
Object current = reg.queryValue(
WinReg.HKEY.HKEY_CURRENT_USER,
"Software\\Microsoft\\DirectX\\UserGpuPreferences",
javaPathGpuReg
);
if (current == null) {
boolean result = reg.setValue(
WinReg.HKEY.HKEY_CURRENT_USER,
"Software\\Microsoft\\DirectX\\UserGpuPreferences",
javaPathGpuReg,
"GpuPreference=2;"
);
if (result)
LOG.info("Successfully applied high performance GPU preference for java: " + javaPathGpuReg);
else
LOG.warning("Failed to apply high performance GPU preference for java: " + javaPathGpuReg);
} else {
LOG.info("GPU preference for %s already exists: %s".formatted(javaPathGpuReg, current));
}
}
} 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(selectedInstanceId), 10));
@@ -643,6 +643,19 @@ public sealed abstract class GameSettings extends ObservableSetting {
return vulkanRenderer;
}
/// Property name for high performance GPU preference.
public static final String PROPERTY_HIGH_PERFORMANCE = "highPerformance";
/// Whether to launch games with high-performance GPU.
@SerializedName(PROPERTY_HIGH_PERFORMANCE)
private final InheritableProperty<Boolean> highPerformance =
newInheritableProperty(PROPERTY_HIGH_PERFORMANCE, false);
/// Returns the high performance GPU preference property.
public InheritableProperty<Boolean> highPerformanceProperty() {
return highPerformance;
}
/// Property name for customized environment variables.
public static final String PROPERTY_ENVIRONMENT_VARIABLES = "environmentVariables";
@@ -787,6 +787,11 @@ public final class GameSettingsPage<S extends GameSettings> extends StackPane
graphicsSettings.getContent().add(vulkanRendererPane);
vulkanRendererPane.setTitle(i18n("settings.advanced.renderer.vulkan"));
var highPerformancePane = createInheritableBooleanButton(GameSettings::highPerformanceProperty);
graphicsSettings.getContent().add(highPerformancePane);
highPerformancePane.setTitle(i18n("settings.advanced.renderer.gpu_preferences"));
highPerformancePane.setSubtitle(i18n("settings.advanced.windows_only"));
this.currentGameVersionNumber.addListener((o, oldValue, newValue) -> {
boolean showBackendChoose = isPresetSetting || newValue.compareTo("26.2-snapshot-2") >= 0;
graphicsBackendPane.setVisible(showBackendChoose);
@@ -1489,6 +1489,7 @@ settings.advanced.renderer.default=Default
settings.advanced.renderer.default.desc=Use System Default
settings.advanced.renderer.opengl=OpenGL Renderer/Driver
settings.advanced.renderer.vulkan=Vulkan Renderer/Driver
settings.advanced.renderer.gpu_preferences=Launch games with high-performance GPU
# Vulkan Renderers
settings.advanced.renderer.lavapipe=Mesa Lavapipe
settings.advanced.renderer.lavapipe.desc=Software Vulkan Renderer
@@ -1535,6 +1536,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
@@ -1292,6 +1292,7 @@ settings.advanced.renderer.default=預設
settings.advanced.renderer.default.desc=使用系統預設繪製器
settings.advanced.renderer.opengl=OpenGL 繪製器/驅動
settings.advanced.renderer.vulkan=Vulkan 繪製器/驅動
settings.advanced.renderer.gpu_preferences=使用高效能 GPU 啟動遊戲
# Vulkan Renderers
settings.advanced.renderer.lavapipe=Mesa Lavapipe
settings.advanced.renderer.lavapipe.desc=軟體 Vulkan 繪製器
@@ -1338,6 +1339,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=前置指令
@@ -1292,6 +1292,7 @@ settings.advanced.renderer.default=默认
settings.advanced.renderer.default.desc=使用系统默认渲染器
settings.advanced.renderer.opengl=OpenGL 渲染器/驱动
settings.advanced.renderer.vulkan=Vulkan 渲染器/驱动
settings.advanced.renderer.gpu_preferences=使用高性能 GPU 启动游戏
# Vulkan Renderers
settings.advanced.renderer.lavapipe=Mesa Lavapipe
settings.advanced.renderer.lavapipe.desc=软件 Vulkan 渲染器
@@ -1338,6 +1339,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=包装命令
@@ -56,4 +56,22 @@ public interface Advapi32 extends StdCallLibrary {
IntByReference lpReserved,
Pointer lpClass, IntByReference lpcchClass,
Pointer lpftLastWriteTime);
/**
* @see <a href="https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regcreatekeyexw">RegCreateKeyExW function</a>
*/
int RegCreateKeyExW(Pointer hKey, WString lpSubKey, int Reserved, WString lpClass,
int dwOptions, int samDesired, Pointer lpSecurityAttributes,
PointerByReference phkResult, IntByReference lpdwDisposition);
/**
* @see <a href="https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regsetvalueexw">RegSetValueExW function</a>
*/
int RegSetValueExW(Pointer hKey, WString lpValueName, int Reserved, int dwType,
Pointer lpData, int cbData);
/**
* @see <a href="https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regdeletevaluew">RegDeleteValueW function</a>
*/
int RegDeleteValueW(Pointer hKey, WString lpValueName);
}
@@ -67,6 +67,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
@@ -114,4 +116,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;
}
@@ -88,6 +88,10 @@ public abstract class WinReg {
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 abstract class WinReg {
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);
}
}
}
}