three-blocks-benchmark 0.4.0 → 0.5.0-alpha.384.g6baf5c5aba0f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # three-blocks-benchmark
2
2
 
3
+ ## 0.5.0-alpha.384.g6baf5c5aba0f
4
+
5
+ - Website: Example previews and precompiled shaders now match the current release.
6
+
7
+ - Library: Large PLY splat files now load through bundled workers.
8
+
9
+ - Devtools: Shader checks bound browser startup and shutdown before the next verification.
10
+
11
+ - Library: Baked Motion automatically prefers HEVC and falls back to AVC on every device.
12
+
13
+ - Library: Baked Motion can author narrower depth tracks to reduce package downloads.
14
+
15
+ - Library: Baked Motion bounds tilt warp reprojection so a starved blend goes stale, not stretched.
16
+
17
+ - Packages: Gaussian benchmarks add the three.js GaussianSplat runtime on its matching dev build.
18
+
19
+ - Library: Gaussian splats keep equal-depth ordering stable and avoid duplicate tile staging.
20
+
21
+ - Library: HMR replaces outgoing shader registrations without reporting false conflicts.
22
+
23
+ - Starter: New projects install the current Three Blocks and Devtools releases.
24
+
25
+ - Tools: Exact-count splats no longer drop small mesh objects with dense triangulation.
26
+
27
+ - Library: WaterVolume keeps hydrostatic density stable at high particle counts.
28
+
29
+ - Library: Sphere rasterization ignores inconsistent overflow readbacks instead of rebuilding.
30
+
31
+ - Website: The water surface demo starts at 500K particles, caps at 1M, and opens wider.
32
+
33
+ - Website: Water demos add larger tiers and a tunable floorless monochrome PBR particle view.
34
+
35
+ - Website: Example recording mode hides every interface except the rendered canvas.
36
+
37
+ - Website: The body-tracking demo now shows the whole live SDF volume instead of one slice.
38
+
39
+ - Library: SDFFieldNodes provides reusable distance, normal, shadow, and occlusion nodes.
40
+
41
+ - Website: Login, setup, and tool stories now use polished, selectable terminals.
42
+
43
+ - Library: Baked Motion preserves atlas alpha, resolves paused seeks, and fits mobile budgets.
44
+
45
+ - Library: Baked Motion tilt packages can ship a coarse all-view atlas for undecoded corners.
46
+
47
+ - Library: Baked Motion and Gaussian splat types now match their supported runtime contracts.
48
+
49
+ - Library: Baked Motion fuses each atlas page into one RGBA texture, halving its GPU memory.
50
+
51
+ - Tools: Baked Motion writes one package format; timelines and camera grids no longer differ.
52
+
53
+ - Tools: Baked Motion can now publish verified HEVC renditions alongside H.264.
54
+
55
+ - Tools: Blender reports consistent hub and Mesh to Splat versions in every install mode.
56
+
57
+ - Website: The launch film now opens on its title and keeps every proof to one fast minute.
58
+
59
+ - Library: Water accepts a shared interaction world, so moving bodies can push it.
60
+
61
+ - Packages: Indirect batching and its dependent examples are no longer included.
62
+
63
+ - Packages: Built from `6baf5c5aba0f`.
64
+
65
+
3
66
  ## 0.4.0
4
67
 
5
68
  ### Minor Changes
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # three-blocks-benchmark
2
2
 
