@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.
Files changed (258) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/DOCS/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md +34 -0
  3. package/DOCS/CI_TESTING.md +38 -0
  4. package/DOCS/CLI_ONBOARDING.md +75 -0
  5. package/DOCS/CONTROL_REFERENCE.md +64 -0
  6. package/DOCS/DEV_TRACK_ANALYSIS.md +77 -0
  7. package/DOCS/DEV_TRACK_PLAN_2026-01-07.md +42 -0
  8. package/DOCS/DEV_TRACK_SESSION_2026-01-31.md +220 -0
  9. package/DOCS/ENV_SETUP.md +189 -0
  10. package/DOCS/EXPORT_FORMATS.md +417 -0
  11. package/DOCS/GPU_DISPOSAL_GUIDE.md +21 -0
  12. package/DOCS/LICENSING_TIERS.md +275 -0
  13. package/DOCS/MASTER_PLAN_2026-01-31.md +570 -0
  14. package/DOCS/OBS_SETUP_GUIDE.md +98 -0
  15. package/DOCS/PROJECT_SETUP.md +66 -0
  16. package/DOCS/RENDERER_LIFECYCLE.md +40 -0
  17. package/DOCS/REPO_MANIFEST.md +121 -0
  18. package/DOCS/SESSION_014_PLAN.md +195 -0
  19. package/DOCS/SESSION_LOG_2026-01-07.md +56 -0
  20. package/DOCS/STRATEGIC_BLUEPRINT_2026-01-07.md +72 -0
  21. package/DOCS/SYSTEM_AUDIT_2026-01-30.md +738 -0
  22. package/DOCS/SYSTEM_INVENTORY.md +520 -0
  23. package/DOCS/TELEMETRY_EXPORTS.md +34 -0
  24. package/DOCS/WEBGPU_STATUS.md +38 -0
  25. package/DOCS/XR_BENCHMARKS.md +608 -0
  26. package/LICENSE +21 -0
  27. package/README.md +426 -0
  28. package/docs/.nojekyll +0 -0
  29. package/docs/01-dissolution_of_euclidean_hegemony.html +346 -0
  30. package/docs/02-hyperspatial_ego_death.html +346 -0
  31. package/docs/03-post_cartesian_sublime.html +346 -0
  32. package/docs/04-crystalline_void_meditation.html +346 -0
  33. package/docs/05-quantum_decoherence_ballet.html +346 -0
  34. package/docs/06-dissolution_of_euclidean_hegemony.html +346 -0
  35. package/docs/07-hyperspatial_ego_death.html +346 -0
  36. package/docs/08-post_cartesian_sublime.html +346 -0
  37. package/docs/09-crystalline_void_meditation.html +346 -0
  38. package/docs/10-quantum_decoherence_ballet.html +346 -0
  39. package/docs/11-dissolution_of_euclidean_hegemony.html +346 -0
  40. package/docs/12-hyperspatial_ego_death.html +346 -0
  41. package/docs/13-post_cartesian_sublime.html +346 -0
  42. package/docs/index.html +794 -0
  43. package/docs/test-hub.html +441 -0
  44. package/docs/url-state.js +102 -0
  45. package/docs/vib3-exports/01-quantum-quantum-tetrahedron-lattice.html +489 -0
  46. package/docs/vib3-exports/02-quantum-quantum-hypersphere-matrix.html +489 -0
  47. package/docs/vib3-exports/03-quantum-quantum-hypertetra-fractal.html +489 -0
  48. package/docs/vib3-exports/04-faceted-faceted-crystal-structure.html +407 -0
  49. package/docs/vib3-exports/05-faceted-faceted-klein-bottle.html +407 -0
  50. package/docs/vib3-exports/06-faceted-faceted-hypertetra-torus.html +407 -0
  51. package/docs/vib3-exports/07-holographic-holographic-wave-field.html +457 -0
  52. package/docs/vib3-exports/08-holographic-holographic-hypersphere-sphere.html +457 -0
  53. package/docs/vib3-exports/09-holographic-holographic-hypertetra-crystal.html +457 -0
  54. package/docs/vib3-exports/index.html +238 -0
  55. package/docs/webgpu-live.html +702 -0
  56. package/package.json +367 -0
  57. package/src/advanced/AIPresetGenerator.js +777 -0
  58. package/src/advanced/MIDIController.js +703 -0
  59. package/src/advanced/OffscreenWorker.js +1051 -0
  60. package/src/advanced/WebGPUCompute.js +1051 -0
  61. package/src/advanced/WebXRRenderer.js +680 -0
  62. package/src/agent/cli/AgentCLI.js +615 -0
  63. package/src/agent/cli/index.js +14 -0
  64. package/src/agent/index.js +73 -0
  65. package/src/agent/mcp/MCPServer.js +950 -0
  66. package/src/agent/mcp/index.js +9 -0
  67. package/src/agent/mcp/tools.js +548 -0
  68. package/src/agent/telemetry/EventStream.js +669 -0
  69. package/src/agent/telemetry/Instrumentation.js +618 -0
  70. package/src/agent/telemetry/TelemetryExporters.js +427 -0
  71. package/src/agent/telemetry/TelemetryService.js +464 -0
  72. package/src/agent/telemetry/index.js +52 -0
  73. package/src/benchmarks/BenchmarkRunner.js +381 -0
  74. package/src/benchmarks/MetricsCollector.js +299 -0
  75. package/src/benchmarks/index.js +9 -0
  76. package/src/benchmarks/scenes.js +259 -0
  77. package/src/cli/index.js +675 -0
  78. package/src/config/ApiConfig.js +88 -0
  79. package/src/core/CanvasManager.js +217 -0
  80. package/src/core/ErrorReporter.js +117 -0
  81. package/src/core/ParameterMapper.js +333 -0
  82. package/src/core/Parameters.js +396 -0
  83. package/src/core/RendererContracts.js +200 -0
  84. package/src/core/UnifiedResourceManager.js +370 -0
  85. package/src/core/VIB3Engine.js +636 -0
  86. package/src/core/renderers/FacetedRendererAdapter.js +32 -0
  87. package/src/core/renderers/HolographicRendererAdapter.js +29 -0
  88. package/src/core/renderers/QuantumRendererAdapter.js +29 -0
  89. package/src/core/renderers/RendererLifecycleManager.js +63 -0
  90. package/src/creative/ColorPresetsSystem.js +980 -0
  91. package/src/creative/ParameterTimeline.js +1061 -0
  92. package/src/creative/PostProcessingPipeline.js +1113 -0
  93. package/src/creative/TransitionAnimator.js +683 -0
  94. package/src/export/CSSExporter.js +226 -0
  95. package/src/export/CardGeneratorBase.js +279 -0
  96. package/src/export/ExportManager.js +580 -0
  97. package/src/export/FacetedCardGenerator.js +279 -0
  98. package/src/export/HolographicCardGenerator.js +543 -0
  99. package/src/export/LottieExporter.js +552 -0
  100. package/src/export/QuantumCardGenerator.js +315 -0
  101. package/src/export/SVGExporter.js +519 -0
  102. package/src/export/ShaderExporter.js +903 -0
  103. package/src/export/TradingCardGenerator.js +3055 -0
  104. package/src/export/TradingCardManager.js +181 -0
  105. package/src/export/VIB3PackageExporter.js +559 -0
  106. package/src/export/index.js +14 -0
  107. package/src/export/systems/TradingCardSystemFaceted.js +494 -0
  108. package/src/export/systems/TradingCardSystemHolographic.js +452 -0
  109. package/src/export/systems/TradingCardSystemQuantum.js +411 -0
  110. package/src/faceted/FacetedSystem.js +963 -0
  111. package/src/features/CollectionManager.js +433 -0
  112. package/src/gallery/CollectionManager.js +240 -0
  113. package/src/gallery/GallerySystem.js +485 -0
  114. package/src/geometry/GeometryFactory.js +314 -0
  115. package/src/geometry/GeometryLibrary.js +72 -0
  116. package/src/geometry/buffers/BufferBuilder.js +338 -0
  117. package/src/geometry/buffers/index.js +18 -0
  118. package/src/geometry/generators/Crystal.js +420 -0
  119. package/src/geometry/generators/Fractal.js +298 -0
  120. package/src/geometry/generators/KleinBottle.js +197 -0
  121. package/src/geometry/generators/Sphere.js +192 -0
  122. package/src/geometry/generators/Tesseract.js +160 -0
  123. package/src/geometry/generators/Tetrahedron.js +225 -0
  124. package/src/geometry/generators/Torus.js +304 -0
  125. package/src/geometry/generators/Wave.js +341 -0
  126. package/src/geometry/index.js +142 -0
  127. package/src/geometry/warp/HypersphereCore.js +211 -0
  128. package/src/geometry/warp/HypertetraCore.js +386 -0
  129. package/src/geometry/warp/index.js +57 -0
  130. package/src/holograms/HolographicVisualizer.js +1073 -0
  131. package/src/holograms/RealHolographicSystem.js +966 -0
  132. package/src/holograms/variantRegistry.js +69 -0
  133. package/src/integrations/FigmaPlugin.js +854 -0
  134. package/src/integrations/OBSMode.js +754 -0
  135. package/src/integrations/ThreeJsPackage.js +660 -0
  136. package/src/integrations/TouchDesignerExport.js +552 -0
  137. package/src/integrations/frameworks/Vib3React.js +591 -0
  138. package/src/integrations/frameworks/Vib3Svelte.js +654 -0
  139. package/src/integrations/frameworks/Vib3Vue.js +628 -0
  140. package/src/llm/LLMParameterInterface.js +240 -0
  141. package/src/llm/LLMParameterUI.js +577 -0
  142. package/src/math/Mat4x4.js +708 -0
  143. package/src/math/Projection.js +341 -0
  144. package/src/math/Rotor4D.js +637 -0
  145. package/src/math/Vec4.js +476 -0
  146. package/src/math/constants.js +164 -0
  147. package/src/math/index.js +68 -0
  148. package/src/math/projections.js +54 -0
  149. package/src/math/rotations.js +196 -0
  150. package/src/quantum/QuantumEngine.js +906 -0
  151. package/src/quantum/QuantumVisualizer.js +1103 -0
  152. package/src/reactivity/ReactivityConfig.js +499 -0
  153. package/src/reactivity/ReactivityManager.js +586 -0
  154. package/src/reactivity/SpatialInputSystem.js +1783 -0
  155. package/src/reactivity/index.js +93 -0
  156. package/src/render/CommandBuffer.js +465 -0
  157. package/src/render/MultiCanvasBridge.js +340 -0
  158. package/src/render/RenderCommand.js +514 -0
  159. package/src/render/RenderResourceRegistry.js +523 -0
  160. package/src/render/RenderState.js +552 -0
  161. package/src/render/RenderTarget.js +512 -0
  162. package/src/render/ShaderLoader.js +253 -0
  163. package/src/render/ShaderProgram.js +599 -0
  164. package/src/render/UnifiedRenderBridge.js +496 -0
  165. package/src/render/backends/WebGLBackend.js +1108 -0
  166. package/src/render/backends/WebGPUBackend.js +1409 -0
  167. package/src/render/commands/CommandBufferExecutor.js +607 -0
  168. package/src/render/commands/RenderCommandBuffer.js +661 -0
  169. package/src/render/commands/index.js +17 -0
  170. package/src/render/index.js +367 -0
  171. package/src/scene/Disposable.js +498 -0
  172. package/src/scene/MemoryPool.js +618 -0
  173. package/src/scene/Node4D.js +697 -0
  174. package/src/scene/ResourceManager.js +599 -0
  175. package/src/scene/Scene4D.js +540 -0
  176. package/src/scene/index.js +98 -0
  177. package/src/schemas/error.schema.json +84 -0
  178. package/src/schemas/extension.schema.json +88 -0
  179. package/src/schemas/index.js +214 -0
  180. package/src/schemas/parameters.schema.json +142 -0
  181. package/src/schemas/tool-pack.schema.json +44 -0
  182. package/src/schemas/tool-response.schema.json +127 -0
  183. package/src/shaders/common/fullscreen.vert.glsl +5 -0
  184. package/src/shaders/common/fullscreen.vert.wgsl +17 -0
  185. package/src/shaders/common/geometry24.glsl +65 -0
  186. package/src/shaders/common/geometry24.wgsl +54 -0
  187. package/src/shaders/common/rotation4d.glsl +85 -0
  188. package/src/shaders/common/rotation4d.wgsl +86 -0
  189. package/src/shaders/common/uniforms.glsl +44 -0
  190. package/src/shaders/common/uniforms.wgsl +48 -0
  191. package/src/shaders/faceted/faceted.frag.glsl +129 -0
  192. package/src/shaders/faceted/faceted.frag.wgsl +164 -0
  193. package/src/shaders/holographic/holographic.frag.glsl +406 -0
  194. package/src/shaders/holographic/holographic.frag.wgsl +185 -0
  195. package/src/shaders/quantum/quantum.frag.glsl +513 -0
  196. package/src/shaders/quantum/quantum.frag.wgsl +361 -0
  197. package/src/testing/ParallelTestFramework.js +519 -0
  198. package/src/testing/__snapshots__/exportFormats.test.js.snap +24 -0
  199. package/src/testing/exportFormats.test.js +8 -0
  200. package/src/testing/projections.test.js +14 -0
  201. package/src/testing/rotations.test.js +37 -0
  202. package/src/ui/InteractivityMenu.js +516 -0
  203. package/src/ui/StatusManager.js +96 -0
  204. package/src/ui/adaptive/renderers/webgpu/BufferLayout.ts +252 -0
  205. package/src/ui/adaptive/renderers/webgpu/PolytopeInstanceBuffer.ts +144 -0
  206. package/src/ui/adaptive/renderers/webgpu/TripleBufferedUniform.ts +170 -0
  207. package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +735 -0
  208. package/src/ui/adaptive/renderers/webgpu/index.ts +112 -0
  209. package/src/variations/VariationManager.js +431 -0
  210. package/src/viewer/AudioReactivity.js +505 -0
  211. package/src/viewer/CardBending.js +481 -0
  212. package/src/viewer/GalleryUI.js +832 -0
  213. package/src/viewer/ReactivityManager.js +590 -0
  214. package/src/viewer/TradingCardExporter.js +600 -0
  215. package/src/viewer/ViewerPortal.js +374 -0
  216. package/src/viewer/index.js +12 -0
  217. package/src/wasm/WasmLoader.js +296 -0
  218. package/src/wasm/index.js +132 -0
  219. package/tools/agentic/mcpTools.js +88 -0
  220. package/tools/cli/agent-cli.js +92 -0
  221. package/tools/export/formats.js +24 -0
  222. package/tools/math/rotation-baseline.mjs +64 -0
  223. package/tools/shader-sync-verify.js +937 -0
  224. package/tools/telemetry/manifestPipeline.js +141 -0
  225. package/tools/telemetry/telemetryEvents.js +35 -0
  226. package/types/adaptive-sdk.d.ts +185 -0
  227. package/types/advanced/AIPresetGenerator.d.ts +81 -0
  228. package/types/advanced/MIDIController.d.ts +100 -0
  229. package/types/advanced/OffscreenWorker.d.ts +82 -0
  230. package/types/advanced/WebGPUCompute.d.ts +52 -0
  231. package/types/advanced/WebXRRenderer.d.ts +77 -0
  232. package/types/advanced/index.d.ts +46 -0
  233. package/types/core/ErrorReporter.d.ts +50 -0
  234. package/types/core/VIB3Engine.d.ts +204 -0
  235. package/types/creative/ColorPresetsSystem.d.ts +91 -0
  236. package/types/creative/ParameterTimeline.d.ts +74 -0
  237. package/types/creative/PostProcessingPipeline.d.ts +109 -0
  238. package/types/creative/TransitionAnimator.d.ts +71 -0
  239. package/types/creative/index.d.ts +35 -0
  240. package/types/integrations/FigmaPlugin.d.ts +46 -0
  241. package/types/integrations/OBSMode.d.ts +74 -0
  242. package/types/integrations/ThreeJsPackage.d.ts +62 -0
  243. package/types/integrations/TouchDesignerExport.d.ts +36 -0
  244. package/types/integrations/Vib3React.d.ts +74 -0
  245. package/types/integrations/Vib3Svelte.d.ts +63 -0
  246. package/types/integrations/Vib3Vue.d.ts +55 -0
  247. package/types/integrations/index.d.ts +52 -0
  248. package/types/reactivity/SpatialInputSystem.d.ts +173 -0
  249. package/types/reactivity/index.d.ts +394 -0
  250. package/types/render/CommandBuffer.d.ts +169 -0
  251. package/types/render/RenderCommand.d.ts +312 -0
  252. package/types/render/RenderState.d.ts +279 -0
  253. package/types/render/RenderTarget.d.ts +254 -0
  254. package/types/render/ShaderProgram.d.ts +277 -0
  255. package/types/render/UnifiedRenderBridge.d.ts +143 -0
  256. package/types/render/WebGLBackend.d.ts +168 -0
  257. package/types/render/WebGPUBackend.d.ts +186 -0
  258. package/types/render/index.d.ts +141 -0
