@babylonjs/lite 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/index.js +381 -375
  2. package/dist/index.js.map +1 -1
  3. package/index.d.ts +757 -0
  4. package/lib/audio/analyzer.js +65 -0
  5. package/lib/audio/analyzer.js.map +1 -0
  6. package/lib/audio/audio-bus.js +38 -0
  7. package/lib/audio/audio-bus.js.map +1 -0
  8. package/lib/audio/audio-engine.js +188 -0
  9. package/lib/audio/audio-engine.js.map +1 -0
  10. package/lib/audio/audio-fetch.js +18 -0
  11. package/lib/audio/audio-fetch.js.map +1 -0
  12. package/lib/audio/audio-param.js +96 -0
  13. package/lib/audio/audio-param.js.map +1 -0
  14. package/lib/audio/audio-signal.js +46 -0
  15. package/lib/audio/audio-signal.js.map +1 -0
  16. package/lib/audio/bus.js +33 -0
  17. package/lib/audio/bus.js.map +1 -0
  18. package/lib/audio/host-types.js +2 -0
  19. package/lib/audio/host-types.js.map +1 -0
  20. package/lib/audio/index.js +12 -0
  21. package/lib/audio/index.js.map +1 -0
  22. package/lib/audio/sound-buffer.js +59 -0
  23. package/lib/audio/sound-buffer.js.map +1 -0
  24. package/lib/audio/sound-source.js +57 -0
  25. package/lib/audio/sound-source.js.map +1 -0
  26. package/lib/audio/sound-sub-graph.js +72 -0
  27. package/lib/audio/sound-sub-graph.js.map +1 -0
  28. package/lib/audio/spatial.js +466 -0
  29. package/lib/audio/spatial.js.map +1 -0
  30. package/lib/audio/static-sound.js +313 -0
  31. package/lib/audio/static-sound.js.map +1 -0
  32. package/lib/audio/stereo.js +40 -0
  33. package/lib/audio/stereo.js.map +1 -0
  34. package/lib/audio/streaming-sound.js +377 -0
  35. package/lib/audio/streaming-sound.js.map +1 -0
  36. package/lib/audio/unmute-ui.js +72 -0
  37. package/lib/audio/unmute-ui.js.map +1 -0
  38. package/lib/audio/visualizer.js +101 -0
  39. package/lib/audio/visualizer.js.map +1 -0
  40. package/lib/engine/engine.js +1 -1
  41. package/lib/index.js +11 -0
  42. package/lib/index.js.map +1 -1
  43. package/lib/light/types.js.map +1 -1
  44. package/lib/loader-gltf/animation-pointer-basecolor.js +25 -0
  45. package/lib/loader-gltf/animation-pointer-basecolor.js.map +1 -0
  46. package/lib/loader-gltf/animation-pointer-ext.js +244 -0
  47. package/lib/loader-gltf/animation-pointer-ext.js.map +1 -0
  48. package/lib/loader-gltf/animation-pointer-lights.js +46 -0
  49. package/lib/loader-gltf/animation-pointer-lights.js.map +1 -0
  50. package/lib/loader-gltf/animation-pointer.js +4 -1
  51. package/lib/loader-gltf/animation-pointer.js.map +1 -1
  52. package/lib/loader-gltf/gltf-animation.js +5 -3
  53. package/lib/loader-gltf/gltf-animation.js.map +1 -1
  54. package/lib/loader-gltf/gltf-color-normalize.js +10 -1
  55. package/lib/loader-gltf/gltf-color-normalize.js.map +1 -1
  56. package/lib/loader-gltf/gltf-feature-animation-pointer.js +67 -47
  57. package/lib/loader-gltf/gltf-feature-animation-pointer.js.map +1 -1
  58. package/lib/loader-gltf/gltf-feature-lights-punctual.js +51 -9
  59. package/lib/loader-gltf/gltf-feature-lights-punctual.js.map +1 -1
  60. package/lib/loader-gltf/gltf-feature-primitive.js +20 -0
  61. package/lib/loader-gltf/gltf-feature-primitive.js.map +1 -0
  62. package/lib/loader-gltf/gltf-feature-registry.js +25 -0
  63. package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
  64. package/lib/loader-gltf/gltf-feature-skeleton.js +18 -3
  65. package/lib/loader-gltf/gltf-feature-skeleton.js.map +1 -1
  66. package/lib/loader-gltf/gltf-interleave.js +3 -2
  67. package/lib/loader-gltf/gltf-interleave.js.map +1 -1
  68. package/lib/loader-gltf/gltf-light-pointer-state.js +18 -0
  69. package/lib/loader-gltf/gltf-light-pointer-state.js.map +1 -0
  70. package/lib/loader-gltf/gltf-parser.js +7 -1
  71. package/lib/loader-gltf/gltf-parser.js.map +1 -1
  72. package/lib/loader-gltf/gltf-pbr-builder-ext.js +1 -1
  73. package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
  74. package/lib/loader-gltf/gltf-pbr-builder.js +1 -1
  75. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  76. package/lib/loader-gltf/gltf-sampler-denorm.js +20 -0
  77. package/lib/loader-gltf/gltf-sampler-denorm.js.map +1 -0
  78. package/lib/loader-gltf/gltf-sampler-desc.js +11 -2
  79. package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
  80. package/lib/loader-gltf/gltf-uv-denorm.js +28 -0
  81. package/lib/loader-gltf/gltf-uv-denorm.js.map +1 -0
  82. package/lib/loader-gltf/load-gltf.js +15 -6
  83. package/lib/loader-gltf/load-gltf.js.map +1 -1
  84. package/lib/material/material-rebuild.js +4 -0
  85. package/lib/material/material-rebuild.js.map +1 -1
  86. package/lib/material/mesh-features.js +8 -1
  87. package/lib/material/mesh-features.js.map +1 -1
  88. package/lib/material/pbr/fragments/reflectance-fragment.js +1 -1
  89. package/lib/material/pbr/fragments/reflectance-fragment.js.map +1 -1
  90. package/lib/material/pbr/fragments/refraction-rtt-fragment.js +1 -1
  91. package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
  92. package/lib/material/pbr/pbr-pipeline.js +7 -3
  93. package/lib/material/pbr/pbr-pipeline.js.map +1 -1
  94. package/lib/material/pbr/pbr-primitive-resolver.js +34 -0
  95. package/lib/material/pbr/pbr-primitive-resolver.js.map +1 -0
  96. package/lib/material/pbr/pbr-renderable.js +1 -1
  97. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  98. package/lib/material/shader/shader-material.js +9 -5
  99. package/lib/material/shader/shader-material.js.map +1 -1
  100. package/lib/material/shader/shader-thin-instance.js +1 -1
  101. package/lib/material/shader/shader-thin-instance.js.map +1 -1
  102. package/lib/material/standard/standard-renderable.js +1 -1
  103. package/lib/material/standard/standard-renderable.js.map +1 -1
  104. package/lib/mesh/mesh-dispose.js +1 -0
  105. package/lib/mesh/mesh-dispose.js.map +1 -1
  106. package/lib/mesh/thin-instance-cull-binding.js +15 -6
  107. package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
  108. package/lib/scene/scene-core.js +1 -0
  109. package/lib/scene/scene-core.js.map +1 -1
  110. package/lib/scene/scene-material-swap.js +2 -0
  111. package/lib/scene/scene-material-swap.js.map +1 -1
  112. package/lib/shadow/csm-shadow-task-hooks.js +67 -9
  113. package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
  114. package/lib/sprite/sprite-2d.js +4 -0
  115. package/lib/sprite/sprite-2d.js.map +1 -1
  116. package/lib/sprite/sprite-pipeline.js +25 -22
  117. package/lib/sprite/sprite-pipeline.js.map +1 -1
  118. package/lib/text/_gpu/text-pipeline.js +1 -1
  119. package/lib/text/_gpu/text-pipeline.js.map +1 -1
  120. package/lib/text/text-renderer.js +3 -1
  121. package/lib/text/text-renderer.js.map +1 -1
  122. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"sprite-pipeline.js","sources":["../../../src/sprite/sprite-pipeline.ts"],"sourcesContent":["/** Internal sprite pipeline helpers: owns WGSL, bind-group schema, pipeline construction, and bind-group creation. */\nimport { U16 } from \"../engine/typed-arrays.js\";\nimport { BU, SS, CW } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Sprite2DLayer, SpriteBlendMode } from \"./sprite-2d.js\";\nimport type { SpriteLayerFx } from \"./custom-shader-core.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport { DEPTH_INSTANCE_STRIDE_BYTES, DEPTH_UVSCROLL_STRIDE_BYTES, PURE_2D_INSTANCE_STRIDE_BYTES, PURE_2D_UVSCROLL_STRIDE_BYTES } from \"./sprite-2d.js\";\n\n/** @internal */\nexport interface SpritePipelineDeviceCache {\n /** @internal */\n _shaderModule: GPUShaderModule | null;\n /** @internal */\n _sceneShaderModule: GPUShaderModule | null;\n /** @internal */\n _shaderModuleUv: GPUShaderModule | null;\n /** @internal */\n _sceneShaderModuleUv: GPUShaderModule | null;\n /** @internal */\n _pipelines: Map<string, GPURenderPipeline>;\n}\n\n/** @internal */\nexport interface SpritePipelineCache {\n /** @internal */\n _devices: WeakMap<GPUDevice, SpritePipelineDeviceCache>;\n}\n\nconst SPRITE_POSITION_OFFSET_BYTES = 0;\nconst SPRITE_SIZE_OFFSET_BYTES = 8;\nconst SPRITE_UV_MIN_OFFSET_BYTES = 16;\nconst SPRITE_UV_MAX_OFFSET_BYTES = 24;\nconst SPRITE_ROTATION_OFFSET_BYTES = 32;\nconst SPRITE_COLOR_OFFSET_BYTES = 36;\nconst SPRITE_DEPTH_OFFSET_BYTES = 52;\n/** uvOffset.xy byte offset: appended after the base layout (52 pure-2D, 56 depth-hosted). */\nconst SPRITE_UVOFFSET_OFFSET_PURE_2D_BYTES = 52;\nconst SPRITE_UVOFFSET_OFFSET_DEPTH_BYTES = 56;\n\nfunction makeSpriteWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll: boolean): string {\n return `${makeSpritePrologueWgsl(hasDepth, spriteGroupIndex, uvScroll)}\n@fragment\nfn fs(in: VOut) -> @location(0) vec4<f32> {\nlet s = textureSample(atlasTex, atlasSamp, in.uv);\nreturn s * in.tint * L.opacityMul;\n}`;\n}\n\n/**\n * Shared WGSL prologue for the sprite shader: the `Layer` UBO, atlas texture + sampler\n * bindings, instance-attribute `VIn` / interpolant `VOut` structs, and the `vs` vertex stage.\n * The default sprite shader appends a trivial textured fragment; the opt-in custom-shader\n * module (`createSprite2DCustomShader`) appends any extra-texture bindings, a `SpriteFx` UBO at\n * `@binding(3 + 2 * extraTextures.length)`, and the user's raw fragment body. Exposed so both\n * paths share one source of truth.\n */\nexport function makeSpritePrologueWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll = false): string {\n const group = `@group(${spriteGroupIndex})`;\n const zAttribute = hasDepth ? `,\\n@location(6) iZ: f32` : \"\";\n const uvOffsetAttribute = uvScroll ? `,\\n@location(7) iUvOffset: vec2<f32>` : \"\";\n const zPosition = hasDepth ? \"1.0 - in.iZ\" : \"0.0\";\n return `struct Layer {\nviewPos: vec2<f32>,\nviewScale: f32,\nviewRot: f32,\nscreenSize: vec2<f32>,\npivot: vec2<f32>,\n// Per-layer opacity, pre-shaped for the layer's blend mode (CPU-side):\n// straight-alpha: (1, 1, 1, opacity) — only alpha is scaled\n// premultiplied: (opacity, opacity, opacity, opacity) — RGB and A scale together\n// One uniform, no shader branch.\nopacityMul: vec4<f32>,\n};\n${group} @binding(0) var<uniform> L: Layer;\n${group} @binding(1) var atlasTex: texture_2d<f32>;\n${group} @binding(2) var atlasSamp: sampler;\nstruct VIn {\n@builtin(vertex_index) vid: u32,\n@location(0) iPos: vec2<f32>,\n@location(1) iSize: vec2<f32>,\n@location(2) iUvMin: vec2<f32>,\n@location(3) iUvMax: vec2<f32>,\n@location(4) iRot: f32,\n@location(5) iColor: vec4<f32>${zAttribute}${uvOffsetAttribute}\n};\nstruct VOut {\n@builtin(position) pos: vec4<f32>,\n@location(0) uv: vec2<f32>,\n@location(1) tint: vec4<f32>,\n};\n@vertex\nfn vs(in: VIn) -> VOut {\nvar corners = array<vec2<f32>, 4>(vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 0.0), vec2<f32>(1.0, 1.0), vec2<f32>(0.0, 1.0));\nlet c = corners[in.vid];\nlet local = (c - L.pivot) * in.iSize;\nlet cr = cos(in.iRot);\nlet sr = sin(in.iRot);\nlet rotated = vec2<f32>(local.x * cr - local.y * sr, local.x * sr + local.y * cr);\nlet layerPx = in.iPos + rotated;\nlet centered = layerPx - L.viewPos;\nlet lc = cos(L.viewRot);\nlet ls = sin(L.viewRot);\nlet viewRot = vec2<f32>(centered.x * lc - centered.y * ls, centered.x * ls + centered.y * lc);\nlet screenPx = viewRot * L.viewScale;\nlet ndc = vec2<f32>(screenPx.x / L.screenSize.x * 2.0 - 1.0, 1.0 - screenPx.y / L.screenSize.y * 2.0);\nlet uv = mix(in.iUvMin, in.iUvMax, c)${uvScroll ? \" + in.iUvOffset\" : \"\"};\nvar out: VOut;\nout.pos = vec4<f32>(ndc, ${zPosition}, 1.0);\nout.uv = uv;\nout.tint = in.iColor;\nreturn out;\n}`;\n}\n\nexport function createSpritePipelineCache(): SpritePipelineCache {\n return {\n _devices: new WeakMap(),\n };\n}\n\nexport function resetSpritePipelineCache(cache: SpritePipelineCache): void {\n cache._devices = new WeakMap();\n}\n\nexport function getSpritePipelineCacheSize(cache: SpritePipelineCache, device: GPUDevice): number {\n return cache._devices.get(device)?._pipelines.size ?? 0;\n}\n\nexport function getOrCreateSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite = false,\n depthStencilFormat?: GPUTextureFormat,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const deviceCache = getSpritePipelineDeviceCache(engine, cache);\n const resolvedDepthStencilFormat = normalizeDepthStencilFormat(hasDepth, depthStencilFormat);\n const key = spritePipelineKey(format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, layer);\n const cached = deviceCache._pipelines.get(key);\n if (cached) {\n return cached;\n }\n\n const pipeline = buildSpritePipeline(engine, deviceCache, format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, sceneBindGroupLayout, layer);\n deviceCache._pipelines.set(key, pipeline);\n return pipeline;\n}\n\nexport function createSpriteLayerBindGroup(\n engine: EngineContext,\n pipeline: GPURenderPipeline,\n spriteBindGroupIndex: 0 | 1,\n layer: Sprite2DLayer,\n uniformBuffer: GPUBuffer,\n fx?: SpriteLayerFx | null\n): GPUBindGroup {\n const tex = layer.atlas.texture;\n const entries: GPUBindGroupEntry[] = [\n { binding: 0, resource: { buffer: uniformBuffer } },\n { binding: 1, resource: tex.view },\n { binding: 2, resource: tex.sampler },\n ];\n if (fx) {\n for (const entry of _getSpriteFxHook()!.bindEntries(fx, 3)) {\n entries.push(entry);\n }\n }\n return engine._device.createBindGroup({\n layout: pipeline.getBindGroupLayout(spriteBindGroupIndex),\n entries,\n });\n}\n\nfunction getSpritePipelineDeviceCache(engine: EngineContext, cache: SpritePipelineCache): SpritePipelineDeviceCache {\n let deviceCache = cache._devices.get(engine._device);\n if (!deviceCache) {\n deviceCache = {\n _shaderModule: null,\n _sceneShaderModule: null,\n _shaderModuleUv: null,\n _sceneShaderModuleUv: null,\n _pipelines: new Map(),\n };\n cache._devices.set(engine._device, deviceCache);\n }\n return deviceCache;\n}\n\nfunction normalizeDepthStencilFormat(hasDepth: boolean, depthStencilFormat?: GPUTextureFormat): GPUTextureFormat | null {\n if (!hasDepth) {\n return null;\n }\n if (!depthStencilFormat) {\n throw new Error(\"Sprite pipeline: depth-enabled pipelines require a depth-stencil format.\");\n }\n return depthStencilFormat;\n}\n\nfunction spritePipelineKey(\n format: GPUTextureFormat,\n sampleCount: number,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite: boolean,\n depthStencilFormat: GPUTextureFormat | null,\n layer?: Sprite2DLayer\n): string {\n const customKey = layer ? (_getSpriteFxHook()?.pipelineKeyPart(layer) ?? \"\") : \"\";\n const uvKey = layer?._uvScroll ? \"1\" : \"0\";\n return `${format}:${sampleCount}:${blendMode._key}:${hasDepth ? 1 : 0}:${depthWrite ? 1 : 0}:${depthStencilFormat ?? \"-\"}:cs${customKey}:uv${uvKey}`;\n}\n\nfunction getShaderModule(engine: EngineContext, cache: SpritePipelineDeviceCache, hasDepth: boolean, layer?: Sprite2DLayer): GPUShaderModule {\n const customModule = layer ? _getSpriteFxHook()?.shaderModule(engine, hasDepth, layer) : null;\n if (customModule) {\n return customModule;\n }\n const uvScroll = layer?._uvScroll === true;\n if (hasDepth) {\n if (uvScroll) {\n cache._sceneShaderModuleUv ??= engine._device.createShaderModule({ code: makeSpriteWgsl(true, 1, true) });\n return cache._sceneShaderModuleUv;\n }\n cache._sceneShaderModule ??= engine._device.createShaderModule({ code: makeSpriteWgsl(true, 1, false) });\n return cache._sceneShaderModule;\n }\n if (uvScroll) {\n cache._shaderModuleUv ??= engine._device.createShaderModule({ code: makeSpriteWgsl(false, 0, true) });\n return cache._shaderModuleUv;\n }\n cache._shaderModule ??= engine._device.createShaderModule({ code: makeSpriteWgsl(false, 0, false) });\n return cache._shaderModule;\n}\n\nfunction buildSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineDeviceCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite: boolean,\n depthStencilFormat: GPUTextureFormat | null,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const device = engine._device;\n const layoutEntries: GPUBindGroupLayoutEntry[] = [\n { binding: 0, visibility: SS.VERTEX | SS.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: SS.FRAGMENT, texture: { sampleType: \"float\" } },\n { binding: 2, visibility: SS.FRAGMENT, sampler: { type: \"filtering\" } },\n ];\n const extraLayoutEntries = layer ? _getSpriteFxHook()?.layoutEntries(layer, 3) : null;\n if (extraLayoutEntries) {\n for (const entry of extraLayoutEntries) {\n layoutEntries.push(entry);\n }\n }\n const bindGroupLayout = device.createBindGroupLayout({ entries: layoutEntries });\n const module = getShaderModule(engine, cache, hasDepth, layer);\n if (hasDepth && !sceneBindGroupLayout) {\n throw new Error(\"Sprite pipeline: depth-enabled pipelines require a scene bind-group layout.\");\n }\n const bindGroupLayouts = hasDepth ? [sceneBindGroupLayout!, bindGroupLayout] : [bindGroupLayout];\n const instanceAttributes: GPUVertexAttribute[] = [\n { shaderLocation: 0, offset: SPRITE_POSITION_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 1, offset: SPRITE_SIZE_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 2, offset: SPRITE_UV_MIN_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 3, offset: SPRITE_UV_MAX_OFFSET_BYTES, format: \"float32x2\" },\n { shaderLocation: 4, offset: SPRITE_ROTATION_OFFSET_BYTES, format: \"float32\" },\n { shaderLocation: 5, offset: SPRITE_COLOR_OFFSET_BYTES, format: \"float32x4\" },\n ];\n if (hasDepth) {\n instanceAttributes.push({ shaderLocation: 6, offset: SPRITE_DEPTH_OFFSET_BYTES, format: \"float32\" });\n }\n const uvScroll = layer?._uvScroll === true;\n if (uvScroll) {\n instanceAttributes.push({\n shaderLocation: 7,\n offset: hasDepth ? SPRITE_UVOFFSET_OFFSET_DEPTH_BYTES : SPRITE_UVOFFSET_OFFSET_PURE_2D_BYTES,\n format: \"float32x2\",\n });\n }\n const arrayStride = uvScroll\n ? hasDepth\n ? DEPTH_UVSCROLL_STRIDE_BYTES\n : PURE_2D_UVSCROLL_STRIDE_BYTES\n : hasDepth\n ? DEPTH_INSTANCE_STRIDE_BYTES\n : PURE_2D_INSTANCE_STRIDE_BYTES;\n const descriptor: GPURenderPipelineDescriptor = {\n layout: device.createPipelineLayout({ bindGroupLayouts }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n {\n arrayStride: arrayStride,\n stepMode: \"instance\",\n attributes: instanceAttributes,\n },\n ],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format, blend: blendMode._descriptor, writeMask: CW.ALL }],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n multisample: { count: sampleCount },\n };\n if (hasDepth) {\n descriptor.depthStencil = {\n format: depthStencilFormat!,\n depthCompare: \"greater-equal\",\n depthWriteEnabled: depthWrite,\n };\n }\n return device.createRenderPipeline(descriptor);\n}\n\n// ─── Per-layer GPU sync helpers ────────────────────────────────────────────\n// Shared by `sprite-renderer.ts` (multi-layer pure-2D pass) and\n// `sprite-renderable.ts` (single-layer depth-hosted scene `Renderable`).\n// The two consumers have different lifecycles (renderer caches a `LayerGpu`\n// per layer; renderable owns one `Sprite2DLayer`) but the per-frame work —\n// \"grow instance buffer if needed\", \"upload dirty instance range\",\n// \"build the 12-float UBO\", \"writeBuffer only if changed\" — is identical.\n\n/** Per-layer UBO size in bytes. 12 floats; struct alignment forced to 16 by `vec4<f32>` fields. */\nexport const LAYER_UBO_BYTES = 48;\n/** Number of floats in the per-layer UBO scratch / lastUbo arrays. */\nexport const LAYER_UBO_FLOATS = LAYER_UBO_BYTES / 4;\n\n/** Shared two-triangle quad index buffer source (4 corners → 6 indices). */\nexport const SHARED_SPRITE_INDEX_DATA: Readonly<Uint16Array> = new U16([0, 1, 2, 0, 2, 3]);\n\n/** Allocate a per-layer instance vertex buffer sized for `capacity` sprites. */\nexport function createSpriteInstanceBuffer(device: GPUDevice, layer: Sprite2DLayer, label?: string): GPUBuffer {\n return device.createBuffer({\n size: layer._capacity * layer._instanceStrideBytes,\n usage: BU.VERTEX | BU.COPY_DST,\n label,\n });\n}\n\n/**\n * Reallocate the instance buffer if `layer._capacity` outgrew the current GPU buffer.\n * Returns the (possibly new) buffer + the new capacity, plus a `reallocated` flag the\n * caller uses to invalidate per-buffer caches (render bundles, `uploadedVersion`, etc).\n */\nexport function ensureSpriteInstanceBuffer(\n device: GPUDevice,\n layer: Sprite2DLayer,\n currentBuffer: GPUBuffer,\n currentCapacity: number,\n label?: string\n): { buffer: GPUBuffer; capacity: number; reallocated: boolean } {\n if (currentCapacity >= layer._capacity) {\n return { buffer: currentBuffer, capacity: currentCapacity, reallocated: false };\n }\n currentBuffer.destroy();\n return {\n buffer: createSpriteInstanceBuffer(device, layer, label),\n capacity: layer._capacity,\n reallocated: true,\n };\n}\n\n/**\n * Sync per-instance vertex data to `instanceBuffer`. Returns the new `uploadedVersion`\n * the caller should store. No-op if `layer._version` hasn't advanced or the layer is\n * empty. On first sight (`uploadedVersion === -1`) uploads `[0, count)`; on subsequent\n * edits uploads only `[_dirtyMin, min(_dirtyMax, count))`. Resets the dirty range.\n */\nexport function uploadSpriteInstances(device: GPUDevice, layer: Sprite2DLayer, instanceBuffer: GPUBuffer, uploadedVersion: number): number {\n if (uploadedVersion === layer._version) {\n return uploadedVersion;\n }\n if (layer.count === 0) {\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n }\n let lo: number;\n let hi: number;\n if (uploadedVersion === -1) {\n lo = 0;\n hi = layer.count;\n } else {\n lo = layer._dirtyMin;\n hi = Math.min(layer._dirtyMax, layer.count);\n }\n if (hi > lo) {\n const offsetBytes = lo * layer._instanceStrideBytes;\n const bytes = (hi - lo) * layer._instanceStrideBytes;\n device.queue.writeBuffer(instanceBuffer, offsetBytes, layer._instanceData.buffer, layer._instanceData.byteOffset + offsetBytes, bytes);\n }\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n}\n\n/**\n * Fill `ubo` (12 floats) with the per-layer UBO contents from `layer` at the given\n * render-target dims. Layout matches the WGSL `Layer` struct (48 bytes total):\n * [0..1] viewPos.xy [2] viewScale [3] viewRot\n * [4..5] screenSize.xy [6..7] pivot.xy\n * [8..11] opacityMul.rgba (pre-shaped per blend mode)\n *\n * Depth-hosted layers keep per-sprite NDC depth on the per-instance vertex buffer\n * (slot [13] of `Sprite2DLayer._instanceData`), not in this UBO — a single\n * depth-hosted layer can mix sprites at different depths. Pure-2D layers have no\n * Z slot.\n *\n * Premultiplied sources need RGB *and* A scaled by opacity for a correct fade;\n * straight-alpha needs only A scaled (the blend stage already uses src.a as factor).\n */\nexport function buildSpriteLayerUbo(layer: Sprite2DLayer, screenWidth: number, screenHeight: number, ubo: Float32Array): void {\n ubo[0] = layer.view.positionPx[0];\n ubo[1] = layer.view.positionPx[1];\n ubo[2] = layer.view.zoom;\n ubo[3] = layer.view.rotation;\n ubo[4] = screenWidth;\n ubo[5] = screenHeight;\n ubo[6] = layer.pivot[0];\n ubo[7] = layer.pivot[1];\n const op = layer.opacity;\n if (layer.blendMode._premultipliedOpacity) {\n ubo[8] = op;\n ubo[9] = op;\n ubo[10] = op;\n ubo[11] = op;\n } else {\n ubo[8] = 1;\n ubo[9] = 1;\n ubo[10] = 1;\n ubo[11] = op;\n }\n}\n\n/**\n * Compare `scratchUbo` to `lastUbo` (LAYER_UBO_FLOATS each) and `writeBuffer` only if they\n * differ. On first call (`alreadyUploaded === false`) forces an unconditional write\n * so `lastUbo` becomes real. Returns the new `alreadyUploaded` value (always `true`\n * after the first call regardless of whether bytes changed).\n */\nexport function writeSpriteLayerUboIfDirty(device: GPUDevice, uniformBuffer: GPUBuffer, scratchUbo: Float32Array, lastUbo: Float32Array, alreadyUploaded: boolean): boolean {\n let dirty = !alreadyUploaded;\n if (!dirty) {\n for (let i = 0; i < LAYER_UBO_FLOATS; i++) {\n if (lastUbo[i] !== scratchUbo[i]) {\n dirty = true;\n break;\n }\n }\n }\n if (dirty) {\n device.queue.writeBuffer(uniformBuffer, 0, scratchUbo.buffer, scratchUbo.byteOffset, LAYER_UBO_BYTES);\n lastUbo.set(scratchUbo);\n }\n return true;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sprite-pipeline.js","sources":["../../../src/sprite/sprite-pipeline.ts"],"sourcesContent":["/** Internal sprite pipeline helpers: owns WGSL, bind-group schema, pipeline construction, and bind-group creation. */\nimport { U16 } from \"../engine/typed-arrays.js\";\nimport { BU, SS, CW } from \"../engine/gpu-flags.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Sprite2DLayer, SpriteBlendMode } from \"./sprite-2d.js\";\nimport type { SpriteLayerFx } from \"./custom-shader-core.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport { DEPTH_INSTANCE_STRIDE_BYTES, DEPTH_UVSCROLL_STRIDE_BYTES, PURE_2D_INSTANCE_STRIDE_BYTES, PURE_2D_UVSCROLL_STRIDE_BYTES } from \"./sprite-2d.js\";\n\n/** @internal */\nexport interface SpritePipelineDeviceCache {\n /** @internal Shader modules keyed by `${hasDepth}:${uvScroll}:${coverageGamma}` 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/** uvOffset.xy byte offset: appended after the base layout (52 pure-2D, 56 depth-hosted). */\nconst SPRITE_UVOFFSET_OFFSET_PURE_2D_BYTES = 52;\nconst SPRITE_UVOFFSET_OFFSET_DEPTH_BYTES = 56;\n\nfunction makeSpriteWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll: boolean, coverageGamma = false): string {\n // Coverage gamma (opt-in, text layers): raise sampled alpha to 1/coverageGamma (L.aa.x) so\n // anti-aliased glyph edges composite heavier, mimicking gamma-space stem darkening. Gated at\n // shader-build time — non-gamma layers ship the trivial textured fragment with no `pow`.\n const coverageLine = coverageGamma ? `\\nlet a = pow(s.a, L.aa.x);\\nreturn vec4<f32>(s.rgb, a) * in.tint * L.opacityMul;` : `\\nreturn s * in.tint * L.opacityMul;`;\n return `${makeSpritePrologueWgsl(hasDepth, spriteGroupIndex, uvScroll)}\n@fragment\nfn fs(in: VOut) -> @location(0) vec4<f32> {\nlet s = textureSample(atlasTex, atlasSamp, in.uv);${coverageLine}\n}`;\n}\n\n/**\n * Shared WGSL prologue for the sprite shader: the `Layer` UBO, atlas texture + sampler\n * bindings, instance-attribute `VIn` / interpolant `VOut` structs, and the `vs` vertex stage.\n * The default sprite shader appends a trivial textured fragment; the opt-in custom-shader\n * module (`createSprite2DCustomShader`) appends any extra-texture bindings, a `SpriteFx` UBO at\n * `@binding(3 + 2 * extraTextures.length)`, and the user's raw fragment body. Exposed so both\n * paths share one source of truth.\n */\nexport function makeSpritePrologueWgsl(hasDepth: boolean, spriteGroupIndex: 0 | 1, uvScroll = false): string {\n const group = `@group(${spriteGroupIndex})`;\n const zAttribute = hasDepth ? `,\\n@location(6) iZ: f32` : \"\";\n const uvOffsetAttribute = uvScroll ? `,\\n@location(7) iUvOffset: vec2<f32>` : \"\";\n const zPosition = hasDepth ? \"1.0 - in.iZ\" : \"0.0\";\n return `struct Layer {\nviewPos: vec2<f32>,\nviewScale: f32,\nviewRot: f32,\nscreenSize: vec2<f32>,\npivot: vec2<f32>,\n// Per-layer opacity, pre-shaped for the layer's blend mode (CPU-side):\n// straight-alpha: (1, 1, 1, opacity) — only alpha is scaled\n// premultiplied: (opacity, opacity, opacity, opacity) — RGB and A scale together\n// One uniform, no shader branch.\nopacityMul: vec4<f32>,\n// Coverage-gamma controls (text layers): .x = 1/coverageGamma applied to sampled alpha by the\n// coverage-gamma shader permutation. .yzw reserved. Always present (UBO is a fixed 64 bytes);\n// unused by the base shader permutation.\naa: vec4<f32>,\n};\n${group} @binding(0) var<uniform> L: Layer;\n${group} @binding(1) var atlasTex: texture_2d<f32>;\n${group} @binding(2) var atlasSamp: sampler;\nstruct VIn {\n@builtin(vertex_index) vid: u32,\n@location(0) iPos: vec2<f32>,\n@location(1) iSize: vec2<f32>,\n@location(2) iUvMin: vec2<f32>,\n@location(3) iUvMax: vec2<f32>,\n@location(4) iRot: f32,\n@location(5) iColor: vec4<f32>${zAttribute}${uvOffsetAttribute}\n};\nstruct VOut {\n@builtin(position) pos: vec4<f32>,\n@location(0) uv: vec2<f32>,\n@location(1) tint: vec4<f32>,\n};\n@vertex\nfn vs(in: VIn) -> VOut {\nvar corners = array<vec2<f32>, 4>(vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 0.0), vec2<f32>(1.0, 1.0), vec2<f32>(0.0, 1.0));\nlet c = corners[in.vid];\nlet local = (c - L.pivot) * in.iSize;\nlet cr = cos(in.iRot);\nlet sr = sin(in.iRot);\nlet rotated = vec2<f32>(local.x * cr - local.y * sr, local.x * sr + local.y * cr);\nlet layerPx = in.iPos + rotated;\nlet centered = layerPx - L.viewPos;\nlet lc = cos(L.viewRot);\nlet ls = sin(L.viewRot);\nlet viewRot = vec2<f32>(centered.x * lc - centered.y * ls, centered.x * ls + centered.y * lc);\nlet screenPx = viewRot * L.viewScale;\nlet ndc = vec2<f32>(screenPx.x / L.screenSize.x * 2.0 - 1.0, 1.0 - screenPx.y / L.screenSize.y * 2.0);\nlet uv = mix(in.iUvMin, in.iUvMax, c)${uvScroll ? \" + in.iUvOffset\" : \"\"};\nvar out: VOut;\nout.pos = vec4<f32>(ndc, ${zPosition}, 1.0);\nout.uv = uv;\nout.tint = in.iColor;\nreturn out;\n}`;\n}\n\nexport function createSpritePipelineCache(): SpritePipelineCache {\n return {\n _devices: new WeakMap(),\n };\n}\n\nexport function resetSpritePipelineCache(cache: SpritePipelineCache): void {\n cache._devices = new WeakMap();\n}\n\nexport function getSpritePipelineCacheSize(cache: SpritePipelineCache, device: GPUDevice): number {\n return cache._devices.get(device)?._pipelines.size ?? 0;\n}\n\nexport function getOrCreateSpritePipeline(\n engine: EngineContext,\n cache: SpritePipelineCache,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n blendMode: SpriteBlendMode,\n hasDepth: boolean,\n depthWrite = false,\n depthStencilFormat?: GPUTextureFormat,\n sceneBindGroupLayout?: GPUBindGroupLayout,\n layer?: Sprite2DLayer\n): GPURenderPipeline {\n const deviceCache = getSpritePipelineDeviceCache(engine, cache);\n const resolvedDepthStencilFormat = normalizeDepthStencilFormat(hasDepth, depthStencilFormat);\n const key = spritePipelineKey(format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, layer);\n const cached = deviceCache._pipelines.get(key);\n if (cached) {\n return cached;\n }\n\n const pipeline = buildSpritePipeline(engine, deviceCache, format, sampleCount, blendMode, hasDepth, depthWrite, resolvedDepthStencilFormat, sceneBindGroupLayout, layer);\n deviceCache._pipelines.set(key, pipeline);\n return pipeline;\n}\n\nexport function createSpriteLayerBindGroup(\n engine: EngineContext,\n pipeline: GPURenderPipeline,\n spriteBindGroupIndex: 0 | 1,\n layer: Sprite2DLayer,\n uniformBuffer: GPUBuffer,\n fx?: SpriteLayerFx | null\n): GPUBindGroup {\n const tex = layer.atlas.texture;\n const entries: GPUBindGroupEntry[] = [\n { binding: 0, resource: { buffer: uniformBuffer } },\n { binding: 1, resource: tex.view },\n { binding: 2, resource: tex.sampler },\n ];\n if (fx) {\n for (const entry of _getSpriteFxHook()!.bindEntries(fx, 3)) {\n entries.push(entry);\n }\n }\n return engine._device.createBindGroup({\n layout: pipeline.getBindGroupLayout(spriteBindGroupIndex),\n entries,\n });\n}\n\nfunction getSpritePipelineDeviceCache(engine: EngineContext, cache: SpritePipelineCache): SpritePipelineDeviceCache {\n let deviceCache = cache._devices.get(engine._device);\n if (!deviceCache) {\n deviceCache = {\n _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?._uvScroll ? \"1\" : \"0\";\n const cgKey = layer?._coverageGamma ? \"1\" : \"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 uvScroll = layer?._uvScroll === true;\n const coverageGamma = layer?._coverageGamma === true;\n const key = `${hasDepth ? 1 : 0}:${uvScroll ? 1 : 0}:${coverageGamma ? 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, coverageGamma),\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 const uvScroll = layer?._uvScroll === true;\n if (uvScroll) {\n instanceAttributes.push({\n shaderLocation: 7,\n offset: hasDepth ? SPRITE_UVOFFSET_OFFSET_DEPTH_BYTES : SPRITE_UVOFFSET_OFFSET_PURE_2D_BYTES,\n format: \"float32x2\",\n });\n }\n const arrayStride = uvScroll\n ? hasDepth\n ? DEPTH_UVSCROLL_STRIDE_BYTES\n : PURE_2D_UVSCROLL_STRIDE_BYTES\n : hasDepth\n ? DEPTH_INSTANCE_STRIDE_BYTES\n : PURE_2D_INSTANCE_STRIDE_BYTES;\n const descriptor: GPURenderPipelineDescriptor = {\n layout: device.createPipelineLayout({ bindGroupLayouts }),\n vertex: {\n module,\n entryPoint: \"vs\",\n buffers: [\n {\n arrayStride: arrayStride,\n stepMode: \"instance\",\n attributes: instanceAttributes,\n },\n ],\n },\n fragment: {\n module,\n entryPoint: \"fs\",\n targets: [{ format, blend: blendMode._descriptor, writeMask: CW.ALL }],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\n multisample: { count: sampleCount },\n };\n if (hasDepth) {\n descriptor.depthStencil = {\n format: depthStencilFormat!,\n depthCompare: \"greater-equal\",\n depthWriteEnabled: depthWrite,\n };\n }\n return device.createRenderPipeline(descriptor);\n}\n\n// ─── Per-layer GPU sync helpers ────────────────────────────────────────────\n// Shared by `sprite-renderer.ts` (multi-layer pure-2D pass) and\n// `sprite-renderable.ts` (single-layer depth-hosted scene `Renderable`).\n// The two consumers have different lifecycles (renderer caches a `LayerGpu`\n// per layer; renderable owns one `Sprite2DLayer`) but the per-frame work —\n// \"grow instance buffer if needed\", \"upload dirty instance range\",\n// \"build the 12-float UBO\", \"writeBuffer only if changed\" — is identical.\n\n/** Per-layer UBO size in bytes. 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 `layer._capacity` outgrew the current GPU buffer.\n * Returns the (possibly new) buffer + the new capacity, plus a `reallocated` flag the\n * caller uses to invalidate per-buffer caches (render bundles, `uploadedVersion`, etc).\n */\nexport function ensureSpriteInstanceBuffer(\n device: GPUDevice,\n layer: Sprite2DLayer,\n currentBuffer: GPUBuffer,\n currentCapacity: number,\n label?: string\n): { buffer: GPUBuffer; capacity: number; reallocated: boolean } {\n if (currentCapacity >= layer._capacity) {\n return { buffer: currentBuffer, capacity: currentCapacity, reallocated: false };\n }\n currentBuffer.destroy();\n return {\n buffer: createSpriteInstanceBuffer(device, layer, label),\n capacity: layer._capacity,\n reallocated: true,\n };\n}\n\n/**\n * Sync per-instance vertex data to `instanceBuffer`. Returns the new `uploadedVersion`\n * the caller should store. No-op if `layer._version` hasn't advanced or the layer is\n * empty. On first sight (`uploadedVersion === -1`) uploads `[0, count)`; on subsequent\n * edits uploads only `[_dirtyMin, min(_dirtyMax, count))`. Resets the dirty range.\n */\nexport function uploadSpriteInstances(device: GPUDevice, layer: Sprite2DLayer, instanceBuffer: GPUBuffer, uploadedVersion: number): number {\n if (uploadedVersion === layer._version) {\n return uploadedVersion;\n }\n if (layer.count === 0) {\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n }\n let lo: number;\n let hi: number;\n if (uploadedVersion === -1) {\n lo = 0;\n hi = layer.count;\n } else {\n lo = layer._dirtyMin;\n hi = Math.min(layer._dirtyMax, layer.count);\n }\n if (hi > lo) {\n const offsetBytes = lo * layer._instanceStrideBytes;\n const bytes = (hi - lo) * layer._instanceStrideBytes;\n device.queue.writeBuffer(instanceBuffer, offsetBytes, layer._instanceData.buffer, layer._instanceData.byteOffset + offsetBytes, bytes);\n }\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n return layer._version;\n}\n\n/**\n * Fill `ubo` (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 const g = layer.coverageGamma;\n // aa.x = 1/coverageGamma; aa.yzw reserved (scratch UBO is zero-initialized and reused, so\n // 13..15 stay 0 without explicit writes).\n ubo[12] = g > 0 ? 1 / g : 1;\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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"}
@@ -2,7 +2,7 @@ import { TEXT_INSTANCE_BYTES } from '../text-data.js';
2
2
 
3
3
  const vertSrc = "struct A{mvp:mat4x4<f32>,viewport:vec4<f32>,color:vec4<f32>}@group(0) @binding(0) var<uniform> textU:A;struct B{@location(0) slugCorner:vec2<f32>,@location(1) slugBounds:vec4<f32>,@location(2) slugAnchor:vec4<f32>,@location(3) slugAtlas:vec4<f32>,@location(4) slugBand:vec4<f32>,@location(5) slugColor:vec4<f32>}struct u{@builtin(position) pos:vec4<f32>,@location(0) vTexcoord:vec2<f32>,@location(1) @interpolate(flat) vBanding:vec4<f32>,@location(2) @interpolate(flat) vGlyph:vec4<f32>,@location(3) @interpolate(flat) vColor:vec4<f32>}@vertex fn main(b:B)->u{if (b.slugAnchor.w>0.5){var d:u;d.pos=vec4<f32>(-2.0,-2.0,-2.0,1.0);d.vTexcoord=vec2<f32>(0.0,0.0);d.vBanding=vec4<f32>(0.0);d.vGlyph=vec4<f32>(0.0);d.vColor=vec4<f32>(0.0);return d;}let x=vec2<f32>(step(0.0,b.slugCorner.x),step(0.0,b.slugCorner.y));let j=mix(b.slugBounds.xy,b.slugBounds.zw,x);let f=b.slugAnchor.z;let v=select(0.0,1.0/f,f!=0.0);let g=b.slugAnchor.xy+j*v;let q=b.slugCorner;let p=vec4<f32>(f,0.0,0.0,f);let a=textU.mvp;let n=vec4<f32>(a[0].x,a[1].x,a[2].x,a[3].x);let h=vec4<f32>(a[0].y,a[1].y,a[2].y,a[3].y);let o=vec4<f32>(a[0].w,a[1].w,a[2].w,a[3].w);let m=normalize(q);let e=dot(o.xy,g)+o.w;let i=dot(o.xy,m);let s=(e*dot(n.xy,m)-i*(dot(n.xy,g)+n.w))*textU.viewport.x;let t=(e*dot(h.xy,m)-i*(dot(h.xy,g)+h.w))*textU.viewport.y;let w=e*e;let l=e*i;let r=s*s+t*t;let k=q*(w*(l+sqrt(r))/(r-l*l));let y=g+k;let z=vec2<f32>(j.x+dot(k,p.xy),j.y+dot(k,p.zw));var c:u;c.pos=a*vec4<f32>(y,0.0,1.0);c.vTexcoord=z;c.vBanding=b.slugBand;c.vGlyph=b.slugAtlas;c.vColor=vec4<f32>(b.slugColor.rgb,b.slugColor.a*textU.color.a);return c;}";
4
4
 
5
- const fragSrc = "@group(0) @binding(1) var s:texture_2d<f32>;@group(0) @binding(2) var w:texture_2d<f32>;struct ra{@location(0) vTexcoord:vec2<f32>,@location(1) @interpolate(flat) vBanding:vec4<f32>,@location(2) @interpolate(flat) vGlyph:vec4<f32>,@location(3) @interpolate(flat) vColor:vec4<f32>,@builtin(front_facing) frontFacing:bool}fn I(ka:f32,la:f32,ga:f32)->i32{let ja:i32=select(0,1,ka<0.0);let fa:i32=select(0,2,la<0.0);let ma:i32=select(0,4,ga<0.0);let na=u32(ja+fa+ma);return (0x2E74>>na)&0x0101;}fn da(a:vec4<f32>,R:vec2<f32>)->vec2<f32>{let n=vec2<f32>(a.x-a.z*2.0+R.x,a.y-a.w*2.0+R.y);let f=vec2<f32>(a.x-a.z,a.y-a.w);let N=1.0/n.y;let M=0.5/f.y;let T=sqrt(max(f.y*f.y-n.y*a.y,0.0));var H=(f.y-T)*N;var G=(f.y+T)*N;if (abs(n.y)<=max(abs(f.y),abs(a.y))*1.0e-4){H=a.y*M;G=a.y*M;}return vec2<f32>((n.x*H-f.x*2.0)*H+a.x,(n.x*G-f.x*2.0)*G+a.x);}fn ca(b:vec4<f32>,U:vec2<f32>)->vec2<f32>{let m=vec2<f32>(b.x-b.z*2.0+U.x,b.y-b.w*2.0+U.y);let c=vec2<f32>(b.x-b.z,b.y-b.w);let ba=1.0/m.x;let Z=0.5/c.x;let X=sqrt(max(c.x*c.x-m.x*b.x,0.0));var C=(c.x-X)*ba;var D=(c.x+X)*ba;if (abs(m.x)<=max(abs(c.x),abs(b.x))*1.0e-4){C=b.x*Z;D=b.x*Z;}return vec2<f32>((m.y*C-c.y*2.0)*C+b.y,(m.y*D-c.y*2.0)*D+b.y);}fn J(V:vec2<i32>,qa:i32)->vec2<i32>{var i=vec2<i32>(V.x+qa,V.y);i.y=i.y+(i.x>>12u);i.x=i.x&4095;return i;}@fragment fn main(d:ra)->@location(0) vec4<f32>{if (!d.frontFacing){discard;}let e=d.vTexcoord;let pa=fwidth(e);let t=1.0/pa;let j=vec2<i32>(i32(d.vGlyph.x+0.5),i32(d.vGlyph.y+0.5));let K=vec2<i32>(i32(d.vGlyph.z+0.5),i32(d.vGlyph.w+0.5));let P=d.vBanding;let Q=clamp(vec2<i32>(e*P.xy+P.zw),vec2<i32>(0,0),K);var k:f32=0.0;var l:f32=0.0;let L=textureLoad(w,vec2<i32>(j.x+Q.y,j.y),0);let ea=i32(L.x+0.5);let oa=i32(L.y+0.5);let W=J(j,oa);for(var q:i32=0;q<ea;q=q+1){let Y=textureLoad(w,vec2<i32>(W.x+q,W.y),0);let x=vec2<i32>(i32(Y.x+0.5),i32(Y.y+0.5));let p=textureLoad(s,x,0)-vec4<f32>(e,e);let A=textureLoad(s,vec2<i32>(x.x+1,x.y),0).xy-e;if (max(max(p.x,p.z),A.x)*t.x<-0.5){break;}let z=I(p.y,p.w,A.y);if (z!=0){let r=da(p,A)*t.x;if ((z&1)!=0){k=k+clamp(r.x+0.5,0.0,1.0);l=max(l,clamp(1.0-abs(r.x)*2.0,0.0,1.0));}if (z>1){k=k-clamp(r.y+0.5,0.0,1.0);l=max(l,clamp(1.0-abs(r.y)*2.0,0.0,1.0));}}}var h:f32=0.0;var g:f32=0.0;let O=textureLoad(w,vec2<i32>(j.x+K.y+1+Q.x,j.y),0);let ha=i32(O.x+0.5);let ia=i32(O.y+0.5);let S=J(j,ia);for(var u:i32=0;u<ha;u=u+1){let aa=textureLoad(w,vec2<i32>(S.x+u,S.y),0);let B=vec2<i32>(i32(aa.x+0.5),i32(aa.y+0.5));let o=textureLoad(s,B,0)-vec4<f32>(e,e);let E=textureLoad(s,vec2<i32>(B.x+1,B.y),0).xy-e;if (max(max(o.y,o.w),E.y)*t.y<-0.5){break;}let y=I(o.x,o.z,E.x);if (y!=0){let v=ca(o,E)*t.y;if ((y&1)!=0){h=h-clamp(v.x+0.5,0.0,1.0);g=max(g,clamp(1.0-abs(v.x)*2.0,0.0,1.0));}if (y>1){h=h+clamp(v.y+0.5,0.0,1.0);g=max(g,clamp(1.0-abs(v.y)*2.0,0.0,1.0));}}}var F=max(abs(k*l+h*g)/max(l+g,1.0/65536.0),min(abs(k),abs(h)));F=clamp(F,0.0,1.0);return d.vColor*F;}";
5
+ const fragSrc = "@group(0) @binding(1) var t:texture_2d<f32>;@group(0) @binding(2) var u:texture_2d<f32>;struct sa{mvp:mat4x4<f32>,viewport:vec4<f32>,color:vec4<f32>}@group(0) @binding(0) var<uniform> textU:sa;struct ra{@location(0) vTexcoord:vec2<f32>,@location(1) @interpolate(flat) vBanding:vec4<f32>,@location(2) @interpolate(flat) vGlyph:vec4<f32>,@location(3) @interpolate(flat) vColor:vec4<f32>,@builtin(front_facing) frontFacing:bool}fn I(qa:f32,pa:f32,oa:f32)->i32{let na:i32=select(0,1,qa<0.0);let la:i32=select(0,2,pa<0.0);let ga:i32=select(0,4,oa<0.0);let fa=u32(na+la+ga);return (0x2E74>>fa)&0x0101;}fn ca(b:vec4<f32>,aa:vec2<f32>)->vec2<f32>{let q=vec2<f32>(b.x-b.z*2.0+aa.x,b.y-b.w*2.0+aa.y);let d=vec2<f32>(b.x-b.z,b.y-b.w);let K=1.0/q.y;let ba=0.5/d.y;let L=sqrt(max(d.y*d.y-q.y*b.y,0.0));var H=(d.y-L)*K;var G=(d.y+L)*K;if (abs(q.y)<=max(abs(d.y),abs(b.y))*1.0e-4){H=b.y*ba;G=b.y*ba;}return vec2<f32>((q.x*H-d.x*2.0)*H+b.x,(q.x*G-d.x*2.0)*G+b.x);}fn da(a:vec4<f32>,Y:vec2<f32>)->vec2<f32>{let n=vec2<f32>(a.x-a.z*2.0+Y.x,a.y-a.w*2.0+Y.y);let c=vec2<f32>(a.x-a.z,a.y-a.w);let T=1.0/n.x;let S=0.5/c.x;let O=sqrt(max(c.x*c.x-n.x*a.x,0.0));var D=(c.x-O)*T;var E=(c.x+O)*T;if (abs(n.x)<=max(abs(c.x),abs(a.x))*1.0e-4){D=a.x*S;E=a.x*S;}return vec2<f32>((n.y*D-c.y*2.0)*D+a.y,(n.y*E-c.y*2.0)*E+a.y);}fn J(N:vec2<i32>,ka:i32)->vec2<i32>{var l=vec2<i32>(N.x+ka,N.y);l.y=l.y+(l.x>>12u);l.x=l.x&4095;return l;}@fragment fn main(f:ra)->@location(0) vec4<f32>{if (!f.frontFacing){discard;}let e=f.vTexcoord;let ma=fwidth(e);let w=1.0/ma;let g=vec2<i32>(i32(f.vGlyph.x+0.5),i32(f.vGlyph.y+0.5));let P=vec2<i32>(i32(f.vGlyph.z+0.5),i32(f.vGlyph.w+0.5));let Q=f.vBanding;let R=clamp(vec2<i32>(e*Q.xy+Q.zw),vec2<i32>(0,0),P);var i:f32=0.0;var h:f32=0.0;let U=textureLoad(u,vec2<i32>(g.x+R.y,g.y),0);let ea=i32(U.x+0.5);let ha=i32(U.y+0.5);let X=J(g,ha);for(var s:i32=0;s<ea;s=s+1){let Z=textureLoad(u,vec2<i32>(X.x+s,X.y),0);let F=vec2<i32>(i32(Z.x+0.5),i32(Z.y+0.5));let o=textureLoad(t,F,0)-vec4<f32>(e,e);let A=textureLoad(t,vec2<i32>(F.x+1,F.y),0).xy-e;if (max(max(o.x,o.z),A.x)*w.x<-0.5){break;}let z=I(o.y,o.w,A.y);if (z!=0){let x=ca(o,A)*w.x;if ((z&1)!=0){i=i+clamp(x.x+0.5,0.0,1.0);h=max(h,clamp(1.0-abs(x.x)*2.0,0.0,1.0));}if (z>1){i=i-clamp(x.y+0.5,0.0,1.0);h=max(h,clamp(1.0-abs(x.y)*2.0,0.0,1.0));}}}var k:f32=0.0;var j:f32=0.0;let W=textureLoad(u,vec2<i32>(g.x+P.y+1+R.x,g.y),0);let ia=i32(W.x+0.5);let ja=i32(W.y+0.5);let V=J(g,ja);for(var r:i32=0;r<ia;r=r+1){let M=textureLoad(u,vec2<i32>(V.x+r,V.y),0);let y=vec2<i32>(i32(M.x+0.5),i32(M.y+0.5));let m=textureLoad(t,y,0)-vec4<f32>(e,e);let B=textureLoad(t,vec2<i32>(y.x+1,y.y),0).xy-e;if (max(max(m.y,m.w),B.y)*w.y<-0.5){break;}let C=I(m.x,m.z,B.x);if (C!=0){let v=da(m,B)*w.y;if ((C&1)!=0){k=k-clamp(v.x+0.5,0.0,1.0);j=max(j,clamp(1.0-abs(v.x)*2.0,0.0,1.0));}if (C>1){k=k+clamp(v.y+0.5,0.0,1.0);j=max(j,clamp(1.0-abs(v.y)*2.0,0.0,1.0));}}}var p=max(abs(i*h+k*j)/max(h+j,1.0/65536.0),min(abs(i),abs(k)));p=clamp(p,0.0,1.0);p=pow(p,textU.color.x);return f.vColor*p;}";
6
6
 
7
7
  let _cache = null;
8
8
  function clearTextPipelineCache(engine) {
@@ -1 +1 @@
1
- {"version":3,"file":"text-pipeline.js","sources":["../../../../src/text/shaders/slug.vert.wgsl?raw","../../../../src/text/shaders/slug.frag.wgsl?raw","../../../../src/text/_gpu/text-pipeline.ts"],"sourcesContent":["export default \"// Slug GPU Font — Vertex Shader (WGSL, instanced layout).\\n// Direct port of Eric Lengyel's Slug dilation (see https://github.com/EricLengyel/Slug\\n// and Babylon.js-3/packages/dev/addons/src/msdfText/shadersWGSL/slug.vertex.fx).\\n// Per-vertex: corner sign (-1/+1 on each axis) — drives both the quad corner\\n// expansion and the dilation normal direction.\\n// Per-instance: bounds (em-space), anchor (object-space) + invScale, atlas locs, band transform.\\n\\nstruct TextU {\\n mvp: mat4x4<f32>,\\n viewport: vec4<f32>,\\n // Whole-draw opacity in .a (rgb unused, always 1). Per-glyph color is the slugColor attribute.\\n color: vec4<f32>,\\n};\\n@group(0) @binding(0) var<uniform> textU: TextU;\\n\\nstruct VIn {\\n @location(0) slugCorner: vec2<f32>,\\n @location(1) slugBounds: vec4<f32>,\\n @location(2) slugAnchor: vec4<f32>,\\n @location(3) slugAtlas: vec4<f32>,\\n @location(4) slugBand: vec4<f32>,\\n @location(5) slugColor: vec4<f32>,\\n};\\n\\nstruct VOut {\\n @builtin(position) pos: vec4<f32>,\\n @location(0) vTexcoord: vec2<f32>,\\n @location(1) @interpolate(flat) vBanding: vec4<f32>,\\n @location(2) @interpolate(flat) vGlyph: vec4<f32>,\\n @location(3) @interpolate(flat) vColor: vec4<f32>,\\n};\\n\\n@vertex\\nfn main(in: VIn) -> VOut {\\n // Dead-slot sentinel: slot allocator marks freed slots by setting slugAnchor.w = 1.0\\n // (live slots always have it as 0.0). Emit a clip-space point at -2 (outside the unit\\n // cube) so all 6 vertices of the quad collapse to the same off-screen position and the\\n // rasterizer culls the resulting zero-area triangles cheaply.\\n if (in.slugAnchor.w > 0.5) {\\n var dead: VOut;\\n dead.pos = vec4<f32>(-2.0, -2.0, -2.0, 1.0);\\n dead.vTexcoord = vec2<f32>(0.0, 0.0);\\n dead.vBanding = vec4<f32>(0.0);\\n dead.vGlyph = vec4<f32>(0.0);\\n dead.vColor = vec4<f32>(0.0);\\n return dead;\\n }\\n\\n // Reconstruct per-vertex data from the shared corner quad + per-instance fields.\\n // Reference shader had: pos (object-space xy), normal (dilation direction xy),\\n // tex (em-space xy), invScale, MVP matrix.\\n let isMax = vec2<f32>(step(0.0, in.slugCorner.x), step(0.0, in.slugCorner.y));\\n let tex = mix(in.slugBounds.xy, in.slugBounds.zw, isMax);\\n let invScale = in.slugAnchor.z;\\n let scale = select(0.0, 1.0 / invScale, invScale != 0.0);\\n let pos = in.slugAnchor.xy + tex * scale;\\n let normal = in.slugCorner;\\n let jac = vec4<f32>(invScale, 0.0, 0.0, invScale);\\n\\n let mvp = textU.mvp;\\n\\n // Extract MVP matrix rows from column-major storage.\\n let row0 = vec4<f32>(mvp[0].x, mvp[1].x, mvp[2].x, mvp[3].x);\\n let row1 = vec4<f32>(mvp[0].y, mvp[1].y, mvp[2].y, mvp[3].y);\\n let row3 = vec4<f32>(mvp[0].w, mvp[1].w, mvp[2].w, mvp[3].w);\\n\\n // Dynamic dilation (SlugDilate) — verbatim from the reference shader.\\n let n = normalize(normal);\\n let s = dot(row3.xy, pos) + row3.w;\\n let t_val = dot(row3.xy, n);\\n\\n let u = (s * dot(row0.xy, n) - t_val * (dot(row0.xy, pos) + row0.w)) * textU.viewport.x;\\n let v = (s * dot(row1.xy, n) - t_val * (dot(row1.xy, pos) + row1.w)) * textU.viewport.y;\\n\\n let s2 = s * s;\\n let st = s * t_val;\\n let uv = u * u + v * v;\\n let d = normal * (s2 * (st + sqrt(uv)) / (uv - st * st));\\n\\n let dilatedPos = pos + d;\\n let dilatedTex = vec2<f32>(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\\n\\n var out: VOut;\\n out.pos = mvp * vec4<f32>(dilatedPos, 0.0, 1.0);\\n out.vTexcoord = dilatedTex;\\n out.vBanding = in.slugBand;\\n out.vGlyph = in.slugAtlas;\\n // Color comes entirely from the per-glyph instance attribute; the uniform contributes\\n // only a whole-draw opacity multiply (textU.color is always (1,1,1,opacity)).\\n out.vColor = vec4<f32>(in.slugColor.rgb, in.slugColor.a * textU.color.a);\\n return out;\\n}\\n\"","export default \"// Slug GPU Font — Fragment Shader (WGSL). Per-pixel coverage from quadratic Bézier bands.\\n\\n@group(0) @binding(1) var curveTex: texture_2d<f32>;\\n@group(0) @binding(2) var bandTex: texture_2d<f32>;\\n\\nstruct FIn {\\n @location(0) vTexcoord: vec2<f32>,\\n @location(1) @interpolate(flat) vBanding: vec4<f32>,\\n @location(2) @interpolate(flat) vGlyph: vec4<f32>,\\n @location(3) @interpolate(flat) vColor: vec4<f32>,\\n @builtin(front_facing) frontFacing: bool,\\n};\\n\\nfn calcRootCode(y1: f32, y2: f32, y3: f32) -> i32 {\\n let i1: i32 = select(0, 1, y1 < 0.0);\\n let i2: i32 = select(0, 2, y2 < 0.0);\\n let i3: i32 = select(0, 4, y3 < 0.0);\\n let shift = u32(i1 + i2 + i3);\\n return (0x2E74 >> shift) & 0x0101;\\n}\\n\\nfn solveHorizPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\\n let a = vec2<f32>(p12.x - p12.z * 2.0 + p3.x, p12.y - p12.w * 2.0 + p3.y);\\n let b = vec2<f32>(p12.x - p12.z, p12.y - p12.w);\\n let ra = 1.0 / a.y;\\n let rb = 0.5 / b.y;\\n let disc = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\\n var t1 = (b.y - disc) * ra;\\n var t2 = (b.y + disc) * ra;\\n if (abs(a.y) <= max(abs(b.y), abs(p12.y)) * 1.0e-4) {\\n t1 = p12.y * rb;\\n t2 = p12.y * rb;\\n }\\n return vec2<f32>((a.x * t1 - b.x * 2.0) * t1 + p12.x, (a.x * t2 - b.x * 2.0) * t2 + p12.x);\\n}\\n\\nfn solveVertPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\\n let a = vec2<f32>(p12.x - p12.z * 2.0 + p3.x, p12.y - p12.w * 2.0 + p3.y);\\n let b = vec2<f32>(p12.x - p12.z, p12.y - p12.w);\\n let ra = 1.0 / a.x;\\n let rb = 0.5 / b.x;\\n let disc = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\\n var t1 = (b.x - disc) * ra;\\n var t2 = (b.x + disc) * ra;\\n if (abs(a.x) <= max(abs(b.x), abs(p12.x)) * 1.0e-4) {\\n t1 = p12.x * rb;\\n t2 = p12.x * rb;\\n }\\n return vec2<f32>((a.y * t1 - b.y * 2.0) * t1 + p12.y, (a.y * t2 - b.y * 2.0) * t2 + p12.y);\\n}\\n\\nfn calcBandLoc(glyphLoc: vec2<i32>, offset: i32) -> vec2<i32> {\\n var bandLoc = vec2<i32>(glyphLoc.x + offset, glyphLoc.y);\\n bandLoc.y = bandLoc.y + (bandLoc.x >> 12u);\\n bandLoc.x = bandLoc.x & 4095;\\n return bandLoc;\\n}\\n\\n@fragment\\nfn main(in: FIn) -> @location(0) vec4<f32> {\\n // Cull back-facing fragments — text quads behave like one-sided sheets of\\n // paper. Without this, the back side of a rotated quad shows geometrically\\n // correct mirror-image text. Callers wanting double-sided readable text\\n // add a second TextRenderable rotated 180° (or use the doubleSided helper).\\n if (!in.frontFacing) {\\n discard;\\n }\\n let renderCoord = in.vTexcoord;\\n let emsPerPixel = fwidth(renderCoord);\\n let pixelsPerEm = 1.0 / emsPerPixel;\\n\\n let glyphLoc = vec2<i32>(i32(in.vGlyph.x + 0.5), i32(in.vGlyph.y + 0.5));\\n let bandMax = vec2<i32>(i32(in.vGlyph.z + 0.5), i32(in.vGlyph.w + 0.5));\\n let bandTransform = in.vBanding;\\n\\n let bandIndex = clamp(\\n vec2<i32>(renderCoord * bandTransform.xy + bandTransform.zw),\\n vec2<i32>(0, 0),\\n bandMax\\n );\\n\\n var xcov: f32 = 0.0;\\n var xwgt: f32 = 0.0;\\n let hbandRaw = textureLoad(bandTex, vec2<i32>(glyphLoc.x + bandIndex.y, glyphLoc.y), 0);\\n let hbandCount = i32(hbandRaw.x + 0.5);\\n let hbandOffset = i32(hbandRaw.y + 0.5);\\n let hbandLoc = calcBandLoc(glyphLoc, hbandOffset);\\n for (var ci: i32 = 0; ci < hbandCount; ci = ci + 1) {\\n let locRaw = textureLoad(bandTex, vec2<i32>(hbandLoc.x + ci, hbandLoc.y), 0);\\n let curveLoc = vec2<i32>(i32(locRaw.x + 0.5), i32(locRaw.y + 0.5));\\n let p12 = textureLoad(curveTex, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\\n let p3 = textureLoad(curveTex, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) { break; }\\n let code = calcRootCode(p12.y, p12.w, p3.y);\\n if (code != 0) {\\n let r = solveHorizPoly(p12, p3) * pixelsPerEm.x;\\n if ((code & 1) != 0) {\\n xcov = xcov + clamp(r.x + 0.5, 0.0, 1.0);\\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\\n }\\n if (code > 1) {\\n xcov = xcov - clamp(r.y + 0.5, 0.0, 1.0);\\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\\n }\\n }\\n }\\n\\n var ycov: f32 = 0.0;\\n var ywgt: f32 = 0.0;\\n let vbandRaw = textureLoad(bandTex, vec2<i32>(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0);\\n let vbandCount = i32(vbandRaw.x + 0.5);\\n let vbandOffset = i32(vbandRaw.y + 0.5);\\n let vbandLoc = calcBandLoc(glyphLoc, vbandOffset);\\n for (var ci: i32 = 0; ci < vbandCount; ci = ci + 1) {\\n let locRaw = textureLoad(bandTex, vec2<i32>(vbandLoc.x + ci, vbandLoc.y), 0);\\n let curveLoc = vec2<i32>(i32(locRaw.x + 0.5), i32(locRaw.y + 0.5));\\n let p12 = textureLoad(curveTex, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\\n let p3 = textureLoad(curveTex, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) { break; }\\n let code = calcRootCode(p12.x, p12.z, p3.x);\\n if (code != 0) {\\n let r = solveVertPoly(p12, p3) * pixelsPerEm.y;\\n if ((code & 1) != 0) {\\n ycov = ycov - clamp(r.x + 0.5, 0.0, 1.0);\\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\\n }\\n if (code > 1) {\\n ycov = ycov + clamp(r.y + 0.5, 0.0, 1.0);\\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\\n }\\n }\\n }\\n\\n var coverage = max(\\n abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0),\\n min(abs(xcov), abs(ycov))\\n );\\n coverage = clamp(coverage, 0.0, 1.0);\\n return in.vColor * coverage;\\n}\\n\"","/** Owns the text render pipeline + bind-group layouts. Lazy per-device cache. */\n\nimport type { EngineContext } from \"../../engine/engine.js\";\nimport vertSrc from \"../shaders/slug.vert.wgsl?raw\";\nimport fragSrc from \"../shaders/slug.frag.wgsl?raw\";\nimport { TEXT_INSTANCE_BYTES } from \"../text-data.js\";\n\nexport interface TextPipelineDeviceCache {\n bindGroupLayout: GPUBindGroupLayout;\n vertModule: GPUShaderModule;\n fragModule: GPUShaderModule;\n quadVertexBuffer: GPUBuffer;\n pipelines: Map<string, GPURenderPipeline>;\n}\n\nlet _cache: WeakMap<GPUDevice, TextPipelineDeviceCache> | null = null;\n\n/** Clear the text pipeline cache for a device, releasing cache-held refs. */\nexport function clearTextPipelineCache(engine: EngineContext): void {\n _cache?.delete(engine._device);\n}\n\n/** Shared 4-vertex unit quad: corner signs (-1,-1), (1,-1), (1,1), (-1,1). */\nconst QUAD_CORNERS = [-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1] as const;\n\nfunction getOrCreateDeviceCache(engine: EngineContext): TextPipelineDeviceCache {\n _cache ??= new WeakMap();\n let cache = _cache.get(engine._device);\n if (cache) {\n return cache;\n }\n const device = engine._device;\n const bindGroupLayout = device.createBindGroupLayout({\n label: \"text-bind-group-layout\",\n entries: [\n { binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: \"unfilterable-float\" } },\n ],\n });\n const vertModule = device.createShaderModule({ label: \"text-vert\", code: vertSrc });\n const fragModule = device.createShaderModule({ label: \"text-frag\", code: fragSrc });\n const corners = new Float32Array(QUAD_CORNERS);\n const quadVertexBuffer = device.createBuffer({\n label: \"text-quad-corners\",\n size: corners.byteLength,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n mappedAtCreation: true,\n });\n new Float32Array(quadVertexBuffer.getMappedRange()).set(corners);\n quadVertexBuffer.unmap();\n\n cache = { bindGroupLayout, vertModule, fragModule, quadVertexBuffer, pipelines: new Map() };\n _cache.set(device, cache);\n return cache;\n}\n\nfunction pipelineKey(format: GPUTextureFormat, sampleCount: number, depthStencilFormat: GPUTextureFormat | null, depthWrite: boolean): string {\n return format + \":\" + sampleCount + \":\" + (depthStencilFormat ?? \"-\") + \":\" + (depthWrite ? \"w\" : \"r\");\n}\n\nexport function getOrCreateTextPipeline(\n engine: EngineContext,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n depthStencilFormat: GPUTextureFormat | null,\n depthWrite: boolean\n): { pipeline: GPURenderPipeline; cache: TextPipelineDeviceCache } {\n const cache = getOrCreateDeviceCache(engine);\n const key = pipelineKey(format, sampleCount, depthStencilFormat, depthWrite);\n let pipeline = cache.pipelines.get(key);\n if (pipeline) {\n return { pipeline, cache };\n }\n const device = engine._device;\n const descriptor: GPURenderPipelineDescriptor = {\n label: \"text-pipeline\",\n layout: device.createPipelineLayout({ bindGroupLayouts: [cache.bindGroupLayout] }),\n vertex: {\n module: cache.vertModule,\n entryPoint: \"main\",\n buffers: [\n {\n arrayStride: 8,\n stepMode: \"vertex\",\n attributes: [{ shaderLocation: 0, offset: 0, format: \"float32x2\" }],\n },\n {\n arrayStride: TEXT_INSTANCE_BYTES,\n stepMode: \"instance\",\n attributes: [\n { shaderLocation: 1, offset: 0, format: \"float32x4\" },\n { shaderLocation: 2, offset: 16, format: \"float32x4\" },\n { shaderLocation: 3, offset: 32, format: \"float32x4\" },\n { shaderLocation: 4, offset: 48, format: \"float32x4\" },\n { shaderLocation: 5, offset: 64, format: \"float32x4\" },\n ],\n },\n ],\n },\n fragment: {\n module: cache.fragModule,\n entryPoint: \"main\",\n targets: [\n {\n format,\n blend: {\n // Premultiplied-alpha blend. The Slug fragment shader outputs\n // `vColor * coverage` — i.e. RGB already premultiplied by coverage\n // (and alpha = coverage) — so the color blend must use srcFactor\n // `one` (NOT `src-alpha`, which would multiply by coverage a second\n // time and render anti-aliased edges as coverage², too dark). The\n // alpha channel already uses `one` for the same reason.\n color: { srcFactor: \"one\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\n alpha: { srcFactor: \"one\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\n },\n },\n ],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\", frontFace: \"ccw\" },\n multisample: { count: sampleCount },\n };\n if (depthStencilFormat) {\n descriptor.depthStencil = {\n format: depthStencilFormat,\n depthCompare: \"greater-equal\",\n depthWriteEnabled: depthWrite,\n };\n }\n pipeline = device.createRenderPipeline(descriptor);\n cache.pipelines.set(key, pipeline);\n return { pipeline, cache };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"text-pipeline.js","sources":["../../../../src/text/shaders/slug.vert.wgsl?raw","../../../../src/text/shaders/slug.frag.wgsl?raw","../../../../src/text/_gpu/text-pipeline.ts"],"sourcesContent":["export default \"// Slug GPU Font — Vertex Shader (WGSL, instanced layout).\\n// Direct port of Eric Lengyel's Slug dilation (see https://github.com/EricLengyel/Slug\\n// and Babylon.js-3/packages/dev/addons/src/msdfText/shadersWGSL/slug.vertex.fx).\\n// Per-vertex: corner sign (-1/+1 on each axis) — drives both the quad corner\\n// expansion and the dilation normal direction.\\n// Per-instance: bounds (em-space), anchor (object-space) + invScale, atlas locs, band transform.\\n\\nstruct TextU {\\n mvp: mat4x4<f32>,\\n viewport: vec4<f32>,\\n // Whole-draw opacity in .a (rgb unused, always 1). Per-glyph color is the slugColor attribute.\\n color: vec4<f32>,\\n};\\n@group(0) @binding(0) var<uniform> textU: TextU;\\n\\nstruct VIn {\\n @location(0) slugCorner: vec2<f32>,\\n @location(1) slugBounds: vec4<f32>,\\n @location(2) slugAnchor: vec4<f32>,\\n @location(3) slugAtlas: vec4<f32>,\\n @location(4) slugBand: vec4<f32>,\\n @location(5) slugColor: vec4<f32>,\\n};\\n\\nstruct VOut {\\n @builtin(position) pos: vec4<f32>,\\n @location(0) vTexcoord: vec2<f32>,\\n @location(1) @interpolate(flat) vBanding: vec4<f32>,\\n @location(2) @interpolate(flat) vGlyph: vec4<f32>,\\n @location(3) @interpolate(flat) vColor: vec4<f32>,\\n};\\n\\n@vertex\\nfn main(in: VIn) -> VOut {\\n // Dead-slot sentinel: slot allocator marks freed slots by setting slugAnchor.w = 1.0\\n // (live slots always have it as 0.0). Emit a clip-space point at -2 (outside the unit\\n // cube) so all 6 vertices of the quad collapse to the same off-screen position and the\\n // rasterizer culls the resulting zero-area triangles cheaply.\\n if (in.slugAnchor.w > 0.5) {\\n var dead: VOut;\\n dead.pos = vec4<f32>(-2.0, -2.0, -2.0, 1.0);\\n dead.vTexcoord = vec2<f32>(0.0, 0.0);\\n dead.vBanding = vec4<f32>(0.0);\\n dead.vGlyph = vec4<f32>(0.0);\\n dead.vColor = vec4<f32>(0.0);\\n return dead;\\n }\\n\\n // Reconstruct per-vertex data from the shared corner quad + per-instance fields.\\n // Reference shader had: pos (object-space xy), normal (dilation direction xy),\\n // tex (em-space xy), invScale, MVP matrix.\\n let isMax = vec2<f32>(step(0.0, in.slugCorner.x), step(0.0, in.slugCorner.y));\\n let tex = mix(in.slugBounds.xy, in.slugBounds.zw, isMax);\\n let invScale = in.slugAnchor.z;\\n let scale = select(0.0, 1.0 / invScale, invScale != 0.0);\\n let pos = in.slugAnchor.xy + tex * scale;\\n let normal = in.slugCorner;\\n let jac = vec4<f32>(invScale, 0.0, 0.0, invScale);\\n\\n let mvp = textU.mvp;\\n\\n // Extract MVP matrix rows from column-major storage.\\n let row0 = vec4<f32>(mvp[0].x, mvp[1].x, mvp[2].x, mvp[3].x);\\n let row1 = vec4<f32>(mvp[0].y, mvp[1].y, mvp[2].y, mvp[3].y);\\n let row3 = vec4<f32>(mvp[0].w, mvp[1].w, mvp[2].w, mvp[3].w);\\n\\n // Dynamic dilation (SlugDilate) — verbatim from the reference shader.\\n let n = normalize(normal);\\n let s = dot(row3.xy, pos) + row3.w;\\n let t_val = dot(row3.xy, n);\\n\\n let u = (s * dot(row0.xy, n) - t_val * (dot(row0.xy, pos) + row0.w)) * textU.viewport.x;\\n let v = (s * dot(row1.xy, n) - t_val * (dot(row1.xy, pos) + row1.w)) * textU.viewport.y;\\n\\n let s2 = s * s;\\n let st = s * t_val;\\n let uv = u * u + v * v;\\n let d = normal * (s2 * (st + sqrt(uv)) / (uv - st * st));\\n\\n let dilatedPos = pos + d;\\n let dilatedTex = vec2<f32>(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\\n\\n var out: VOut;\\n out.pos = mvp * vec4<f32>(dilatedPos, 0.0, 1.0);\\n out.vTexcoord = dilatedTex;\\n out.vBanding = in.slugBand;\\n out.vGlyph = in.slugAtlas;\\n // Color comes entirely from the per-glyph instance attribute; the uniform contributes\\n // only a whole-draw opacity multiply (textU.color is always (1,1,1,opacity)).\\n out.vColor = vec4<f32>(in.slugColor.rgb, in.slugColor.a * textU.color.a);\\n return out;\\n}\\n\"","export default \"// Slug GPU Font — Fragment Shader (WGSL). Per-pixel coverage from quadratic Bézier bands.\\n\\n@group(0) @binding(1) var curveTex: texture_2d<f32>;\\n@group(0) @binding(2) var bandTex: texture_2d<f32>;\\n\\nstruct TextU {\\n mvp: mat4x4<f32>,\\n viewport: vec4<f32>,\\n // .a = whole-draw opacity. .x = coverage-gamma reciprocal (1/coverageGamma) applied to\\n // anti-aliased edge coverage below. .yz unused.\\n color: vec4<f32>,\\n};\\n@group(0) @binding(0) var<uniform> textU: TextU;\\n\\nstruct FIn {\\n @location(0) vTexcoord: vec2<f32>,\\n @location(1) @interpolate(flat) vBanding: vec4<f32>,\\n @location(2) @interpolate(flat) vGlyph: vec4<f32>,\\n @location(3) @interpolate(flat) vColor: vec4<f32>,\\n @builtin(front_facing) frontFacing: bool,\\n};\\n\\nfn calcRootCode(y1: f32, y2: f32, y3: f32) -> i32 {\\n let i1: i32 = select(0, 1, y1 < 0.0);\\n let i2: i32 = select(0, 2, y2 < 0.0);\\n let i3: i32 = select(0, 4, y3 < 0.0);\\n let shift = u32(i1 + i2 + i3);\\n return (0x2E74 >> shift) & 0x0101;\\n}\\n\\nfn solveHorizPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\\n let a = vec2<f32>(p12.x - p12.z * 2.0 + p3.x, p12.y - p12.w * 2.0 + p3.y);\\n let b = vec2<f32>(p12.x - p12.z, p12.y - p12.w);\\n let ra = 1.0 / a.y;\\n let rb = 0.5 / b.y;\\n let disc = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\\n var t1 = (b.y - disc) * ra;\\n var t2 = (b.y + disc) * ra;\\n if (abs(a.y) <= max(abs(b.y), abs(p12.y)) * 1.0e-4) {\\n t1 = p12.y * rb;\\n t2 = p12.y * rb;\\n }\\n return vec2<f32>((a.x * t1 - b.x * 2.0) * t1 + p12.x, (a.x * t2 - b.x * 2.0) * t2 + p12.x);\\n}\\n\\nfn solveVertPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\\n let a = vec2<f32>(p12.x - p12.z * 2.0 + p3.x, p12.y - p12.w * 2.0 + p3.y);\\n let b = vec2<f32>(p12.x - p12.z, p12.y - p12.w);\\n let ra = 1.0 / a.x;\\n let rb = 0.5 / b.x;\\n let disc = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\\n var t1 = (b.x - disc) * ra;\\n var t2 = (b.x + disc) * ra;\\n if (abs(a.x) <= max(abs(b.x), abs(p12.x)) * 1.0e-4) {\\n t1 = p12.x * rb;\\n t2 = p12.x * rb;\\n }\\n return vec2<f32>((a.y * t1 - b.y * 2.0) * t1 + p12.y, (a.y * t2 - b.y * 2.0) * t2 + p12.y);\\n}\\n\\nfn calcBandLoc(glyphLoc: vec2<i32>, offset: i32) -> vec2<i32> {\\n var bandLoc = vec2<i32>(glyphLoc.x + offset, glyphLoc.y);\\n bandLoc.y = bandLoc.y + (bandLoc.x >> 12u);\\n bandLoc.x = bandLoc.x & 4095;\\n return bandLoc;\\n}\\n\\n@fragment\\nfn main(in: FIn) -> @location(0) vec4<f32> {\\n // Cull back-facing fragments — text quads behave like one-sided sheets of\\n // paper. Without this, the back side of a rotated quad shows geometrically\\n // correct mirror-image text. Callers wanting double-sided readable text\\n // add a second TextRenderable rotated 180° (or use the doubleSided helper).\\n if (!in.frontFacing) {\\n discard;\\n }\\n let renderCoord = in.vTexcoord;\\n let emsPerPixel = fwidth(renderCoord);\\n let pixelsPerEm = 1.0 / emsPerPixel;\\n\\n let glyphLoc = vec2<i32>(i32(in.vGlyph.x + 0.5), i32(in.vGlyph.y + 0.5));\\n let bandMax = vec2<i32>(i32(in.vGlyph.z + 0.5), i32(in.vGlyph.w + 0.5));\\n let bandTransform = in.vBanding;\\n\\n let bandIndex = clamp(\\n vec2<i32>(renderCoord * bandTransform.xy + bandTransform.zw),\\n vec2<i32>(0, 0),\\n bandMax\\n );\\n\\n var xcov: f32 = 0.0;\\n var xwgt: f32 = 0.0;\\n let hbandRaw = textureLoad(bandTex, vec2<i32>(glyphLoc.x + bandIndex.y, glyphLoc.y), 0);\\n let hbandCount = i32(hbandRaw.x + 0.5);\\n let hbandOffset = i32(hbandRaw.y + 0.5);\\n let hbandLoc = calcBandLoc(glyphLoc, hbandOffset);\\n for (var ci: i32 = 0; ci < hbandCount; ci = ci + 1) {\\n let locRaw = textureLoad(bandTex, vec2<i32>(hbandLoc.x + ci, hbandLoc.y), 0);\\n let curveLoc = vec2<i32>(i32(locRaw.x + 0.5), i32(locRaw.y + 0.5));\\n let p12 = textureLoad(curveTex, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\\n let p3 = textureLoad(curveTex, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) { break; }\\n let code = calcRootCode(p12.y, p12.w, p3.y);\\n if (code != 0) {\\n let r = solveHorizPoly(p12, p3) * pixelsPerEm.x;\\n if ((code & 1) != 0) {\\n xcov = xcov + clamp(r.x + 0.5, 0.0, 1.0);\\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\\n }\\n if (code > 1) {\\n xcov = xcov - clamp(r.y + 0.5, 0.0, 1.0);\\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\\n }\\n }\\n }\\n\\n var ycov: f32 = 0.0;\\n var ywgt: f32 = 0.0;\\n let vbandRaw = textureLoad(bandTex, vec2<i32>(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0);\\n let vbandCount = i32(vbandRaw.x + 0.5);\\n let vbandOffset = i32(vbandRaw.y + 0.5);\\n let vbandLoc = calcBandLoc(glyphLoc, vbandOffset);\\n for (var ci: i32 = 0; ci < vbandCount; ci = ci + 1) {\\n let locRaw = textureLoad(bandTex, vec2<i32>(vbandLoc.x + ci, vbandLoc.y), 0);\\n let curveLoc = vec2<i32>(i32(locRaw.x + 0.5), i32(locRaw.y + 0.5));\\n let p12 = textureLoad(curveTex, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\\n let p3 = textureLoad(curveTex, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) { break; }\\n let code = calcRootCode(p12.x, p12.z, p3.x);\\n if (code != 0) {\\n let r = solveVertPoly(p12, p3) * pixelsPerEm.y;\\n if ((code & 1) != 0) {\\n ycov = ycov - clamp(r.x + 0.5, 0.0, 1.0);\\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\\n }\\n if (code > 1) {\\n ycov = ycov + clamp(r.y + 0.5, 0.0, 1.0);\\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\\n }\\n }\\n }\\n\\n var coverage = max(\\n abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0),\\n min(abs(xcov), abs(ycov))\\n );\\n coverage = clamp(coverage, 0.0, 1.0);\\n // Coverage gamma: raise edge coverage to 1/coverageGamma so anti-aliased edges composite\\n // heavier (mimics gamma-space stem darkening). textU.color.x is 1 by default (no-op).\\n coverage = pow(coverage, textU.color.x);\\n return in.vColor * coverage;\\n}\\n\"","/** Owns the text render pipeline + bind-group layouts. Lazy per-device cache. */\n\nimport type { EngineContext } from \"../../engine/engine.js\";\nimport vertSrc from \"../shaders/slug.vert.wgsl?raw\";\nimport fragSrc from \"../shaders/slug.frag.wgsl?raw\";\nimport { TEXT_INSTANCE_BYTES } from \"../text-data.js\";\n\nexport interface TextPipelineDeviceCache {\n bindGroupLayout: GPUBindGroupLayout;\n vertModule: GPUShaderModule;\n fragModule: GPUShaderModule;\n quadVertexBuffer: GPUBuffer;\n pipelines: Map<string, GPURenderPipeline>;\n}\n\nlet _cache: WeakMap<GPUDevice, TextPipelineDeviceCache> | null = null;\n\n/** Clear the text pipeline cache for a device, releasing cache-held refs. */\nexport function clearTextPipelineCache(engine: EngineContext): void {\n _cache?.delete(engine._device);\n}\n\n/** Shared 4-vertex unit quad: corner signs (-1,-1), (1,-1), (1,1), (-1,1). */\nconst QUAD_CORNERS = [-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1] as const;\n\nfunction getOrCreateDeviceCache(engine: EngineContext): TextPipelineDeviceCache {\n _cache ??= new WeakMap();\n let cache = _cache.get(engine._device);\n if (cache) {\n return cache;\n }\n const device = engine._device;\n const bindGroupLayout = device.createBindGroupLayout({\n label: \"text-bind-group-layout\",\n entries: [\n { binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: \"uniform\" } },\n { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: \"unfilterable-float\" } },\n { binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: \"unfilterable-float\" } },\n ],\n });\n const vertModule = device.createShaderModule({ label: \"text-vert\", code: vertSrc });\n const fragModule = device.createShaderModule({ label: \"text-frag\", code: fragSrc });\n const corners = new Float32Array(QUAD_CORNERS);\n const quadVertexBuffer = device.createBuffer({\n label: \"text-quad-corners\",\n size: corners.byteLength,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n mappedAtCreation: true,\n });\n new Float32Array(quadVertexBuffer.getMappedRange()).set(corners);\n quadVertexBuffer.unmap();\n\n cache = { bindGroupLayout, vertModule, fragModule, quadVertexBuffer, pipelines: new Map() };\n _cache.set(device, cache);\n return cache;\n}\n\nfunction pipelineKey(format: GPUTextureFormat, sampleCount: number, depthStencilFormat: GPUTextureFormat | null, depthWrite: boolean): string {\n return format + \":\" + sampleCount + \":\" + (depthStencilFormat ?? \"-\") + \":\" + (depthWrite ? \"w\" : \"r\");\n}\n\nexport function getOrCreateTextPipeline(\n engine: EngineContext,\n format: GPUTextureFormat,\n sampleCount: 1 | 4,\n depthStencilFormat: GPUTextureFormat | null,\n depthWrite: boolean\n): { pipeline: GPURenderPipeline; cache: TextPipelineDeviceCache } {\n const cache = getOrCreateDeviceCache(engine);\n const key = pipelineKey(format, sampleCount, depthStencilFormat, depthWrite);\n let pipeline = cache.pipelines.get(key);\n if (pipeline) {\n return { pipeline, cache };\n }\n const device = engine._device;\n const descriptor: GPURenderPipelineDescriptor = {\n label: \"text-pipeline\",\n layout: device.createPipelineLayout({ bindGroupLayouts: [cache.bindGroupLayout] }),\n vertex: {\n module: cache.vertModule,\n entryPoint: \"main\",\n buffers: [\n {\n arrayStride: 8,\n stepMode: \"vertex\",\n attributes: [{ shaderLocation: 0, offset: 0, format: \"float32x2\" }],\n },\n {\n arrayStride: TEXT_INSTANCE_BYTES,\n stepMode: \"instance\",\n attributes: [\n { shaderLocation: 1, offset: 0, format: \"float32x4\" },\n { shaderLocation: 2, offset: 16, format: \"float32x4\" },\n { shaderLocation: 3, offset: 32, format: \"float32x4\" },\n { shaderLocation: 4, offset: 48, format: \"float32x4\" },\n { shaderLocation: 5, offset: 64, format: \"float32x4\" },\n ],\n },\n ],\n },\n fragment: {\n module: cache.fragModule,\n entryPoint: \"main\",\n targets: [\n {\n format,\n blend: {\n // Premultiplied-alpha blend. The Slug fragment shader outputs\n // `vColor * coverage` — i.e. RGB already premultiplied by coverage\n // (and alpha = coverage) — so the color blend must use srcFactor\n // `one` (NOT `src-alpha`, which would multiply by coverage a second\n // time and render anti-aliased edges as coverage², too dark). The\n // alpha channel already uses `one` for the same reason.\n color: { srcFactor: \"one\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\n alpha: { srcFactor: \"one\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\n },\n },\n ],\n },\n primitive: { topology: \"triangle-list\", cullMode: \"none\", frontFace: \"ccw\" },\n multisample: { count: sampleCount },\n };\n if (depthStencilFormat) {\n descriptor.depthStencil = {\n format: depthStencilFormat,\n depthCompare: \"greater-equal\",\n depthWriteEnabled: depthWrite,\n };\n }\n pipeline = device.createRenderPipeline(descriptor);\n cache.pipelines.set(key, pipeline);\n return { pipeline, cache };\n}\n"],"names":[],"mappings":"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"}
@@ -13,6 +13,7 @@ function createTextLayer(data, options) {
13
13
  scale: options?.scale ?? 1,
14
14
  order: options?.order ?? 0,
15
15
  opacity: options?.opacity ?? 1,
16
+ coverageGamma: options?.coverageGamma ?? 1,
16
17
  visible: options?.visible ?? true,
17
18
  _version: 0
18
19
  };
@@ -152,7 +153,8 @@ function uploadLayer(rr, lg, bindGroupLayout) {
152
153
  lg.uploadedViewportW = W;
153
154
  lg.uploadedViewportH = H;
154
155
  }
155
- const col = new Float32Array([1, 1, 1, layer.opacity]);
156
+ const gammaInv = layer.coverageGamma > 0 ? 1 / layer.coverageGamma : 1;
157
+ const col = new Float32Array([gammaInv, 1, 1, layer.opacity]);
156
158
  device.queue.writeBuffer(lg.textU, 80, col.buffer, col.byteOffset, 16);
157
159
  }
158
160
  function disposeLayerGpu(lg) {
@@ -1 +1 @@
1
- {"version":3,"file":"text-renderer.js","sources":["../../../src/text/text-renderer.ts"],"sourcesContent":["/** TextRenderer — a standalone `RenderingContext` that draws one or more\n * `TextLayer`s directly to the swapchain. Sibling of `SpriteRenderer`:\n * owns its own render pass, no scene / camera dependency.\n *\n * `TextLayer` is the 2D pixel-space placement record bound to a single TextData; it\n * lives here because it is consumed exclusively by `TextRenderer`. */\n\nimport { registerRenderingContext, unregisterRenderingContext } from \"../engine/engine.js\";\nimport type { RenderingContext } from \"../engine/engine.js\";\nimport type { SurfaceContext } from \"../engine/surface.js\";\nimport { createEmptyUniformBuffer } from \"../resource/gpu-buffers.js\";\nimport type { TextData } from \"./text-data.js\";\nimport { TEXT_INSTANCE_BYTES } from \"./text-data.js\";\nimport { ensureSharedAtlasGpu } from \"./_gpu/text-textures.js\";\nimport { getOrCreateTextPipeline } from \"./_gpu/text-pipeline.js\";\n\n// ─── TextLayer ────────────────────────────────────────────────────────────\n\n/** Initial placement and compositing options for a 2D text layer in a standalone text renderer. */\nexport interface TextLayerOptions {\n /** Top-left origin (in canvas pixels) for the layer's local coordinate frame. Default (0, 0). */\n readonly positionPx?: { readonly x: number; readonly y: number };\n /** Z-axis rotation about `positionPx`, in radians. Default 0. */\n readonly rotationRad?: number;\n /** Uniform scale applied to the laid-out text. Default 1. */\n readonly scale?: number;\n /** Sort order within a renderer (lower draws first). Default 0. */\n readonly order?: number;\n /** Alpha multiplier in [0, 1]. Default 1. */\n readonly opacity?: number;\n /** Default true. */\n readonly visible?: boolean;\n}\n\n/** Pure-data 2D text layer. Mutate fields directly between frames. */\nexport interface TextLayer {\n /** @internal */\n readonly _kind: \"text-layer\";\n readonly data: TextData;\n positionPx: { x: number; y: number };\n rotationRad: number;\n scale: number;\n order: number;\n opacity: number;\n visible: boolean;\n /** @internal Monotonic version bumped by helpers that mutate placement. */\n _version: number;\n}\n\n/** Create a 2D text layer that places a `TextData` block in canvas pixel space.\n *\n * @param data - Text data block drawn by the layer.\n * @param options - Optional pixel placement, scale, order, opacity, and visibility.\n * @returns A mutable layer object for use with a `TextRenderer`. */\nexport function createTextLayer(data: TextData, options?: TextLayerOptions): TextLayer {\n return {\n _kind: \"text-layer\",\n data,\n positionPx: { x: options?.positionPx?.x ?? 0, y: options?.positionPx?.y ?? 0 },\n rotationRad: options?.rotationRad ?? 0,\n scale: options?.scale ?? 1,\n order: options?.order ?? 0,\n opacity: options?.opacity ?? 1,\n visible: options?.visible ?? true,\n _version: 0,\n };\n}\n\n/** Update the layer's pixel position. Convenience wrapper. */\nexport function setTextLayerPosition(layer: TextLayer, x: number, y: number): void {\n layer.positionPx.x = x;\n layer.positionPx.y = y;\n layer._version++;\n}\n\n// ─── TextRenderer ─────────────────────────────────────────────────────────\n\nconst KIND = \"text-renderer\" as const;\n\n/** UBO: mat4 mvp (64B) + viewport vec4 (16B) + color vec4 (16B). */\nconst TEXT_UBO_BYTES = 96;\n\n/** Options for creating a standalone text renderer that draws directly to a surface. */\nexport interface TextRendererOptions {\n layers: readonly TextLayer[];\n /** Default true. Set false for HUD overlays so the text pass preserves existing scene color. */\n clear?: boolean;\n /** Default `{ r: 0, g: 0, b: 0, a: 1 }`. */\n clearValue?: GPUColorDict;\n}\n\n/** Standalone rendering context that draws sorted 2D text layers directly to the swapchain. */\nexport interface TextRenderer extends RenderingContext {\n /** @internal */\n readonly _kind: typeof KIND;\n readonly layers: readonly TextLayer[];\n /** @internal Mutable alias of {@link layers} (same array reference). */\n _layers: TextLayer[];\n /** @internal */ readonly _surface: SurfaceContext;\n /** @internal Per-layer GPU resources, keyed by layer. */\n _layerGpu: Map<TextLayer, LayerGpu>;\n /** @internal */ _targetWidth: number;\n /** @internal */ _targetHeight: number;\n /** @internal */ _disposed: boolean;\n /** @internal */ _clear: boolean;\n}\n\n/** @internal Per-layer GPU resources owned by the renderer. */\ninterface LayerGpu {\n layer: TextLayer;\n textU: GPUBuffer;\n instanceBuf: GPUBuffer;\n instanceCap: number;\n pipeline: GPURenderPipeline | null;\n /** Per-draw-group bind groups; rebuilt when atlas grows. */\n bindGroups: GPUBindGroup[];\n bindGroupAtlasVersions: number[];\n uploadedDataVersion: number;\n uploadedViewportW: number;\n uploadedViewportH: number;\n /** Snapshot of (posX, posY, rot, scale, W, H) to skip mvp upload when unchanged. */\n lastMvpInputs: Float32Array;\n mvpUploaded: boolean;\n}\n\nconst _mvpScratch = new Float32Array(16);\n\nfunction buildLayerMvp(layer: TextLayer, targetW: number, targetH: number, out: Float32Array): void {\n const s = layer.scale;\n const r = layer.rotationRad;\n const cr = Math.cos(r);\n const sr = Math.sin(r);\n const px = layer.positionPx.x;\n const py = layer.positionPx.y;\n // Map glyph-local (font Y-up) coords through (scale, flip-Y) → rotate → translate → ortho(W,H, Y-down).\n // Equivalent compact affine — see plan note for derivation. Column-major.\n const cx = (2 * s) / targetW;\n const cy = (2 * s) / targetH;\n out.fill(0);\n out[0] = cx * cr; // col 0, row 0\n out[1] = -cy * sr; // col 0, row 1\n out[4] = cx * sr; // col 1, row 0\n out[5] = cy * cr; // col 1, row 1\n out[10] = 1; // depth pass-through (we don't write depth)\n out[12] = (2 * px) / targetW - 1;\n out[13] = 1 - (2 * py) / targetH;\n out[15] = 1;\n}\n\nfunction ensureLayerGpu(rr: TextRenderer, layer: TextLayer): LayerGpu {\n let lg = rr._layerGpu.get(layer);\n if (lg) {\n return lg;\n }\n const engine = rr._surface.engine;\n const device = engine._device;\n const cap = Math.max(layer.data._instanceCount, 8);\n lg = {\n layer,\n textU: createEmptyUniformBuffer(engine, TEXT_UBO_BYTES, \"text-layer-ubo\"),\n instanceBuf: device.createBuffer({\n label: \"text-layer-instances\",\n size: cap * TEXT_INSTANCE_BYTES,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n }),\n instanceCap: cap,\n pipeline: null,\n bindGroups: [],\n bindGroupAtlasVersions: [],\n uploadedDataVersion: -1,\n uploadedViewportW: 0,\n uploadedViewportH: 0,\n lastMvpInputs: new Float32Array(6),\n mvpUploaded: false,\n };\n rr._layerGpu.set(layer, lg);\n return lg;\n}\n\nfunction ensureInstanceCapacity(device: GPUDevice, lg: LayerGpu, needed: number): void {\n if (needed <= lg.instanceCap) {\n return;\n }\n let cap = lg.instanceCap;\n while (cap < needed) {\n cap *= 2;\n }\n lg.instanceBuf.destroy();\n lg.instanceBuf = device.createBuffer({\n label: \"text-layer-instances\",\n size: cap * TEXT_INSTANCE_BYTES,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n });\n lg.instanceCap = cap;\n lg.uploadedDataVersion = -1;\n}\n\nfunction uploadLayer(rr: TextRenderer, lg: LayerGpu, bindGroupLayout: GPUBindGroupLayout): void {\n const device = rr._surface.engine._device;\n const layer = lg.layer;\n const data = layer.data;\n\n // Atlas + bind groups per draw group.\n for (let i = 0; i < data._groups.length; i++) {\n const g = data._groups[i]!;\n const { rebuilt, gpu: atlasGpu } = ensureSharedAtlasGpu(device, g.curveSet.atlas);\n const current = lg.bindGroups[i];\n const currentVer = lg.bindGroupAtlasVersions[i] ?? -1;\n if (!current || rebuilt || currentVer !== atlasGpu.uploadedVersion) {\n lg.bindGroups[i] = device.createBindGroup({\n label: \"text-renderer-bg0-\" + g.curveSetId,\n layout: bindGroupLayout,\n entries: [\n { binding: 0, resource: { buffer: lg.textU } },\n { binding: 1, resource: atlasGpu.curveTex.createView() },\n { binding: 2, resource: atlasGpu.bandTex.createView() },\n ],\n });\n lg.bindGroupAtlasVersions[i] = atlasGpu.uploadedVersion;\n }\n }\n if (lg.bindGroups.length > data._groups.length) {\n lg.bindGroups.length = data._groups.length;\n lg.bindGroupAtlasVersions.length = data._groups.length;\n }\n\n // Instance buffer.\n ensureInstanceCapacity(device, lg, data._instanceCount);\n if (lg.uploadedDataVersion !== data._version && data._instanceCount > 0) {\n const dirtyValid = lg.uploadedDataVersion !== -1 && data._dirtyEnd > data._dirtyStart;\n if (dirtyValid) {\n const startFloats = data._dirtyStart * (TEXT_INSTANCE_BYTES / 4);\n const endFloats = data._dirtyEnd * (TEXT_INSTANCE_BYTES / 4);\n const view = data._instances.subarray(startFloats, endFloats);\n device.queue.writeBuffer(lg.instanceBuf, data._dirtyStart * TEXT_INSTANCE_BYTES, view.buffer as ArrayBuffer, view.byteOffset, view.byteLength);\n } else {\n const view = data._instances.subarray(0, data._instanceCount * (TEXT_INSTANCE_BYTES / 4));\n device.queue.writeBuffer(lg.instanceBuf, 0, view.buffer as ArrayBuffer, view.byteOffset, view.byteLength);\n }\n lg.uploadedDataVersion = data._version;\n data._dirtyStart = 0;\n data._dirtyEnd = 0;\n }\n\n // MVP — skip upload when nothing relevant changed.\n const W = rr._targetWidth;\n const H = rr._targetHeight;\n const mi = lg.lastMvpInputs;\n if (!lg.mvpUploaded || mi[0] !== layer.positionPx.x || mi[1] !== layer.positionPx.y || mi[2] !== layer.rotationRad || mi[3] !== layer.scale || mi[4] !== W || mi[5] !== H) {\n buildLayerMvp(layer, W, H, _mvpScratch);\n device.queue.writeBuffer(lg.textU, 0, _mvpScratch.buffer as ArrayBuffer, _mvpScratch.byteOffset, 64);\n mi[0] = layer.positionPx.x;\n mi[1] = layer.positionPx.y;\n mi[2] = layer.rotationRad;\n mi[3] = layer.scale;\n mi[4] = W;\n mi[5] = H;\n lg.mvpUploaded = true;\n }\n\n // Viewport (only used by Slug dilation; pixel reciprocal is fine to refresh on resize).\n if (lg.uploadedViewportW !== W || lg.uploadedViewportH !== H) {\n const vp = new Float32Array([W, H, 0, 0]);\n device.queue.writeBuffer(lg.textU, 64, vp.buffer as ArrayBuffer, vp.byteOffset, 16);\n lg.uploadedViewportW = W;\n lg.uploadedViewportH = H;\n }\n\n // Color uniform carries the whole-layer opacity as alpha (RGB = white). Per-glyph/per-run\n // color comes from the instance `slugColor` attribute and is multiplied by this in the shader.\n const col = new Float32Array([1, 1, 1, layer.opacity]);\n device.queue.writeBuffer(lg.textU, 80, col.buffer as ArrayBuffer, col.byteOffset, 16);\n}\n\nfunction disposeLayerGpu(lg: LayerGpu): void {\n lg.textU.destroy();\n lg.instanceBuf.destroy();\n}\n\nfunction compareLayers(a: TextLayer, b: TextLayer): number {\n return a.order - b.order;\n}\n\n/** Create a standalone text renderer for one surface.\n *\n * @param surface - Surface whose swapchain receives the text pass.\n * @param opts - Initial layers and clear settings.\n * @returns A rendering context that can be registered with the engine. */\nexport function createTextRenderer(surface: SurfaceContext, opts: TextRendererOptions): TextRenderer {\n const canvas = surface.canvas;\n const layers = opts.layers.slice();\n\n const rr: TextRenderer = {\n _kind: KIND,\n _surface: surface,\n _layerGpu: new Map(),\n _targetWidth: canvas.width,\n _targetHeight: canvas.height,\n _disposed: false,\n _clear: opts.clear ?? true,\n layers,\n _layers: layers,\n clearColor: opts.clearValue ?? { r: 0, g: 0, b: 0, a: 1 },\n _drawCallsPre: 0,\n _update(): void {\n textRendererUpdate(rr);\n },\n _record(): number {\n return textRendererRecord(rr);\n },\n };\n return rr;\n}\n\nfunction textRendererUpdate(rr: TextRenderer): void {\n if (rr._disposed) {\n return;\n }\n const size = rr._surface.canvas;\n rr._targetWidth = size.width;\n rr._targetHeight = size.height;\n\n if (rr._layers.length > 1) {\n rr._layers.sort(compareLayers);\n }\n\n // Pipeline: depth-less, sampleCount=1, swapchain format. The key is identical for every\n // layer, so resolve it once per frame and reuse the pipeline + bind-group layout below.\n const { pipeline, cache } = getOrCreateTextPipeline(rr._surface.engine, rr._surface.format, 1, null, false);\n\n for (const layer of rr._layers) {\n if (!layer.visible) {\n continue;\n }\n const lg = ensureLayerGpu(rr, layer);\n if (lg.pipeline !== pipeline) {\n lg.pipeline = pipeline;\n // Pipeline change → bind groups must be rebuilt against new bindGroupLayout.\n lg.bindGroups.length = 0;\n lg.bindGroupAtlasVersions.length = 0;\n }\n uploadLayer(rr, lg, cache.bindGroupLayout);\n }\n}\n\nfunction textRendererRecord(rr: TextRenderer): number {\n if (rr._disposed) {\n return 0;\n }\n const eng = rr._surface.engine;\n const encoder = eng._currentEncoder;\n const swapView = rr._surface.scRT._colorView!;\n\n const pass = encoder.beginRenderPass({\n colorAttachments: [\n {\n view: swapView,\n clearValue: rr.clearColor,\n loadOp: rr._clear ? \"clear\" : \"load\",\n storeOp: \"store\",\n },\n ],\n });\n\n let drawCalls = 0;\n let lastPipeline: GPURenderPipeline | null = null;\n const { cache } = getOrCreateTextPipeline(rr._surface.engine, rr._surface.format, 1, null, false);\n const quadVertex = cache.quadVertexBuffer;\n pass.setVertexBuffer(0, quadVertex);\n\n for (const layer of rr._layers) {\n if (!layer.visible) {\n continue;\n }\n const lg = rr._layerGpu.get(layer);\n if (!lg || !lg.pipeline) {\n continue;\n }\n const data = layer.data;\n if (data._instanceCount === 0) {\n continue;\n }\n if (lastPipeline !== lg.pipeline) {\n pass.setPipeline(lg.pipeline);\n lastPipeline = lg.pipeline;\n }\n pass.setVertexBuffer(1, lg.instanceBuf);\n for (let i = 0; i < data._groups.length; i++) {\n const g = data._groups[i]!;\n const bg = lg.bindGroups[i];\n if (g.slotCount === 0 || !bg) {\n continue;\n }\n pass.setBindGroup(0, bg);\n pass.draw(6, g.slotCount, 0, g.slotStart);\n drawCalls++;\n }\n }\n\n pass.end();\n return drawCalls;\n}\n\n/** Add a text layer to an existing renderer if it is not already present.\n *\n * @param tr - Text renderer to mutate.\n * @param layer - Layer to append; draw order is sorted during renderer update. */\nexport function addTextRendererLayer(tr: TextRenderer, layer: TextLayer): void {\n if (tr._disposed) {\n throw new Error(\"TextRenderer has been disposed.\");\n }\n if (tr._layers.includes(layer)) {\n return;\n }\n tr._layers.push(layer);\n}\n\n/** Remove a text layer and release its per-layer GPU buffers.\n *\n * @returns `true` when the layer was present and removed. */\nexport function removeTextRendererLayer(tr: TextRenderer, layer: TextLayer): boolean {\n const i = tr._layers.indexOf(layer);\n if (i < 0) {\n return false;\n }\n tr._layers.splice(i, 1);\n const lg = tr._layerGpu.get(layer);\n if (lg) {\n disposeLayerGpu(lg);\n tr._layerGpu.delete(layer);\n }\n return true;\n}\n\n/** Register a text renderer with its surface so the engine updates and records it each frame. */\nexport function registerTextRenderer(tr: TextRenderer): void {\n registerRenderingContext(tr._surface, tr);\n}\n\n/** Unregister a text renderer from its surface without disposing its layer data. */\nexport function unregisterTextRenderer(tr: TextRenderer): void {\n unregisterRenderingContext(tr._surface, tr);\n}\n\n/** Dispose a text renderer and all per-layer GPU resources it owns. Layer `TextData` remains caller-owned. */\nexport function disposeTextRenderer(tr: TextRenderer): void {\n if (tr._disposed) {\n return;\n }\n unregisterTextRenderer(tr);\n tr._disposed = true;\n for (const lg of tr._layerGpu.values()) {\n disposeLayerGpu(lg);\n }\n tr._layerGpu.clear();\n tr._layers.length = 0;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"text-renderer.js","sources":["../../../src/text/text-renderer.ts"],"sourcesContent":["/** TextRenderer — a standalone `RenderingContext` that draws one or more\n * `TextLayer`s directly to the swapchain. Sibling of `SpriteRenderer`:\n * owns its own render pass, no scene / camera dependency.\n *\n * `TextLayer` is the 2D pixel-space placement record bound to a single TextData; it\n * lives here because it is consumed exclusively by `TextRenderer`. */\n\nimport { registerRenderingContext, unregisterRenderingContext } from \"../engine/engine.js\";\nimport type { RenderingContext } from \"../engine/engine.js\";\nimport type { SurfaceContext } from \"../engine/surface.js\";\nimport { createEmptyUniformBuffer } from \"../resource/gpu-buffers.js\";\nimport type { TextData } from \"./text-data.js\";\nimport { TEXT_INSTANCE_BYTES } from \"./text-data.js\";\nimport { ensureSharedAtlasGpu } from \"./_gpu/text-textures.js\";\nimport { getOrCreateTextPipeline } from \"./_gpu/text-pipeline.js\";\n\n// ─── TextLayer ────────────────────────────────────────────────────────────\n\n/** Initial placement and compositing options for a 2D text layer in a standalone text renderer. */\nexport interface TextLayerOptions {\n /** Top-left origin (in canvas pixels) for the layer's local coordinate frame. Default (0, 0). */\n readonly positionPx?: { readonly x: number; readonly y: number };\n /** Z-axis rotation about `positionPx`, in radians. Default 0. */\n readonly rotationRad?: number;\n /** Uniform scale applied to the laid-out text. Default 1. */\n readonly scale?: number;\n /** Sort order within a renderer (lower draws first). Default 0. */\n readonly order?: number;\n /** Alpha multiplier in [0, 1]. Default 1. */\n readonly opacity?: number;\n /** Coverage gamma for anti-aliased edges. Raises per-pixel coverage to `1/coverageGamma`\n * before compositing, darkening/thickening anti-aliased edges to mimic the gamma-space\n * blending used by native text renderers (DirectWrite/CoreText \"stem darkening\"). Only\n * meaningful when rendering into an sRGB (linear-blended) surface, where correct linear\n * AA otherwise makes text look lighter/thinner than gamma-space rasterizers. Values \\>1\n * thicken; 1 (default) is a no-op. Typical text values are ~1.8–2.2. */\n readonly coverageGamma?: number;\n /** Default true. */\n readonly visible?: boolean;\n}\n\n/** Pure-data 2D text layer. Mutate fields directly between frames. */\nexport interface TextLayer {\n /** @internal */\n readonly _kind: \"text-layer\";\n readonly data: TextData;\n positionPx: { x: number; y: number };\n rotationRad: number;\n scale: number;\n order: number;\n opacity: number;\n /** Coverage gamma for anti-aliased edges (see `TextLayerOptions.coverageGamma`). Default 1. */\n coverageGamma: number;\n visible: boolean;\n /** @internal Monotonic version bumped by helpers that mutate placement. */\n _version: number;\n}\n\n/** Create a 2D text layer that places a `TextData` block in canvas pixel space.\n *\n * @param data - Text data block drawn by the layer.\n * @param options - Optional pixel placement, scale, order, opacity, and visibility.\n * @returns A mutable layer object for use with a `TextRenderer`. */\nexport function createTextLayer(data: TextData, options?: TextLayerOptions): TextLayer {\n return {\n _kind: \"text-layer\",\n data,\n positionPx: { x: options?.positionPx?.x ?? 0, y: options?.positionPx?.y ?? 0 },\n rotationRad: options?.rotationRad ?? 0,\n scale: options?.scale ?? 1,\n order: options?.order ?? 0,\n opacity: options?.opacity ?? 1,\n coverageGamma: options?.coverageGamma ?? 1,\n visible: options?.visible ?? true,\n _version: 0,\n };\n}\n\n/** Update the layer's pixel position. Convenience wrapper. */\nexport function setTextLayerPosition(layer: TextLayer, x: number, y: number): void {\n layer.positionPx.x = x;\n layer.positionPx.y = y;\n layer._version++;\n}\n\n// ─── TextRenderer ─────────────────────────────────────────────────────────\n\nconst KIND = \"text-renderer\" as const;\n\n/** UBO: mat4 mvp (64B) + viewport vec4 (16B) + color vec4 (16B). */\nconst TEXT_UBO_BYTES = 96;\n\n/** Options for creating a standalone text renderer that draws directly to a surface. */\nexport interface TextRendererOptions {\n layers: readonly TextLayer[];\n /** Default true. Set false for HUD overlays so the text pass preserves existing scene color. */\n clear?: boolean;\n /** Default `{ r: 0, g: 0, b: 0, a: 1 }`. */\n clearValue?: GPUColorDict;\n}\n\n/** Standalone rendering context that draws sorted 2D text layers directly to the swapchain. */\nexport interface TextRenderer extends RenderingContext {\n /** @internal */\n readonly _kind: typeof KIND;\n readonly layers: readonly TextLayer[];\n /** @internal Mutable alias of {@link layers} (same array reference). */\n _layers: TextLayer[];\n /** @internal */ readonly _surface: SurfaceContext;\n /** @internal Per-layer GPU resources, keyed by layer. */\n _layerGpu: Map<TextLayer, LayerGpu>;\n /** @internal */ _targetWidth: number;\n /** @internal */ _targetHeight: number;\n /** @internal */ _disposed: boolean;\n /** @internal */ _clear: boolean;\n}\n\n/** @internal Per-layer GPU resources owned by the renderer. */\ninterface LayerGpu {\n layer: TextLayer;\n textU: GPUBuffer;\n instanceBuf: GPUBuffer;\n instanceCap: number;\n pipeline: GPURenderPipeline | null;\n /** Per-draw-group bind groups; rebuilt when atlas grows. */\n bindGroups: GPUBindGroup[];\n bindGroupAtlasVersions: number[];\n uploadedDataVersion: number;\n uploadedViewportW: number;\n uploadedViewportH: number;\n /** Snapshot of (posX, posY, rot, scale, W, H) to skip mvp upload when unchanged. */\n lastMvpInputs: Float32Array;\n mvpUploaded: boolean;\n}\n\nconst _mvpScratch = new Float32Array(16);\n\nfunction buildLayerMvp(layer: TextLayer, targetW: number, targetH: number, out: Float32Array): void {\n const s = layer.scale;\n const r = layer.rotationRad;\n const cr = Math.cos(r);\n const sr = Math.sin(r);\n const px = layer.positionPx.x;\n const py = layer.positionPx.y;\n // Map glyph-local (font Y-up) coords through (scale, flip-Y) → rotate → translate → ortho(W,H, Y-down).\n // Equivalent compact affine — see plan note for derivation. Column-major.\n const cx = (2 * s) / targetW;\n const cy = (2 * s) / targetH;\n out.fill(0);\n out[0] = cx * cr; // col 0, row 0\n out[1] = -cy * sr; // col 0, row 1\n out[4] = cx * sr; // col 1, row 0\n out[5] = cy * cr; // col 1, row 1\n out[10] = 1; // depth pass-through (we don't write depth)\n out[12] = (2 * px) / targetW - 1;\n out[13] = 1 - (2 * py) / targetH;\n out[15] = 1;\n}\n\nfunction ensureLayerGpu(rr: TextRenderer, layer: TextLayer): LayerGpu {\n let lg = rr._layerGpu.get(layer);\n if (lg) {\n return lg;\n }\n const engine = rr._surface.engine;\n const device = engine._device;\n const cap = Math.max(layer.data._instanceCount, 8);\n lg = {\n layer,\n textU: createEmptyUniformBuffer(engine, TEXT_UBO_BYTES, \"text-layer-ubo\"),\n instanceBuf: device.createBuffer({\n label: \"text-layer-instances\",\n size: cap * TEXT_INSTANCE_BYTES,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n }),\n instanceCap: cap,\n pipeline: null,\n bindGroups: [],\n bindGroupAtlasVersions: [],\n uploadedDataVersion: -1,\n uploadedViewportW: 0,\n uploadedViewportH: 0,\n lastMvpInputs: new Float32Array(6),\n mvpUploaded: false,\n };\n rr._layerGpu.set(layer, lg);\n return lg;\n}\n\nfunction ensureInstanceCapacity(device: GPUDevice, lg: LayerGpu, needed: number): void {\n if (needed <= lg.instanceCap) {\n return;\n }\n let cap = lg.instanceCap;\n while (cap < needed) {\n cap *= 2;\n }\n lg.instanceBuf.destroy();\n lg.instanceBuf = device.createBuffer({\n label: \"text-layer-instances\",\n size: cap * TEXT_INSTANCE_BYTES,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n });\n lg.instanceCap = cap;\n lg.uploadedDataVersion = -1;\n}\n\nfunction uploadLayer(rr: TextRenderer, lg: LayerGpu, bindGroupLayout: GPUBindGroupLayout): void {\n const device = rr._surface.engine._device;\n const layer = lg.layer;\n const data = layer.data;\n\n // Atlas + bind groups per draw group.\n for (let i = 0; i < data._groups.length; i++) {\n const g = data._groups[i]!;\n const { rebuilt, gpu: atlasGpu } = ensureSharedAtlasGpu(device, g.curveSet.atlas);\n const current = lg.bindGroups[i];\n const currentVer = lg.bindGroupAtlasVersions[i] ?? -1;\n if (!current || rebuilt || currentVer !== atlasGpu.uploadedVersion) {\n lg.bindGroups[i] = device.createBindGroup({\n label: \"text-renderer-bg0-\" + g.curveSetId,\n layout: bindGroupLayout,\n entries: [\n { binding: 0, resource: { buffer: lg.textU } },\n { binding: 1, resource: atlasGpu.curveTex.createView() },\n { binding: 2, resource: atlasGpu.bandTex.createView() },\n ],\n });\n lg.bindGroupAtlasVersions[i] = atlasGpu.uploadedVersion;\n }\n }\n if (lg.bindGroups.length > data._groups.length) {\n lg.bindGroups.length = data._groups.length;\n lg.bindGroupAtlasVersions.length = data._groups.length;\n }\n\n // Instance buffer.\n ensureInstanceCapacity(device, lg, data._instanceCount);\n if (lg.uploadedDataVersion !== data._version && data._instanceCount > 0) {\n const dirtyValid = lg.uploadedDataVersion !== -1 && data._dirtyEnd > data._dirtyStart;\n if (dirtyValid) {\n const startFloats = data._dirtyStart * (TEXT_INSTANCE_BYTES / 4);\n const endFloats = data._dirtyEnd * (TEXT_INSTANCE_BYTES / 4);\n const view = data._instances.subarray(startFloats, endFloats);\n device.queue.writeBuffer(lg.instanceBuf, data._dirtyStart * TEXT_INSTANCE_BYTES, view.buffer as ArrayBuffer, view.byteOffset, view.byteLength);\n } else {\n const view = data._instances.subarray(0, data._instanceCount * (TEXT_INSTANCE_BYTES / 4));\n device.queue.writeBuffer(lg.instanceBuf, 0, view.buffer as ArrayBuffer, view.byteOffset, view.byteLength);\n }\n lg.uploadedDataVersion = data._version;\n data._dirtyStart = 0;\n data._dirtyEnd = 0;\n }\n\n // MVP — skip upload when nothing relevant changed.\n const W = rr._targetWidth;\n const H = rr._targetHeight;\n const mi = lg.lastMvpInputs;\n if (!lg.mvpUploaded || mi[0] !== layer.positionPx.x || mi[1] !== layer.positionPx.y || mi[2] !== layer.rotationRad || mi[3] !== layer.scale || mi[4] !== W || mi[5] !== H) {\n buildLayerMvp(layer, W, H, _mvpScratch);\n device.queue.writeBuffer(lg.textU, 0, _mvpScratch.buffer as ArrayBuffer, _mvpScratch.byteOffset, 64);\n mi[0] = layer.positionPx.x;\n mi[1] = layer.positionPx.y;\n mi[2] = layer.rotationRad;\n mi[3] = layer.scale;\n mi[4] = W;\n mi[5] = H;\n lg.mvpUploaded = true;\n }\n\n // Viewport (only used by Slug dilation; pixel reciprocal is fine to refresh on resize).\n if (lg.uploadedViewportW !== W || lg.uploadedViewportH !== H) {\n const vp = new Float32Array([W, H, 0, 0]);\n device.queue.writeBuffer(lg.textU, 64, vp.buffer as ArrayBuffer, vp.byteOffset, 16);\n lg.uploadedViewportW = W;\n lg.uploadedViewportH = H;\n }\n\n // Color uniform carries the whole-layer opacity as alpha. The R slot (otherwise unused;\n // RGB does not tint per-glyph color) carries the coverage-gamma reciprocal applied to\n // anti-aliased edge coverage in the fragment shader. G/B stay 1.\n const gammaInv = layer.coverageGamma > 0 ? 1 / layer.coverageGamma : 1;\n const col = new Float32Array([gammaInv, 1, 1, layer.opacity]);\n device.queue.writeBuffer(lg.textU, 80, col.buffer as ArrayBuffer, col.byteOffset, 16);\n}\n\nfunction disposeLayerGpu(lg: LayerGpu): void {\n lg.textU.destroy();\n lg.instanceBuf.destroy();\n}\n\nfunction compareLayers(a: TextLayer, b: TextLayer): number {\n return a.order - b.order;\n}\n\n/** Create a standalone text renderer for one surface.\n *\n * @param surface - Surface whose swapchain receives the text pass.\n * @param opts - Initial layers and clear settings.\n * @returns A rendering context that can be registered with the engine. */\nexport function createTextRenderer(surface: SurfaceContext, opts: TextRendererOptions): TextRenderer {\n const canvas = surface.canvas;\n const layers = opts.layers.slice();\n\n const rr: TextRenderer = {\n _kind: KIND,\n _surface: surface,\n _layerGpu: new Map(),\n _targetWidth: canvas.width,\n _targetHeight: canvas.height,\n _disposed: false,\n _clear: opts.clear ?? true,\n layers,\n _layers: layers,\n clearColor: opts.clearValue ?? { r: 0, g: 0, b: 0, a: 1 },\n _drawCallsPre: 0,\n _update(): void {\n textRendererUpdate(rr);\n },\n _record(): number {\n return textRendererRecord(rr);\n },\n };\n return rr;\n}\n\nfunction textRendererUpdate(rr: TextRenderer): void {\n if (rr._disposed) {\n return;\n }\n const size = rr._surface.canvas;\n rr._targetWidth = size.width;\n rr._targetHeight = size.height;\n\n if (rr._layers.length > 1) {\n rr._layers.sort(compareLayers);\n }\n\n // Pipeline: depth-less, sampleCount=1, swapchain format. The key is identical for every\n // layer, so resolve it once per frame and reuse the pipeline + bind-group layout below.\n const { pipeline, cache } = getOrCreateTextPipeline(rr._surface.engine, rr._surface.format, 1, null, false);\n\n for (const layer of rr._layers) {\n if (!layer.visible) {\n continue;\n }\n const lg = ensureLayerGpu(rr, layer);\n if (lg.pipeline !== pipeline) {\n lg.pipeline = pipeline;\n // Pipeline change → bind groups must be rebuilt against new bindGroupLayout.\n lg.bindGroups.length = 0;\n lg.bindGroupAtlasVersions.length = 0;\n }\n uploadLayer(rr, lg, cache.bindGroupLayout);\n }\n}\n\nfunction textRendererRecord(rr: TextRenderer): number {\n if (rr._disposed) {\n return 0;\n }\n const eng = rr._surface.engine;\n const encoder = eng._currentEncoder;\n const swapView = rr._surface.scRT._colorView!;\n\n const pass = encoder.beginRenderPass({\n colorAttachments: [\n {\n view: swapView,\n clearValue: rr.clearColor,\n loadOp: rr._clear ? \"clear\" : \"load\",\n storeOp: \"store\",\n },\n ],\n });\n\n let drawCalls = 0;\n let lastPipeline: GPURenderPipeline | null = null;\n const { cache } = getOrCreateTextPipeline(rr._surface.engine, rr._surface.format, 1, null, false);\n const quadVertex = cache.quadVertexBuffer;\n pass.setVertexBuffer(0, quadVertex);\n\n for (const layer of rr._layers) {\n if (!layer.visible) {\n continue;\n }\n const lg = rr._layerGpu.get(layer);\n if (!lg || !lg.pipeline) {\n continue;\n }\n const data = layer.data;\n if (data._instanceCount === 0) {\n continue;\n }\n if (lastPipeline !== lg.pipeline) {\n pass.setPipeline(lg.pipeline);\n lastPipeline = lg.pipeline;\n }\n pass.setVertexBuffer(1, lg.instanceBuf);\n for (let i = 0; i < data._groups.length; i++) {\n const g = data._groups[i]!;\n const bg = lg.bindGroups[i];\n if (g.slotCount === 0 || !bg) {\n continue;\n }\n pass.setBindGroup(0, bg);\n pass.draw(6, g.slotCount, 0, g.slotStart);\n drawCalls++;\n }\n }\n\n pass.end();\n return drawCalls;\n}\n\n/** Add a text layer to an existing renderer if it is not already present.\n *\n * @param tr - Text renderer to mutate.\n * @param layer - Layer to append; draw order is sorted during renderer update. */\nexport function addTextRendererLayer(tr: TextRenderer, layer: TextLayer): void {\n if (tr._disposed) {\n throw new Error(\"TextRenderer has been disposed.\");\n }\n if (tr._layers.includes(layer)) {\n return;\n }\n tr._layers.push(layer);\n}\n\n/** Remove a text layer and release its per-layer GPU buffers.\n *\n * @returns `true` when the layer was present and removed. */\nexport function removeTextRendererLayer(tr: TextRenderer, layer: TextLayer): boolean {\n const i = tr._layers.indexOf(layer);\n if (i < 0) {\n return false;\n }\n tr._layers.splice(i, 1);\n const lg = tr._layerGpu.get(layer);\n if (lg) {\n disposeLayerGpu(lg);\n tr._layerGpu.delete(layer);\n }\n return true;\n}\n\n/** Register a text renderer with its surface so the engine updates and records it each frame. */\nexport function registerTextRenderer(tr: TextRenderer): void {\n registerRenderingContext(tr._surface, tr);\n}\n\n/** Unregister a text renderer from its surface without disposing its layer data. */\nexport function unregisterTextRenderer(tr: TextRenderer): void {\n unregisterRenderingContext(tr._surface, tr);\n}\n\n/** Dispose a text renderer and all per-layer GPU resources it owns. Layer `TextData` remains caller-owned. */\nexport function disposeTextRenderer(tr: TextRenderer): void {\n if (tr._disposed) {\n return;\n }\n unregisterTextRenderer(tr);\n tr._disposed = true;\n for (const lg of tr._layerGpu.values()) {\n disposeLayerGpu(lg);\n }\n tr._layerGpu.clear();\n tr._layers.length = 0;\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babylonjs/lite",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "A lightweight, tree-shakable, WebGPU-first rendering library derived from Babylon.js.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://doc.babylonjs.com/lite/",
@@ -22,7 +22,7 @@
22
22
  "unpkg": "./dist/index.js",
23
23
  "sideEffects": false,
24
24
  "babylonLiteRelease": {
25
- "azureBuildId": "55198",
26
- "sourceVersion": "9a6619e6ab55aa579c025bc06a78ae33a7e6199f"
25
+ "azureBuildId": "55345",
26
+ "sourceVersion": "0a556d366125eaaf1e7d1276199f1bb4de95aa1b"
27
27
  }
28
28
  }