Builds and tracing

Measured build times, tree quality and ray rates, with how each number was taken.

Every number here says where it was measured. The package has two speed targets, the RTX 3060 Laptop and the Apple M5 Pro. The Intel UHD runs every check for correctness only. Numbers from the Apple M5 are indicative: its clocks drift by up to about 30% within a run, so those benches report ratios taken within one round, not absolute rates.

Method

  • Rays: a grid of probes fills the scene's bounds, and each probe fires 256 rays in random directions from a fixed seed. Any-hit rays go from each closest hit toward a fixed direction. The rays are generated once and stored in a buffer, so every tracer gets the same input.
  • Timing: GPU timestamps around the trace dispatch, the median of several runs after warm-ups. Without timestamps, a fence-based wall time over 20 dispatches, labelled as such.
  • The GPU is loaded and clocks are checked. A run counts only if it started cool, fewer than 5% of its samples were thermally throttled, and it had enough samples ("the clock rule").
  • three-mesh-bvh runs as a three WebGPURenderer compute node reading the same ray buffer, its per-mesh trees built with { strategy: SAH } explicitly (its default is CENTER).
  • The pages are tests/gpu/pages/trace-bench*; results go in bench/results/trace.csv.

Sponza on the RTX 3060 Laptop

5,744,384 triangles in 132 meshes. Headless Chrome 156 on Windows.

WhatNumberConditions
GPU H-PLOC build, whole scene46.6 msMedian of the 2 runs valid under the clock rule, loaded SM clock about 1,000 MHz, power-limited
CPU tinybvh build, whole scene1.6-1.9 sIn a browser worker, single-threaded; medians of 3 runs per session
GPU LBVH build, whole scene31.2 msMedian of 3, before the clock rule existed
H-PLOC tree qualitySAH cost 1.032x tinybvh'sLower is better; the LBVH's is 1.217x
Trace speed on H-PLOC treesclosest-hit 0.95x, any-hit 1.02x of tinybvh's treesSame rays, both tracers in each run, median of 3. The runs were flagged low-clock, so the ratio is more reliable than the rates
Trace speed on LBVH treesclosest-hit 0.82x, any-hit 0.80xAs above
Closest-hit rate, tinybvh trees104.3 Mrays/s, 1.20x three-mesh-bvhMedian of 3
Closest-hit rate, Intel UHD12.8 Mrays/s, 1.97x three-mesh-bvhMedian of 3; correctness device, for scale only
GPU top-level refit, 10,000 moving instancesabout 0.09 ms (0.086-0.091)The refit's own page, two measurements

three-mesh-bvh's trees converted to our layout traced at 96.7 Mrays/s and took 7.4 s to build, against 101.6 Mrays/s and 5.3 s for tinybvh's, in the same session. These runs predate the 2026-11 levers: instanced trees, f32 nodes.

The 2026-11 levers on the RTX 3060 Laptop

Sponza and Bistro interior surface-ray pages (trace-bench-surface-*), Windows, headless Chrome 157, 2026-11-13, three runs each. Each lever is timed in paired rounds against the layout before it (instanced, f32 nodes, records, the 40-byte record, anyHitOrder: 'far'), with the order reversed every other round. A figure is the median over the runs of each run's median-of-rounds ratio; the range covers surface and air rays, instanced and merged trees. Above 1 is faster.

LeverSponzaBistro interiorDefault now
Merged static instances, over instancedclosest 1.44-1.67x, any 1.24-1.48xclosest 1.19-1.20x, any 1.16-1.35xon
Quantized nodes, over f32closest 1.04-1.22x, any 0.98-1.03xclosest 1.02-1.08x, any 0.98-1.01xon
Indexed leaves, vec4, over records0.92-0.97x0.92-0.97xoff
Indexed leaves, packed, over records0.89-0.95x0.82-0.88xoff
40-byte hit record, over 24-byte0.88-0.92x0.93-0.94x40-byte
anyHitOrder: 'larger', over 'far'instanced 1.11-1.45x, merged air 1.07x, merged surface 0.87xinstanced 1.18-1.48x, merged air 1.03x, merged surface 0.80x'far'

Compact against full record, indexed against records and every any-hit order against far agreed on every ray. No quantized t differed from the f32 one. Merged against instanced: closest hits named the instanced tree's instance and triangle on 99.949-99.996% of rays; the rest are near-ties at a shared distance, where the merged walk can pick another instance or triangle. Any hits blocked exactly the same rays.

On the Apple M5 (indicative)

WhatNumberConditions
SceneTrees first trees, Sponza100-190 ms after the first updatescene-trees-sponza page, Chrome, several runs and caps
SceneTrees every tinybvh tree in, Sponza285-370 ms after the first updatethe same
Merged static instances, Sponza surface raysclosest 1.85x, any 1.36x of instancedMerged static instances
GPU batch of 65k raysabout 1.9 ms (35 Mrays/s)three/raycast example, a 100k-triangle knot from outside, timestamps
Path tracerabout 120 Mrays/sthree/pathtracer example, wall time of one dispatch
CPU raycast, 100k triangles692,630 per second firstHitOnlyRaycast, Node 22

The variants' trace speed on the M5 (surface-ray pages, paired rounds with the order reversed, against f32 nodes and records): indexed leaves 0.83-0.99x with vec4 positions and 0.75-0.92x packed; quantized nodes 0.93-0.97x. The 40-byte hit record measured 0.96-1.15x the 24-byte one.

Not measured yet

  • On the RTX 3060: the correctness pages for the 2026-11 work, SceneTrees memory and the build-peak cap on D3D12, shader compile times, and quantized nodes and indexed leaves together.
  • quantizedDecode: 'folded' (the folded decode of quantized nodes, opt-in) on any GPU: the surface-ray pages time it against the exact decode (folded_over_exact); it has only passed the correctness pages on SwiftShader.
  • The Intel UHD has not run the 2026-11 work at all.
  • The comparison suite against three-mesh-bvh and others (compare/) has run on the Apple M5 only.

Reproducing

  • pnpm test:gpu:page trace-bench-sponza (and the other trace-bench-* pages, run by exact name) runs a trace bench on the machine's GPU. Sponza and Bistro need their assets; see tests/gpu/README.md.
  • pnpm test:gpu:page scene-trees-sponza reports SceneTrees load times and memory.
  • node bench/three-raycast.ts, bench/query-parity.ts and bench/closest-point.ts run the CPU benches in Node.

See also