Skip to content

UniformsGroup: Pool per-uniform update-range objects.#33427

Merged
Mugen87 merged 2 commits intomrdoob:devfrom
RenaudRohlinger:perf/uniforms-group-range-pooling
Apr 21, 2026
Merged

UniformsGroup: Pool per-uniform update-range objects.#33427
Mugen87 merged 2 commits intomrdoob:devfrom
RenaudRohlinger:perf/uniforms-group-range-pooling

Conversation

@RenaudRohlinger
Copy link
Copy Markdown
Collaborator

@RenaudRohlinger RenaudRohlinger commented Apr 20, 2026

Related: #33419
Description

UniformsGroup.addUniformUpdateRange allocated a fresh { start, count } object every frame for every uniform whose value changed. Keep the range objects in _updateRangeCache across frames; mark each as added: false in clearUpdateRanges() so the next frame pushes the same cached object onto updateRanges rather than allocating a new one.

Before / after — webgpu_backdrop_water, 10 s, 1 KB sampling, Inspector stripped, 3-run average

metric dev this PR Δ
updateNumber @ UniformsGroup.js:267 (caller) 0.70 KB 0.00 KB −0.70 KB
{ start, count } allocations per frame N/uniform 0 after first use structural
Total sampled heap 408.99 KB 400.75 KB −2.0%

This contribution is funded by Spawn

`addUniformUpdateRange` allocated a fresh `{start, count}` object every
frame, for every uniform whose value changed. On an animated scene with
dozens of per-object uniforms this was hundreds of allocations per
frame — the single largest remaining source of sampled-heap traffic
after the earlier backend fixes.

Keep the range objects in `_updateRangeCache` across frames; mark each
as `added: false` on `clearUpdateRanges()` so the next frame pushes the
same cached object back onto `updateRanges` instead of allocating a
new one.

Measured on webgpu_backdrop_water (Inspector stripped, 10 s, 1 KB
sampling, 3-run average):
  total sampled heap  97.0 KB → 66.5 KB  (-31.5%)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 20, 2026

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 365.3
86.77
365.3
86.77
+0 B
+0 B
WebGPU 638.99
177.4
639.09
177.44
+105 B
+39 B
WebGPU Nodes 637.11
177.11
637.21
177.15
+105 B
+39 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 497.82
121.45
497.82
121.45
+0 B
+0 B
WebGPU 710.84
192.25
710.94
192.29
+105 B
+41 B
WebGPU Nodes 660.06
179.57
660.16
179.61
+105 B
+41 B

Comment thread src/renderers/common/UniformsGroup.js Outdated
Reviewer feedback: the `added` flag attached to each cached range object
muddles the shape that `Buffer._updateRanges` and its downstream
consumers (WebGL/WebGPU upload paths) expect to see — a clean
`{start, count}`.

Move the "already pushed this cycle" membership into a separate
`_addedIndices` Set on the instance. The cached range objects stay a
pure `{start, count}` pair, and `start`/`count` are re-assigned from the
uniform on every `addUniformUpdateRange` call (rather than only on cache
miss), so any in-place mutation downstream — e.g. WebGL's sort/merge of
ranges — cannot leak stale state into the next frame. `clearUpdateRanges`
simplifies to clearing the Set and delegating to `super`.

No behavioral change for consumers; same pooling, cleaner contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@RenaudRohlinger RenaudRohlinger added this to the r185 milestone Apr 20, 2026
@Mugen87 Mugen87 merged commit 3d97098 into mrdoob:dev Apr 21, 2026
15 of 16 checks passed
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.

2 participants