@babylonjs/lite 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/index.js +596 -526
- package/dist/index.js.map +1 -1
- package/index.d.ts +107 -4
- package/lib/engine/engine.js +1 -1
- package/lib/engine/engine.js.map +1 -1
- package/lib/frame-graph/render-task.js +6 -4
- package/lib/frame-graph/render-task.js.map +1 -1
- package/lib/index.js +4 -1
- package/lib/index.js.map +1 -1
- package/lib/loader-gltf/gltf-ext-quantization.js +4 -1
- package/lib/loader-gltf/gltf-ext-quantization.js.map +1 -1
- package/lib/loader-gltf/gltf-feature-registry.js +11 -1
- package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
- package/lib/loader-gltf/gltf-feature-variants.js +11 -1
- package/lib/loader-gltf/gltf-feature-variants.js.map +1 -1
- package/lib/loader-gltf/gltf-json-asset.js +8 -1
- package/lib/loader-gltf/gltf-json-asset.js.map +1 -1
- package/lib/loader-gltf/gltf-material.js +4 -29
- package/lib/loader-gltf/gltf-material.js.map +1 -1
- package/lib/loader-gltf/gltf-parser.js +1 -8
- package/lib/loader-gltf/gltf-parser.js.map +1 -1
- package/lib/loader-gltf/gltf-pbr-builder.js +1 -15
- package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
- package/lib/loader-gltf/gltf-variants.js +5 -10
- package/lib/loader-gltf/gltf-variants.js.map +1 -1
- package/lib/loader-gltf/load-gltf.js +19 -29
- package/lib/loader-gltf/load-gltf.js.map +1 -1
- package/lib/material/pbr/background-dds-skybox.js +35 -93
- package/lib/material/pbr/background-dds-skybox.js.map +1 -1
- package/lib/material/pbr/background-ground.js +13 -18
- package/lib/material/pbr/background-ground.js.map +1 -1
- package/lib/material/pbr/background-hdr-skybox.js +35 -93
- package/lib/material/pbr/background-hdr-skybox.js.map +1 -1
- package/lib/material/pbr/background-solid-skybox.js +35 -93
- package/lib/material/pbr/background-solid-skybox.js.map +1 -1
- package/lib/material/pbr/fragments/clearcoat-fragment.js +5 -1
- package/lib/material/pbr/fragments/clearcoat-fragment.js.map +1 -1
- package/lib/material/pbr/fragments/iridescence-fragment.js +4 -1
- package/lib/material/pbr/fragments/iridescence-fragment.js.map +1 -1
- package/lib/material/pbr/fragments/refraction-rtt-fragment.js +7 -2
- package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
- package/lib/material/pbr/fragments/sheen-fragment.js +2 -1
- package/lib/material/pbr/fragments/sheen-fragment.js.map +1 -1
- package/lib/material/pbr/fragments/subsurface-fragment.js +2 -1
- package/lib/material/pbr/fragments/subsurface-fragment.js.map +1 -1
- package/lib/material/pbr/fragments/unlit-fragment.js +1 -2
- package/lib/material/pbr/fragments/unlit-fragment.js.map +1 -1
- package/lib/material/pbr/pbr-compose.js +2 -0
- package/lib/material/pbr/pbr-compose.js.map +1 -1
- package/lib/material/pbr/pbr-flag-bits.js +1 -16
- package/lib/material/pbr/pbr-flag-bits.js.map +1 -1
- package/lib/material/pbr/pbr-flags.js +1 -1
- package/lib/material/pbr/pbr-fog-wgsl.js +1 -1
- package/lib/material/pbr/pbr-fog-wgsl.js.map +1 -1
- package/lib/material/pbr/pbr-geometry-output-shader.js +2 -1
- package/lib/material/pbr/pbr-geometry-output-shader.js.map +1 -1
- package/lib/material/pbr/pbr-geometry-view.js +2 -1
- package/lib/material/pbr/pbr-geometry-view.js.map +1 -1
- package/lib/material/pbr/pbr-renderable.js +4 -0
- package/lib/material/pbr/pbr-renderable.js.map +1 -1
- package/lib/material/pbr/pbr-template-gamma.js +7 -0
- package/lib/material/pbr/pbr-template-gamma.js.map +1 -0
- package/lib/material/pbr/pbr-template.js +2 -2
- package/lib/material/pbr/pbr-template.js.map +1 -1
- package/lib/material/standard/skybox-cubemap.js +1 -1
- package/lib/material/standard/skybox-cubemap.js.map +1 -1
- package/lib/material/standard/standard-template.js +1 -1
- package/lib/material/standard/standard-template.js.map +1 -1
- package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline-sh.js +3 -5
- package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline-sh.js.map +1 -1
- package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline.js +3 -5
- package/lib/mesh/GaussianSplatting/gaussian-splatting-pipeline.js.map +1 -1
- package/lib/particle/node/blocks/particle-condition-block.js +55 -0
- package/lib/particle/node/blocks/particle-condition-block.js.map +1 -0
- package/lib/particle/node/blocks/particle-float-to-int-block.js +28 -0
- package/lib/particle/node/blocks/particle-float-to-int-block.js.map +1 -0
- package/lib/particle/node/blocks/particle-gradient-block.js +32 -0
- package/lib/particle/node/blocks/particle-gradient-block.js.map +1 -0
- package/lib/particle/node/blocks/particle-gradient-value-block.js +11 -0
- package/lib/particle/node/blocks/particle-gradient-value-block.js.map +1 -0
- package/lib/particle/node/blocks/particle-lerp-block.js +2 -24
- package/lib/particle/node/blocks/particle-lerp-block.js.map +1 -1
- package/lib/particle/node/blocks/particle-lerp.js +27 -0
- package/lib/particle/node/blocks/particle-lerp.js.map +1 -0
- package/lib/particle/node/blocks/particle-vector-length-block.js +30 -0
- package/lib/particle/node/blocks/particle-vector-length-block.js.map +1 -0
- package/lib/particle/node/blocks/system-block.js +13 -3
- package/lib/particle/node/blocks/system-block.js.map +1 -1
- package/lib/particle/node/blocks/texture-source-block.js +2 -1
- package/lib/particle/node/blocks/texture-source-block.js.map +1 -1
- package/lib/particle/node/blocks/update-size-block.js +19 -0
- package/lib/particle/node/blocks/update-size-block.js.map +1 -0
- package/lib/particle/node/npe-build-state.js +10 -0
- package/lib/particle/node/npe-build-state.js.map +1 -1
- package/lib/particle/node/npe-registry.js +12 -0
- package/lib/particle/node/npe-registry.js.map +1 -1
- package/lib/particle/particle-system.js +7 -2
- package/lib/particle/particle-system.js.map +1 -1
- package/lib/picking/billboard-pick-pipeline.js +269 -0
- package/lib/picking/billboard-pick-pipeline.js.map +1 -0
- package/lib/picking/gpu-picker.js +37 -37
- package/lib/picking/gpu-picker.js.map +1 -1
- package/lib/picking/gs-picking-pipeline.js +26 -1
- package/lib/picking/gs-picking-pipeline.js.map +1 -1
- package/lib/picking/pick-contributor.js +13 -0
- package/lib/picking/pick-contributor.js.map +1 -0
- package/lib/picking/picking-info.js.map +1 -1
- package/lib/scene/scene-core.js +2 -2
- package/lib/scene/scene-core.js.map +1 -1
- package/lib/shader/wgsl-fog.js +18 -0
- package/lib/shader/wgsl-fog.js.map +1 -0
- package/lib/shader/wgsl-helpers.js +1 -16
- package/lib/shader/wgsl-helpers.js.map +1 -1
- package/lib/sprite/billboard-scene.js +2 -0
- package/lib/sprite/billboard-scene.js.map +1 -1
- package/lib/sprite/picking/pick-billboard.js +16 -0
- package/lib/sprite/picking/pick-billboard.js.map +1 -0
- package/lib/sprite/picking/pick-sprite-2d.js +41 -0
- package/lib/sprite/picking/pick-sprite-2d.js.map +1 -0
- package/lib/sprite/shared/sprite-atlas.js +4 -1
- package/lib/sprite/shared/sprite-atlas.js.map +1 -1
- package/lib/sprite/sprite-2d.js.map +1 -1
- package/lib/sprite/sprite-pipeline.js +18 -1
- package/lib/sprite/sprite-pipeline.js.map +1 -1
- package/lib/sprite/sprite-renderer.js +12 -6
- package/lib/sprite/sprite-renderer.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sprite-2d.js","sources":["../../../src/sprite/sprite-2d.ts"],"sourcesContent":["/**\n * `Sprite2DLayer` — pixel-coordinate sprite layer. Pure-data interface +\n * standalone Index API for add / update / remove / setFrame. The layer is\n * owned by a `SpriteRenderer` (pure-2D / HUD `depth: \"none\"` path) or by a\n * scene renderable added through `addDepthHostedSpriteLayer`\n * (`depth: \"test\" | \"test-write\"`).\n *\n * The Index API stays the storage foundation. The optional Handle API lives in\n * `sprite-2d-handle.ts` and lazily installs hooks only when handles are used.\n */\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { SpriteAtlas } from \"./shared/sprite-atlas.js\";\nimport { resolveSpriteFrame } from \"./shared/sprite-atlas.js\";\nimport type { Sprite2DCustomShader } from \"./sprite-custom-shader.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport type { SpriteBlendDescriptor } from \"./sprite-blend.js\";\nimport { spriteBlendAlpha } from \"./sprite-blend.js\";\n\n/**\n * Output blend mode for a sprite layer — a pure-data descriptor value. Import one of\n * `spriteBlendAlpha` (default), `spriteBlendPremultiplied`, or `spriteBlendAdditive` and pass\n * it as `Sprite2DLayerOptions.blendMode`. (Type alias of {@link SpriteBlendDescriptor}.)\n */\nexport type SpriteBlendMode = SpriteBlendDescriptor;\n\n/** Depth participation. `\"none\"` uses `SpriteRenderer`; depth-enabled modes use `addToScene`. */\nexport type Sprite2DDepthMode = \"none\" | \"test\" | \"test-write\";\n\n/** Per-layer 2D camera (pan / zoom / rotation). Identity = pixel-perfect HUD. */\nexport interface Sprite2DView {\n positionPx: [number, number];\n zoom: number;\n rotation: number;\n}\n\n/** Options accepted by `createSprite2DLayer`. */\nexport interface Sprite2DLayerOptions {\n capacity?: number;\n blendMode?: SpriteBlendMode;\n opacity?: number;\n visible?: boolean;\n order?: number;\n view?: Partial<Sprite2DView>;\n depth?: Sprite2DDepthMode;\n /**\n * Layer-wide rotation / scaling pivot in normalised sprite-local space\n * (`[0,0]` = top-left, `[0.5, 0.5]` = center, `[1,1]` = bottom-right).\n * The pivot point of every sprite in the layer lands at its `positionPx`\n * and is the center of `rotation`. Defaults to `[0.5, 0.5]` (center) to\n * match Babylon.js sprite behavior. Per-sprite / per-frame pivot is a\n * future PR — most 2D HUD layers want one uniform pivot anyway.\n */\n pivot?: [number, number];\n /**\n * Opt-in per-layer custom fragment shader (see `createSprite2DCustomShader`). Works on both\n * pure-2D (`depth: \"none\"`) layers drawn by a `SpriteRenderer` and depth-hosted\n * (`depth: \"test\" | \"test-write\"`) layers. Drives procedural effects (animated sky, clouds,\n * water / heat shimmer, twinkle, vignette) from a built-in `fx.time` clock plus an optional\n * `fx.params` vec4 set via `setSprite2DShaderParams`.\n */\n customShader?: Sprite2DCustomShader;\n /**\n * Default NDC depth (`0` = near, `1` = far) for sprites added to this layer when their\n * `Sprite2DProps.z` is omitted. Only meaningful for `depth: \"test\" | \"test-write\"` layers\n * (depth-hosted sprites added to a `SceneContext` via `addDepthHostedSpriteLayer`).\n *\n * Depth-hosted layers store one Z per sprite (slot [13] of their 14-float instance buffer),\n * so a single layer can mix sprites at different depths — e.g. one in front of a box, one\n * behind it. Pure-2D layers (`depth: \"none\"`) use the 13-float HUD layout and carry no Z slot.\n * Defaults to `0.5`. Mutating `layer.layerZ` after sprites have been added does **not**\n * retroactively change them; it only affects sprites added afterwards. To move an existing\n * depth-hosted sprite, call `updateSprite2DIndex(layer, idx, { z: … })`.\n */\n layerZ?: number;\n}\n\n/** A `Sprite2DLayer` — pure data, no methods. */\nexport interface Sprite2DLayer {\n /** @internal */\n readonly _entityType: \"sprite-2d-layer\";\n readonly atlas: SpriteAtlas;\n readonly depth: Sprite2DDepthMode;\n readonly blendMode: SpriteBlendMode;\n opacity: number;\n visible: boolean;\n order: number;\n view: Sprite2DView;\n /** Layer-wide pivot in normalised sprite-local space; see `Sprite2DLayerOptions.pivot`. */\n pivot: [number, number];\n /**\n * Opt-in custom fragment shader for this layer; see `Sprite2DLayerOptions.customShader`.\n * **Absent** (not `null`) on plain layers — never default-initialized, so the always-loaded\n * path carries zero custom-shader bytes (see `sprite-fx-hook.ts`). Present only when the\n * layer was created with a `customShader`.\n */\n readonly customShader?: Sprite2DCustomShader;\n /**\n * User `fx.params` vec4 fed to a custom shader each frame; mutate via `setSprite2DShaderParams`.\n * **Absent** on plain layers (only allocated for custom-shader layers, or lazily by the setter).\n */\n shaderParams?: [number, number, number, number];\n /**\n * @internal Opt-in per-sprite UV-scroll vertex attribute (`uvOffset.xy` at `@location(7)`);\n * enabled via {@link setSprite2DUvOffset}. **Absent** (`undefined`) on plain layers — never\n * default-initialized, so the always-loaded path and every non-scroll sprite scene keep the\n * narrow layout and base shader. The first `setSprite2DUvOffset` call widens the instance stride\n * by two floats and stashes the precomputed attribute here, so the always-loaded pipeline just\n * *consumes* it as data (`instanceAttributes.push(layer._uvScrollAttr)`) — the attribute-building\n * code lives entirely in the opt-in `sprite-2d-uvscroll.ts`. Presence also serves as the\n * \"is wide\" flag (drives the shader permutation key + the prologue `+ in.iUvOffset`).\n */\n _uvScrollAttr?: GPUVertexAttribute;\n /**\n * @internal Opt-in coverage-gamma value (glyph \"stem darkening\"); set only via\n * {@link setSprite2DCoverageGamma}. **Absent** (`undefined`, treated as `1` = no-op) on plain\n * layers — never default-initialized, so the always-loaded path keeps the base shader and\n * non-gamma scenes ship zero gamma bytes. A value `!= null && !== 1` activates the gamma shader\n * permutation (the per-fragment `pow`) and is read each frame into the layer UBO; it stays\n * live-mutable through the setter. Internal so callers can't write a value that the always-loaded\n * path would silently ignore — enabling is exclusively the setter's job.\n */\n _coverageGamma?: number;\n /** Default NDC depth for newly added sprites; see `Sprite2DLayerOptions.layerZ`. */\n layerZ: number;\n readonly count: number;\n\n /** @internal Capacity of the per-instance buffer (in sprites). */\n _capacity: number;\n /** @internal Per-instance stride in floats; 13 for pure-2D, 14 for depth-hosted. */\n readonly _instanceFloatsPerSprite: number;\n /** @internal Per-instance stride in bytes; 52 for pure-2D, 56 for depth-hosted. */\n readonly _instanceStrideBytes: number;\n /** @internal Per-instance CPU staging buffer; layout = `_instanceFloatsPerSprite` per sprite. */\n _instanceData: Float32Array;\n /**\n * @internal CPU-only side buffer holding the **true** (un-hidden) size of every sprite,\n * laid out as `[w0, h0, w1, h1, …]` (`SAVED_SIZE_FLOATS_PER_SPRITE` = 2 floats per sprite).\n *\n * **Invariant:** this buffer always holds the sprite's real size, regardless of visibility.\n * It exists because `visible: false` is implemented by zeroing the GPU-side size slots\n * (degenerate quad → free rasterizer cull) — a free hide on the GPU at the cost of 8 B per\n * sprite on the CPU. Without this shadow, a `visible: true` patch that omits `sizePx`\n * would have no way to recover the original size. Grown in lockstep with `_instanceData`.\n */\n _savedSize: Float32Array;\n /** @internal Bumped on any structural / per-instance edit; renderer compares. */\n _version: number;\n /** @internal Min dirty index inclusive (for partial uploads). */\n _dirtyMin: number;\n /** @internal Max dirty index exclusive. */\n _dirtyMax: number;\n /** @internal Optional hooks installed by the opt-in handle module. */\n _handleHooks?: Sprite2DIndexHandleHooks;\n}\n\n/** @internal Lazy hooks used by the opt-in Handle API to track swap-removes. */\nexport interface Sprite2DIndexHandleHooks {\n readonly removeIndex: (index: number, last: number) => void;\n readonly clear: () => void;\n}\n\n/** Per-sprite init record passed to `addSprite2DIndex` / `updateSprite2DIndex`. */\nexport interface Sprite2DProps {\n positionPx: [number, number];\n sizePx?: [number, number];\n frame?: number;\n rotation?: number;\n color?: [number, number, number, number];\n flipX?: boolean;\n flipY?: boolean;\n visible?: boolean;\n /** Reserved for picking. Accepted but unused today. */\n pickable?: boolean;\n /** Reserved for clip animation. Accepted but unused today. */\n clip?: unknown;\n /**\n * Per-sprite NDC depth (`0` = near, `1` = far). Only stored and consumed by depth-hosted\n * layers (`depth: \"test\" | \"test-write\"`); pure-2D HUD layers use a 13-float layout and\n * do not allocate a Z slot. When omitted on add for a depth-hosted layer, defaults to the\n * **owning layer's** `layerZ` at the moment of insertion. When omitted on update, the\n * sprite's existing Z is preserved. Mutate freely — the next binding update will re-upload\n * only the dirty range.\n */\n z?: number;\n}\n\n/**\n * Pure-2D per-instance vertex layout (13 floats = 52 bytes, `depth: \"none\"`):\n * ```\n * [0..1] positionPx.xy (float32x2 @ offset 0)\n * [2..3] sizePx.xy (float32x2 @ offset 8)\n * [4..5] uvMin.xy (float32x2 @ offset 16)\n * [6..7] uvMax.xy (float32x2 @ offset 24)\n * [8] rotation (float32 @ offset 32)\n * [9..12] colorRGBA (float32x4 @ offset 36)\n * ```\n *\n * Depth-hosted layers (`depth: \"test\" | \"test-write\"`) extend this to 14 floats = 56 bytes:\n * ```\n * [13] z (NDC depth) (float32 @ offset 52, consumed only by depth-hosted pipelines)\n * ```\n *\n * The opt-in per-sprite UV-scroll feature (`setSprite2DUvOffset`, see `sprite-2d-uvscroll.ts`)\n * appends two more floats (`uvOffset.xy`) *after* the base layout, orthogonally to depth:\n * ```\n * pure-2D + uvScroll (15 floats = 60 bytes): [13..14] uvOffset.xy (float32x2 @ offset 52)\n * depth + uvScroll (16 floats = 64 bytes): [14..15] uvOffset.xy (float32x2 @ offset 56)\n * ```\n * The depth Z stays at slot [13]; uvOffset is appended after it. Non-scroll layers carry no\n * uvOffset slot and ship none of the widening (the wide layout lives entirely in the opt-in module).\n *\n * Visibility (`visible: false`) is implemented by zeroing slots [2..3]; the sprite's true\n * size lives in `layer._savedSize` so a later `visible: true` (without re-supplying\n * `sizePx`) can restore it. See `_savedSize` for the invariant.\n */\nexport const PURE_2D_INSTANCE_FLOATS_PER_SPRITE = 13;\nexport const DEPTH_INSTANCE_FLOATS_PER_SPRITE = 14;\n/** @internal Pure-2D per-sprite stride in bytes. */\nexport const PURE_2D_INSTANCE_STRIDE_BYTES = PURE_2D_INSTANCE_FLOATS_PER_SPRITE * 4;\n/** @internal Depth-hosted per-sprite stride in bytes. */\nexport const DEPTH_INSTANCE_STRIDE_BYTES = DEPTH_INSTANCE_FLOATS_PER_SPRITE * 4;\n/** @internal Per-sprite stride (in floats) of the `_savedSize` shadow buffer: `[w, h]`. */\nexport const SAVED_SIZE_FLOATS_PER_SPRITE = 2;\n\nconst DEFAULT_CAPACITY = 16;\n\n/** Create a new (empty) `Sprite2DLayer` backed by `atlas`. */\nexport function createSprite2DLayer(atlas: SpriteAtlas, opts: Sprite2DLayerOptions = {}): Sprite2DLayer {\n const depth = opts.depth ?? \"none\";\n const blendMode = opts.blendMode ?? spriteBlendAlpha;\n\n const capacity = Math.max(1, opts.capacity ?? DEFAULT_CAPACITY);\n const view: Sprite2DView = {\n positionPx: [opts.view?.positionPx?.[0] ?? 0, opts.view?.positionPx?.[1] ?? 0],\n zoom: opts.view?.zoom ?? 1,\n rotation: opts.view?.rotation ?? 0,\n };\n\n const instanceFloatsPerSprite = depth === \"none\" ? PURE_2D_INSTANCE_FLOATS_PER_SPRITE : DEPTH_INSTANCE_FLOATS_PER_SPRITE;\n const instanceStrideBytes = instanceFloatsPerSprite * 4;\n const instanceData = new F32(capacity * instanceFloatsPerSprite);\n const layer: Sprite2DLayer = {\n _entityType: \"sprite-2d-layer\",\n atlas,\n depth,\n blendMode,\n opacity: opts.opacity ?? 1,\n visible: opts.visible ?? true,\n order: opts.order ?? 0,\n view,\n pivot: [opts.pivot?.[0] ?? 0.5, opts.pivot?.[1] ?? 0.5],\n layerZ: opts.layerZ ?? 0.5,\n count: 0,\n _capacity: capacity,\n _instanceFloatsPerSprite: instanceFloatsPerSprite,\n _instanceStrideBytes: instanceStrideBytes,\n _instanceData: instanceData,\n _savedSize: new F32(capacity * SAVED_SIZE_FLOATS_PER_SPRITE),\n _version: 0,\n _dirtyMin: 0,\n _dirtyMax: 0,\n };\n // Zero-default-init discipline: the base layer never names `customShader` / `shaderParams`.\n // When (and only when) a custom shader was supplied, the registered hook copies it on — the\n // impl lives in the tree-shaken `sprite-custom-shader` module, so plain scenes ship none of it.\n _getSpriteFxHook()?.initLayer(layer, opts);\n return layer;\n}\n\n/**\n * Set the user `fx.params` vec4 fed to this layer's custom shader (`createSprite2DCustomShader`)\n * each frame. No visual effect unless the layer was created with a `customShader`. Read in WGSL\n * as `fx.params`. Mutates in place; the renderer re-uploads the small FX UBO next frame.\n */\nexport function setSprite2DShaderParams(layer: Sprite2DLayer, params: readonly [number, number, number, number]): void {\n // Lazy-allocate: the base layer never names `shaderParams` (the custom-shader hook sets it only\n // when `opts.customShader` is present), so a plain layer keeps the field off the always-loaded path.\n const target = (layer.shaderParams ??= [0, 0, 0, 0]);\n target[0] = params[0];\n target[1] = params[1];\n target[2] = params[2];\n target[3] = params[3];\n}\n\nfunction growCapacity(layer: Sprite2DLayer, minCapacity: number): void {\n let cap = layer._capacity;\n while (cap < minCapacity) {\n cap *= 2;\n }\n const next = new F32(cap * layer._instanceFloatsPerSprite);\n next.set(layer._instanceData);\n layer._instanceData = next;\n const nextSaved = new F32(cap * SAVED_SIZE_FLOATS_PER_SPRITE);\n nextSaved.set(layer._savedSize);\n layer._savedSize = nextSaved;\n layer._capacity = cap;\n}\n\nfunction setSprite2DCount(layer: Sprite2DLayer, count: number): void {\n (layer as { count: number }).count = count;\n}\n\n/**\n * Write one sprite's instance data into `layer._instanceData[base..base+layer._instanceFloatsPerSprite]`.\n *\n * Two call sites with different shapes:\n * - **add**: `prev === null`. `props` is a full `Sprite2DProps` (positionPx required).\n * Unspecified fields take their documented defaults (size=frame.sourceSizePx or 0,\n * UVs=[0,0,1,1], rotation=0, color=opaque white, visible=true).\n * - **update**: `prev` is the existing per-layer instance slice. Unspecified fields are preserved.\n *\n * Resolution rules (per field): `props` value if given, else (on add) the default, else `prev`.\n * `frame` is a higher-level intent: when supplied it stomps the four UV slots from the atlas\n * (then `flipX`/`flipY` swap them). It does **not** by itself imply a size change — `sizePx`\n * remains independent — but on add, a missing `sizePx` falls back to `frame.sourceSizePx`.\n *\n * **Visibility model (the part that needs explaining):**\n * - `_savedSize[slot]` always stores the sprite's *true* size (unaffected by visibility).\n * - `data[base+2..+3]` (the GPU-visible size) is `_savedSize` when visible, else `(0, 0)`.\n * - We detect previous visibility by checking `prev[2]==0 && prev[3]==0` (only hidden sprites\n * have zeroed GPU size). The CPU shadow gives us back the true size for free.\n */\nfunction writeInstance(layer: Sprite2DLayer, slotIndex: number, props: Partial<Sprite2DProps>, prev: Float32Array | null): void {\n const data = layer._instanceData;\n const base = slotIndex * layer._instanceFloatsPerSprite;\n const savedBase = slotIndex * SAVED_SIZE_FLOATS_PER_SPRITE; // [w, h] per sprite\n const isAdd = prev === null;\n\n // Optional frame lookup (used for UV stomp + size default on add).\n const frame = props.frame !== undefined ? layer.atlas.frames[resolveSpriteFrame(layer.atlas, props.frame)]! : null;\n\n // ── Position (required on add; preserved on update if omitted) ──────────────────────\n const posX = props.positionPx ? props.positionPx[0] : prev![0]!;\n const posY = props.positionPx ? props.positionPx[1] : prev![1]!;\n\n // ── True size (props.sizePx → frame default → previous true size) ───────────────────\n // The shadow buffer makes \"previous true size\" cheap and unambiguous regardless of visibility.\n let trueW: number;\n let trueH: number;\n if (props.sizePx) {\n trueW = props.sizePx[0];\n trueH = props.sizePx[1];\n } else if (frame) {\n trueW = frame.sourceSizePx[0];\n trueH = frame.sourceSizePx[1];\n } else if (isAdd) {\n trueW = 0;\n trueH = 0;\n } else {\n trueW = layer._savedSize[savedBase]!;\n trueH = layer._savedSize[savedBase + 1]!;\n }\n layer._savedSize[savedBase] = trueW;\n layer._savedSize[savedBase + 1] = trueH;\n\n // ── Visibility (props.visible → preserved → default true on add) ────────────────────\n let visible: boolean;\n if (props.visible !== undefined) {\n visible = props.visible;\n } else if (isAdd) {\n visible = true;\n } else {\n // Previous sprite was hidden iff its GPU size was zeroed.\n visible = prev![2]! !== 0 || prev![3]! !== 0;\n }\n\n // ── UVs (frame stomps; else preserved; else default [0,0,1,1] on add) ───────────────\n // flipX/flipY apply on top, by swapping the U/V endpoints.\n let uMin: number;\n let vMin: number;\n let uMax: number;\n let vMax: number;\n if (frame) {\n uMin = frame.uvMin[0];\n vMin = frame.uvMin[1];\n uMax = frame.uvMax[0];\n vMax = frame.uvMax[1];\n } else if (isAdd) {\n uMin = 0;\n vMin = 0;\n uMax = 1;\n vMax = 1;\n } else {\n uMin = prev![4]!;\n vMin = prev![5]!;\n uMax = prev![6]!;\n vMax = prev![7]!;\n }\n if (props.flipX === true) {\n const t = uMin;\n uMin = uMax;\n uMax = t;\n }\n if (props.flipY === true) {\n const t = vMin;\n vMin = vMax;\n vMax = t;\n }\n\n // ── Rotation ────────────────────────────────────────────────────────────────────────\n const rotation = props.rotation ?? (prev ? prev[8]! : 0);\n\n // ── Per-instance Z (depth-hosted only) ──────────────────────────────────────────────\n // Pure-2D layers intentionally have no slot [13]; `z` is accepted by the public API but\n // not allocated, uploaded, declared, or fetched by HUD pipelines.\n const hasDepthSlot = layer.depth !== \"none\";\n const z = hasDepthSlot ? (props.z ?? (prev ? prev[13]! : layer.layerZ)) : 0;\n\n // ── Write the float slots ──────────────────────────────────────────────────────────\n data[base + 0] = posX;\n data[base + 1] = posY;\n data[base + 2] = visible ? trueW : 0;\n data[base + 3] = visible ? trueH : 0;\n data[base + 4] = uMin;\n data[base + 5] = vMin;\n data[base + 6] = uMax;\n data[base + 7] = vMax;\n data[base + 8] = rotation;\n\n // ── Color (float32x4 to match Babylon.js SpriteRenderer's color precision) ─────────\n if (props.color) {\n data[base + 9] = props.color[0];\n data[base + 10] = props.color[1];\n data[base + 11] = props.color[2];\n data[base + 12] = props.color[3];\n } else if (isAdd) {\n data[base + 9] = 1;\n data[base + 10] = 1;\n data[base + 11] = 1;\n data[base + 12] = 1;\n }\n // else: previous color floats are already in place — nothing to write.\n\n // ── Per-instance Z (slot [13], depth-hosted layout only) ───────────────────────────\n if (hasDepthSlot) {\n data[base + 13] = z;\n }\n // Per-sprite uvOffset (wide layout) is written only via the opt-in `setSprite2DUvOffset`\n // (see `sprite-2d-uvscroll.ts`); `writeInstance` touches only base slots, so a wide layer's\n // uvOffset floats are preserved across add / update.\n}\n\nfunction markDirty(layer: Sprite2DLayer, lo: number, hi: number): void {\n if (layer._dirtyMin >= layer._dirtyMax) {\n layer._dirtyMin = lo;\n layer._dirtyMax = hi;\n } else {\n if (lo < layer._dirtyMin) {\n layer._dirtyMin = lo;\n }\n if (hi > layer._dirtyMax) {\n layer._dirtyMax = hi;\n }\n }\n layer._version = (layer._version + 1) | 0;\n}\n\n/** @internal Mark a dirty sprite range + bump version. Exposed for the opt-in uvScroll module. */\nexport function _markSprite2DDirty(layer: Sprite2DLayer, lo: number, hi: number): void {\n markDirty(layer, lo, hi);\n}\n\n/** Add one sprite. Returns its index. Grows capacity as needed. */\nexport function addSprite2DIndex(layer: Sprite2DLayer, props: Sprite2DProps): number {\n if (props.positionPx === undefined) {\n throw new Error(\"addSprite2DIndex: positionPx required.\");\n }\n const idx = layer.count;\n if (idx >= layer._capacity) {\n growCapacity(layer, idx + 1);\n }\n writeInstance(layer, idx, props, null);\n setSprite2DCount(layer, layer.count + 1);\n markDirty(layer, idx, idx + 1);\n return idx;\n}\n\n/** Patch one sprite. Unspecified fields are preserved. */\nexport function updateSprite2DIndex(layer: Sprite2DLayer, index: number, patch: Partial<Sprite2DProps>): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`updateSprite2DIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const base = index * layer._instanceFloatsPerSprite;\n const prev = layer._instanceData.subarray(base, base + layer._instanceFloatsPerSprite);\n writeInstance(layer, index, patch, prev);\n markDirty(layer, index, index + 1);\n}\n\n/** Swap-remove a sprite. The last sprite (if any) takes its slot. */\nexport function removeSprite2DIndex(layer: Sprite2DLayer, index: number): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`removeSprite2DIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const last = layer.count - 1;\n layer._handleHooks?.removeIndex(index, last);\n if (index !== last) {\n layer._instanceData.copyWithin(index * layer._instanceFloatsPerSprite, last * layer._instanceFloatsPerSprite, (last + 1) * layer._instanceFloatsPerSprite);\n // Carry the swapped sprite's saved-size shadow with it (`[w, h]` per sprite).\n layer._savedSize.copyWithin(index * SAVED_SIZE_FLOATS_PER_SPRITE, last * SAVED_SIZE_FLOATS_PER_SPRITE, (last + 1) * SAVED_SIZE_FLOATS_PER_SPRITE);\n }\n // Clear the now-unused tail saved-size slot so a future re-add starts clean.\n layer._savedSize[last * SAVED_SIZE_FLOATS_PER_SPRITE] = 0;\n layer._savedSize[last * SAVED_SIZE_FLOATS_PER_SPRITE + 1] = 0;\n setSprite2DCount(layer, last);\n markDirty(layer, index, index + 1);\n}\n\n/** Clear all sprites from a layer while preserving allocated capacity. */\nexport function clearSprite2DLayer(layer: Sprite2DLayer): void {\n const count = layer.count;\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n layer._handleHooks?.clear();\n if (count === 0) {\n return;\n }\n layer._savedSize.fill(0, 0, count * SAVED_SIZE_FLOATS_PER_SPRITE);\n setSprite2DCount(layer, 0);\n layer._version = (layer._version + 1) | 0;\n}\n\n/**\n * Update only the frame UVs for one sprite.\n *\n * The sprite keeps its explicit `sizePx`/saved size. For atlas-driven size\n * changes, call `updateSprite2DIndex(layer, index, { frame, sizePx })`.\n * Existing flip state is preserved for non-degenerate UV ranges.\n */\nexport function setSprite2DFrameIndex(layer: Sprite2DLayer, index: number, frame: number): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`setSprite2DFrameIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const frameIdx = resolveSpriteFrame(layer.atlas, frame);\n const f = layer.atlas.frames[frameIdx]!;\n const base = index * layer._instanceFloatsPerSprite;\n const flipX = layer._instanceData[base + 4]! > layer._instanceData[base + 6]!;\n const flipY = layer._instanceData[base + 5]! > layer._instanceData[base + 7]!;\n layer._instanceData[base + 4] = flipX ? f.uvMax[0] : f.uvMin[0];\n layer._instanceData[base + 5] = flipY ? f.uvMax[1] : f.uvMin[1];\n layer._instanceData[base + 6] = flipX ? f.uvMin[0] : f.uvMax[0];\n layer._instanceData[base + 7] = flipY ? f.uvMin[1] : f.uvMax[1];\n markDirty(layer, index, index + 1);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sprite-2d.js","sources":["../../../src/sprite/sprite-2d.ts"],"sourcesContent":["/**\n * `Sprite2DLayer` — pixel-coordinate sprite layer. Pure-data interface +\n * standalone Index API for add / update / remove / setFrame. The layer is\n * owned by a `SpriteRenderer` (pure-2D / HUD `depth: \"none\"` path) or by a\n * scene renderable added through `addDepthHostedSpriteLayer`\n * (`depth: \"test\" | \"test-write\"`).\n *\n * The Index API stays the storage foundation. The optional Handle API lives in\n * `sprite-2d-handle.ts` and lazily installs hooks only when handles are used.\n */\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { SpriteAtlas } from \"./shared/sprite-atlas.js\";\nimport { resolveSpriteFrame } from \"./shared/sprite-atlas.js\";\nimport type { Sprite2DCustomShader } from \"./sprite-custom-shader.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport type { SpriteBlendDescriptor } from \"./sprite-blend.js\";\nimport { spriteBlendAlpha } from \"./sprite-blend.js\";\n\n/**\n * Output blend mode for a sprite layer — a pure-data descriptor value. Import one of\n * `spriteBlendAlpha` (default), `spriteBlendPremultiplied`, or `spriteBlendAdditive` and pass\n * it as `Sprite2DLayerOptions.blendMode`. (Type alias of {@link SpriteBlendDescriptor}.)\n */\nexport type SpriteBlendMode = SpriteBlendDescriptor;\n\n/** Depth participation. `\"none\"` uses `SpriteRenderer`; depth-enabled modes use `addToScene`. */\nexport type Sprite2DDepthMode = \"none\" | \"test\" | \"test-write\";\n\n/** Per-layer 2D camera (pan / zoom / rotation). Identity = pixel-perfect HUD. */\nexport interface Sprite2DView {\n positionPx: [number, number];\n zoom: number;\n rotation: number;\n}\n\n/** Options accepted by `createSprite2DLayer`. */\nexport interface Sprite2DLayerOptions {\n capacity?: number;\n blendMode?: SpriteBlendMode;\n opacity?: number;\n visible?: boolean;\n order?: number;\n view?: Partial<Sprite2DView>;\n depth?: Sprite2DDepthMode;\n /**\n * Layer-wide rotation / scaling pivot in normalised sprite-local space\n * (`[0,0]` = top-left, `[0.5, 0.5]` = center, `[1,1]` = bottom-right).\n * The pivot point of every sprite in the layer lands at its `positionPx`\n * and is the center of `rotation`. Defaults to `[0.5, 0.5]` (center) to\n * match Babylon.js sprite behavior. Per-sprite / per-frame pivot is a\n * future PR — most 2D HUD layers want one uniform pivot anyway.\n */\n pivot?: [number, number];\n /**\n * Opt-in per-layer custom fragment shader (see `createSprite2DCustomShader`). Works on both\n * pure-2D (`depth: \"none\"`) layers drawn by a `SpriteRenderer` and depth-hosted\n * (`depth: \"test\" | \"test-write\"`) layers. Drives procedural effects (animated sky, clouds,\n * water / heat shimmer, twinkle, vignette) from a built-in `fx.time` clock plus an optional\n * `fx.params` vec4 set via `setSprite2DShaderParams`.\n */\n customShader?: Sprite2DCustomShader;\n /**\n * Default NDC depth (`0` = near, `1` = far) for sprites added to this layer when their\n * `Sprite2DProps.z` is omitted. Only meaningful for `depth: \"test\" | \"test-write\"` layers\n * (depth-hosted sprites added to a `SceneContext` via `addDepthHostedSpriteLayer`).\n *\n * Depth-hosted layers store one Z per sprite (slot [13] of their 14-float instance buffer),\n * so a single layer can mix sprites at different depths — e.g. one in front of a box, one\n * behind it. Pure-2D layers (`depth: \"none\"`) use the 13-float HUD layout and carry no Z slot.\n * Defaults to `0.5`. Mutating `layer.layerZ` after sprites have been added does **not**\n * retroactively change them; it only affects sprites added afterwards. To move an existing\n * depth-hosted sprite, call `updateSprite2DIndex(layer, idx, { z: … })`.\n */\n layerZ?: number;\n}\n\n/** A `Sprite2DLayer` — pure data, no methods. */\nexport interface Sprite2DLayer {\n /** @internal */\n readonly _entityType: \"sprite-2d-layer\";\n readonly atlas: SpriteAtlas;\n readonly depth: Sprite2DDepthMode;\n readonly blendMode: SpriteBlendMode;\n opacity: number;\n visible: boolean;\n order: number;\n view: Sprite2DView;\n /** Layer-wide pivot in normalised sprite-local space; see `Sprite2DLayerOptions.pivot`. */\n pivot: [number, number];\n /**\n * Opt-in custom fragment shader for this layer; see `Sprite2DLayerOptions.customShader`.\n * **Absent** (not `null`) on plain layers — never default-initialized, so the always-loaded\n * path carries zero custom-shader bytes (see `sprite-fx-hook.ts`). Present only when the\n * layer was created with a `customShader`.\n */\n readonly customShader?: Sprite2DCustomShader;\n /**\n * User `fx.params` vec4 fed to a custom shader each frame; mutate via `setSprite2DShaderParams`.\n * **Absent** on plain layers (only allocated for custom-shader layers, or lazily by the setter).\n */\n shaderParams?: [number, number, number, number];\n /**\n * @internal Opt-in per-sprite UV-scroll vertex attribute (`uvOffset.xy` at `@location(7)`);\n * enabled via {@link setSprite2DUvOffset}. **Absent** (`undefined`) on plain layers — never\n * default-initialized, so the always-loaded path and every non-scroll sprite scene keep the\n * narrow layout and base shader. The first `setSprite2DUvOffset` call widens the instance stride\n * by two floats and stashes the precomputed attribute here, so the always-loaded pipeline just\n * *consumes* it as data (`instanceAttributes.push(layer._uvScrollAttr)`) — the attribute-building\n * code lives entirely in the opt-in `sprite-2d-uvscroll.ts`. Presence also serves as the\n * \"is wide\" flag (drives the shader permutation key + the prologue `+ in.iUvOffset`).\n */\n _uvScrollAttr?: GPUVertexAttribute;\n /**\n * @internal Opt-in coverage-gamma value (glyph \"stem darkening\"); set only via\n * {@link setSprite2DCoverageGamma}. **Absent** (`undefined`, treated as `1` = no-op) on plain\n * layers — never default-initialized, so the always-loaded path keeps the base shader and\n * non-gamma scenes ship zero gamma bytes. A value `!= null && !== 1` activates the gamma shader\n * permutation (the per-fragment `pow`) and is read each frame into the layer UBO; it stays\n * live-mutable through the setter. Internal so callers can't write a value that the always-loaded\n * path would silently ignore — enabling is exclusively the setter's job.\n */\n _coverageGamma?: number;\n /** Default NDC depth for newly added sprites; see `Sprite2DLayerOptions.layerZ`. */\n layerZ: number;\n readonly count: number;\n\n /** @internal Capacity of the per-instance buffer (in sprites). */\n _capacity: number;\n /** @internal Per-instance stride in floats; 13 for pure-2D, 14 for depth-hosted. */\n readonly _instanceFloatsPerSprite: number;\n /** @internal Per-instance stride in bytes; 52 for pure-2D, 56 for depth-hosted. */\n readonly _instanceStrideBytes: number;\n /** @internal Per-instance CPU staging buffer; layout = `_instanceFloatsPerSprite` per sprite. */\n _instanceData: Float32Array;\n /**\n * @internal CPU-only side buffer holding the **true** (un-hidden) size of every sprite,\n * laid out as `[w0, h0, w1, h1, …]` (`SAVED_SIZE_FLOATS_PER_SPRITE` = 2 floats per sprite).\n *\n * **Invariant:** this buffer always holds the sprite's real size, regardless of visibility.\n * It exists because `visible: false` is implemented by zeroing the GPU-side size slots\n * (degenerate quad → free rasterizer cull) — a free hide on the GPU at the cost of 8 B per\n * sprite on the CPU. Without this shadow, a `visible: true` patch that omits `sizePx`\n * would have no way to recover the original size. Grown in lockstep with `_instanceData`.\n */\n _savedSize: Float32Array;\n /** @internal Bumped on any structural / per-instance edit; renderer compares. */\n _version: number;\n /** @internal Min dirty index inclusive (for partial uploads). */\n _dirtyMin: number;\n /** @internal Max dirty index exclusive. */\n _dirtyMax: number;\n /** @internal Optional hooks installed by the opt-in handle module. */\n _handleHooks?: Sprite2DIndexHandleHooks;\n}\n\n/** @internal Lazy hooks used by the opt-in Handle API to track swap-removes. */\nexport interface Sprite2DIndexHandleHooks {\n readonly removeIndex: (index: number, last: number) => void;\n readonly clear: () => void;\n}\n\n/** Per-sprite init record passed to `addSprite2DIndex` / `updateSprite2DIndex`. */\nexport interface Sprite2DProps {\n positionPx: [number, number];\n sizePx?: [number, number];\n frame?: number;\n rotation?: number;\n color?: [number, number, number, number];\n flipX?: boolean;\n flipY?: boolean;\n visible?: boolean;\n /** Reserved for clip animation. Accepted but unused today. */\n clip?: unknown;\n /**\n * Per-sprite NDC depth (`0` = near, `1` = far). Only stored and consumed by depth-hosted\n * layers (`depth: \"test\" | \"test-write\"`); pure-2D HUD layers use a 13-float layout and\n * do not allocate a Z slot. When omitted on add for a depth-hosted layer, defaults to the\n * **owning layer's** `layerZ` at the moment of insertion. When omitted on update, the\n * sprite's existing Z is preserved. Mutate freely — the next binding update will re-upload\n * only the dirty range.\n */\n z?: number;\n}\n\n/**\n * Pure-2D per-instance vertex layout (13 floats = 52 bytes, `depth: \"none\"`):\n * ```\n * [0..1] positionPx.xy (float32x2 @ offset 0)\n * [2..3] sizePx.xy (float32x2 @ offset 8)\n * [4..5] uvMin.xy (float32x2 @ offset 16)\n * [6..7] uvMax.xy (float32x2 @ offset 24)\n * [8] rotation (float32 @ offset 32)\n * [9..12] colorRGBA (float32x4 @ offset 36)\n * ```\n *\n * Depth-hosted layers (`depth: \"test\" | \"test-write\"`) extend this to 14 floats = 56 bytes:\n * ```\n * [13] z (NDC depth) (float32 @ offset 52, consumed only by depth-hosted pipelines)\n * ```\n *\n * The opt-in per-sprite UV-scroll feature (`setSprite2DUvOffset`, see `sprite-2d-uvscroll.ts`)\n * appends two more floats (`uvOffset.xy`) *after* the base layout, orthogonally to depth:\n * ```\n * pure-2D + uvScroll (15 floats = 60 bytes): [13..14] uvOffset.xy (float32x2 @ offset 52)\n * depth + uvScroll (16 floats = 64 bytes): [14..15] uvOffset.xy (float32x2 @ offset 56)\n * ```\n * The depth Z stays at slot [13]; uvOffset is appended after it. Non-scroll layers carry no\n * uvOffset slot and ship none of the widening (the wide layout lives entirely in the opt-in module).\n *\n * Visibility (`visible: false`) is implemented by zeroing slots [2..3]; the sprite's true\n * size lives in `layer._savedSize` so a later `visible: true` (without re-supplying\n * `sizePx`) can restore it. See `_savedSize` for the invariant.\n */\nexport const PURE_2D_INSTANCE_FLOATS_PER_SPRITE = 13;\nexport const DEPTH_INSTANCE_FLOATS_PER_SPRITE = 14;\n/** @internal Pure-2D per-sprite stride in bytes. */\nexport const PURE_2D_INSTANCE_STRIDE_BYTES = PURE_2D_INSTANCE_FLOATS_PER_SPRITE * 4;\n/** @internal Depth-hosted per-sprite stride in bytes. */\nexport const DEPTH_INSTANCE_STRIDE_BYTES = DEPTH_INSTANCE_FLOATS_PER_SPRITE * 4;\n/** @internal Per-sprite stride (in floats) of the `_savedSize` shadow buffer: `[w, h]`. */\nexport const SAVED_SIZE_FLOATS_PER_SPRITE = 2;\n\nconst DEFAULT_CAPACITY = 16;\n\n/** Create a new (empty) `Sprite2DLayer` backed by `atlas`. */\nexport function createSprite2DLayer(atlas: SpriteAtlas, opts: Sprite2DLayerOptions = {}): Sprite2DLayer {\n const depth = opts.depth ?? \"none\";\n const blendMode = opts.blendMode ?? spriteBlendAlpha;\n\n const capacity = Math.max(1, opts.capacity ?? DEFAULT_CAPACITY);\n const view: Sprite2DView = {\n positionPx: [opts.view?.positionPx?.[0] ?? 0, opts.view?.positionPx?.[1] ?? 0],\n zoom: opts.view?.zoom ?? 1,\n rotation: opts.view?.rotation ?? 0,\n };\n\n const instanceFloatsPerSprite = depth === \"none\" ? PURE_2D_INSTANCE_FLOATS_PER_SPRITE : DEPTH_INSTANCE_FLOATS_PER_SPRITE;\n const instanceStrideBytes = instanceFloatsPerSprite * 4;\n const instanceData = new F32(capacity * instanceFloatsPerSprite);\n const layer: Sprite2DLayer = {\n _entityType: \"sprite-2d-layer\",\n atlas,\n depth,\n blendMode,\n opacity: opts.opacity ?? 1,\n visible: opts.visible ?? true,\n order: opts.order ?? 0,\n view,\n pivot: [opts.pivot?.[0] ?? 0.5, opts.pivot?.[1] ?? 0.5],\n layerZ: opts.layerZ ?? 0.5,\n count: 0,\n _capacity: capacity,\n _instanceFloatsPerSprite: instanceFloatsPerSprite,\n _instanceStrideBytes: instanceStrideBytes,\n _instanceData: instanceData,\n _savedSize: new F32(capacity * SAVED_SIZE_FLOATS_PER_SPRITE),\n _version: 0,\n _dirtyMin: 0,\n _dirtyMax: 0,\n };\n // Zero-default-init discipline: the base layer never names `customShader` / `shaderParams`.\n // When (and only when) a custom shader was supplied, the registered hook copies it on — the\n // impl lives in the tree-shaken `sprite-custom-shader` module, so plain scenes ship none of it.\n _getSpriteFxHook()?.initLayer(layer, opts);\n return layer;\n}\n\n/**\n * Set the user `fx.params` vec4 fed to this layer's custom shader (`createSprite2DCustomShader`)\n * each frame. No visual effect unless the layer was created with a `customShader`. Read in WGSL\n * as `fx.params`. Mutates in place; the renderer re-uploads the small FX UBO next frame.\n */\nexport function setSprite2DShaderParams(layer: Sprite2DLayer, params: readonly [number, number, number, number]): void {\n // Lazy-allocate: the base layer never names `shaderParams` (the custom-shader hook sets it only\n // when `opts.customShader` is present), so a plain layer keeps the field off the always-loaded path.\n const target = (layer.shaderParams ??= [0, 0, 0, 0]);\n target[0] = params[0];\n target[1] = params[1];\n target[2] = params[2];\n target[3] = params[3];\n}\n\nfunction growCapacity(layer: Sprite2DLayer, minCapacity: number): void {\n let cap = layer._capacity;\n while (cap < minCapacity) {\n cap *= 2;\n }\n const next = new F32(cap * layer._instanceFloatsPerSprite);\n next.set(layer._instanceData);\n layer._instanceData = next;\n const nextSaved = new F32(cap * SAVED_SIZE_FLOATS_PER_SPRITE);\n nextSaved.set(layer._savedSize);\n layer._savedSize = nextSaved;\n layer._capacity = cap;\n}\n\nfunction setSprite2DCount(layer: Sprite2DLayer, count: number): void {\n (layer as { count: number }).count = count;\n}\n\n/**\n * Write one sprite's instance data into `layer._instanceData[base..base+layer._instanceFloatsPerSprite]`.\n *\n * Two call sites with different shapes:\n * - **add**: `prev === null`. `props` is a full `Sprite2DProps` (positionPx required).\n * Unspecified fields take their documented defaults (size=frame.sourceSizePx or 0,\n * UVs=[0,0,1,1], rotation=0, color=opaque white, visible=true).\n * - **update**: `prev` is the existing per-layer instance slice. Unspecified fields are preserved.\n *\n * Resolution rules (per field): `props` value if given, else (on add) the default, else `prev`.\n * `frame` is a higher-level intent: when supplied it stomps the four UV slots from the atlas\n * (then `flipX`/`flipY` swap them). It does **not** by itself imply a size change — `sizePx`\n * remains independent — but on add, a missing `sizePx` falls back to `frame.sourceSizePx`.\n *\n * **Visibility model (the part that needs explaining):**\n * - `_savedSize[slot]` always stores the sprite's *true* size (unaffected by visibility).\n * - `data[base+2..+3]` (the GPU-visible size) is `_savedSize` when visible, else `(0, 0)`.\n * - We detect previous visibility by checking `prev[2]==0 && prev[3]==0` (only hidden sprites\n * have zeroed GPU size). The CPU shadow gives us back the true size for free.\n */\nfunction writeInstance(layer: Sprite2DLayer, slotIndex: number, props: Partial<Sprite2DProps>, prev: Float32Array | null): void {\n const data = layer._instanceData;\n const base = slotIndex * layer._instanceFloatsPerSprite;\n const savedBase = slotIndex * SAVED_SIZE_FLOATS_PER_SPRITE; // [w, h] per sprite\n const isAdd = prev === null;\n\n // Optional frame lookup (used for UV stomp + size default on add).\n const frame = props.frame !== undefined ? layer.atlas.frames[resolveSpriteFrame(layer.atlas, props.frame)]! : null;\n\n // ── Position (required on add; preserved on update if omitted) ──────────────────────\n const posX = props.positionPx ? props.positionPx[0] : prev![0]!;\n const posY = props.positionPx ? props.positionPx[1] : prev![1]!;\n\n // ── True size (props.sizePx → frame default → previous true size) ───────────────────\n // The shadow buffer makes \"previous true size\" cheap and unambiguous regardless of visibility.\n let trueW: number;\n let trueH: number;\n if (props.sizePx) {\n trueW = props.sizePx[0];\n trueH = props.sizePx[1];\n } else if (frame) {\n trueW = frame.sourceSizePx[0];\n trueH = frame.sourceSizePx[1];\n } else if (isAdd) {\n trueW = 0;\n trueH = 0;\n } else {\n trueW = layer._savedSize[savedBase]!;\n trueH = layer._savedSize[savedBase + 1]!;\n }\n layer._savedSize[savedBase] = trueW;\n layer._savedSize[savedBase + 1] = trueH;\n\n // ── Visibility (props.visible → preserved → default true on add) ────────────────────\n let visible: boolean;\n if (props.visible !== undefined) {\n visible = props.visible;\n } else if (isAdd) {\n visible = true;\n } else {\n // Previous sprite was hidden iff its GPU size was zeroed.\n visible = prev![2]! !== 0 || prev![3]! !== 0;\n }\n\n // ── UVs (frame stomps; else preserved; else default [0,0,1,1] on add) ───────────────\n // flipX/flipY apply on top, by swapping the U/V endpoints.\n let uMin: number;\n let vMin: number;\n let uMax: number;\n let vMax: number;\n if (frame) {\n uMin = frame.uvMin[0];\n vMin = frame.uvMin[1];\n uMax = frame.uvMax[0];\n vMax = frame.uvMax[1];\n } else if (isAdd) {\n uMin = 0;\n vMin = 0;\n uMax = 1;\n vMax = 1;\n } else {\n uMin = prev![4]!;\n vMin = prev![5]!;\n uMax = prev![6]!;\n vMax = prev![7]!;\n }\n if (props.flipX === true) {\n const t = uMin;\n uMin = uMax;\n uMax = t;\n }\n if (props.flipY === true) {\n const t = vMin;\n vMin = vMax;\n vMax = t;\n }\n\n // ── Rotation ────────────────────────────────────────────────────────────────────────\n const rotation = props.rotation ?? (prev ? prev[8]! : 0);\n\n // ── Per-instance Z (depth-hosted only) ──────────────────────────────────────────────\n // Pure-2D layers intentionally have no slot [13]; `z` is accepted by the public API but\n // not allocated, uploaded, declared, or fetched by HUD pipelines.\n const hasDepthSlot = layer.depth !== \"none\";\n const z = hasDepthSlot ? (props.z ?? (prev ? prev[13]! : layer.layerZ)) : 0;\n\n // ── Write the float slots ──────────────────────────────────────────────────────────\n data[base + 0] = posX;\n data[base + 1] = posY;\n data[base + 2] = visible ? trueW : 0;\n data[base + 3] = visible ? trueH : 0;\n data[base + 4] = uMin;\n data[base + 5] = vMin;\n data[base + 6] = uMax;\n data[base + 7] = vMax;\n data[base + 8] = rotation;\n\n // ── Color (float32x4 to match Babylon.js SpriteRenderer's color precision) ─────────\n if (props.color) {\n data[base + 9] = props.color[0];\n data[base + 10] = props.color[1];\n data[base + 11] = props.color[2];\n data[base + 12] = props.color[3];\n } else if (isAdd) {\n data[base + 9] = 1;\n data[base + 10] = 1;\n data[base + 11] = 1;\n data[base + 12] = 1;\n }\n // else: previous color floats are already in place — nothing to write.\n\n // ── Per-instance Z (slot [13], depth-hosted layout only) ───────────────────────────\n if (hasDepthSlot) {\n data[base + 13] = z;\n }\n // Per-sprite uvOffset (wide layout) is written only via the opt-in `setSprite2DUvOffset`\n // (see `sprite-2d-uvscroll.ts`); `writeInstance` touches only base slots, so a wide layer's\n // uvOffset floats are preserved across add / update.\n}\n\nfunction markDirty(layer: Sprite2DLayer, lo: number, hi: number): void {\n if (layer._dirtyMin >= layer._dirtyMax) {\n layer._dirtyMin = lo;\n layer._dirtyMax = hi;\n } else {\n if (lo < layer._dirtyMin) {\n layer._dirtyMin = lo;\n }\n if (hi > layer._dirtyMax) {\n layer._dirtyMax = hi;\n }\n }\n layer._version = (layer._version + 1) | 0;\n}\n\n/** @internal Mark a dirty sprite range + bump version. Exposed for the opt-in uvScroll module. */\nexport function _markSprite2DDirty(layer: Sprite2DLayer, lo: number, hi: number): void {\n markDirty(layer, lo, hi);\n}\n\n/** Add one sprite. Returns its index. Grows capacity as needed. */\nexport function addSprite2DIndex(layer: Sprite2DLayer, props: Sprite2DProps): number {\n if (props.positionPx === undefined) {\n throw new Error(\"addSprite2DIndex: positionPx required.\");\n }\n const idx = layer.count;\n if (idx >= layer._capacity) {\n growCapacity(layer, idx + 1);\n }\n writeInstance(layer, idx, props, null);\n setSprite2DCount(layer, layer.count + 1);\n markDirty(layer, idx, idx + 1);\n return idx;\n}\n\n/** Patch one sprite. Unspecified fields are preserved. */\nexport function updateSprite2DIndex(layer: Sprite2DLayer, index: number, patch: Partial<Sprite2DProps>): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`updateSprite2DIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const base = index * layer._instanceFloatsPerSprite;\n const prev = layer._instanceData.subarray(base, base + layer._instanceFloatsPerSprite);\n writeInstance(layer, index, patch, prev);\n markDirty(layer, index, index + 1);\n}\n\n/** Swap-remove a sprite. The last sprite (if any) takes its slot. */\nexport function removeSprite2DIndex(layer: Sprite2DLayer, index: number): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`removeSprite2DIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const last = layer.count - 1;\n layer._handleHooks?.removeIndex(index, last);\n if (index !== last) {\n layer._instanceData.copyWithin(index * layer._instanceFloatsPerSprite, last * layer._instanceFloatsPerSprite, (last + 1) * layer._instanceFloatsPerSprite);\n // Carry the swapped sprite's saved-size shadow with it (`[w, h]` per sprite).\n layer._savedSize.copyWithin(index * SAVED_SIZE_FLOATS_PER_SPRITE, last * SAVED_SIZE_FLOATS_PER_SPRITE, (last + 1) * SAVED_SIZE_FLOATS_PER_SPRITE);\n }\n // Clear the now-unused tail saved-size slot so a future re-add starts clean.\n layer._savedSize[last * SAVED_SIZE_FLOATS_PER_SPRITE] = 0;\n layer._savedSize[last * SAVED_SIZE_FLOATS_PER_SPRITE + 1] = 0;\n setSprite2DCount(layer, last);\n markDirty(layer, index, index + 1);\n}\n\n/** Clear all sprites from a layer while preserving allocated capacity. */\nexport function clearSprite2DLayer(layer: Sprite2DLayer): void {\n const count = layer.count;\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n layer._handleHooks?.clear();\n if (count === 0) {\n return;\n }\n layer._savedSize.fill(0, 0, count * SAVED_SIZE_FLOATS_PER_SPRITE);\n setSprite2DCount(layer, 0);\n layer._version = (layer._version + 1) | 0;\n}\n\n/**\n * Update only the frame UVs for one sprite.\n *\n * The sprite keeps its explicit `sizePx`/saved size. For atlas-driven size\n * changes, call `updateSprite2DIndex(layer, index, { frame, sizePx })`.\n * Existing flip state is preserved for non-degenerate UV ranges.\n */\nexport function setSprite2DFrameIndex(layer: Sprite2DLayer, index: number, frame: number): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`setSprite2DFrameIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const frameIdx = resolveSpriteFrame(layer.atlas, frame);\n const f = layer.atlas.frames[frameIdx]!;\n const base = index * layer._instanceFloatsPerSprite;\n const flipX = layer._instanceData[base + 4]! > layer._instanceData[base + 6]!;\n const flipY = layer._instanceData[base + 5]! > layer._instanceData[base + 7]!;\n layer._instanceData[base + 4] = flipX ? f.uvMax[0] : f.uvMin[0];\n layer._instanceData[base + 5] = flipY ? f.uvMax[1] : f.uvMin[1];\n layer._instanceData[base + 6] = flipX ? f.uvMin[0] : f.uvMax[0];\n layer._instanceData[base + 7] = flipY ? f.uvMin[1] : f.uvMax[1];\n markDirty(layer, index, index + 1);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -81,6 +81,23 @@ function createSpritePipelineCache() {
|
|
|
81
81
|
function resetSpritePipelineCache(cache) {
|
|
82
82
|
cache._devices = /* @__PURE__ */ new WeakMap();
|
|
83
83
|
}
|
|
84
|
+
let _sharedSpriteRendererPipelineCache = null;
|
|
85
|
+
let _sharedSpriteRendererPipelineCacheRefs = 0;
|
|
86
|
+
function acquireSharedSpriteRendererPipelineCache() {
|
|
87
|
+
_sharedSpriteRendererPipelineCache ??= createSpritePipelineCache();
|
|
88
|
+
_sharedSpriteRendererPipelineCacheRefs++;
|
|
89
|
+
return _sharedSpriteRendererPipelineCache;
|
|
90
|
+
}
|
|
91
|
+
function releaseSharedSpriteRendererPipelineCache() {
|
|
92
|
+
if (_sharedSpriteRendererPipelineCacheRefs === 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
_sharedSpriteRendererPipelineCacheRefs--;
|
|
96
|
+
if (_sharedSpriteRendererPipelineCacheRefs === 0 && _sharedSpriteRendererPipelineCache) {
|
|
97
|
+
resetSpritePipelineCache(_sharedSpriteRendererPipelineCache);
|
|
98
|
+
_sharedSpriteRendererPipelineCache = null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
84
101
|
function getSpritePipelineCacheSize(cache, device) {
|
|
85
102
|
return cache._devices.get(device)?._pipelines.size ?? 0;
|
|
86
103
|
}
|
|
@@ -312,5 +329,5 @@ function writeSpriteLayerUboIfDirty(device, uniformBuffer, scratchUbo, lastUbo,
|
|
|
312
329
|
return true;
|
|
313
330
|
}
|
|
314
331
|
|
|
315
|
-
export { LAYER_UBO_BYTES, LAYER_UBO_FLOATS, SHARED_SPRITE_INDEX_DATA, buildSpriteLayerUbo, createSpriteInstanceBuffer, createSpriteLayerBindGroup, createSpritePipelineCache, ensureSpriteInstanceBuffer, getOrCreateSpritePipeline, getSpritePipelineCacheSize, makeSpritePrologueWgsl, resetSpritePipelineCache, uploadSpriteInstances, writeSpriteLayerUboIfDirty };
|
|
332
|
+
export { LAYER_UBO_BYTES, LAYER_UBO_FLOATS, SHARED_SPRITE_INDEX_DATA, acquireSharedSpriteRendererPipelineCache, buildSpriteLayerUbo, createSpriteInstanceBuffer, createSpriteLayerBindGroup, createSpritePipelineCache, ensureSpriteInstanceBuffer, getOrCreateSpritePipeline, getSpritePipelineCacheSize, makeSpritePrologueWgsl, releaseSharedSpriteRendererPipelineCache, resetSpritePipelineCache, uploadSpriteInstances, writeSpriteLayerUboIfDirty };
|
|
316
333
|
//# sourceMappingURL=sprite-pipeline.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sprite-pipeline.js","sources":["../../../src/sprite/sprite-pipeline.ts"],"sourcesContent":["/** Internal sprite pipeline helpers: owns WGSL, bind-group schema, pipeline construction, and bind-group creation. */\nimport { U16 } from \"../engine/typed-arrays.js\";\nimport { BU, SS, CW } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Sprite2DLayer, SpriteBlendMode } from \"./sprite-2d.js\";\nimport type { SpriteLayerFx } from \"./custom-shader-core.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport { _getSpriteCoverageGammaHook } from \"./sprite-coverage-gamma-hook.js\";\nimport { DEPTH_INSTANCE_STRIDE_BYTES, PURE_2D_INSTANCE_STRIDE_BYTES } from \"./sprite-2d.js\";\n\n/** @internal */\nexport interface SpritePipelineDeviceCache {\n /** @internal Shader modules keyed by `${hasDepth}:${uvScroll}` permutation. */\n _shaderModules: Map<string, GPUShaderModule>;\n /** @internal */\n _pipelines: Map<string, GPURenderPipeline>;\n}\n\n/** @internal */\nexport interface SpritePipelineCache {\n /** @internal */\n _devices: WeakMap<GPUDevice, SpritePipelineDeviceCache>;\n}\n\nconst SPRITE_POSITION_OFFSET_BYTES = 0;\nconst SPRITE_SIZE_OFFSET_BYTES = 8;\nconst SPRITE_UV_MIN_OFFSET_BYTES = 16;\nconst SPRITE_UV_MAX_OFFSET_BYTES = 24;\nconst SPRITE_ROTATION_OFFSET_BYTES = 32;\nconst SPRITE_COLOR_OFFSET_BYTES = 36;\nconst SPRITE_DEPTH_OFFSET_BYTES = 52;\n\nfunction makeSpriteWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll: boolean): string {\n return `${makeSpritePrologueWgsl(hasDepth, spriteGroupIndex, uvScroll)}\n@fragment\nfn fs(in: O) -> @location(0) vec4f {\nlet s = textureSample(atlasTex, atlasSamp, in.uv);\nreturn s * in.tint * L.opacityMul;\n}`;\n}\n\n/**\n * Shared WGSL prologue for the sprite shader: the `Layer` UBO, atlas texture + sampler\n * bindings, instance-attribute `VIn` / interpolant `VOut` structs, and the `vs` vertex stage.\n * The default sprite shader appends a trivial textured fragment; the opt-in custom-shader\n * module (`createSprite2DCustomShader`) appends any extra-texture bindings, a `SpriteFx` UBO at\n * `@binding(3 + 2 * extraTextures.length)`, and the user's raw fragment body. Exposed so both\n * paths share one source of truth.\n */\nexport function makeSpritePrologueWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll = false): string {\n const group = `@group(${spriteGroupIndex})`;\n const zAttribute = hasDepth ? `,\\n@location(6) z: f32` : \"\";\n const uvOffsetAttribute = uvScroll ? `,\\n@location(7) o: vec2f` : \"\";\n const zPosition = hasDepth ? \"1 - in.z\" : \"0\";\n return `struct Lr {\nviewPos: vec2f,\nviewScale: f32,\nviewRot: f32,\nscreenSize: vec2f,\npivot: vec2f,\nopacityMul: vec4f,\naa: vec4f,\n};\n${group} @binding(0) var<uniform> L: Lr;\n${group} @binding(1) var atlasTex: texture_2d<f32>;\n${group} @binding(2) var atlasSamp: sampler;\nstruct I {\n@builtin(vertex_index) vid: u32,\n@location(0) p: vec2f,\n@location(1) s: vec2f,\n@location(2) a: vec2f,\n@location(3) b: vec2f,\n@location(4) r: f32,\n@location(5) c: vec4f${zAttribute}${uvOffsetAttribute}\n};\nstruct O {\n@builtin(position) p: vec4f,\n@location(0) uv: vec2f,\n@location(1) tint: vec4f,\n};\n@vertex\nfn vs(in: I) -> O {\nvar q = array<vec2f, 4>(vec2f(0, 0), vec2f(1, 0), vec2f(1, 1), vec2f(0, 1));\nlet c = q[in.vid];\nlet l = (c - L.pivot) * in.s;\nlet cr = cos(in.r);\nlet sr = sin(in.r);\nlet r = vec2f(l.x * cr - l.y * sr, l.x * sr + l.y * cr);\nlet p = in.p + r - L.viewPos;\nlet lc = cos(L.viewRot);\nlet ls = sin(L.viewRot);\nlet v = vec2f(p.x * lc - p.y * ls, p.x * ls + p.y * lc) * L.viewScale;\nlet n = vec2f(v.x / L.screenSize.x * 2 - 1, 1 - v.y / L.screenSize.y * 2);\nlet uv = mix(in.a, in.b, c)${uvScroll ? \" + in.o\" : \"\"};\nvar out: O;\nout.p = vec4f(n, ${zPosition}, 1);\nout.uv = uv;\nout.tint = in.c;\nreturn out;\n}`;\n}\n\nexport function createSpritePipelineCache(): SpritePipelineCache {\n return {\n _devices: new WeakMap(),\n };\n}\n\nexport function resetSpritePipelineCache(cache: SpritePipelineCache): void {\n cache._devices = new WeakMap();\n}\n\nexport function getSpritePipelineCacheSize(cache: SpritePipelineCache, device: GPUDevice): number {\n return cache._devices.get(device)?._pipelines.size ?? 0;\n}\n\nexport function getOrCreateSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite = false,\n depthStencilFormat?: GPUTextureFormat,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const deviceCache = getSpritePipelineDeviceCache(engine, cache);\n const resolvedDepthStencilFormat = normalizeDepthStencilFormat(hasDepth, depthStencilFormat);\n const key = spritePipelineKey(format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, layer);\n const cached = deviceCache._pipelines.get(key);\n if (cached) {\n return cached;\n }\n\n const pipeline = buildSpritePipeline(engine, deviceCache, format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, sceneBindGroupLayout, layer);\n deviceCache._pipelines.set(key, pipeline);\n return pipeline;\n}\n\nexport function createSpriteLayerBindGroup(\n engine: EngineContext,\n pipeline: GPURenderPipeline,\n spriteBindGroupIndex: 0 | 1,\n layer: Sprite2DLayer,\n uniformBuffer: GPUBuffer,\n fx?: SpriteLayerFx | null\n): GPUBindGroup {\n const tex = layer.atlas.texture;\n const entries: GPUBindGroupEntry[] = [\n { binding: 0, resource: { buffer: uniformBuffer } },\n { binding: 1, resource: tex.view },\n { binding: 2, resource: tex.sampler },\n ];\n if (fx) {\n for (const entry of _getSpriteFxHook()!.bindEntries(fx, 3)) {\n entries.push(entry);\n }\n }\n return engine._device.createBindGroup({\n layout: pipeline.getBindGroupLayout(spriteBindGroupIndex),\n entries,\n });\n}\n\nfunction getSpritePipelineDeviceCache(engine: EngineContext, cache: SpritePipelineCache): SpritePipelineDeviceCache {\n let deviceCache = cache._devices.get(engine._device);\n if (!deviceCache) {\n deviceCache = {\n _shaderModules: new Map(),\n _pipelines: new Map(),\n };\n cache._devices.set(engine._device, deviceCache);\n }\n return deviceCache;\n}\n\nfunction normalizeDepthStencilFormat(hasDepth: boolean, depthStencilFormat?: GPUTextureFormat): GPUTextureFormat | null {\n if (!hasDepth) {\n return null;\n }\n if (!depthStencilFormat) {\n throw new Error(\"Sprite pipeline: depth-enabled pipelines require a depth-stencil format.\");\n }\n return depthStencilFormat;\n}\n\nfunction spritePipelineKey(\n format: GPUTextureFormat,\n sampleCount: number,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite: boolean,\n depthStencilFormat: GPUTextureFormat | null,\n layer?: Sprite2DLayer\n): string {\n const customKey = layer ? (_getSpriteFxHook()?.pipelineKeyPart(layer) ?? \"\") : \"\";\n const uvKey = layer?._uvScrollAttr ? \"1\" : \"0\";\n const cgKey = layer ? (_getSpriteCoverageGammaHook()?.pipelineKeyPart(layer) ?? \"0\") : \"0\";\n return `${format}:${sampleCount}:${blendMode._key}:${hasDepth ? 1 : 0}:${depthWrite ? 1 : 0}:${depthStencilFormat ?? \"-\"}:cs${customKey}:uv${uvKey}:cg${cgKey}`;\n}\n\nfunction getShaderModule(engine: EngineContext, cache: SpritePipelineDeviceCache, hasDepth: boolean, layer?: Sprite2DLayer): GPUShaderModule {\n const customModule = layer ? _getSpriteFxHook()?.shaderModule(engine, hasDepth, layer) : null;\n if (customModule) {\n return customModule;\n }\n const gammaModule = layer ? _getSpriteCoverageGammaHook()?.shaderModule(engine, hasDepth, layer) : null;\n if (gammaModule) {\n return gammaModule;\n }\n const uvScroll = layer?._uvScrollAttr != null;\n const key = `${hasDepth ? 1 : 0}:${uvScroll ? 1 : 0}`;\n let module = cache._shaderModules.get(key);\n if (!module) {\n module = engine._device.createShaderModule({\n code: makeSpriteWgsl(hasDepth, hasDepth ? 1 : 0, uvScroll),\n });\n cache._shaderModules.set(key, module);\n }\n return module;\n}\n\nfunction buildSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineDeviceCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite: boolean,\n depthStencilFormat: GPUTextureFormat | null,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const device = engine._device;\n const layoutEntries: GPUBindGroupLayoutEntry[] = [\n { binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: SS.FRAGMENT, texture: { sampleType: \"float\" } },\n { binding: 2, visibility: SS.FRAGMENT, sampler: { type: \"filtering\" } },\n ];\n const extraLayoutEntries = layer ? _getSpriteFxHook()?.layoutEntries(layer, 3) : null;\n if (extraLayoutEntries) {\n for (const entry of extraLayoutEntries) {\n layoutEntries.push(entry);\n }\n }\n const bindGroupLayout = device.createBindGroupLayout({ entries: layoutEntries });\n const module = getShaderModule(engine, cache, hasDepth, layer);\n if (hasDepth && !sceneBindGroupLayout) {\n throw new Error(\"Sprite pipeline: depth-enabled pipelines require a scene bind-group layout.\");\n }\n const bindGroupLayouts = hasDepth ? [sceneBindGroupLayout!, bindGroupLayout] : [bindGroupLayout];\n const instanceAttributes: GPUVertexAttribute[] = [\n { shaderLocation: 0, offset: SPRITE_POSITION_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 1, offset: SPRITE_SIZE_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 2, offset: SPRITE_UV_MIN_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 3, offset: SPRITE_UV_MAX_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 4, offset: SPRITE_ROTATION_OFFSET_BYTES, format: \"float32\" },\n { shaderLocation: 5, offset: SPRITE_COLOR_OFFSET_BYTES, format: \"float32x4\" },\n ];\n if (hasDepth) {\n instanceAttributes.push({ shaderLocation: 6, offset: SPRITE_DEPTH_OFFSET_BYTES, format: \"float32\" });\n }\n // uvScroll (opt-in via setSprite2DUvOffset) appends a `uvOffset.xy` attribute after the base\n // layout, like `hasDepth` appends `@location(6)`. The attribute is precomputed by the opt-in\n // module and stashed on the layer as plain data — so the always-loaded path ships none of the\n // attribute-building, just this data consume. The widened stride is likewise already on the layer.\n if (layer?._uvScrollAttr) {\n instanceAttributes.push(layer._uvScrollAttr);\n }\n const arrayStride = layer?._instanceStrideBytes ?? (hasDepth ? DEPTH_INSTANCE_STRIDE_BYTES : PURE_2D_INSTANCE_STRIDE_BYTES);\n const descriptor: GPURenderPipelineDescriptor = {\n layout: device.createPipelineLayout({ bindGroupLayouts }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n {\n arrayStride: arrayStride,\n stepMode: \"instance\",\n attributes: instanceAttributes,\n },\n ],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format, blend: blendMode._descriptor, writeMask: CW.ALL }],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n multisample: { count: sampleCount },\n };\n if (hasDepth) {\n descriptor.depthStencil = {\n format: depthStencilFormat!,\n depthCompare: \"greater-equal\",\n depthWriteEnabled: depthWrite,\n };\n }\n return device.createRenderPipeline(descriptor);\n}\n\n// ─── Per-layer GPU sync helpers ────────────────────────────────────────────\n// Shared by `sprite-renderer.ts` (multi-layer pure-2D pass) and\n// `sprite-renderable.ts` (single-layer depth-hosted scene `Renderable`).\n// The two consumers have different lifecycles (renderer caches a `LayerGpu`\n// per layer; renderable owns one `Sprite2DLayer`) but the per-frame work —\n// \"grow instance buffer if needed\", \"upload dirty instance range\",\n// \"build the 12-float UBO\", \"writeBuffer only if changed\" — is identical.\n\n/** Per-layer UBO size in bytes. 16 floats; struct alignment forced to 16 by `vec4<f32>` fields. */\nexport const LAYER_UBO_BYTES = 64;\n/** Number of floats in the per-layer UBO scratch / lastUbo arrays. */\nexport const LAYER_UBO_FLOATS = LAYER_UBO_BYTES / 4;\n\n/** Shared two-triangle quad index buffer source (4 corners → 6 indices). */\nexport const SHARED_SPRITE_INDEX_DATA: Readonly<Uint16Array> = new U16([0, 1, 2, 0, 2, 3]);\n\n/** Allocate a per-layer instance vertex buffer sized for `capacity` sprites. */\nexport function createSpriteInstanceBuffer(device: GPUDevice, layer: Sprite2DLayer, label?: string): GPUBuffer {\n return device.createBuffer({\n size: layer._capacity * layer._instanceStrideBytes,\n usage: BU.VERTEX | BU.COPY_DST,\n label,\n });\n}\n\n/**\n * Reallocate the instance buffer if it can no longer hold `layer._capacity` sprites at the layer's\n * current per-sprite stride. The comparison is **byte-based** (`currentBuffer.size` vs the required\n * byte size) so it triggers both on capacity growth *and* on a stride change — e.g. when the opt-in\n * `setSprite2DUvOffset` widens a previously narrow layer in place. Returns the (possibly new) buffer\n * plus a `reallocated` flag the caller uses to invalidate per-buffer caches (render bundles, etc).\n */\nexport function ensureSpriteInstanceBuffer(\n device: GPUDevice,\n layer: Sprite2DLayer,\n currentBuffer: GPUBuffer,\n currentCapacity: number,\n label?: string\n): { buffer: GPUBuffer; capacity: number; reallocated: boolean } {\n const neededBytes = layer._capacity * layer._instanceStrideBytes;\n if (currentBuffer.size >= neededBytes) {\n return { buffer: currentBuffer, capacity: currentCapacity, reallocated: false };\n }\n currentBuffer.destroy();\n return {\n buffer: createSpriteInstanceBuffer(device, layer, label),\n capacity: layer._capacity,\n reallocated: true,\n };\n}\n\n/**\n * Sync per-instance vertex data to `instanceBuffer`. Returns the new `uploadedVersion`\n * the caller should store. No-op if `layer._version` hasn't advanced or the layer is\n * empty. On first sight (`uploadedVersion === -1`) uploads `[0, count)`; on subsequent\n * edits uploads only `[_dirtyMin, min(_dirtyMax, count))`. Resets the dirty range.\n */\nexport function uploadSpriteInstances(device: GPUDevice, layer: Sprite2DLayer, instanceBuffer: GPUBuffer, uploadedVersion: number): number {\n if (uploadedVersion === layer._version) {\n return uploadedVersion;\n }\n if (layer.count === 0) {\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n }\n let lo: number;\n let hi: number;\n if (uploadedVersion === -1) {\n lo = 0;\n hi = layer.count;\n } else {\n lo = layer._dirtyMin;\n hi = Math.min(layer._dirtyMax, layer.count);\n }\n if (hi > lo) {\n const offsetBytes = lo * layer._instanceStrideBytes;\n const bytes = (hi - lo) * layer._instanceStrideBytes;\n device.queue.writeBuffer(instanceBuffer, offsetBytes, layer._instanceData.buffer, layer._instanceData.byteOffset + offsetBytes, bytes);\n }\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n}\n\n/**\n * Fill `ubo` (16 floats) with the per-layer UBO contents from `layer` at the given\n * render-target dims. Layout matches the WGSL `Layer` struct (64 bytes total):\n * [0..1] viewPos.xy [2] viewScale [3] viewRot\n * [4..5] screenSize.xy [6..7] pivot.xy\n * [8..11] opacityMul.rgba (pre-shaped per blend mode)\n * [12] 1/coverageGamma (coverage-gamma layers only) [13..15] reserved\n *\n * Depth-hosted layers keep per-sprite NDC depth on the per-instance vertex buffer\n * (slot [13] of `Sprite2DLayer._instanceData`), not in this UBO — a single\n * depth-hosted layer can mix sprites at different depths. Pure-2D layers have no\n * Z slot.\n *\n * Premultiplied sources need RGB *and* A scaled by opacity for a correct fade;\n * straight-alpha needs only A scaled (the blend stage already uses src.a as factor).\n */\nexport function buildSpriteLayerUbo(layer: Sprite2DLayer, screenWidth: number, screenHeight: number, ubo: Float32Array): void {\n ubo[0] = layer.view.positionPx[0];\n ubo[1] = layer.view.positionPx[1];\n ubo[2] = layer.view.zoom;\n ubo[3] = layer.view.rotation;\n ubo[4] = screenWidth;\n ubo[5] = screenHeight;\n ubo[6] = layer.pivot[0];\n ubo[7] = layer.pivot[1];\n const op = layer.opacity;\n if (layer.blendMode._premultipliedOpacity) {\n ubo[8] = op;\n ubo[9] = op;\n ubo[10] = op;\n ubo[11] = op;\n } else {\n ubo[8] = 1;\n ubo[9] = 1;\n ubo[10] = 1;\n ubo[11] = op;\n }\n // Coverage gamma (opt-in via setSprite2DCoverageGamma): the hook writes aa.x = 1/coverageGamma\n // for gamma layers and 0 otherwise. Absent when no gamma layer exists, so non-gamma scenes ship\n // no gamma bytes here; aa.yzw stay 0 (scratch UBO is zero-initialized and reused).\n _getSpriteCoverageGammaHook()?.writeUbo(layer, ubo);\n}\n\n/**\n * Compare `scratchUbo` to `lastUbo` (LAYER_UBO_FLOATS each) and `writeBuffer` only if they\n * differ. On first call (`alreadyUploaded === false`) forces an unconditional write\n * so `lastUbo` becomes real. Returns the new `alreadyUploaded` value (always `true`\n * after the first call regardless of whether bytes changed).\n */\nexport function writeSpriteLayerUboIfDirty(device: GPUDevice, uniformBuffer: GPUBuffer, scratchUbo: Float32Array, lastUbo: Float32Array, alreadyUploaded: boolean): boolean {\n let dirty = !alreadyUploaded;\n if (!dirty) {\n for (let i = 0; i < LAYER_UBO_FLOATS; i++) {\n if (lastUbo[i] !== scratchUbo[i]) {\n dirty = true;\n break;\n }\n }\n }\n if (dirty) {\n device.queue.writeBuffer(uniformBuffer, 0, scratchUbo.buffer, scratchUbo.byteOffset, LAYER_UBO_BYTES);\n lastUbo.set(scratchUbo);\n }\n return true;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sprite-pipeline.js","sources":["../../../src/sprite/sprite-pipeline.ts"],"sourcesContent":["/** Internal sprite pipeline helpers: owns WGSL, bind-group schema, pipeline construction, and bind-group creation. */\nimport { U16 } from \"../engine/typed-arrays.js\";\nimport { BU, SS, CW } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Sprite2DLayer, SpriteBlendMode } from \"./sprite-2d.js\";\nimport type { SpriteLayerFx } from \"./custom-shader-core.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport { _getSpriteCoverageGammaHook } from \"./sprite-coverage-gamma-hook.js\";\nimport { DEPTH_INSTANCE_STRIDE_BYTES, PURE_2D_INSTANCE_STRIDE_BYTES } from \"./sprite-2d.js\";\n\n/** @internal */\nexport interface SpritePipelineDeviceCache {\n /** @internal Shader modules keyed by `${hasDepth}:${uvScroll}` permutation. */\n _shaderModules: Map<string, GPUShaderModule>;\n /** @internal */\n _pipelines: Map<string, GPURenderPipeline>;\n}\n\n/** @internal */\nexport interface SpritePipelineCache {\n /** @internal */\n _devices: WeakMap<GPUDevice, SpritePipelineDeviceCache>;\n}\n\nconst SPRITE_POSITION_OFFSET_BYTES = 0;\nconst SPRITE_SIZE_OFFSET_BYTES = 8;\nconst SPRITE_UV_MIN_OFFSET_BYTES = 16;\nconst SPRITE_UV_MAX_OFFSET_BYTES = 24;\nconst SPRITE_ROTATION_OFFSET_BYTES = 32;\nconst SPRITE_COLOR_OFFSET_BYTES = 36;\nconst SPRITE_DEPTH_OFFSET_BYTES = 52;\n\nfunction makeSpriteWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll: boolean): string {\n return `${makeSpritePrologueWgsl(hasDepth, spriteGroupIndex, uvScroll)}\n@fragment\nfn fs(in: O) -> @location(0) vec4f {\nlet s = textureSample(atlasTex, atlasSamp, in.uv);\nreturn s * in.tint * L.opacityMul;\n}`;\n}\n\n/**\n * Shared WGSL prologue for the sprite shader: the `Layer` UBO, atlas texture + sampler\n * bindings, instance-attribute `VIn` / interpolant `VOut` structs, and the `vs` vertex stage.\n * The default sprite shader appends a trivial textured fragment; the opt-in custom-shader\n * module (`createSprite2DCustomShader`) appends any extra-texture bindings, a `SpriteFx` UBO at\n * `@binding(3 + 2 * extraTextures.length)`, and the user's raw fragment body. Exposed so both\n * paths share one source of truth.\n */\nexport function makeSpritePrologueWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll = false): string {\n const group = `@group(${spriteGroupIndex})`;\n const zAttribute = hasDepth ? `,\\n@location(6) z: f32` : \"\";\n const uvOffsetAttribute = uvScroll ? `,\\n@location(7) o: vec2f` : \"\";\n const zPosition = hasDepth ? \"1 - in.z\" : \"0\";\n return `struct Lr {\nviewPos: vec2f,\nviewScale: f32,\nviewRot: f32,\nscreenSize: vec2f,\npivot: vec2f,\nopacityMul: vec4f,\naa: vec4f,\n};\n${group} @binding(0) var<uniform> L: Lr;\n${group} @binding(1) var atlasTex: texture_2d<f32>;\n${group} @binding(2) var atlasSamp: sampler;\nstruct I {\n@builtin(vertex_index) vid: u32,\n@location(0) p: vec2f,\n@location(1) s: vec2f,\n@location(2) a: vec2f,\n@location(3) b: vec2f,\n@location(4) r: f32,\n@location(5) c: vec4f${zAttribute}${uvOffsetAttribute}\n};\nstruct O {\n@builtin(position) p: vec4f,\n@location(0) uv: vec2f,\n@location(1) tint: vec4f,\n};\n@vertex\nfn vs(in: I) -> O {\nvar q = array<vec2f, 4>(vec2f(0, 0), vec2f(1, 0), vec2f(1, 1), vec2f(0, 1));\nlet c = q[in.vid];\nlet l = (c - L.pivot) * in.s;\nlet cr = cos(in.r);\nlet sr = sin(in.r);\nlet r = vec2f(l.x * cr - l.y * sr, l.x * sr + l.y * cr);\nlet p = in.p + r - L.viewPos;\nlet lc = cos(L.viewRot);\nlet ls = sin(L.viewRot);\nlet v = vec2f(p.x * lc - p.y * ls, p.x * ls + p.y * lc) * L.viewScale;\nlet n = vec2f(v.x / L.screenSize.x * 2 - 1, 1 - v.y / L.screenSize.y * 2);\nlet uv = mix(in.a, in.b, c)${uvScroll ? \" + in.o\" : \"\"};\nvar out: O;\nout.p = vec4f(n, ${zPosition}, 1);\nout.uv = uv;\nout.tint = in.c;\nreturn out;\n}`;\n}\n\nexport function createSpritePipelineCache(): SpritePipelineCache {\n return {\n _devices: new WeakMap(),\n };\n}\n\nexport function resetSpritePipelineCache(cache: SpritePipelineCache): void {\n cache._devices = new WeakMap();\n}\n\n// Process-wide sprite pipeline cache shared by every `SpriteRenderer` (the HUD /\n// pure-2D path). Compiled `GPUShaderModule`s + `GPURenderPipeline`s are keyed by\n// `GPUDevice` inside the cache, so instances on the same device dedupe pipelines,\n// and destroy→recreate of a renderer for the same canvas pays no recompile.\n// Lazy-init on first acquire (per GUIDANCE §4 — module-level `null` initializer +\n// helper, never a top-level `new WeakMap()`), refcounted so the cache is released\n// exactly when the last `SpriteRenderer` is disposed. The billboard/depth\n// (`buildSpriteRenderable`) path keeps its own shared cache; the two are disjoint\n// by construction (different pipeline keys) and could be unified in a follow-up.\nlet _sharedSpriteRendererPipelineCache: SpritePipelineCache | null = null;\nlet _sharedSpriteRendererPipelineCacheRefs = 0;\n\n/** @internal Acquire (refcount++) the process-wide SpriteRenderer pipeline cache. */\nexport function acquireSharedSpriteRendererPipelineCache(): SpritePipelineCache {\n _sharedSpriteRendererPipelineCache ??= createSpritePipelineCache();\n _sharedSpriteRendererPipelineCacheRefs++;\n return _sharedSpriteRendererPipelineCache;\n}\n\n/** @internal Release (refcount--) the process-wide SpriteRenderer pipeline cache.\n * Clears the cache (dropping cache-held pipeline/shader refs) when the last user\n * is disposed. Safe to over-call; refcount floors at 0. */\nexport function releaseSharedSpriteRendererPipelineCache(): void {\n if (_sharedSpriteRendererPipelineCacheRefs === 0) {\n return;\n }\n _sharedSpriteRendererPipelineCacheRefs--;\n if (_sharedSpriteRendererPipelineCacheRefs === 0 && _sharedSpriteRendererPipelineCache) {\n resetSpritePipelineCache(_sharedSpriteRendererPipelineCache);\n _sharedSpriteRendererPipelineCache = null;\n }\n}\n\nexport function getSpritePipelineCacheSize(cache: SpritePipelineCache, device: GPUDevice): number {\n return cache._devices.get(device)?._pipelines.size ?? 0;\n}\n\nexport function getOrCreateSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite = false,\n depthStencilFormat?: GPUTextureFormat,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const deviceCache = getSpritePipelineDeviceCache(engine, cache);\n const resolvedDepthStencilFormat = normalizeDepthStencilFormat(hasDepth, depthStencilFormat);\n const key = spritePipelineKey(format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, layer);\n const cached = deviceCache._pipelines.get(key);\n if (cached) {\n return cached;\n }\n\n const pipeline = buildSpritePipeline(engine, deviceCache, format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, sceneBindGroupLayout, layer);\n deviceCache._pipelines.set(key, pipeline);\n return pipeline;\n}\n\nexport function createSpriteLayerBindGroup(\n engine: EngineContext,\n pipeline: GPURenderPipeline,\n spriteBindGroupIndex: 0 | 1,\n layer: Sprite2DLayer,\n uniformBuffer: GPUBuffer,\n fx?: SpriteLayerFx | null\n): GPUBindGroup {\n const tex = layer.atlas.texture;\n const entries: GPUBindGroupEntry[] = [\n { binding: 0, resource: { buffer: uniformBuffer } },\n { binding: 1, resource: tex.view },\n { binding: 2, resource: tex.sampler },\n ];\n if (fx) {\n for (const entry of _getSpriteFxHook()!.bindEntries(fx, 3)) {\n entries.push(entry);\n }\n }\n return engine._device.createBindGroup({\n layout: pipeline.getBindGroupLayout(spriteBindGroupIndex),\n entries,\n });\n}\n\nfunction getSpritePipelineDeviceCache(engine: EngineContext, cache: SpritePipelineCache): SpritePipelineDeviceCache {\n let deviceCache = cache._devices.get(engine._device);\n if (!deviceCache) {\n deviceCache = {\n _shaderModules: new Map(),\n _pipelines: new Map(),\n };\n cache._devices.set(engine._device, deviceCache);\n }\n return deviceCache;\n}\n\nfunction normalizeDepthStencilFormat(hasDepth: boolean, depthStencilFormat?: GPUTextureFormat): GPUTextureFormat | null {\n if (!hasDepth) {\n return null;\n }\n if (!depthStencilFormat) {\n throw new Error(\"Sprite pipeline: depth-enabled pipelines require a depth-stencil format.\");\n }\n return depthStencilFormat;\n}\n\nfunction spritePipelineKey(\n format: GPUTextureFormat,\n sampleCount: number,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite: boolean,\n depthStencilFormat: GPUTextureFormat | null,\n layer?: Sprite2DLayer\n): string {\n const customKey = layer ? (_getSpriteFxHook()?.pipelineKeyPart(layer) ?? \"\") : \"\";\n const uvKey = layer?._uvScrollAttr ? \"1\" : \"0\";\n const cgKey = layer ? (_getSpriteCoverageGammaHook()?.pipelineKeyPart(layer) ?? \"0\") : \"0\";\n return `${format}:${sampleCount}:${blendMode._key}:${hasDepth ? 1 : 0}:${depthWrite ? 1 : 0}:${depthStencilFormat ?? \"-\"}:cs${customKey}:uv${uvKey}:cg${cgKey}`;\n}\n\nfunction getShaderModule(engine: EngineContext, cache: SpritePipelineDeviceCache, hasDepth: boolean, layer?: Sprite2DLayer): GPUShaderModule {\n const customModule = layer ? _getSpriteFxHook()?.shaderModule(engine, hasDepth, layer) : null;\n if (customModule) {\n return customModule;\n }\n const gammaModule = layer ? _getSpriteCoverageGammaHook()?.shaderModule(engine, hasDepth, layer) : null;\n if (gammaModule) {\n return gammaModule;\n }\n const uvScroll = layer?._uvScrollAttr != null;\n const key = `${hasDepth ? 1 : 0}:${uvScroll ? 1 : 0}`;\n let module = cache._shaderModules.get(key);\n if (!module) {\n module = engine._device.createShaderModule({\n code: makeSpriteWgsl(hasDepth, hasDepth ? 1 : 0, uvScroll),\n });\n cache._shaderModules.set(key, module);\n }\n return module;\n}\n\nfunction buildSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineDeviceCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite: boolean,\n depthStencilFormat: GPUTextureFormat | null,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const device = engine._device;\n const layoutEntries: GPUBindGroupLayoutEntry[] = [\n { binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: SS.FRAGMENT, texture: { sampleType: \"float\" } },\n { binding: 2, visibility: SS.FRAGMENT, sampler: { type: \"filtering\" } },\n ];\n const extraLayoutEntries = layer ? _getSpriteFxHook()?.layoutEntries(layer, 3) : null;\n if (extraLayoutEntries) {\n for (const entry of extraLayoutEntries) {\n layoutEntries.push(entry);\n }\n }\n const bindGroupLayout = device.createBindGroupLayout({ entries: layoutEntries });\n const module = getShaderModule(engine, cache, hasDepth, layer);\n if (hasDepth && !sceneBindGroupLayout) {\n throw new Error(\"Sprite pipeline: depth-enabled pipelines require a scene bind-group layout.\");\n }\n const bindGroupLayouts = hasDepth ? [sceneBindGroupLayout!, bindGroupLayout] : [bindGroupLayout];\n const instanceAttributes: GPUVertexAttribute[] = [\n { shaderLocation: 0, offset: SPRITE_POSITION_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 1, offset: SPRITE_SIZE_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 2, offset: SPRITE_UV_MIN_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 3, offset: SPRITE_UV_MAX_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 4, offset: SPRITE_ROTATION_OFFSET_BYTES, format: \"float32\" },\n { shaderLocation: 5, offset: SPRITE_COLOR_OFFSET_BYTES, format: \"float32x4\" },\n ];\n if (hasDepth) {\n instanceAttributes.push({ shaderLocation: 6, offset: SPRITE_DEPTH_OFFSET_BYTES, format: \"float32\" });\n }\n // uvScroll (opt-in via setSprite2DUvOffset) appends a `uvOffset.xy` attribute after the base\n // layout, like `hasDepth` appends `@location(6)`. The attribute is precomputed by the opt-in\n // module and stashed on the layer as plain data — so the always-loaded path ships none of the\n // attribute-building, just this data consume. The widened stride is likewise already on the layer.\n if (layer?._uvScrollAttr) {\n instanceAttributes.push(layer._uvScrollAttr);\n }\n const arrayStride = layer?._instanceStrideBytes ?? (hasDepth ? DEPTH_INSTANCE_STRIDE_BYTES : PURE_2D_INSTANCE_STRIDE_BYTES);\n const descriptor: GPURenderPipelineDescriptor = {\n layout: device.createPipelineLayout({ bindGroupLayouts }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n {\n arrayStride: arrayStride,\n stepMode: \"instance\",\n attributes: instanceAttributes,\n },\n ],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format, blend: blendMode._descriptor, writeMask: CW.ALL }],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n multisample: { count: sampleCount },\n };\n if (hasDepth) {\n descriptor.depthStencil = {\n format: depthStencilFormat!,\n depthCompare: \"greater-equal\",\n depthWriteEnabled: depthWrite,\n };\n }\n return device.createRenderPipeline(descriptor);\n}\n\n// ─── Per-layer GPU sync helpers ────────────────────────────────────────────\n// Shared by `sprite-renderer.ts` (multi-layer pure-2D pass) and\n// `sprite-renderable.ts` (single-layer depth-hosted scene `Renderable`).\n// The two consumers have different lifecycles (renderer caches a `LayerGpu`\n// per layer; renderable owns one `Sprite2DLayer`) but the per-frame work —\n// \"grow instance buffer if needed\", \"upload dirty instance range\",\n// \"build the 12-float UBO\", \"writeBuffer only if changed\" — is identical.\n\n/** Per-layer UBO size in bytes. 16 floats; struct alignment forced to 16 by `vec4<f32>` fields. */\nexport const LAYER_UBO_BYTES = 64;\n/** Number of floats in the per-layer UBO scratch / lastUbo arrays. */\nexport const LAYER_UBO_FLOATS = LAYER_UBO_BYTES / 4;\n\n/** Shared two-triangle quad index buffer source (4 corners → 6 indices). */\nexport const SHARED_SPRITE_INDEX_DATA: Readonly<Uint16Array> = new U16([0, 1, 2, 0, 2, 3]);\n\n/** Allocate a per-layer instance vertex buffer sized for `capacity` sprites. */\nexport function createSpriteInstanceBuffer(device: GPUDevice, layer: Sprite2DLayer, label?: string): GPUBuffer {\n return device.createBuffer({\n size: layer._capacity * layer._instanceStrideBytes,\n usage: BU.VERTEX | BU.COPY_DST,\n label,\n });\n}\n\n/**\n * Reallocate the instance buffer if it can no longer hold `layer._capacity` sprites at the layer's\n * current per-sprite stride. The comparison is **byte-based** (`currentBuffer.size` vs the required\n * byte size) so it triggers both on capacity growth *and* on a stride change — e.g. when the opt-in\n * `setSprite2DUvOffset` widens a previously narrow layer in place. Returns the (possibly new) buffer\n * plus a `reallocated` flag the caller uses to invalidate per-buffer caches (render bundles, etc).\n */\nexport function ensureSpriteInstanceBuffer(\n device: GPUDevice,\n layer: Sprite2DLayer,\n currentBuffer: GPUBuffer,\n currentCapacity: number,\n label?: string\n): { buffer: GPUBuffer; capacity: number; reallocated: boolean } {\n const neededBytes = layer._capacity * layer._instanceStrideBytes;\n if (currentBuffer.size >= neededBytes) {\n return { buffer: currentBuffer, capacity: currentCapacity, reallocated: false };\n }\n currentBuffer.destroy();\n return {\n buffer: createSpriteInstanceBuffer(device, layer, label),\n capacity: layer._capacity,\n reallocated: true,\n };\n}\n\n/**\n * Sync per-instance vertex data to `instanceBuffer`. Returns the new `uploadedVersion`\n * the caller should store. No-op if `layer._version` hasn't advanced or the layer is\n * empty. On first sight (`uploadedVersion === -1`) uploads `[0, count)`; on subsequent\n * edits uploads only `[_dirtyMin, min(_dirtyMax, count))`. Resets the dirty range.\n */\nexport function uploadSpriteInstances(device: GPUDevice, layer: Sprite2DLayer, instanceBuffer: GPUBuffer, uploadedVersion: number): number {\n if (uploadedVersion === layer._version) {\n return uploadedVersion;\n }\n if (layer.count === 0) {\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n }\n let lo: number;\n let hi: number;\n if (uploadedVersion === -1) {\n lo = 0;\n hi = layer.count;\n } else {\n lo = layer._dirtyMin;\n hi = Math.min(layer._dirtyMax, layer.count);\n }\n if (hi > lo) {\n const offsetBytes = lo * layer._instanceStrideBytes;\n const bytes = (hi - lo) * layer._instanceStrideBytes;\n device.queue.writeBuffer(instanceBuffer, offsetBytes, layer._instanceData.buffer, layer._instanceData.byteOffset + offsetBytes, bytes);\n }\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n}\n\n/**\n * Fill `ubo` (16 floats) with the per-layer UBO contents from `layer` at the given\n * render-target dims. Layout matches the WGSL `Layer` struct (64 bytes total):\n * [0..1] viewPos.xy [2] viewScale [3] viewRot\n * [4..5] screenSize.xy [6..7] pivot.xy\n * [8..11] opacityMul.rgba (pre-shaped per blend mode)\n * [12] 1/coverageGamma (coverage-gamma layers only) [13..15] reserved\n *\n * Depth-hosted layers keep per-sprite NDC depth on the per-instance vertex buffer\n * (slot [13] of `Sprite2DLayer._instanceData`), not in this UBO — a single\n * depth-hosted layer can mix sprites at different depths. Pure-2D layers have no\n * Z slot.\n *\n * Premultiplied sources need RGB *and* A scaled by opacity for a correct fade;\n * straight-alpha needs only A scaled (the blend stage already uses src.a as factor).\n */\nexport function buildSpriteLayerUbo(layer: Sprite2DLayer, screenWidth: number, screenHeight: number, ubo: Float32Array): void {\n ubo[0] = layer.view.positionPx[0];\n ubo[1] = layer.view.positionPx[1];\n ubo[2] = layer.view.zoom;\n ubo[3] = layer.view.rotation;\n ubo[4] = screenWidth;\n ubo[5] = screenHeight;\n ubo[6] = layer.pivot[0];\n ubo[7] = layer.pivot[1];\n const op = layer.opacity;\n if (layer.blendMode._premultipliedOpacity) {\n ubo[8] = op;\n ubo[9] = op;\n ubo[10] = op;\n ubo[11] = op;\n } else {\n ubo[8] = 1;\n ubo[9] = 1;\n ubo[10] = 1;\n ubo[11] = op;\n }\n // Coverage gamma (opt-in via setSprite2DCoverageGamma): the hook writes aa.x = 1/coverageGamma\n // for gamma layers and 0 otherwise. Absent when no gamma layer exists, so non-gamma scenes ship\n // no gamma bytes here; aa.yzw stay 0 (scratch UBO is zero-initialized and reused).\n _getSpriteCoverageGammaHook()?.writeUbo(layer, ubo);\n}\n\n/**\n * Compare `scratchUbo` to `lastUbo` (LAYER_UBO_FLOATS each) and `writeBuffer` only if they\n * differ. On first call (`alreadyUploaded === false`) forces an unconditional write\n * so `lastUbo` becomes real. Returns the new `alreadyUploaded` value (always `true`\n * after the first call regardless of whether bytes changed).\n */\nexport function writeSpriteLayerUboIfDirty(device: GPUDevice, uniformBuffer: GPUBuffer, scratchUbo: Float32Array, lastUbo: Float32Array, alreadyUploaded: boolean): boolean {\n let dirty = !alreadyUploaded;\n if (!dirty) {\n for (let i = 0; i < LAYER_UBO_FLOATS; i++) {\n if (lastUbo[i] !== scratchUbo[i]) {\n dirty = true;\n break;\n }\n }\n }\n if (dirty) {\n device.queue.writeBuffer(uniformBuffer, 0, scratchUbo.buffer, scratchUbo.byteOffset, LAYER_UBO_BYTES);\n lastUbo.set(scratchUbo);\n }\n return true;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { BU } from '../engine/gpu-flags.js';
|
|
|
3
3
|
import { registerRenderingContext, unregisterRenderingContext, getRenderTargetSize } from '../engine/engine.js';
|
|
4
4
|
import { createMappedBuffer, createEmptyUniformBuffer } from '../resource/gpu-buffers.js';
|
|
5
5
|
import { _getSpriteFxHook } from './sprite-fx-hook.js';
|
|
6
|
-
import { getOrCreateSpritePipeline, SHARED_SPRITE_INDEX_DATA,
|
|
6
|
+
import { getOrCreateSpritePipeline, SHARED_SPRITE_INDEX_DATA, releaseSharedSpriteRendererPipelineCache, getSpritePipelineCacheSize, acquireSharedSpriteRendererPipelineCache, createSpriteLayerBindGroup, createSpriteInstanceBuffer, LAYER_UBO_BYTES, ensureSpriteInstanceBuffer, uploadSpriteInstances, buildSpriteLayerUbo, writeSpriteLayerUboIfDirty } from './sprite-pipeline.js';
|
|
7
7
|
|
|
8
8
|
const KIND = "sprite-renderer";
|
|
9
9
|
function ensureLayerGpu(rr, layer) {
|
|
@@ -78,7 +78,7 @@ function createSpriteRenderer(surface, opts) {
|
|
|
78
78
|
_kind: KIND,
|
|
79
79
|
_surface: surface,
|
|
80
80
|
_indexBuffer: indexBuffer,
|
|
81
|
-
_pipelineCache:
|
|
81
|
+
_pipelineCache: acquireSharedSpriteRendererPipelineCache(),
|
|
82
82
|
_layerGpu: /* @__PURE__ */ new Map(),
|
|
83
83
|
_visibleBundles: [],
|
|
84
84
|
_targetWidth: canvas.width,
|
|
@@ -99,8 +99,14 @@ function createSpriteRenderer(surface, opts) {
|
|
|
99
99
|
return spriteRendererRecord(rr);
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
try {
|
|
103
|
+
for (const layer of rr.layers) {
|
|
104
|
+
getOrCreateSpritePipeline(engine, rr._pipelineCache, surface.format, 1, layer.blendMode, false, false, void 0, void 0, layer);
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
releaseSharedSpriteRendererPipelineCache();
|
|
108
|
+
indexBuffer.destroy();
|
|
109
|
+
throw err;
|
|
104
110
|
}
|
|
105
111
|
return rr;
|
|
106
112
|
}
|
|
@@ -144,7 +150,7 @@ function spriteRendererRecord(rr) {
|
|
|
144
150
|
assertSpriteRendererLayers(rr.layers);
|
|
145
151
|
const eng = rr._surface.engine;
|
|
146
152
|
const encoder = eng._currentEncoder;
|
|
147
|
-
const swapView = rr._targetView ??
|
|
153
|
+
const swapView = rr._targetView ?? rr._surface.scRT._colorView;
|
|
148
154
|
const pass = encoder.beginRenderPass({
|
|
149
155
|
colorAttachments: [
|
|
150
156
|
{
|
|
@@ -258,7 +264,7 @@ function disposeSpriteRenderer(sr) {
|
|
|
258
264
|
sr._visibleBundles.length = 0;
|
|
259
265
|
sr._beforeUpdate.length = 0;
|
|
260
266
|
sr._indexBuffer.destroy();
|
|
261
|
-
|
|
267
|
+
releaseSharedSpriteRendererPipelineCache();
|
|
262
268
|
sr._layers.length = 0;
|
|
263
269
|
}
|
|
264
270
|
function _spriteRendererPipelineCacheSize(sr) {
|
|
@@ -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);\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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"}
|
|
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 acquireSharedSpriteRendererPipelineCache,\n ensureSpriteInstanceBuffer,\n getOrCreateSpritePipeline,\n getSpritePipelineCacheSize,\n releaseSharedSpriteRendererPipelineCache,\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 Reference to the process-wide shared sprite pipeline cache\n * (device-keyed). Not owned by this renderer — acquired at create, released\n * at dispose; never `reset` per-renderer. */\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: acquireSharedSpriteRendererPipelineCache(),\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 // If pipeline compilation throws here, `rr` is never returned and `disposeSpriteRenderer`\n // is never called, so unwind the resources we've already acquired (the shared-cache\n // refcount and the index buffer) to avoid leaking them for the rest of the process.\n try {\n for (const layer of rr.layers) {\n getOrCreateSpritePipeline(engine, rr._pipelineCache, surface.format, 1, layer.blendMode, false, false, undefined, undefined, layer);\n }\n } catch (err) {\n releaseSharedSpriteRendererPipelineCache();\n indexBuffer.destroy();\n throw err;\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 ?? rr._surface.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 // Release our refcount on the process-wide shared pipeline cache instead of\n // clearing it — other live `SpriteRenderer`s on this device still need the\n // compiled pipelines. The cache is cleared only when the last renderer is\n // disposed (refcount hits 0).\n releaseSharedSpriteRendererPipelineCache();\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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@babylonjs/lite",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.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/",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"babylonLiteRelease": {
|
|
37
|
-
"azureBuildId": "
|
|
38
|
-
"sourceVersion": "
|
|
37
|
+
"azureBuildId": "56098",
|
|
38
|
+
"sourceVersion": "2e50a77046ec4b4891791e7ef31834e1543aaf09"
|
|
39
39
|
}
|
|
40
40
|
}
|