diff --git a/apps/desktop/electron/bootstrap-platform.cjs b/apps/desktop/electron/bootstrap-platform.cjs index 2058d90a8f6..e217a4c6fc8 100644 --- a/apps/desktop/electron/bootstrap-platform.cjs +++ b/apps/desktop/electron/bootstrap-platform.cjs @@ -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 ":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') { diff --git a/apps/desktop/electron/bootstrap-platform.test.cjs b/apps/desktop/electron/bootstrap-platform.test.cjs index 3a401564cfa..2d3ba37636a 100644 --- a/apps/desktop/electron/bootstrap-platform.test.cjs +++ b/apps/desktop/electron/bootstrap-platform.test.cjs @@ -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). diff --git a/apps/desktop/electron/main.cjs b/apps/desktop/electron/main.cjs index 35e8a942347..4c6d12b7c57 100644 --- a/apps/desktop/electron/main.cjs +++ b/apps/desktop/electron/main.cjs @@ -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