fix(desktop): don't treat WSLg as a remote display

WSLg renders Linux GUIs locally through a vGPU surface rather than
shipping frames over the wire, so it doesn't show the remote-compositor
flicker — confirmed by a WSL user seeing zero flickering. Drop the WSL
branch from detectRemoteDisplay so WSLg keeps hardware acceleration;
detection now covers only genuinely-remote displays (SSH X11 forwarding,
VNC, RDP). The HERMES_DESKTOP_DISABLE_GPU override still works for anyone
who does hit it.
This commit is contained in:
Brooklyn Nicholson 2026-06-03 00:42:05 -05:00
parent 6a2909fe5a
commit d0ea4caf7f
3 changed files with 25 additions and 28 deletions

View file

@ -51,7 +51,6 @@ const GPU_OVERRIDE_OFF = new Set(['0', 'false', 'no', 'off'])
function detectRemoteDisplay(options = {}) {
const env = options.env ?? process.env
const platform = options.platform ?? process.platform
const isWsl = options.isWsl ?? isWslEnvironment(env, platform)
const override = String(env.HERMES_DESKTOP_DISABLE_GPU || '').trim().toLowerCase()
if (GPU_OVERRIDE_ON.has(override)) return 'override (HERMES_DESKTOP_DISABLE_GPU)'
@ -64,12 +63,12 @@ function detectRemoteDisplay(options = {}) {
if (platform === 'linux') {
// X11 forwarding sets DISPLAY to "<host>:N" (e.g. "localhost:10.0"); a
// local X server is ":0"/":1" with no host part before the colon.
// NB: WSLg deliberately isn't treated as remote — it reports
// GPU-accelerated vGPU surfaces locally and doesn't show the flicker.
const display = String(env.DISPLAY || '')
if (display.includes(':') && display.split(':')[0]) {
return `x11-forwarding (DISPLAY=${display})`
}
// WSLg pipes the GUI through an RDP/Wayland bridge — same flicker profile.
if (isWsl) return 'wslg'
}
if (platform === 'win32') {

View file

@ -35,12 +35,19 @@ test('bundledRuntimeImportCheck selects platform-specific import checks', () =>
test('detectRemoteDisplay keeps GPU on for local sessions', () => {
// Plain local X11, Wayland, native Windows, native macOS — no remote signal.
assert.equal(detectRemoteDisplay({ env: { DISPLAY: ':0' }, platform: 'linux', isWsl: false }), null)
assert.equal(detectRemoteDisplay({ env: { WAYLAND_DISPLAY: 'wayland-0' }, platform: 'linux', isWsl: false }), null)
assert.equal(detectRemoteDisplay({ env: { DISPLAY: ':0' }, platform: 'linux' }), null)
assert.equal(detectRemoteDisplay({ env: { WAYLAND_DISPLAY: 'wayland-0' }, platform: 'linux' }), null)
assert.equal(detectRemoteDisplay({ env: { SESSIONNAME: 'Console' }, platform: 'win32' }), null)
assert.equal(detectRemoteDisplay({ env: {}, platform: 'darwin' }), null)
})
test('detectRemoteDisplay does not treat WSLg as remote', () => {
// WSLg renders locally via vGPU and doesn't show the flicker, so a WSL
// session with a local DISPLAY keeps hardware acceleration on.
assert.equal(detectRemoteDisplay({ env: { WSL_DISTRO_NAME: 'Ubuntu', DISPLAY: ':0' }, platform: 'linux' }), null)
assert.equal(detectRemoteDisplay({ env: { WSL_INTEROP: '/run/WSL/1_interop', DISPLAY: ':0' }, platform: 'linux' }), null)
})
test('detectRemoteDisplay flags SSH sessions on any platform', () => {
assert.equal(detectRemoteDisplay({ env: { SSH_CONNECTION: '1.2.3.4 5 6.7.8.9 22' }, platform: 'linux' }), 'ssh-session')
assert.equal(detectRemoteDisplay({ env: { SSH_CLIENT: '1.2.3.4 5 22' }, platform: 'darwin' }), 'ssh-session')
@ -48,29 +55,19 @@ test('detectRemoteDisplay flags SSH sessions on any platform', () => {
})
test('detectRemoteDisplay flags forwarded X11 displays but not local ones', () => {
assert.match(
String(detectRemoteDisplay({ env: { DISPLAY: 'localhost:10.0' }, platform: 'linux', isWsl: false })),
/x11-forwarding/
)
assert.match(
String(detectRemoteDisplay({ env: { DISPLAY: '192.168.1.5:0' }, platform: 'linux', isWsl: false })),
/x11-forwarding/
)
assert.equal(detectRemoteDisplay({ env: { DISPLAY: ':1' }, platform: 'linux', isWsl: false }), null)
assert.match(String(detectRemoteDisplay({ env: { DISPLAY: 'localhost:10.0' }, platform: 'linux' })), /x11-forwarding/)
assert.match(String(detectRemoteDisplay({ env: { DISPLAY: '192.168.1.5:0' }, platform: 'linux' })), /x11-forwarding/)
assert.equal(detectRemoteDisplay({ env: { DISPLAY: ':1' }, platform: 'linux' }), null)
})
test('detectRemoteDisplay flags WSLg and RDP', () => {
assert.equal(detectRemoteDisplay({ env: { DISPLAY: ':0' }, platform: 'linux', isWsl: true }), 'wslg')
assert.match(
String(detectRemoteDisplay({ env: { SESSIONNAME: 'RDP-Tcp#7' }, platform: 'win32' })),
/^rdp/
)
test('detectRemoteDisplay flags RDP sessions', () => {
assert.match(String(detectRemoteDisplay({ env: { SESSIONNAME: 'RDP-Tcp#7' }, platform: 'win32' })), /^rdp/)
})
test('detectRemoteDisplay honors the HERMES_DESKTOP_DISABLE_GPU override both ways', () => {
// Force-on even on a local display.
assert.match(
String(detectRemoteDisplay({ env: { HERMES_DESKTOP_DISABLE_GPU: '1', DISPLAY: ':0' }, platform: 'linux', isWsl: false })),
String(detectRemoteDisplay({ env: { HERMES_DESKTOP_DISABLE_GPU: '1', DISPLAY: ':0' }, platform: 'linux' })),
/override/
)
// Force-off even over SSH (escape hatch when a remote display has working accel).

View file

@ -74,15 +74,16 @@ const IS_WINDOWS = process.platform === 'win32'
const IS_WSL = isWslEnvironment()
const APP_ROOT = app.getAppPath()
// Remote displays (SSH X11 forwarding, VNC, RDP, WSLg) make Chromium's GPU
// Remote displays (SSH X11 forwarding, VNC, RDP) make Chromium's GPU
// compositor flicker — accelerated layers can't be presented cleanly over the
// wire, so the window flashes during scroll/streaming/animation. Local
// Windows/macOS composite on the GPU and never see it. Fall back to software
// rendering when a remote display is detected; it's rock-steady over the wire
// and the CPU cost is negligible next to the connection's latency. Must run
// before app `ready` — these switches only apply pre-launch. Override with
// HERMES_DESKTOP_DISABLE_GPU (1/true → always disable, 0/false → keep GPU on).
const REMOTE_DISPLAY_REASON = detectRemoteDisplay({ isWsl: IS_WSL })
// Windows/macOS (and WSLg, which renders locally via vGPU) composite on the
// GPU and never see it. Fall back to software rendering when a remote display
// is detected; it's rock-steady over the wire and the CPU cost is negligible
// next to the connection's latency. Must run before app `ready` — these
// switches only apply pre-launch. Override with HERMES_DESKTOP_DISABLE_GPU
// (1/true → always disable, 0/false → keep GPU on).
const REMOTE_DISPLAY_REASON = detectRemoteDisplay()
if (REMOTE_DISPLAY_REASON) {
app.disableHardwareAcceleration()
// Belt-and-suspenders for X11/VNC, where the Viz compositor can still glitch