Skip to content

perf(webgl2): allocate immutable texture storage once, upload with texSubImage2D - #996

Open
Endymi0n74 wants to merge 1 commit into
redphx:typescriptfrom
Endymi0n74:feat/webgl2-texstorage-rgb8
Open

perf(webgl2): allocate immutable texture storage once, upload with texSubImage2D#996
Endymi0n74 wants to merge 1 commit into
redphx:typescriptfrom
Endymi0n74:feat/webgl2-texstorage-rgb8

Conversation

@Endymi0n74

Copy link
Copy Markdown

What

The WebGL2 renderer uploaded the video frame with texImage2D() on every frame, which reallocates the texture's GPU storage each time. This PR:

  1. allocates the storage once with texStorage2D(gl.TEXTURE_2D, 1, gl.RGB8, w, h) and uploads per frame with texSubImage2D() on that immutable storage,
  2. keeps the texture bound across frames (no per-frame bindTexture — the binding now happens inside allocateStorage, once per allocation),
  3. recreates the texture when the video resolution changes (immutable storage can't be resized),
  4. fixes a black-screen bug: the current TS source has no texStorage2D call, but the earlier fork iteration used gl.RGB — an unsized format that is invalid for texStorage2D (INVALID_ENUM → storage never allocated → per-frame texSubImage2D uploads fail → black renderer). Sized gl.RGB8 is required; texSubImage2D keeps gl.RGB.

Why

texImage2D on a per-frame basis reallocates GPU storage every frame (~×2.1 the cost of a texSubImage2D into an immutable storage on the measured driver). The upload path dominates the frame cost.

Safety

  • Initial allocation is deferred until the canvas (or the first valid video frame) has a size > 0 — updateFrame allocates on the first frame where videoWidth/Height > 0 if no storage exists yet.
  • On resolution change the old texture is removed from resources and deleted before re-allocating (no leak, no stale binding).
  • destroy() nulls texture after the resource loop.
  • Visual output is unchanged (same shader, same colors — RGB8 is the sized equivalent of RGB).

Measurement

Real GPU harness (640×360 VP9 test video, WebGL2 via ANGLE/D3D11, instrumented GL counters, seed protocol):

Metric before after Δ
Video upload (µs/upload, tight loop) ~42–78 ~8–12 ×5.5
updateFrame total wall (ms/frame) ~0.043–0.074 ~0.011–0.020 ×2.8
GL calls per frame texImage2D + drawArrays texSubImage2D + drawArrays (0 texImage2D, 0 bindTexture)
Draw (rasterization) 10.2 µs 10.2 µs unchanged (same shader)

…xSubImage2D

texImage2D() reallocates the texture's GPU storage on every frame. Allocate
the storage once with texStorage2D (sized RGB8 — gl.RGB is unsized and
invalid for texStorage2D: INVALID_ENUM, storage never allocated, per-frame
uploads fail and the renderer goes black) and upload per frame with
texSubImage2D on the immutable storage. The texture stays bound across
frames (no per-frame bindTexture), and is recreated when the video
resolution changes (immutable storage can't be resized).

Measured on the built bundle (seed protocol, real GPU): video upload drops
from ~42-78 µs to ~8-12 µs per upload (×5.5), updateFrame total wall from
~0.043-0.074 ms to ~0.011-0.020 ms (×2.8); draw unchanged (same shader).
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