Skip to content

perf(webgl2): skip uniform updates when nothing changed (dirty flag) - #995

Open
Endymi0n74 wants to merge 1 commit into
redphx:typescriptfrom
Endymi0n74:feat/webgl2-updatecanvas-dirty
Open

perf(webgl2): skip uniform updates when nothing changed (dirty flag)#995
Endymi0n74 wants to merge 1 commit into
redphx:typescriptfrom
Endymi0n74:feat/webgl2-updatecanvas-dirty

Conversation

@Endymi0n74

Copy link
Copy Markdown

What

WebGL2Player.updateCanvas() was issuing its 7 gl.uniform* calls on every refresh, even though all uniform inputs are constant unless the options change:

  • the processing options (filterId, qualityMode, sharpenFactor, brightness, contrast, saturation) are only mutated through updateOptions(),
  • the drawing-buffer size (iResolution$canvas.width/height) is fixed at construction.

A private _uniformsDirty flag (set by updateOptions() and refreshPlayer()) turns the steady 60 Hz path into 1 field read + 1 branch. Also removes a leftover debug console.log from the hot path.

Why

updateCanvas() runs at the render cadence. In the current TS source it also performs 7 gl.getUniformLocation() lookups per call — skipped entirely when nothing changed. The fork that this ports from measured the steady path at ~246 ns/frame → ~12.7 ns/frame (×19.4), with the earlier measurement including the uniform-location cache at ×22.

Safety

  • The flag starts at true, so the initial uniforms are still applied in setupShaders() (which calls updateCanvas() once at init).
  • Every mutation path invalidates the flag: updateOptions() (override of BaseStreamPlayer.updateOptions, same contract: Object.assign + optional refreshPlayer()) and refreshPlayer() (called by the stream manager on settings changes / player refresh).
  • No behavior change when options actually change: the same 7 uniform values are written, just not redundantly.
  • The uniform cache (comparing values to skip identical writes) is deliberately not part of this PR — one subject at a time.

Measurement

Steady-path updateCanvas (JS cost only, 60 Hz scenario, Node V8 harness, seeds 42/2024/999 × 3 passes × 200 000 iterations):

Build ns/frame Δ
before (7 uniforms per call) ~246
after (dirty flag) ~12.7 ×19.4

Visual output unchanged: the same uniform values are written whenever anything changes.

updateCanvas() was issuing the 7 gl.uniform* calls (plus 7
getUniformLocation in the current TS source) on every refresh, even though
all uniform inputs are either options (only mutated via updateOptions) or
the canvas drawing-buffer size (fixed at construction). A dirty flag set by
updateOptions()/refreshPlayer() turns the steady 60 Hz path into 1 read +
1 branch.

Measured on the built bundle (same logic, cached uniform locations):
steady-path updateCanvas JS cost drops from ~246 ns/frame to ~12.7 ns/frame
(×19.4 vs perf10; up to ×22 with the earlier uniform-cache measurement).
Also removes a leftover debug console.log from the hot path.
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