Skip to content

Avoid GL readbacks for perf #3558

Open
PaulHax wants to merge 2 commits into
Kitware:masterfrom
PaulHax:gl-readback-fixes
Open

Avoid GL readbacks for perf #3558
PaulHax wants to merge 2 commits into
Kitware:masterfrom
PaulHax:gl-readback-fixes

Conversation

@PaulHax

@PaulHax PaulHax commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Two hot-path gl.getParameter readbacks of dynamic GL state removed.

Context

Reading dynamic state back is a synchronization point with the GPU process. On an uncontended context browsers answer from CPU-side state and the query is nearly free; standalone vtk.js scenes show no measurable change from this PR. But when vtk.js shares a context with a busy host (e.g. rendering inside a MapLibre custom layer), Chromium answers each query with a GPU-process round-trip (~0.2 ms), and the first query of the frame additionally blocks until the host's queued GPU work drains (20-45 ms measured mid-frame over a terrain-heavy map, spikes past 100 ms). A per-draw query keeps re-paying smaller stalls as each draw refills the queue.

Demo, no vtk.js involved (gl-readback-stall-demo branch, one self-contained HTML file; serve it and open): a MapLibre terrain page whose custom layer just issues N × getParameter(DEPTH_WRITEMASK) per frame, the pattern vtk.js's draw path had. On Chromium (ANGLE D3D12): N=0 → 30 fps; N=200, i.e. a 200-actor scene before this PR → 12 fps, with the first query of each frame costing 43 ms p50 / 137 ms max.

Changes

  • Helper: drawArrays read DEPTH_WRITEMASK unconditionally on every draw call, but only the point-picking pass uses the value. The readback (and restore) is now scoped to pointPicking; ordinary rendering issues no query.
  • Framebuffer: saveCurrentBindings queried FRAMEBUFFER_BINDING on every save (e.g. each ForwardPass zbuffer capture). vtkOpenGLRenderWindow now carries a JS-side mirror (set/getFramebufferBinding, gated by an explicit getFramebufferBindingKnown() flag). The mirror is inert until first seeded, so stock behavior is byte-identical. Once a caller seeds it (e.g. an embedding host that knows what it bound), saves use the mirror and vtkFramebuffer bind/restore keep it in sync. A unit test covers both modes, including nested save/bind/restore through the mirror.

Results

A/B benchmark (200-actor scene in a MapLibre terrain custom layer, continuous camera motion, Chromium/ANGLE D3D12; harness pairs this branch with the external-context render window follow-up): mean vtk render time 79 ms → 14 ms per frame, whole-frame 91 ms → 35 ms (11 → 29 fps). The measured win is dominated by the per-draw Helper readback; the Framebuffer mirror removes the queries that remain once an embedding host declares its framebuffer (any single remaining query inherits the whole stall), so the steady-state render path can issue zero readbacks.

PaulHax added 2 commits July 16, 2026 13:41
drawArrays read DEPTH_WRITEMASK back with gl.getParameter on every draw
call, but the value is only used to save/restore the depth mask around
the pointPicking path. Each such readback is a synchronous CPU/GPU sync
point, so ordinary rendering paid one pipeline stall per drawn polydata
for a value it never used. Scope the readback (and the restore) to
pointPicking.
…ave/restore

saveCurrentBindings read the binding back with
gl.getParameter(FRAMEBUFFER_BINDING), a synchronous CPU/GPU sync point,
on every save, e.g. once per ForwardPass zbuffer capture.

OpenGLRenderWindow now carries a JS-side mirror of the raw binding
(setFramebufferBinding/getFramebufferBinding). It is inert (undefined)
by default, so stock behavior is unchanged. Once a caller seeds it
(e.g. a host embedding vtk.js in its own context, which knows what it
bound), saves use the mirror instead of querying, and vtkFramebuffer
bind/restore keep it in sync.

Covered by a test asserting both modes: unseeded saves still query GL,
seeded saves issue zero FRAMEBUFFER_BINDING queries and the mirror
follows nested bind/save/restore.
@PaulHax PaulHax changed the title Gl readback fixes GL readback fixes Jul 16, 2026
@PaulHax PaulHax changed the title GL readback fixes Avoid GL readbacks for perf Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant