@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 +1 @@
1
- {"version":3,"file":"sprite-renderer.js","sources":["../../../src/sprite/sprite-renderer.ts"],"sourcesContent":["/**\n * `SpriteRenderer` — owns the shared index buffer and per-layer GPU state\n * required to draw `Sprite2DLayer`s. Implements\n * `RenderingContext` directly, so it plugs into `engine._renderingContexts`\n * the same way a `SceneContext` does.\n *\n * Scope:\n * - Pure-2D / HUD path only — all layers must use `depth: \"none\"`.\n * - One sprite-pipeline cache per renderer instance, keyed by format,\n * blend mode, and sample count. The direct HUD path always uses\n * `sampleCount=1` and `hasDepth=false`.\n * - The renderer opens a sprite-only swapchain pass using the engine's\n * current command encoder and swapchain view. Off-screen / HUD-to-texture\n * rendering is deferred until there is a concrete caller.\n */\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport { BU } from \"../engine/gpu-flags.js\";\nimport { registerRenderingContext, unregisterRenderingContext, getRenderTargetSize } from \"../engine/engine.js\";\nimport type { RenderingContext } from \"../engine/engine.js\";\nimport type { SurfaceContext } from \"../engine/surface.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 type { Texture2D } from \"../texture/texture-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 getSpritePipelineCacheSize,\n resetSpritePipelineCache,\n uploadSpriteInstances,\n writeSpriteLayerUboIfDirty,\n} from \"./sprite-pipeline.js\";\nimport type { SpritePipelineCache } from \"./sprite-pipeline.js\";\n\n/** Tag used by the engine and by tests to identify a sprite renderer. */\nconst KIND = \"sprite-renderer\" as const;\n\n/** Options accepted by `createSpriteRenderer`. */\nexport interface SpriteRendererOptions {\n /** Layers to draw, in registration order. The renderer also re-sorts internally each frame. */\n layers: readonly Sprite2DLayer[];\n /** Default true. Set false for HUD overlays so the sprite pass preserves existing scene color. */\n clear?: boolean;\n /** Default `{ r: 0, g: 0, b: 0, a: 1 }`. */\n clearValue?: GPUColorDict;\n}\n\n/**\n * A `SpriteRenderer` — pure data, plugs into `engine._renderingContexts`.\n * Inherits `clearColor`, `_drawCallsPre`, `_update`, `_record` from `RenderingContext`;\n * adds only its discriminator tag and the renderer-owned layer list.\n *\n * **Lifecycle.** A `SpriteRenderer` is independent of any `SceneContext` — it is\n * registered directly on the engine, opens its own sampleCount=1 swapchain pass, and\n * must be disposed by the caller. Two patterns:\n *\n * 1. **Standalone** (no scene, or one or more renderers alongside a scene):\n * `registerSpriteRenderer` after `createSpriteRenderer`, `disposeSpriteRenderer`\n * on shutdown. Caller owns the lifetime end-to-end.\n * 2. **HUD-on-3D** (renderer overlaid on a scene): same `register` step,\n * then tie disposal to the scene with\n * `onSceneDispose(scene, () => disposeSpriteRenderer(hud))` —\n * `disposeScene` then cleans it up automatically. Register the renderer\n * *after* `registerScene` so it draws on top.\n *\n * Scene 52 demonstrates pattern (2). For depth-hosted sprites that should\n * sort against 3D meshes, use `addDepthHostedSpriteLayer(scene, layer)` with\n * a depth-enabled `Sprite2DLayer` instead — that route is fully owned by the scene.\n */\nexport interface SpriteRenderer extends RenderingContext {\n /** @internal */\n readonly _kind: typeof KIND;\n /** Renderer-owned layer membership. Use `addSpriteRendererLayer` / `removeSpriteRendererLayer` to mutate. */\n readonly layers: readonly Sprite2DLayer[];\n /** @internal Mutable alias of {@link layers} — same array, used by internal helpers. */\n _layers: Sprite2DLayer[];\n /** @internal Surface this renderer draws into. */\n _surface: SurfaceContext;\n /** @internal */\n _indexBuffer: GPUBuffer;\n /** @internal */\n _pipelineCache: SpritePipelineCache;\n /** @internal */\n _layerGpu: Map<Sprite2DLayer, LayerGpu>;\n /** @internal Hooks run at the start of `_update`, before layer uploads. */\n _beforeUpdate: ((deltaMs: number) => void)[];\n /** @internal Cleanup callbacks run by `disposeSpriteRenderer`; optional integrations register here. */\n _disposeCallbacks: (() => void)[];\n /** @internal */\n _visibleBundles: GPURenderBundle[];\n /** @internal Captured each `_update`, read in `_record`. */\n _targetWidth: number;\n /** @internal */\n _targetHeight: number;\n /** @internal */\n _disposed: boolean;\n /** @internal Whether this pass clears the swapchain before drawing. False for HUD overlays. */\n _clear: boolean;\n /** @internal Offscreen color-attachment view to render into; null = the swapchain.\n * Set via {@link setSpriteRendererTarget} (which takes a {@link Texture2D} target)\n * for render-to-texture / post-process. */\n _targetView: GPUTextureView | null;\n}\n\n/** @internal Per-layer GPU resources owned by the renderer. */\ninterface LayerGpu {\n layer: Sprite2DLayer;\n instanceBuffer: GPUBuffer;\n instanceBufferCapacity: number;\n uniformBuffer: GPUBuffer;\n /** Built once per layer; the bind group binds the uniform buffer + atlas texture/sampler,\n * none of which change after construction (atlas is `readonly` on the layer; uniform\n * buffer is allocated once in `ensureLayerGpu`). Cleared if we ever recreate either. */\n bindGroup: GPUBindGroup | null;\n uploadedVersion: number;\n /** Opaque fx attachment (`SpriteFx` UBO, scratch, elapsed time); non-null only for `customShader` layers. */\n fx: SpriteLayerFx | null;\n /** Cached pipeline object. Refreshed when target-defining GPU state resolves to a different pipeline. */\n pipeline: GPURenderPipeline | null;\n /** Snapshot of the last UBO bytes written to `uniformBuffer`. We rebuild the UBO into\n * `_scratchUbo` each frame, then `writeBuffer` only if the contents actually changed.\n * For static scenes (steady-state) this skips one `queue.writeBuffer` per layer per frame. */\n lastUbo: Float32Array;\n /** False until the first UBO upload. Forces an unconditional first write so `lastUbo` is real. */\n uboUploaded: boolean;\n /** Pre-recorded GPU command bundle: `setIndexBuffer` + `setPipeline` + `setBindGroup` +\n * `setVertexBuffer` + `drawIndexed`. Collected into a reused bundle array for\n * near-zero per-frame CPU command-recording cost (the big WebGPU win for static scenes —\n * see `scene-core.ts._record` for the same pattern). Invalidated when `layer.count` changes\n * (the `drawIndexed` instance count is baked into the bundle) or when the instance buffer is\n * reallocated by `ensureLayerGpu` (the bundle holds a GPUBuffer reference). The UBO contents\n * may freely change frame-to-frame — the bundle binds the buffer *object*, not its bytes. */\n renderBundle: GPURenderBundle | null;\n /** `layer.count` value the cached `renderBundle` was recorded against. */\n bundleCount: number;\n}\n\n/**\n * Lazy GPU-resource provisioner for one layer. On first sight: allocates the per-instance\n * vertex buffer + the 48 B layer UBO and stashes a `LayerGpu` record in `_layerGpu`. On\n * subsequent calls where the layer's CPU `_capacity` outgrew the GPU buffer (after\n * `growCapacity` doubled the array): destroys + reallocates the instance buffer at the\n * new size and forces a full re-upload via `uploadedVersion = -1`. The bind group is\n * left intact — it doesn't reference the instance buffer (vertex buffers are bound\n * separately at draw time), only the uniform buffer + atlas, neither of which moves.\n */\nfunction ensureLayerGpu(rr: SpriteRenderer, layer: Sprite2DLayer): LayerGpu {\n let lg = rr._layerGpu.get(layer);\n if (!lg) {\n const cap = layer._capacity;\n const instanceBuffer = createSpriteInstanceBuffer(rr._surface.engine._device, layer, \"sprite-layer-instances\");\n const uniformBuffer = createEmptyUniformBuffer(rr._surface.engine, LAYER_UBO_BYTES, \"sprite-layer-ubo\");\n const fx = _getSpriteFxHook()?.createLayerFx(rr._surface.engine, \"sprite-layer-fx-ubo\", layer) ?? null;\n lg = {\n layer,\n instanceBuffer,\n instanceBufferCapacity: cap,\n uniformBuffer,\n bindGroup: null,\n uploadedVersion: -1,\n fx,\n pipeline: null,\n lastUbo: new F32(LAYER_UBO_BYTES / 4),\n uboUploaded: false,\n renderBundle: null,\n bundleCount: -1,\n };\n rr._layerGpu.set(layer, lg);\n }\n const grown = ensureSpriteInstanceBuffer(rr._surface.engine._device, layer, lg.instanceBuffer, lg.instanceBufferCapacity, \"sprite-layer-instances\");\n if (grown.reallocated) {\n lg.instanceBuffer = grown.buffer;\n lg.instanceBufferCapacity = grown.capacity;\n lg.uploadedVersion = -1;\n // Bundle baked a reference to the *old* GPUBuffer; the new buffer needs a re-record.\n lg.renderBundle = null;\n }\n return lg;\n}\n\n/** Sync one layer's GPU state to its CPU state — instance vertex data + per-layer UBO.\n * Both helpers are version-/dirty-gated and skip work in the steady state. */\nfunction uploadLayer(rr: SpriteRenderer, lg: LayerGpu, deltaMs: number): void {\n const layer = lg.layer;\n lg.uploadedVersion = uploadSpriteInstances(rr._surface.engine._device, layer, lg.instanceBuffer, lg.uploadedVersion);\n buildSpriteLayerUbo(layer, rr._targetWidth, rr._targetHeight, _scratchUbo);\n lg.uboUploaded = writeSpriteLayerUboIfDirty(rr._surface.engine._device, lg.uniformBuffer, _scratchUbo, lg.lastUbo, lg.uboUploaded);\n if (lg.fx) {\n _getSpriteFxHook()!.updateFx(lg.fx, layer, deltaMs);\n }\n}\n\nfunction disposeLayerGpu(lg: LayerGpu): void {\n lg.instanceBuffer.destroy();\n lg.uniformBuffer.destroy();\n if (lg.fx) {\n _getSpriteFxHook()!.disposeFx(lg.fx);\n }\n}\n\nconst _scratchUbo = new F32(LAYER_UBO_BYTES / 4);\n\n/**\n * Build (and cache) the bind group that attaches `lg.uniformBuffer` + atlas texture +\n * sampler to the pipeline's sprite `@group(0)` schema. All three resources are immutable for\n * the layer's lifetime, so this runs at most once per layer; subsequent calls return\n * the cached group. The instance buffer is **not** in the bind group — it's a vertex\n * buffer, bound separately at draw time — which is why instance-buffer growth in\n * `ensureLayerGpu` doesn't invalidate this cache.\n */\nfunction ensureBindGroup(rr: SpriteRenderer, lg: LayerGpu, pipeline: GPURenderPipeline): GPUBindGroup {\n if (lg.bindGroup) {\n return lg.bindGroup;\n }\n lg.bindGroup = createSpriteLayerBindGroup(rr._surface.engine, pipeline, 0, lg.layer, lg.uniformBuffer, lg.fx);\n return lg.bindGroup;\n}\n\n/** Sort key for layers within a renderer: ascending `order` (back-to-front draw order). */\nfunction compareLayers(a: Sprite2DLayer, b: Sprite2DLayer): number {\n if (a.order !== b.order) {\n return a.order - b.order;\n }\n return 0;\n}\n\n/** Create a `SpriteRenderer` for `surface`, pre-warming pipelines for the layers' blend\n * modes. Pass the engine directly for the common single-canvas case (since\n * `EngineContext extends SurfaceContext`); pass an auxiliary surface created via\n * `createSurface` for multi-canvas. */\nexport function createSpriteRenderer(surface: SurfaceContext, opts: SpriteRendererOptions): SpriteRenderer {\n const engine = surface.engine;\n assertSpriteRendererLayers(opts.layers);\n const indexBuffer = createMappedBuffer(engine, SHARED_SPRITE_INDEX_DATA, BU.INDEX);\n const canvas = surface.canvas;\n\n const layers = opts.layers.slice();\n const rr: SpriteRenderer = {\n _kind: KIND,\n _surface: surface,\n _indexBuffer: indexBuffer,\n _pipelineCache: createSpritePipelineCache(),\n _layerGpu: new Map(),\n _visibleBundles: [],\n _targetWidth: canvas.width,\n _targetHeight: canvas.height,\n _disposed: false,\n _clear: opts.clear ?? true,\n _targetView: null,\n _beforeUpdate: [],\n _disposeCallbacks: [],\n layers,\n _layers: layers,\n clearColor: opts.clearValue ?? { r: 0, g: 0, b: 0, a: 1 },\n _drawCallsPre: 0,\n _update(): void {\n spriteRendererUpdate(rr);\n },\n _record(): number {\n return spriteRendererRecord(rr);\n },\n };\n\n // Pre-warm pipelines currently in use, so the first frame doesn't pay compile cost.\n for (const layer of rr.layers) {\n getOrCreateSpritePipeline(engine, rr._pipelineCache, surface.format, 1, layer.blendMode, false, false, undefined, undefined, layer);\n }\n\n return rr;\n}\n\nfunction assertSpriteRendererLayers(layers: readonly Sprite2DLayer[]): void {\n for (const layer of layers) {\n assertSpriteRendererLayer(layer);\n }\n}\n\nfunction assertSpriteRendererLayer(layer: Sprite2DLayer): void {\n if (layer.depth !== \"none\") {\n throw new Error('SpriteRenderer only supports Sprite2DLayer with depth: \"none\". Use addDepthHostedSpriteLayer(scene, layer) for depth-hosted sprites.');\n }\n}\n\n/**\n * Per-frame **update** pass (called by the engine before this renderer records its pass).\n * Refreshes target dims (canvas may have resized), sorts `rr.layers` in place by\n * `order` (TimSort is O(n) on already-sorted input — effectively free in steady state),\n * then walks every visible non-empty layer and runs `ensureLayerGpu` + `uploadLayer`.\n * No GPU draw work here — only buffer uploads via `writeBuffer`.\n */\nfunction spriteRendererUpdate(rr: SpriteRenderer): void {\n if (rr._disposed) {\n return;\n }\n const deltaMs = rr._surface.engine._currentDelta ?? 0;\n for (const hook of rr._beforeUpdate) {\n hook(deltaMs);\n }\n assertSpriteRendererLayers(rr.layers);\n const targetSize = getRenderTargetSize(rr._surface);\n rr._targetWidth = targetSize.width;\n rr._targetHeight = targetSize.height;\n\n // Sort layers in place by `order` once per frame. TimSort is O(n) on already-sorted input,\n // so this is effectively free in the steady state. Documented side-effect on `rr.layers`\n // (registration order is not the ground truth — `layer.order` is). Skipped for the common\n // single-layer case to avoid even the comparator-call overhead.\n if (rr.layers.length > 1) {\n rr._layers.sort(compareLayers);\n }\n\n for (const layer of rr.layers) {\n if (!layer.visible || layer.count === 0) {\n continue;\n }\n const lg = ensureLayerGpu(rr, layer);\n uploadLayer(rr, lg, deltaMs);\n }\n}\n\n/**\n * Per-frame **record** pass (called by the engine after `_update`).\n * For each visible non-empty layer: builds (or reuses) a `GPURenderBundle` that bakes\n * `setIndexBuffer` + `setPipeline` + `setBindGroup` + `setVertexBuffer` + `drawIndexed`,\n * then queues it for a single `pass.executeBundles(...)` replay. The bundle is the per-frame\n * fast path — it skips Chromium's per-call WebGPU validation and IPC, which dominates\n * CPU cost for static scenes at multi-kHz framerates. Bundle is rebuilt only when\n * `layer.count` changes or the instance buffer was reallocated.\n * Returns one draw call per visible non-empty layer (1000 sprites in a layer = 1 draw\n * call thanks to instancing).\n */\nfunction spriteRendererRecord(rr: SpriteRenderer): number {\n if (rr._disposed) {\n return 0;\n }\n assertSpriteRendererLayers(rr.layers);\n const eng = rr._surface.engine;\n const encoder = eng._currentEncoder;\n const swapView = rr._targetView ?? eng.scRT._colorView!;\n\n // Open a sampleCount=1 render pass on the target view (the swapchain by default, or an\n // offscreen render texture when one is set via setSpriteRendererTarget). This keeps HUD\n // sprites from resolving a fresh MSAA target over the already-rendered scene.\n const pass = encoder.beginRenderPass({\n colorAttachments: [\n {\n view: swapView,\n clearValue: rr.clearColor,\n loadOp: rr._clear ? \"clear\" : \"load\",\n storeOp: \"store\",\n },\n ],\n });\n let drawCalls = 0;\n const visibleBundles = rr._visibleBundles;\n visibleBundles.length = 0;\n\n for (const layer of rr.layers) {\n if (!layer.visible || layer.count === 0) {\n continue;\n }\n const lg = rr._layerGpu.get(layer);\n if (!lg) {\n continue;\n }\n const sampleCount = 1;\n const pipeline = getOrCreateSpritePipeline(\n rr._surface.engine,\n rr._pipelineCache,\n rr._surface.format,\n sampleCount,\n layer.blendMode,\n false,\n false,\n undefined,\n undefined,\n layer\n );\n if (lg.pipeline !== pipeline) {\n lg.pipeline = pipeline;\n lg.bindGroup = null;\n lg.renderBundle = null;\n }\n const bg = ensureBindGroup(rr, lg, pipeline);\n // (Re)record the bundle when count changes (drawIndexed instance count is baked in)\n // or when ensureLayerGpu reallocated the instance buffer (renderBundle was nulled).\n if (lg.renderBundle == null || lg.bundleCount !== layer.count) {\n const be = rr._surface.engine._device.createRenderBundleEncoder({\n colorFormats: [rr._surface.format],\n sampleCount,\n });\n be.setIndexBuffer(rr._indexBuffer, \"uint16\");\n be.setPipeline(pipeline);\n be.setBindGroup(0, bg);\n be.setVertexBuffer(0, lg.instanceBuffer);\n be.drawIndexed(6, layer.count, 0, 0, 0);\n lg.renderBundle = be.finish();\n lg.bundleCount = layer.count;\n }\n visibleBundles.push(lg.renderBundle!);\n drawCalls++;\n }\n\n if (visibleBundles.length > 0) {\n pass.executeBundles(visibleBundles);\n }\n pass.end();\n return drawCalls;\n}\n\n/** Add a pure-2D layer to the renderer. No-op if the layer is already present. */\nexport function addSpriteRendererLayer(sr: SpriteRenderer, layer: Sprite2DLayer): void {\n if (sr._disposed) {\n throw new Error(\"SpriteRenderer has been disposed.\");\n }\n assertSpriteRendererLayer(layer);\n if (sr.layers.includes(layer)) {\n return;\n }\n sr._layers.push(layer);\n getOrCreateSpritePipeline(sr._surface.engine, sr._pipelineCache, sr._surface.format, 1, layer.blendMode, false);\n}\n\n/** Remove a layer from the renderer and destroy any GPU resources cached for it. */\nexport function removeSpriteRendererLayer(sr: SpriteRenderer, layer: Sprite2DLayer): boolean {\n const index = sr.layers.indexOf(layer);\n if (index < 0) {\n return false;\n }\n sr._layers.splice(index, 1);\n const lg = sr._layerGpu.get(layer);\n if (lg) {\n disposeLayerGpu(lg);\n sr._layerGpu.delete(layer);\n }\n return true;\n}\n\n/** Push the renderer onto its engine's `_renderingContexts`. Idempotent — a second call is a no-op. */\nexport function registerSpriteRenderer(sr: SpriteRenderer): void {\n registerRenderingContext(sr._surface, sr);\n}\n\n/**\n * Redirect a sprite renderer's output to an offscreen render {@link Texture2D} `target` (for\n * render-to-texture / post-processing), or pass `null` to render to the swapchain (the\n * default). Pair with {@link createRenderTexture2D} at its default format and the renderer's\n * target size. The target's texture must be the engine's swapchain format: sprite pipelines\n * are baked with `engine.format`, so a target of any OTHER format fails WebGPU validation at\n * render-pass begin. A second renderer can then sample that texture (e.g. a fullscreen\n * custom-shader layer) and present it. Renderers registered later run later, so register the\n * offscreen scene pass before the presenting pass.\n */\nexport function setSpriteRendererTarget(sr: SpriteRenderer, target: Texture2D | null): void {\n sr._targetView = target ? target.view : null;\n}\n\n/** Splice the renderer out of its engine's `_renderingContexts`. No-op if not present. */\nexport function unregisterSpriteRenderer(sr: SpriteRenderer): void {\n unregisterRenderingContext(sr._surface, sr);\n}\n\n/**\n * Destroy all GPU resources owned by the renderer, unregister it from the engine, and clear `layers`.\n * Idempotent. To tie disposal to a scene, call\n * `onSceneDispose(scene, () => disposeSpriteRenderer(sr))` after `registerSpriteRenderer` —\n * see the `SpriteRenderer` doc-comment.\n */\nexport function disposeSpriteRenderer(sr: SpriteRenderer): void {\n if (sr._disposed) {\n return;\n }\n unregisterSpriteRenderer(sr);\n sr._disposed = true;\n const disposeCallbacks = sr._disposeCallbacks.slice();\n sr._disposeCallbacks.length = 0;\n for (const dispose of disposeCallbacks) {\n dispose();\n }\n for (const lg of sr._layerGpu.values()) {\n disposeLayerGpu(lg);\n }\n sr._layerGpu.clear();\n sr._visibleBundles.length = 0;\n sr._beforeUpdate.length = 0;\n sr._indexBuffer.destroy();\n resetSpritePipelineCache(sr._pipelineCache);\n sr._layers.length = 0;\n}\n\n/** @internal Test-only accessor for pipeline-cache size. */\nexport function _spriteRendererPipelineCacheSize(sr: SpriteRenderer): number {\n return getSpritePipelineCacheSize(sr._pipelineCache, sr._surface.engine._device);\n}\n"],"names":[],"mappings":";;;;;;;AA0CA,MAAM,IAAA,GAAO,iBAAA;AA+Gb,SAAS,cAAA,CAAe,IAAoB,KAAA,EAAgC;AACxE,EAAA,IAAI,EAAA,GAAK,EAAA,CAAG,SAAA,CAAU,GAAA,CAAI,KAAK,CAAA;AAC/B,EAAA,IAAI,CAAC,EAAA,EAAI;AACL,IAAA,MAAM,MAAM,KAAA,CAAM,SAAA;AAClB,IAAA,MAAM,iBAAiB,0BAAA,CAA2B,EAAA,CAAG,SAAS,MAAA,CAAO,OAAA,EAAS,OAAO,wBAAwB,CAAA;AAC7G,IAAA,MAAM,gBAAgB,wBAAA,CAAyB,EAAA,CAAG,QAAA,CAAS,MAAA,EAAQ,iBAAiB,kBAAkB,CAAA;AACtG,IAAA,MAAM,EAAA,GAAK,kBAAiB,EAAG,aAAA,CAAc,GAAG,QAAA,CAAS,MAAA,EAAQ,qBAAA,EAAuB,KAAK,CAAA,IAAK,IAAA;AAClG,IAAA,EAAA,GAAK;AAAA,MACD,KAAA;AAAA,MACA,cAAA;AAAA,MACA,sBAAA,EAAwB,GAAA;AAAA,MACxB,aAAA;AAAA,MACA,SAAA,EAAW,IAAA;AAAA,MACX,eAAA,EAAiB,EAAA;AAAA,MACjB,EAAA;AAAA,MACA,QAAA,EAAU,IAAA;AAAA,MACV,OAAA,EAAS,IAAI,GAAA,CAAI,eAAA,GAAkB,CAAC,CAAA;AAAA,MACpC,WAAA,EAAa,KAAA;AAAA,MACb,YAAA,EAAc,IAAA;AAAA,MACd,WAAA,EAAa;AAAA,KACjB;AACA,IAAA,EAAA,CAAG,SAAA,CAAU,GAAA,CAAI,KAAA,EAAO,EAAE,CAAA;AAAA,EAC9B;AACA,EAAA,MAAM,KAAA,GAAQ,0BAAA,CAA2B,EAAA,CAAG,QAAA,CAAS,MAAA,CAAO,OAAA,EAAS,KAAA,EAAO,EAAA,CAAG,cAAA,EAAgB,EAAA,CAAG,sBAAA,EAAwB,wBAAwB,CAAA;AAClJ,EAAA,IAAI,MAAM,WAAA,EAAa;AACnB,IAAA,EAAA,CAAG,iBAAiB,KAAA,CAAM,MAAA;AAC1B,IAAA,EAAA,CAAG,yBAAyB,KAAA,CAAM,QAAA;AAClC,IAAA,EAAA,CAAG,eAAA,GAAkB,EAAA;AAErB,IAAA,EAAA,CAAG,YAAA,GAAe,IAAA;AAAA,EACtB;AACA,EAAA,OAAO,EAAA;AACX;AAIA,SAAS,WAAA,CAAY,EAAA,EAAoB,EAAA,EAAc,OAAA,EAAuB;AAC1E,EAAA,MAAM,QAAQ,EAAA,CAAG,KAAA;AACjB,EAAA,EAAA,CAAG,eAAA,GAAkB,qBAAA,CAAsB,EAAA,CAAG,QAAA,CAAS,MAAA,CAAO,SAAS,KAAA,EAAO,EAAA,CAAG,cAAA,EAAgB,EAAA,CAAG,eAAe,CAAA;AACnH,EAAA,mBAAA,CAAoB,KAAA,EAAO,EAAA,CAAG,YAAA,EAAc,EAAA,CAAG,eAAe,WAAW,CAAA;AACzE,EAAA,EAAA,CAAG,WAAA,GAAc,0BAAA,CAA2B,EAAA,CAAG,QAAA,CAAS,MAAA,CAAO,OAAA,EAAS,EAAA,CAAG,aAAA,EAAe,WAAA,EAAa,EAAA,CAAG,OAAA,EAAS,EAAA,CAAG,WAAW,CAAA;AACjI,EAAA,IAAI,GAAG,EAAA,EAAI;AACP,IAAA,gBAAA,EAAiB,CAAG,QAAA,CAAS,EAAA,CAAG,EAAA,EAAI,OAAO,OAAO,CAAA;AAAA,EACtD;AACJ;AAEA,SAAS,gBAAgB,EAAA,EAAoB;AACzC,EAAA,EAAA,CAAG,eAAe,OAAA,EAAQ;AAC1B,EAAA,EAAA,CAAG,cAAc,OAAA,EAAQ;AACzB,EAAA,IAAI,GAAG,EAAA,EAAI;AACP,IAAA,gBAAA,EAAiB,CAAG,SAAA,CAAU,EAAA,CAAG,EAAE,CAAA;AAAA,EACvC;AACJ;AAEA,MAAM,WAAA,GAAc,IAAI,GAAA,CAAI,eAAA,GAAkB,CAAC,CAAA;AAU/C,SAAS,eAAA,CAAgB,EAAA,EAAoB,EAAA,EAAc,QAAA,EAA2C;AAClG,EAAA,IAAI,GAAG,SAAA,EAAW;AACd,IAAA,OAAO,EAAA,CAAG,SAAA;AAAA,EACd;AACA,EAAA,EAAA,CAAG,SAAA,GAAY,0BAAA,CAA2B,EAAA,CAAG,QAAA,CAAS,MAAA,EAAQ,QAAA,EAAU,CAAA,EAAG,EAAA,CAAG,KAAA,EAAO,EAAA,CAAG,aAAA,EAAe,EAAA,CAAG,EAAE,CAAA;AAC5G,EAAA,OAAO,EAAA,CAAG,SAAA;AACd;AAGA,SAAS,aAAA,CAAc,GAAkB,CAAA,EAA0B;AAC/D,EAAA,IAAI,CAAA,CAAE,KAAA,KAAU,CAAA,CAAE,KAAA,EAAO;AACrB,IAAA,OAAO,CAAA,CAAE,QAAQ,CAAA,CAAE,KAAA;AAAA,EACvB;AACA,EAAA,OAAO,CAAA;AACX;AAMO,SAAS,oBAAA,CAAqB,SAAyB,IAAA,EAA6C;AACvG,EAAA,MAAM,SAAS,OAAA,CAAQ,MAAA;AACvB,EAAA,0BAAA,CAA2B,KAAK,MAAM,CAAA;AACtC,EAAA,MAAM,WAAA,GAAc,kBAAA,CAAmB,MAAA,EAAQ,wBAAA,EAA0B,GAAG,KAAK,CAAA;AACjF,EAAA,MAAM,SAAS,OAAA,CAAQ,MAAA;AAEvB,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,KAAA,EAAM;AACjC,EAAA,MAAM,EAAA,GAAqB;AAAA,IACvB,KAAA,EAAO,IAAA;AAAA,IACP,QAAA,EAAU,OAAA;AAAA,IACV,YAAA,EAAc,WAAA;AAAA,IACd,gBAAgB,yBAAA,EAA0B;AAAA,IAC1C,SAAA,sBAAe,GAAA,EAAI;AAAA,IACnB,iBAAiB,EAAC;AAAA,IAClB,cAAc,MAAA,CAAO,KAAA;AAAA,IACrB,eAAe,MAAA,CAAO,MAAA;AAAA,IACtB,SAAA,EAAW,KAAA;AAAA,IACX,MAAA,EAAQ,KAAK,KAAA,IAAS,IAAA;AAAA,IACtB,WAAA,EAAa,IAAA;AAAA,IACb,eAAe,EAAC;AAAA,IAChB,mBAAmB,EAAC;AAAA,IACpB,MAAA;AAAA,IACA,OAAA,EAAS,MAAA;AAAA,IACT,UAAA,EAAY,IAAA,CAAK,UAAA,IAAc,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAE;AAAA,IACxD,aAAA,EAAe,CAAA;AAAA,IACf,OAAA,GAAgB;AACZ,MAAA,oBAAA,CAAqB,EAAE,CAAA;AAAA,IAC3B,CAAA;AAAA,IACA,OAAA,GAAkB;AACd,MAAA,OAAO,qBAAqB,EAAE,CAAA;AAAA,IAClC;AAAA,GACJ;AAGA,EAAA,KAAA,MAAW,KAAA,IAAS,GAAG,MAAA,EAAQ;AAC3B,IAAA,yBAAA,CAA0B,MAAA,EAAQ,EAAA,CAAG,cAAA,EAAgB,OAAA,CAAQ,MAAA,EAAQ,CAAA,EAAG,KAAA,CAAM,SAAA,EAAW,KAAA,EAAO,KAAA,EAAO,MAAA,EAAW,MAAA,EAAW,KAAK,CAAA;AAAA,EACtI;AAEA,EAAA,OAAO,EAAA;AACX;AAEA,SAAS,2BAA2B,MAAA,EAAwC;AACxE,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AACxB,IAAA,yBAAA,CAA0B,KAAK,CAAA;AAAA,EACnC;AACJ;AAEA,SAAS,0BAA0B,KAAA,EAA4B;AAC3D,EAAA,IAAI,KAAA,CAAM,UAAU,MAAA,EAAQ;AACxB,IAAA,MAAM,IAAI,MAAM,sIAAsI,CAAA;AAAA,EAC1J;AACJ;AASA,SAAS,qBAAqB,EAAA,EAA0B;AACpD,EAAA,IAAI,GAAG,SAAA,EAAW;AACd,IAAA;AAAA,EACJ;AACA,EAAA,MAAM,OAAA,GAAU,EAAA,CAAG,QAAA,CAAS,MAAA,CAAO,aAAA,IAAiB,CAAA;AACpD,EAAA,KAAA,MAAW,IAAA,IAAQ,GAAG,aAAA,EAAe;AACjC,IAAA,IAAA,CAAK,OAAO,CAAA;AAAA,EAChB;AACA,EAAA,0BAAA,CAA2B,GAAG,MAAM,CAAA;AACpC,EAAA,MAAM,UAAA,GAAa,mBAAA,CAAoB,EAAA,CAAG,QAAQ,CAAA;AAClD,EAAA,EAAA,CAAG,eAAe,UAAA,CAAW,KAAA;AAC7B,EAAA,EAAA,CAAG,gBAAgB,UAAA,CAAW,MAAA;AAM9B,EAAA,IAAI,EAAA,CAAG,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG;AACtB,IAAA,EAAA,CAAG,OAAA,CAAQ,KAAK,aAAa,CAAA;AAAA,EACjC;AAEA,EAAA,KAAA,MAAW,KAAA,IAAS,GAAG,MAAA,EAAQ;AAC3B,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,KAAA,CAAM,UAAU,CAAA,EAAG;AACrC,MAAA;AAAA,IACJ;AACA,IAAA,MAAM,EAAA,GAAK,cAAA,CAAe,EAAA,EAAI,KAAK,CAAA;AACnC,IAAA,WAAA,CAAY,EAAA,EAAI,IAAI,OAAO,CAAA;AAAA,EAC/B;AACJ;AAaA,SAAS,qBAAqB,EAAA,EAA4B;AACtD,EAAA,IAAI,GAAG,SAAA,EAAW;AACd,IAAA,OAAO,CAAA;AAAA,EACX;AACA,EAAA,0BAAA,CAA2B,GAAG,MAAM,CAAA;AACpC,EAAA,MAAM,GAAA,GAAM,GAAG,QAAA,CAAS,MAAA;AACxB,EAAA,MAAM,UAAU,GAAA,CAAI,eAAA;AACpB,EAAA,MAAM,QAAA,GAAW,EAAA,CAAG,WAAA,IAAe,GAAA,CAAI,IAAA,CAAK,UAAA;AAK5C,EAAA,MAAM,IAAA,GAAO,QAAQ,eAAA,CAAgB;AAAA,IACjC,gBAAA,EAAkB;AAAA,MACd;AAAA,QACI,IAAA,EAAM,QAAA;AAAA,QACN,YAAY,EAAA,CAAG,UAAA;AAAA,QACf,MAAA,EAAQ,EAAA,CAAG,MAAA,GAAS,OAAA,GAAU,MAAA;AAAA,QAC9B,OAAA,EAAS;AAAA;AACb;AACJ,GACH,CAAA;AACD,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,MAAM,iBAAiB,EAAA,CAAG,eAAA;AAC1B,EAAA,cAAA,CAAe,MAAA,GAAS,CAAA;AAExB,EAAA,KAAA,MAAW,KAAA,IAAS,GAAG,MAAA,EAAQ;AAC3B,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,KAAA,CAAM,UAAU,CAAA,EAAG;AACrC,MAAA;AAAA,IACJ;AACA,IAAA,MAAM,EAAA,GAAK,EAAA,CAAG,SAAA,CAAU,GAAA,CAAI,KAAK,CAAA;AACjC,IAAA,IAAI,CAAC,EAAA,EAAI;AACL,MAAA;AAAA,IACJ;AACA,IAAA,MAAM,WAAA,GAAc,CAAA;AACpB,IAAA,MAAM,QAAA,GAAW,yBAAA;AAAA,MACb,GAAG,QAAA,CAAS,MAAA;AAAA,MACZ,EAAA,CAAG,cAAA;AAAA,MACH,GAAG,QAAA,CAAS,MAAA;AAAA,MACZ,WAAA;AAAA,MACA,KAAA,CAAM,SAAA;AAAA,MACN,KAAA;AAAA,MACA,KAAA;AAAA,MACA,MAAA;AAAA,MACA,MAAA;AAAA,MACA;AAAA,KACJ;AACA,IAAA,IAAI,EAAA,CAAG,aAAa,QAAA,EAAU;AAC1B,MAAA,EAAA,CAAG,QAAA,GAAW,QAAA;AACd,MAAA,EAAA,CAAG,SAAA,GAAY,IAAA;AACf,MAAA,EAAA,CAAG,YAAA,GAAe,IAAA;AAAA,IACtB;AACA,IAAA,MAAM,EAAA,GAAK,eAAA,CAAgB,EAAA,EAAI,EAAA,EAAI,QAAQ,CAAA;AAG3C,IAAA,IAAI,GAAG,YAAA,IAAgB,IAAA,IAAQ,EAAA,CAAG,WAAA,KAAgB,MAAM,KAAA,EAAO;AAC3D,MAAA,MAAM,EAAA,GAAK,EAAA,CAAG,QAAA,CAAS,MAAA,CAAO,QAAQ,yBAAA,CAA0B;AAAA,QAC5D,YAAA,EAAc,CAAC,EAAA,CAAG,QAAA,CAAS,MAAM,CAAA;AAAA,QACjC;AAAA,OACH,CAAA;AACD,MAAA,EAAA,CAAG,cAAA,CAAe,EAAA,CAAG,YAAA,EAAc,QAAQ,CAAA;AAC3C,MAAA,EAAA,CAAG,YAAY,QAAQ,CAAA;AACvB,MAAA,EAAA,CAAG,YAAA,CAAa,GAAG,EAAE,CAAA;AACrB,MAAA,EAAA,CAAG,eAAA,CAAgB,CAAA,EAAG,EAAA,CAAG,cAAc,CAAA;AACvC,MAAA,EAAA,CAAG,YAAY,CAAA,EAAG,KAAA,CAAM,KAAA,EAAO,CAAA,EAAG,GAAG,CAAC,CAAA;AACtC,MAAA,EAAA,CAAG,YAAA,GAAe,GAAG,MAAA,EAAO;AAC5B,MAAA,EAAA,CAAG,cAAc,KAAA,CAAM,KAAA;AAAA,IAC3B;AACA,IAAA,cAAA,CAAe,IAAA,CAAK,GAAG,YAAa,CAAA;AACpC,IAAA,SAAA,EAAA;AAAA,EACJ;AAEA,EAAA,IAAI,cAAA,CAAe,SAAS,CAAA,EAAG;AAC3B,IAAA,IAAA,CAAK,eAAe,cAAc,CAAA;AAAA,EACtC;AACA,EAAA,IAAA,CAAK,GAAA,EAAI;AACT,EAAA,OAAO,SAAA;AACX;AAGO,SAAS,sBAAA,CAAuB,IAAoB,KAAA,EAA4B;AACnF,EAAA,IAAI,GAAG,SAAA,EAAW;AACd,IAAA,MAAM,IAAI,MAAM,mCAAmC,CAAA;AAAA,EACvD;AACA,EAAA,yBAAA,CAA0B,KAAK,CAAA;AAC/B,EAAA,IAAI,EAAA,CAAG,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,EAAG;AAC3B,IAAA;AAAA,EACJ;AACA,EAAA,EAAA,CAAG,OAAA,CAAQ,KAAK,KAAK,CAAA;AACrB,EAAA,yBAAA,CAA0B,EAAA,CAAG,QAAA,CAAS,MAAA,EAAQ,EAAA,CAAG,cAAA,EAAgB,EAAA,CAAG,QAAA,CAAS,MAAA,EAAQ,CAAA,EAAG,KAAA,CAAM,SAAA,EAAW,KAAK,CAAA;AAClH;AAGO,SAAS,yBAAA,CAA0B,IAAoB,KAAA,EAA+B;AACzF,EAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA;AACrC,EAAA,IAAI,QAAQ,CAAA,EAAG;AACX,IAAA,OAAO,KAAA;AAAA,EACX;AACA,EAAA,EAAA,CAAG,OAAA,CAAQ,MAAA,CAAO,KAAA,EAAO,CAAC,CAAA;AAC1B,EAAA,MAAM,EAAA,GAAK,EAAA,CAAG,SAAA,CAAU,GAAA,CAAI,KAAK,CAAA;AACjC,EAAA,IAAI,EAAA,EAAI;AACJ,IAAA,eAAA,CAAgB,EAAE,CAAA;AAClB,IAAA,EAAA,CAAG,SAAA,CAAU,OAAO,KAAK,CAAA;AAAA,EAC7B;AACA,EAAA,OAAO,IAAA;AACX;AAGO,SAAS,uBAAuB,EAAA,EAA0B;AAC7D,EAAA,wBAAA,CAAyB,EAAA,CAAG,UAAU,EAAE,CAAA;AAC5C;AAYO,SAAS,uBAAA,CAAwB,IAAoB,MAAA,EAAgC;AACxF,EAAA,EAAA,CAAG,WAAA,GAAc,MAAA,GAAS,MAAA,CAAO,IAAA,GAAO,IAAA;AAC5C;AAGO,SAAS,yBAAyB,EAAA,EAA0B;AAC/D,EAAA,0BAAA,CAA2B,EAAA,CAAG,UAAU,EAAE,CAAA;AAC9C;AAQO,SAAS,sBAAsB,EAAA,EAA0B;AAC5D,EAAA,IAAI,GAAG,SAAA,EAAW;AACd,IAAA;AAAA,EACJ;AACA,EAAA,wBAAA,CAAyB,EAAE,CAAA;AAC3B,EAAA,EAAA,CAAG,SAAA,GAAY,IAAA;AACf,EAAA,MAAM,gBAAA,GAAmB,EAAA,CAAG,iBAAA,CAAkB,KAAA,EAAM;AACpD,EAAA,EAAA,CAAG,kBAAkB,MAAA,GAAS,CAAA;AAC9B,EAAA,KAAA,MAAW,WAAW,gBAAA,EAAkB;AACpC,IAAA,OAAA,EAAQ;AAAA,EACZ;AACA,EAAA,KAAA,MAAW,EAAA,IAAM,EAAA,CAAG,SAAA,CAAU,MAAA,EAAO,EAAG;AACpC,IAAA,eAAA,CAAgB,EAAE,CAAA;AAAA,EACtB;AACA,EAAA,EAAA,CAAG,UAAU,KAAA,EAAM;AACnB,EAAA,EAAA,CAAG,gBAAgB,MAAA,GAAS,CAAA;AAC5B,EAAA,EAAA,CAAG,cAAc,MAAA,GAAS,CAAA;AAC1B,EAAA,EAAA,CAAG,aAAa,OAAA,EAAQ;AACxB,EAAA,wBAAA,CAAyB,GAAG,cAAc,CAAA;AAC1C,EAAA,EAAA,CAAG,QAAQ,MAAA,GAAS,CAAA;AACxB;AAGO,SAAS,iCAAiC,EAAA,EAA4B;AACzE,EAAA,OAAO,2BAA2B,EAAA,CAAG,cAAA,EAAgB,EAAA,CAAG,QAAA,CAAS,OAAO,OAAO,CAAA;AACnF;;;;"}
1
+ {"version":3,"file":"sprite-renderer.js","sources":["../../../src/sprite/sprite-renderer.ts"],"sourcesContent":["/**\n * `SpriteRenderer` — owns the shared index buffer and per-layer GPU state\n * required to draw `Sprite2DLayer`s. Implements\n * `RenderingContext` directly, so it plugs into `engine._renderingContexts`\n * the same way a `SceneContext` does.\n *\n * Scope:\n * - Pure-2D / HUD path only — all layers must use `depth: \"none\"`.\n * - One sprite-pipeline cache per renderer instance, keyed by format,\n * blend mode, and sample count. The direct HUD path always uses\n * `sampleCount=1` and `hasDepth=false`.\n * - The renderer opens a sprite-only swapchain pass using the engine's\n * current command encoder and swapchain view. Off-screen / HUD-to-texture\n * rendering is deferred until there is a concrete caller.\n */\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport { BU } from \"../engine/gpu-flags.js\";\nimport { registerRenderingContext, unregisterRenderingContext, getRenderTargetSize } from \"../engine/engine.js\";\nimport type { RenderingContext } from \"../engine/engine.js\";\nimport type { SurfaceContext } from \"../engine/surface.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 type { Texture2D } from \"../texture/texture-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 getSpritePipelineCacheSize,\n resetSpritePipelineCache,\n uploadSpriteInstances,\n writeSpriteLayerUboIfDirty,\n} from \"./sprite-pipeline.js\";\nimport type { SpritePipelineCache } from \"./sprite-pipeline.js\";\n\n/** Tag used by the engine and by tests to identify a sprite renderer. */\nconst KIND = \"sprite-renderer\" as const;\n\n/** Options accepted by `createSpriteRenderer`. */\nexport interface SpriteRendererOptions {\n /** Layers to draw, in registration order. The renderer also re-sorts internally each frame. */\n layers: readonly Sprite2DLayer[];\n /** Default true. Set false for HUD overlays so the sprite pass preserves existing scene color. */\n clear?: boolean;\n /** Default `{ r: 0, g: 0, b: 0, a: 1 }`. */\n clearValue?: GPUColorDict;\n}\n\n/**\n * A `SpriteRenderer` — pure data, plugs into `engine._renderingContexts`.\n * Inherits `clearColor`, `_drawCallsPre`, `_update`, `_record` from `RenderingContext`;\n * adds only its discriminator tag and the renderer-owned layer list.\n *\n * **Lifecycle.** A `SpriteRenderer` is independent of any `SceneContext` — it is\n * registered directly on the engine, opens its own sampleCount=1 swapchain pass, and\n * must be disposed by the caller. Two patterns:\n *\n * 1. **Standalone** (no scene, or one or more renderers alongside a scene):\n * `registerSpriteRenderer` after `createSpriteRenderer`, `disposeSpriteRenderer`\n * on shutdown. Caller owns the lifetime end-to-end.\n * 2. **HUD-on-3D** (renderer overlaid on a scene): same `register` step,\n * then tie disposal to the scene with\n * `onSceneDispose(scene, () => disposeSpriteRenderer(hud))` —\n * `disposeScene` then cleans it up automatically. Register the renderer\n * *after* `registerScene` so it draws on top.\n *\n * Scene 52 demonstrates pattern (2). For depth-hosted sprites that should\n * sort against 3D meshes, use `addDepthHostedSpriteLayer(scene, layer)` with\n * a depth-enabled `Sprite2DLayer` instead — that route is fully owned by the scene.\n */\nexport interface SpriteRenderer extends RenderingContext {\n /** @internal */\n readonly _kind: typeof KIND;\n /** Renderer-owned layer membership. Use `addSpriteRendererLayer` / `removeSpriteRendererLayer` to mutate. */\n readonly layers: readonly Sprite2DLayer[];\n /** @internal Mutable alias of {@link layers} — same array, used by internal helpers. */\n _layers: Sprite2DLayer[];\n /** @internal Surface this renderer draws into. */\n _surface: SurfaceContext;\n /** @internal */\n _indexBuffer: GPUBuffer;\n /** @internal */\n _pipelineCache: SpritePipelineCache;\n /** @internal */\n _layerGpu: Map<Sprite2DLayer, LayerGpu>;\n /** @internal Hooks run at the start of `_update`, before layer uploads. */\n _beforeUpdate: ((deltaMs: number) => void)[];\n /** @internal Cleanup callbacks run by `disposeSpriteRenderer`; optional integrations register here. */\n _disposeCallbacks: (() => void)[];\n /** @internal */\n _visibleBundles: GPURenderBundle[];\n /** @internal Captured each `_update`, read in `_record`. */\n _targetWidth: number;\n /** @internal */\n _targetHeight: number;\n /** @internal */\n _disposed: boolean;\n /** @internal Whether this pass clears the swapchain before drawing. False for HUD overlays. */\n _clear: boolean;\n /** @internal Offscreen color-attachment view to render into; null = the swapchain.\n * Set via {@link setSpriteRendererTarget} (which takes a {@link Texture2D} target)\n * for render-to-texture / post-process. */\n _targetView: GPUTextureView | null;\n}\n\n/** @internal Per-layer GPU resources owned by the renderer. */\ninterface LayerGpu {\n layer: Sprite2DLayer;\n instanceBuffer: GPUBuffer;\n instanceBufferCapacity: number;\n uniformBuffer: GPUBuffer;\n /** Built once per layer; the bind group binds the uniform buffer + atlas texture/sampler,\n * none of which change after construction (atlas is `readonly` on the layer; uniform\n * buffer is allocated once in `ensureLayerGpu`). Cleared if we ever recreate either. */\n bindGroup: GPUBindGroup | null;\n uploadedVersion: number;\n /** Opaque fx attachment (`SpriteFx` UBO, scratch, elapsed time); non-null only for `customShader` layers. */\n fx: SpriteLayerFx | null;\n /** Cached pipeline object. Refreshed when target-defining GPU state resolves to a different pipeline. */\n pipeline: GPURenderPipeline | null;\n /** Snapshot of the last UBO bytes written to `uniformBuffer`. We rebuild the UBO into\n * `_scratchUbo` each frame, then `writeBuffer` only if the contents actually changed.\n * For static scenes (steady-state) this skips one `queue.writeBuffer` per layer per frame. */\n lastUbo: Float32Array;\n /** False until the first UBO upload. Forces an unconditional first write so `lastUbo` is real. */\n uboUploaded: boolean;\n /** Pre-recorded GPU command bundle: `setIndexBuffer` + `setPipeline` + `setBindGroup` +\n * `setVertexBuffer` + `drawIndexed`. Collected into a reused bundle array for\n * near-zero per-frame CPU command-recording cost (the big WebGPU win for static scenes —\n * see `scene-core.ts._record` for the same pattern). Invalidated when `layer.count` changes\n * (the `drawIndexed` instance count is baked into the bundle) or when the instance buffer is\n * reallocated by `ensureLayerGpu` (the bundle holds a GPUBuffer reference). The UBO contents\n * may freely change frame-to-frame — the bundle binds the buffer *object*, not its bytes. */\n renderBundle: GPURenderBundle | null;\n /** `layer.count` value the cached `renderBundle` was recorded against. */\n bundleCount: number;\n}\n\n/**\n * Lazy GPU-resource provisioner for one layer. On first sight: allocates the per-instance\n * vertex buffer + the 48 B layer UBO and stashes a `LayerGpu` record in `_layerGpu`. On\n * subsequent calls where the layer's CPU `_capacity` outgrew the GPU buffer (after\n * `growCapacity` doubled the array): destroys + reallocates the instance buffer at the\n * new size and forces a full re-upload via `uploadedVersion = -1`. The bind group is\n * left intact — it doesn't reference the instance buffer (vertex buffers are bound\n * separately at draw time), only the uniform buffer + atlas, neither of which moves.\n */\nfunction ensureLayerGpu(rr: SpriteRenderer, layer: Sprite2DLayer): LayerGpu {\n let lg = rr._layerGpu.get(layer);\n if (!lg) {\n const cap = layer._capacity;\n const instanceBuffer = createSpriteInstanceBuffer(rr._surface.engine._device, layer);\n const uniformBuffer = createEmptyUniformBuffer(rr._surface.engine, LAYER_UBO_BYTES);\n const fx = _getSpriteFxHook()?.createLayerFx(rr._surface.engine, \"sprite-layer-fx-ubo\", layer) ?? null;\n lg = {\n layer,\n instanceBuffer,\n instanceBufferCapacity: cap,\n uniformBuffer,\n bindGroup: null,\n uploadedVersion: -1,\n fx,\n pipeline: null,\n lastUbo: new F32(LAYER_UBO_BYTES / 4),\n uboUploaded: false,\n renderBundle: null,\n bundleCount: -1,\n };\n rr._layerGpu.set(layer, lg);\n }\n const grown = ensureSpriteInstanceBuffer(rr._surface.engine._device, layer, lg.instanceBuffer, lg.instanceBufferCapacity);\n if (grown.reallocated) {\n lg.instanceBuffer = grown.buffer;\n lg.instanceBufferCapacity = grown.capacity;\n lg.uploadedVersion = -1;\n // Bundle baked a reference to the *old* GPUBuffer; the new buffer needs a re-record.\n lg.renderBundle = null;\n }\n return lg;\n}\n\n/** Sync one layer's GPU state to its CPU state — instance vertex data + per-layer UBO.\n * Both helpers are version-/dirty-gated and skip work in the steady state. */\nfunction uploadLayer(rr: SpriteRenderer, lg: LayerGpu, deltaMs: number): void {\n const layer = lg.layer;\n lg.uploadedVersion = uploadSpriteInstances(rr._surface.engine._device, layer, lg.instanceBuffer, lg.uploadedVersion);\n buildSpriteLayerUbo(layer, rr._targetWidth, rr._targetHeight, _scratchUbo);\n lg.uboUploaded = writeSpriteLayerUboIfDirty(rr._surface.engine._device, lg.uniformBuffer, _scratchUbo, lg.lastUbo, lg.uboUploaded);\n if (lg.fx) {\n _getSpriteFxHook()!.updateFx(lg.fx, layer, deltaMs);\n }\n}\n\nfunction disposeLayerGpu(lg: LayerGpu): void {\n lg.instanceBuffer.destroy();\n lg.uniformBuffer.destroy();\n if (lg.fx) {\n _getSpriteFxHook()!.disposeFx(lg.fx);\n }\n}\n\nconst _scratchUbo = new F32(LAYER_UBO_BYTES / 4);\n\n/**\n * Build (and cache) the bind group that attaches `lg.uniformBuffer` + atlas texture +\n * sampler to the pipeline's sprite `@group(0)` schema. All three resources are immutable for\n * the layer's lifetime, so this runs at most once per layer; subsequent calls return\n * the cached group. The instance buffer is **not** in the bind group — it's a vertex\n * buffer, bound separately at draw time — which is why instance-buffer growth in\n * `ensureLayerGpu` doesn't invalidate this cache.\n */\nfunction ensureBindGroup(rr: SpriteRenderer, lg: LayerGpu, pipeline: GPURenderPipeline): GPUBindGroup {\n if (lg.bindGroup) {\n return lg.bindGroup;\n }\n lg.bindGroup = createSpriteLayerBindGroup(rr._surface.engine, pipeline, 0, lg.layer, lg.uniformBuffer, lg.fx);\n return lg.bindGroup;\n}\n\n/** Sort key for layers within a renderer: ascending `order` (back-to-front draw order). */\nfunction compareLayers(a: Sprite2DLayer, b: Sprite2DLayer): number {\n if (a.order !== b.order) {\n return a.order - b.order;\n }\n return 0;\n}\n\n/** Create a `SpriteRenderer` for `surface`, pre-warming pipelines for the layers' blend\n * modes. Pass the engine directly for the common single-canvas case (since\n * `EngineContext extends SurfaceContext`); pass an auxiliary surface created via\n * `createSurface` for multi-canvas. */\nexport function createSpriteRenderer(surface: SurfaceContext, opts: SpriteRendererOptions): SpriteRenderer {\n const engine = surface.engine;\n assertSpriteRendererLayers(opts.layers);\n const indexBuffer = createMappedBuffer(engine, SHARED_SPRITE_INDEX_DATA, BU.INDEX);\n const canvas = surface.canvas;\n\n const layers = opts.layers.slice();\n const rr: SpriteRenderer = {\n _kind: KIND,\n _surface: surface,\n _indexBuffer: indexBuffer,\n _pipelineCache: createSpritePipelineCache(),\n _layerGpu: new Map(),\n _visibleBundles: [],\n _targetWidth: canvas.width,\n _targetHeight: canvas.height,\n _disposed: false,\n _clear: opts.clear ?? true,\n _targetView: null,\n _beforeUpdate: [],\n _disposeCallbacks: [],\n layers,\n _layers: layers,\n clearColor: opts.clearValue ?? { r: 0, g: 0, b: 0, a: 1 },\n _drawCallsPre: 0,\n _update(): void {\n spriteRendererUpdate(rr);\n },\n _record(): number {\n return spriteRendererRecord(rr);\n },\n };\n\n // Pre-warm pipelines currently in use, so the first frame doesn't pay compile cost.\n for (const layer of rr.layers) {\n getOrCreateSpritePipeline(engine, rr._pipelineCache, surface.format, 1, layer.blendMode, false, false, undefined, undefined, layer);\n }\n\n return rr;\n}\n\nfunction assertSpriteRendererLayers(layers: readonly Sprite2DLayer[]): void {\n for (const layer of layers) {\n assertSpriteRendererLayer(layer);\n }\n}\n\nfunction assertSpriteRendererLayer(layer: Sprite2DLayer): void {\n if (layer.depth !== \"none\") {\n throw new Error('SpriteRenderer requires depth: \"none\".');\n }\n}\n\n/**\n * Per-frame **update** pass (called by the engine before this renderer records its pass).\n * Refreshes target dims (canvas may have resized), sorts `rr.layers` in place by\n * `order` (TimSort is O(n) on already-sorted input — effectively free in steady state),\n * then walks every visible non-empty layer and runs `ensureLayerGpu` + `uploadLayer`.\n * No GPU draw work here — only buffer uploads via `writeBuffer`.\n */\nfunction spriteRendererUpdate(rr: SpriteRenderer): void {\n if (rr._disposed) {\n return;\n }\n const deltaMs = rr._surface.engine._currentDelta ?? 0;\n for (const hook of rr._beforeUpdate) {\n hook(deltaMs);\n }\n assertSpriteRendererLayers(rr.layers);\n const targetSize = getRenderTargetSize(rr._surface);\n rr._targetWidth = targetSize.width;\n rr._targetHeight = targetSize.height;\n\n // Sort layers in place by `order` once per frame. TimSort is O(n) on already-sorted input,\n // so this is effectively free in the steady state. Documented side-effect on `rr.layers`\n // (registration order is not the ground truth — `layer.order` is). Skipped for the common\n // single-layer case to avoid even the comparator-call overhead.\n if (rr.layers.length > 1) {\n rr._layers.sort(compareLayers);\n }\n\n for (const layer of rr.layers) {\n if (!layer.visible || layer.count === 0) {\n continue;\n }\n const lg = ensureLayerGpu(rr, layer);\n uploadLayer(rr, lg, deltaMs);\n }\n}\n\n/**\n * Per-frame **record** pass (called by the engine after `_update`).\n * For each visible non-empty layer: builds (or reuses) a `GPURenderBundle` that bakes\n * `setIndexBuffer` + `setPipeline` + `setBindGroup` + `setVertexBuffer` + `drawIndexed`,\n * then queues it for a single `pass.executeBundles(...)` replay. The bundle is the per-frame\n * fast path — it skips Chromium's per-call WebGPU validation and IPC, which dominates\n * CPU cost for static scenes at multi-kHz framerates. Bundle is rebuilt only when\n * `layer.count` changes or the instance buffer was reallocated.\n * Returns one draw call per visible non-empty layer (1000 sprites in a layer = 1 draw\n * call thanks to instancing).\n */\nfunction spriteRendererRecord(rr: SpriteRenderer): number {\n if (rr._disposed) {\n return 0;\n }\n assertSpriteRendererLayers(rr.layers);\n const eng = rr._surface.engine;\n const encoder = eng._currentEncoder;\n const swapView = rr._targetView ?? eng.scRT._colorView!;\n\n // Open a sampleCount=1 render pass on the target view (the swapchain by default, or an\n // offscreen render texture when one is set via setSpriteRendererTarget). This keeps HUD\n // sprites from resolving a fresh MSAA target over the already-rendered scene.\n const pass = encoder.beginRenderPass({\n colorAttachments: [\n {\n view: swapView,\n clearValue: rr.clearColor,\n loadOp: rr._clear ? \"clear\" : \"load\",\n storeOp: \"store\",\n },\n ],\n });\n let drawCalls = 0;\n const visibleBundles = rr._visibleBundles;\n visibleBundles.length = 0;\n\n for (const layer of rr.layers) {\n if (!layer.visible || layer.count === 0) {\n continue;\n }\n const lg = rr._layerGpu.get(layer);\n if (!lg) {\n continue;\n }\n const sampleCount = 1;\n const pipeline = getOrCreateSpritePipeline(\n rr._surface.engine,\n rr._pipelineCache,\n rr._surface.format,\n sampleCount,\n layer.blendMode,\n false,\n false,\n undefined,\n undefined,\n layer\n );\n if (lg.pipeline !== pipeline) {\n lg.pipeline = pipeline;\n lg.bindGroup = null;\n lg.renderBundle = null;\n }\n const bg = ensureBindGroup(rr, lg, pipeline);\n // (Re)record the bundle when count changes (drawIndexed instance count is baked in)\n // or when ensureLayerGpu reallocated the instance buffer (renderBundle was nulled).\n if (lg.renderBundle == null || lg.bundleCount !== layer.count) {\n const be = rr._surface.engine._device.createRenderBundleEncoder({\n colorFormats: [rr._surface.format],\n sampleCount,\n });\n be.setIndexBuffer(rr._indexBuffer, \"uint16\");\n be.setPipeline(pipeline);\n be.setBindGroup(0, bg);\n be.setVertexBuffer(0, lg.instanceBuffer);\n be.drawIndexed(6, layer.count, 0, 0, 0);\n lg.renderBundle = be.finish();\n lg.bundleCount = layer.count;\n }\n visibleBundles.push(lg.renderBundle!);\n drawCalls++;\n }\n\n if (visibleBundles.length > 0) {\n pass.executeBundles(visibleBundles);\n }\n pass.end();\n return drawCalls;\n}\n\n/** Add a pure-2D layer to the renderer. No-op if the layer is already present. */\nexport function addSpriteRendererLayer(sr: SpriteRenderer, layer: Sprite2DLayer): void {\n if (sr._disposed) {\n throw new Error(\"SpriteRenderer has been disposed.\");\n }\n assertSpriteRendererLayer(layer);\n if (sr.layers.includes(layer)) {\n return;\n }\n sr._layers.push(layer);\n getOrCreateSpritePipeline(sr._surface.engine, sr._pipelineCache, sr._surface.format, 1, layer.blendMode, false);\n}\n\n/** Remove a layer from the renderer and destroy any GPU resources cached for it. */\nexport function removeSpriteRendererLayer(sr: SpriteRenderer, layer: Sprite2DLayer): boolean {\n const index = sr.layers.indexOf(layer);\n if (index < 0) {\n return false;\n }\n sr._layers.splice(index, 1);\n const lg = sr._layerGpu.get(layer);\n if (lg) {\n disposeLayerGpu(lg);\n sr._layerGpu.delete(layer);\n }\n return true;\n}\n\n/** Push the renderer onto its engine's `_renderingContexts`. Idempotent — a second call is a no-op. */\nexport function registerSpriteRenderer(sr: SpriteRenderer): void {\n registerRenderingContext(sr._surface, sr);\n}\n\n/**\n * Redirect a sprite renderer's output to an offscreen render {@link Texture2D} `target` (for\n * render-to-texture / post-processing), or pass `null` to render to the swapchain (the\n * default). Pair with {@link createRenderTexture2D} at its default format and the renderer's\n * target size. The target's texture must be the engine's swapchain format: sprite pipelines\n * are baked with `engine.format`, so a target of any OTHER format fails WebGPU validation at\n * render-pass begin. A second renderer can then sample that texture (e.g. a fullscreen\n * custom-shader layer) and present it. Renderers registered later run later, so register the\n * offscreen scene pass before the presenting pass.\n */\nexport function setSpriteRendererTarget(sr: SpriteRenderer, target: Texture2D | null): void {\n sr._targetView = target ? target.view : null;\n}\n\n/** Splice the renderer out of its engine's `_renderingContexts`. No-op if not present. */\nexport function unregisterSpriteRenderer(sr: SpriteRenderer): void {\n unregisterRenderingContext(sr._surface, sr);\n}\n\n/**\n * Destroy all GPU resources owned by the renderer, unregister it from the engine, and clear `layers`.\n * Idempotent. To tie disposal to a scene, call\n * `onSceneDispose(scene, () => disposeSpriteRenderer(sr))` after `registerSpriteRenderer` —\n * see the `SpriteRenderer` doc-comment.\n */\nexport function disposeSpriteRenderer(sr: SpriteRenderer): void {\n if (sr._disposed) {\n return;\n }\n unregisterSpriteRenderer(sr);\n sr._disposed = true;\n const disposeCallbacks = sr._disposeCallbacks.slice();\n sr._disposeCallbacks.length = 0;\n for (const dispose of disposeCallbacks) {\n dispose();\n }\n for (const lg of sr._layerGpu.values()) {\n disposeLayerGpu(lg);\n }\n sr._layerGpu.clear();\n sr._visibleBundles.length = 0;\n sr._beforeUpdate.length = 0;\n sr._indexBuffer.destroy();\n resetSpritePipelineCache(sr._pipelineCache);\n sr._layers.length = 0;\n}\n\n/** @internal Test-only accessor for pipeline-cache size. */\nexport function _spriteRendererPipelineCacheSize(sr: SpriteRenderer): number {\n return getSpritePipelineCacheSize(sr._pipelineCache, sr._surface.engine._device);\n}\n"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { buildSpriteRenderable } from './sprite-renderable.js';
3
3
 
4
4
  function addDepthHostedSpriteLayer(scene, layer) {
5
5
  if (layer.depth === "none") {
6
- throw new Error('Sprite2DLayer with depth: "none" must be rendered via createSpriteRenderer, not addDepthHostedSpriteLayer.');
6
+ throw new Error('Depth-hosted sprites require depth != "none".');
7
7
  }
8
8
  addDeferredSceneRenderables(scene, (engine) => {
9
9
  const built = buildSpriteRenderable(engine, layer);
@@ -1 +1 @@
1
- {"version":3,"file":"sprite-scene.js","sources":["../../../src/sprite/sprite-scene.ts"],"sourcesContent":["import type { SceneContext } from \"../scene/scene-core.js\";\nimport { addDeferredSceneRenderables } from \"../scene/scene-core.js\";\nimport type { Sprite2DLayer } from \"./sprite-2d.js\";\nimport { buildSpriteRenderable } from \"./sprite-renderable.js\";\n\n/**\n * Add a depth-hosted Sprite2D layer to a SceneContext via the scene's optional\n * renderable extension hook. Pure HUD layers (`depth: \"none\"`) are rendered by\n * `createSpriteRenderer + registerSpriteRenderer` instead.\n */\nexport function addDepthHostedSpriteLayer(scene: SceneContext, layer: Sprite2DLayer): void {\n if (layer.depth === \"none\") {\n throw new Error('Sprite2DLayer with depth: \"none\" must be rendered via createSpriteRenderer, not addDepthHostedSpriteLayer.');\n }\n addDeferredSceneRenderables(scene, (engine) => {\n const built = buildSpriteRenderable(engine, layer);\n return { renderables: [built.renderable], dispose: built.dispose };\n });\n}\n"],"names":[],"mappings":";;;AAUO,SAAS,yBAAA,CAA0B,OAAqB,KAAA,EAA4B;AACvF,EAAA,IAAI,KAAA,CAAM,UAAU,MAAA,EAAQ;AACxB,IAAA,MAAM,IAAI,MAAM,4GAA4G,CAAA;AAAA,EAChI;AACA,EAAA,2BAAA,CAA4B,KAAA,EAAO,CAAC,MAAA,KAAW;AAC3C,IAAA,MAAM,KAAA,GAAQ,qBAAA,CAAsB,MAAA,EAAQ,KAAK,CAAA;AACjD,IAAA,OAAO,EAAE,aAAa,CAAC,KAAA,CAAM,UAAU,CAAA,EAAG,OAAA,EAAS,MAAM,OAAA,EAAQ;AAAA,EACrE,CAAC,CAAA;AACL;;;;"}
1
+ {"version":3,"file":"sprite-scene.js","sources":["../../../src/sprite/sprite-scene.ts"],"sourcesContent":["import type { SceneContext } from \"../scene/scene-core.js\";\nimport { addDeferredSceneRenderables } from \"../scene/scene-core.js\";\nimport type { Sprite2DLayer } from \"./sprite-2d.js\";\nimport { buildSpriteRenderable } from \"./sprite-renderable.js\";\n\n/**\n * Add a depth-hosted Sprite2D layer to a SceneContext via the scene's optional\n * renderable extension hook. Pure HUD layers (`depth: \"none\"`) are rendered by\n * `createSpriteRenderer + registerSpriteRenderer` instead.\n */\nexport function addDepthHostedSpriteLayer(scene: SceneContext, layer: Sprite2DLayer): void {\n if (layer.depth === \"none\") {\n throw new Error('Depth-hosted sprites require depth != \"none\".');\n }\n addDeferredSceneRenderables(scene, (engine) => {\n const built = buildSpriteRenderable(engine, layer);\n return { renderables: [built.renderable], dispose: built.dispose };\n });\n}\n"],"names":[],"mappings":";;;AAUO,SAAS,yBAAA,CAA0B,OAAqB,KAAA,EAA4B;AACvF,EAAA,IAAI,KAAA,CAAM,UAAU,MAAA,EAAQ;AACxB,IAAA,MAAM,IAAI,MAAM,+CAA+C,CAAA;AAAA,EACnE;AACA,EAAA,2BAAA,CAA4B,KAAA,EAAO,CAAC,MAAA,KAAW;AAC3C,IAAA,MAAM,KAAA,GAAQ,qBAAA,CAAsB,MAAA,EAAQ,KAAK,CAAA;AACjD,IAAA,OAAO,EAAE,aAAa,CAAC,KAAA,CAAM,UAAU,CAAA,EAAG,OAAA,EAAS,MAAM,OAAA,EAAQ;AAAA,EACrE,CAAC,CAAA;AACL;;;;"}
@@ -2,7 +2,7 @@ import { TEXT_INSTANCE_BYTES } from '../text-data.js';
2
2
 
3
3
  const vertSrc = "struct A{mvp:mat4x4<f32>,viewport:vec4<f32>,color:vec4<f32>}@group(0) @binding(0) var<uniform> textU:A;struct B{@location(0) slugCorner:vec2<f32>,@location(1) slugBounds:vec4<f32>,@location(2) slugAnchor:vec4<f32>,@location(3) slugAtlas:vec4<f32>,@location(4) slugBand:vec4<f32>,@location(5) slugColor:vec4<f32>}struct u{@builtin(position) pos:vec4<f32>,@location(0) vTexcoord:vec2<f32>,@location(1) @interpolate(flat) vBanding:vec4<f32>,@location(2) @interpolate(flat) vGlyph:vec4<f32>,@location(3) @interpolate(flat) vColor:vec4<f32>}@vertex fn main(b:B)->u{if (b.slugAnchor.w>0.5){var d:u;d.pos=vec4<f32>(-2.0,-2.0,-2.0,1.0);d.vTexcoord=vec2<f32>(0.0,0.0);d.vBanding=vec4<f32>(0.0);d.vGlyph=vec4<f32>(0.0);d.vColor=vec4<f32>(0.0);return d;}let x=vec2<f32>(step(0.0,b.slugCorner.x),step(0.0,b.slugCorner.y));let j=mix(b.slugBounds.xy,b.slugBounds.zw,x);let f=b.slugAnchor.z;let v=select(0.0,1.0/f,f!=0.0);let g=b.slugAnchor.xy+j*v;let q=b.slugCorner;let p=vec4<f32>(f,0.0,0.0,f);let a=textU.mvp;let n=vec4<f32>(a[0].x,a[1].x,a[2].x,a[3].x);let h=vec4<f32>(a[0].y,a[1].y,a[2].y,a[3].y);let o=vec4<f32>(a[0].w,a[1].w,a[2].w,a[3].w);let m=normalize(q);let e=dot(o.xy,g)+o.w;let i=dot(o.xy,m);let s=(e*dot(n.xy,m)-i*(dot(n.xy,g)+n.w))*textU.viewport.x;let t=(e*dot(h.xy,m)-i*(dot(h.xy,g)+h.w))*textU.viewport.y;let w=e*e;let l=e*i;let r=s*s+t*t;let k=q*(w*(l+sqrt(r))/(r-l*l));let y=g+k;let z=vec2<f32>(j.x+dot(k,p.xy),j.y+dot(k,p.zw));var c:u;c.pos=a*vec4<f32>(y,0.0,1.0);c.vTexcoord=z;c.vBanding=b.slugBand;c.vGlyph=b.slugAtlas;c.vColor=vec4<f32>(b.slugColor.rgb,b.slugColor.a*textU.color.a);return c;}";
4
4
 
5
- const fragSrc = "@group(0) @binding(1) var s:texture_2d<f32>;@group(0) @binding(2) var w:texture_2d<f32>;struct ra{@location(0) vTexcoord:vec2<f32>,@location(1) @interpolate(flat) vBanding:vec4<f32>,@location(2) @interpolate(flat) vGlyph:vec4<f32>,@location(3) @interpolate(flat) vColor:vec4<f32>,@builtin(front_facing) frontFacing:bool}fn I(ka:f32,la:f32,ga:f32)->i32{let ja:i32=select(0,1,ka<0.0);let fa:i32=select(0,2,la<0.0);let ma:i32=select(0,4,ga<0.0);let na=u32(ja+fa+ma);return (0x2E74>>na)&0x0101;}fn da(a:vec4<f32>,R:vec2<f32>)->vec2<f32>{let n=vec2<f32>(a.x-a.z*2.0+R.x,a.y-a.w*2.0+R.y);let f=vec2<f32>(a.x-a.z,a.y-a.w);let N=1.0/n.y;let M=0.5/f.y;let T=sqrt(max(f.y*f.y-n.y*a.y,0.0));var H=(f.y-T)*N;var G=(f.y+T)*N;if (abs(n.y)<=max(abs(f.y),abs(a.y))*1.0e-4){H=a.y*M;G=a.y*M;}return vec2<f32>((n.x*H-f.x*2.0)*H+a.x,(n.x*G-f.x*2.0)*G+a.x);}fn ca(b:vec4<f32>,U:vec2<f32>)->vec2<f32>{let m=vec2<f32>(b.x-b.z*2.0+U.x,b.y-b.w*2.0+U.y);let c=vec2<f32>(b.x-b.z,b.y-b.w);let ba=1.0/m.x;let Z=0.5/c.x;let X=sqrt(max(c.x*c.x-m.x*b.x,0.0));var C=(c.x-X)*ba;var D=(c.x+X)*ba;if (abs(m.x)<=max(abs(c.x),abs(b.x))*1.0e-4){C=b.x*Z;D=b.x*Z;}return vec2<f32>((m.y*C-c.y*2.0)*C+b.y,(m.y*D-c.y*2.0)*D+b.y);}fn J(V:vec2<i32>,qa:i32)->vec2<i32>{var i=vec2<i32>(V.x+qa,V.y);i.y=i.y+(i.x>>12u);i.x=i.x&4095;return i;}@fragment fn main(d:ra)->@location(0) vec4<f32>{if (!d.frontFacing){discard;}let e=d.vTexcoord;let pa=fwidth(e);let t=1.0/pa;let j=vec2<i32>(i32(d.vGlyph.x+0.5),i32(d.vGlyph.y+0.5));let K=vec2<i32>(i32(d.vGlyph.z+0.5),i32(d.vGlyph.w+0.5));let P=d.vBanding;let Q=clamp(vec2<i32>(e*P.xy+P.zw),vec2<i32>(0,0),K);var k:f32=0.0;var l:f32=0.0;let L=textureLoad(w,vec2<i32>(j.x+Q.y,j.y),0);let ea=i32(L.x+0.5);let oa=i32(L.y+0.5);let W=J(j,oa);for(var q:i32=0;q<ea;q=q+1){let Y=textureLoad(w,vec2<i32>(W.x+q,W.y),0);let x=vec2<i32>(i32(Y.x+0.5),i32(Y.y+0.5));let p=textureLoad(s,x,0)-vec4<f32>(e,e);let A=textureLoad(s,vec2<i32>(x.x+1,x.y),0).xy-e;if (max(max(p.x,p.z),A.x)*t.x<-0.5){break;}let z=I(p.y,p.w,A.y);if (z!=0){let r=da(p,A)*t.x;if ((z&1)!=0){k=k+clamp(r.x+0.5,0.0,1.0);l=max(l,clamp(1.0-abs(r.x)*2.0,0.0,1.0));}if (z>1){k=k-clamp(r.y+0.5,0.0,1.0);l=max(l,clamp(1.0-abs(r.y)*2.0,0.0,1.0));}}}var h:f32=0.0;var g:f32=0.0;let O=textureLoad(w,vec2<i32>(j.x+K.y+1+Q.x,j.y),0);let ha=i32(O.x+0.5);let ia=i32(O.y+0.5);let S=J(j,ia);for(var u:i32=0;u<ha;u=u+1){let aa=textureLoad(w,vec2<i32>(S.x+u,S.y),0);let B=vec2<i32>(i32(aa.x+0.5),i32(aa.y+0.5));let o=textureLoad(s,B,0)-vec4<f32>(e,e);let E=textureLoad(s,vec2<i32>(B.x+1,B.y),0).xy-e;if (max(max(o.y,o.w),E.y)*t.y<-0.5){break;}let y=I(o.x,o.z,E.x);if (y!=0){let v=ca(o,E)*t.y;if ((y&1)!=0){h=h-clamp(v.x+0.5,0.0,1.0);g=max(g,clamp(1.0-abs(v.x)*2.0,0.0,1.0));}if (y>1){h=h+clamp(v.y+0.5,0.0,1.0);g=max(g,clamp(1.0-abs(v.y)*2.0,0.0,1.0));}}}var F=max(abs(k*l+h*g)/max(l+g,1.0/65536.0),min(abs(k),abs(h)));F=clamp(F,0.0,1.0);return d.vColor*F;}";
5
+ const fragSrc = "@group(0) @binding(1) var t:texture_2d<f32>;@group(0) @binding(2) var u:texture_2d<f32>;struct sa{mvp:mat4x4<f32>,viewport:vec4<f32>,color:vec4<f32>}@group(0) @binding(0) var<uniform> textU:sa;struct ra{@location(0) vTexcoord:vec2<f32>,@location(1) @interpolate(flat) vBanding:vec4<f32>,@location(2) @interpolate(flat) vGlyph:vec4<f32>,@location(3) @interpolate(flat) vColor:vec4<f32>,@builtin(front_facing) frontFacing:bool}fn I(qa:f32,pa:f32,oa:f32)->i32{let na:i32=select(0,1,qa<0.0);let la:i32=select(0,2,pa<0.0);let ga:i32=select(0,4,oa<0.0);let fa=u32(na+la+ga);return (0x2E74>>fa)&0x0101;}fn ca(b:vec4<f32>,aa:vec2<f32>)->vec2<f32>{let q=vec2<f32>(b.x-b.z*2.0+aa.x,b.y-b.w*2.0+aa.y);let d=vec2<f32>(b.x-b.z,b.y-b.w);let K=1.0/q.y;let ba=0.5/d.y;let L=sqrt(max(d.y*d.y-q.y*b.y,0.0));var H=(d.y-L)*K;var G=(d.y+L)*K;if (abs(q.y)<=max(abs(d.y),abs(b.y))*1.0e-4){H=b.y*ba;G=b.y*ba;}return vec2<f32>((q.x*H-d.x*2.0)*H+b.x,(q.x*G-d.x*2.0)*G+b.x);}fn da(a:vec4<f32>,Y:vec2<f32>)->vec2<f32>{let n=vec2<f32>(a.x-a.z*2.0+Y.x,a.y-a.w*2.0+Y.y);let c=vec2<f32>(a.x-a.z,a.y-a.w);let T=1.0/n.x;let S=0.5/c.x;let O=sqrt(max(c.x*c.x-n.x*a.x,0.0));var D=(c.x-O)*T;var E=(c.x+O)*T;if (abs(n.x)<=max(abs(c.x),abs(a.x))*1.0e-4){D=a.x*S;E=a.x*S;}return vec2<f32>((n.y*D-c.y*2.0)*D+a.y,(n.y*E-c.y*2.0)*E+a.y);}fn J(N:vec2<i32>,ka:i32)->vec2<i32>{var l=vec2<i32>(N.x+ka,N.y);l.y=l.y+(l.x>>12u);l.x=l.x&4095;return l;}@fragment fn main(f:ra)->@location(0) vec4<f32>{if (!f.frontFacing){discard;}let e=f.vTexcoord;let ma=fwidth(e);let w=1.0/ma;let g=vec2<i32>(i32(f.vGlyph.x+0.5),i32(f.vGlyph.y+0.5));let P=vec2<i32>(i32(f.vGlyph.z+0.5),i32(f.vGlyph.w+0.5));let Q=f.vBanding;let R=clamp(vec2<i32>(e*Q.xy+Q.zw),vec2<i32>(0,0),P);var i:f32=0.0;var h:f32=0.0;let U=textureLoad(u,vec2<i32>(g.x+R.y,g.y),0);let ea=i32(U.x+0.5);let ha=i32(U.y+0.5);let X=J(g,ha);for(var s:i32=0;s<ea;s=s+1){let Z=textureLoad(u,vec2<i32>(X.x+s,X.y),0);let F=vec2<i32>(i32(Z.x+0.5),i32(Z.y+0.5));let o=textureLoad(t,F,0)-vec4<f32>(e,e);let A=textureLoad(t,vec2<i32>(F.x+1,F.y),0).xy-e;if (max(max(o.x,o.z),A.x)*w.x<-0.5){break;}let z=I(o.y,o.w,A.y);if (z!=0){let x=ca(o,A)*w.x;if ((z&1)!=0){i=i+clamp(x.x+0.5,0.0,1.0);h=max(h,clamp(1.0-abs(x.x)*2.0,0.0,1.0));}if (z>1){i=i-clamp(x.y+0.5,0.0,1.0);h=max(h,clamp(1.0-abs(x.y)*2.0,0.0,1.0));}}}var k:f32=0.0;var j:f32=0.0;let W=textureLoad(u,vec2<i32>(g.x+P.y+1+R.x,g.y),0);let ia=i32(W.x+0.5);let ja=i32(W.y+0.5);let V=J(g,ja);for(var r:i32=0;r<ia;r=r+1){let M=textureLoad(u,vec2<i32>(V.x+r,V.y),0);let y=vec2<i32>(i32(M.x+0.5),i32(M.y+0.5));let m=textureLoad(t,y,0)-vec4<f32>(e,e);let B=textureLoad(t,vec2<i32>(y.x+1,y.y),0).xy-e;if (max(max(m.y,m.w),B.y)*w.y<-0.5){break;}let C=I(m.x,m.z,B.x);if (C!=0){let v=da(m,B)*w.y;if ((C&1)!=0){k=k-clamp(v.x+0.5,0.0,1.0);j=max(j,clamp(1.0-abs(v.x)*2.0,0.0,1.0));}if (C>1){k=k+clamp(v.y+0.5,0.0,1.0);j=max(j,clamp(1.0-abs(v.y)*2.0,0.0,1.0));}}}var p=max(abs(i*h+k*j)/max(h+j,1.0/65536.0),min(abs(i),abs(k)));p=clamp(p,0.0,1.0);p=pow(p,textU.color.x);return f.vColor*p;}";
6
6
 
7
7
  let _cache = null;
8
8
  function clearTextPipelineCache(engine) {
@@ -1 +1 @@
1
- {"version":3,"file":"text-pipeline.js","sources":["../../../../src/text/shaders/slug.vert.wgsl?raw","../../../../src/text/shaders/slug.frag.wgsl?raw","../../../../src/text/_gpu/text-pipeline.ts"],"sourcesContent":["export default \"// Slug GPU Font — Vertex Shader (WGSL, instanced layout).\\n// Direct port of Eric Lengyel's Slug dilation (see https://github.com/EricLengyel/Slug\\n// and Babylon.js-3/packages/dev/addons/src/msdfText/shadersWGSL/slug.vertex.fx).\\n// Per-vertex: corner sign (-1/+1 on each axis) — drives both the quad corner\\n// expansion and the dilation normal direction.\\n// Per-instance: bounds (em-space), anchor (object-space) + invScale, atlas locs, band transform.\\n\\nstruct TextU {\\n mvp: mat4x4<f32>,\\n viewport: vec4<f32>,\\n // Whole-draw opacity in .a (rgb unused, always 1). Per-glyph color is the slugColor attribute.\\n color: vec4<f32>,\\n};\\n@group(0) @binding(0) var<uniform> textU: TextU;\\n\\nstruct VIn {\\n @location(0) slugCorner: vec2<f32>,\\n @location(1) slugBounds: vec4<f32>,\\n @location(2) slugAnchor: vec4<f32>,\\n @location(3) slugAtlas: vec4<f32>,\\n @location(4) slugBand: vec4<f32>,\\n @location(5) slugColor: vec4<f32>,\\n};\\n\\nstruct VOut {\\n @builtin(position) pos: vec4<f32>,\\n @location(0) vTexcoord: vec2<f32>,\\n @location(1) @interpolate(flat) vBanding: vec4<f32>,\\n @location(2) @interpolate(flat) vGlyph: vec4<f32>,\\n @location(3) @interpolate(flat) vColor: vec4<f32>,\\n};\\n\\n@vertex\\nfn main(in: VIn) -> VOut {\\n // Dead-slot sentinel: slot allocator marks freed slots by setting slugAnchor.w = 1.0\\n // (live slots always have it as 0.0). Emit a clip-space point at -2 (outside the unit\\n // cube) so all 6 vertices of the quad collapse to the same off-screen position and the\\n // rasterizer culls the resulting zero-area triangles cheaply.\\n if (in.slugAnchor.w > 0.5) {\\n var dead: VOut;\\n dead.pos = vec4<f32>(-2.0, -2.0, -2.0, 1.0);\\n dead.vTexcoord = vec2<f32>(0.0, 0.0);\\n dead.vBanding = vec4<f32>(0.0);\\n dead.vGlyph = vec4<f32>(0.0);\\n dead.vColor = vec4<f32>(0.0);\\n return dead;\\n }\\n\\n // Reconstruct per-vertex data from the shared corner quad + per-instance fields.\\n // Reference shader had: pos (object-space xy), normal (dilation direction xy),\\n // tex (em-space xy), invScale, MVP matrix.\\n let isMax = vec2<f32>(step(0.0, in.slugCorner.x), step(0.0, in.slugCorner.y));\\n let tex = mix(in.slugBounds.xy, in.slugBounds.zw, isMax);\\n let invScale = in.slugAnchor.z;\\n let scale = select(0.0, 1.0 / invScale, invScale != 0.0);\\n let pos = in.slugAnchor.xy + tex * scale;\\n let normal = in.slugCorner;\\n let jac = vec4<f32>(invScale, 0.0, 0.0, invScale);\\n\\n let mvp = textU.mvp;\\n\\n // Extract MVP matrix rows from column-major storage.\\n let row0 = vec4<f32>(mvp[0].x, mvp[1].x, mvp[2].x, mvp[3].x);\\n let row1 = vec4<f32>(mvp[0].y, mvp[1].y, mvp[2].y, mvp[3].y);\\n let row3 = vec4<f32>(mvp[0].w, mvp[1].w, mvp[2].w, mvp[3].w);\\n\\n // Dynamic dilation (SlugDilate) — verbatim from the reference shader.\\n let n = normalize(normal);\\n let s = dot(row3.xy, pos) + row3.w;\\n let t_val = dot(row3.xy, n);\\n\\n let u = (s * dot(row0.xy, n) - t_val * (dot(row0.xy, pos) + row0.w)) * textU.viewport.x;\\n let v = (s * dot(row1.xy, n) - t_val * (dot(row1.xy, pos) + row1.w)) * textU.viewport.y;\\n\\n let s2 = s * s;\\n let st = s * t_val;\\n let uv = u * u + v * v;\\n let d = normal * (s2 * (st + sqrt(uv)) / (uv - st * st));\\n\\n let dilatedPos = pos + d;\\n let dilatedTex = vec2<f32>(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\\n\\n var out: VOut;\\n out.pos = mvp * vec4<f32>(dilatedPos, 0.0, 1.0);\\n out.vTexcoord = dilatedTex;\\n out.vBanding = in.slugBand;\\n out.vGlyph = in.slugAtlas;\\n // Color comes entirely from the per-glyph instance attribute; the uniform contributes\\n // only a whole-draw opacity multiply (textU.color is always (1,1,1,opacity)).\\n out.vColor = vec4<f32>(in.slugColor.rgb, in.slugColor.a * textU.color.a);\\n return out;\\n}\\n\"","export default \"// Slug GPU Font — Fragment Shader (WGSL). Per-pixel coverage from quadratic Bézier bands.\\n\\n@group(0) @binding(1) var curveTex: texture_2d<f32>;\\n@group(0) @binding(2) var bandTex: texture_2d<f32>;\\n\\nstruct FIn {\\n @location(0) vTexcoord: vec2<f32>,\\n @location(1) @interpolate(flat) vBanding: vec4<f32>,\\n @location(2) @interpolate(flat) vGlyph: vec4<f32>,\\n @location(3) @interpolate(flat) vColor: vec4<f32>,\\n @builtin(front_facing) frontFacing: bool,\\n};\\n\\nfn calcRootCode(y1: f32, y2: f32, y3: f32) -> i32 {\\n let i1: i32 = select(0, 1, y1 < 0.0);\\n let i2: i32 = select(0, 2, y2 < 0.0);\\n let i3: i32 = select(0, 4, y3 < 0.0);\\n let shift = u32(i1 + i2 + i3);\\n return (0x2E74 >> shift) & 0x0101;\\n}\\n\\nfn solveHorizPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\\n let a = vec2<f32>(p12.x - p12.z * 2.0 + p3.x, p12.y - p12.w * 2.0 + p3.y);\\n let b = vec2<f32>(p12.x - p12.z, p12.y - p12.w);\\n let ra = 1.0 / a.y;\\n let rb = 0.5 / b.y;\\n let disc = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\\n var t1 = (b.y - disc) * ra;\\n var t2 = (b.y + disc) * ra;\\n if (abs(a.y) <= max(abs(b.y), abs(p12.y)) * 1.0e-4) {\\n t1 = p12.y * rb;\\n t2 = p12.y * rb;\\n }\\n return vec2<f32>((a.x * t1 - b.x * 2.0) * t1 + p12.x, (a.x * t2 - b.x * 2.0) * t2 + p12.x);\\n}\\n\\nfn solveVertPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\\n let a = vec2<f32>(p12.x - p12.z * 2.0 + p3.x, p12.y - p12.w * 2.0 + p3.y);\\n let b = vec2<f32>(p12.x - p12.z, p12.y - p12.w);\\n let ra = 1.0 / a.x;\\n let rb = 0.5 / b.x;\\n let disc = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\\n var t1 = (b.x - disc) * ra;\\n var t2 = (b.x + disc) * ra;\\n if (abs(a.x) <= max(abs(b.x), abs(p12.x)) * 1.0e-4) {\\n t1 = p12.x * rb;\\n t2 = p12.x * rb;\\n }\\n return vec2<f32>((a.y * t1 - b.y * 2.0) * t1 + p12.y, (a.y * t2 - b.y * 2.0) * t2 + p12.y);\\n}\\n\\nfn calcBandLoc(glyphLoc: vec2<i32>, offset: i32) -> vec2<i32> {\\n var bandLoc = vec2<i32>(glyphLoc.x + offset, glyphLoc.y);\\n bandLoc.y = bandLoc.y + (bandLoc.x >> 12u);\\n bandLoc.x = bandLoc.x & 4095;\\n return bandLoc;\\n}\\n\\n@fragment\\nfn main(in: FIn) -> @location(0) vec4<f32> {\\n // Cull back-facing fragments — text quads behave like one-sided sheets of\\n // paper. Without this, the back side of a rotated quad shows geometrically\\n // correct mirror-image text. Callers wanting double-sided readable text\\n // add a second TextRenderable rotated 180° (or use the doubleSided helper).\\n if (!in.frontFacing) {\\n discard;\\n }\\n let renderCoord = in.vTexcoord;\\n let emsPerPixel = fwidth(renderCoord);\\n let pixelsPerEm = 1.0 / emsPerPixel;\\n\\n let glyphLoc = vec2<i32>(i32(in.vGlyph.x + 0.5), i32(in.vGlyph.y + 0.5));\\n let bandMax = vec2<i32>(i32(in.vGlyph.z + 0.5), i32(in.vGlyph.w + 0.5));\\n let bandTransform = in.vBanding;\\n\\n let bandIndex = clamp(\\n vec2<i32>(renderCoord * bandTransform.xy + bandTransform.zw),\\n vec2<i32>(0, 0),\\n bandMax\\n );\\n\\n var xcov: f32 = 0.0;\\n var xwgt: f32 = 0.0;\\n let hbandRaw = textureLoad(bandTex, vec2<i32>(glyphLoc.x + bandIndex.y, glyphLoc.y), 0);\\n let hbandCount = i32(hbandRaw.x + 0.5);\\n let hbandOffset = i32(hbandRaw.y + 0.5);\\n let hbandLoc = calcBandLoc(glyphLoc, hbandOffset);\\n for (var ci: i32 = 0; ci < hbandCount; ci = ci + 1) {\\n let locRaw = textureLoad(bandTex, vec2<i32>(hbandLoc.x + ci, hbandLoc.y), 0);\\n let curveLoc = vec2<i32>(i32(locRaw.x + 0.5), i32(locRaw.y + 0.5));\\n let p12 = textureLoad(curveTex, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\\n let p3 = textureLoad(curveTex, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) { break; }\\n let code = calcRootCode(p12.y, p12.w, p3.y);\\n if (code != 0) {\\n let r = solveHorizPoly(p12, p3) * pixelsPerEm.x;\\n if ((code & 1) != 0) {\\n xcov = xcov + clamp(r.x + 0.5, 0.0, 1.0);\\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\\n }\\n if (code > 1) {\\n xcov = xcov - clamp(r.y + 0.5, 0.0, 1.0);\\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\\n }\\n }\\n }\\n\\n var ycov: f32 = 0.0;\\n var ywgt: f32 = 0.0;\\n let vbandRaw = textureLoad(bandTex, vec2<i32>(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0);\\n let vbandCount = i32(vbandRaw.x + 0.5);\\n let vbandOffset = i32(vbandRaw.y + 0.5);\\n let vbandLoc = calcBandLoc(glyphLoc, vbandOffset);\\n for (var ci: i32 = 0; ci < vbandCount; ci = ci + 1) {\\n let locRaw = textureLoad(bandTex, vec2<i32>(vbandLoc.x + ci, vbandLoc.y), 0);\\n let curveLoc = vec2<i32>(i32(locRaw.x + 0.5), i32(locRaw.y + 0.5));\\n let p12 = textureLoad(curveTex, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\\n let p3 = textureLoad(curveTex, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) { break; }\\n let code = calcRootCode(p12.x, p12.z, p3.x);\\n if (code != 0) {\\n let r = solveVertPoly(p12, p3) * pixelsPerEm.y;\\n if ((code & 1) != 0) {\\n ycov = ycov - clamp(r.x + 0.5, 0.0, 1.0);\\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\\n }\\n if (code > 1) {\\n ycov = ycov + clamp(r.y + 0.5, 0.0, 1.0);\\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\\n }\\n }\\n }\\n\\n var coverage = max(\\n abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0),\\n min(abs(xcov), abs(ycov))\\n );\\n coverage = clamp(coverage, 0.0, 1.0);\\n return in.vColor * coverage;\\n}\\n\"","/** Owns the text render pipeline + bind-group layouts. Lazy per-device cache. */\n\nimport type { EngineContext } from \"../../engine/engine.js\";\nimport vertSrc from \"../shaders/slug.vert.wgsl?raw\";\nimport fragSrc from \"../shaders/slug.frag.wgsl?raw\";\nimport { TEXT_INSTANCE_BYTES } from \"../text-data.js\";\n\nexport interface TextPipelineDeviceCache {\n bindGroupLayout: GPUBindGroupLayout;\n vertModule: GPUShaderModule;\n fragModule: GPUShaderModule;\n quadVertexBuffer: GPUBuffer;\n pipelines: Map<string, GPURenderPipeline>;\n}\n\nlet _cache: WeakMap<GPUDevice, TextPipelineDeviceCache> | null = null;\n\n/** Clear the text pipeline cache for a device, releasing cache-held refs. */\nexport function clearTextPipelineCache(engine: EngineContext): void {\n _cache?.delete(engine._device);\n}\n\n/** Shared 4-vertex unit quad: corner signs (-1,-1), (1,-1), (1,1), (-1,1). */\nconst QUAD_CORNERS = [-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1] as const;\n\nfunction getOrCreateDeviceCache(engine: EngineContext): TextPipelineDeviceCache {\n _cache ??= new WeakMap();\n let cache = _cache.get(engine._device);\n if (cache) {\n return cache;\n }\n const device = engine._device;\n const bindGroupLayout = device.createBindGroupLayout({\n label: \"text-bind-group-layout\",\n entries: [\n { binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: \"unfilterable-float\" } },\n ],\n });\n const vertModule = device.createShaderModule({ label: \"text-vert\", code: vertSrc });\n const fragModule = device.createShaderModule({ label: \"text-frag\", code: fragSrc });\n const corners = new Float32Array(QUAD_CORNERS);\n const quadVertexBuffer = device.createBuffer({\n label: \"text-quad-corners\",\n size: corners.byteLength,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n mappedAtCreation: true,\n });\n new Float32Array(quadVertexBuffer.getMappedRange()).set(corners);\n quadVertexBuffer.unmap();\n\n cache = { bindGroupLayout, vertModule, fragModule, quadVertexBuffer, pipelines: new Map() };\n _cache.set(device, cache);\n return cache;\n}\n\nfunction pipelineKey(format: GPUTextureFormat, sampleCount: number, depthStencilFormat: GPUTextureFormat | null, depthWrite: boolean): string {\n return format + \":\" + sampleCount + \":\" + (depthStencilFormat ?? \"-\") + \":\" + (depthWrite ? \"w\" : \"r\");\n}\n\nexport function getOrCreateTextPipeline(\n engine: EngineContext,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n depthStencilFormat: GPUTextureFormat | null,\n depthWrite: boolean\n): { pipeline: GPURenderPipeline; cache: TextPipelineDeviceCache } {\n const cache = getOrCreateDeviceCache(engine);\n const key = pipelineKey(format, sampleCount, depthStencilFormat, depthWrite);\n let pipeline = cache.pipelines.get(key);\n if (pipeline) {\n return { pipeline, cache };\n }\n const device = engine._device;\n const descriptor: GPURenderPipelineDescriptor = {\n label: \"text-pipeline\",\n layout: device.createPipelineLayout({ bindGroupLayouts: [cache.bindGroupLayout] }),\n vertex: {\n module: cache.vertModule,\n entryPoint: \"main\",\n buffers: [\n {\n arrayStride: 8,\n stepMode: \"vertex\",\n attributes: [{ shaderLocation: 0, offset: 0, format: \"float32x2\" }],\n },\n {\n arrayStride: TEXT_INSTANCE_BYTES,\n stepMode: \"instance\",\n attributes: [\n { shaderLocation: 1, offset: 0, format: \"float32x4\" },\n { shaderLocation: 2, offset: 16, format: \"float32x4\" },\n { shaderLocation: 3, offset: 32, format: \"float32x4\" },\n { shaderLocation: 4, offset: 48, format: \"float32x4\" },\n { shaderLocation: 5, offset: 64, format: \"float32x4\" },\n ],\n },\n ],\n },\n fragment: {\n module: cache.fragModule,\n entryPoint: \"main\",\n targets: [\n {\n format,\n blend: {\n // Premultiplied-alpha blend. The Slug fragment shader outputs\n // `vColor * coverage` — i.e. RGB already premultiplied by coverage\n // (and alpha = coverage) — so the color blend must use srcFactor\n // `one` (NOT `src-alpha`, which would multiply by coverage a second\n // time and render anti-aliased edges as coverage², too dark). The\n // alpha channel already uses `one` for the same reason.\n color: { srcFactor: \"one\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\n alpha: { srcFactor: \"one\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\n },\n },\n ],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\", frontFace: \"ccw\" },\n multisample: { count: sampleCount },\n };\n if (depthStencilFormat) {\n descriptor.depthStencil = {\n format: depthStencilFormat,\n depthCompare: \"greater-equal\",\n depthWriteEnabled: depthWrite,\n };\n }\n pipeline = device.createRenderPipeline(descriptor);\n cache.pipelines.set(key, pipeline);\n return { pipeline, cache };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"text-pipeline.js","sources":["../../../../src/text/shaders/slug.vert.wgsl?raw","../../../../src/text/shaders/slug.frag.wgsl?raw","../../../../src/text/_gpu/text-pipeline.ts"],"sourcesContent":["export default \"// Slug GPU Font — Vertex Shader (WGSL, instanced layout).\\n// Direct port of Eric Lengyel's Slug dilation (see https://github.com/EricLengyel/Slug\\n// and Babylon.js-3/packages/dev/addons/src/msdfText/shadersWGSL/slug.vertex.fx).\\n// Per-vertex: corner sign (-1/+1 on each axis) — drives both the quad corner\\n// expansion and the dilation normal direction.\\n// Per-instance: bounds (em-space), anchor (object-space) + invScale, atlas locs, band transform.\\n\\nstruct TextU {\\n mvp: mat4x4<f32>,\\n viewport: vec4<f32>,\\n // Whole-draw opacity in .a (rgb unused, always 1). Per-glyph color is the slugColor attribute.\\n color: vec4<f32>,\\n};\\n@group(0) @binding(0) var<uniform> textU: TextU;\\n\\nstruct VIn {\\n @location(0) slugCorner: vec2<f32>,\\n @location(1) slugBounds: vec4<f32>,\\n @location(2) slugAnchor: vec4<f32>,\\n @location(3) slugAtlas: vec4<f32>,\\n @location(4) slugBand: vec4<f32>,\\n @location(5) slugColor: vec4<f32>,\\n};\\n\\nstruct VOut {\\n @builtin(position) pos: vec4<f32>,\\n @location(0) vTexcoord: vec2<f32>,\\n @location(1) @interpolate(flat) vBanding: vec4<f32>,\\n @location(2) @interpolate(flat) vGlyph: vec4<f32>,\\n @location(3) @interpolate(flat) vColor: vec4<f32>,\\n};\\n\\n@vertex\\nfn main(in: VIn) -> VOut {\\n // Dead-slot sentinel: slot allocator marks freed slots by setting slugAnchor.w = 1.0\\n // (live slots always have it as 0.0). Emit a clip-space point at -2 (outside the unit\\n // cube) so all 6 vertices of the quad collapse to the same off-screen position and the\\n // rasterizer culls the resulting zero-area triangles cheaply.\\n if (in.slugAnchor.w > 0.5) {\\n var dead: VOut;\\n dead.pos = vec4<f32>(-2.0, -2.0, -2.0, 1.0);\\n dead.vTexcoord = vec2<f32>(0.0, 0.0);\\n dead.vBanding = vec4<f32>(0.0);\\n dead.vGlyph = vec4<f32>(0.0);\\n dead.vColor = vec4<f32>(0.0);\\n return dead;\\n }\\n\\n // Reconstruct per-vertex data from the shared corner quad + per-instance fields.\\n // Reference shader had: pos (object-space xy), normal (dilation direction xy),\\n // tex (em-space xy), invScale, MVP matrix.\\n let isMax = vec2<f32>(step(0.0, in.slugCorner.x), step(0.0, in.slugCorner.y));\\n let tex = mix(in.slugBounds.xy, in.slugBounds.zw, isMax);\\n let invScale = in.slugAnchor.z;\\n let scale = select(0.0, 1.0 / invScale, invScale != 0.0);\\n let pos = in.slugAnchor.xy + tex * scale;\\n let normal = in.slugCorner;\\n let jac = vec4<f32>(invScale, 0.0, 0.0, invScale);\\n\\n let mvp = textU.mvp;\\n\\n // Extract MVP matrix rows from column-major storage.\\n let row0 = vec4<f32>(mvp[0].x, mvp[1].x, mvp[2].x, mvp[3].x);\\n let row1 = vec4<f32>(mvp[0].y, mvp[1].y, mvp[2].y, mvp[3].y);\\n let row3 = vec4<f32>(mvp[0].w, mvp[1].w, mvp[2].w, mvp[3].w);\\n\\n // Dynamic dilation (SlugDilate) — verbatim from the reference shader.\\n let n = normalize(normal);\\n let s = dot(row3.xy, pos) + row3.w;\\n let t_val = dot(row3.xy, n);\\n\\n let u = (s * dot(row0.xy, n) - t_val * (dot(row0.xy, pos) + row0.w)) * textU.viewport.x;\\n let v = (s * dot(row1.xy, n) - t_val * (dot(row1.xy, pos) + row1.w)) * textU.viewport.y;\\n\\n let s2 = s * s;\\n let st = s * t_val;\\n let uv = u * u + v * v;\\n let d = normal * (s2 * (st + sqrt(uv)) / (uv - st * st));\\n\\n let dilatedPos = pos + d;\\n let dilatedTex = vec2<f32>(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\\n\\n var out: VOut;\\n out.pos = mvp * vec4<f32>(dilatedPos, 0.0, 1.0);\\n out.vTexcoord = dilatedTex;\\n out.vBanding = in.slugBand;\\n out.vGlyph = in.slugAtlas;\\n // Color comes entirely from the per-glyph instance attribute; the uniform contributes\\n // only a whole-draw opacity multiply (textU.color is always (1,1,1,opacity)).\\n out.vColor = vec4<f32>(in.slugColor.rgb, in.slugColor.a * textU.color.a);\\n return out;\\n}\\n\"","export default \"// Slug GPU Font — Fragment Shader (WGSL). Per-pixel coverage from quadratic Bézier bands.\\n\\n@group(0) @binding(1) var curveTex: texture_2d<f32>;\\n@group(0) @binding(2) var bandTex: texture_2d<f32>;\\n\\nstruct TextU {\\n mvp: mat4x4<f32>,\\n viewport: vec4<f32>,\\n // .a = whole-draw opacity. .x = coverage-gamma reciprocal (1/coverageGamma) applied to\\n // anti-aliased edge coverage below. .yz unused.\\n color: vec4<f32>,\\n};\\n@group(0) @binding(0) var<uniform> textU: TextU;\\n\\nstruct FIn {\\n @location(0) vTexcoord: vec2<f32>,\\n @location(1) @interpolate(flat) vBanding: vec4<f32>,\\n @location(2) @interpolate(flat) vGlyph: vec4<f32>,\\n @location(3) @interpolate(flat) vColor: vec4<f32>,\\n @builtin(front_facing) frontFacing: bool,\\n};\\n\\nfn calcRootCode(y1: f32, y2: f32, y3: f32) -> i32 {\\n let i1: i32 = select(0, 1, y1 < 0.0);\\n let i2: i32 = select(0, 2, y2 < 0.0);\\n let i3: i32 = select(0, 4, y3 < 0.0);\\n let shift = u32(i1 + i2 + i3);\\n return (0x2E74 >> shift) & 0x0101;\\n}\\n\\nfn solveHorizPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\\n let a = vec2<f32>(p12.x - p12.z * 2.0 + p3.x, p12.y - p12.w * 2.0 + p3.y);\\n let b = vec2<f32>(p12.x - p12.z, p12.y - p12.w);\\n let ra = 1.0 / a.y;\\n let rb = 0.5 / b.y;\\n let disc = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\\n var t1 = (b.y - disc) * ra;\\n var t2 = (b.y + disc) * ra;\\n if (abs(a.y) <= max(abs(b.y), abs(p12.y)) * 1.0e-4) {\\n t1 = p12.y * rb;\\n t2 = p12.y * rb;\\n }\\n return vec2<f32>((a.x * t1 - b.x * 2.0) * t1 + p12.x, (a.x * t2 - b.x * 2.0) * t2 + p12.x);\\n}\\n\\nfn solveVertPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\\n let a = vec2<f32>(p12.x - p12.z * 2.0 + p3.x, p12.y - p12.w * 2.0 + p3.y);\\n let b = vec2<f32>(p12.x - p12.z, p12.y - p12.w);\\n let ra = 1.0 / a.x;\\n let rb = 0.5 / b.x;\\n let disc = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\\n var t1 = (b.x - disc) * ra;\\n var t2 = (b.x + disc) * ra;\\n if (abs(a.x) <= max(abs(b.x), abs(p12.x)) * 1.0e-4) {\\n t1 = p12.x * rb;\\n t2 = p12.x * rb;\\n }\\n return vec2<f32>((a.y * t1 - b.y * 2.0) * t1 + p12.y, (a.y * t2 - b.y * 2.0) * t2 + p12.y);\\n}\\n\\nfn calcBandLoc(glyphLoc: vec2<i32>, offset: i32) -> vec2<i32> {\\n var bandLoc = vec2<i32>(glyphLoc.x + offset, glyphLoc.y);\\n bandLoc.y = bandLoc.y + (bandLoc.x >> 12u);\\n bandLoc.x = bandLoc.x & 4095;\\n return bandLoc;\\n}\\n\\n@fragment\\nfn main(in: FIn) -> @location(0) vec4<f32> {\\n // Cull back-facing fragments — text quads behave like one-sided sheets of\\n // paper. Without this, the back side of a rotated quad shows geometrically\\n // correct mirror-image text. Callers wanting double-sided readable text\\n // add a second TextRenderable rotated 180° (or use the doubleSided helper).\\n if (!in.frontFacing) {\\n discard;\\n }\\n let renderCoord = in.vTexcoord;\\n let emsPerPixel = fwidth(renderCoord);\\n let pixelsPerEm = 1.0 / emsPerPixel;\\n\\n let glyphLoc = vec2<i32>(i32(in.vGlyph.x + 0.5), i32(in.vGlyph.y + 0.5));\\n let bandMax = vec2<i32>(i32(in.vGlyph.z + 0.5), i32(in.vGlyph.w + 0.5));\\n let bandTransform = in.vBanding;\\n\\n let bandIndex = clamp(\\n vec2<i32>(renderCoord * bandTransform.xy + bandTransform.zw),\\n vec2<i32>(0, 0),\\n bandMax\\n );\\n\\n var xcov: f32 = 0.0;\\n var xwgt: f32 = 0.0;\\n let hbandRaw = textureLoad(bandTex, vec2<i32>(glyphLoc.x + bandIndex.y, glyphLoc.y), 0);\\n let hbandCount = i32(hbandRaw.x + 0.5);\\n let hbandOffset = i32(hbandRaw.y + 0.5);\\n let hbandLoc = calcBandLoc(glyphLoc, hbandOffset);\\n for (var ci: i32 = 0; ci < hbandCount; ci = ci + 1) {\\n let locRaw = textureLoad(bandTex, vec2<i32>(hbandLoc.x + ci, hbandLoc.y), 0);\\n let curveLoc = vec2<i32>(i32(locRaw.x + 0.5), i32(locRaw.y + 0.5));\\n let p12 = textureLoad(curveTex, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\\n let p3 = textureLoad(curveTex, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) { break; }\\n let code = calcRootCode(p12.y, p12.w, p3.y);\\n if (code != 0) {\\n let r = solveHorizPoly(p12, p3) * pixelsPerEm.x;\\n if ((code & 1) != 0) {\\n xcov = xcov + clamp(r.x + 0.5, 0.0, 1.0);\\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\\n }\\n if (code > 1) {\\n xcov = xcov - clamp(r.y + 0.5, 0.0, 1.0);\\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\\n }\\n }\\n }\\n\\n var ycov: f32 = 0.0;\\n var ywgt: f32 = 0.0;\\n let vbandRaw = textureLoad(bandTex, vec2<i32>(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0);\\n let vbandCount = i32(vbandRaw.x + 0.5);\\n let vbandOffset = i32(vbandRaw.y + 0.5);\\n let vbandLoc = calcBandLoc(glyphLoc, vbandOffset);\\n for (var ci: i32 = 0; ci < vbandCount; ci = ci + 1) {\\n let locRaw = textureLoad(bandTex, vec2<i32>(vbandLoc.x + ci, vbandLoc.y), 0);\\n let curveLoc = vec2<i32>(i32(locRaw.x + 0.5), i32(locRaw.y + 0.5));\\n let p12 = textureLoad(curveTex, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\\n let p3 = textureLoad(curveTex, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) { break; }\\n let code = calcRootCode(p12.x, p12.z, p3.x);\\n if (code != 0) {\\n let r = solveVertPoly(p12, p3) * pixelsPerEm.y;\\n if ((code & 1) != 0) {\\n ycov = ycov - clamp(r.x + 0.5, 0.0, 1.0);\\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\\n }\\n if (code > 1) {\\n ycov = ycov + clamp(r.y + 0.5, 0.0, 1.0);\\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\\n }\\n }\\n }\\n\\n var coverage = max(\\n abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0),\\n min(abs(xcov), abs(ycov))\\n );\\n coverage = clamp(coverage, 0.0, 1.0);\\n // Coverage gamma: raise edge coverage to 1/coverageGamma so anti-aliased edges composite\\n // heavier (mimics gamma-space stem darkening). textU.color.x is 1 by default (no-op).\\n coverage = pow(coverage, textU.color.x);\\n return in.vColor * coverage;\\n}\\n\"","/** Owns the text render pipeline + bind-group layouts. Lazy per-device cache. */\n\nimport type { EngineContext } from \"../../engine/engine.js\";\nimport vertSrc from \"../shaders/slug.vert.wgsl?raw\";\nimport fragSrc from \"../shaders/slug.frag.wgsl?raw\";\nimport { TEXT_INSTANCE_BYTES } from \"../text-data.js\";\n\nexport interface TextPipelineDeviceCache {\n bindGroupLayout: GPUBindGroupLayout;\n vertModule: GPUShaderModule;\n fragModule: GPUShaderModule;\n quadVertexBuffer: GPUBuffer;\n pipelines: Map<string, GPURenderPipeline>;\n}\n\nlet _cache: WeakMap<GPUDevice, TextPipelineDeviceCache> | null = null;\n\n/** Clear the text pipeline cache for a device, releasing cache-held refs. */\nexport function clearTextPipelineCache(engine: EngineContext): void {\n _cache?.delete(engine._device);\n}\n\n/** Shared 4-vertex unit quad: corner signs (-1,-1), (1,-1), (1,1), (-1,1). */\nconst QUAD_CORNERS = [-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1] as const;\n\nfunction getOrCreateDeviceCache(engine: EngineContext): TextPipelineDeviceCache {\n _cache ??= new WeakMap();\n let cache = _cache.get(engine._device);\n if (cache) {\n return cache;\n }\n const device = engine._device;\n const bindGroupLayout = device.createBindGroupLayout({\n label: \"text-bind-group-layout\",\n entries: [\n { binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: \"unfilterable-float\" } },\n ],\n });\n const vertModule = device.createShaderModule({ label: \"text-vert\", code: vertSrc });\n const fragModule = device.createShaderModule({ label: \"text-frag\", code: fragSrc });\n const corners = new Float32Array(QUAD_CORNERS);\n const quadVertexBuffer = device.createBuffer({\n label: \"text-quad-corners\",\n size: corners.byteLength,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n mappedAtCreation: true,\n });\n new Float32Array(quadVertexBuffer.getMappedRange()).set(corners);\n quadVertexBuffer.unmap();\n\n cache = { bindGroupLayout, vertModule, fragModule, quadVertexBuffer, pipelines: new Map() };\n _cache.set(device, cache);\n return cache;\n}\n\nfunction pipelineKey(format: GPUTextureFormat, sampleCount: number, depthStencilFormat: GPUTextureFormat | null, depthWrite: boolean): string {\n return format + \":\" + sampleCount + \":\" + (depthStencilFormat ?? \"-\") + \":\" + (depthWrite ? \"w\" : \"r\");\n}\n\nexport function getOrCreateTextPipeline(\n engine: EngineContext,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n depthStencilFormat: GPUTextureFormat | null,\n depthWrite: boolean\n): { pipeline: GPURenderPipeline; cache: TextPipelineDeviceCache } {\n const cache = getOrCreateDeviceCache(engine);\n const key = pipelineKey(format, sampleCount, depthStencilFormat, depthWrite);\n let pipeline = cache.pipelines.get(key);\n if (pipeline) {\n return { pipeline, cache };\n }\n const device = engine._device;\n const descriptor: GPURenderPipelineDescriptor = {\n label: \"text-pipeline\",\n layout: device.createPipelineLayout({ bindGroupLayouts: [cache.bindGroupLayout] }),\n vertex: {\n module: cache.vertModule,\n entryPoint: \"main\",\n buffers: [\n {\n arrayStride: 8,\n stepMode: \"vertex\",\n attributes: [{ shaderLocation: 0, offset: 0, format: \"float32x2\" }],\n },\n {\n arrayStride: TEXT_INSTANCE_BYTES,\n stepMode: \"instance\",\n attributes: [\n { shaderLocation: 1, offset: 0, format: \"float32x4\" },\n { shaderLocation: 2, offset: 16, format: \"float32x4\" },\n { shaderLocation: 3, offset: 32, format: \"float32x4\" },\n { shaderLocation: 4, offset: 48, format: \"float32x4\" },\n { shaderLocation: 5, offset: 64, format: \"float32x4\" },\n ],\n },\n ],\n },\n fragment: {\n module: cache.fragModule,\n entryPoint: \"main\",\n targets: [\n {\n format,\n blend: {\n // Premultiplied-alpha blend. The Slug fragment shader outputs\n // `vColor * coverage` — i.e. RGB already premultiplied by coverage\n // (and alpha = coverage) — so the color blend must use srcFactor\n // `one` (NOT `src-alpha`, which would multiply by coverage a second\n // time and render anti-aliased edges as coverage², too dark). The\n // alpha channel already uses `one` for the same reason.\n color: { srcFactor: \"one\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\n alpha: { srcFactor: \"one\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\n },\n },\n ],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\", frontFace: \"ccw\" },\n multisample: { count: sampleCount },\n };\n if (depthStencilFormat) {\n descriptor.depthStencil = {\n format: depthStencilFormat,\n depthCompare: \"greater-equal\",\n depthWriteEnabled: depthWrite,\n };\n }\n pipeline = device.createRenderPipeline(descriptor);\n cache.pipelines.set(key, pipeline);\n return { pipeline, cache };\n}\n"],"names":[],"mappings":"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"}
@@ -13,6 +13,7 @@ function createTextLayer(data, options) {
13
13
  scale: options?.scale ?? 1,
14
14
  order: options?.order ?? 0,
15
15
  opacity: options?.opacity ?? 1,
16
+ coverageGamma: options?.coverageGamma ?? 1,
16
17
  visible: options?.visible ?? true,
17
18
  _version: 0
18
19
  };
@@ -152,7 +153,8 @@ function uploadLayer(rr, lg, bindGroupLayout) {
152
153
  lg.uploadedViewportW = W;
153
154
  lg.uploadedViewportH = H;
154
155
  }
155
- const col = new Float32Array([1, 1, 1, layer.opacity]);
156
+ const gammaInv = layer.coverageGamma > 0 ? 1 / layer.coverageGamma : 1;
157
+ const col = new Float32Array([gammaInv, 1, 1, layer.opacity]);
156
158
  device.queue.writeBuffer(lg.textU, 80, col.buffer, col.byteOffset, 16);
157
159
  }
158
160
  function disposeLayerGpu(lg) {
@@ -1 +1 @@
1
- {"version":3,"file":"text-renderer.js","sources":["../../../src/text/text-renderer.ts"],"sourcesContent":["/** TextRenderer — a standalone `RenderingContext` that draws one or more\n * `TextLayer`s directly to the swapchain. Sibling of `SpriteRenderer`:\n * owns its own render pass, no scene / camera dependency.\n *\n * `TextLayer` is the 2D pixel-space placement record bound to a single TextData; it\n * lives here because it is consumed exclusively by `TextRenderer`. */\n\nimport { registerRenderingContext, unregisterRenderingContext } from \"../engine/engine.js\";\nimport type { RenderingContext } from \"../engine/engine.js\";\nimport type { SurfaceContext } from \"../engine/surface.js\";\nimport { createEmptyUniformBuffer } from \"../resource/gpu-buffers.js\";\nimport type { TextData } from \"./text-data.js\";\nimport { TEXT_INSTANCE_BYTES } from \"./text-data.js\";\nimport { ensureSharedAtlasGpu } from \"./_gpu/text-textures.js\";\nimport { getOrCreateTextPipeline } from \"./_gpu/text-pipeline.js\";\n\n// ─── TextLayer ────────────────────────────────────────────────────────────\n\n/** Initial placement and compositing options for a 2D text layer in a standalone text renderer. */\nexport interface TextLayerOptions {\n /** Top-left origin (in canvas pixels) for the layer's local coordinate frame. Default (0, 0). */\n readonly positionPx?: { readonly x: number; readonly y: number };\n /** Z-axis rotation about `positionPx`, in radians. Default 0. */\n readonly rotationRad?: number;\n /** Uniform scale applied to the laid-out text. Default 1. */\n readonly scale?: number;\n /** Sort order within a renderer (lower draws first). Default 0. */\n readonly order?: number;\n /** Alpha multiplier in [0, 1]. Default 1. */\n readonly opacity?: number;\n /** Default true. */\n readonly visible?: boolean;\n}\n\n/** Pure-data 2D text layer. Mutate fields directly between frames. */\nexport interface TextLayer {\n /** @internal */\n readonly _kind: \"text-layer\";\n readonly data: TextData;\n positionPx: { x: number; y: number };\n rotationRad: number;\n scale: number;\n order: number;\n opacity: number;\n visible: boolean;\n /** @internal Monotonic version bumped by helpers that mutate placement. */\n _version: number;\n}\n\n/** Create a 2D text layer that places a `TextData` block in canvas pixel space.\n *\n * @param data - Text data block drawn by the layer.\n * @param options - Optional pixel placement, scale, order, opacity, and visibility.\n * @returns A mutable layer object for use with a `TextRenderer`. */\nexport function createTextLayer(data: TextData, options?: TextLayerOptions): TextLayer {\n return {\n _kind: \"text-layer\",\n data,\n positionPx: { x: options?.positionPx?.x ?? 0, y: options?.positionPx?.y ?? 0 },\n rotationRad: options?.rotationRad ?? 0,\n scale: options?.scale ?? 1,\n order: options?.order ?? 0,\n opacity: options?.opacity ?? 1,\n visible: options?.visible ?? true,\n _version: 0,\n };\n}\n\n/** Update the layer's pixel position. Convenience wrapper. */\nexport function setTextLayerPosition(layer: TextLayer, x: number, y: number): void {\n layer.positionPx.x = x;\n layer.positionPx.y = y;\n layer._version++;\n}\n\n// ─── TextRenderer ─────────────────────────────────────────────────────────\n\nconst KIND = \"text-renderer\" as const;\n\n/** UBO: mat4 mvp (64B) + viewport vec4 (16B) + color vec4 (16B). */\nconst TEXT_UBO_BYTES = 96;\n\n/** Options for creating a standalone text renderer that draws directly to a surface. */\nexport interface TextRendererOptions {\n layers: readonly TextLayer[];\n /** Default true. Set false for HUD overlays so the text pass preserves existing scene color. */\n clear?: boolean;\n /** Default `{ r: 0, g: 0, b: 0, a: 1 }`. */\n clearValue?: GPUColorDict;\n}\n\n/** Standalone rendering context that draws sorted 2D text layers directly to the swapchain. */\nexport interface TextRenderer extends RenderingContext {\n /** @internal */\n readonly _kind: typeof KIND;\n readonly layers: readonly TextLayer[];\n /** @internal Mutable alias of {@link layers} (same array reference). */\n _layers: TextLayer[];\n /** @internal */ readonly _surface: SurfaceContext;\n /** @internal Per-layer GPU resources, keyed by layer. */\n _layerGpu: Map<TextLayer, LayerGpu>;\n /** @internal */ _targetWidth: number;\n /** @internal */ _targetHeight: number;\n /** @internal */ _disposed: boolean;\n /** @internal */ _clear: boolean;\n}\n\n/** @internal Per-layer GPU resources owned by the renderer. */\ninterface LayerGpu {\n layer: TextLayer;\n textU: GPUBuffer;\n instanceBuf: GPUBuffer;\n instanceCap: number;\n pipeline: GPURenderPipeline | null;\n /** Per-draw-group bind groups; rebuilt when atlas grows. */\n bindGroups: GPUBindGroup[];\n bindGroupAtlasVersions: number[];\n uploadedDataVersion: number;\n uploadedViewportW: number;\n uploadedViewportH: number;\n /** Snapshot of (posX, posY, rot, scale, W, H) to skip mvp upload when unchanged. */\n lastMvpInputs: Float32Array;\n mvpUploaded: boolean;\n}\n\nconst _mvpScratch = new Float32Array(16);\n\nfunction buildLayerMvp(layer: TextLayer, targetW: number, targetH: number, out: Float32Array): void {\n const s = layer.scale;\n const r = layer.rotationRad;\n const cr = Math.cos(r);\n const sr = Math.sin(r);\n const px = layer.positionPx.x;\n const py = layer.positionPx.y;\n // Map glyph-local (font Y-up) coords through (scale, flip-Y) → rotate → translate → ortho(W,H, Y-down).\n // Equivalent compact affine — see plan note for derivation. Column-major.\n const cx = (2 * s) / targetW;\n const cy = (2 * s) / targetH;\n out.fill(0);\n out[0] = cx * cr; // col 0, row 0\n out[1] = -cy * sr; // col 0, row 1\n out[4] = cx * sr; // col 1, row 0\n out[5] = cy * cr; // col 1, row 1\n out[10] = 1; // depth pass-through (we don't write depth)\n out[12] = (2 * px) / targetW - 1;\n out[13] = 1 - (2 * py) / targetH;\n out[15] = 1;\n}\n\nfunction ensureLayerGpu(rr: TextRenderer, layer: TextLayer): LayerGpu {\n let lg = rr._layerGpu.get(layer);\n if (lg) {\n return lg;\n }\n const engine = rr._surface.engine;\n const device = engine._device;\n const cap = Math.max(layer.data._instanceCount, 8);\n lg = {\n layer,\n textU: createEmptyUniformBuffer(engine, TEXT_UBO_BYTES, \"text-layer-ubo\"),\n instanceBuf: device.createBuffer({\n label: \"text-layer-instances\",\n size: cap * TEXT_INSTANCE_BYTES,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n }),\n instanceCap: cap,\n pipeline: null,\n bindGroups: [],\n bindGroupAtlasVersions: [],\n uploadedDataVersion: -1,\n uploadedViewportW: 0,\n uploadedViewportH: 0,\n lastMvpInputs: new Float32Array(6),\n mvpUploaded: false,\n };\n rr._layerGpu.set(layer, lg);\n return lg;\n}\n\nfunction ensureInstanceCapacity(device: GPUDevice, lg: LayerGpu, needed: number): void {\n if (needed <= lg.instanceCap) {\n return;\n }\n let cap = lg.instanceCap;\n while (cap < needed) {\n cap *= 2;\n }\n lg.instanceBuf.destroy();\n lg.instanceBuf = device.createBuffer({\n label: \"text-layer-instances\",\n size: cap * TEXT_INSTANCE_BYTES,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n });\n lg.instanceCap = cap;\n lg.uploadedDataVersion = -1;\n}\n\nfunction uploadLayer(rr: TextRenderer, lg: LayerGpu, bindGroupLayout: GPUBindGroupLayout): void {\n const device = rr._surface.engine._device;\n const layer = lg.layer;\n const data = layer.data;\n\n // Atlas + bind groups per draw group.\n for (let i = 0; i < data._groups.length; i++) {\n const g = data._groups[i]!;\n const { rebuilt, gpu: atlasGpu } = ensureSharedAtlasGpu(device, g.curveSet.atlas);\n const current = lg.bindGroups[i];\n const currentVer = lg.bindGroupAtlasVersions[i] ?? -1;\n if (!current || rebuilt || currentVer !== atlasGpu.uploadedVersion) {\n lg.bindGroups[i] = device.createBindGroup({\n label: \"text-renderer-bg0-\" + g.curveSetId,\n layout: bindGroupLayout,\n entries: [\n { binding: 0, resource: { buffer: lg.textU } },\n { binding: 1, resource: atlasGpu.curveTex.createView() },\n { binding: 2, resource: atlasGpu.bandTex.createView() },\n ],\n });\n lg.bindGroupAtlasVersions[i] = atlasGpu.uploadedVersion;\n }\n }\n if (lg.bindGroups.length > data._groups.length) {\n lg.bindGroups.length = data._groups.length;\n lg.bindGroupAtlasVersions.length = data._groups.length;\n }\n\n // Instance buffer.\n ensureInstanceCapacity(device, lg, data._instanceCount);\n if (lg.uploadedDataVersion !== data._version && data._instanceCount > 0) {\n const dirtyValid = lg.uploadedDataVersion !== -1 && data._dirtyEnd > data._dirtyStart;\n if (dirtyValid) {\n const startFloats = data._dirtyStart * (TEXT_INSTANCE_BYTES / 4);\n const endFloats = data._dirtyEnd * (TEXT_INSTANCE_BYTES / 4);\n const view = data._instances.subarray(startFloats, endFloats);\n device.queue.writeBuffer(lg.instanceBuf, data._dirtyStart * TEXT_INSTANCE_BYTES, view.buffer as ArrayBuffer, view.byteOffset, view.byteLength);\n } else {\n const view = data._instances.subarray(0, data._instanceCount * (TEXT_INSTANCE_BYTES / 4));\n device.queue.writeBuffer(lg.instanceBuf, 0, view.buffer as ArrayBuffer, view.byteOffset, view.byteLength);\n }\n lg.uploadedDataVersion = data._version;\n data._dirtyStart = 0;\n data._dirtyEnd = 0;\n }\n\n // MVP — skip upload when nothing relevant changed.\n const W = rr._targetWidth;\n const H = rr._targetHeight;\n const mi = lg.lastMvpInputs;\n if (!lg.mvpUploaded || mi[0] !== layer.positionPx.x || mi[1] !== layer.positionPx.y || mi[2] !== layer.rotationRad || mi[3] !== layer.scale || mi[4] !== W || mi[5] !== H) {\n buildLayerMvp(layer, W, H, _mvpScratch);\n device.queue.writeBuffer(lg.textU, 0, _mvpScratch.buffer as ArrayBuffer, _mvpScratch.byteOffset, 64);\n mi[0] = layer.positionPx.x;\n mi[1] = layer.positionPx.y;\n mi[2] = layer.rotationRad;\n mi[3] = layer.scale;\n mi[4] = W;\n mi[5] = H;\n lg.mvpUploaded = true;\n }\n\n // Viewport (only used by Slug dilation; pixel reciprocal is fine to refresh on resize).\n if (lg.uploadedViewportW !== W || lg.uploadedViewportH !== H) {\n const vp = new Float32Array([W, H, 0, 0]);\n device.queue.writeBuffer(lg.textU, 64, vp.buffer as ArrayBuffer, vp.byteOffset, 16);\n lg.uploadedViewportW = W;\n lg.uploadedViewportH = H;\n }\n\n // Color uniform carries the whole-layer opacity as alpha (RGB = white). Per-glyph/per-run\n // color comes from the instance `slugColor` attribute and is multiplied by this in the shader.\n const col = new Float32Array([1, 1, 1, layer.opacity]);\n device.queue.writeBuffer(lg.textU, 80, col.buffer as ArrayBuffer, col.byteOffset, 16);\n}\n\nfunction disposeLayerGpu(lg: LayerGpu): void {\n lg.textU.destroy();\n lg.instanceBuf.destroy();\n}\n\nfunction compareLayers(a: TextLayer, b: TextLayer): number {\n return a.order - b.order;\n}\n\n/** Create a standalone text renderer for one surface.\n *\n * @param surface - Surface whose swapchain receives the text pass.\n * @param opts - Initial layers and clear settings.\n * @returns A rendering context that can be registered with the engine. */\nexport function createTextRenderer(surface: SurfaceContext, opts: TextRendererOptions): TextRenderer {\n const canvas = surface.canvas;\n const layers = opts.layers.slice();\n\n const rr: TextRenderer = {\n _kind: KIND,\n _surface: surface,\n _layerGpu: new Map(),\n _targetWidth: canvas.width,\n _targetHeight: canvas.height,\n _disposed: false,\n _clear: opts.clear ?? true,\n layers,\n _layers: layers,\n clearColor: opts.clearValue ?? { r: 0, g: 0, b: 0, a: 1 },\n _drawCallsPre: 0,\n _update(): void {\n textRendererUpdate(rr);\n },\n _record(): number {\n return textRendererRecord(rr);\n },\n };\n return rr;\n}\n\nfunction textRendererUpdate(rr: TextRenderer): void {\n if (rr._disposed) {\n return;\n }\n const size = rr._surface.canvas;\n rr._targetWidth = size.width;\n rr._targetHeight = size.height;\n\n if (rr._layers.length > 1) {\n rr._layers.sort(compareLayers);\n }\n\n // Pipeline: depth-less, sampleCount=1, swapchain format. The key is identical for every\n // layer, so resolve it once per frame and reuse the pipeline + bind-group layout below.\n const { pipeline, cache } = getOrCreateTextPipeline(rr._surface.engine, rr._surface.format, 1, null, false);\n\n for (const layer of rr._layers) {\n if (!layer.visible) {\n continue;\n }\n const lg = ensureLayerGpu(rr, layer);\n if (lg.pipeline !== pipeline) {\n lg.pipeline = pipeline;\n // Pipeline change → bind groups must be rebuilt against new bindGroupLayout.\n lg.bindGroups.length = 0;\n lg.bindGroupAtlasVersions.length = 0;\n }\n uploadLayer(rr, lg, cache.bindGroupLayout);\n }\n}\n\nfunction textRendererRecord(rr: TextRenderer): number {\n if (rr._disposed) {\n return 0;\n }\n const eng = rr._surface.engine;\n const encoder = eng._currentEncoder;\n const swapView = rr._surface.scRT._colorView!;\n\n const pass = encoder.beginRenderPass({\n colorAttachments: [\n {\n view: swapView,\n clearValue: rr.clearColor,\n loadOp: rr._clear ? \"clear\" : \"load\",\n storeOp: \"store\",\n },\n ],\n });\n\n let drawCalls = 0;\n let lastPipeline: GPURenderPipeline | null = null;\n const { cache } = getOrCreateTextPipeline(rr._surface.engine, rr._surface.format, 1, null, false);\n const quadVertex = cache.quadVertexBuffer;\n pass.setVertexBuffer(0, quadVertex);\n\n for (const layer of rr._layers) {\n if (!layer.visible) {\n continue;\n }\n const lg = rr._layerGpu.get(layer);\n if (!lg || !lg.pipeline) {\n continue;\n }\n const data = layer.data;\n if (data._instanceCount === 0) {\n continue;\n }\n if (lastPipeline !== lg.pipeline) {\n pass.setPipeline(lg.pipeline);\n lastPipeline = lg.pipeline;\n }\n pass.setVertexBuffer(1, lg.instanceBuf);\n for (let i = 0; i < data._groups.length; i++) {\n const g = data._groups[i]!;\n const bg = lg.bindGroups[i];\n if (g.slotCount === 0 || !bg) {\n continue;\n }\n pass.setBindGroup(0, bg);\n pass.draw(6, g.slotCount, 0, g.slotStart);\n drawCalls++;\n }\n }\n\n pass.end();\n return drawCalls;\n}\n\n/** Add a text layer to an existing renderer if it is not already present.\n *\n * @param tr - Text renderer to mutate.\n * @param layer - Layer to append; draw order is sorted during renderer update. */\nexport function addTextRendererLayer(tr: TextRenderer, layer: TextLayer): void {\n if (tr._disposed) {\n throw new Error(\"TextRenderer has been disposed.\");\n }\n if (tr._layers.includes(layer)) {\n return;\n }\n tr._layers.push(layer);\n}\n\n/** Remove a text layer and release its per-layer GPU buffers.\n *\n * @returns `true` when the layer was present and removed. */\nexport function removeTextRendererLayer(tr: TextRenderer, layer: TextLayer): boolean {\n const i = tr._layers.indexOf(layer);\n if (i < 0) {\n return false;\n }\n tr._layers.splice(i, 1);\n const lg = tr._layerGpu.get(layer);\n if (lg) {\n disposeLayerGpu(lg);\n tr._layerGpu.delete(layer);\n }\n return true;\n}\n\n/** Register a text renderer with its surface so the engine updates and records it each frame. */\nexport function registerTextRenderer(tr: TextRenderer): void {\n registerRenderingContext(tr._surface, tr);\n}\n\n/** Unregister a text renderer from its surface without disposing its layer data. */\nexport function unregisterTextRenderer(tr: TextRenderer): void {\n unregisterRenderingContext(tr._surface, tr);\n}\n\n/** Dispose a text renderer and all per-layer GPU resources it owns. Layer `TextData` remains caller-owned. */\nexport function disposeTextRenderer(tr: TextRenderer): void {\n if (tr._disposed) {\n return;\n }\n unregisterTextRenderer(tr);\n tr._disposed = true;\n for (const lg of tr._layerGpu.values()) {\n disposeLayerGpu(lg);\n }\n tr._layerGpu.clear();\n tr._layers.length = 0;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"text-renderer.js","sources":["../../../src/text/text-renderer.ts"],"sourcesContent":["/** TextRenderer — a standalone `RenderingContext` that draws one or more\n * `TextLayer`s directly to the swapchain. Sibling of `SpriteRenderer`:\n * owns its own render pass, no scene / camera dependency.\n *\n * `TextLayer` is the 2D pixel-space placement record bound to a single TextData; it\n * lives here because it is consumed exclusively by `TextRenderer`. */\n\nimport { registerRenderingContext, unregisterRenderingContext } from \"../engine/engine.js\";\nimport type { RenderingContext } from \"../engine/engine.js\";\nimport type { SurfaceContext } from \"../engine/surface.js\";\nimport { createEmptyUniformBuffer } from \"../resource/gpu-buffers.js\";\nimport type { TextData } from \"./text-data.js\";\nimport { TEXT_INSTANCE_BYTES } from \"./text-data.js\";\nimport { ensureSharedAtlasGpu } from \"./_gpu/text-textures.js\";\nimport { getOrCreateTextPipeline } from \"./_gpu/text-pipeline.js\";\n\n// ─── TextLayer ────────────────────────────────────────────────────────────\n\n/** Initial placement and compositing options for a 2D text layer in a standalone text renderer. */\nexport interface TextLayerOptions {\n /** Top-left origin (in canvas pixels) for the layer's local coordinate frame. Default (0, 0). */\n readonly positionPx?: { readonly x: number; readonly y: number };\n /** Z-axis rotation about `positionPx`, in radians. Default 0. */\n readonly rotationRad?: number;\n /** Uniform scale applied to the laid-out text. Default 1. */\n readonly scale?: number;\n /** Sort order within a renderer (lower draws first). Default 0. */\n readonly order?: number;\n /** Alpha multiplier in [0, 1]. Default 1. */\n readonly opacity?: number;\n /** Coverage gamma for anti-aliased edges. Raises per-pixel coverage to `1/coverageGamma`\n * before compositing, darkening/thickening anti-aliased edges to mimic the gamma-space\n * blending used by native text renderers (DirectWrite/CoreText \"stem darkening\"). Only\n * meaningful when rendering into an sRGB (linear-blended) surface, where correct linear\n * AA otherwise makes text look lighter/thinner than gamma-space rasterizers. Values \\>1\n * thicken; 1 (default) is a no-op. Typical text values are ~1.8–2.2. */\n readonly coverageGamma?: number;\n /** Default true. */\n readonly visible?: boolean;\n}\n\n/** Pure-data 2D text layer. Mutate fields directly between frames. */\nexport interface TextLayer {\n /** @internal */\n readonly _kind: \"text-layer\";\n readonly data: TextData;\n positionPx: { x: number; y: number };\n rotationRad: number;\n scale: number;\n order: number;\n opacity: number;\n /** Coverage gamma for anti-aliased edges (see `TextLayerOptions.coverageGamma`). Default 1. */\n coverageGamma: number;\n visible: boolean;\n /** @internal Monotonic version bumped by helpers that mutate placement. */\n _version: number;\n}\n\n/** Create a 2D text layer that places a `TextData` block in canvas pixel space.\n *\n * @param data - Text data block drawn by the layer.\n * @param options - Optional pixel placement, scale, order, opacity, and visibility.\n * @returns A mutable layer object for use with a `TextRenderer`. */\nexport function createTextLayer(data: TextData, options?: TextLayerOptions): TextLayer {\n return {\n _kind: \"text-layer\",\n data,\n positionPx: { x: options?.positionPx?.x ?? 0, y: options?.positionPx?.y ?? 0 },\n rotationRad: options?.rotationRad ?? 0,\n scale: options?.scale ?? 1,\n order: options?.order ?? 0,\n opacity: options?.opacity ?? 1,\n coverageGamma: options?.coverageGamma ?? 1,\n visible: options?.visible ?? true,\n _version: 0,\n };\n}\n\n/** Update the layer's pixel position. Convenience wrapper. */\nexport function setTextLayerPosition(layer: TextLayer, x: number, y: number): void {\n layer.positionPx.x = x;\n layer.positionPx.y = y;\n layer._version++;\n}\n\n// ─── TextRenderer ─────────────────────────────────────────────────────────\n\nconst KIND = \"text-renderer\" as const;\n\n/** UBO: mat4 mvp (64B) + viewport vec4 (16B) + color vec4 (16B). */\nconst TEXT_UBO_BYTES = 96;\n\n/** Options for creating a standalone text renderer that draws directly to a surface. */\nexport interface TextRendererOptions {\n layers: readonly TextLayer[];\n /** Default true. Set false for HUD overlays so the text pass preserves existing scene color. */\n clear?: boolean;\n /** Default `{ r: 0, g: 0, b: 0, a: 1 }`. */\n clearValue?: GPUColorDict;\n}\n\n/** Standalone rendering context that draws sorted 2D text layers directly to the swapchain. */\nexport interface TextRenderer extends RenderingContext {\n /** @internal */\n readonly _kind: typeof KIND;\n readonly layers: readonly TextLayer[];\n /** @internal Mutable alias of {@link layers} (same array reference). */\n _layers: TextLayer[];\n /** @internal */ readonly _surface: SurfaceContext;\n /** @internal Per-layer GPU resources, keyed by layer. */\n _layerGpu: Map<TextLayer, LayerGpu>;\n /** @internal */ _targetWidth: number;\n /** @internal */ _targetHeight: number;\n /** @internal */ _disposed: boolean;\n /** @internal */ _clear: boolean;\n}\n\n/** @internal Per-layer GPU resources owned by the renderer. */\ninterface LayerGpu {\n layer: TextLayer;\n textU: GPUBuffer;\n instanceBuf: GPUBuffer;\n instanceCap: number;\n pipeline: GPURenderPipeline | null;\n /** Per-draw-group bind groups; rebuilt when atlas grows. */\n bindGroups: GPUBindGroup[];\n bindGroupAtlasVersions: number[];\n uploadedDataVersion: number;\n uploadedViewportW: number;\n uploadedViewportH: number;\n /** Snapshot of (posX, posY, rot, scale, W, H) to skip mvp upload when unchanged. */\n lastMvpInputs: Float32Array;\n mvpUploaded: boolean;\n}\n\nconst _mvpScratch = new Float32Array(16);\n\nfunction buildLayerMvp(layer: TextLayer, targetW: number, targetH: number, out: Float32Array): void {\n const s = layer.scale;\n const r = layer.rotationRad;\n const cr = Math.cos(r);\n const sr = Math.sin(r);\n const px = layer.positionPx.x;\n const py = layer.positionPx.y;\n // Map glyph-local (font Y-up) coords through (scale, flip-Y) → rotate → translate → ortho(W,H, Y-down).\n // Equivalent compact affine — see plan note for derivation. Column-major.\n const cx = (2 * s) / targetW;\n const cy = (2 * s) / targetH;\n out.fill(0);\n out[0] = cx * cr; // col 0, row 0\n out[1] = -cy * sr; // col 0, row 1\n out[4] = cx * sr; // col 1, row 0\n out[5] = cy * cr; // col 1, row 1\n out[10] = 1; // depth pass-through (we don't write depth)\n out[12] = (2 * px) / targetW - 1;\n out[13] = 1 - (2 * py) / targetH;\n out[15] = 1;\n}\n\nfunction ensureLayerGpu(rr: TextRenderer, layer: TextLayer): LayerGpu {\n let lg = rr._layerGpu.get(layer);\n if (lg) {\n return lg;\n }\n const engine = rr._surface.engine;\n const device = engine._device;\n const cap = Math.max(layer.data._instanceCount, 8);\n lg = {\n layer,\n textU: createEmptyUniformBuffer(engine, TEXT_UBO_BYTES, \"text-layer-ubo\"),\n instanceBuf: device.createBuffer({\n label: \"text-layer-instances\",\n size: cap * TEXT_INSTANCE_BYTES,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n }),\n instanceCap: cap,\n pipeline: null,\n bindGroups: [],\n bindGroupAtlasVersions: [],\n uploadedDataVersion: -1,\n uploadedViewportW: 0,\n uploadedViewportH: 0,\n lastMvpInputs: new Float32Array(6),\n mvpUploaded: false,\n };\n rr._layerGpu.set(layer, lg);\n return lg;\n}\n\nfunction ensureInstanceCapacity(device: GPUDevice, lg: LayerGpu, needed: number): void {\n if (needed <= lg.instanceCap) {\n return;\n }\n let cap = lg.instanceCap;\n while (cap < needed) {\n cap *= 2;\n }\n lg.instanceBuf.destroy();\n lg.instanceBuf = device.createBuffer({\n label: \"text-layer-instances\",\n size: cap * TEXT_INSTANCE_BYTES,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n });\n lg.instanceCap = cap;\n lg.uploadedDataVersion = -1;\n}\n\nfunction uploadLayer(rr: TextRenderer, lg: LayerGpu, bindGroupLayout: GPUBindGroupLayout): void {\n const device = rr._surface.engine._device;\n const layer = lg.layer;\n const data = layer.data;\n\n // Atlas + bind groups per draw group.\n for (let i = 0; i < data._groups.length; i++) {\n const g = data._groups[i]!;\n const { rebuilt, gpu: atlasGpu } = ensureSharedAtlasGpu(device, g.curveSet.atlas);\n const current = lg.bindGroups[i];\n const currentVer = lg.bindGroupAtlasVersions[i] ?? -1;\n if (!current || rebuilt || currentVer !== atlasGpu.uploadedVersion) {\n lg.bindGroups[i] = device.createBindGroup({\n label: \"text-renderer-bg0-\" + g.curveSetId,\n layout: bindGroupLayout,\n entries: [\n { binding: 0, resource: { buffer: lg.textU } },\n { binding: 1, resource: atlasGpu.curveTex.createView() },\n { binding: 2, resource: atlasGpu.bandTex.createView() },\n ],\n });\n lg.bindGroupAtlasVersions[i] = atlasGpu.uploadedVersion;\n }\n }\n if (lg.bindGroups.length > data._groups.length) {\n lg.bindGroups.length = data._groups.length;\n lg.bindGroupAtlasVersions.length = data._groups.length;\n }\n\n // Instance buffer.\n ensureInstanceCapacity(device, lg, data._instanceCount);\n if (lg.uploadedDataVersion !== data._version && data._instanceCount > 0) {\n const dirtyValid = lg.uploadedDataVersion !== -1 && data._dirtyEnd > data._dirtyStart;\n if (dirtyValid) {\n const startFloats = data._dirtyStart * (TEXT_INSTANCE_BYTES / 4);\n const endFloats = data._dirtyEnd * (TEXT_INSTANCE_BYTES / 4);\n const view = data._instances.subarray(startFloats, endFloats);\n device.queue.writeBuffer(lg.instanceBuf, data._dirtyStart * TEXT_INSTANCE_BYTES, view.buffer as ArrayBuffer, view.byteOffset, view.byteLength);\n } else {\n const view = data._instances.subarray(0, data._instanceCount * (TEXT_INSTANCE_BYTES / 4));\n device.queue.writeBuffer(lg.instanceBuf, 0, view.buffer as ArrayBuffer, view.byteOffset, view.byteLength);\n }\n lg.uploadedDataVersion = data._version;\n data._dirtyStart = 0;\n data._dirtyEnd = 0;\n }\n\n // MVP — skip upload when nothing relevant changed.\n const W = rr._targetWidth;\n const H = rr._targetHeight;\n const mi = lg.lastMvpInputs;\n if (!lg.mvpUploaded || mi[0] !== layer.positionPx.x || mi[1] !== layer.positionPx.y || mi[2] !== layer.rotationRad || mi[3] !== layer.scale || mi[4] !== W || mi[5] !== H) {\n buildLayerMvp(layer, W, H, _mvpScratch);\n device.queue.writeBuffer(lg.textU, 0, _mvpScratch.buffer as ArrayBuffer, _mvpScratch.byteOffset, 64);\n mi[0] = layer.positionPx.x;\n mi[1] = layer.positionPx.y;\n mi[2] = layer.rotationRad;\n mi[3] = layer.scale;\n mi[4] = W;\n mi[5] = H;\n lg.mvpUploaded = true;\n }\n\n // Viewport (only used by Slug dilation; pixel reciprocal is fine to refresh on resize).\n if (lg.uploadedViewportW !== W || lg.uploadedViewportH !== H) {\n const vp = new Float32Array([W, H, 0, 0]);\n device.queue.writeBuffer(lg.textU, 64, vp.buffer as ArrayBuffer, vp.byteOffset, 16);\n lg.uploadedViewportW = W;\n lg.uploadedViewportH = H;\n }\n\n // Color uniform carries the whole-layer opacity as alpha. The R slot (otherwise unused;\n // RGB does not tint per-glyph color) carries the coverage-gamma reciprocal applied to\n // anti-aliased edge coverage in the fragment shader. G/B stay 1.\n const gammaInv = layer.coverageGamma > 0 ? 1 / layer.coverageGamma : 1;\n const col = new Float32Array([gammaInv, 1, 1, layer.opacity]);\n device.queue.writeBuffer(lg.textU, 80, col.buffer as ArrayBuffer, col.byteOffset, 16);\n}\n\nfunction disposeLayerGpu(lg: LayerGpu): void {\n lg.textU.destroy();\n lg.instanceBuf.destroy();\n}\n\nfunction compareLayers(a: TextLayer, b: TextLayer): number {\n return a.order - b.order;\n}\n\n/** Create a standalone text renderer for one surface.\n *\n * @param surface - Surface whose swapchain receives the text pass.\n * @param opts - Initial layers and clear settings.\n * @returns A rendering context that can be registered with the engine. */\nexport function createTextRenderer(surface: SurfaceContext, opts: TextRendererOptions): TextRenderer {\n const canvas = surface.canvas;\n const layers = opts.layers.slice();\n\n const rr: TextRenderer = {\n _kind: KIND,\n _surface: surface,\n _layerGpu: new Map(),\n _targetWidth: canvas.width,\n _targetHeight: canvas.height,\n _disposed: false,\n _clear: opts.clear ?? true,\n layers,\n _layers: layers,\n clearColor: opts.clearValue ?? { r: 0, g: 0, b: 0, a: 1 },\n _drawCallsPre: 0,\n _update(): void {\n textRendererUpdate(rr);\n },\n _record(): number {\n return textRendererRecord(rr);\n },\n };\n return rr;\n}\n\nfunction textRendererUpdate(rr: TextRenderer): void {\n if (rr._disposed) {\n return;\n }\n const size = rr._surface.canvas;\n rr._targetWidth = size.width;\n rr._targetHeight = size.height;\n\n if (rr._layers.length > 1) {\n rr._layers.sort(compareLayers);\n }\n\n // Pipeline: depth-less, sampleCount=1, swapchain format. The key is identical for every\n // layer, so resolve it once per frame and reuse the pipeline + bind-group layout below.\n const { pipeline, cache } = getOrCreateTextPipeline(rr._surface.engine, rr._surface.format, 1, null, false);\n\n for (const layer of rr._layers) {\n if (!layer.visible) {\n continue;\n }\n const lg = ensureLayerGpu(rr, layer);\n if (lg.pipeline !== pipeline) {\n lg.pipeline = pipeline;\n // Pipeline change → bind groups must be rebuilt against new bindGroupLayout.\n lg.bindGroups.length = 0;\n lg.bindGroupAtlasVersions.length = 0;\n }\n uploadLayer(rr, lg, cache.bindGroupLayout);\n }\n}\n\nfunction textRendererRecord(rr: TextRenderer): number {\n if (rr._disposed) {\n return 0;\n }\n const eng = rr._surface.engine;\n const encoder = eng._currentEncoder;\n const swapView = rr._surface.scRT._colorView!;\n\n const pass = encoder.beginRenderPass({\n colorAttachments: [\n {\n view: swapView,\n clearValue: rr.clearColor,\n loadOp: rr._clear ? \"clear\" : \"load\",\n storeOp: \"store\",\n },\n ],\n });\n\n let drawCalls = 0;\n let lastPipeline: GPURenderPipeline | null = null;\n const { cache } = getOrCreateTextPipeline(rr._surface.engine, rr._surface.format, 1, null, false);\n const quadVertex = cache.quadVertexBuffer;\n pass.setVertexBuffer(0, quadVertex);\n\n for (const layer of rr._layers) {\n if (!layer.visible) {\n continue;\n }\n const lg = rr._layerGpu.get(layer);\n if (!lg || !lg.pipeline) {\n continue;\n }\n const data = layer.data;\n if (data._instanceCount === 0) {\n continue;\n }\n if (lastPipeline !== lg.pipeline) {\n pass.setPipeline(lg.pipeline);\n lastPipeline = lg.pipeline;\n }\n pass.setVertexBuffer(1, lg.instanceBuf);\n for (let i = 0; i < data._groups.length; i++) {\n const g = data._groups[i]!;\n const bg = lg.bindGroups[i];\n if (g.slotCount === 0 || !bg) {\n continue;\n }\n pass.setBindGroup(0, bg);\n pass.draw(6, g.slotCount, 0, g.slotStart);\n drawCalls++;\n }\n }\n\n pass.end();\n return drawCalls;\n}\n\n/** Add a text layer to an existing renderer if it is not already present.\n *\n * @param tr - Text renderer to mutate.\n * @param layer - Layer to append; draw order is sorted during renderer update. */\nexport function addTextRendererLayer(tr: TextRenderer, layer: TextLayer): void {\n if (tr._disposed) {\n throw new Error(\"TextRenderer has been disposed.\");\n }\n if (tr._layers.includes(layer)) {\n return;\n }\n tr._layers.push(layer);\n}\n\n/** Remove a text layer and release its per-layer GPU buffers.\n *\n * @returns `true` when the layer was present and removed. */\nexport function removeTextRendererLayer(tr: TextRenderer, layer: TextLayer): boolean {\n const i = tr._layers.indexOf(layer);\n if (i < 0) {\n return false;\n }\n tr._layers.splice(i, 1);\n const lg = tr._layerGpu.get(layer);\n if (lg) {\n disposeLayerGpu(lg);\n tr._layerGpu.delete(layer);\n }\n return true;\n}\n\n/** Register a text renderer with its surface so the engine updates and records it each frame. */\nexport function registerTextRenderer(tr: TextRenderer): void {\n registerRenderingContext(tr._surface, tr);\n}\n\n/** Unregister a text renderer from its surface without disposing its layer data. */\nexport function unregisterTextRenderer(tr: TextRenderer): void {\n unregisterRenderingContext(tr._surface, tr);\n}\n\n/** Dispose a text renderer and all per-layer GPU resources it owns. Layer `TextData` remains caller-owned. */\nexport function disposeTextRenderer(tr: TextRenderer): void {\n if (tr._disposed) {\n return;\n }\n unregisterTextRenderer(tr);\n tr._disposed = true;\n for (const lg of tr._layerGpu.values()) {\n disposeLayerGpu(lg);\n }\n tr._layerGpu.clear();\n tr._layers.length = 0;\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babylonjs/lite",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "A lightweight, tree-shakable, WebGPU-first rendering library derived from Babylon.js.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://doc.babylonjs.com/lite/",
@@ -22,7 +22,7 @@
22
22
  "unpkg": "./dist/index.js",
23
23
  "sideEffects": false,
24
24
  "babylonLiteRelease": {
25
- "azureBuildId": "55198",
26
- "sourceVersion": "9a6619e6ab55aa579c025bc06a78ae33a7e6199f"
25
+ "azureBuildId": "55411",
26
+ "sourceVersion": "e533d48d9eff9391768488b35503868cf4d76d5e"
27
27
  }
28
28
  }