@vib3code/sdk 2.0.1
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 +118 -0
- package/DOCS/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md +34 -0
- package/DOCS/CI_TESTING.md +38 -0
- package/DOCS/CLI_ONBOARDING.md +75 -0
- package/DOCS/CONTROL_REFERENCE.md +64 -0
- package/DOCS/DEV_TRACK_ANALYSIS.md +77 -0
- package/DOCS/DEV_TRACK_PLAN_2026-01-07.md +42 -0
- package/DOCS/DEV_TRACK_SESSION_2026-01-31.md +220 -0
- package/DOCS/ENV_SETUP.md +189 -0
- package/DOCS/EXPORT_FORMATS.md +417 -0
- package/DOCS/GPU_DISPOSAL_GUIDE.md +21 -0
- package/DOCS/LICENSING_TIERS.md +275 -0
- package/DOCS/MASTER_PLAN_2026-01-31.md +570 -0
- package/DOCS/OBS_SETUP_GUIDE.md +98 -0
- package/DOCS/PROJECT_SETUP.md +66 -0
- package/DOCS/RENDERER_LIFECYCLE.md +40 -0
- package/DOCS/REPO_MANIFEST.md +121 -0
- package/DOCS/SESSION_014_PLAN.md +195 -0
- package/DOCS/SESSION_LOG_2026-01-07.md +56 -0
- package/DOCS/STRATEGIC_BLUEPRINT_2026-01-07.md +72 -0
- package/DOCS/SYSTEM_AUDIT_2026-01-30.md +738 -0
- package/DOCS/SYSTEM_INVENTORY.md +520 -0
- package/DOCS/TELEMETRY_EXPORTS.md +34 -0
- package/DOCS/WEBGPU_STATUS.md +38 -0
- package/DOCS/XR_BENCHMARKS.md +608 -0
- package/LICENSE +21 -0
- package/README.md +426 -0
- package/docs/.nojekyll +0 -0
- package/docs/01-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/02-hyperspatial_ego_death.html +346 -0
- package/docs/03-post_cartesian_sublime.html +346 -0
- package/docs/04-crystalline_void_meditation.html +346 -0
- package/docs/05-quantum_decoherence_ballet.html +346 -0
- package/docs/06-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/07-hyperspatial_ego_death.html +346 -0
- package/docs/08-post_cartesian_sublime.html +346 -0
- package/docs/09-crystalline_void_meditation.html +346 -0
- package/docs/10-quantum_decoherence_ballet.html +346 -0
- package/docs/11-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/12-hyperspatial_ego_death.html +346 -0
- package/docs/13-post_cartesian_sublime.html +346 -0
- package/docs/index.html +794 -0
- package/docs/test-hub.html +441 -0
- package/docs/url-state.js +102 -0
- package/docs/vib3-exports/01-quantum-quantum-tetrahedron-lattice.html +489 -0
- package/docs/vib3-exports/02-quantum-quantum-hypersphere-matrix.html +489 -0
- package/docs/vib3-exports/03-quantum-quantum-hypertetra-fractal.html +489 -0
- package/docs/vib3-exports/04-faceted-faceted-crystal-structure.html +407 -0
- package/docs/vib3-exports/05-faceted-faceted-klein-bottle.html +407 -0
- package/docs/vib3-exports/06-faceted-faceted-hypertetra-torus.html +407 -0
- package/docs/vib3-exports/07-holographic-holographic-wave-field.html +457 -0
- package/docs/vib3-exports/08-holographic-holographic-hypersphere-sphere.html +457 -0
- package/docs/vib3-exports/09-holographic-holographic-hypertetra-crystal.html +457 -0
- package/docs/vib3-exports/index.html +238 -0
- package/docs/webgpu-live.html +702 -0
- package/package.json +367 -0
- package/src/advanced/AIPresetGenerator.js +777 -0
- package/src/advanced/MIDIController.js +703 -0
- package/src/advanced/OffscreenWorker.js +1051 -0
- package/src/advanced/WebGPUCompute.js +1051 -0
- package/src/advanced/WebXRRenderer.js +680 -0
- package/src/agent/cli/AgentCLI.js +615 -0
- package/src/agent/cli/index.js +14 -0
- package/src/agent/index.js +73 -0
- package/src/agent/mcp/MCPServer.js +950 -0
- package/src/agent/mcp/index.js +9 -0
- package/src/agent/mcp/tools.js +548 -0
- package/src/agent/telemetry/EventStream.js +669 -0
- package/src/agent/telemetry/Instrumentation.js +618 -0
- package/src/agent/telemetry/TelemetryExporters.js +427 -0
- package/src/agent/telemetry/TelemetryService.js +464 -0
- package/src/agent/telemetry/index.js +52 -0
- package/src/benchmarks/BenchmarkRunner.js +381 -0
- package/src/benchmarks/MetricsCollector.js +299 -0
- package/src/benchmarks/index.js +9 -0
- package/src/benchmarks/scenes.js +259 -0
- package/src/cli/index.js +675 -0
- package/src/config/ApiConfig.js +88 -0
- package/src/core/CanvasManager.js +217 -0
- package/src/core/ErrorReporter.js +117 -0
- package/src/core/ParameterMapper.js +333 -0
- package/src/core/Parameters.js +396 -0
- package/src/core/RendererContracts.js +200 -0
- package/src/core/UnifiedResourceManager.js +370 -0
- package/src/core/VIB3Engine.js +636 -0
- package/src/core/renderers/FacetedRendererAdapter.js +32 -0
- package/src/core/renderers/HolographicRendererAdapter.js +29 -0
- package/src/core/renderers/QuantumRendererAdapter.js +29 -0
- package/src/core/renderers/RendererLifecycleManager.js +63 -0
- package/src/creative/ColorPresetsSystem.js +980 -0
- package/src/creative/ParameterTimeline.js +1061 -0
- package/src/creative/PostProcessingPipeline.js +1113 -0
- package/src/creative/TransitionAnimator.js +683 -0
- package/src/export/CSSExporter.js +226 -0
- package/src/export/CardGeneratorBase.js +279 -0
- package/src/export/ExportManager.js +580 -0
- package/src/export/FacetedCardGenerator.js +279 -0
- package/src/export/HolographicCardGenerator.js +543 -0
- package/src/export/LottieExporter.js +552 -0
- package/src/export/QuantumCardGenerator.js +315 -0
- package/src/export/SVGExporter.js +519 -0
- package/src/export/ShaderExporter.js +903 -0
- package/src/export/TradingCardGenerator.js +3055 -0
- package/src/export/TradingCardManager.js +181 -0
- package/src/export/VIB3PackageExporter.js +559 -0
- package/src/export/index.js +14 -0
- package/src/export/systems/TradingCardSystemFaceted.js +494 -0
- package/src/export/systems/TradingCardSystemHolographic.js +452 -0
- package/src/export/systems/TradingCardSystemQuantum.js +411 -0
- package/src/faceted/FacetedSystem.js +963 -0
- package/src/features/CollectionManager.js +433 -0
- package/src/gallery/CollectionManager.js +240 -0
- package/src/gallery/GallerySystem.js +485 -0
- package/src/geometry/GeometryFactory.js +314 -0
- package/src/geometry/GeometryLibrary.js +72 -0
- package/src/geometry/buffers/BufferBuilder.js +338 -0
- package/src/geometry/buffers/index.js +18 -0
- package/src/geometry/generators/Crystal.js +420 -0
- package/src/geometry/generators/Fractal.js +298 -0
- package/src/geometry/generators/KleinBottle.js +197 -0
- package/src/geometry/generators/Sphere.js +192 -0
- package/src/geometry/generators/Tesseract.js +160 -0
- package/src/geometry/generators/Tetrahedron.js +225 -0
- package/src/geometry/generators/Torus.js +304 -0
- package/src/geometry/generators/Wave.js +341 -0
- package/src/geometry/index.js +142 -0
- package/src/geometry/warp/HypersphereCore.js +211 -0
- package/src/geometry/warp/HypertetraCore.js +386 -0
- package/src/geometry/warp/index.js +57 -0
- package/src/holograms/HolographicVisualizer.js +1073 -0
- package/src/holograms/RealHolographicSystem.js +966 -0
- package/src/holograms/variantRegistry.js +69 -0
- package/src/integrations/FigmaPlugin.js +854 -0
- package/src/integrations/OBSMode.js +754 -0
- package/src/integrations/ThreeJsPackage.js +660 -0
- package/src/integrations/TouchDesignerExport.js +552 -0
- package/src/integrations/frameworks/Vib3React.js +591 -0
- package/src/integrations/frameworks/Vib3Svelte.js +654 -0
- package/src/integrations/frameworks/Vib3Vue.js +628 -0
- package/src/llm/LLMParameterInterface.js +240 -0
- package/src/llm/LLMParameterUI.js +577 -0
- package/src/math/Mat4x4.js +708 -0
- package/src/math/Projection.js +341 -0
- package/src/math/Rotor4D.js +637 -0
- package/src/math/Vec4.js +476 -0
- package/src/math/constants.js +164 -0
- package/src/math/index.js +68 -0
- package/src/math/projections.js +54 -0
- package/src/math/rotations.js +196 -0
- package/src/quantum/QuantumEngine.js +906 -0
- package/src/quantum/QuantumVisualizer.js +1103 -0
- package/src/reactivity/ReactivityConfig.js +499 -0
- package/src/reactivity/ReactivityManager.js +586 -0
- package/src/reactivity/SpatialInputSystem.js +1783 -0
- package/src/reactivity/index.js +93 -0
- package/src/render/CommandBuffer.js +465 -0
- package/src/render/MultiCanvasBridge.js +340 -0
- package/src/render/RenderCommand.js +514 -0
- package/src/render/RenderResourceRegistry.js +523 -0
- package/src/render/RenderState.js +552 -0
- package/src/render/RenderTarget.js +512 -0
- package/src/render/ShaderLoader.js +253 -0
- package/src/render/ShaderProgram.js +599 -0
- package/src/render/UnifiedRenderBridge.js +496 -0
- package/src/render/backends/WebGLBackend.js +1108 -0
- package/src/render/backends/WebGPUBackend.js +1409 -0
- package/src/render/commands/CommandBufferExecutor.js +607 -0
- package/src/render/commands/RenderCommandBuffer.js +661 -0
- package/src/render/commands/index.js +17 -0
- package/src/render/index.js +367 -0
- package/src/scene/Disposable.js +498 -0
- package/src/scene/MemoryPool.js +618 -0
- package/src/scene/Node4D.js +697 -0
- package/src/scene/ResourceManager.js +599 -0
- package/src/scene/Scene4D.js +540 -0
- package/src/scene/index.js +98 -0
- package/src/schemas/error.schema.json +84 -0
- package/src/schemas/extension.schema.json +88 -0
- package/src/schemas/index.js +214 -0
- package/src/schemas/parameters.schema.json +142 -0
- package/src/schemas/tool-pack.schema.json +44 -0
- package/src/schemas/tool-response.schema.json +127 -0
- package/src/shaders/common/fullscreen.vert.glsl +5 -0
- package/src/shaders/common/fullscreen.vert.wgsl +17 -0
- package/src/shaders/common/geometry24.glsl +65 -0
- package/src/shaders/common/geometry24.wgsl +54 -0
- package/src/shaders/common/rotation4d.glsl +85 -0
- package/src/shaders/common/rotation4d.wgsl +86 -0
- package/src/shaders/common/uniforms.glsl +44 -0
- package/src/shaders/common/uniforms.wgsl +48 -0
- package/src/shaders/faceted/faceted.frag.glsl +129 -0
- package/src/shaders/faceted/faceted.frag.wgsl +164 -0
- package/src/shaders/holographic/holographic.frag.glsl +406 -0
- package/src/shaders/holographic/holographic.frag.wgsl +185 -0
- package/src/shaders/quantum/quantum.frag.glsl +513 -0
- package/src/shaders/quantum/quantum.frag.wgsl +361 -0
- package/src/testing/ParallelTestFramework.js +519 -0
- package/src/testing/__snapshots__/exportFormats.test.js.snap +24 -0
- package/src/testing/exportFormats.test.js +8 -0
- package/src/testing/projections.test.js +14 -0
- package/src/testing/rotations.test.js +37 -0
- package/src/ui/InteractivityMenu.js +516 -0
- package/src/ui/StatusManager.js +96 -0
- package/src/ui/adaptive/renderers/webgpu/BufferLayout.ts +252 -0
- package/src/ui/adaptive/renderers/webgpu/PolytopeInstanceBuffer.ts +144 -0
- package/src/ui/adaptive/renderers/webgpu/TripleBufferedUniform.ts +170 -0
- package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +735 -0
- package/src/ui/adaptive/renderers/webgpu/index.ts +112 -0
- package/src/variations/VariationManager.js +431 -0
- package/src/viewer/AudioReactivity.js +505 -0
- package/src/viewer/CardBending.js +481 -0
- package/src/viewer/GalleryUI.js +832 -0
- package/src/viewer/ReactivityManager.js +590 -0
- package/src/viewer/TradingCardExporter.js +600 -0
- package/src/viewer/ViewerPortal.js +374 -0
- package/src/viewer/index.js +12 -0
- package/src/wasm/WasmLoader.js +296 -0
- package/src/wasm/index.js +132 -0
- package/tools/agentic/mcpTools.js +88 -0
- package/tools/cli/agent-cli.js +92 -0
- package/tools/export/formats.js +24 -0
- package/tools/math/rotation-baseline.mjs +64 -0
- package/tools/shader-sync-verify.js +937 -0
- package/tools/telemetry/manifestPipeline.js +141 -0
- package/tools/telemetry/telemetryEvents.js +35 -0
- package/types/adaptive-sdk.d.ts +185 -0
- package/types/advanced/AIPresetGenerator.d.ts +81 -0
- package/types/advanced/MIDIController.d.ts +100 -0
- package/types/advanced/OffscreenWorker.d.ts +82 -0
- package/types/advanced/WebGPUCompute.d.ts +52 -0
- package/types/advanced/WebXRRenderer.d.ts +77 -0
- package/types/advanced/index.d.ts +46 -0
- package/types/core/ErrorReporter.d.ts +50 -0
- package/types/core/VIB3Engine.d.ts +204 -0
- package/types/creative/ColorPresetsSystem.d.ts +91 -0
- package/types/creative/ParameterTimeline.d.ts +74 -0
- package/types/creative/PostProcessingPipeline.d.ts +109 -0
- package/types/creative/TransitionAnimator.d.ts +71 -0
- package/types/creative/index.d.ts +35 -0
- package/types/integrations/FigmaPlugin.d.ts +46 -0
- package/types/integrations/OBSMode.d.ts +74 -0
- package/types/integrations/ThreeJsPackage.d.ts +62 -0
- package/types/integrations/TouchDesignerExport.d.ts +36 -0
- package/types/integrations/Vib3React.d.ts +74 -0
- package/types/integrations/Vib3Svelte.d.ts +63 -0
- package/types/integrations/Vib3Vue.d.ts +55 -0
- package/types/integrations/index.d.ts +52 -0
- package/types/reactivity/SpatialInputSystem.d.ts +173 -0
- package/types/reactivity/index.d.ts +394 -0
- package/types/render/CommandBuffer.d.ts +169 -0
- package/types/render/RenderCommand.d.ts +312 -0
- package/types/render/RenderState.d.ts +279 -0
- package/types/render/RenderTarget.d.ts +254 -0
- package/types/render/ShaderProgram.d.ts +277 -0
- package/types/render/UnifiedRenderBridge.d.ts +143 -0
- package/types/render/WebGLBackend.d.ts +168 -0
- package/types/render/WebGPUBackend.d.ts +186 -0
- package/types/render/index.d.ts +141 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BenchmarkRunner - Run performance benchmarks on VIB3+ engine
|
|
3
|
+
*
|
|
4
|
+
* Executes standardized benchmark scenes and collects metrics
|
|
5
|
+
* for performance analysis and regression detection.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { MetricsCollector } from './MetricsCollector.js';
|
|
9
|
+
import { BENCHMARK_SCENES } from './scenes.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Default benchmark options
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_OPTIONS = {
|
|
15
|
+
duration: 10000, // 10 seconds per scene
|
|
16
|
+
warmupDuration: 2000, // 2 second warmup
|
|
17
|
+
platform: 'desktop',
|
|
18
|
+
verbose: false
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Platform-specific thresholds
|
|
23
|
+
*/
|
|
24
|
+
const PLATFORM_THRESHOLDS = {
|
|
25
|
+
desktop: {
|
|
26
|
+
maxFrameTime: 11, // 90 FPS
|
|
27
|
+
minFPS: 90,
|
|
28
|
+
maxMemory: 200
|
|
29
|
+
},
|
|
30
|
+
mobile: {
|
|
31
|
+
maxFrameTime: 14, // 72 FPS
|
|
32
|
+
minFPS: 72,
|
|
33
|
+
maxMemory: 100
|
|
34
|
+
},
|
|
35
|
+
web: {
|
|
36
|
+
maxFrameTime: 16.67, // 60 FPS
|
|
37
|
+
minFPS: 60,
|
|
38
|
+
maxMemory: 150
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Benchmark runner for VIB3+ engine
|
|
44
|
+
*/
|
|
45
|
+
export class BenchmarkRunner {
|
|
46
|
+
/**
|
|
47
|
+
* Create a benchmark runner
|
|
48
|
+
* @param {object} engine - VIB3+ engine instance
|
|
49
|
+
* @param {object} options - Runner options
|
|
50
|
+
*/
|
|
51
|
+
constructor(engine, options = {}) {
|
|
52
|
+
/** @type {object} Engine instance */
|
|
53
|
+
this.engine = engine;
|
|
54
|
+
|
|
55
|
+
/** @type {object} Runner options */
|
|
56
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
57
|
+
|
|
58
|
+
/** @type {MetricsCollector} Metrics collector */
|
|
59
|
+
this.collector = new MetricsCollector({
|
|
60
|
+
renderer: engine?.renderer || null
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/** @type {Array<object>} Benchmark results */
|
|
64
|
+
this.results = [];
|
|
65
|
+
|
|
66
|
+
/** @type {boolean} Running state */
|
|
67
|
+
this.running = false;
|
|
68
|
+
|
|
69
|
+
/** @type {Function|null} Frame callback */
|
|
70
|
+
this._frameCallback = null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Run a single benchmark scene
|
|
75
|
+
* @param {string} sceneName - Name of scene from BENCHMARK_SCENES
|
|
76
|
+
* @param {number} duration - Duration in milliseconds
|
|
77
|
+
* @returns {Promise<object>} Benchmark result
|
|
78
|
+
*/
|
|
79
|
+
async runBenchmark(sceneName, duration = this.options.duration) {
|
|
80
|
+
const scene = BENCHMARK_SCENES[sceneName];
|
|
81
|
+
if (!scene) {
|
|
82
|
+
throw new Error(`Unknown benchmark scene: ${sceneName}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (this.options.verbose) {
|
|
86
|
+
console.log(`Running benchmark: ${scene.name}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Configure scene
|
|
90
|
+
if (this.engine?.setParameters) {
|
|
91
|
+
this.engine.setParameters(scene.params);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.collector.reset();
|
|
95
|
+
this.running = true;
|
|
96
|
+
|
|
97
|
+
// Warmup phase
|
|
98
|
+
if (this.options.verbose) {
|
|
99
|
+
console.log(` Warmup: ${this.options.warmupDuration}ms`);
|
|
100
|
+
}
|
|
101
|
+
await this._runFrames(this.options.warmupDuration);
|
|
102
|
+
this.collector.reset();
|
|
103
|
+
|
|
104
|
+
// Measurement phase
|
|
105
|
+
if (this.options.verbose) {
|
|
106
|
+
console.log(` Measuring: ${duration}ms`);
|
|
107
|
+
}
|
|
108
|
+
await this._runFrames(duration);
|
|
109
|
+
|
|
110
|
+
this.running = false;
|
|
111
|
+
|
|
112
|
+
// Collect results
|
|
113
|
+
const stats = this.collector.getStats();
|
|
114
|
+
const thresholds = PLATFORM_THRESHOLDS[this.options.platform] || PLATFORM_THRESHOLDS.desktop;
|
|
115
|
+
const passed = this._evaluateThresholds(stats, thresholds);
|
|
116
|
+
|
|
117
|
+
const result = {
|
|
118
|
+
scene: sceneName,
|
|
119
|
+
sceneName: scene.name,
|
|
120
|
+
description: scene.description,
|
|
121
|
+
params: scene.params,
|
|
122
|
+
duration,
|
|
123
|
+
...stats,
|
|
124
|
+
timestamp: new Date().toISOString(),
|
|
125
|
+
platform: this.options.platform,
|
|
126
|
+
thresholds,
|
|
127
|
+
passed
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
this.results.push(result);
|
|
131
|
+
|
|
132
|
+
if (this.options.verbose) {
|
|
133
|
+
const status = passed.overall ? '✓ PASS' : '✗ FAIL';
|
|
134
|
+
console.log(` Result: ${status} (${stats.fps.avg.toFixed(1)} FPS, ${stats.frameTime.p95.toFixed(2)}ms p95)`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Run frames for specified duration
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
async _runFrames(duration) {
|
|
145
|
+
return new Promise((resolve) => {
|
|
146
|
+
const startTime = performance.now();
|
|
147
|
+
|
|
148
|
+
const frame = () => {
|
|
149
|
+
if (!this.running || performance.now() - startTime >= duration) {
|
|
150
|
+
resolve();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.collector.beginFrame();
|
|
155
|
+
|
|
156
|
+
// Render frame if engine available
|
|
157
|
+
if (this.engine?.renderFrame) {
|
|
158
|
+
this.engine.renderFrame();
|
|
159
|
+
} else if (this.engine?.render) {
|
|
160
|
+
this.engine.render();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this.collector.endFrame();
|
|
164
|
+
|
|
165
|
+
// Use requestAnimationFrame for realistic timing
|
|
166
|
+
if (typeof requestAnimationFrame !== 'undefined') {
|
|
167
|
+
requestAnimationFrame(frame);
|
|
168
|
+
} else {
|
|
169
|
+
setImmediate(frame);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
frame();
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Evaluate results against thresholds
|
|
179
|
+
* @private
|
|
180
|
+
*/
|
|
181
|
+
_evaluateThresholds(stats, thresholds) {
|
|
182
|
+
if (!stats) {
|
|
183
|
+
return { overall: false, frameTime: false, fps: false, memory: false };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const results = {
|
|
187
|
+
frameTime: stats.frameTime.p95 <= thresholds.maxFrameTime,
|
|
188
|
+
fps: stats.fps.avg >= thresholds.minFPS,
|
|
189
|
+
memory: !stats.memory || stats.memory.used <= thresholds.maxMemory
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
results.overall = results.frameTime && results.fps && results.memory;
|
|
193
|
+
|
|
194
|
+
return results;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Run all benchmark scenes
|
|
199
|
+
* @param {number} duration - Duration per scene
|
|
200
|
+
* @returns {Promise<object>} All results
|
|
201
|
+
*/
|
|
202
|
+
async runAllBenchmarks(duration = this.options.duration) {
|
|
203
|
+
const results = {};
|
|
204
|
+
const sceneNames = Object.keys(BENCHMARK_SCENES);
|
|
205
|
+
|
|
206
|
+
if (this.options.verbose) {
|
|
207
|
+
console.log(`Running ${sceneNames.length} benchmark scenes...`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const sceneName of sceneNames) {
|
|
211
|
+
try {
|
|
212
|
+
results[sceneName] = await this.runBenchmark(sceneName, duration);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
results[sceneName] = {
|
|
215
|
+
scene: sceneName,
|
|
216
|
+
error: error.message,
|
|
217
|
+
passed: { overall: false }
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return results;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Run benchmarks matching a pattern
|
|
227
|
+
* @param {string|RegExp} pattern - Scene name pattern
|
|
228
|
+
* @param {number} duration - Duration per scene
|
|
229
|
+
* @returns {Promise<object>}
|
|
230
|
+
*/
|
|
231
|
+
async runMatchingBenchmarks(pattern, duration = this.options.duration) {
|
|
232
|
+
const regex = pattern instanceof RegExp ? pattern : new RegExp(pattern);
|
|
233
|
+
const matchingScenes = Object.keys(BENCHMARK_SCENES).filter(name => regex.test(name));
|
|
234
|
+
|
|
235
|
+
const results = {};
|
|
236
|
+
for (const sceneName of matchingScenes) {
|
|
237
|
+
results[sceneName] = await this.runBenchmark(sceneName, duration);
|
|
238
|
+
}
|
|
239
|
+
return results;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Compare current results against a baseline
|
|
244
|
+
* @param {object} baseline - Baseline results
|
|
245
|
+
* @param {number} threshold - Regression threshold (e.g., 0.15 = 15%)
|
|
246
|
+
* @returns {object} Comparison results
|
|
247
|
+
*/
|
|
248
|
+
compareToBaseline(baseline, threshold = 0.15) {
|
|
249
|
+
const comparisons = {};
|
|
250
|
+
|
|
251
|
+
for (const result of this.results) {
|
|
252
|
+
const baselineResult = baseline.results?.[result.scene];
|
|
253
|
+
if (!baselineResult) {
|
|
254
|
+
comparisons[result.scene] = {
|
|
255
|
+
status: 'new',
|
|
256
|
+
message: 'No baseline for comparison'
|
|
257
|
+
};
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const frameTimeRatio = result.frameTime.avg / baselineResult.frameTime.avg;
|
|
262
|
+
const fpsRatio = baselineResult.fps.avg / result.fps.avg;
|
|
263
|
+
|
|
264
|
+
const regression = frameTimeRatio > (1 + threshold) || fpsRatio > (1 + threshold);
|
|
265
|
+
|
|
266
|
+
comparisons[result.scene] = {
|
|
267
|
+
status: regression ? 'regression' : 'pass',
|
|
268
|
+
frameTimeRatio: frameTimeRatio.toFixed(3),
|
|
269
|
+
fpsRatio: fpsRatio.toFixed(3),
|
|
270
|
+
threshold,
|
|
271
|
+
current: {
|
|
272
|
+
frameTime: result.frameTime.avg,
|
|
273
|
+
fps: result.fps.avg
|
|
274
|
+
},
|
|
275
|
+
baseline: {
|
|
276
|
+
frameTime: baselineResult.frameTime.avg,
|
|
277
|
+
fps: baselineResult.fps.avg
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const hasRegression = Object.values(comparisons).some(c => c.status === 'regression');
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
overall: hasRegression ? 'regression' : 'pass',
|
|
286
|
+
threshold,
|
|
287
|
+
comparisons
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Export results as report
|
|
293
|
+
* @returns {object}
|
|
294
|
+
*/
|
|
295
|
+
exportReport() {
|
|
296
|
+
const passedCount = this.results.filter(r => r.passed?.overall).length;
|
|
297
|
+
const failedCount = this.results.length - passedCount;
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
generated: new Date().toISOString(),
|
|
301
|
+
platform: this.detectPlatform(),
|
|
302
|
+
summary: {
|
|
303
|
+
total: this.results.length,
|
|
304
|
+
passed: passedCount,
|
|
305
|
+
failed: failedCount
|
|
306
|
+
},
|
|
307
|
+
results: this.results.reduce((acc, r) => {
|
|
308
|
+
acc[r.scene] = r;
|
|
309
|
+
return acc;
|
|
310
|
+
}, {})
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Export results as markdown
|
|
316
|
+
* @returns {string}
|
|
317
|
+
*/
|
|
318
|
+
exportMarkdown() {
|
|
319
|
+
let md = '# VIB3+ Benchmark Results\n\n';
|
|
320
|
+
md += `**Generated:** ${new Date().toISOString()}\n`;
|
|
321
|
+
md += `**Platform:** ${this.options.platform}\n\n`;
|
|
322
|
+
|
|
323
|
+
md += '## Summary\n\n';
|
|
324
|
+
const passed = this.results.filter(r => r.passed?.overall).length;
|
|
325
|
+
md += `- **Passed:** ${passed}/${this.results.length}\n`;
|
|
326
|
+
md += `- **Failed:** ${this.results.length - passed}/${this.results.length}\n\n`;
|
|
327
|
+
|
|
328
|
+
md += '## Results\n\n';
|
|
329
|
+
md += '| Scene | FPS | Frame Time (p95) | Status |\n';
|
|
330
|
+
md += '|-------|-----|------------------|--------|\n';
|
|
331
|
+
|
|
332
|
+
for (const result of this.results) {
|
|
333
|
+
const status = result.passed?.overall ? '✓ Pass' : '✗ Fail';
|
|
334
|
+
const fps = result.fps?.avg?.toFixed(1) || 'N/A';
|
|
335
|
+
const frameTime = result.frameTime?.p95?.toFixed(2) || 'N/A';
|
|
336
|
+
md += `| ${result.sceneName} | ${fps} | ${frameTime}ms | ${status} |\n`;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return md;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Detect current platform
|
|
344
|
+
* @returns {object}
|
|
345
|
+
*/
|
|
346
|
+
detectPlatform() {
|
|
347
|
+
const info = {
|
|
348
|
+
type: this.options.platform,
|
|
349
|
+
timestamp: new Date().toISOString()
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
if (typeof navigator !== 'undefined') {
|
|
353
|
+
info.userAgent = navigator.userAgent;
|
|
354
|
+
info.cores = navigator.hardwareConcurrency || 'unknown';
|
|
355
|
+
info.memory = navigator.deviceMemory || 'unknown';
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (this.engine?.getGPUInfo) {
|
|
359
|
+
info.gpu = this.engine.getGPUInfo();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return info;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Clear all results
|
|
367
|
+
*/
|
|
368
|
+
clearResults() {
|
|
369
|
+
this.results = [];
|
|
370
|
+
this.collector.reset();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Stop current benchmark
|
|
375
|
+
*/
|
|
376
|
+
stop() {
|
|
377
|
+
this.running = false;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export default BenchmarkRunner;
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MetricsCollector - Collect performance metrics for VIB3+ engine
|
|
3
|
+
*
|
|
4
|
+
* Captures frame time, FPS, memory usage, draw calls, and triangle counts
|
|
5
|
+
* for performance analysis and regression detection.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Performance metrics collector
|
|
10
|
+
*/
|
|
11
|
+
export class MetricsCollector {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
/** @type {Array<object>} Sample history */
|
|
14
|
+
this.samples = [];
|
|
15
|
+
|
|
16
|
+
/** @type {number} Total frames counted */
|
|
17
|
+
this.frameCount = 0;
|
|
18
|
+
|
|
19
|
+
/** @type {number} Last frame timestamp */
|
|
20
|
+
this.lastFrameTime = 0;
|
|
21
|
+
|
|
22
|
+
/** @type {number|null} Current frame start time */
|
|
23
|
+
this.frameStart = null;
|
|
24
|
+
|
|
25
|
+
/** @type {object|null} Renderer reference for stats */
|
|
26
|
+
this.renderer = options.renderer || null;
|
|
27
|
+
|
|
28
|
+
/** @type {number} Maximum samples to keep */
|
|
29
|
+
this.maxSamples = options.maxSamples || 1000;
|
|
30
|
+
|
|
31
|
+
/** @type {boolean} Whether to track memory */
|
|
32
|
+
this.trackMemory = options.trackMemory !== false;
|
|
33
|
+
|
|
34
|
+
/** @type {number} Session start time */
|
|
35
|
+
this.sessionStart = performance.now();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Set renderer reference for draw call/triangle stats
|
|
40
|
+
* @param {object} renderer
|
|
41
|
+
*/
|
|
42
|
+
setRenderer(renderer) {
|
|
43
|
+
this.renderer = renderer;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Mark the beginning of a frame
|
|
48
|
+
*/
|
|
49
|
+
beginFrame() {
|
|
50
|
+
this.frameStart = performance.now();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Mark the end of a frame and record metrics
|
|
55
|
+
*/
|
|
56
|
+
endFrame() {
|
|
57
|
+
if (this.frameStart === null) {
|
|
58
|
+
console.warn('MetricsCollector: endFrame called without beginFrame');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const now = performance.now();
|
|
63
|
+
const frameTime = now - this.frameStart;
|
|
64
|
+
this.frameCount++;
|
|
65
|
+
|
|
66
|
+
const sample = {
|
|
67
|
+
timestamp: Date.now(),
|
|
68
|
+
frameNumber: this.frameCount,
|
|
69
|
+
frameTime,
|
|
70
|
+
fps: frameTime > 0 ? 1000 / frameTime : 0,
|
|
71
|
+
memory: this.trackMemory ? this.getMemoryUsage() : null,
|
|
72
|
+
drawCalls: this.getDrawCallCount(),
|
|
73
|
+
triangles: this.getTriangleCount()
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
this.samples.push(sample);
|
|
77
|
+
|
|
78
|
+
// Keep sample buffer bounded
|
|
79
|
+
if (this.samples.length > this.maxSamples) {
|
|
80
|
+
this.samples.shift();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.lastFrameTime = frameTime;
|
|
84
|
+
this.frameStart = null;
|
|
85
|
+
|
|
86
|
+
return sample;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get current memory usage (if available)
|
|
91
|
+
* @returns {object|null}
|
|
92
|
+
*/
|
|
93
|
+
getMemoryUsage() {
|
|
94
|
+
// Only available in Chrome with --enable-precise-memory-info flag
|
|
95
|
+
if (typeof performance !== 'undefined' && performance.memory) {
|
|
96
|
+
return {
|
|
97
|
+
used: performance.memory.usedJSHeapSize / 1024 / 1024,
|
|
98
|
+
total: performance.memory.totalJSHeapSize / 1024 / 1024,
|
|
99
|
+
limit: performance.memory.jsHeapSizeLimit / 1024 / 1024
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get draw call count from renderer
|
|
107
|
+
* @returns {number}
|
|
108
|
+
*/
|
|
109
|
+
getDrawCallCount() {
|
|
110
|
+
if (this.renderer?.stats?.drawCalls !== undefined) {
|
|
111
|
+
return this.renderer.stats.drawCalls;
|
|
112
|
+
}
|
|
113
|
+
if (this.renderer?.getStats) {
|
|
114
|
+
const stats = this.renderer.getStats();
|
|
115
|
+
return stats?.drawCalls ?? 0;
|
|
116
|
+
}
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get triangle count from renderer
|
|
122
|
+
* @returns {number}
|
|
123
|
+
*/
|
|
124
|
+
getTriangleCount() {
|
|
125
|
+
if (this.renderer?.stats?.triangles !== undefined) {
|
|
126
|
+
return this.renderer.stats.triangles;
|
|
127
|
+
}
|
|
128
|
+
if (this.renderer?.getStats) {
|
|
129
|
+
const stats = this.renderer.getStats();
|
|
130
|
+
return stats?.triangles ?? 0;
|
|
131
|
+
}
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Calculate statistics from collected samples
|
|
137
|
+
* @returns {object|null}
|
|
138
|
+
*/
|
|
139
|
+
getStats() {
|
|
140
|
+
if (this.samples.length === 0) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const frameTimes = this.samples.map(s => s.frameTime);
|
|
145
|
+
const fpsValues = this.samples.map(s => s.fps);
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
frameTime: {
|
|
149
|
+
min: Math.min(...frameTimes),
|
|
150
|
+
max: Math.max(...frameTimes),
|
|
151
|
+
avg: this.average(frameTimes),
|
|
152
|
+
median: this.median(frameTimes),
|
|
153
|
+
stdDev: this.standardDeviation(frameTimes),
|
|
154
|
+
p50: this.percentile(frameTimes, 50),
|
|
155
|
+
p90: this.percentile(frameTimes, 90),
|
|
156
|
+
p95: this.percentile(frameTimes, 95),
|
|
157
|
+
p99: this.percentile(frameTimes, 99)
|
|
158
|
+
},
|
|
159
|
+
fps: {
|
|
160
|
+
min: Math.min(...fpsValues),
|
|
161
|
+
max: Math.max(...fpsValues),
|
|
162
|
+
avg: this.average(fpsValues),
|
|
163
|
+
median: this.median(fpsValues)
|
|
164
|
+
},
|
|
165
|
+
memory: this.samples[this.samples.length - 1].memory,
|
|
166
|
+
drawCalls: this.samples[this.samples.length - 1].drawCalls,
|
|
167
|
+
triangles: this.samples[this.samples.length - 1].triangles,
|
|
168
|
+
sampleCount: this.samples.length,
|
|
169
|
+
totalFrames: this.frameCount,
|
|
170
|
+
sessionDuration: (performance.now() - this.sessionStart) / 1000
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Calculate percentile of array
|
|
176
|
+
* @param {number[]} arr
|
|
177
|
+
* @param {number} p - Percentile (0-100)
|
|
178
|
+
* @returns {number}
|
|
179
|
+
*/
|
|
180
|
+
percentile(arr, p) {
|
|
181
|
+
if (arr.length === 0) return 0;
|
|
182
|
+
const sorted = [...arr].sort((a, b) => a - b);
|
|
183
|
+
const index = Math.ceil((p / 100) * sorted.length) - 1;
|
|
184
|
+
return sorted[Math.max(0, index)];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Calculate median of array
|
|
189
|
+
* @param {number[]} arr
|
|
190
|
+
* @returns {number}
|
|
191
|
+
*/
|
|
192
|
+
median(arr) {
|
|
193
|
+
return this.percentile(arr, 50);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Calculate average of array
|
|
198
|
+
* @param {number[]} arr
|
|
199
|
+
* @returns {number}
|
|
200
|
+
*/
|
|
201
|
+
average(arr) {
|
|
202
|
+
if (arr.length === 0) return 0;
|
|
203
|
+
return arr.reduce((a, b) => a + b, 0) / arr.length;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Calculate standard deviation of array
|
|
208
|
+
* @param {number[]} arr
|
|
209
|
+
* @returns {number}
|
|
210
|
+
*/
|
|
211
|
+
standardDeviation(arr) {
|
|
212
|
+
if (arr.length === 0) return 0;
|
|
213
|
+
const avg = this.average(arr);
|
|
214
|
+
const squareDiffs = arr.map(value => Math.pow(value - avg, 2));
|
|
215
|
+
return Math.sqrt(this.average(squareDiffs));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Reset all collected data
|
|
220
|
+
*/
|
|
221
|
+
reset() {
|
|
222
|
+
this.samples = [];
|
|
223
|
+
this.frameCount = 0;
|
|
224
|
+
this.lastFrameTime = 0;
|
|
225
|
+
this.frameStart = null;
|
|
226
|
+
this.sessionStart = performance.now();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Export samples as CSV
|
|
231
|
+
* @returns {string}
|
|
232
|
+
*/
|
|
233
|
+
exportCSV() {
|
|
234
|
+
const headers = ['timestamp', 'frameNumber', 'frameTime', 'fps', 'memoryUsed', 'drawCalls', 'triangles'];
|
|
235
|
+
const rows = this.samples.map(s => [
|
|
236
|
+
s.timestamp,
|
|
237
|
+
s.frameNumber,
|
|
238
|
+
s.frameTime.toFixed(3),
|
|
239
|
+
s.fps.toFixed(1),
|
|
240
|
+
s.memory?.used?.toFixed(2) ?? '',
|
|
241
|
+
s.drawCalls,
|
|
242
|
+
s.triangles
|
|
243
|
+
]);
|
|
244
|
+
return [headers.join(','), ...rows.map(r => r.join(','))].join('\n');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Export samples as JSON
|
|
249
|
+
* @returns {object}
|
|
250
|
+
*/
|
|
251
|
+
exportJSON() {
|
|
252
|
+
return {
|
|
253
|
+
stats: this.getStats(),
|
|
254
|
+
samples: this.samples,
|
|
255
|
+
exportedAt: new Date().toISOString()
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Get last N samples
|
|
261
|
+
* @param {number} n
|
|
262
|
+
* @returns {object[]}
|
|
263
|
+
*/
|
|
264
|
+
getLastSamples(n) {
|
|
265
|
+
return this.samples.slice(-n);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Check if performance meets thresholds
|
|
270
|
+
* @param {object} thresholds
|
|
271
|
+
* @returns {object}
|
|
272
|
+
*/
|
|
273
|
+
checkThresholds(thresholds = {}) {
|
|
274
|
+
const stats = this.getStats();
|
|
275
|
+
if (!stats) return { passed: false, reason: 'No data' };
|
|
276
|
+
|
|
277
|
+
const defaults = {
|
|
278
|
+
maxFrameTime: 16.67, // 60fps
|
|
279
|
+
minFPS: 60,
|
|
280
|
+
maxMemory: 200 // MB
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const limits = { ...defaults, ...thresholds };
|
|
284
|
+
const results = {
|
|
285
|
+
frameTime: stats.frameTime.p95 <= limits.maxFrameTime,
|
|
286
|
+
fps: stats.fps.avg >= limits.minFPS,
|
|
287
|
+
memory: !stats.memory || stats.memory.used <= limits.maxMemory
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
passed: results.frameTime && results.fps && results.memory,
|
|
292
|
+
results,
|
|
293
|
+
stats,
|
|
294
|
+
thresholds: limits
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export default MetricsCollector;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VIB3+ Benchmarks Module
|
|
3
|
+
*
|
|
4
|
+
* Performance testing and metrics collection for the VIB3+ engine.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { MetricsCollector } from './MetricsCollector.js';
|
|
8
|
+
export { BenchmarkRunner } from './BenchmarkRunner.js';
|
|
9
|
+
export { BENCHMARK_SCENES, getScene, getSceneNames, getScenesByTier, getScenesBySystem } from './scenes.js';
|