3
- Reproducible browser benchmarks for Three Blocks, the unmerged three.js
4
- `GaussianSplatMesh` addon ([PR #33950][pr]), Spark, SuperSplat,
5
- GaussianSplats3D, and Babylon.js.
3
+ Reproducible browser benchmarks for Three Blocks, the three.js `GaussianSplat`
4
+ addon ([merged into `dev`][pr], vendored with the `dev` build it needs — see
5
+ `vendor/README.md`), Spark, SuperSplat, GaussianSplats3D, and Babylon.js.
6
6
 
7
7
  [pr]: https://github.com/mrdoob/three.js/pull/33950
8
8
  The default `standard` suite uses fixed held-out cameras from Mip-NeRF 360
@@ -228,11 +228,10 @@ async function createThreeBlocks( bytes ) {
228
228
 
229
229
  async function createThreeGS( bytes ) {
230
230
 
231
- const [ THREE, { PLYLoader }, { GaussianSplatMesh }, splatUtils ] = await Promise.all( [
231
+ const [ THREE, { GaussianSplatPLYLoader }, { GaussianSplat } ] = await Promise.all( [
232
232
  import( 'three/webgpu' ),
233
- import( 'three/addons/loaders/PLYLoader.js' ),
234
- import( '../vendor/three-gs/objects/GaussianSplatMesh.js' ),
235
- import( '../vendor/three-gs/utils/GaussianSplatUtils.js' ),
233
+ import( '../vendor/three-gs/loaders/GaussianSplatPLYLoader.js' ),
234
+ import( '../vendor/three-gs/objects/GaussianSplat.js' ),
236
235
  ] );
237
236
  const renderer = new THREE.WebGPURenderer( {
238
237
  canvas,
@@ -254,12 +253,10 @@ async function createThreeGS( bytes ) {
254
253
  const camera = new THREE.PerspectiveCamera( benchmarkViewport.fov, benchmarkViewport.width / benchmarkViewport.height, 0.01, 100 );
255
254
  setThreeCamera( camera, benchmarkPose );
256
255
 
257
- // The PR reuses the stock PLYLoader plus a conversion helper rather than shipping its own
258
- // splat PLY parser, so the benchmark loads it exactly that way.
259
- const loader = new PLYLoader();
260
- loader.setCustomPropertyNameMapping( splatUtils.GAUSSIAN_SPLAT_PLY_PROPERTY_MAPPING );
261
- const splatGeometry = splatUtils.createGaussianSplatGeometryFromPLYGeometry( loader.parse( bytes ) );
262
- const splats = new GaussianSplatMesh( splatGeometry );
256
+ // The merged addon ships its own splat PLY loader, which detects the file's spherical
257
+ // harmonics degree from the header, so the benchmark loads it exactly that way.
258
+ const splatGeometry = new GaussianSplatPLYLoader().parse( bytes );
259
+ const splats = new GaussianSplat( splatGeometry );
263
260
  // The dataset PLY is in source (COLMAP-style Y-down) space; apply the suite's shared Rx180
264
261
  // model transform, as the other runtimes do.
265
262
  splats.quaternion.set( 1, 0, 0, 0 );
package/lib/run.mjs CHANGED
@@ -36,6 +36,11 @@ const superSplatRuntime = path.resolve(
36
36
  path.dirname( fileURLToPath( import.meta.resolve( '@playcanvas/supersplat-viewer' ) ) ),
37
37
  '../public/index.js',
38
38
  );
39
+ // The Gaussian splat addon merged into three.js `dev` after r185 and ships in no npm
40
+ // release yet, so its lane runs against the vendored `dev` build it was merged into. Every
41
+ // other runtime keeps the installed `three` release.
42
+ const THREE_DEV_COMMIT = '00ea66ecb4b253c78e65b5f5a558af4696ba88a7';
43
+ const threeDevBuild = path.join( packageRoot, 'vendor/three-dev/build' );
39
44
  const ownManifest = JSON.parse( await fs.readFile( path.join( packageRoot, 'package.json' ), 'utf8' ) );
40
45
  const threeBlocksManifest = JSON.parse( await fs.readFile( path.join( threeBlocksRoot, 'package.json' ), 'utf8' ) );
41
46
 
@@ -50,10 +55,10 @@ const RENDERERS = [
50
55
  {
51
56
  id: 'three-gs',
52
57
  label: 'three.js splats',
53
- version: 'PR #33950',
58
+ version: 'dev',
54
59
  backend: 'WebGPU',
55
- source: 'https://github.com/mrdoob/three.js/pull/33950',
56
- commit: '5b8fcba16c8e0ab1e3745cd2c8185fe527998a36',
60
+ source: 'https://github.com/mrdoob/three.js/blob/dev/examples/jsm/objects/GaussianSplat.js',
61
+ commit: THREE_DEV_COMMIT,
57
62
  },
58
63
  {
59
64
  id: 'spark',
@@ -262,9 +267,13 @@ async function serveDirectory( root, externalFiles = new Map() ) {
262
267
 
263
268
  async function runPage( browser, baseUrl, { renderer, suite, item, warmup, frames } ) {
264
269
 
270
+ const viewport = { width: item.width ?? VIEWPORT.width, height: item.height ?? VIEWPORT.height };
265
271
  const context = await browser.newContext( {
266
- viewport: { width: item.width ?? VIEWPORT.width, height: item.height ?? VIEWPORT.height },
272
+ viewport,
267
273
  deviceScaleFactor: item.dpr ?? VIEWPORT.dpr,
274
+ // Presentation capture only: one .webm per page run at the exact viewport,
275
+ // off by default so published metrics never carry the recorder's overhead.
276
+ ...( process.env.TB_RECORD_VIDEO ? { recordVideo: { dir: process.env.TB_RECORD_VIDEO, size: viewport } } : {} ),
268
277
  } );
269
278
  const page = await context.newPage();
270
279
  const diagnostics = [];
@@ -315,12 +324,24 @@ async function runPage( browser, baseUrl, { renderer, suite, item, warmup, frame
315
324
 
316
325
  } finally {
317
326
 
327
+ const video = process.env.TB_RECORD_VIDEO ? page.video() : null;
318
328
  await context.close();
329
+ if ( video ) {
330
+
331
+ // The recorder finalizes on close; rename from playwright's random hash
332
+ // to the run order so a capture maps back to its stdout line.
333
+ const sequence = String( recordedRuns ++ ).padStart( 3, '0' );
334
+ await video.saveAs( path.join( process.env.TB_RECORD_VIDEO, `${sequence}-${item.id}-${renderer}.webm` ) );
335
+ await video.delete();
336
+
337
+ }
319
338
 
320
339
  }
321
340
 
322
341
  }
323
342
 
343
+ let recordedRuns = 0;
344
+
324
345
  function gpuSummary( systemInfo ) {
325
346
 
326
347
  const gpu = systemInfo.gpu ?? {};
@@ -430,6 +451,13 @@ export async function runBenchmark( options ) {
430
451
  resolve: {
431
452
  alias: [
432
453
  { find: 'supersplat-viewer-runtime', replacement: superSplatRuntime },
454
+ // The three.js addon lane resolves every `three` specifier to the vendored
455
+ // `dev` build, exactly as the addon's own example importmap does.
456
+ ...( renderer === 'three-gs' ? [
457
+ { find: 'three/tsl', replacement: path.join( threeDevBuild, 'three.tsl.js' ) },
458
+ { find: 'three/webgpu', replacement: path.join( threeDevBuild, 'three.webgpu.js' ) },
459
+ { find: 'three', replacement: path.join( threeDevBuild, 'three.webgpu.js' ) },
460
+ ] : [] ),
433
461
  // Both loaders spell their worker as `new URL( './<name>.js', import.meta.url )` so
434
462
  // bundlers emit the entry. The published chunks sit at the dist root, where those
435
463
  // paths do not exist; point them at the real worker modules.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "three-blocks-benchmark",
3
- "version": "0.4.0",
4
- "description": "Reproducible Gaussian splat browser benchmarks for Three Blocks, the three.js GaussianSplatMesh addon, Spark, SuperSplat, GaussianSplats3D, and Babylon.js",
3
+ "version": "0.5.0-alpha.384.g6baf5c5aba0f",
4
+ "description": "Reproducible Gaussian splat browser benchmarks for Three Blocks, the three.js GaussianSplat addon, Spark, SuperSplat, GaussianSplats3D, and Babylon.js",
5
5
  "keywords": [
6
6
  "three",
7
7
  "threejs",
@@ -52,7 +52,11 @@
52
52
  "playwright": "1.61.1",
53
53
  "pngjs": "7.0.0",
54
54
  "three": "0.185.1",
55
- "three-blocks": "0.10.0",
55
+ "three-blocks": "0.11.0-alpha.384.g6baf5c5aba0f",
56
56
  "vite": "8.1.3"
57
+ },
58
+ "threeBlocks": {
59
+ "channel": "alpha",
60
+ "site": "https://preprod.threejs-blocks.com"
57
61
  }
58
62
  }
@@ -0,0 +1,45 @@
1
+ # three.js Gaussian splat addon (`dev`)
2
+
3
+ Verbatim copies of the merged Gaussian splat addon from mrdoob/three.js
4
+ ([PR #33950][pr], merged 2026-08-08) and of the `three` build it runs against,
5
+ both pinned to commit `00ea66ecb4b253c78e65b5f5a558af4696ba88a7`.
6
+
7
+ The addon uses TSL nodes that landed after r185 (`unpackUnorm4x8`), so it runs
8
+ on no published `three` release. Until r186 ships, the benchmark vendors the
9
+ `dev` build alongside the addon and resolves every `three` specifier in the
10
+ `three-gs` bundle to it — the same mapping the addon's own example importmap
11
+ uses. Every other runtime keeps the installed `three` release.
12
+
13
+ | File | SHA-256 |
14
+ | --- | --- |
15
+ | `three-gs/objects/GaussianSplat.js` | `c239cde1abe239307c7dcc919d2a94c10e79a9393ffc3ba3e57dd8da53c367e6` |
16
+ | `three-gs/gpgpu/CountingSort.js` | `e9e90b6940a4da923534c3f249e8e7bd2c7603f8b432c54c62ecdc6db4be47e3` |
17
+ | `three-gs/utils/GaussianSplatUtils.js` | `4b9b010149b3e1a7673de37638365e937e005a03ba706e4cd81301b717d30f54` |
18
+ | `three-gs/loaders/GaussianSplatPLYLoader.js` | `072def58e1c9d081bb5b3ce2641a652d1075f3a45d778874a38a765cfcd1dd60` |
19
+ | `three-gs/loaders/PLYLoader.js` | `67b8c1015209b89081a92fdd69dc9df4a61284c3e9f8bb5380e441e4c64e5111` |
20
+ | `three-dev/build/three.core.js` | `262ac37bc647e6e0393bb3ff1be9a610971ac6c9e49643a4a5ee914947cd5d88` |
21
+ | `three-dev/build/three.webgpu.js` | `e906db5b44edcdf547eb3d7705e3a527ac851a7fad04db04c930066cb760fced` |
22
+ | `three-dev/build/three.tsl.js` | `24728c07fcdb9a57afb5a41c9bc05db2052ebd528c945e7e5fe0963e48eb7937` |
23
+
24
+ Refresh with:
25
+
26
+ ```sh
27
+ sha=00ea66ecb4b253c78e65b5f5a558af4696ba88a7
28
+ for f in objects/GaussianSplat.js gpgpu/CountingSort.js utils/GaussianSplatUtils.js \
29
+ loaders/GaussianSplatPLYLoader.js loaders/PLYLoader.js; do
30
+ curl -sSfL "https://raw.githubusercontent.com/mrdoob/three.js/$sha/examples/jsm/$f" -o "three-gs/$f"
31
+ done
32
+ for f in three.core.js three.webgpu.js three.tsl.js; do
33
+ curl -sSfL "https://raw.githubusercontent.com/mrdoob/three.js/$sha/build/$f" -o "three-dev/build/$f"
34
+ done
35
+ ```
36
+
37
+ The `three-gs` layout mirrors `examples/jsm/` so the files stay byte-identical
38
+ to upstream (`GaussianSplat.js` imports `../gpgpu/CountingSort.js`, the splat
39
+ PLY loader imports `./PLYLoader.js`). Drop `three-dev/` and alias the `three`
40
+ specifiers back to the installed release once r186 publishes the addon.
41
+
42
+ [pr]: https://github.com/mrdoob/three.js/pull/33950
43
+
44
+ Licensed under the MIT License, Copyright © 2010-2026 three.js authors:
45
+ <https://github.com/mrdoob/three.js/blob/dev/LICENSE>