@babylonjs/lite 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/index.js +512 -512
  2. package/dist/index.js.map +1 -1
  3. package/index.d.ts +829 -28
  4. package/lib/audio/analyzer.js +65 -0
  5. package/lib/audio/analyzer.js.map +1 -0
  6. package/lib/audio/audio-bus.js +38 -0
  7. package/lib/audio/audio-bus.js.map +1 -0
  8. package/lib/audio/audio-engine.js +188 -0
  9. package/lib/audio/audio-engine.js.map +1 -0
  10. package/lib/audio/audio-fetch.js +18 -0
  11. package/lib/audio/audio-fetch.js.map +1 -0
  12. package/lib/audio/audio-param.js +96 -0
  13. package/lib/audio/audio-param.js.map +1 -0
  14. package/lib/audio/audio-signal.js +46 -0
  15. package/lib/audio/audio-signal.js.map +1 -0
  16. package/lib/audio/bus.js +33 -0
  17. package/lib/audio/bus.js.map +1 -0
  18. package/lib/audio/host-types.js +2 -0
  19. package/lib/audio/host-types.js.map +1 -0
  20. package/lib/audio/index.js +12 -0
  21. package/lib/audio/index.js.map +1 -0
  22. package/lib/audio/sound-buffer.js +59 -0
  23. package/lib/audio/sound-buffer.js.map +1 -0
  24. package/lib/audio/sound-source.js +57 -0
  25. package/lib/audio/sound-source.js.map +1 -0
  26. package/lib/audio/sound-sub-graph.js +72 -0
  27. package/lib/audio/sound-sub-graph.js.map +1 -0
  28. package/lib/audio/spatial.js +466 -0
  29. package/lib/audio/spatial.js.map +1 -0
  30. package/lib/audio/static-sound.js +313 -0
  31. package/lib/audio/static-sound.js.map +1 -0
  32. package/lib/audio/stereo.js +40 -0
  33. package/lib/audio/stereo.js.map +1 -0
  34. package/lib/audio/streaming-sound.js +377 -0
  35. package/lib/audio/streaming-sound.js.map +1 -0
  36. package/lib/audio/unmute-ui.js +72 -0
  37. package/lib/audio/unmute-ui.js.map +1 -0
  38. package/lib/audio/visualizer.js +101 -0
  39. package/lib/audio/visualizer.js.map +1 -0
  40. package/lib/camera/geospatial-camera-controls.js +22 -0
  41. package/lib/camera/geospatial-camera-controls.js.map +1 -1
  42. package/lib/camera/geospatial-camera-fly.js +2 -1
  43. package/lib/camera/geospatial-camera-fly.js.map +1 -1
  44. package/lib/effect/effect-renderer.js +1 -1
  45. package/lib/effect/effect-renderer.js.map +1 -1
  46. package/lib/engine/engine.js +1 -1
  47. package/lib/index.js +15 -1
  48. package/lib/index.js.map +1 -1
  49. package/lib/light/types.js.map +1 -1
  50. package/lib/loader-gltf/animation-pointer-basecolor.js +25 -0
  51. package/lib/loader-gltf/animation-pointer-basecolor.js.map +1 -0
  52. package/lib/loader-gltf/animation-pointer-ext.js +244 -0
  53. package/lib/loader-gltf/animation-pointer-ext.js.map +1 -0
  54. package/lib/loader-gltf/animation-pointer-lights.js +46 -0
  55. package/lib/loader-gltf/animation-pointer-lights.js.map +1 -0
  56. package/lib/loader-gltf/animation-pointer.js +4 -1
  57. package/lib/loader-gltf/animation-pointer.js.map +1 -1
  58. package/lib/loader-gltf/gltf-animation.js +5 -3
  59. package/lib/loader-gltf/gltf-animation.js.map +1 -1
  60. package/lib/loader-gltf/gltf-color-normalize.js +10 -1
  61. package/lib/loader-gltf/gltf-color-normalize.js.map +1 -1
  62. package/lib/loader-gltf/gltf-feature-animation-pointer.js +67 -47
  63. package/lib/loader-gltf/gltf-feature-animation-pointer.js.map +1 -1
  64. package/lib/loader-gltf/gltf-feature-lights-punctual.js +51 -9
  65. package/lib/loader-gltf/gltf-feature-lights-punctual.js.map +1 -1
  66. package/lib/loader-gltf/gltf-feature-primitive.js +20 -0
  67. package/lib/loader-gltf/gltf-feature-primitive.js.map +1 -0
  68. package/lib/loader-gltf/gltf-feature-registry.js +25 -0
  69. package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
  70. package/lib/loader-gltf/gltf-feature-skeleton.js +18 -3
  71. package/lib/loader-gltf/gltf-feature-skeleton.js.map +1 -1
  72. package/lib/loader-gltf/gltf-interleave.js +3 -2
  73. package/lib/loader-gltf/gltf-interleave.js.map +1 -1
  74. package/lib/loader-gltf/gltf-light-pointer-state.js +18 -0
  75. package/lib/loader-gltf/gltf-light-pointer-state.js.map +1 -0
  76. package/lib/loader-gltf/gltf-parser.js +7 -1
  77. package/lib/loader-gltf/gltf-parser.js.map +1 -1
  78. package/lib/loader-gltf/gltf-pbr-builder-ext.js +1 -1
  79. package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
  80. package/lib/loader-gltf/gltf-pbr-builder.js +1 -1
  81. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  82. package/lib/loader-gltf/gltf-sampler-denorm.js +20 -0
  83. package/lib/loader-gltf/gltf-sampler-denorm.js.map +1 -0
  84. package/lib/loader-gltf/gltf-sampler-desc.js +11 -2
  85. package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
  86. package/lib/loader-gltf/gltf-uv-denorm.js +28 -0
  87. package/lib/loader-gltf/gltf-uv-denorm.js.map +1 -0
  88. package/lib/loader-gltf/load-gltf.js +15 -6
  89. package/lib/loader-gltf/load-gltf.js.map +1 -1
  90. package/lib/material/material-rebuild.js +4 -0
  91. package/lib/material/material-rebuild.js.map +1 -1
  92. package/lib/material/mesh-features.js +8 -1
  93. package/lib/material/mesh-features.js.map +1 -1
  94. package/lib/material/pbr/fragments/reflectance-fragment.js +1 -1
  95. package/lib/material/pbr/fragments/reflectance-fragment.js.map +1 -1
  96. package/lib/material/pbr/fragments/refraction-rtt-fragment.js +1 -1
  97. package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
  98. package/lib/material/pbr/pbr-pipeline.js +7 -3
  99. package/lib/material/pbr/pbr-pipeline.js.map +1 -1
  100. package/lib/material/pbr/pbr-primitive-resolver.js +34 -0
  101. package/lib/material/pbr/pbr-primitive-resolver.js.map +1 -0
  102. package/lib/material/pbr/pbr-renderable.js +1 -1
  103. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  104. package/lib/material/shader/shader-material.js +9 -5
  105. package/lib/material/shader/shader-material.js.map +1 -1
  106. package/lib/material/shader/shader-thin-instance.js +1 -1
  107. package/lib/material/shader/shader-thin-instance.js.map +1 -1
  108. package/lib/material/standard/standard-renderable.js +1 -1
  109. package/lib/material/standard/standard-renderable.js.map +1 -1
  110. package/lib/mesh/mesh-dispose.js +1 -0
  111. package/lib/mesh/mesh-dispose.js.map +1 -1
  112. package/lib/mesh/thin-instance-cull-binding.js +15 -6
  113. package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
  114. package/lib/post-process/taa.js +193 -0
  115. package/lib/post-process/taa.js.map +1 -0
  116. package/lib/scene/scene-core.js +1 -0
  117. package/lib/scene/scene-core.js.map +1 -1
  118. package/lib/scene/scene-material-swap.js +2 -0
  119. package/lib/scene/scene-material-swap.js.map +1 -1
  120. package/lib/shadow/csm-shadow-task-hooks.js +67 -9
  121. package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
  122. package/lib/sprite/billboard-custom-shader.js +32 -32
  123. package/lib/sprite/billboard-custom-shader.js.map +1 -1
  124. package/lib/sprite/billboard-pipeline.js +54 -56
  125. package/lib/sprite/billboard-pipeline.js.map +1 -1
  126. package/lib/sprite/custom-shader-core.js +1 -1
  127. package/lib/sprite/custom-shader-core.js.map +1 -1
  128. package/lib/sprite/shared/sprite-atlas.js +2 -2
  129. package/lib/sprite/shared/sprite-atlas.js.map +1 -1
  130. package/lib/sprite/sprite-2d-coverage-gamma.js +58 -0
  131. package/lib/sprite/sprite-2d-coverage-gamma.js.map +1 -0
  132. package/lib/sprite/sprite-2d-uvscroll.js +39 -0
  133. package/lib/sprite/sprite-2d-uvscroll.js.map +1 -0
  134. package/lib/sprite/sprite-2d.js +6 -36
  135. package/lib/sprite/sprite-2d.js.map +1 -1
  136. package/lib/sprite/sprite-coverage-gamma-hook.js +10 -0
  137. package/lib/sprite/sprite-coverage-gamma-hook.js.map +1 -0
  138. package/lib/sprite/sprite-custom-shader.js +2 -2
  139. package/lib/sprite/sprite-custom-shader.js.map +1 -1
  140. package/lib/sprite/sprite-pipeline.js +61 -73
  141. package/lib/sprite/sprite-pipeline.js.map +1 -1
  142. package/lib/sprite/sprite-renderable.js +5 -5
  143. package/lib/sprite/sprite-renderable.js.map +1 -1
  144. package/lib/sprite/sprite-renderer.js +4 -4
  145. package/lib/sprite/sprite-renderer.js.map +1 -1
  146. package/lib/sprite/sprite-scene.js +1 -1
  147. package/lib/sprite/sprite-scene.js.map +1 -1
  148. package/lib/text/_gpu/text-pipeline.js +1 -1
  149. package/lib/text/_gpu/text-pipeline.js.map +1 -1
  150. package/lib/text/text-renderer.js +3 -1
  151. package/lib/text/text-renderer.js.map +1 -1
  152. package/package.json +3 -3
@@ -1,7 +1,8 @@
1
1
  import { U16 } from '../engine/typed-arrays.js';
2
2
  import { BU, SS, CW } from '../engine/gpu-flags.js';
3
3
  import { _getSpriteFxHook } from './sprite-fx-hook.js';
4
- import { DEPTH_UVSCROLL_STRIDE_BYTES, PURE_2D_UVSCROLL_STRIDE_BYTES, DEPTH_INSTANCE_STRIDE_BYTES, PURE_2D_INSTANCE_STRIDE_BYTES } from './sprite-2d.js';
4
+ import { _getSpriteCoverageGammaHook } from './sprite-coverage-gamma-hook.js';
5
+ import { DEPTH_INSTANCE_STRIDE_BYTES, PURE_2D_INSTANCE_STRIDE_BYTES } from './sprite-2d.js';
5
6
 
6
7
  const SPRITE_POSITION_OFFSET_BYTES = 0;
7
8
  const SPRITE_SIZE_OFFSET_BYTES = 8;
@@ -10,12 +11,10 @@ const SPRITE_UV_MAX_OFFSET_BYTES = 24;
10
11
  const SPRITE_ROTATION_OFFSET_BYTES = 32;
11
12
  const SPRITE_COLOR_OFFSET_BYTES = 36;
