Skip to content

[Bug]: Server preview browser is laggy: always launched with --disable-gpu and device scale 2 #26523

Open

Description

@nitedani

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Run the T3 server on a Linux host that has a GPU. Here: Ryzen 9 5950X (32 threads), RTX 3080 Ti with /dev/dri/renderD128 available, client 0.2 ms away on gigabit.
  2. Open a thread's server preview browser on a normal web app, such as a React dashboard with a long table.
  3. Scroll or interact with the page.

Expected behavior

The preview stays smooth. The page renders at 60 fps on that host, and the client link has plenty of headroom.

Actual behavior

The preview is laggy enough to be unusable for interactive work.

The network is not the cause. Between client and server I measured 0.2 ms RTT and 897 Mbit/s over Tailscale on a direct path (948 Mbit/s on the LAN). The server is mostly idle (load ~4 of 32).

The limit is on the capture side. ServerBrowserContexts.launchOptions() always passes --disable-gpu and --force-device-scale-factor=2, so every frame is rasterized in software (SwiftShader) at 4× the pixels, then JPEG-encoded.

I ran a standalone benchmark on the server itself, with no network involved. It used the same chrome-headless-shell 156.0.8078.70 that T3 downloads, a 1440×900 viewport and Page.startScreencast (JPEG, quality 80), with each frame acked on receipt. For 10 s the page scrolled 40 px on every requestAnimationFrame:

Launch flags Screencast fps KB/frame Mbit/s Server CPU
as T3 launches now (--disable-gpu, SwiftShader, scale 2) 30.8 348 88 2.8 cores
GPU (--use-gl=angle --use-angle=gl-egl --ignore-gpu-blocklist --enable-gpu), scale 2 54.0 347 153 8.8 cores
GPU, scale 1 60.0 136 67 2.8 cores
  • The page itself paints at 60 fps. The loss is in software raster at scale 2.
  • With the GPU flags, WebGL reports the NVIDIA renderer, so the GPU path works in this headless shell.

Impact

The server preview browser is the main way to look at and drive a dev app from a remote T3 client. On this hardware it is too laggy to use for clicking through a UI. The only alternative is opening the app directly in a local browser, which defeats the feature.

Version or commit

T3 0.1.4-nightly.20261027.3796. chrome-headless-shell 156.0.8078.70 (linux64).

Environment

  • Server: Linux LXC container on Proxmox, NVIDIA driver with /dev/dri/renderD128 passed through.
  • Client: Chrome on Windows 11, connected over Tailscale (direct path).

Possible fix

  • Don't force --disable-gpu when the host has a usable GPU. This could be an operator opt-in env, like T3CODE_SERVER_BROWSER_SANDBOX, or a detected default.
  • Make the device scale factor configurable. With the GPU, scale 1 reaches 60 fps at the same CPU as today and 60% smaller frames.

I'm happy to test a build.

Workaround

None on the server: the flags are hard-coded and there is no env override. Opening the app in a local browser works.

Activity

  1. nitedani commented on Oct 28, 2026

    @nitedani
    Author

    Measured on the same host: 1440×900, 10 s, page scrolled 40 px per requestAnimationFrame (60 changes/s), no network.

    Path fps vs today Stream Server CPU NVENC
    today: CDP screencast, --disable-gpu, scale 2 30.8 — 88 Mbit/s 2.8 cores —
    screencast, GPU raster, scale 2 54.0 +75% 153 Mbit/s 8.8 cores —
    screencast, GPU raster, scale 1 60.0 +95% 67 Mbit/s 2.8 cores —
    tab capture + WebRTC, H.264 (CPU encode) 58.2 +89% 0.7 Mbit/s 5.4 cores* —
    tab capture + WebRTC, AV1 (CPU encode) 58.8 +91% 1.8 Mbit/s 6.6 cores* —
    Xvfb + ffmpeg, libx264 59.9 +94% 5.4 Mbit/s 2.3 cores —
    Xvfb + ffmpeg, h264_nvenc 59.9 +94% 6.9 Mbit/s 1.7 cores 4.6%
    Xvfb + ffmpeg, hevc_nvenc 60.0 +95% 3.4 Mbit/s 1.6 cores 4.3%

    * The WebRTC loopback also decodes the stream in the same browser.

    • Cheapest fix: drop --disable-gpu and use scale 1. Same CPU as today, 60 fps.
    • Tab capture: works with --headless=new --auto-accept-this-tab-capture. chrome-headless-shell rejects getDisplayMedia with NotSupportedError.
    • GPU encode: Chrome on Linux has no NVENC path; it only hardware-encodes through VA-API. NVENC through ffmpeg or GStreamer works, at about 5% of the encoder.
    • Transport: the client already has the device stream's RTCPeerConnection path to carry a video stream.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions