Conversation
There was a problem hiding this comment.
Pull request overview
Adds an optional Windows-only launch behavior to prefer the high-performance GPU by temporarily writing UserGpuPreferences registry values, exposed via a new launcher setting.
Changes:
- Extend the Windows registry helper (WinReg/JNA) to support setting and deleting REG_SZ values.
- Add a new persisted config flag and settings UI toggle for “high-performance GPU” on Windows.
- Hook game launch flow to create (and later remove) the relevant
UserGpuPreferencesregistry entry.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinReg.java | Adds setValue / deleteValue APIs and JNA implementations for registry writes. |
| HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinConstants.java | Adds registry access/constants needed for write/delete operations. |
| HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/Advapi32.java | Adds JNA signatures for RegCreateKeyExW / RegSetValueExW / RegDeleteValueW. |
| HMCL/src/main/resources/assets/lang/I18N*.properties | Adds i18n strings for the new Windows GPU preference setting. |
| HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java | Adds a new toggle to the settings UI bound to the new config property. |
| HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java | Persists the new windowsHighPerformance flag. |
| HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java | Writes/deletes the GPU preference registry value around the launch lifecycle. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public abstract boolean setValue(HKEY root, String key, String valueName, String value); | ||
|
|
||
| public abstract boolean deleteValue(HKEY root, String key, String valueName); |
There was a problem hiding this comment.
New WinReg mutation APIs (setValue/deleteValue) are introduced, but the existing WinRegTest only covers querying. Please add JUnit coverage for setting a REG_SZ value and deleting it (including verifying behavior when the value/key does not exist), so regressions in JNA signatures/encoding are caught.
| WinReg.INSTANCE.deleteValue( | ||
| WinReg.HKEY.HKEY_CURRENT_USER, | ||
| "Software\\Microsoft\\DirectX\\UserGpuPreferences", | ||
| process.getProcess().info().command().orElseThrow() | ||
| ); |
There was a problem hiding this comment.
The registry value name used for cleanup should match exactly the value name used when setting it. Here the value name is derived from javaVersionRef.get().getBinary() during setValue, but cleanup uses process.getProcess().info().command().orElseThrow(), which may differ (quoting/canonicalization) or be empty and throw, leaving the registry entry behind or breaking finishLaunch. Store the exact value name string you used for setValue and reuse it for deletion, and avoid orElseThrow() here.
| WinReg.INSTANCE.deleteValue( | |
| WinReg.HKEY.HKEY_CURRENT_USER, | |
| "Software\\Microsoft\\DirectX\\UserGpuPreferences", | |
| process.getProcess().info().command().orElseThrow() | |
| ); | |
| String valueName = null; | |
| JavaRuntime javaRuntime = javaVersionRef.get(); | |
| if (javaRuntime != null && javaRuntime.getBinary() != null) { | |
| valueName = javaRuntime.getBinary().toString(); | |
| } | |
| if (valueName != null) { | |
| WinReg.INSTANCE.deleteValue( | |
| WinReg.HKEY.HKEY_CURRENT_USER, | |
| "Software\\Microsoft\\DirectX\\UserGpuPreferences", | |
| valueName | |
| ); | |
| } |
| try { | ||
| Thread.sleep(5000L); | ||
| } catch (InterruptedException ignored) {} | ||
| WinReg.INSTANCE.deleteValue( | ||
| WinReg.HKEY.HKEY_CURRENT_USER, | ||
| "Software\\Microsoft\\DirectX\\UserGpuPreferences", | ||
| process.getProcess().info().command().orElseThrow() | ||
| ); |
There was a problem hiding this comment.
finishLaunch() performs a 5s Thread.sleep and ignores interrupts. This method is invoked from onLog() while holding lock (see onLog around the finishLaunch() call), so sleeping here can block log processing and hold the lock for 5 seconds. Consider scheduling the delayed registry cleanup asynchronously (after releasing the lock), and if catching InterruptedException, restore the interrupt flag with Thread.currentThread().interrupt().
| try { | |
| Thread.sleep(5000L); | |
| } catch (InterruptedException ignored) {} | |
| WinReg.INSTANCE.deleteValue( | |
| WinReg.HKEY.HKEY_CURRENT_USER, | |
| "Software\\Microsoft\\DirectX\\UserGpuPreferences", | |
| process.getProcess().info().command().orElseThrow() | |
| ); | |
| CompletableFuture.runAsync(() -> { | |
| try { | |
| Thread.sleep(5000L); | |
| } catch (InterruptedException e) { | |
| Thread.currentThread().interrupt(); | |
| return; | |
| } | |
| WinReg.INSTANCE.deleteValue( | |
| WinReg.HKEY.HKEY_CURRENT_USER, | |
| "Software\\Microsoft\\DirectX\\UserGpuPreferences", | |
| process.getProcess().info().command().orElseThrow() | |
| ); | |
| }); |
No description provided.