three-blocks-benchmark 0.5.1 → 0.6.0

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,17 @@
1
1
  # three-blocks-benchmark
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 02f3072: Devtools: Run shader captures and project checks in Node with fewer dependencies.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [02f3072]
12
+ - @three-blocks/devtools@11.1.0
13
+ - three-blocks@0.12.1
14
+
3
15
  ## 0.5.1
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # three-blocks-benchmark
2
2
 
3
- Reproducible browser benchmarks for Three Blocks, the official three.js r186
3
+ Reproducible Node.js benchmarks for Three Blocks, the official three.js r186
4
4
  `GaussianSplat` addon, Spark, SuperSplat Viewer 1.31.2, SuperSplat Editor 3.0.0's
5
5
  renderer, GaussianSplats3D, and Babylon.js.
6
6
 
@@ -18,12 +18,6 @@ Published results and methodology:
18
18
 
19
19
  ## Run
20
20
 
21
- Install Chromium once:
22
-
23
- ```sh
24
- npx playwright install chromium
25
- ```
26
-
27
21
  Run the one-view standard validation from npm:
28
22
 
29
23
  ```sh
@@ -63,12 +57,10 @@ a micro scene.
63
57
  `--revision <value>` and `--dirty` add optional repository metadata; neither
64
58
  Git nor a checkout is required.
65
59
 
66
- The CLI uses full Chromium in headless mode, including the Metal ANGLE backend
67
- on macOS. Publication runs reject software GPU adapters.
68
- Memory sampling uses [Chromium's `ForceEagerMeasureMemory` test mode][eager-memory] after
69
- forced GC, outside the load and frame clocks, to avoid waiting for idle GC.
70
-
71
- [eager-memory]: https://chromium.googlesource.com/chromium/src/+/refs/tags/143.0.7458.0/third_party/blink/web_tests/VirtualTestSuites
60
+ The CLI renders in Node.js through native ANGLE (WebGL) and Dawn (WebGPU).
61
+ Publication runs reject software GPU adapters. Memory sampling records the
62
+ isolated Node process's RSS after forced GC, outside the load and frame clocks;
63
+ these values are not comparable to historical browser heap measurements.
72
64
 
73
65
  Frame timing directly executes each renderer's frame, without waiting for a display
74
66
  refresh, and cycles through the three held-out cameras. SuperSplat's complete viewer
@@ -22,8 +22,6 @@ Options:
22
22
  --renderer <id> Validate one renderer (not publication evidence)
23
23
  --quick Use one view/scene and short measurements
24
24
  --omit-captures Write metrics JSON without renderer captures
25
- --headed Show Chromium
26
- --headless Hide Chromium (default)
27
25
  --revision <value> Record an optional source revision
28
26
  --dirty Record that the source tree had changes
29
27
  --clean Record that the source tree was clean
@@ -39,7 +37,7 @@ function parseArgs( rawArgs ) {
39
37
  const argument = rawArgs[ index ];
40
38
  if ( argument === '--quick' ) options.quick = true;
41
39
  else if ( argument === '--omit-captures' ) options.omitCaptures = true;
42
- else if ( argument === '--headed' ) options.headed = true;
40
+ else if ( argument === '--headed' ) throw new Error( 'Benchmarks run in headless Node.' );
43
41
  else if ( argument === '--headless' ) options.headed = false;
44
42
  else if ( argument === '--dirty' ) options.workingTreeDirty = true;
45
43
  else if ( argument === '--clean' ) options.workingTreeDirty = false;
@@ -688,6 +688,7 @@ async function measureBrowserMemory() {
688
688
 
689
689
  window.gc?.();
690
690
  await new Promise( resolve => requestAnimationFrame( resolve ) );
691
+ if ( typeof globalThis.__THREE_BLOCKS_NODE_MEMORY__ === 'function' ) return { bytes: globalThis.__THREE_BLOCKS_NODE_MEMORY__().rss, source: 'node.process.memoryUsage.rss' };
691
692
  if ( typeof performance.measureUserAgentSpecificMemory === 'function' ) {
692
693
 
693
694
  try {
package/lib/run.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { launchNodeRenderer } from '@three-blocks/devtools/node';
1
2
  import { createHash } from 'node:crypto';
2
3
  import { createReadStream } from 'node:fs';
3
4
  import fs from 'node:fs/promises';
@@ -6,7 +7,6 @@ import os from 'node:os';
6
7
  import path from 'node:path';
7
8
  import process from 'node:process';
8
9
  import { fileURLToPath } from 'node:url';
9
- import { chromium } from 'playwright';
10
10
  import { PNG } from 'pngjs';
11
11
  import { build } from 'vite';
12
12
  import {
@@ -340,7 +340,7 @@ async function runPage( browser, baseUrl, { renderer, suite, item, warmup, frame
340
340
  await context.close();
341
341
  if ( video ) {
342
342
 
343
- // The recorder finalizes on close; rename from playwright's random hash
343
+ // The native recorder finalizes on close; rename its temporary file
344
344
  // to the run order so a capture maps back to its stdout line.
345
345
  const sequence = String( recordedRuns ++ ).padStart( 3, '0' );
346
346
  await video.saveAs( path.join( process.env.TB_RECORD_VIDEO, `${sequence}-${item.id}-${renderer}.webm` ) );
@@ -354,27 +354,11 @@ async function runPage( browser, baseUrl, { renderer, suite, item, warmup, frame
354
354
 
355
355
  let recordedRuns = 0;
356
356
 
357
- function gpuSummary( systemInfo ) {
358
-
359
- const gpu = systemInfo.gpu ?? {};
360
- return {
361
- devices: ( gpu.devices ?? [] ).map( device => ( {
362
- vendorId: device.vendorId,
363
- deviceId: device.deviceId,
364
- vendor: device.vendorString,
365
- device: device.deviceString,
366
- driverVendor: device.driverVendor,
367
- driverVersion: device.driverVersion,
368
- } ) ),
369
- auxAttributes: gpu.auxAttributes ?? {},
370
- };
371
-
372
- }
373
357
 
374
358
  function isPhysicalGpu( gpu ) {
375
359
 
376
360
  const description = JSON.stringify( gpu ).toLowerCase();
377
- return ! /swiftshader|llvmpipe|software rasterizer/.test( description );
361
+ return Boolean( gpu.vendor && ( gpu.device || gpu.architecture ) ) && gpu.isFallbackAdapter !== true && ! /swiftshader|llvmpipe|software rasterizer/.test( description );
378
362
 
379
363
  }
380
364
 
@@ -407,7 +391,8 @@ export async function runBenchmark( options ) {
407
391
 
408
392
  }
409
393
  const quick = options.quick === true;
410
- const headed = options.headed ?? false;
394
+ if ( options.headed === true ) throw new Error( 'Benchmarks run in headless Node.' );
395
+ const headed = false;
411
396
  const repetitions = quick ? 1 : 3;
412
397
  // Warm each runtime with the same moving-camera sequence used for measurement.
413
398
  const warmupFrames = quick ? 2 : 60;
@@ -517,23 +502,8 @@ export async function runBenchmark( options ) {
517
502
  }
518
503
  const bundleSha256 = await directoryDigest( bundleRoot );
519
504
  server = await serveDirectory( bundleRoot, externalFiles );
520
- browser = await chromium.launch( {
521
- channel: 'chromium',
522
- headless: ! headed,
523
- args: [
524
- '--enable-unsafe-webgpu',
525
- '--js-flags=--expose-gc',
526
- // Chromium's own measure-memory tests use eager collection. The harness
527
- // samples after forced GC, outside timing, without waiting for idle GC.
528
- '--enable-blink-features=ForceEagerMeasureMemory',
529
- // Headless Chromium on macOS otherwise picks SwiftShader, which fails the
530
- // physical-GPU gate and captures blank frames.
531
- ...( process.platform === 'darwin' ? [ '--use-angle=metal' ] : [] ),
532
- ],
533
- } );
534
- const cdp = await browser.newBrowserCDPSession();
535
- const systemInfo = await cdp.send( 'SystemInfo.getInfo' );
536
- const gpu = gpuSummary( systemInfo );
505
+ browser = await launchNodeRenderer();
506
+ const gpu = await browser.gpuInfo();
537
507
  const physicalGpu = isPhysicalGpu( gpu );
538
508
  if ( ! quick && ! physicalGpu ) {
539
509
 
@@ -719,7 +689,7 @@ export async function runBenchmark( options ) {
719
689
  browser: await browser.version(),
720
690
  headed,
721
691
  physicalGpu,
722
- memoryMeasurement: 'Forced GC followed by eager measureUserAgentSpecificMemory, outside timing.',
692
+ memoryMeasurement: 'Node renderer process resident memory after GC, outside timing.',
723
693
  gpu,
724
694
  os: { platform: process.platform, release: os.release(), architecture: os.arch() },
725
695
  cpu: os.cpus()[ 0 ]?.model ?? null,
@@ -734,7 +704,7 @@ export async function runBenchmark( options ) {
734
704
  frame: suiteId === 'standard'
735
705
  ? 'Round-robin held-out-camera update through a complete current-camera sort, index upload and GPU-complete draw, without a display-frame wait; quick uses its single validation view.'
736
706
  : 'Camera update through a complete current-camera sort and GPU-complete draw on a deterministic 2.5-degree orbit.',
737
- memory: 'Chromium page memory after forced GC; renderer GPU bytes are secondary because scopes differ.',
707
+ memory: 'Node renderer process RSS after forced GC; renderer GPU bytes are secondary because scopes differ.',
738
708
  quality: suiteId === 'standard'
739
709
  ? `PSNR and dm_pix-compatible RGB SSIM on decoded sRGB captures versus held-out ground truth, after ${warmupFrames} stationary frames outside the timing window.`
740
710
  : 'Not measured; synthetic micro scenes are performance-only.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-blocks-benchmark",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Reproducible Gaussian splat benchmarks for Three Blocks, three.js r186, Spark, SuperSplat Viewer and Editor 3, GaussianSplats3D, and Babylon.js",
5
5
  "keywords": [
6
6
  "three",
@@ -51,11 +51,11 @@
51
51
  "@playcanvas/splat-transform": "3.3.3",
52
52
  "@playcanvas/supersplat-viewer": "1.31.2",
53
53
  "@sparkjsdev/spark": "2.1.0",
54
+ "@three-blocks/devtools": "11.1.0",
54
55
  "playcanvas": "2.22.0",
55
- "playwright": "1.61.1",
56
56
  "pngjs": "7.0.0",
57
57
  "three": "0.185.1",
58
- "three-blocks": "0.12.0",
58
+ "three-blocks": "0.12.1",
59
59
  "three-r186": "npm:three@0.186.0",
60
60
  "vite": "8.1.3"
61
61
  }