Skip to content

Avoid redundant OpenGL state changes during a draw - #47

Open
relh wants to merge 1 commit into
treeform:masterfrom
relh:perf/pbr-caching-core
Open

Avoid redundant OpenGL state changes during a draw#47
relh wants to merge 1 commit into
treeform:masterfrom
relh:perf/pbr-caching-core

Conversation

@relh

@relh relh commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What changed

The OpenGL PBR renderer now skips unchanged uniforms, material settings, texture binds, and enable state within one ctx.draw call. State is reset at the draw boundary, so the public API and behavior stay the same.

Result

In a dense scene, an opaque primitive using the same material drops from about 55 GL calls to about 5.

Checks

Upstream CI passes on every backend.

renderPbrPrimitive re-uploaded every uniform, rebound all texture units,
and re-toggled blend/depth/cull for every primitive, then reset that
state after each draw - ~55 GL calls per primitive. A glTF model with
many primitives (the common case) pays this per primitive even when the
frame constants and material are unchanged.

This caches within one ctx.draw:
- Uniform values are per-program GL state, so a PbrPassValues shadow
  dirty-checks the frame-level block (view/proj/lightSpace, lighting,
  fog, camera, tint, debugView, env) and uploads only on change.
- The seven sampler-unit uniforms move to setupPbr (set once).
- Material uniforms upload only when the material or its version changes;
  texture binds key on the actual GL id (so a late texture upload is
  still caught) with an epoch guard against ids recycled by
  glDeleteTextures.
- Blend/depth-mask/cull/front-face go through an enable-state shadow.

The binding/enable caches reset at the start of each ctx.draw (foreign
GL between draws may have changed them) and after the library's own
shadow/skybox passes; the uniform value cache is per-program and
persists. Submission order, per-draw back-to-front blended flush, and
post-draw GL state are unchanged, so output is identical. No API change.

Measured on a dense scene (~1300 primitives, macOS GL): per-primitive
uniform+bind cost drops from the bulk of renderPbrPrimitive's self time
to noise; ~5 GL calls for an opaque same-material primitive vs ~55.
@relh relh changed the title Cache PBR uniform values and GL binds within a draw Avoid redundant OpenGL state changes during a draw Jul 22, 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