12
13
  const SPRITE_DEPTH_OFFSET_BYTES = 52;
13
- const SPRITE_UVOFFSET_OFFSET_PURE_2D_BYTES = 52;
14
- const SPRITE_UVOFFSET_OFFSET_DEPTH_BYTES = 56;
15
14
  function makeSpriteWgsl(hasDepth, spriteGroupIndex, uvScroll) {
16
15
  return `${makeSpritePrologueWgsl(hasDepth, spriteGroupIndex, uvScroll)}
17
16
  @fragment
18
- fn fs(in: VOut) -> @location(0) vec4<f32> {
17
+ fn fs(in: O) -> @location(0) vec4f {
19
18
  let s = textureSample(atlasTex, atlasSamp, in.uv);
20
19
  return s * in.tint * L.opacityMul;
21
20
  }`;
@@ -23,59 +22,54 @@ return s * in.tint * L.opacityMul;
23
22
  function makeSpritePrologueWgsl(hasDepth, spriteGroupIndex, uvScroll = false) {
24
23
  const group = `@group(${spriteGroupIndex})`;
25
24
  const zAttribute = hasDepth ? `,
26
- @location(6) iZ: f32` : "";
25
+ @location(6) z: f32` : "";
27
26
  const uvOffsetAttribute = uvScroll ? `,
28
- @location(7) iUvOffset: vec2<f32>` : "";
29
- const zPosition = hasDepth ? "1.0 - in.iZ" : "0.0";
30
- return `struct Layer {
31
- viewPos: vec2<f32>,
27
+ @location(7) o: vec2f` : "";
28
+ const zPosition = hasDepth ? "1 - in.z" : "0";
29
+ return `struct Lr {
30
+ viewPos: vec2f,
32
31
  viewScale: f32,
33
32
  viewRot: f32,
34
- screenSize: vec2<f32>,
35
- pivot: vec2<f32>,
36
- // Per-layer opacity, pre-shaped for the layer's blend mode (CPU-side):
37
- // straight-alpha: (1, 1, 1, opacity) — only alpha is scaled
38
- // premultiplied: (opacity, opacity, opacity, opacity) — RGB and A scale together
39
- // One uniform, no shader branch.
40
- opacityMul: vec4<f32>,
33
+ screenSize: vec2f,
34
+ pivot: vec2f,
35
+ opacityMul: vec4f,
36
+ aa: vec4f,
41
37
  };
42
- ${group} @binding(0) var<uniform> L: Layer;
38
+ ${group} @binding(0) var<uniform> L: Lr;
43
39
  ${group} @binding(1) var atlasTex: texture_2d<f32>;
44
40
  ${group} @binding(2) var atlasSamp: sampler;
45
- struct VIn {
41
+ struct I {
46
42
  @builtin(vertex_index) vid: u32,
47
- @location(0) iPos: vec2<f32>,
48
- @location(1) iSize: vec2<f32>,
49
- @location(2) iUvMin: vec2<f32>,
50
- @location(3) iUvMax: vec2<f32>,
51
- @location(4) iRot: f32,
52
- @location(5) iColor: vec4<f32>${zAttribute}${uvOffsetAttribute}
43
+ @location(0) p: vec2f,
44
+ @location(1) s: vec2f,
45
+ @location(2) a: vec2f,
46
+ @location(3) b: vec2f,
47
+ @location(4) r: f32,
48
+ @location(5) c: vec4f${zAttribute}${uvOffsetAttribute}
53
49
  };
54
- struct VOut {
55
- @builtin(position) pos: vec4<f32>,
56
- @location(0) uv: vec2<f32>,
57
- @location(1) tint: vec4<f32>,
50
+ struct O {
51
+ @builtin(position) p: vec4f,
52
+ @location(0) uv: vec2f,
53
+ @location(1) tint: vec4f,
58
54
  };
59
55
  @vertex
60
- fn vs(in: VIn) -> VOut {
61
- var corners = array<vec2<f32>, 4>(vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 0.0), vec2<f32>(1.0, 1.0), vec2<f32>(0.0, 1.0));
62
- let c = corners[in.vid];
63
- let local = (c - L.pivot) * in.iSize;
64
- let cr = cos(in.iRot);
65
- let sr = sin(in.iRot);
66
- let rotated = vec2<f32>(local.x * cr - local.y * sr, local.x * sr + local.y * cr);
67
- let layerPx = in.iPos + rotated;
68
- let centered = layerPx - L.viewPos;
56
+ fn vs(in: I) -> O {
57
+ var q = array<vec2f, 4>(vec2f(0, 0), vec2f(1, 0), vec2f(1, 1), vec2f(0, 1));
58
+ let c = q[in.vid];
59
+ let l = (c - L.pivot) * in.s;
60
+ let cr = cos(in.r);
61
+ let sr = sin(in.r);
62
+ let r = vec2f(l.x * cr - l.y * sr, l.x * sr + l.y * cr);
63
+ let p = in.p + r - L.viewPos;
69
64
  let lc = cos(L.viewRot);
70
65
  let ls = sin(L.viewRot);
71
- let viewRot = vec2<f32>(centered.x * lc - centered.y * ls, centered.x * ls + centered.y * lc);
72
- let screenPx = viewRot * L.viewScale;
73
- let ndc = vec2<f32>(screenPx.x / L.screenSize.x * 2.0 - 1.0, 1.0 - screenPx.y / L.screenSize.y * 2.0);
74
- let uv = mix(in.iUvMin, in.iUvMax, c)${uvScroll ? " + in.iUvOffset" : ""};
75
- var out: VOut;
76
- out.pos = vec4<f32>(ndc, ${zPosition}, 1.0);
66
+ let v = vec2f(p.x * lc - p.y * ls, p.x * ls + p.y * lc) * L.viewScale;
67
+ let n = vec2f(v.x / L.screenSize.x * 2 - 1, 1 - v.y / L.screenSize.y * 2);
68
+ let uv = mix(in.a, in.b, c)${uvScroll ? " + in.o" : ""};
69
+ var out: O;
70
+ out.p = vec4f(n, ${zPosition}, 1);
77
71
  out.uv = uv;
78
- out.tint = in.iColor;
72
+ out.tint = in.c;
79
73
  return out;
80
74
  }`;
81
75
  }
@@ -123,10 +117,7 @@ function getSpritePipelineDeviceCache(engine, cache) {
123
117
  let deviceCache = cache._devices.get(engine._device);
124
118
  if (!deviceCache) {
125
119
  deviceCache = {
126
- _shaderModule: null,
127
- _sceneShaderModule: null,
128
- _shaderModuleUv: null,
129
- _sceneShaderModuleUv: null,
120
+ _shaderModules: /* @__PURE__ */ new Map(),
130
121
  _pipelines: /* @__PURE__ */ new Map()
131
122
  };
132
123
  cache._devices.set(engine._device, deviceCache);
@@ -144,29 +135,29 @@ function normalizeDepthStencilFormat(hasDepth, depthStencilFormat) {
144
135
  }
145
136
  function spritePipelineKey(format, sampleCount, blendMode, hasDepth, depthWrite, depthStencilFormat, layer) {
146
137
  const customKey = layer ? _getSpriteFxHook()?.pipelineKeyPart(layer) ?? "" : "";
147
- const uvKey = layer?._uvScroll ? "1" : "0";
148
- return `${format}:${sampleCount}:${blendMode._key}:${hasDepth ? 1 : 0}:${depthWrite ? 1 : 0}:${depthStencilFormat ?? "-"}:cs${customKey}:uv${uvKey}`;
138
+ const uvKey = layer?._uvScrollAttr ? "1" : "0";
139
+ const cgKey = layer ? _getSpriteCoverageGammaHook()?.pipelineKeyPart(layer) ?? "0" : "0";
140
+ return `${format}:${sampleCount}:${blendMode._key}:${hasDepth ? 1 : 0}:${depthWrite ? 1 : 0}:${depthStencilFormat ?? "-"}:cs${customKey}:uv${uvKey}:cg${cgKey}`;
149
141
  }
150
142
  function getShaderModule(engine, cache, hasDepth, layer) {
151
143
  const customModule = layer ? _getSpriteFxHook()?.shaderModule(engine, hasDepth, layer) : null;
152
144
  if (customModule) {
153
145
  return customModule;
154
146
  }
155
- const uvScroll = layer?._uvScroll === true;
156
- if (hasDepth) {
157
- if (uvScroll) {
158
- cache._sceneShaderModuleUv ??= engine._device.createShaderModule({ code: makeSpriteWgsl(true, 1, true) });
159
- return cache._sceneShaderModuleUv;
160
- }
161
- cache._sceneShaderModule ??= engine._device.createShaderModule({ code: makeSpriteWgsl(true, 1, false) });
162
- return cache._sceneShaderModule;
147
+ const gammaModule = layer ? _getSpriteCoverageGammaHook()?.shaderModule(engine, hasDepth, layer) : null;
148
+ if (gammaModule) {
149
+ return gammaModule;
163
150
  }
164
- if (uvScroll) {
165
- cache._shaderModuleUv ??= engine._device.createShaderModule({ code: makeSpriteWgsl(false, 0, true) });
166
- return cache._shaderModuleUv;
151
+ const uvScroll = layer?._uvScrollAttr != null;
152
+ const key = `${hasDepth ? 1 : 0}:${uvScroll ? 1 : 0}`;
153
+ let module = cache._shaderModules.get(key);
154
+ if (!module) {
155
+ module = engine._device.createShaderModule({
156
+ code: makeSpriteWgsl(hasDepth, hasDepth ? 1 : 0, uvScroll)
157
+ });
158
+ cache._shaderModules.set(key, module);
167
159
  }
168
- cache._shaderModule ??= engine._device.createShaderModule({ code: makeSpriteWgsl(false, 0, false) });
169
- return cache._shaderModule;
160
+ return module;
170
161
  }
171
162
  function buildSpritePipeline(engine, cache, format, sampleCount, blendMode, hasDepth, depthWrite, depthStencilFormat, sceneBindGroupLayout, layer) {
172
163
  const device = engine._device;
@@ -198,15 +189,10 @@ function buildSpritePipeline(engine, cache, format, sampleCount, blendMode, hasD
198
189
  if (hasDepth) {
199
190
  instanceAttributes.push({ shaderLocation: 6, offset: SPRITE_DEPTH_OFFSET_BYTES, format: "float32" });
200
191
  }
201
- const uvScroll = layer?._uvScroll === true;
202
- if (uvScroll) {
203
- instanceAttributes.push({
204
- shaderLocation: 7,
205
- offset: hasDepth ? SPRITE_UVOFFSET_OFFSET_DEPTH_BYTES : SPRITE_UVOFFSET_OFFSET_PURE_2D_BYTES,
206
- format: "float32x2"
207
- });
192
+ if (layer?._uvScrollAttr) {
193
+ instanceAttributes.push(layer._uvScrollAttr);
208
194
  }
209
- const arrayStride = uvScroll ? hasDepth ? DEPTH_UVSCROLL_STRIDE_BYTES : PURE_2D_UVSCROLL_STRIDE_BYTES : hasDepth ? DEPTH_INSTANCE_STRIDE_BYTES : PURE_2D_INSTANCE_STRIDE_BYTES;
195
+ const arrayStride = layer?._instanceStrideBytes ?? (hasDepth ? DEPTH_INSTANCE_STRIDE_BYTES : PURE_2D_INSTANCE_STRIDE_BYTES);
210
196
  const descriptor = {
211
197
  layout: device.createPipelineLayout({ bindGroupLayouts }),
212
198
  vertex: {
@@ -237,7 +223,7 @@ function buildSpritePipeline(engine, cache, format, sampleCount, blendMode, hasD
237
223
  }
238
224
  return device.createRenderPipeline(descriptor);
239
225
  }
240
- const LAYER_UBO_BYTES = 48;
226
+ const LAYER_UBO_BYTES = 64;
241
227
  const LAYER_UBO_FLOATS = LAYER_UBO_BYTES / 4;
242
228
  const SHARED_SPRITE_INDEX_DATA = new U16([0, 1, 2, 0, 2, 3]);
243
229
  function createSpriteInstanceBuffer(device, layer, label) {
@@ -248,7 +234,8 @@ function createSpriteInstanceBuffer(device, layer, label) {
248
234
  });
249
235
  }
250
236
  function ensureSpriteInstanceBuffer(device, layer, currentBuffer, currentCapacity, label) {
251
- if (currentCapacity >= layer._capacity) {
237
+ const neededBytes = layer._capacity * layer._instanceStrideBytes;
238
+ if (currentBuffer.size >= neededBytes) {
252
239
  return { buffer: currentBuffer, capacity: currentCapacity, reallocated: false };
253
240
  }
254
241
  currentBuffer.destroy();
@@ -306,6 +293,7 @@ function buildSpriteLayerUbo(layer, screenWidth, screenHeight, ubo) {
306
293
  ubo[10] = 1;
307
294
  ubo[11] = op;
308
295
  }
296
+ _getSpriteCoverageGammaHook()?.writeUbo(layer, ubo);
309
297
  }
310
298
  function writeSpriteLayerUboIfDirty(device, uniformBuffer, scratchUbo, lastUbo, alreadyUploaded) {
311
299
  let dirty = !alreadyUploaded;
@@ -1 +1 @@
1
- {"version":3,"file":"sprite-pipeline.js","sources":["../../../src/sprite/sprite-pipeline.ts"],"sourcesContent":["/** Internal sprite pipeline helpers: owns WGSL, bind-group schema, pipeline construction, and bind-group creation. */\nimport { U16 } from \"../engine/typed-arrays.js\";\nimport { BU, SS, CW } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Sprite2DLayer, SpriteBlendMode } from \"./sprite-2d.js\";\nimport type { SpriteLayerFx } from \"./custom-shader-core.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport { DEPTH_INSTANCE_STRIDE_BYTES, DEPTH_UVSCROLL_STRIDE_BYTES, PURE_2D_INSTANCE_STRIDE_BYTES, PURE_2D_UVSCROLL_STRIDE_BYTES } from \"./sprite-2d.js\";\n\n/** @internal */\nexport interface SpritePipelineDeviceCache {\n /** @internal */\n _shaderModule: GPUShaderModule | null;\n /** @internal */\n _sceneShaderModule: GPUShaderModule | null;\n /** @internal */\n _shaderModuleUv: GPUShaderModule | null;\n /** @internal */\n _sceneShaderModuleUv: GPUShaderModule | null;\n /** @internal */\n _pipelines: Map<string, GPURenderPipeline>;\n}\n\n/** @internal */\nexport interface SpritePipelineCache {\n /** @internal */\n _devices: WeakMap<GPUDevice, SpritePipelineDeviceCache>;\n}\n\nconst SPRITE_POSITION_OFFSET_BYTES = 0;\nconst SPRITE_SIZE_OFFSET_BYTES = 8;\nconst SPRITE_UV_MIN_OFFSET_BYTES = 16;\nconst SPRITE_UV_MAX_OFFSET_BYTES = 24;\nconst SPRITE_ROTATION_OFFSET_BYTES = 32;\nconst SPRITE_COLOR_OFFSET_BYTES = 36;\nconst SPRITE_DEPTH_OFFSET_BYTES = 52;\n/** uvOffset.xy byte offset: appended after the base layout (52 pure-2D, 56 depth-hosted). */\nconst SPRITE_UVOFFSET_OFFSET_PURE_2D_BYTES = 52;\nconst SPRITE_UVOFFSET_OFFSET_DEPTH_BYTES = 56;\n\nfunction makeSpriteWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll: boolean): string {\n return `${makeSpritePrologueWgsl(hasDepth, spriteGroupIndex, uvScroll)}\n@fragment\nfn fs(in: VOut) -> @location(0) vec4<f32> {\nlet s = textureSample(atlasTex, atlasSamp, in.uv);\nreturn s * in.tint * L.opacityMul;\n}`;\n}\n\n/**\n * Shared WGSL prologue for the sprite shader: the `Layer` UBO, atlas texture + sampler\n * bindings, instance-attribute `VIn` / interpolant `VOut` structs, and the `vs` vertex stage.\n * The default sprite shader appends a trivial textured fragment; the opt-in custom-shader\n * module (`createSprite2DCustomShader`) appends any extra-texture bindings, a `SpriteFx` UBO at\n * `@binding(3 + 2 * extraTextures.length)`, and the user's raw fragment body. Exposed so both\n * paths share one source of truth.\n */\nexport function makeSpritePrologueWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll = false): string {\n const group = `@group(${spriteGroupIndex})`;\n const zAttribute = hasDepth ? `,\\n@location(6) iZ: f32` : \"\";\n const uvOffsetAttribute = uvScroll ? `,\\n@location(7) iUvOffset: vec2<f32>` : \"\";\n const zPosition = hasDepth ? \"1.0 - in.iZ\" : \"0.0\";\n return `struct Layer {\nviewPos: vec2<f32>,\nviewScale: f32,\nviewRot: f32,\nscreenSize: vec2<f32>,\npivot: vec2<f32>,\n// Per-layer opacity, pre-shaped for the layer's blend mode (CPU-side):\n// straight-alpha: (1, 1, 1, opacity) — only alpha is scaled\n// premultiplied: (opacity, opacity, opacity, opacity) — RGB and A scale together\n// One uniform, no shader branch.\nopacityMul: vec4<f32>,\n};\n${group} @binding(0) var<uniform> L: Layer;\n${group} @binding(1) var atlasTex: texture_2d<f32>;\n${group} @binding(2) var atlasSamp: sampler;\nstruct VIn {\n@builtin(vertex_index) vid: u32,\n@location(0) iPos: vec2<f32>,\n@location(1) iSize: vec2<f32>,\n@location(2) iUvMin: vec2<f32>,\n@location(3) iUvMax: vec2<f32>,\n@location(4) iRot: f32,\n@location(5) iColor: vec4<f32>${zAttribute}${uvOffsetAttribute}\n};\nstruct VOut {\n@builtin(position) pos: vec4<f32>,\n@location(0) uv: vec2<f32>,\n@location(1) tint: vec4<f32>,\n};\n@vertex\nfn vs(in: VIn) -> VOut {\nvar corners = array<vec2<f32>, 4>(vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 0.0), vec2<f32>(1.0, 1.0), vec2<f32>(0.0, 1.0));\nlet c = corners[in.vid];\nlet local = (c - L.pivot) * in.iSize;\nlet cr = cos(in.iRot);\nlet sr = sin(in.iRot);\nlet rotated = vec2<f32>(local.x * cr - local.y * sr, local.x * sr + local.y * cr);\nlet layerPx = in.iPos + rotated;\nlet centered = layerPx - L.viewPos;\nlet lc = cos(L.viewRot);\nlet ls = sin(L.viewRot);\nlet viewRot = vec2<f32>(centered.x * lc - centered.y * ls, centered.x * ls + centered.y * lc);\nlet screenPx = viewRot * L.viewScale;\nlet ndc = vec2<f32>(screenPx.x / L.screenSize.x * 2.0 - 1.0, 1.0 - screenPx.y / L.screenSize.y * 2.0);\nlet uv = mix(in.iUvMin, in.iUvMax, c)${uvScroll ? \" + in.iUvOffset\" : \"\"};\nvar out: VOut;\nout.pos = vec4<f32>(ndc, ${zPosition}, 1.0);\nout.uv = uv;\nout.tint = in.iColor;\nreturn out;\n}`;\n}\n\nexport function createSpritePipelineCache(): SpritePipelineCache {\n return {\n _devices: new WeakMap(),\n };\n}\n\nexport function resetSpritePipelineCache(cache: SpritePipelineCache): void {\n cache._devices = new WeakMap();\n}\n\nexport function getSpritePipelineCacheSize(cache: SpritePipelineCache, device: GPUDevice): number {\n return cache._devices.get(device)?._pipelines.size ?? 0;\n}\n\nexport function getOrCreateSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite = false,\n depthStencilFormat?: GPUTextureFormat,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const deviceCache = getSpritePipelineDeviceCache(engine, cache);\n const resolvedDepthStencilFormat = normalizeDepthStencilFormat(hasDepth, depthStencilFormat);\n const key = spritePipelineKey(format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, layer);\n const cached = deviceCache._pipelines.get(key);\n if (cached) {\n return cached;\n }\n\n const pipeline = buildSpritePipeline(engine, deviceCache, format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, sceneBindGroupLayout, layer);\n deviceCache._pipelines.set(key, pipeline);\n return pipeline;\n}\n\nexport function createSpriteLayerBindGroup(\n engine: EngineContext,\n pipeline: GPURenderPipeline,\n spriteBindGroupIndex: 0 | 1,\n layer: Sprite2DLayer,\n uniformBuffer: GPUBuffer,\n fx?: SpriteLayerFx | null\n): GPUBindGroup {\n const tex = layer.atlas.texture;\n const entries: GPUBindGroupEntry[] = [\n { binding: 0, resource: { buffer: uniformBuffer } },\n { binding: 1, resource: tex.view },\n { binding: 2, resource: tex.sampler },\n ];\n if (fx) {\n for (const entry of _getSpriteFxHook()!.bindEntries(fx, 3)) {\n entries.push(entry);\n }\n }\n return engine._device.createBindGroup({\n layout: pipeline.getBindGroupLayout(spriteBindGroupIndex),\n entries,\n });\n}\n\nfunction getSpritePipelineDeviceCache(engine: EngineContext, cache: SpritePipelineCache): SpritePipelineDeviceCache {\n let deviceCache = cache._devices.get(engine._device);\n if (!deviceCache) {\n deviceCache = {\n _shaderModule: null,\n _sceneShaderModule: null,\n _shaderModuleUv: null,\n _sceneShaderModuleUv: null,\n _pipelines: new Map(),\n };\n cache._devices.set(engine._device, deviceCache);\n }\n return deviceCache;\n}\n\nfunction normalizeDepthStencilFormat(hasDepth: boolean, depthStencilFormat?: GPUTextureFormat): GPUTextureFormat | null {\n if (!hasDepth) {\n return null;\n }\n if (!depthStencilFormat) {\n throw new Error(\"Sprite pipeline: depth-enabled pipelines require a depth-stencil format.\");\n }\n return depthStencilFormat;\n}\n\nfunction spritePipelineKey(\n format: GPUTextureFormat,\n sampleCount: number,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite: boolean,\n depthStencilFormat: GPUTextureFormat | null,\n layer?: Sprite2DLayer\n): string {\n const customKey = layer ? (_getSpriteFxHook()?.pipelineKeyPart(layer) ?? \"\") : \"\";\n const uvKey = layer?._uvScroll ? \"1\" : \"0\";\n return `${format}:${sampleCount}:${blendMode._key}:${hasDepth ? 1 : 0}:${depthWrite ? 1 : 0}:${depthStencilFormat ?? \"-\"}:cs${customKey}:uv${uvKey}`;\n}\n\nfunction getShaderModule(engine: EngineContext, cache: SpritePipelineDeviceCache, hasDepth: boolean, layer?: Sprite2DLayer): GPUShaderModule {\n const customModule = layer ? _getSpriteFxHook()?.shaderModule(engine, hasDepth, layer) : null;\n if (customModule) {\n return customModule;\n }\n const uvScroll = layer?._uvScroll === true;\n if (hasDepth) {\n if (uvScroll) {\n cache._sceneShaderModuleUv ??= engine._device.createShaderModule({ code: makeSpriteWgsl(true, 1, true) });\n return cache._sceneShaderModuleUv;\n }\n cache._sceneShaderModule ??= engine._device.createShaderModule({ code: makeSpriteWgsl(true, 1, false) });\n return cache._sceneShaderModule;\n }\n if (uvScroll) {\n cache._shaderModuleUv ??= engine._device.createShaderModule({ code: makeSpriteWgsl(false, 0, true) });\n return cache._shaderModuleUv;\n }\n cache._shaderModule ??= engine._device.createShaderModule({ code: makeSpriteWgsl(false, 0, false) });\n return cache._shaderModule;\n}\n\nfunction buildSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineDeviceCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite: boolean,\n depthStencilFormat: GPUTextureFormat | null,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const device = engine._device;\n const layoutEntries: GPUBindGroupLayoutEntry[] = [\n { binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: SS.FRAGMENT, texture: { sampleType: \"float\" } },\n { binding: 2, visibility: SS.FRAGMENT, sampler: { type: \"filtering\" } },\n ];\n const extraLayoutEntries = layer ? _getSpriteFxHook()?.layoutEntries(layer, 3) : null;\n if (extraLayoutEntries) {\n for (const entry of extraLayoutEntries) {\n layoutEntries.push(entry);\n }\n }\n const bindGroupLayout = device.createBindGroupLayout({ entries: layoutEntries });\n const module = getShaderModule(engine, cache, hasDepth, layer);\n if (hasDepth && !sceneBindGroupLayout) {\n throw new Error(\"Sprite pipeline: depth-enabled pipelines require a scene bind-group layout.\");\n }\n const bindGroupLayouts = hasDepth ? [sceneBindGroupLayout!, bindGroupLayout] : [bindGroupLayout];\n const instanceAttributes: GPUVertexAttribute[] = [\n { shaderLocation: 0, offset: SPRITE_POSITION_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 1, offset: SPRITE_SIZE_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 2, offset: SPRITE_UV_MIN_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 3, offset: SPRITE_UV_MAX_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 4, offset: SPRITE_ROTATION_OFFSET_BYTES, format: \"float32\" },\n { shaderLocation: 5, offset: SPRITE_COLOR_OFFSET_BYTES, format: \"float32x4\" },\n ];\n if (hasDepth) {\n instanceAttributes.push({ shaderLocation: 6, offset: SPRITE_DEPTH_OFFSET_BYTES, format: \"float32\" });\n }\n const uvScroll = layer?._uvScroll === true;\n if (uvScroll) {\n instanceAttributes.push({\n shaderLocation: 7,\n offset: hasDepth ? SPRITE_UVOFFSET_OFFSET_DEPTH_BYTES : SPRITE_UVOFFSET_OFFSET_PURE_2D_BYTES,\n format: \"float32x2\",\n });\n }\n const arrayStride = uvScroll\n ? hasDepth\n ? DEPTH_UVSCROLL_STRIDE_BYTES\n : PURE_2D_UVSCROLL_STRIDE_BYTES\n : hasDepth\n ? DEPTH_INSTANCE_STRIDE_BYTES\n : PURE_2D_INSTANCE_STRIDE_BYTES;\n const descriptor: GPURenderPipelineDescriptor = {\n layout: device.createPipelineLayout({ bindGroupLayouts }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n {\n arrayStride: arrayStride,\n stepMode: \"instance\",\n attributes: instanceAttributes,\n },\n ],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format, blend: blendMode._descriptor, writeMask: CW.ALL }],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n multisample: { count: sampleCount },\n };\n if (hasDepth) {\n descriptor.depthStencil = {\n format: depthStencilFormat!,\n depthCompare: \"greater-equal\",\n depthWriteEnabled: depthWrite,\n };\n }\n return device.createRenderPipeline(descriptor);\n}\n\n// ─── Per-layer GPU sync helpers ────────────────────────────────────────────\n// Shared by `sprite-renderer.ts` (multi-layer pure-2D pass) and\n// `sprite-renderable.ts` (single-layer depth-hosted scene `Renderable`).\n// The two consumers have different lifecycles (renderer caches a `LayerGpu`\n// per layer; renderable owns one `Sprite2DLayer`) but the per-frame work —\n// \"grow instance buffer if needed\", \"upload dirty instance range\",\n// \"build the 12-float UBO\", \"writeBuffer only if changed\" — is identical.\n\n/** Per-layer UBO size in bytes. 12 floats; struct alignment forced to 16 by `vec4<f32>` fields. */\nexport const LAYER_UBO_BYTES = 48;\n/** Number of floats in the per-layer UBO scratch / lastUbo arrays. */\nexport const LAYER_UBO_FLOATS = LAYER_UBO_BYTES / 4;\n\n/** Shared two-triangle quad index buffer source (4 corners → 6 indices). */\nexport const SHARED_SPRITE_INDEX_DATA: Readonly<Uint16Array> = new U16([0, 1, 2, 0, 2, 3]);\n\n/** Allocate a per-layer instance vertex buffer sized for `capacity` sprites. */\nexport function createSpriteInstanceBuffer(device: GPUDevice, layer: Sprite2DLayer, label?: string): GPUBuffer {\n return device.createBuffer({\n size: layer._capacity * layer._instanceStrideBytes,\n usage: BU.VERTEX | BU.COPY_DST,\n label,\n });\n}\n\n/**\n * Reallocate the instance buffer if `layer._capacity` outgrew the current GPU buffer.\n * Returns the (possibly new) buffer + the new capacity, plus a `reallocated` flag the\n * caller uses to invalidate per-buffer caches (render bundles, `uploadedVersion`, etc).\n */\nexport function ensureSpriteInstanceBuffer(\n device: GPUDevice,\n layer: Sprite2DLayer,\n currentBuffer: GPUBuffer,\n currentCapacity: number,\n label?: string\n): { buffer: GPUBuffer; capacity: number; reallocated: boolean } {\n if (currentCapacity >= layer._capacity) {\n return { buffer: currentBuffer, capacity: currentCapacity, reallocated: false };\n }\n currentBuffer.destroy();\n return {\n buffer: createSpriteInstanceBuffer(device, layer, label),\n capacity: layer._capacity,\n reallocated: true,\n };\n}\n\n/**\n * Sync per-instance vertex data to `instanceBuffer`. Returns the new `uploadedVersion`\n * the caller should store. No-op if `layer._version` hasn't advanced or the layer is\n * empty. On first sight (`uploadedVersion === -1`) uploads `[0, count)`; on subsequent\n * edits uploads only `[_dirtyMin, min(_dirtyMax, count))`. Resets the dirty range.\n */\nexport function uploadSpriteInstances(device: GPUDevice, layer: Sprite2DLayer, instanceBuffer: GPUBuffer, uploadedVersion: number): number {\n if (uploadedVersion === layer._version) {\n return uploadedVersion;\n }\n if (layer.count === 0) {\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n }\n let lo: number;\n let hi: number;\n if (uploadedVersion === -1) {\n lo = 0;\n hi = layer.count;\n } else {\n lo = layer._dirtyMin;\n hi = Math.min(layer._dirtyMax, layer.count);\n }\n if (hi > lo) {\n const offsetBytes = lo * layer._instanceStrideBytes;\n const bytes = (hi - lo) * layer._instanceStrideBytes;\n device.queue.writeBuffer(instanceBuffer, offsetBytes, layer._instanceData.buffer, layer._instanceData.byteOffset + offsetBytes, bytes);\n }\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n}\n\n/**\n * Fill `ubo` (12 floats) with the per-layer UBO contents from `layer` at the given\n * render-target dims. Layout matches the WGSL `Layer` struct (48 bytes total):\n * [0..1] viewPos.xy [2] viewScale [3] viewRot\n * [4..5] screenSize.xy [6..7] pivot.xy\n * [8..11] opacityMul.rgba (pre-shaped per blend mode)\n *\n * Depth-hosted layers keep per-sprite NDC depth on the per-instance vertex buffer\n * (slot [13] of `Sprite2DLayer._instanceData`), not in this UBO — a single\n * depth-hosted layer can mix sprites at different depths. Pure-2D layers have no\n * Z slot.\n *\n * Premultiplied sources need RGB *and* A scaled by opacity for a correct fade;\n * straight-alpha needs only A scaled (the blend stage already uses src.a as factor).\n */\nexport function buildSpriteLayerUbo(layer: Sprite2DLayer, screenWidth: number, screenHeight: number, ubo: Float32Array): void {\n ubo[0] = layer.view.positionPx[0];\n ubo[1] = layer.view.positionPx[1];\n ubo[2] = layer.view.zoom;\n ubo[3] = layer.view.rotation;\n ubo[4] = screenWidth;\n ubo[5] = screenHeight;\n ubo[6] = layer.pivot[0];\n ubo[7] = layer.pivot[1];\n const op = layer.opacity;\n if (layer.blendMode._premultipliedOpacity) {\n ubo[8] = op;\n ubo[9] = op;\n ubo[10] = op;\n ubo[11] = op;\n } else {\n ubo[8] = 1;\n ubo[9] = 1;\n ubo[10] = 1;\n ubo[11] = op;\n }\n}\n\n/**\n * Compare `scratchUbo` to `lastUbo` (LAYER_UBO_FLOATS each) and `writeBuffer` only if they\n * differ. On first call (`alreadyUploaded === false`) forces an unconditional write\n * so `lastUbo` becomes real. Returns the new `alreadyUploaded` value (always `true`\n * after the first call regardless of whether bytes changed).\n */\nexport function writeSpriteLayerUboIfDirty(device: GPUDevice, uniformBuffer: GPUBuffer, scratchUbo: Float32Array, lastUbo: Float32Array, alreadyUploaded: boolean): boolean {\n let dirty = !alreadyUploaded;\n if (!dirty) {\n for (let i = 0; i < LAYER_UBO_FLOATS; i++) {\n if (lastUbo[i] !== scratchUbo[i]) {\n dirty = true;\n break;\n }\n }\n }\n if (dirty) {\n device.queue.writeBuffer(uniformBuffer, 0, scratchUbo.buffer, scratchUbo.byteOffset, LAYER_UBO_BYTES);\n lastUbo.set(scratchUbo);\n }\n return true;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sprite-pipeline.js","sources":["../../../src/sprite/sprite-pipeline.ts"],"sourcesContent":["/** Internal sprite pipeline helpers: owns WGSL, bind-group schema, pipeline construction, and bind-group creation. */\nimport { U16 } from \"../engine/typed-arrays.js\";\nimport { BU, SS, CW } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Sprite2DLayer, SpriteBlendMode } from \"./sprite-2d.js\";\nimport type { SpriteLayerFx } from \"./custom-shader-core.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport { _getSpriteCoverageGammaHook } from \"./sprite-coverage-gamma-hook.js\";\nimport { DEPTH_INSTANCE_STRIDE_BYTES, PURE_2D_INSTANCE_STRIDE_BYTES } from \"./sprite-2d.js\";\n\n/** @internal */\nexport interface SpritePipelineDeviceCache {\n /** @internal Shader modules keyed by `${hasDepth}:${uvScroll}` permutation. */\n _shaderModules: Map<string, GPUShaderModule>;\n /** @internal */\n _pipelines: Map<string, GPURenderPipeline>;\n}\n\n/** @internal */\nexport interface SpritePipelineCache {\n /** @internal */\n _devices: WeakMap<GPUDevice, SpritePipelineDeviceCache>;\n}\n\nconst SPRITE_POSITION_OFFSET_BYTES = 0;\nconst SPRITE_SIZE_OFFSET_BYTES = 8;\nconst SPRITE_UV_MIN_OFFSET_BYTES = 16;\nconst SPRITE_UV_MAX_OFFSET_BYTES = 24;\nconst SPRITE_ROTATION_OFFSET_BYTES = 32;\nconst SPRITE_COLOR_OFFSET_BYTES = 36;\nconst SPRITE_DEPTH_OFFSET_BYTES = 52;\n\nfunction makeSpriteWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll: boolean): string {\n return `${makeSpritePrologueWgsl(hasDepth, spriteGroupIndex, uvScroll)}\n@fragment\nfn fs(in: O) -> @location(0) vec4f {\nlet s = textureSample(atlasTex, atlasSamp, in.uv);\nreturn s * in.tint * L.opacityMul;\n}`;\n}\n\n/**\n * Shared WGSL prologue for the sprite shader: the `Layer` UBO, atlas texture + sampler\n * bindings, instance-attribute `VIn` / interpolant `VOut` structs, and the `vs` vertex stage.\n * The default sprite shader appends a trivial textured fragment; the opt-in custom-shader\n * module (`createSprite2DCustomShader`) appends any extra-texture bindings, a `SpriteFx` UBO at\n * `@binding(3 + 2 * extraTextures.length)`, and the user's raw fragment body. Exposed so both\n * paths share one source of truth.\n */\nexport function makeSpritePrologueWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll = false): string {\n const group = `@group(${spriteGroupIndex})`;\n const zAttribute = hasDepth ? `,\\n@location(6) z: f32` : \"\";\n const uvOffsetAttribute = uvScroll ? `,\\n@location(7) o: vec2f` : \"\";\n const zPosition = hasDepth ? \"1 - in.z\" : \"0\";\n return `struct Lr {\nviewPos: vec2f,\nviewScale: f32,\nviewRot: f32,\nscreenSize: vec2f,\npivot: vec2f,\nopacityMul: vec4f,\naa: vec4f,\n};\n${group} @binding(0) var<uniform> L: Lr;\n${group} @binding(1) var atlasTex: texture_2d<f32>;\n${group} @binding(2) var atlasSamp: sampler;\nstruct I {\n@builtin(vertex_index) vid: u32,\n@location(0) p: vec2f,\n@location(1) s: vec2f,\n@location(2) a: vec2f,\n@location(3) b: vec2f,\n@location(4) r: f32,\n@location(5) c: vec4f${zAttribute}${uvOffsetAttribute}\n};\nstruct O {\n@builtin(position) p: vec4f,\n@location(0) uv: vec2f,\n@location(1) tint: vec4f,\n};\n@vertex\nfn vs(in: I) -> O {\nvar q = array<vec2f, 4>(vec2f(0, 0), vec2f(1, 0), vec2f(1, 1), vec2f(0, 1));\nlet c = q[in.vid];\nlet l = (c - L.pivot) * in.s;\nlet cr = cos(in.r);\nlet sr = sin(in.r);\nlet r = vec2f(l.x * cr - l.y * sr, l.x * sr + l.y * cr);\nlet p = in.p + r - L.viewPos;\nlet lc = cos(L.viewRot);\nlet ls = sin(L.viewRot);\nlet v = vec2f(p.x * lc - p.y * ls, p.x * ls + p.y * lc) * L.viewScale;\nlet n = vec2f(v.x / L.screenSize.x * 2 - 1, 1 - v.y / L.screenSize.y * 2);\nlet uv = mix(in.a, in.b, c)${uvScroll ? \" + in.o\" : \"\"};\nvar out: O;\nout.p = vec4f(n, ${zPosition}, 1);\nout.uv = uv;\nout.tint = in.c;\nreturn out;\n}`;\n}\n\nexport function createSpritePipelineCache(): SpritePipelineCache {\n return {\n _devices: new WeakMap(),\n };\n}\n\nexport function resetSpritePipelineCache(cache: SpritePipelineCache): void {\n cache._devices = new WeakMap();\n}\n\nexport function getSpritePipelineCacheSize(cache: SpritePipelineCache, device: GPUDevice): number {\n return cache._devices.get(device)?._pipelines.size ?? 0;\n}\n\nexport function getOrCreateSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite = false,\n depthStencilFormat?: GPUTextureFormat,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const deviceCache = getSpritePipelineDeviceCache(engine, cache);\n const resolvedDepthStencilFormat = normalizeDepthStencilFormat(hasDepth, depthStencilFormat);\n const key = spritePipelineKey(format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, layer);\n const cached = deviceCache._pipelines.get(key);\n if (cached) {\n return cached;\n }\n\n const pipeline = buildSpritePipeline(engine, deviceCache, format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, sceneBindGroupLayout, layer);\n deviceCache._pipelines.set(key, pipeline);\n return pipeline;\n}\n\nexport function createSpriteLayerBindGroup(\n engine: EngineContext,\n pipeline: GPURenderPipeline,\n spriteBindGroupIndex: 0 | 1,\n layer: Sprite2DLayer,\n uniformBuffer: GPUBuffer,\n fx?: SpriteLayerFx | null\n): GPUBindGroup {\n const tex = layer.atlas.texture;\n const entries: GPUBindGroupEntry[] = [\n { binding: 0, resource: { buffer: uniformBuffer } },\n { binding: 1, resource: tex.view },\n { binding: 2, resource: tex.sampler },\n ];\n if (fx) {\n for (const entry of _getSpriteFxHook()!.bindEntries(fx, 3)) {\n entries.push(entry);\n }\n }\n return engine._device.createBindGroup({\n layout: pipeline.getBindGroupLayout(spriteBindGroupIndex),\n entries,\n });\n}\n\nfunction getSpritePipelineDeviceCache(engine: EngineContext, cache: SpritePipelineCache): SpritePipelineDeviceCache {\n let deviceCache = cache._devices.get(engine._device);\n if (!deviceCache) {\n deviceCache = {\n _shaderModules: new Map(),\n _pipelines: new Map(),\n };\n cache._devices.set(engine._device, deviceCache);\n }\n return deviceCache;\n}\n\nfunction normalizeDepthStencilFormat(hasDepth: boolean, depthStencilFormat?: GPUTextureFormat): GPUTextureFormat | null {\n if (!hasDepth) {\n return null;\n }\n if (!depthStencilFormat) {\n throw new Error(\"Sprite pipeline: depth-enabled pipelines require a depth-stencil format.\");\n }\n return depthStencilFormat;\n}\n\nfunction spritePipelineKey(\n format: GPUTextureFormat,\n sampleCount: number,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite: boolean,\n depthStencilFormat: GPUTextureFormat | null,\n layer?: Sprite2DLayer\n): string {\n const customKey = layer ? (_getSpriteFxHook()?.pipelineKeyPart(layer) ?? \"\") : \"\";\n const uvKey = layer?._uvScrollAttr ? \"1\" : \"0\";\n const cgKey = layer ? (_getSpriteCoverageGammaHook()?.pipelineKeyPart(layer) ?? \"0\") : \"0\";\n return `${format}:${sampleCount}:${blendMode._key}:${hasDepth ? 1 : 0}:${depthWrite ? 1 : 0}:${depthStencilFormat ?? \"-\"}:cs${customKey}:uv${uvKey}:cg${cgKey}`;\n}\n\nfunction getShaderModule(engine: EngineContext, cache: SpritePipelineDeviceCache, hasDepth: boolean, layer?: Sprite2DLayer): GPUShaderModule {\n const customModule = layer ? _getSpriteFxHook()?.shaderModule(engine, hasDepth, layer) : null;\n if (customModule) {\n return customModule;\n }\n const gammaModule = layer ? _getSpriteCoverageGammaHook()?.shaderModule(engine, hasDepth, layer) : null;\n if (gammaModule) {\n return gammaModule;\n }\n const uvScroll = layer?._uvScrollAttr != null;\n const key = `${hasDepth ? 1 : 0}:${uvScroll ? 1 : 0}`;\n let module = cache._shaderModules.get(key);\n if (!module) {\n module = engine._device.createShaderModule({\n code: makeSpriteWgsl(hasDepth, hasDepth ? 1 : 0, uvScroll),\n });\n cache._shaderModules.set(key, module);\n }\n return module;\n}\n\nfunction buildSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineDeviceCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite: boolean,\n depthStencilFormat: GPUTextureFormat | null,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const device = engine._device;\n const layoutEntries: GPUBindGroupLayoutEntry[] = [\n { binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: SS.FRAGMENT, texture: { sampleType: \"float\" } },\n { binding: 2, visibility: SS.FRAGMENT, sampler: { type: \"filtering\" } },\n ];\n const extraLayoutEntries = layer ? _getSpriteFxHook()?.layoutEntries(layer, 3) : null;\n if (extraLayoutEntries) {\n for (const entry of extraLayoutEntries) {\n layoutEntries.push(entry);\n }\n }\n const bindGroupLayout = device.createBindGroupLayout({ entries: layoutEntries });\n const module = getShaderModule(engine, cache, hasDepth, layer);\n if (hasDepth && !sceneBindGroupLayout) {\n throw new Error(\"Sprite pipeline: depth-enabled pipelines require a scene bind-group layout.\");\n }\n const bindGroupLayouts = hasDepth ? [sceneBindGroupLayout!, bindGroupLayout] : [bindGroupLayout];\n const instanceAttributes: GPUVertexAttribute[] = [\n { shaderLocation: 0, offset: SPRITE_POSITION_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 1, offset: SPRITE_SIZE_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 2, offset: SPRITE_UV_MIN_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 3, offset: SPRITE_UV_MAX_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 4, offset: SPRITE_ROTATION_OFFSET_BYTES, format: \"float32\" },\n { shaderLocation: 5, offset: SPRITE_COLOR_OFFSET_BYTES, format: \"float32x4\" },\n ];\n if (hasDepth) {\n instanceAttributes.push({ shaderLocation: 6, offset: SPRITE_DEPTH_OFFSET_BYTES, format: \"float32\" });\n }\n // uvScroll (opt-in via setSprite2DUvOffset) appends a `uvOffset.xy` attribute after the base\n // layout, like `hasDepth` appends `@location(6)`. The attribute is precomputed by the opt-in\n // module and stashed on the layer as plain data — so the always-loaded path ships none of the\n // attribute-building, just this data consume. The widened stride is likewise already on the layer.\n if (layer?._uvScrollAttr) {\n instanceAttributes.push(layer._uvScrollAttr);\n }\n const arrayStride = layer?._instanceStrideBytes ?? (hasDepth ? DEPTH_INSTANCE_STRIDE_BYTES : PURE_2D_INSTANCE_STRIDE_BYTES);\n const descriptor: GPURenderPipelineDescriptor = {\n layout: device.createPipelineLayout({ bindGroupLayouts }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n {\n arrayStride: arrayStride,\n stepMode: \"instance\",\n attributes: instanceAttributes,\n },\n ],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format, blend: blendMode._descriptor, writeMask: CW.ALL }],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n multisample: { count: sampleCount },\n };\n if (hasDepth) {\n descriptor.depthStencil = {\n format: depthStencilFormat!,\n depthCompare: \"greater-equal\",\n depthWriteEnabled: depthWrite,\n };\n }\n return device.createRenderPipeline(descriptor);\n}\n\n// ─── Per-layer GPU sync helpers ────────────────────────────────────────────\n// Shared by `sprite-renderer.ts` (multi-layer pure-2D pass) and\n// `sprite-renderable.ts` (single-layer depth-hosted scene `Renderable`).\n// The two consumers have different lifecycles (renderer caches a `LayerGpu`\n// per layer; renderable owns one `Sprite2DLayer`) but the per-frame work —\n// \"grow instance buffer if needed\", \"upload dirty instance range\",\n// \"build the 12-float UBO\", \"writeBuffer only if changed\" — is identical.\n\n/** Per-layer UBO size in bytes. 16 floats; struct alignment forced to 16 by `vec4<f32>` fields. */\nexport const LAYER_UBO_BYTES = 64;\n/** Number of floats in the per-layer UBO scratch / lastUbo arrays. */\nexport const LAYER_UBO_FLOATS = LAYER_UBO_BYTES / 4;\n\n/** Shared two-triangle quad index buffer source (4 corners → 6 indices). */\nexport const SHARED_SPRITE_INDEX_DATA: Readonly<Uint16Array> = new U16([0, 1, 2, 0, 2, 3]);\n\n/** Allocate a per-layer instance vertex buffer sized for `capacity` sprites. */\nexport function createSpriteInstanceBuffer(device: GPUDevice, layer: Sprite2DLayer, label?: string): GPUBuffer {\n return device.createBuffer({\n size: layer._capacity * layer._instanceStrideBytes,\n usage: BU.VERTEX | BU.COPY_DST,\n label,\n });\n}\n\n/**\n * Reallocate the instance buffer if it can no longer hold `layer._capacity` sprites at the layer's\n * current per-sprite stride. The comparison is **byte-based** (`currentBuffer.size` vs the required\n * byte size) so it triggers both on capacity growth *and* on a stride change — e.g. when the opt-in\n * `setSprite2DUvOffset` widens a previously narrow layer in place. Returns the (possibly new) buffer\n * plus a `reallocated` flag the caller uses to invalidate per-buffer caches (render bundles, etc).\n */\nexport function ensureSpriteInstanceBuffer(\n device: GPUDevice,\n layer: Sprite2DLayer,\n currentBuffer: GPUBuffer,\n currentCapacity: number,\n label?: string\n): { buffer: GPUBuffer; capacity: number; reallocated: boolean } {\n const neededBytes = layer._capacity * layer._instanceStrideBytes;\n if (currentBuffer.size >= neededBytes) {\n return { buffer: currentBuffer, capacity: currentCapacity, reallocated: false };\n }\n currentBuffer.destroy();\n return {\n buffer: createSpriteInstanceBuffer(device, layer, label),\n capacity: layer._capacity,\n reallocated: true,\n };\n}\n\n/**\n * Sync per-instance vertex data to `instanceBuffer`. Returns the new `uploadedVersion`\n * the caller should store. No-op if `layer._version` hasn't advanced or the layer is\n * empty. On first sight (`uploadedVersion === -1`) uploads `[0, count)`; on subsequent\n * edits uploads only `[_dirtyMin, min(_dirtyMax, count))`. Resets the dirty range.\n */\nexport function uploadSpriteInstances(device: GPUDevice, layer: Sprite2DLayer, instanceBuffer: GPUBuffer, uploadedVersion: number): number {\n if (uploadedVersion === layer._version) {\n return uploadedVersion;\n }\n if (layer.count === 0) {\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n }\n let lo: number;\n let hi: number;\n if (uploadedVersion === -1) {\n lo = 0;\n hi = layer.count;\n } else {\n lo = layer._dirtyMin;\n hi = Math.min(layer._dirtyMax, layer.count);\n }\n if (hi > lo) {\n const offsetBytes = lo * layer._instanceStrideBytes;\n const bytes = (hi - lo) * layer._instanceStrideBytes;\n device.queue.writeBuffer(instanceBuffer, offsetBytes, layer._instanceData.buffer, layer._instanceData.byteOffset + offsetBytes, bytes);\n }\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n}\n\n/**\n * Fill `ubo` (16 floats) with the per-layer UBO contents from `layer` at the given\n * render-target dims. Layout matches the WGSL `Layer` struct (64 bytes total):\n * [0..1] viewPos.xy [2] viewScale [3] viewRot\n * [4..5] screenSize.xy [6..7] pivot.xy\n * [8..11] opacityMul.rgba (pre-shaped per blend mode)\n * [12] 1/coverageGamma (coverage-gamma layers only) [13..15] reserved\n *\n * Depth-hosted layers keep per-sprite NDC depth on the per-instance vertex buffer\n * (slot [13] of `Sprite2DLayer._instanceData`), not in this UBO — a single\n * depth-hosted layer can mix sprites at different depths. Pure-2D layers have no\n * Z slot.\n *\n * Premultiplied sources need RGB *and* A scaled by opacity for a correct fade;\n * straight-alpha needs only A scaled (the blend stage already uses src.a as factor).\n */\nexport function buildSpriteLayerUbo(layer: Sprite2DLayer, screenWidth: number, screenHeight: number, ubo: Float32Array): void {\n ubo[0] = layer.view.positionPx[0];\n ubo[1] = layer.view.positionPx[1];\n ubo[2] = layer.view.zoom;\n ubo[3] = layer.view.rotation;\n ubo[4] = screenWidth;\n ubo[5] = screenHeight;\n ubo[6] = layer.pivot[0];\n ubo[7] = layer.pivot[1];\n const op = layer.opacity;\n if (layer.blendMode._premultipliedOpacity) {\n ubo[8] = op;\n ubo[9] = op;\n ubo[10] = op;\n ubo[11] = op;\n } else {\n ubo[8] = 1;\n ubo[9] = 1;\n ubo[10] = 1;\n ubo[11] = op;\n }\n // Coverage gamma (opt-in via setSprite2DCoverageGamma): the hook writes aa.x = 1/coverageGamma\n // for gamma layers and 0 otherwise. Absent when no gamma layer exists, so non-gamma scenes ship\n // no gamma bytes here; aa.yzw stay 0 (scratch UBO is zero-initialized and reused).\n _getSpriteCoverageGammaHook()?.writeUbo(layer, ubo);\n}\n\n/**\n * Compare `scratchUbo` to `lastUbo` (LAYER_UBO_FLOATS each) and `writeBuffer` only if they\n * differ. On first call (`alreadyUploaded === false`) forces an unconditional write\n * so `lastUbo` becomes real. Returns the new `alreadyUploaded` value (always `true`\n * after the first call regardless of whether bytes changed).\n */\nexport function writeSpriteLayerUboIfDirty(device: GPUDevice, uniformBuffer: GPUBuffer, scratchUbo: Float32Array, lastUbo: Float32Array, alreadyUploaded: boolean): boolean {\n let dirty = !alreadyUploaded;\n if (!dirty) {\n for (let i = 0; i < LAYER_UBO_FLOATS; i++) {\n if (lastUbo[i] !== scratchUbo[i]) {\n dirty = true;\n break;\n }\n }\n }\n if (dirty) {\n device.queue.writeBuffer(uniformBuffer, 0, scratchUbo.buffer, scratchUbo.byteOffset, LAYER_UBO_BYTES);\n lastUbo.set(scratchUbo);\n }\n return true;\n}\n"],"names":[],"mappings":"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"}
@@ -24,13 +24,13 @@ function releaseSharedPipelineCache() {
24
24
  }
25
25
  function buildSpriteRenderable(engine, layer) {
26
26
  if (layer.depth === "none") {
27
- throw new Error('Sprite2DLayer with depth: "none" must be rendered via createSpriteRenderer, not addDepthHostedSpriteLayer.');
27
+ throw new Error('Depth-hosted sprites require depth != "none".');
28
28
  }
29
29
  const indexBuffer = createMappedBuffer(engine, SHARED_SPRITE_INDEX_DATA, BU.INDEX);
30
- const uniformBuffer = createEmptyUniformBuffer(engine, LAYER_UBO_BYTES, "sprite-depth-hosted-ubo");
30
+ const uniformBuffer = createEmptyUniformBuffer(engine, LAYER_UBO_BYTES);
31
31
  const cap = layer._capacity;
32
- const instanceBuffer = createSpriteInstanceBuffer(engine._device, layer, "sprite-depth-hosted-instances");
33
- const fx = _getSpriteFxHook()?.createLayerFx(engine, "sprite-depth-hosted-fx-ubo", layer) ?? null;
32
+ const instanceBuffer = createSpriteInstanceBuffer(engine._device, layer);
33
+ const fx = _getSpriteFxHook()?.createLayerFx(engine, "", layer) ?? null;
34
34
  const isTransparent = layer.depth === "test";
35
35
  const isDirect = layer.depth === "test-write";
36
36
  const renderable = {
@@ -108,7 +108,7 @@ function uploadLayer(r, target) {
108
108
  if (r._fx) {
109
109
  _getSpriteFxHook().updateFx(r._fx, r._layer, r._engine._currentDelta);
110
110
  }
111
- const grown = ensureSpriteInstanceBuffer(r._engine._device, r._layer, r._instanceBuffer, r._instanceBufferCapacity, "sprite-depth-hosted-instances");
111
+ const grown = ensureSpriteInstanceBuffer(r._engine._device, r._layer, r._instanceBuffer, r._instanceBufferCapacity);
112
112
  if (grown.reallocated) {
113
113
  r._instanceBuffer = grown.buffer;
114
114
  r._instanceBufferCapacity = grown.capacity;
@@ -1 +1 @@
1
- {"version":3,"file":"sprite-renderable.js","sources":["../../../src/sprite/sprite-renderable.ts"],"sourcesContent":["/**\n * `sprite-renderable.ts` — wraps a single depth-hosted `Sprite2DLayer`\n * (`depth: \"test\"` or `\"test-write\"`) as a scene `Renderable`. Drawn inside\n * the scene's main 3D pass alongside meshes, so it participates in the\n * engine's depth attachment and gets occluded by (or occludes) regular\n * geometry based on its `layerZ`.\n *\n * Reached through `addDepthHostedSpriteLayer` when a depth-hosted `Sprite2DLayer`\n * is added to a scene. Pure-2D scenes and mesh-only scenes pay zero runtime bytes\n * for this module when the depth-hosted sprite API is not imported.\n *\n * Per-layer GPU work (instance / UBO upload, capacity grow, change-detect)\n * is shared with `sprite-renderer.ts` via helpers in `sprite-pipeline.ts`.\n * Each renderable still owns its own GPU resources (one layer per renderable\n * vs. the renderer's many-layer Map) — only the per-frame sync logic is\n * shared.\n */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport { BU } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { RenderTargetSignature } from \"../engine/render-target.js\";\nimport type { DrawBinding, DrawUpdateContext, Renderable } from \"../render/renderable.js\";\nimport { getSceneBindGroupLayout } from \"../render/scene-helpers.js\";\nimport { createEmptyUniformBuffer, createMappedBuffer } from \"../resource/gpu-buffers.js\";\nimport type { SpriteLayerFx } from \"./custom-shader-core.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport type { Sprite2DLayer } from \"./sprite-2d.js\";\nimport {\n LAYER_UBO_BYTES,\n SHARED_SPRITE_INDEX_DATA,\n buildSpriteLayerUbo,\n createSpriteInstanceBuffer,\n createSpriteLayerBindGroup,\n createSpritePipelineCache,\n ensureSpriteInstanceBuffer,\n getOrCreateSpritePipeline,\n resetSpritePipelineCache,\n uploadSpriteInstances,\n writeSpriteLayerUboIfDirty,\n} from \"./sprite-pipeline.js\";\nimport type { SpritePipelineCache } from \"./sprite-pipeline.js\";\n\n// Shared sprite pipeline cache across every depth-hosted Sprite2DLayer renderable\n// in the process. Lazy-init on first acquire (per GUIDANCE §4 — module-level\n// `null` initializer + `getCache()`-style helper, never a top-level `new Map()`).\n// Refcounted so the cache (and its compiled GPUShaderModule + pipelines) is\n// released exactly when the last depth-hosted renderable is disposed; device\n// changes are handled inside `getOrCreateSpritePipeline` via its per-device cache.\n// The SpriteRenderer (HUD) path uses its own per-renderer cache.\nlet _sharedPipelineCache: SpritePipelineCache | null = null;\nlet _sharedPipelineCacheRefs = 0;\n\nfunction acquireSharedPipelineCache(): SpritePipelineCache {\n _sharedPipelineCache ??= createSpritePipelineCache();\n _sharedPipelineCacheRefs++;\n return _sharedPipelineCache;\n}\n\nfunction releaseSharedPipelineCache(): void {\n if (_sharedPipelineCacheRefs === 0) {\n return;\n }\n _sharedPipelineCacheRefs--;\n if (_sharedPipelineCacheRefs === 0 && _sharedPipelineCache) {\n resetSpritePipelineCache(_sharedPipelineCache);\n _sharedPipelineCache = null;\n }\n}\n\ninterface SpriteRenderableInternal extends Renderable {\n _engine: EngineContext;\n _layer: Sprite2DLayer;\n _indexBuffer: GPUBuffer;\n _uniformBuffer: GPUBuffer;\n _instanceBuffer: GPUBuffer;\n _instanceBufferCapacity: number;\n _pipelineCache: SpritePipelineCache;\n _bindGroups: Map<GPURenderPipeline, GPUBindGroup>;\n _uploadedVersion: number;\n _uboUploaded: boolean;\n _lastUbo: Float32Array;\n _scratchUbo: Float32Array;\n _fx: SpriteLayerFx | null;\n _disposed: boolean;\n}\n\n/**\n * Build a `Renderable` for a depth-hosted `Sprite2DLayer`. Returns the\n * renderable plus a `dispose` callback that destroys all per-layer GPU\n * resources and clears the pipeline cache.\n *\n * Throws if `layer.depth === \"none\"` — pure-2D HUD layers must be rendered\n * via `createSpriteRenderer + registerSpriteRenderer`, not as a scene\n * `Renderable`. The check lives here as a second line of defense; the public\n * opt-in scene add function also rejects pure HUD layers before registration.\n *\n * Caller is responsible for\n * pushing `renderable` into `_renderables` and `dispose` into `_disposables`.\n */\nexport function buildSpriteRenderable(engine: EngineContext, layer: Sprite2DLayer): { renderable: Renderable; dispose: () => void } {\n if (layer.depth === \"none\") {\n throw new Error('Sprite2DLayer with depth: \"none\" must be rendered via createSpriteRenderer, not addDepthHostedSpriteLayer.');\n }\n const indexBuffer = createMappedBuffer(engine, SHARED_SPRITE_INDEX_DATA, BU.INDEX);\n const uniformBuffer = createEmptyUniformBuffer(engine, LAYER_UBO_BYTES, \"sprite-depth-hosted-ubo\");\n const cap = layer._capacity;\n const instanceBuffer = createSpriteInstanceBuffer(engine._device, layer, \"sprite-depth-hosted-instances\");\n const fx = _getSpriteFxHook()?.createLayerFx(engine, \"sprite-depth-hosted-fx-ubo\", layer) ?? null;\n\n const isTransparent = layer.depth === \"test\";\n const isDirect = layer.depth === \"test-write\";\n const renderable: SpriteRenderableInternal = {\n // Depth-write sprite layers are mutable instanced batches, so route them through\n // the direct-draw phase after cached opaque meshes and before transparent draws.\n order: isTransparent ? 200 : 100,\n isTransparent,\n _direct: isDirect,\n _engine: engine,\n _layer: layer,\n _indexBuffer: indexBuffer,\n _uniformBuffer: uniformBuffer,\n _instanceBuffer: instanceBuffer,\n _instanceBufferCapacity: cap,\n _pipelineCache: acquireSharedPipelineCache(),\n _bindGroups: new Map(),\n _uploadedVersion: -1,\n _uboUploaded: false,\n _lastUbo: new F32(LAYER_UBO_BYTES / 4),\n _scratchUbo: new F32(LAYER_UBO_BYTES / 4),\n _fx: fx,\n _disposed: false,\n bind(engine, target) {\n return bindLayer(renderable, engine, target);\n },\n };\n\n return {\n renderable,\n dispose() {\n disposeRenderable(renderable);\n },\n };\n}\n\n/** Resolve this sprite layer against a render-pass target and return the per-frame draw binding. */\nfunction bindLayer(r: SpriteRenderableInternal, engine: EngineContext, target: RenderTargetSignature): DrawBinding {\n if (!target._depthStencilFormat) {\n throw new Error(\"Depth-hosted Sprite2DLayer requires a depth-stencil render target.\");\n }\n const sampleCount = target._sampleCount === 1 ? 1 : 4;\n const depthWrite = r._layer.depth === \"test-write\";\n const pipeline = getOrCreateSpritePipeline(\n engine,\n r._pipelineCache,\n target._colorFormat!,\n sampleCount,\n r._layer.blendMode,\n true,\n depthWrite,\n target._depthStencilFormat,\n getSceneBindGroupLayout(engine),\n r._layer\n );\n let bindGroup = r._bindGroups.get(pipeline);\n if (!bindGroup) {\n bindGroup = createSpriteLayerBindGroup(engine, pipeline, 1, r._layer, r._uniformBuffer, r._fx);\n r._bindGroups.set(pipeline, bindGroup);\n }\n return {\n renderable: r,\n pipeline,\n update(context) {\n uploadLayer(r, context);\n },\n draw(pass) {\n return drawLayer(r, bindGroup, pass);\n },\n };\n}\n\n/** Sync per-instance vertex data and the per-layer UBO via the shared pipeline helpers. */\nfunction uploadLayer(r: SpriteRenderableInternal, target: DrawUpdateContext): void {\n if (r._disposed) {\n return;\n }\n // Match the pure-2D `SpriteRenderer` path: skip invisible / empty layers entirely so `fx.time`\n // (and the FX UBO write) stays consistent across both paths and we avoid wasted `writeBuffer` traffic.\n if (!r._layer.visible || r._layer.count === 0) {\n return;\n }\n if (r._fx) {\n _getSpriteFxHook()!.updateFx(r._fx, r._layer, r._engine._currentDelta);\n }\n const grown = ensureSpriteInstanceBuffer(r._engine._device, r._layer, r._instanceBuffer, r._instanceBufferCapacity, \"sprite-depth-hosted-instances\");\n if (grown.reallocated) {\n r._instanceBuffer = grown.buffer;\n r._instanceBufferCapacity = grown.capacity;\n r._uploadedVersion = -1;\n }\n r._uploadedVersion = uploadSpriteInstances(r._engine._device, r._layer, r._instanceBuffer, r._uploadedVersion);\n buildSpriteLayerUbo(r._layer, target.targetWidth, target.targetHeight, r._scratchUbo);\n r._uboUploaded = writeSpriteLayerUboIfDirty(r._engine._device, r._uniformBuffer, r._scratchUbo, r._lastUbo, r._uboUploaded);\n}\n\n/** Issue the indexed instanced draw for this depth-hosted sprite layer. */\nfunction drawLayer(r: SpriteRenderableInternal, bindGroup: GPUBindGroup, pass: GPURenderPassEncoder | GPURenderBundleEncoder): number {\n if (r._disposed || !r._layer.visible || r._layer.count === 0) {\n return 0;\n }\n pass.setBindGroup(1, bindGroup);\n pass.setIndexBuffer(r._indexBuffer, \"uint16\");\n pass.setVertexBuffer(0, r._instanceBuffer);\n pass.drawIndexed(6, r._layer.count, 0, 0, 0);\n return 1;\n}\n\nfunction disposeRenderable(r: SpriteRenderableInternal): void {\n if (r._disposed) {\n return;\n }\n r._disposed = true;\n r._instanceBuffer.destroy();\n r._uniformBuffer.destroy();\n r._indexBuffer.destroy();\n if (r._fx) {\n _getSpriteFxHook()!.disposeFx(r._fx);\n }\n r._bindGroups.clear();\n // Drop the layer back-reference so a disposed renderable doesn't keep the\n // user's Sprite2DLayer (and its CPU instance/savedSize buffers) alive.\n // Cast through unknown — the field is non-null in the live path; only\n // disposed renderables (no longer touched by render code) ever see null.\n (r as unknown as { _layer: Sprite2DLayer | null })._layer = null;\n releaseSharedPipelineCache();\n}\n"],"names":["engine"],"mappings":"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"}
1
+ {"version":3,"file":"sprite-renderable.js","sources":["../../../src/sprite/sprite-renderable.ts"],"sourcesContent":["/**\n * `sprite-renderable.ts` — wraps a single depth-hosted `Sprite2DLayer`\n * (`depth: \"test\"` or `\"test-write\"`) as a scene `Renderable`. Drawn inside\n * the scene's main 3D pass alongside meshes, so it participates in the\n * engine's depth attachment and gets occluded by (or occludes) regular\n * geometry based on its `layerZ`.\n *\n * Reached through `addDepthHostedSpriteLayer` when a depth-hosted `Sprite2DLayer`\n * is added to a scene. Pure-2D scenes and mesh-only scenes pay zero runtime bytes\n * for this module when the depth-hosted sprite API is not imported.\n *\n * Per-layer GPU work (instance / UBO upload, capacity grow, change-detect)\n * is shared with `sprite-renderer.ts` via helpers in `sprite-pipeline.ts`.\n * Each renderable still owns its own GPU resources (one layer per renderable\n * vs. the renderer's many-layer Map) — only the per-frame sync logic is\n * shared.\n */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport { BU } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { RenderTargetSignature } from \"../engine/render-target.js\";\nimport type { DrawBinding, DrawUpdateContext, Renderable } from \"../render/renderable.js\";\nimport { getSceneBindGroupLayout } from \"../render/scene-helpers.js\";\nimport { createEmptyUniformBuffer, createMappedBuffer } from \"../resource/gpu-buffers.js\";\nimport type { SpriteLayerFx } from \"./custom-shader-core.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport type { Sprite2DLayer } from \"./sprite-2d.js\";\nimport {\n LAYER_UBO_BYTES,\n SHARED_SPRITE_INDEX_DATA,\n buildSpriteLayerUbo,\n createSpriteInstanceBuffer,\n createSpriteLayerBindGroup,\n createSpritePipelineCache,\n ensureSpriteInstanceBuffer,\n getOrCreateSpritePipeline,\n resetSpritePipelineCache,\n uploadSpriteInstances,\n writeSpriteLayerUboIfDirty,\n} from \"./sprite-pipeline.js\";\nimport type { SpritePipelineCache } from \"./sprite-pipeline.js\";\n\n// Shared sprite pipeline cache across every depth-hosted Sprite2DLayer renderable\n// in the process. Lazy-init on first acquire (per GUIDANCE §4 — module-level\n// `null` initializer + `getCache()`-style helper, never a top-level `new Map()`).\n// Refcounted so the cache (and its compiled GPUShaderModule + pipelines) is\n// released exactly when the last depth-hosted renderable is disposed; device\n// changes are handled inside `getOrCreateSpritePipeline` via its per-device cache.\n// The SpriteRenderer (HUD) path uses its own per-renderer cache.\nlet _sharedPipelineCache: SpritePipelineCache | null = null;\nlet _sharedPipelineCacheRefs = 0;\n\nfunction acquireSharedPipelineCache(): SpritePipelineCache {\n _sharedPipelineCache ??= createSpritePipelineCache();\n _sharedPipelineCacheRefs++;\n return _sharedPipelineCache;\n}\n\nfunction releaseSharedPipelineCache(): void {\n if (_sharedPipelineCacheRefs === 0) {\n return;\n }\n _sharedPipelineCacheRefs--;\n if (_sharedPipelineCacheRefs === 0 && _sharedPipelineCache) {\n resetSpritePipelineCache(_sharedPipelineCache);\n _sharedPipelineCache = null;\n }\n}\n\ninterface SpriteRenderableInternal extends Renderable {\n _engine: EngineContext;\n _layer: Sprite2DLayer;\n _indexBuffer: GPUBuffer;\n _uniformBuffer: GPUBuffer;\n _instanceBuffer: GPUBuffer;\n _instanceBufferCapacity: number;\n _pipelineCache: SpritePipelineCache;\n _bindGroups: Map<GPURenderPipeline, GPUBindGroup>;\n _uploadedVersion: number;\n _uboUploaded: boolean;\n _lastUbo: Float32Array;\n _scratchUbo: Float32Array;\n _fx: SpriteLayerFx | null;\n _disposed: boolean;\n}\n\n/**\n * Build a `Renderable` for a depth-hosted `Sprite2DLayer`. Returns the\n * renderable plus a `dispose` callback that destroys all per-layer GPU\n * resources and clears the pipeline cache.\n *\n * Throws if `layer.depth === \"none\"` — pure-2D HUD layers must be rendered\n * via `createSpriteRenderer + registerSpriteRenderer`, not as a scene\n * `Renderable`. The check lives here as a second line of defense; the public\n * opt-in scene add function also rejects pure HUD layers before registration.\n *\n * Caller is responsible for\n * pushing `renderable` into `_renderables` and `dispose` into `_disposables`.\n */\nexport function buildSpriteRenderable(engine: EngineContext, layer: Sprite2DLayer): { renderable: Renderable; dispose: () => void } {\n if (layer.depth === \"none\") {\n throw new Error('Depth-hosted sprites require depth != \"none\".');\n }\n const indexBuffer = createMappedBuffer(engine, SHARED_SPRITE_INDEX_DATA, BU.INDEX);\n const uniformBuffer = createEmptyUniformBuffer(engine, LAYER_UBO_BYTES);\n const cap = layer._capacity;\n const instanceBuffer = createSpriteInstanceBuffer(engine._device, layer);\n const fx = _getSpriteFxHook()?.createLayerFx(engine, \"\", layer) ?? null;\n\n const isTransparent = layer.depth === \"test\";\n const isDirect = layer.depth === \"test-write\";\n const renderable: SpriteRenderableInternal = {\n // Depth-write sprite layers are mutable instanced batches, so route them through\n // the direct-draw phase after cached opaque meshes and before transparent draws.\n order: isTransparent ? 200 : 100,\n isTransparent,\n _direct: isDirect,\n _engine: engine,\n _layer: layer,\n _indexBuffer: indexBuffer,\n _uniformBuffer: uniformBuffer,\n _instanceBuffer: instanceBuffer,\n _instanceBufferCapacity: cap,\n _pipelineCache: acquireSharedPipelineCache(),\n _bindGroups: new Map(),\n _uploadedVersion: -1,\n _uboUploaded: false,\n _lastUbo: new F32(LAYER_UBO_BYTES / 4),\n _scratchUbo: new F32(LAYER_UBO_BYTES / 4),\n _fx: fx,\n _disposed: false,\n bind(engine, target) {\n return bindLayer(renderable, engine, target);\n },\n };\n\n return {\n renderable,\n dispose() {\n disposeRenderable(renderable);\n },\n };\n}\n\n/** Resolve this sprite layer against a render-pass target and return the per-frame draw binding. */\nfunction bindLayer(r: SpriteRenderableInternal, engine: EngineContext, target: RenderTargetSignature): DrawBinding {\n if (!target._depthStencilFormat) {\n throw new Error(\"Depth-hosted Sprite2DLayer requires a depth-stencil render target.\");\n }\n const sampleCount = target._sampleCount === 1 ? 1 : 4;\n const depthWrite = r._layer.depth === \"test-write\";\n const pipeline = getOrCreateSpritePipeline(\n engine,\n r._pipelineCache,\n target._colorFormat!,\n sampleCount,\n r._layer.blendMode,\n true,\n depthWrite,\n target._depthStencilFormat,\n getSceneBindGroupLayout(engine),\n r._layer\n );\n let bindGroup = r._bindGroups.get(pipeline);\n if (!bindGroup) {\n bindGroup = createSpriteLayerBindGroup(engine, pipeline, 1, r._layer, r._uniformBuffer, r._fx);\n r._bindGroups.set(pipeline, bindGroup);\n }\n return {\n renderable: r,\n pipeline,\n update(context) {\n uploadLayer(r, context);\n },\n draw(pass) {\n return drawLayer(r, bindGroup, pass);\n },\n };\n}\n\n/** Sync per-instance vertex data and the per-layer UBO via the shared pipeline helpers. */\nfunction uploadLayer(r: SpriteRenderableInternal, target: DrawUpdateContext): void {\n if (r._disposed) {\n return;\n }\n // Match the pure-2D `SpriteRenderer` path: skip invisible / empty layers entirely so `fx.time`\n // (and the FX UBO write) stays consistent across both paths and we avoid wasted `writeBuffer` traffic.\n if (!r._layer.visible || r._layer.count === 0) {\n return;\n }\n if (r._fx) {\n _getSpriteFxHook()!.updateFx(r._fx, r._layer, r._engine._currentDelta);\n }\n const grown = ensureSpriteInstanceBuffer(r._engine._device, r._layer, r._instanceBuffer, r._instanceBufferCapacity);\n if (grown.reallocated) {\n r._instanceBuffer = grown.buffer;\n r._instanceBufferCapacity = grown.capacity;\n r._uploadedVersion = -1;\n }\n r._uploadedVersion = uploadSpriteInstances(r._engine._device, r._layer, r._instanceBuffer, r._uploadedVersion);\n buildSpriteLayerUbo(r._layer, target.targetWidth, target.targetHeight, r._scratchUbo);\n r._uboUploaded = writeSpriteLayerUboIfDirty(r._engine._device, r._uniformBuffer, r._scratchUbo, r._lastUbo, r._uboUploaded);\n}\n\n/** Issue the indexed instanced draw for this depth-hosted sprite layer. */\nfunction drawLayer(r: SpriteRenderableInternal, bindGroup: GPUBindGroup, pass: GPURenderPassEncoder | GPURenderBundleEncoder): number {\n if (r._disposed || !r._layer.visible || r._layer.count === 0) {\n return 0;\n }\n pass.setBindGroup(1, bindGroup);\n pass.setIndexBuffer(r._indexBuffer, \"uint16\");\n pass.setVertexBuffer(0, r._instanceBuffer);\n pass.drawIndexed(6, r._layer.count, 0, 0, 0);\n return 1;\n}\n\nfunction disposeRenderable(r: SpriteRenderableInternal): void {\n if (r._disposed) {\n return;\n }\n r._disposed = true;\n r._instanceBuffer.destroy();\n r._uniformBuffer.destroy();\n r._indexBuffer.destroy();\n if (r._fx) {\n _getSpriteFxHook()!.disposeFx(r._fx);\n }\n r._bindGroups.clear();\n // Drop the layer back-reference so a disposed renderable doesn't keep the\n // user's Sprite2DLayer (and its CPU instance/savedSize buffers) alive.\n // Cast through unknown — the field is non-null in the live path; only\n // disposed renderables (no longer touched by render code) ever see null.\n (r as unknown as { _layer: Sprite2DLayer | null })._layer = null;\n releaseSharedPipelineCache();\n}\n"],"names":["engine"],"mappings":"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"}
@@ -10,8 +10,8 @@ function ensureLayerGpu(rr, layer) {
10
10
  let lg = rr._layerGpu.get(layer);
11
11
  if (!lg) {
12
12
  const cap = layer._capacity;
13
- const instanceBuffer = createSpriteInstanceBuffer(rr._surface.engine._device, layer, "sprite-layer-instances");
14
- const uniformBuffer = createEmptyUniformBuffer(rr._surface.engine, LAYER_UBO_BYTES, "sprite-layer-ubo");
13
+ const instanceBuffer = createSpriteInstanceBuffer(rr._surface.engine._device, layer);
14
+ const uniformBuffer = createEmptyUniformBuffer(rr._surface.engine, LAYER_UBO_BYTES);
15
15
  const fx = _getSpriteFxHook()?.createLayerFx(rr._surface.engine, "sprite-layer-fx-ubo", layer) ?? null;
16
16
  lg = {
17
17
  layer,
@@ -29,7 +29,7 @@ function ensureLayerGpu(rr, layer) {
29
29
  };
30
30
  rr._layerGpu.set(layer, lg);
31
31
  }
32
- const grown = ensureSpriteInstanceBuffer(rr._surface.engine._device, layer, lg.instanceBuffer, lg.instanceBufferCapacity, "sprite-layer-instances");
32
+ const grown = ensureSpriteInstanceBuffer(rr._surface.engine._device, layer, lg.instanceBuffer, lg.instanceBufferCapacity);
33
33
  if (grown.reallocated) {
34
34
  lg.instanceBuffer = grown.buffer;
35
35
  lg.instanceBufferCapacity = grown.capacity;
@@ -111,7 +111,7 @@ function assertSpriteRendererLayers(layers) {
111
111
  }
112
112
  function assertSpriteRendererLayer(layer) {
113
113
  if (layer.depth !== "none") {
114
- throw new Error('SpriteRenderer only supports Sprite2DLayer with depth: "none". Use addDepthHostedSpriteLayer(scene, layer) for depth-hosted sprites.');
114
+ throw new Error('SpriteRenderer requires depth: "none".');
115
115
  }
116
116
  }
117
117
  function spriteRendererUpdate(rr) {