Repository navigation
Mesh: Add .setDrawRange, support shared scene geometry - #35112
donmccurdy wants to merge 1 commit into
Conversation
📦 Bundle sizeFull ESM build including core, minified and gzipped.
🌳 Bundle size after tree-shakingMinimal build including a renderer, camera, empty scene, and dependencies.
|
🖼️ E2E screenshot tests❌ 1 example(s) failed (full artifacts).
|
|
Doesn't today's API already cover the use case? You can create N BufferGeometry objects that share the same attributes and index, each with its own draw range. Can you please inlcude this setup in your performance test? |
|
Also a separate draw range API on This will be quite a large change because a lot of code relies on the Also, how would you implement view frustum culling with a single combined |



Summary
As discussed in #17089, scenes with many meshes incur overhead from rebinding WebGL buffers for each mesh, because each BufferGeometry is uploaded to the GPU as (at best!) a single WebGL buffer. A few earlier PRs (now stalled) tried to reduce the number of WebGL buffers by allowing BufferGeometries to share BufferAttributes and InterleavedBuffers.
I'm hoping solve the problem from the other direction instead: given a single BufferGeometry containing a larger vertex stream, allow any Mesh to draw any vertex range of that geometry, using a per-mesh
drawRangeproperty. By reusing a single BufferGeometry (and a single WebGL buffer) the cost of binding WebGL buffers can be mostly avoided.If both
mesh.drawRangeandgeometry.drawRangeare set, the intersection of the two ranges is used (more an arbitrary choice than a preference, open to change this).Implications for Loaders
When loading 3D models from more optimized exporters (see: gltfpack or glTF Transform) it should be possible (in a future PR) for GLTFLoader to use a single shared BufferGeometry, without any runtime work to merge many small meshes. Users of glTF could then get an optimized scene with just one BufferGeometry, regardless of how many objects were in the source model. Some care is required on the part of the exporter to format buffers for this purpose.
Comparison to BatchedMesh
BatchedMesh is still going to be more performant; it eliminates more work than just binding WebGL buffers. However, for draw-call-heavy applications that can't use BatchedMesh, sharing a BufferGeometry for many objects in a scene provides a major performance optimization for relatively little effort.
If we also update loaders as described above, users already running gltfpack or glTF Transform may receive 30–40% of BatchedMesh's benefits with no code changes at all.
Performance results
Measured by generating 1000 distinct geometries, then merging them with
BufferGeometryUtils.mergeGeometries. The scene was rendered with the 1000 distinct geometries, and then with a single merged geometry for the entire scene; in both cases the scene contained 1000 meshes with unique transforms:After each render, I read back one pixel (WebGL) or awaited
renderer.backend.device.queue.onSubmittedWorkDone()(WebGPU).Results formatted as
<separate geometry median ms> → <shared geometry median ms>.(WebGPU)
(WebGL)
Devices: Apple MacBook Pro 2021 (M1 Pro), and iPhone 16e.
I had some trouble collecting accurate timing on WebGPU in Firefox and Safari on iOS, but haven't spent time investigating that. My results on iOS are not as strongly positive, for whatever reason.
Related