Skip to content

add fast path for compute bounding sphere/box from attribute data for common case (~2.5x speedup) - #34204

Open
bhouston wants to merge 1 commit into
mrdoob:devfrom
bhouston:faster-computeBoundingSphere
Open

add fast path for compute bounding sphere/box from attribute data for common case (~2.5x speedup)#34204
bhouston wants to merge 1 commit into
mrdoob:devfrom
bhouston:faster-computeBoundingSphere

Conversation

@bhouston

Copy link
Copy Markdown
Contributor

When profiling the new Gaussian Splatting code I noticed that on Safari, one of the slowest steps was computing the bounding sphere from the BufferGeometry. So I optimized that.

The key insight is that generally BufferGeometry attribute data for positions is not interleaved and it is usually 32 or 64 bit floats. Thus we can avoid accessing the attribute data via its accessors and just access it directly in the majority of cases. This removes indirection on every single data access.

The performance for the fast path is pretty good when the assumptions are meet, otherwise it is identical to before:

vertices old (ms) new (ms) speedup
10,000 0.155 0.024 6.5x
100,000 0.539 0.224 2.4x
1,000,000 5.54 2.29 2.4x
5,000,000 30.4 11.0 2.8x

I've applied this improvement to both BufferGeometry computeBoundingSphere and the calculation of bounding boxes in Box3. There is a benchmark to confirm this improvement.

@github-actions

Copy link
Copy Markdown

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 366.31
86.86
366.31
86.86
+0 B
+0 B
WebGPU 696.95
192.4
696.95
192.4
+0 B
+0 B
WebGPU Nodes 694.94
192.1
694.94
192.1
+0 B
+0 B

🌳 Bundle size after tree-shaking

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

Before After Diff
WebGL 509.7
123.83
510.37
124.05
+670 B
+224 B
WebGPU 771.58
207.3
772.25
207.52
+670 B
+224 B
WebGPU Nodes 720.54
194.63
721.21
194.86
+670 B
+228 B

daltino

This comment was marked as spam.

const array = position.array;

_vector.fromBufferAttribute( position, i );
if ( position.isInterleavedBufferAttribute !== true && position.normalized === false &&

@Mugen87 Mugen87 Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we create a new method on buffer attribute level called supportsDirectAccess()?

This method would decide on attribute level if a direct access is allowed. The policy would be "non-normalized, non-interleaved, float storage".

So BufferAttribute should check:

supportsDirectAccess() {

	const array = this.array;

	return this.normalized === false &&
		( array instanceof Float32Array || array instanceof Float64Array ||
			( typeof Float16Array !== 'undefined' && array instanceof Float16Array ) );

}

InterleavedBufferAttribute and GLBufferAttribute both return false.

@@ -0,0 +1,131 @@
/**
* Benchmark: BufferGeometry.computeBoundingSphere() fast path vs the previous

@Mugen87 Mugen87 Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we have removed the earlier benchmark suite on purpose. They were not in used because developers often applied benchmarks via jsbench or jsfiddle which are easier to share and execute on various systems like mobile devices.

I would prefer not to adding test/benchmark back.

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.

3 participants