fix(signal-viewer): activate the WebGL2 waveform path (mount-order race pinned it to Canvas2D)#32
Merged
Merged
Conversation
… renderer The hybrid renderer was constructed as soon as the BASE chrome canvas mounted, tolerating a null waveform canvas. Because the base <canvas> precedes the waveform <canvas> in the DOM, its ref callback fires first, so the renderer was always built with waveform=null — pinning every session to the Canvas2D fallback for the view's lifetime (the rendererRef guard blocks reconstruction once the waveform canvas arrives). WebGL2 therefore never activated in the real Signal Viewer, even though the renderer itself is correct and the fidelity gate (which constructs the renderers directly) passed. Require both canvases before constructing.
…ebGL waveform canvas Regression guard for the WebGL2-activation bug where tryInitRenderer constructed HybridSignalRenderer as soon as the base chrome canvas mounted (`if (!base) return;`), tolerating a null waveform canvas and pinning the renderer to the Canvas2D fallback forever. Mounts the real SignalViewer with minimal mocked session data and a spy HybridSignalRenderer that records constructor args, then asserts the second argument (the WebGL waveform layer) is a non-null HTMLCanvasElement distinct from the base canvas. Red against the pre-fix `if (!base) return;`, green against `if (!base || !waveform) return;`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CzEJ1kUhwobqVTnVusLcb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The WebGL2 hybrid renderer shipped in #31 never actually activated in the live Signal Viewer — every session silently ran the Canvas2D fallback, so pan/zoom performance was unchanged. A production Edge trace (scroll + zoom) showed 100% old Canvas2D draw calls (
drawLine/drawEnvelope/renderDuringPan) and the same full-canvas GPU texture re-upload (~270–466 MB/frame, 135 dropped frames) — the exact signature of the fallback path. The shipped bundle does contain the WebGL code (confirmedu_clipScale/drawArraysInstanced/WebGLUnavailablein the loaded chunk), so this is a runtime wiring failure, not a stale cache or a missing build.Root cause — a deterministic mount-order race
tryInitRendererinSignalViewer.tsxconstructed theHybridSignalRendereras soon as the base chrome canvas mounted, tolerating a null waveform canvas (if (!base) return;). The base<canvas>precedes the transparent WebGL waveform<canvas>in the DOM, so its ref-callback fires first → the renderer was always built asnew HybridSignalRenderer(base, /* waveform */ null, …), which pins it to the Canvas2D fallback for the view's lifetime (therendererRef.currentguard then blocks reconstruction when the waveform canvas mounts a moment later). WebGL2 therefore never activated for anyone.Fix
Require both canvases before constructing (
if (!base || !waveform) return;). The waveform canvas is rendered unconditionally and mounts in the same commit, so this never deadlocks; if WebGL2 is genuinely unavailable at runtime,HybridSignalRendererstill falls back internally as designed.Why CI didn't catch it (and the new guard test)
The fidelity gate and unit tests construct the renderers directly (via a harness), proving they're correct — but nothing asserted that the real
SignalVieweractually activates WebGL. This PR adds that missing seam:SignalViewer.webglWiring.test.tsxrenders the real viewer and assertsHybridSignalRendereris constructed with a non-nullHTMLCanvasElementas the waveform argument. Verified red on the pre-fix guard (expected null not to be null), green with the fix.Tests
After this merges and deploys, a hard-reload should show GPU-accelerated pan/zoom (the renderer from #31 was correct all along — it just was never switched on).
🤖 Generated with Claude Code
https://claude.ai/code/session_012CzEJ1kUhwobqVTnVusLcb
Generated by Claude Code