@@ -0,0 +1,1409 @@
1
+ /**
2
+ * WebGPUBackend - WebGPU rendering backend for VIB3+ engine
3
+ *
4
+ * Features:
5
+ * - Device/context initialization with feature detection
6
+ * - Canvas configuration + resize handling
7
+ * - Shader pipeline management (custom WGSL compilation)
8
+ * - Uniform buffer handling (custom layouts)
9
+ * - Fullscreen quad procedural rendering (core VIB3+ pattern)
10
+ * - Multi-pipeline support with named pipelines
11
+ * - Texture creation and binding
12
+ * - Render state management
13
+ */
14
+
15
+ import { RenderResourceRegistry } from '../RenderResourceRegistry.js';
16
+
17
+ /**
18
+ * Default vertex shader for geometry rendering
19
+ */
20
+ const DEFAULT_VERTEX_SHADER = /* wgsl */`
21
+ struct Uniforms {
22
+ modelMatrix: mat4x4<f32>,
23
+ viewMatrix: mat4x4<f32>,
24
+ projectionMatrix: mat4x4<f32>,
25
+ time: f32,
26
+ dimension: f32,
27
+ _padding: vec2<f32>,
28
+ };
29
+
30
+ @group(0) @binding(0) var<uniform> uniforms: Uniforms;
31
+
32
+ struct VertexInput {
33
+ @location(0) position: vec4<f32>,
34
+ @location(1) color: vec4<f32>,
35
+ };
36
+
37
+ struct VertexOutput {
38
+ @builtin(position) position: vec4<f32>,
39
+ @location(0) color: vec4<f32>,
40
+ @location(1) worldPos: vec3<f32>,
41
+ };
42
+
43
+ @vertex
44
+ fn main(input: VertexInput) -> VertexOutput {
45
+ var output: VertexOutput;
46
+
47
+ // Apply 4D to 3D projection based on dimension parameter
48
+ let w = input.position.w;
49
+ let projectionFactor = 1.0 / (uniforms.dimension - w);
50
+ let projected = vec4<f32>(
51
+ input.position.x * projectionFactor,
52
+ input.position.y * projectionFactor,
53
+ input.position.z * projectionFactor,
54
+ 1.0
55
+ );
56
+
57
+ // Apply model-view-projection
58
+ let worldPos = uniforms.modelMatrix * projected;
59
+ let viewPos = uniforms.viewMatrix * worldPos;
60
+ output.position = uniforms.projectionMatrix * viewPos;
61
+ output.worldPos = worldPos.xyz;
62
+ output.color = input.color;
63
+
64
+ return output;
65
+ }
66
+ `;
67
+
68
+ /**
69
+ * Default fragment shader for geometry rendering
70
+ */
71
+ const DEFAULT_FRAGMENT_SHADER = /* wgsl */`
72
+ struct FragmentInput {
73
+ @location(0) color: vec4<f32>,
74
+ @location(1) worldPos: vec3<f32>,
75
+ };
76
+
77
+ @fragment
78
+ fn main(input: FragmentInput) -> @location(0) vec4<f32> {
79
+ // Add subtle depth-based shading
80
+ let depth = clamp(length(input.worldPos) * 0.2, 0.0, 1.0);
81
+ let shaded = input.color.rgb * (1.0 - depth * 0.3);
82
+
83
+ return vec4<f32>(shaded, input.color.a);
84
+ }
85
+ `;
86
+
87
+ /**
88
+ * Fullscreen quad vertex shader for procedural rendering.
89
+ * Generates a fullscreen triangle (3 vertices, no vertex buffer needed).
90
+ */
91
+ const FULLSCREEN_VERTEX_SHADER = /* wgsl */`
92
+ struct VertexOutput {
93
+ @builtin(position) position: vec4<f32>,
94
+ @location(0) uv: vec2<f32>,
95
+ };
96
+
97
+ @vertex
98
+ fn main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
99
+ var output: VertexOutput;
100
+
101
+ // Generate fullscreen triangle (covers entire screen with 3 vertices)
102
+ // Uses the oversized triangle technique - no vertex buffer needed
103
+ let x = f32(i32(vertexIndex & 1u) * 4 - 1);
104
+ let y = f32(i32(vertexIndex >> 1u) * 4 - 1);
105
+ output.position = vec4<f32>(x, y, 0.0, 1.0);
106
+ output.uv = vec2<f32>((x + 1.0) * 0.5, (1.0 - y) * 0.5);
107
+
108
+ return output;
109
+ }
110
+ `;
111
+
112
+ /**
113
+ * VIB3+ standard uniform struct for procedural shaders (WGSL).
114
+ * Matches the GLSL uniforms used in Quantum/Faceted/Holographic systems.
115
+ */
116
+ const VIB3_UNIFORM_STRUCT = /* wgsl */`
117
+ struct VIB3Uniforms {
118
+ // Time and resolution
119
+ time: f32,
120
+ _pad0: f32,
121
+ resolution: vec2<f32>,
122
+
123
+ // Geometry selection (0-23)
124
+ geometry: f32,
125
+
126
+ // 6D Rotation (radians)
127
+ rot4dXY: f32,
128
+ rot4dXZ: f32,
129
+ rot4dYZ: f32,
130
+ rot4dXW: f32,
131
+ rot4dYW: f32,
132
+ rot4dZW: f32,
133
+
134
+ // Visual parameters
135
+ dimension: f32,
136
+ gridDensity: f32,
137
+ morphFactor: f32,
138
+ chaos: f32,
139
+ speed: f32,
140
+ hue: f32,
141
+ intensity: f32,
142
+ saturation: f32,
143
+
144
+ // Reactivity
145
+ mouseIntensity: f32,
146
+ clickIntensity: f32,
147
+ bass: f32,
148
+ mid: f32,
149
+ high: f32,
150
+
151
+ // Layer parameters (for holographic multi-layer)
152
+ layerScale: f32,
153
+ layerOpacity: f32,
154
+ _pad1: f32,
155
+ layerColor: vec3<f32>,
156
+ densityMult: f32,
157
+ speedMult: f32,
158
+ _pad2: vec3<f32>,
159
+ };
160
+ `;
161
+
162
+ /**
163
+ * VIB3+ 4D rotation functions in WGSL
164
+ */
165
+ const VIB3_ROTATION_WGSL = /* wgsl */`
166
+ fn rotateXY(angle: f32) -> mat4x4<f32> {
167
+ let c = cos(angle);
168
+ let s = sin(angle);
169
+ return mat4x4<f32>(
170
+ vec4<f32>(c, -s, 0.0, 0.0),
171
+ vec4<f32>(s, c, 0.0, 0.0),
172
+ vec4<f32>(0.0, 0.0, 1.0, 0.0),
173
+ vec4<f32>(0.0, 0.0, 0.0, 1.0)
174
+ );
175
+ }
176
+
177
+ fn rotateXZ(angle: f32) -> mat4x4<f32> {
178
+ let c = cos(angle);
179
+ let s = sin(angle);
180
+ return mat4x4<f32>(
181
+ vec4<f32>(c, 0.0, -s, 0.0),
182
+ vec4<f32>(0.0, 1.0, 0.0, 0.0),
183
+ vec4<f32>(s, 0.0, c, 0.0),
184
+ vec4<f32>(0.0, 0.0, 0.0, 1.0)
185
+ );
186
+ }
187
+
188
+ fn rotateYZ(angle: f32) -> mat4x4<f32> {
189
+ let c = cos(angle);
190
+ let s = sin(angle);
191
+ return mat4x4<f32>(
192
+ vec4<f32>(1.0, 0.0, 0.0, 0.0),
193
+ vec4<f32>(0.0, c, -s, 0.0),
194
+ vec4<f32>(0.0, s, c, 0.0),
195
+ vec4<f32>(0.0, 0.0, 0.0, 1.0)
196
+ );
197
+ }
198
+
199
+ fn rotateXW(angle: f32) -> mat4x4<f32> {
200
+ let c = cos(angle);
201
+ let s = sin(angle);
202
+ return mat4x4<f32>(
203
+ vec4<f32>(c, 0.0, 0.0, -s),
204
+ vec4<f32>(0.0, 1.0, 0.0, 0.0),
205
+ vec4<f32>(0.0, 0.0, 1.0, 0.0),
206
+ vec4<f32>(s, 0.0, 0.0, c)
207
+ );
208
+ }
209
+
210
+ fn rotateYW(angle: f32) -> mat4x4<f32> {
211
+ let c = cos(angle);
212
+ let s = sin(angle);
213
+ return mat4x4<f32>(
214
+ vec4<f32>(1.0, 0.0, 0.0, 0.0),
215
+ vec4<f32>(0.0, c, 0.0, -s),
216
+ vec4<f32>(0.0, 0.0, 1.0, 0.0),
217
+ vec4<f32>(0.0, s, 0.0, c)
218
+ );
219
+ }
220
+
221
+ fn rotateZW(angle: f32) -> mat4x4<f32> {
222
+ let c = cos(angle);
223
+ let s = sin(angle);
224
+ return mat4x4<f32>(
225
+ vec4<f32>(1.0, 0.0, 0.0, 0.0),
226
+ vec4<f32>(0.0, 1.0, 0.0, 0.0),
227
+ vec4<f32>(0.0, 0.0, c, -s),
228
+ vec4<f32>(0.0, 0.0, s, c)
229
+ );
230
+ }
231
+
232
+ fn apply6DRotation(pos: vec4<f32>, u: VIB3Uniforms) -> vec4<f32> {
233
+ var p = pos;
234
+ p = rotateXY(u.rot4dXY) * p;
235
+ p = rotateXZ(u.rot4dXZ) * p;
236
+ p = rotateYZ(u.rot4dYZ) * p;
237
+ p = rotateXW(u.rot4dXW) * p;
238
+ p = rotateYW(u.rot4dYW) * p;
239
+ p = rotateZW(u.rot4dZW) * p;
240
+ return p;
241
+ }
242
+ `;
243
+
244
+ /**
245
+ * WebGPU feature flags
246
+ */
247
+ export const WebGPUFeatures = {
248
+ TIMESTAMP_QUERY: 'timestamp-query',
249
+ INDIRECT_FIRST_INSTANCE: 'indirect-first-instance',
250
+ SHADER_F16: 'shader-f16',
251
+ DEPTH_CLIP_CONTROL: 'depth-clip-control',
252
+ DEPTH32_STENCIL8: 'depth32float-stencil8',
253
+ TEXTURE_COMPRESSION_BC: 'texture-compression-bc',
254
+ RG11B10_UFLOAT_RENDERABLE: 'rg11b10ufloat-renderable',
255
+ BGRA8_UNORM_STORAGE: 'bgra8unorm-storage'
256
+ };
257
+
258
+ /**
259
+ * WGSL shader library for VIB3+ systems
260
+ */
261
+ export const WGSLShaderLib = {
262
+ uniformStruct: VIB3_UNIFORM_STRUCT,
263
+ rotation4D: VIB3_ROTATION_WGSL,
264
+ fullscreenVertex: FULLSCREEN_VERTEX_SHADER
265
+ };
266
+
267
+ export class WebGPUBackend {
268
+ /**
269
+ * @param {object} params
270
+ * @param {HTMLCanvasElement} params.canvas
271
+ * @param {GPUDevice} params.device
272
+ * @param {GPUCanvasContext} params.context
273
+ * @param {GPUTextureFormat} params.format
274
+ * @param {GPUAdapter} [params.adapter]
275
+ * @param {object} [options]
276
+ */
277
+ constructor({ canvas, device, context, format, adapter }, options = {}) {
278
+ this.canvas = canvas;
279
+ this.device = device;
280
+ this.context = context;
281
+ this.format = format;
282
+ this.adapter = adapter || null;
283
+
284
+ this.debug = options.debug || false;
285
+ this.depthEnabled = options.depth !== false;
286
+ this._resources = options.resourceRegistry || new RenderResourceRegistry();
287
+
288
+ /** @type {GPUTexture|null} */
289
+ this._depthTexture = null;
290
+
291
+ /** @type {Map<string, GPURenderPipeline>} */
292
+ this._pipelines = new Map();
293
+
294
+ /** @type {Map<string, GPUShaderModule>} */
295
+ this._shaderModules = new Map();
296
+
297
+ /** @type {GPUBuffer|null} - Default geometry uniform buffer */
298
+ this._uniformBuffer = null;
299
+
300
+ /** @type {GPUBindGroup|null} */
301
+ this._uniformBindGroup = null;
302
+
303
+ /** @type {GPUBindGroupLayout|null} */
304
+ this._uniformBindGroupLayout = null;
305
+
306
+ /** @type {Map<string, {buffer: GPUBuffer, bindGroup: GPUBindGroup, layout: GPUBindGroupLayout}>} */
307
+ this._customUniformBuffers = new Map();
308
+
309
+ /** @type {Map<string, GPUTexture>} */
310
+ this._textures = new Map();
311
+
312
+ /** @type {Map<string, GPUSampler>} */
313
+ this._samplers = new Map();
314
+
315
+ /** @type {Set<string>} */
316
+ this._enabledFeatures = new Set(options.features || []);
317
+
318
+ this._stats = {
319
+ frames: 0,
320
+ commandEncoders: 0,
321
+ drawCalls: 0,
322
+ triangles: 0,
323
+ pipelineChanges: 0
324
+ };
325
+
326
+ // Initialize uniform buffer
327
+ this._initUniformBuffer();
328
+
329
+ // Create default pipeline
330
+ this._createDefaultPipeline();
331
+
332
+ this.resize(canvas.clientWidth || canvas.width, canvas.clientHeight || canvas.height);
333
+ }
334
+
335
+ // ========================================================================
336
+ // Feature Detection
337
+ // ========================================================================
338
+
339
+ /**
340
+ * Check if a feature is supported
341
+ * @param {string} feature
342
+ * @returns {boolean}
343
+ */
344
+ hasFeature(feature) {
345
+ return this._enabledFeatures.has(feature);
346
+ }
347
+
348
+ /**
349
+ * Get GPU info
350
+ * @returns {object}
351
+ */
352
+ getGPUInfo() {
353
+ if (!this.adapter) return { vendor: 'unknown', architecture: 'unknown' };
354
+
355
+ return {
356
+ vendor: this.adapter.info?.vendor || 'unknown',
357
+ architecture: this.adapter.info?.architecture || 'unknown',
358
+ device: this.adapter.info?.device || 'unknown',
359
+ description: this.adapter.info?.description || 'unknown',
360
+ features: Array.from(this._enabledFeatures)
361
+ };
362
+ }
363
+
364
+ // ========================================================================
365
+ // Uniform Buffer Management
366
+ // ========================================================================
367
+
368
+ /**
369
+ * Initialize default uniform buffer (geometry rendering)
370
+ * @private
371
+ */
372
+ _initUniformBuffer() {
373
+ const uniformBufferSize = 256;
374
+
375
+ this._uniformBuffer = this.device.createBuffer({
376
+ size: uniformBufferSize,
377
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
378
+ });
379
+ this._resources.register('buffer', this._uniformBuffer);
380
+
381
+ this._uniformBindGroupLayout = this.device.createBindGroupLayout({
382
+ entries: [{
383
+ binding: 0,
384
+ visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
385
+ buffer: { type: 'uniform' }
386
+ }]
387
+ });
388
+
389
+ this._uniformBindGroup = this.device.createBindGroup({
390
+ layout: this._uniformBindGroupLayout,
391
+ entries: [{
392
+ binding: 0,
393
+ resource: { buffer: this._uniformBuffer }
394
+ }]
395
+ });
396
+ }
397
+
398
+ /**
399
+ * Create a custom uniform buffer with its own bind group.
400
+ * Used for VIB3+ procedural shader uniforms.
401
+ * @param {string} name - Unique name for the buffer
402
+ * @param {number} size - Buffer size in bytes (will be aligned to 256)
403
+ * @param {number} [visibility] - Shader stage visibility
404
+ * @returns {{buffer: GPUBuffer, bindGroup: GPUBindGroup, layout: GPUBindGroupLayout}}
405
+ */
406
+ createCustomUniformBuffer(name, size, visibility) {
407
+ const vis = visibility ??
408
+ (GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT);
409
+
410
+ // Align to 256 bytes for WebGPU requirements
411
+ const alignedSize = Math.ceil(size / 256) * 256;
412
+
413
+ const buffer = this.device.createBuffer({
414
+ size: alignedSize,
415
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
416
+ });
417
+ this._resources.register('buffer', buffer);
418
+
419
+ const layout = this.device.createBindGroupLayout({
420
+ entries: [{
421
+ binding: 0,
422
+ visibility: vis,
423
+ buffer: { type: 'uniform' }
424
+ }]
425
+ });
426
+
427
+ const bindGroup = this.device.createBindGroup({
428
+ layout,
429
+ entries: [{
430
+ binding: 0,
431
+ resource: { buffer }
432
+ }]
433
+ });
434
+
435
+ const entry = { buffer, bindGroup, layout };
436
+ this._customUniformBuffers.set(name, entry);
437
+ return entry;
438
+ }
439
+
440
+ /**
441
+ * Update a custom uniform buffer with Float32Array data
442
+ * @param {string} name - Buffer name
443
+ * @param {Float32Array} data - Data to write
444
+ */
445
+ updateCustomUniforms(name, data) {
446
+ const entry = this._customUniformBuffers.get(name);
447
+ if (!entry) {
448
+ if (this.debug) {
449
+ console.warn(`Custom uniform buffer "${name}" not found`);
450
+ }
451
+ return;
452
+ }
453
+ this.device.queue.writeBuffer(entry.buffer, 0, data);
454
+ }
455
+
456
+ /**
457
+ * Get a custom uniform buffer entry
458
+ * @param {string} name
459
+ * @returns {{buffer: GPUBuffer, bindGroup: GPUBindGroup, layout: GPUBindGroupLayout}|undefined}
460
+ */
461
+ getCustomUniformBuffer(name) {
462
+ return this._customUniformBuffers.get(name);
463
+ }
464
+
465
+ /**
466
+ * Update default uniform buffer with current state
467
+ * @param {object} uniforms
468
+ */
469
+ updateUniforms(uniforms) {
470
+ const data = new Float32Array(64); // 256 bytes / 4
471
+
472
+ const model = uniforms.modelMatrix || [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
473
+ data.set(model, 0);
474
+
475
+ const view = uniforms.viewMatrix || [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,-3,1];
476
+ data.set(view, 16);
477
+
478
+ const proj = uniforms.projectionMatrix || this._createProjectionMatrix();
479
+ data.set(proj, 32);
480
+
481
+ data[48] = uniforms.time || 0;
482
+ data[49] = uniforms.dimension || 3.5;
483
+
484
+ this.device.queue.writeBuffer(this._uniformBuffer, 0, data);
485
+ }
486
+
487
+ /**
488
+ * Create perspective projection matrix
489
+ * @private
490
+ */
491
+ _createProjectionMatrix() {
492
+ const fov = Math.PI / 4;
493
+ const aspect = this.canvas.width / this.canvas.height;
494
+ const near = 0.1;
495
+ const far = 100;
496
+
497
+ const f = 1.0 / Math.tan(fov / 2);
498
+ const rangeInv = 1 / (near - far);
499
+
500
+ return [
501
+ f / aspect, 0, 0, 0,
502
+ 0, f, 0, 0,
503
+ 0, 0, (near + far) * rangeInv, -1,
504
+ 0, 0, near * far * rangeInv * 2, 0
505
+ ];
506
+ }
507
+
508
+ // ========================================================================
509
+ // Shader & Pipeline Management
510
+ // ========================================================================
511
+
512
+ /**
513
+ * Compile a WGSL shader module from source code
514
+ * @param {string} name - Unique shader name
515
+ * @param {string} code - WGSL source code
516
+ * @returns {GPUShaderModule}
517
+ */
518
+ compileShader(name, code) {
519
+ const module = this.device.createShaderModule({
520
+ code,
521
+ label: name
522
+ });
523
+ this._shaderModules.set(name, module);
524
+ return module;
525
+ }
526
+
527
+ /**
528
+ * Get or create shader module
529
+ * @param {string} name
530
+ * @param {string} code
531
+ * @returns {GPUShaderModule}
532
+ */
533
+ _getOrCreateShaderModule(name, code) {
534
+ if (this._shaderModules.has(name)) {
535
+ return this._shaderModules.get(name);
536
+ }
537
+ return this.compileShader(name, code);
538
+ }
539
+
540
+ /**
541
+ * Create a fullscreen quad pipeline for procedural fragment rendering.
542
+ * This is the core rendering pattern for all VIB3+ visualization systems.
543
+ *
544
+ * @param {string} name - Pipeline name
545
+ * @param {string} fragmentCode - WGSL fragment shader code
546
+ * @param {object} [options]
547
+ * @param {string} [options.vertexCode] - Custom vertex shader (defaults to fullscreen quad)
548
+ * @param {GPUBindGroupLayout[]} [options.bindGroupLayouts] - Custom bind group layouts
549
+ * @param {object} [options.blend] - Custom blend state
550
+ * @param {boolean} [options.depth] - Enable depth testing
551
+ * @returns {GPURenderPipeline}
552
+ */
553
+ createFullscreenPipeline(name, fragmentCode, options = {}) {
554
+ const vertexCode = options.vertexCode || FULLSCREEN_VERTEX_SHADER;
555
+ const vertexModule = this._getOrCreateShaderModule(
556
+ `${name}-vertex`, vertexCode
557
+ );
558
+ const fragmentModule = this._getOrCreateShaderModule(
559
+ `${name}-fragment`, fragmentCode
560
+ );
561
+
562
+ // Use provided layouts or default to the standard uniform layout
563
+ const bindGroupLayouts = options.bindGroupLayouts ||
564
+ [this._uniformBindGroupLayout];
565
+
566
+ const pipelineLayout = this.device.createPipelineLayout({
567
+ bindGroupLayouts
568
+ });
569
+
570
+ const blend = options.blend || {
571
+ color: {
572
+ srcFactor: 'src-alpha',
573
+ dstFactor: 'one-minus-src-alpha',
574
+ operation: 'add'
575
+ },
576
+ alpha: {
577
+ srcFactor: 'one',
578
+ dstFactor: 'one-minus-src-alpha',
579
+ operation: 'add'
580
+ }
581
+ };
582
+
583
+ const useDepth = options.depth !== undefined ? options.depth : false;
584
+
585
+ const pipeline = this.device.createRenderPipeline({
586
+ layout: pipelineLayout,
587
+ vertex: {
588
+ module: vertexModule,
589
+ entryPoint: 'main',
590
+ // No vertex buffers - fullscreen triangle uses vertex_index
591
+ },
592
+ fragment: {
593
+ module: fragmentModule,
594
+ entryPoint: 'main',
595
+ targets: [{
596
+ format: this.format,
597
+ blend
598
+ }]
599
+ },
600
+ primitive: {
601
+ topology: 'triangle-list'
602
+ },
603
+ depthStencil: useDepth ? {
604
+ depthWriteEnabled: true,
605
+ depthCompare: 'less',
606
+ format: 'depth24plus'
607
+ } : undefined
608
+ });
609
+
610
+ this._pipelines.set(name, pipeline);
611
+ return pipeline;
612
+ }
613
+
614
+ /**
615
+ * Create a custom render pipeline with vertex buffers (for geometry rendering)
616
+ * @param {string} name - Pipeline name
617
+ * @param {object} desc - Pipeline descriptor
618
+ * @param {string} desc.vertexCode - WGSL vertex shader
619
+ * @param {string} desc.fragmentCode - WGSL fragment shader
620
+ * @param {GPUVertexBufferLayout[]} [desc.vertexBuffers] - Vertex buffer layouts
621
+ * @param {GPUBindGroupLayout[]} [desc.bindGroupLayouts]
622
+ * @param {object} [desc.blend]
623
+ * @param {string} [desc.topology]
624
+ * @param {boolean} [desc.depth]
625
+ * @returns {GPURenderPipeline}
626
+ */
627
+ createPipeline(name, desc) {
628
+ const vertexModule = this._getOrCreateShaderModule(
629
+ `${name}-vertex`, desc.vertexCode
630
+ );
631
+ const fragmentModule = this._getOrCreateShaderModule(
632
+ `${name}-fragment`, desc.fragmentCode
633
+ );
634
+
635
+ const bindGroupLayouts = desc.bindGroupLayouts ||
636
+ [this._uniformBindGroupLayout];
637
+
638
+ const pipelineLayout = this.device.createPipelineLayout({
639
+ bindGroupLayouts
640
+ });
641
+
642
+ const blend = desc.blend || {
643
+ color: {
644
+ srcFactor: 'src-alpha',
645
+ dstFactor: 'one-minus-src-alpha',
646
+ operation: 'add'
647
+ },
648
+ alpha: {
649
+ srcFactor: 'one',
650
+ dstFactor: 'one-minus-src-alpha',
651
+ operation: 'add'
652
+ }
653
+ };
654
+
655
+ const pipeline = this.device.createRenderPipeline({
656
+ layout: pipelineLayout,
657
+ vertex: {
658
+ module: vertexModule,
659
+ entryPoint: 'main',
660
+ buffers: desc.vertexBuffers || [{
661
+ arrayStride: 32,
662
+ attributes: [
663
+ { shaderLocation: 0, offset: 0, format: 'float32x4' },
664
+ { shaderLocation: 1, offset: 16, format: 'float32x4' }
665
+ ]
666
+ }]
667
+ },
668
+ fragment: {
669
+ module: fragmentModule,
670
+ entryPoint: 'main',
671
+ targets: [{
672
+ format: this.format,
673
+ blend
674
+ }]
675
+ },
676
+ primitive: {
677
+ topology: desc.topology || 'triangle-list',
678
+ cullMode: desc.cullMode || 'none',
679
+ frontFace: 'ccw'
680
+ },
681
+ depthStencil: (desc.depth !== false && this.depthEnabled) ? {
682
+ depthWriteEnabled: true,
683
+ depthCompare: 'less',
684
+ format: 'depth24plus'
685
+ } : undefined
686
+ });
687
+
688
+ this._pipelines.set(name, pipeline);
689
+ return pipeline;
690
+ }
691
+
692
+ /**
693
+ * Get a named pipeline
694
+ * @param {string} name
695
+ * @returns {GPURenderPipeline|undefined}
696
+ */
697
+ getPipeline(name) {
698
+ return this._pipelines.get(name);
699
+ }
700
+
701
+ /**
702
+ * Create default rendering pipeline
703
+ * @private
704
+ */
705
+ _createDefaultPipeline() {
706
+ const vertexModule = this._getOrCreateShaderModule('default-vertex', DEFAULT_VERTEX_SHADER);
707
+ const fragmentModule = this._getOrCreateShaderModule('default-fragment', DEFAULT_FRAGMENT_SHADER);
708
+
709
+ const pipelineLayout = this.device.createPipelineLayout({
710
+ bindGroupLayouts: [this._uniformBindGroupLayout]
711
+ });
712
+
713
+ const pipeline = this.device.createRenderPipeline({
714
+ layout: pipelineLayout,
715
+ vertex: {
716
+ module: vertexModule,
717
+ entryPoint: 'main',
718
+ buffers: [{
719
+ arrayStride: 32,
720
+ attributes: [
721
+ { shaderLocation: 0, offset: 0, format: 'float32x4' },
722
+ { shaderLocation: 1, offset: 16, format: 'float32x4' }
723
+ ]
724
+ }]
725
+ },
726
+ fragment: {
727
+ module: fragmentModule,
728
+ entryPoint: 'main',
729
+ targets: [{
730
+ format: this.format,
731
+ blend: {
732
+ color: {
733
+ srcFactor: 'src-alpha',
734
+ dstFactor: 'one-minus-src-alpha',
735
+ operation: 'add'
736
+ },
737
+ alpha: {
738
+ srcFactor: 'one',
739
+ dstFactor: 'one-minus-src-alpha',
740
+ operation: 'add'
741
+ }
742
+ }
743
+ }]
744
+ },
745
+ primitive: {
746
+ topology: 'triangle-list',
747
+ cullMode: 'back',
748
+ frontFace: 'ccw'
749
+ },
750
+ depthStencil: this.depthEnabled ? {
751
+ depthWriteEnabled: true,
752
+ depthCompare: 'less',
753
+ format: 'depth24plus'
754
+ } : undefined
755
+ });
756
+
757
+ this._pipelines.set('default', pipeline);
758
+ }
759
+
760
+ // ========================================================================
761
+ // Texture Management
762
+ // ========================================================================
763
+
764
+ /**
765
+ * Create a 2D texture
766
+ * @param {string} name
767
+ * @param {object} desc
768
+ * @param {number} desc.width
769
+ * @param {number} desc.height
770
+ * @param {string} [desc.format]
771
+ * @param {number} [desc.usage]
772
+ * @returns {GPUTexture}
773
+ */
774
+ createTexture(name, desc) {
775
+ const texture = this.device.createTexture({
776
+ size: { width: desc.width, height: desc.height },
777
+ format: desc.format || 'rgba8unorm',
778
+ usage: desc.usage || (
779
+ GPUTextureUsage.TEXTURE_BINDING |
780
+ GPUTextureUsage.COPY_DST |
781
+ GPUTextureUsage.RENDER_ATTACHMENT
782
+ )
783
+ });
784
+
785
+ this._resources.register('texture', texture);
786
+ this._textures.set(name, texture);
787
+ return texture;
788
+ }
789
+
790
+ /**
791
+ * Create a sampler
792
+ * @param {string} name
793
+ * @param {object} [desc]
794
+ * @returns {GPUSampler}
795
+ */
796
+ createSampler(name, desc = {}) {
797
+ const sampler = this.device.createSampler({
798
+ magFilter: desc.magFilter || 'linear',
799
+ minFilter: desc.minFilter || 'linear',
800
+ mipmapFilter: desc.mipmapFilter || 'linear',
801
+ addressModeU: desc.addressModeU || 'clamp-to-edge',
802
+ addressModeV: desc.addressModeV || 'clamp-to-edge'
803
+ });
804
+
805
+ this._samplers.set(name, sampler);
806
+ return sampler;
807
+ }
808
+
809
+ /**
810
+ * Get a texture by name
811
+ * @param {string} name
812
+ * @returns {GPUTexture|undefined}
813
+ */
814
+ getTexture(name) {
815
+ return this._textures.get(name);
816
+ }
817
+
818
+ /**
819
+ * Get a sampler by name
820
+ * @param {string} name
821
+ * @returns {GPUSampler|undefined}
822
+ */
823
+ getSampler(name) {
824
+ return this._samplers.get(name);
825
+ }
826
+
827
+ // ========================================================================
828
+ // Canvas / Resize
829
+ // ========================================================================
830
+
831
+ /**
832
+ * Resize the canvas and recreate depth resources if enabled.
833
+ * @param {number} width
834
+ * @param {number} height
835
+ */
836
+ resize(width, height) {
837
+ const clampedWidth = Math.max(1, Math.floor(width));
838
+ const clampedHeight = Math.max(1, Math.floor(height));
839
+
840
+ this.canvas.width = clampedWidth;
841
+ this.canvas.height = clampedHeight;
842
+
843
+ this.context.configure({
844
+ device: this.device,
845
+ format: this.format,
846
+ alphaMode: 'premultiplied'
847
+ });
848
+
849
+ if (this.depthEnabled) {
850
+ this._destroyDepthTexture();
851
+ this._depthTexture = this.device.createTexture({
852
+ size: { width: clampedWidth, height: clampedHeight, depthOrArrayLayers: 1 },
853
+ format: 'depth24plus',
854
+ usage: GPUTextureUsage.RENDER_ATTACHMENT
855
+ });
856
+ this._resources.register('texture', this._depthTexture);
857
+ }
858
+ }
859
+
860
+ // ========================================================================
861
+ // Buffer Creation
862
+ // ========================================================================
863
+
864
+ /**
865
+ * Create a vertex buffer from geometry data
866
+ * @param {Float32Array} data - Interleaved vertex data
867
+ * @returns {GPUBuffer}
868
+ */
869
+ createVertexBuffer(data) {
870
+ const buffer = this.device.createBuffer({
871
+ size: data.byteLength,
872
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
873
+ mappedAtCreation: true
874
+ });
875
+
876
+ new Float32Array(buffer.getMappedRange()).set(data);
877
+ buffer.unmap();
878
+
879
+ this._resources.register('buffer', buffer);
880
+ return buffer;
881
+ }
882
+
883
+ /**
884
+ * Create an index buffer
885
+ * @param {Uint16Array|Uint32Array} data
886
+ * @returns {GPUBuffer}
887
+ */
888
+ createIndexBuffer(data) {
889
+ const buffer = this.device.createBuffer({
890
+ size: data.byteLength,
891
+ usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
892
+ mappedAtCreation: true
893
+ });
894
+
895
+ if (data instanceof Uint16Array) {
896
+ new Uint16Array(buffer.getMappedRange()).set(data);
897
+ } else {
898
+ new Uint32Array(buffer.getMappedRange()).set(data);
899
+ }
900
+ buffer.unmap();
901
+
902
+ this._resources.register('buffer', buffer);
903
+ return buffer;
904
+ }
905
+
906
+ // ========================================================================
907
+ // Rendering - Fullscreen Quad (VIB3+ Procedural)
908
+ // ========================================================================
909
+
910
+ /**
911
+ * Render a fullscreen quad using a named pipeline.
912
+ * This is the primary rendering method for VIB3+ procedural visualization.
913
+ *
914
+ * @param {object} options
915
+ * @param {string} options.pipeline - Pipeline name (created via createFullscreenPipeline)
916
+ * @param {GPUBindGroup[]} [options.bindGroups] - Bind groups to set
917
+ * @param {number[]} [options.clearColor] - RGBA clear color (0-1)
918
+ * @param {boolean} [options.clear] - Whether to clear (default true)
919
+ */
920
+ renderFullscreenQuad(options) {
921
+ const {
922
+ pipeline: pipelineName,
923
+ bindGroups = [],
924
+ clearColor = [0, 0, 0, 1],
925
+ clear = true
926
+ } = options;
927
+
928
+ const pipeline = this._pipelines.get(pipelineName);
929
+ if (!pipeline) {
930
+ if (this.debug) {
931
+ console.warn(`Pipeline "${pipelineName}" not found`);
932
+ }
933
+ return;
934
+ }
935
+
936
+ const encoder = this.device.createCommandEncoder();
937
+ this._stats.commandEncoders += 1;
938
+
939
+ const colorView = this.context.getCurrentTexture().createView();
940
+
941
+ const pass = encoder.beginRenderPass({
942
+ colorAttachments: [{
943
+ view: colorView,
944
+ clearValue: {
945
+ r: clearColor[0],
946
+ g: clearColor[1],
947
+ b: clearColor[2],
948
+ a: clearColor[3]
949
+ },
950
+ loadOp: clear ? 'clear' : 'load',
951
+ storeOp: 'store'
952
+ }]
953
+ });
954
+
955
+ pass.setPipeline(pipeline);
956
+ this._stats.pipelineChanges += 1;
957
+
958
+ // Set bind groups
959
+ for (let i = 0; i < bindGroups.length; i++) {
960
+ pass.setBindGroup(i, bindGroups[i]);
961
+ }
962
+
963
+ // Draw fullscreen triangle (3 vertices, no vertex buffer)
964
+ pass.draw(3);
965
+ this._stats.drawCalls += 1;
966
+
967
+ pass.end();
968
+ this.device.queue.submit([encoder.finish()]);
969
+ this._stats.frames += 1;
970
+ }
971
+
972
+ // ========================================================================
973
+ // Rendering - Geometry
974
+ // ========================================================================
975
+
976
+ /**
977
+ * Render a single frame (clear-only pass by default).
978
+ * @param {object} [options]
979
+ * @param {number[]} [options.clearColor] - RGBA in 0-1
980
+ */
981
+ renderFrame(options = {}) {
982
+ const clearColor = options.clearColor || [0, 0, 0, 1];
983
+ const encoder = this.device.createCommandEncoder();
984
+ this._stats.commandEncoders += 1;
985
+
986
+ const colorView = this.context.getCurrentTexture().createView();
987
+ const depthAttachment = this.depthEnabled && this._depthTexture
988
+ ? {
989
+ view: this._depthTexture.createView(),
990
+ depthClearValue: 1.0,
991
+ depthLoadOp: 'clear',
992
+ depthStoreOp: 'store'
993
+ }
994
+ : undefined;
995
+
996
+ const pass = encoder.beginRenderPass({
997
+ colorAttachments: [{
998
+ view: colorView,
999
+ clearValue: { r: clearColor[0], g: clearColor[1], b: clearColor[2], a: clearColor[3] },
1000
+ loadOp: 'clear',
1001
+ storeOp: 'store'
1002
+ }],
1003
+ depthStencilAttachment: depthAttachment
1004
+ });
1005
+ pass.end();
1006
+
1007
+ this.device.queue.submit([encoder.finish()]);
1008
+ this._stats.frames += 1;
1009
+ }
1010
+
1011
+ /**
1012
+ * Render geometry with the default pipeline
1013
+ * @param {object} options
1014
+ * @param {GPUBuffer} options.vertexBuffer
1015
+ * @param {GPUBuffer} [options.indexBuffer]
1016
+ * @param {number} options.vertexCount
1017
+ * @param {number} [options.indexCount]
1018
+ * @param {object} [options.uniforms]
1019
+ * @param {number[]} [options.clearColor]
1020
+ */
1021
+ renderGeometry(options) {
1022
+ const {
1023
+ vertexBuffer,
1024
+ indexBuffer,
1025
+ vertexCount,
1026
+ indexCount,
1027
+ uniforms = {},
1028
+ clearColor = [0, 0, 0, 1]
1029
+ } = options;
1030
+
1031
+ this.updateUniforms(uniforms);
1032
+
1033
+ const encoder = this.device.createCommandEncoder();
1034
+ this._stats.commandEncoders += 1;
1035
+
1036
+ const colorView = this.context.getCurrentTexture().createView();
1037
+ const depthAttachment = this.depthEnabled && this._depthTexture
1038
+ ? {
1039
+ view: this._depthTexture.createView(),
1040
+ depthClearValue: 1.0,
1041
+ depthLoadOp: 'clear',
1042
+ depthStoreOp: 'store'
1043
+ }
1044
+ : undefined;
1045
+
1046
+ const pass = encoder.beginRenderPass({
1047
+ colorAttachments: [{
1048
+ view: colorView,
1049
+ clearValue: { r: clearColor[0], g: clearColor[1], b: clearColor[2], a: clearColor[3] },
1050
+ loadOp: 'clear',
1051
+ storeOp: 'store'
1052
+ }],
1053
+ depthStencilAttachment: depthAttachment
1054
+ });
1055
+
1056
+ const pipeline = this._pipelines.get('default');
1057
+ pass.setPipeline(pipeline);
1058
+ this._stats.pipelineChanges += 1;
1059
+
1060
+ pass.setBindGroup(0, this._uniformBindGroup);
1061
+ pass.setVertexBuffer(0, vertexBuffer);
1062
+
1063
+ if (indexBuffer && indexCount) {
1064
+ pass.setIndexBuffer(indexBuffer, 'uint16');
1065
+ pass.drawIndexed(indexCount);
1066
+ this._stats.triangles += indexCount / 3;
1067
+ } else {
1068
+ pass.draw(vertexCount);
1069
+ this._stats.triangles += vertexCount / 3;
1070
+ }
1071
+
1072
+ this._stats.drawCalls += 1;
1073
+
1074
+ pass.end();
1075
+ this.device.queue.submit([encoder.finish()]);
1076
+ this._stats.frames += 1;
1077
+ }
1078
+
1079
+ /**
1080
+ * Render using a named pipeline with arbitrary vertex/index buffers
1081
+ * @param {object} options
1082
+ * @param {string} options.pipeline - Pipeline name
1083
+ * @param {GPUBuffer} options.vertexBuffer
1084
+ * @param {GPUBuffer} [options.indexBuffer]
1085
+ * @param {number} options.vertexCount
1086
+ * @param {number} [options.indexCount]
1087
+ * @param {GPUBindGroup[]} [options.bindGroups]
1088
+ * @param {number[]} [options.clearColor]
1089
+ * @param {boolean} [options.clear]
1090
+ */
1091
+ renderWithPipeline(options) {
1092
+ const {
1093
+ pipeline: pipelineName,
1094
+ vertexBuffer,
1095
+ indexBuffer,
1096
+ vertexCount,
1097
+ indexCount,
1098
+ bindGroups = [],
1099
+ clearColor = [0, 0, 0, 1],
1100
+ clear = true
1101
+ } = options;
1102
+
1103
+ const pipeline = this._pipelines.get(pipelineName);
1104
+ if (!pipeline) {
1105
+ if (this.debug) {
1106
+ console.warn(`Pipeline "${pipelineName}" not found`);
1107
+ }
1108
+ return;
1109
+ }
1110
+
1111
+ const encoder = this.device.createCommandEncoder();
1112
+ this._stats.commandEncoders += 1;
1113
+
1114
+ const colorView = this.context.getCurrentTexture().createView();
1115
+ const depthAttachment = this.depthEnabled && this._depthTexture
1116
+ ? {
1117
+ view: this._depthTexture.createView(),
1118
+ depthClearValue: 1.0,
1119
+ depthLoadOp: clear ? 'clear' : 'load',
1120
+ depthStoreOp: 'store'
1121
+ }
1122
+ : undefined;
1123
+
1124
+ const pass = encoder.beginRenderPass({
1125
+ colorAttachments: [{
1126
+ view: colorView,
1127
+ clearValue: { r: clearColor[0], g: clearColor[1], b: clearColor[2], a: clearColor[3] },
1128
+ loadOp: clear ? 'clear' : 'load',
1129
+ storeOp: 'store'
1130
+ }],
1131
+ depthStencilAttachment: depthAttachment
1132
+ });
1133
+
1134
+ pass.setPipeline(pipeline);
1135
+ this._stats.pipelineChanges += 1;
1136
+
1137
+ for (let i = 0; i < bindGroups.length; i++) {
1138
+ pass.setBindGroup(i, bindGroups[i]);
1139
+ }
1140
+
1141
+ pass.setVertexBuffer(0, vertexBuffer);
1142
+
1143
+ if (indexBuffer && indexCount) {
1144
+ pass.setIndexBuffer(indexBuffer, 'uint16');
1145
+ pass.drawIndexed(indexCount);
1146
+ this._stats.triangles += indexCount / 3;
1147
+ } else {
1148
+ pass.draw(vertexCount);
1149
+ this._stats.triangles += vertexCount / 3;
1150
+ }
1151
+
1152
+ this._stats.drawCalls += 1;
1153
+
1154
+ pass.end();
1155
+ this.device.queue.submit([encoder.finish()]);
1156
+ this._stats.frames += 1;
1157
+ }
1158
+
1159
+ // ========================================================================
1160
+ // Manual Render Pass Control
1161
+ // ========================================================================
1162
+
1163
+ /**
1164
+ * Begin a new render pass (for manual control)
1165
+ * @param {object} [options]
1166
+ * @returns {{encoder: GPUCommandEncoder, pass: GPURenderPassEncoder}}
1167
+ */
1168
+ beginRenderPass(options = {}) {
1169
+ const clearColor = options.clearColor || [0, 0, 0, 1];
1170
+ const encoder = this.device.createCommandEncoder();
1171
+
1172
+ const colorView = this.context.getCurrentTexture().createView();
1173
+ const depthAttachment = this.depthEnabled && this._depthTexture
1174
+ ? {
1175
+ view: this._depthTexture.createView(),
1176
+ depthClearValue: 1.0,
1177
+ depthLoadOp: options.loadDepth ? 'load' : 'clear',
1178
+ depthStoreOp: 'store'
1179
+ }
1180
+ : undefined;
1181
+
1182
+ const pass = encoder.beginRenderPass({
1183
+ colorAttachments: [{
1184
+ view: colorView,
1185
+ clearValue: { r: clearColor[0], g: clearColor[1], b: clearColor[2], a: clearColor[3] },
1186
+ loadOp: options.loadColor ? 'load' : 'clear',
1187
+ storeOp: 'store'
1188
+ }],
1189
+ depthStencilAttachment: depthAttachment
1190
+ });
1191
+
1192
+ return { encoder, pass };
1193
+ }
1194
+
1195
+ /**
1196
+ * End a render pass and submit
1197
+ * @param {GPUCommandEncoder} encoder
1198
+ * @param {GPURenderPassEncoder} pass
1199
+ */
1200
+ endRenderPass(encoder, pass) {
1201
+ pass.end();
1202
+ this.device.queue.submit([encoder.finish()]);
1203
+ this._stats.frames += 1;
1204
+ }
1205
+
1206
+ // ========================================================================
1207
+ // Statistics & Cleanup
1208
+ // ========================================================================
1209
+
1210
+ /**
1211
+ * Return backend statistics.
1212
+ */
1213
+ getStats() {
1214
+ return {
1215
+ ...this._stats,
1216
+ resources: this._resources.getStats()
1217
+ };
1218
+ }
1219
+
1220
+ /**
1221
+ * Reset per-frame statistics
1222
+ */
1223
+ resetFrameStats() {
1224
+ this._stats.drawCalls = 0;
1225
+ this._stats.triangles = 0;
1226
+ this._stats.pipelineChanges = 0;
1227
+ }
1228
+
1229
+ /**
1230
+ * Dispose of GPU resources.
1231
+ */
1232
+ dispose() {
1233
+ this._destroyDepthTexture();
1234
+
1235
+ // Destroy uniform buffers
1236
+ if (this._uniformBuffer) {
1237
+ this._uniformBuffer.destroy();
1238
+ this._uniformBuffer = null;
1239
+ }
1240
+
1241
+ // Destroy custom uniform buffers
1242
+ for (const [, entry] of this._customUniformBuffers) {
1243
+ entry.buffer.destroy();
1244
+ }
1245
+ this._customUniformBuffers.clear();
1246
+
1247
+ // Destroy textures
1248
+ for (const [, texture] of this._textures) {
1249
+ texture.destroy();
1250
+ }
1251
+ this._textures.clear();
1252
+ this._samplers.clear();
1253
+
1254
+ // Clear pipelines and shaders
1255
+ this._pipelines.clear();
1256
+ this._shaderModules.clear();
1257
+
1258
+ this._resources.disposeAll();
1259
+ }
1260
+
1261
+ _destroyDepthTexture() {
1262
+ if (this._depthTexture) {
1263
+ this._resources.release('texture', this._depthTexture);
1264
+ this._depthTexture.destroy();
1265
+ this._depthTexture = null;
1266
+ }
1267
+ }
1268
+ }
1269
+
1270
+ /**
1271
+ * Check if WebGPU is available
1272
+ * @returns {boolean}
1273
+ */
1274
+ export function isWebGPUSupported() {
1275
+ return typeof navigator !== 'undefined' && !!navigator.gpu;
1276
+ }
1277
+
1278
+ /**
1279
+ * Get available WebGPU features
1280
+ * @returns {Promise<Set<string>|null>}
1281
+ */
1282
+ export async function getWebGPUFeatures() {
1283
+ if (!isWebGPUSupported()) return null;
1284
+
1285
+ try {
1286
+ const adapter = await navigator.gpu.requestAdapter();
1287
+ if (!adapter) return null;
1288
+
1289
+ return new Set(adapter.features);
1290
+ } catch {
1291
+ return null;
1292
+ }
1293
+ }
1294
+
1295
+ /**
1296
+ * Create a WebGPU backend (async).
1297
+ * @param {HTMLCanvasElement} canvas
1298
+ * @param {object} [options]
1299
+ * @param {string} [options.powerPreference] - 'high-performance' or 'low-power'
1300
+ * @param {string[]} [options.requiredFeatures] - Features to request
1301
+ * @param {boolean} [options.debug] - Enable debug mode
1302
+ * @param {boolean} [options.depth] - Enable depth buffer
1303
+ * @returns {Promise<WebGPUBackend|null>}
1304
+ */
1305
+ export async function createWebGPUBackend(canvas, options = {}) {
1306
+ if (!canvas || !isWebGPUSupported()) {
1307
+ if (options.debug) {
1308
+ console.warn('WebGPU not supported');
1309
+ }
1310
+ return null;
1311
+ }
1312
+
1313
+ const context = canvas.getContext('webgpu');
1314
+ if (!context) {
1315
+ if (options.debug) {
1316
+ console.warn('Could not get WebGPU context');
1317
+ }
1318
+ return null;
1319
+ }
1320
+
1321
+ try {
1322
+ const adapter = await navigator.gpu.requestAdapter({
1323
+ powerPreference: options.powerPreference || 'high-performance'
1324
+ });
1325
+
1326
+ if (!adapter) {
1327
+ if (options.debug) {
1328
+ console.warn('Could not get WebGPU adapter');
1329
+ }
1330
+ return null;
1331
+ }
1332
+
1333
+ // Determine which features to request
1334
+ const availableFeatures = new Set(adapter.features);
1335
+ const requestedFeatures = [];
1336
+
1337
+ const requiredFeatures = options.requiredFeatures || [];
1338
+ for (const feature of requiredFeatures) {
1339
+ if (availableFeatures.has(feature)) {
1340
+ requestedFeatures.push(feature);
1341
+ } else if (options.debug) {
1342
+ console.warn(`WebGPU feature not available: ${feature}`);
1343
+ }
1344
+ }
1345
+
1346
+ const optionalFeatures = [
1347
+ WebGPUFeatures.TIMESTAMP_QUERY,
1348
+ WebGPUFeatures.INDIRECT_FIRST_INSTANCE
1349
+ ];
1350
+
1351
+ for (const feature of optionalFeatures) {
1352
+ if (availableFeatures.has(feature) && !requestedFeatures.includes(feature)) {
1353
+ requestedFeatures.push(feature);
1354
+ }
1355
+ }
1356
+
1357
+ const device = await adapter.requestDevice({
1358
+ requiredFeatures: requestedFeatures.length > 0 ? requestedFeatures : undefined
1359
+ });
1360
+
1361
+ device.lost.then((info) => {
1362
+ console.error('WebGPU device lost:', info.reason, info.message);
1363
+ });
1364
+
1365
+ if (options.debug) {
1366
+ device.onuncapturederror = (event) => {
1367
+ console.error('WebGPU error:', event.error.message);
1368
+ };
1369
+ }
1370
+
1371
+ const format = navigator.gpu.getPreferredCanvasFormat();
1372
+
1373
+ if (options.debug) {
1374
+ console.log('WebGPU initialized:', {
1375
+ vendor: adapter.info?.vendor,
1376
+ architecture: adapter.info?.architecture,
1377
+ format,
1378
+ features: requestedFeatures
1379
+ });
1380
+ }
1381
+
1382
+ return new WebGPUBackend(
1383
+ { canvas, device, context, format, adapter },
1384
+ { ...options, features: requestedFeatures }
1385
+ );
1386
+ } catch (error) {
1387
+ if (options.debug) {
1388
+ console.error('WebGPU initialization failed:', error);
1389
+ }
1390
+ return null;
1391
+ }
1392
+ }
1393
+
1394
+ /**
1395
+ * Create WebGPU backend with fallback to WebGL
1396
+ * @param {HTMLCanvasElement} canvas
1397
+ * @param {object} [options]
1398
+ * @returns {Promise<{backend: WebGPUBackend|null, type: 'webgpu'|'webgl'|null}>}
1399
+ */
1400
+ export async function createWebGPUWithFallback(canvas, options = {}) {
1401
+ const webgpuBackend = await createWebGPUBackend(canvas, options);
1402
+ if (webgpuBackend) {
1403
+ return { backend: webgpuBackend, type: 'webgpu' };
1404
+ }
1405
+
1406
+ return { backend: null, type: null };
1407
+ }
1408
+
1409
+ export default WebGPUBackend;