From 3951f0e5751a1501f6ff42cec4b44f9a8f4ef2a9 Mon Sep 17 00:00:00 2001 From: David Catuhe Date: Thu, 16 Jul 2026 15:27:24 -0700 Subject: [PATCH 1/7] feat: add thin-instance LOD and CSM caster caps Add GPU-driven near/far thin-instance partitioning with partner lifecycle handling, preserve original framebuffer coordinates for picking discard shaders, and support per-caster CSM cascade limits. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 956e9bbb-057c-48ee-87ad-0e25093fd1ce --- docs/lite/architecture/00-overview.md | 1 + docs/lite/architecture/12-thin-instances.md | 27 +- docs/lite/architecture/16-shadow-generator.md | 5 + docs/lite/architecture/17-cascaded-shadow.md | 17 + docs/lite/architecture/18-picking.md | 22 +- lab/public/bundle/manifest/scene1.json | 2 +- lab/public/bundle/manifest/scene10.json | 2 +- lab/public/bundle/manifest/scene104.json | 6 +- lab/public/bundle/manifest/scene105.json | 4 +- lab/public/bundle/manifest/scene11.json | 6 +- lab/public/bundle/manifest/scene111.json | 2 +- lab/public/bundle/manifest/scene112.json | 4 +- lab/public/bundle/manifest/scene114.json | 2 +- lab/public/bundle/manifest/scene115.json | 6 +- lab/public/bundle/manifest/scene116.json | 2 +- lab/public/bundle/manifest/scene118.json | 2 +- lab/public/bundle/manifest/scene12.json | 6 +- lab/public/bundle/manifest/scene122.json | 2 +- lab/public/bundle/manifest/scene123.json | 2 +- lab/public/bundle/manifest/scene124.json | 2 +- lab/public/bundle/manifest/scene127.json | 2 +- lab/public/bundle/manifest/scene128.json | 2 +- lab/public/bundle/manifest/scene129.json | 6 +- lab/public/bundle/manifest/scene13.json | 2 +- lab/public/bundle/manifest/scene14.json | 2 +- lab/public/bundle/manifest/scene141.json | 2 +- lab/public/bundle/manifest/scene143.json | 2 +- lab/public/bundle/manifest/scene144.json | 8 +- lab/public/bundle/manifest/scene145.json | 4 +- lab/public/bundle/manifest/scene146.json | 4 +- lab/public/bundle/manifest/scene147.json | 6 +- lab/public/bundle/manifest/scene148.json | 6 +- lab/public/bundle/manifest/scene149.json | 2 +- lab/public/bundle/manifest/scene152.json | 6 +- lab/public/bundle/manifest/scene157.json | 6 +- lab/public/bundle/manifest/scene158.json | 6 +- lab/public/bundle/manifest/scene159.json | 2 +- lab/public/bundle/manifest/scene160.json | 2 +- lab/public/bundle/manifest/scene161.json | 2 +- lab/public/bundle/manifest/scene162.json | 2 +- lab/public/bundle/manifest/scene163.json | 2 +- lab/public/bundle/manifest/scene164.json | 8 +- lab/public/bundle/manifest/scene165.json | 2 +- lab/public/bundle/manifest/scene17.json | 4 +- lab/public/bundle/manifest/scene171.json | 4 +- lab/public/bundle/manifest/scene173.json | 4 +- lab/public/bundle/manifest/scene174.json | 4 +- lab/public/bundle/manifest/scene175.json | 4 +- lab/public/bundle/manifest/scene176.json | 6 +- lab/public/bundle/manifest/scene177.json | 2 +- lab/public/bundle/manifest/scene178.json | 4 +- lab/public/bundle/manifest/scene179.json | 2 +- lab/public/bundle/manifest/scene18.json | 2 +- lab/public/bundle/manifest/scene19.json | 2 +- lab/public/bundle/manifest/scene20.json | 2 +- lab/public/bundle/manifest/scene205.json | 4 +- lab/public/bundle/manifest/scene206.json | 2 +- lab/public/bundle/manifest/scene21.json | 2 +- lab/public/bundle/manifest/scene210.json | 4 +- lab/public/bundle/manifest/scene211.json | 6 +- lab/public/bundle/manifest/scene212.json | 4 +- lab/public/bundle/manifest/scene213.json | 2 +- lab/public/bundle/manifest/scene214.json | 2 +- lab/public/bundle/manifest/scene215.json | 6 +- lab/public/bundle/manifest/scene216.json | 2 +- lab/public/bundle/manifest/scene217.json | 2 +- lab/public/bundle/manifest/scene218.json | 4 +- lab/public/bundle/manifest/scene219.json | 2 +- lab/public/bundle/manifest/scene22.json | 2 +- lab/public/bundle/manifest/scene221.json | 4 +- lab/public/bundle/manifest/scene222.json | 2 +- lab/public/bundle/manifest/scene223.json | 4 +- lab/public/bundle/manifest/scene229.json | 2 +- lab/public/bundle/manifest/scene23.json | 4 +- lab/public/bundle/manifest/scene24.json | 2 +- lab/public/bundle/manifest/scene240.json | 4 +- lab/public/bundle/manifest/scene241.json | 4 +- lab/public/bundle/manifest/scene242.json | 4 +- lab/public/bundle/manifest/scene243.json | 6 +- lab/public/bundle/manifest/scene244.json | 4 +- lab/public/bundle/manifest/scene245.json | 6 +- lab/public/bundle/manifest/scene246.json | 6 +- lab/public/bundle/manifest/scene247.json | 6 +- lab/public/bundle/manifest/scene248.json | 2 +- lab/public/bundle/manifest/scene249.json | 2 +- lab/public/bundle/manifest/scene251.json | 6 +- lab/public/bundle/manifest/scene253.json | 8 +- lab/public/bundle/manifest/scene254.json | 4 +- lab/public/bundle/manifest/scene255.json | 6 +- lab/public/bundle/manifest/scene257.json | 4 +- lab/public/bundle/manifest/scene258.json | 2 +- lab/public/bundle/manifest/scene259.json | 2 +- lab/public/bundle/manifest/scene26.json | 2 +- lab/public/bundle/manifest/scene260.json | 4 +- lab/public/bundle/manifest/scene265.json | 4 +- lab/public/bundle/manifest/scene27.json | 6 +- lab/public/bundle/manifest/scene28.json | 4 +- lab/public/bundle/manifest/scene29.json | 6 +- lab/public/bundle/manifest/scene30.json | 4 +- lab/public/bundle/manifest/scene31.json | 4 +- lab/public/bundle/manifest/scene32.json | 4 +- lab/public/bundle/manifest/scene33.json | 4 +- lab/public/bundle/manifest/scene34.json | 4 +- lab/public/bundle/manifest/scene35.json | 6 +- lab/public/bundle/manifest/scene37.json | 4 +- lab/public/bundle/manifest/scene39.json | 4 +- lab/public/bundle/manifest/scene41.json | 6 +- lab/public/bundle/manifest/scene47.json | 4 +- lab/public/bundle/manifest/scene49.json | 2 +- lab/public/bundle/manifest/scene5.json | 8 +- lab/public/bundle/manifest/scene54.json | 2 +- lab/public/bundle/manifest/scene56.json | 2 +- lab/public/bundle/manifest/scene57.json | 2 +- lab/public/bundle/manifest/scene59.json | 2 +- lab/public/bundle/manifest/scene6.json | 2 +- lab/public/bundle/manifest/scene7.json | 6 +- lab/public/bundle/manifest/scene73.json | 4 +- lab/public/bundle/manifest/scene8.json | 2 +- lab/public/bundle/manifest/scene9.json | 2 +- lab/public/bundle/manifest/scene94.json | 2 +- lab/public/bundle/manifest/scene95.json | 2 +- lab/public/bundle/manifest/scene98.json | 4 +- lab/public/bundle/manifest/scene99.json | 6 +- .../src/frame-graph/shadow-inputs.ts | 22 ++ packages/babylon-lite/src/index.ts | 6 +- .../babylon-lite/src/mesh/mesh-dispose.ts | 2 + packages/babylon-lite/src/mesh/mesh.ts | 4 + .../src/mesh/thin-instance-cull-binding.ts | 66 +++- .../src/mesh/thin-instance-gpu-culling.ts | 309 ++++++++++++++++-- .../babylon-lite/src/mesh/thin-instance.ts | 163 +++++++++ .../babylon-lite/src/picking/gpu-picker.ts | 17 +- .../src/picking/picking-pipeline.ts | 2 +- .../src/picking/picking-shader.ts | 16 +- .../babylon-lite/src/scene/transform-node.ts | 3 + .../src/shadow/csm-shadow-task-hooks.ts | 32 +- .../unit/audit/no-direct-mat4-writes.test.ts | 2 +- .../lite/unit/mesh-clone-gpu-dispose.test.ts | 39 +++ tests/lite/unit/picking-discard-api.test.ts | 52 ++- .../unit/shadow-caster-max-cascade.test.ts | 92 ++++++ .../unit/thin-instance-gpu-culling.test.ts | 156 ++++++++- tests/lite/unit/thin-instance-lod.test.ts | 148 +++++++++ 141 files changed, 1360 insertions(+), 275 deletions(-) create mode 100644 tests/lite/unit/shadow-caster-max-cascade.test.ts create mode 100644 tests/lite/unit/thin-instance-lod.test.ts diff --git a/docs/lite/architecture/00-overview.md b/docs/lite/architecture/00-overview.md index 025893b639..c7568a1395 100644 --- a/docs/lite/architecture/00-overview.md +++ b/docs/lite/architecture/00-overview.md @@ -381,6 +381,7 @@ createEsmDirectionalShadowGenerator(engine: Engine, light: DirectionalLight, con createPcfSpotlightShadowGenerator(engine: Engine, light: SpotLight, config?: PcfSpotlightShadowGeneratorConfig): ShadowGenerator createPcfDirectionalShadowGenerator(engine: Engine, light: DirectionalLight, config?: PcfDirectionalShadowGeneratorConfig): ShadowGenerator setShadowTaskCasterMeshes(shadowGenerator: ShadowGenerator, casterMeshes: readonly Mesh[]): void +setShadowCasterMaxCascade(mesh: Mesh, maxCascade: number): void // Animation createAnimationController(skeleton, scene): AnimationController diff --git a/docs/lite/architecture/12-thin-instances.md b/docs/lite/architecture/12-thin-instances.md index fe127d08eb..193a93cfcb 100644 --- a/docs/lite/architecture/12-thin-instances.md +++ b/docs/lite/architecture/12-thin-instances.md @@ -35,6 +35,8 @@ export interface ThinInstanceData { _colorGpuBufferStorage: boolean; _colorGpuVersion: number; _gpuCullingEnabled: boolean; // opt-in GPU frustum culling + indirect draw + _lodPartner?: Mesh | null; // lower-detail mesh receiving the far cull bucket + _lodSource?: Mesh | null; // source mesh when this mesh is the LOD partner } ``` @@ -70,6 +72,17 @@ export function setThinInstanceColors(mesh: Mesh, colors: Float32Array): void; /** Enable/disable per-pass GPU frustum culling. Must be called before registerScene(). */ export function enableThinInstanceGpuCulling(mesh: Mesh, enabled?: boolean): void; + +interface ThinInstanceLodPartnerOptions { + distance: number; // camera-space switch distance in world units + band?: number; // deterministic per-instance dither width, default 0 +} + +/** Split one GPU-culling result into near/full-detail and far/LOD buckets. */ +export function setThinInstanceLodPartner(fullMesh: Mesh, lodMesh: Mesh, options: ThinInstanceLodPartnerOptions): void; + +/** Restore the two meshes to independent rendering. */ +export function clearThinInstanceLodPartner(fullMesh: Mesh): void; ``` `setThinInstanceDrawCount()` is the count-only path for fixed-capacity pools whose CPU array and GPU @@ -256,7 +269,7 @@ Returns the updated `slot` number — the next free vertex buffer slot after all GPU culling is opt-in through `enableThinInstanceGpuCulling(mesh)`. The helper only flips state on existing `ThinInstanceData`; the compute module is dynamically imported (via the shared `thin-instance-cull-binding.ts` lifecycle helper) by the Standard, PBR, and ShaderMaterial group builders only when at least one mesh in that material family has `_gpuCullingEnabled === true`. -The per-binding cull lifecycle is factored into one shared module, `packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts`, used identically by all three material families. Its `tryBind()` seam is called from each renderable's `bind()`: it gates on opt-in + opaque-only, marks the renderable `_direct`, creates the per-binding `ThinInstanceGpuCullState`, registers its disposal, and returns a `TiCullBinding` whose `update()` dispatches the compute cull pass and whose `draw()` issues `drawIndexedIndirect` (or falls back to a normal instanced draw when culling did not run). +The per-binding cull lifecycle is factored into one shared module, `packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts`, used identically by all three material families. Its `tryBind()` seam is called from each renderable's `bind()`: it gates on opt-in + opaque-only, creates the per-binding `ThinInstanceGpuCullState`, registers its disposal, and returns a `TiCullBinding` whose `update()` dispatches the compute cull pass and whose `draw()` issues `drawIndexedIndirect` (or falls back to a normal instanced draw when culling did not run). ### Scope @@ -289,7 +302,7 @@ Cull state is owned by each `DrawBinding`, not by `ThinInstanceData`, because th 6. The compute shader transforms the local sphere by `mesh.world * instanceWorld`, tests it against all planes, atomically appends visible instances to compacted buffers, and atomically increments `args[1]`. 7. The draw closure binds compacted buffers and calls `drawIndexedIndirect(argsBuffer, 0)`. -Cull-enabled renderables set `_direct = true` so they are encoded every frame with current buffer objects instead of being captured in the opaque render bundle. This avoids stale render-bundle references when thin-instance capacity grows. +Cull-enabled opaque renderables remain bundle-compatible because their compacted buffers and indirect argument buffers are stable. Any buffer replacement bumps the global visibility epoch, forcing the opaque render bundle to record the new handles before it executes. ### Compute Shader Outline @@ -307,6 +320,16 @@ if (sphereIntersectsFrustum(center, radius)) { The test is conservative: spheres touching a plane stay visible. This preserves parity with non-culled rendering. +### Distance LOD Pairing + +`setThinInstanceLodPartner(fullMesh, lodMesh, { distance, band })` extends the opt-in GPU culler with a second compacted output. Both meshes must already have thin-instance state and must be distinct, opaque, non-transmissive meshes. The full-detail mesh must be the only source for the partner, and neither mesh may simultaneously participate in another LOD chain. LOD-paired thin-instance state cannot be shared through `cloneTransformNode`; pair meshes only before cloning or use independent thin-instance data. `distance` and `band` are finite non-negative world-space values. + +The source mesh retains in-frustum instances closer than the threshold. Farther instances are compacted into stable matrix/color buffers and an indirect argument buffer consumed by the partner. `band` applies a deterministic instance-index hash in `[-band/2, +band/2]`, spreading transitions without frame-to-frame noise. The partner's geometry and material pipeline are its own, but its drawn matrices and optional instance colors come from the source. A partner that renders instance colors therefore requires the source draw to provide them too. + +The far bucket is published per `RenderTargetSignature`, so main, shadow, and auxiliary camera passes never share compacted state. Partner renderables use the direct-draw phase and resolve the current bucket after all binding updates and the shared compute batch have completed; scene insertion order therefore cannot introduce a one-frame lag or capture stale cross-renderable handles in an opaque bundle. Source renderables retain their normal stable-buffer bundle path. If source culling is unavailable, disabled, hidden, or empty, the source falls back to drawing all active instances and the paired partner draws nothing. + +Pairing is configured after `setThinInstances()` and before `registerScene()`. Repeating the call with the same pair updates `distance`/`band` live. `clearThinInstanceLodPartner()` makes the partner fall back to its independent instance draw immediately; pairing links are also detached automatically when either mesh is disposed so no draw retains destroyed far-bucket buffers. + --- ## Pipeline Configuration diff --git a/docs/lite/architecture/16-shadow-generator.md b/docs/lite/architecture/16-shadow-generator.md index fa17244745..db19730723 100644 --- a/docs/lite/architecture/16-shadow-generator.md +++ b/docs/lite/architecture/16-shadow-generator.md @@ -54,10 +54,14 @@ export interface ShadowTaskInputs { } export function setShadowTaskCasterMeshes(shadowGenerator: ShadowGenerator, casterMeshes: readonly Mesh[]): void; + +export function setShadowCasterMaxCascade(mesh: Mesh, maxCascade: number): void; ``` Caster meshes are task inputs. Scene setup creates the filter-specific `ShadowGenerator`, assigns it to the light, calls `setShadowTaskCasterMeshes()` to provide the caster list, then calls `registerSceneWithShadowSupport()` instead of `registerScene()` to install the scene-owned shadow task. +`setShadowCasterMaxCascade()` is CSM-specific: it limits a caster to layers `0..maxCascade`, accepts a non-negative integer or `Infinity`, and is applied when a new caster-array instance is supplied. ESM and single-map PCF ignore it. + ### Common ShadowGenerator Interface (`shadow-generator.ts`) ```typescript @@ -161,6 +165,7 @@ import { ensurePcfShadowTaskState, preloadPcfShadowTaskState, renderPcfShadowMap Shadow generators share math and UBO packing helpers only. Caster ownership lives in `ShadowTaskInputs`: - **`setShadowTaskCasterMeshes()`** — registers the caster mesh list for a generator in lazy task-owned input state. +- **`setShadowCasterMaxCascade()`** — stores a lazy per-mesh CSM cascade cap; `Infinity` clears it. - **`writeShadowUboFields()`** — packs a `ShadowGenerator`'s light matrix (16 floats), depth values (2 floats + 2 padding), and shadowsInfo (4 floats) into a 24-float array for downstream UBO upload. - **`buildLightViewMatrix()` / `multiply4x4()`** — shared light-space matrix math for ESM and PCF task paths. - **`createShadowParamsUBO()` / `createSharedShadowUBO()`** — shared GPU buffer setup for generator-owned receiver resources. diff --git a/docs/lite/architecture/17-cascaded-shadow.md b/docs/lite/architecture/17-cascaded-shadow.md index 73202d860a..718cb0968f 100644 --- a/docs/lite/architecture/17-cascaded-shadow.md +++ b/docs/lite/architecture/17-cascaded-shadow.md @@ -41,6 +41,8 @@ function createCsmDirectionalShadowGenerator(engine: EngineContext, light: Direc function getCsmReceiverTexture(shadowGenerator: ShadowGenerator): Texture2D; function onCsmReceiverUpdate(shadowGenerator: ShadowGenerator, callback: (data: Float32Array) => void): () => void; + +function setShadowCasterMaxCascade(mesh: Mesh, maxCascade: number): void; ``` Usage mirrors the other directional generators: @@ -54,6 +56,13 @@ setShadowTaskCasterMeshes(light.shadowGenerator, casterMeshes); await registerSceneWithShadowSupport(scene); ``` +`setShadowCasterMaxCascade(mesh, maxCascade)` limits a caster to cascade layers +`0..maxCascade` (`0` is nearest). The default is all cascades; pass `Infinity` to +restore it. Values must be non-negative integer indexes or `Infinity`. The cap is +snapshotted when `setShadowTaskCasterMeshes` supplies the caster set, so changing a +live cap requires re-supplying a new caster-array instance. CSM updates only the +changed caster's per-cascade task membership; ESM and single-map PCF ignore the cap. + Custom `ShaderMaterial` receivers use the same public generator without reading its internal WebGPU resources: @@ -233,6 +242,10 @@ Shadow-map recreation is not supported by the current fixed generator configurat therefore the wrapper remains valid until the generator's GPU resources are disposed with the scene. +Each CSM task state also snapshots every caster's maximum cascade. When a new caster +array is supplied without material changes, the incremental diff removes and re-adds +only new, removed, or re-capped casters, preserving all unchanged caster packets. + ## Babylon.js Equivalence Map | Babylon.js | Babylon Lite | @@ -290,6 +303,10 @@ adapter creates one explicit 2d-array depth wrapper, reuses the generator textur comparison sampler without exposing them in its signature, preserves wrapper identity, survives a ShaderMaterial acquire/release cycle, and rejects ESM/PCF generators. +`tests/lite/unit/shadow-caster-max-cascade.test.ts` validates cap input, default and +reset behavior, and incremental reassignment of an existing caster across cascade +tasks after a live cap change. + ## File Manifest - `shadow/csm-directional-shadow-generator.ts` — public factory, custom-receiver diff --git a/docs/lite/architecture/18-picking.md b/docs/lite/architecture/18-picking.md index 2129f6c795..d81a43d907 100644 --- a/docs/lite/architecture/18-picking.md +++ b/docs/lite/architecture/18-picking.md @@ -47,6 +47,22 @@ interface Ray { direction: [number, number, number]; length: number; } + +interface PickOptions { + filter?: (mesh: Mesh) => boolean; + discard?: PickDiscardRule; + debugLabel?: string; +} + +// WGSL shape injected for custom discard rules. +struct PickDiscardInput { + worldPos: vec3f, + fragmentCoord: vec2f, // selected pixel center in the original backing framebuffer + pickId: u32, + thinInstanceIndex: u32, + hasThinInstance: u32, + instanceExtras: vec4f, +} ``` ### Functions @@ -90,7 +106,7 @@ and .babylon loader. No copies needed — the arrays already exist in JS memory. 1. Each mesh (or thin instance) is assigned a sequential pick ID (1-based; 0 = miss). 2. A WGSL shader writes the 24-bit pick ID as RGB at `@location(0)` and the fragment's NDC depth at `@location(1)`. -3. The pass uses a **pick-zoomed view-projection** so only the picked pixel survives, drawing all meshes to a **1×1** target: two colour attachments (`rgba8unorm` pick ID + `r32float` NDC depth) plus a `depth24plus` depth buffer (reverse-Z, compare `greater`). +3. The pass uses a **pick-zoomed view-projection** so only the picked pixel survives, drawing all meshes to a **1×1** target: two colour attachments (`rgba8unorm` pick ID + `r32float` NDC depth) plus a `depth24plus` depth buffer (reverse-Z, compare `greater`). Because the 1×1 fragment position is always `(0.5, 0.5)`, the scene UBO separately carries the selected pixel center in original backing-framebuffer coordinates for custom discard WGSL. 4. The 1×1 pick-ID and depth texels are copied to staging buffers and read back. 5. The pick ID is decoded: `(r << 16) | (g << 8) | b`. 6. The world-space hit point is reconstructed by unprojecting NDC + the read-back depth through `inverse(VP)`. @@ -179,13 +195,13 @@ each attachment is a single texel: **Regular meshes:** | Group | Binding | Type | Content | |-------|---------|------|---------| -| 0 | 0 | uniform | `mat4x4f` — viewProjection (shared, 64 bytes) | +| 0 | 0 | uniform | `mat4x4f` viewProjection + `vec2f` original fragment coordinate (shared, 80 bytes) | | 1 | 0 | uniform | `mat4x4f` world + `u32` pickId (80 bytes, 16-aligned) | **Thin-instanced meshes:** | Group | Binding | Type | Content | |-------|---------|------|---------| -| 0 | 0 | uniform | `mat4x4f` — viewProjection (shared) | +| 0 | 0 | uniform | `mat4x4f` viewProjection + `vec2f` original fragment coordinate (shared, 80 bytes) | | 1 | 0 | uniform | `u32` baseMeshPickId (16 bytes, padded) | | 1 | 1 | read-only-storage | `array` — instance world matrices | diff --git a/lab/public/bundle/manifest/scene1.json b/lab/public/bundle/manifest/scene1.json index 183022e97a..deab76641a 100644 --- a/lab/public/bundle/manifest/scene1.json +++ b/lab/public/bundle/manifest/scene1.json @@ -9,7 +9,7 @@ "scene1-generate-mipmaps-DfKYIHYG.js", "scene1-gltf-glb-parser-BL74Wcec.js", "scene1-ibl-fragment-CeRrvT96.js", - "scene1-pbr-renderable-D8VHJTRA.js", + "scene1-pbr-renderable-7OwI6cwq.js", "scene1-rgbd-decode-JSHbS7uW.js", "scene1-scene-uniforms-FTYH6Ct0.js", "scene1-singlelight-hemispheric-wgsl-dhCCcXC9.js", diff --git a/lab/public/bundle/manifest/scene10.json b/lab/public/bundle/manifest/scene10.json index e0df1c0597..a182546ce8 100644 --- a/lab/public/bundle/manifest/scene10.json +++ b/lab/public/bundle/manifest/scene10.json @@ -3,7 +3,7 @@ "gzipKB": 21.7, "ignoredRawKB": 0, "runtimeChunks": [ - "scene10-pbr-renderable-CFGNBcGv.js", + "scene10-pbr-renderable-tRg_51jp.js", "scene10-singlelight-hemispheric-wgsl-w0cK80Wf.js", "scene10.js" ] diff --git a/lab/public/bundle/manifest/scene104.json b/lab/public/bundle/manifest/scene104.json index ebbd59c84b..a54dbc396c 100644 --- a/lab/public/bundle/manifest/scene104.json +++ b/lab/public/bundle/manifest/scene104.json @@ -1,13 +1,13 @@ { - "rawKB": 101.6, + "rawKB": 101.7, "gzipKB": 39.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene104-generate-mipmaps-C2O_HNRi.js", - "scene104-gltf-feature-registry-DExyFmjz.js", + "scene104-gltf-feature-registry-CXpnn35c.js", "scene104-gltf-glb-parser-BJfNq64D.js", "scene104-shader-composer-DishuYLa.js", - "scene104-standard-renderable-CHoHvWO9.js", + "scene104-standard-renderable-DamwGKld.js", "scene104-std-lightmap-fragment-BtZeffyx.js", "scene104-wgsl-fog-m3PkjS7i.js", "scene104.js" diff --git a/lab/public/bundle/manifest/scene105.json b/lab/public/bundle/manifest/scene105.json index c4e92174d0..b4073fc532 100644 --- a/lab/public/bundle/manifest/scene105.json +++ b/lab/public/bundle/manifest/scene105.json @@ -4,10 +4,10 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene105-generate-mipmaps-q97-3GGe.js", - "scene105-gltf-feature-registry-CyU-hHDJ.js", + "scene105-gltf-feature-registry-BIPoqOM_.js", "scene105-gltf-glb-parser-BsWC1iTB.js", "scene105-shader-composer-DishuYLa.js", - "scene105-standard-renderable-BXA9dKAx.js", + "scene105-standard-renderable-df5uKVi6.js", "scene105-std-lightmap-fragment-DJHrjMOe.js", "scene105-wgsl-fog-m3PkjS7i.js", "scene105.js" diff --git a/lab/public/bundle/manifest/scene11.json b/lab/public/bundle/manifest/scene11.json index a97055671d..da42c3e3bd 100644 --- a/lab/public/bundle/manifest/scene11.json +++ b/lab/public/bundle/manifest/scene11.json @@ -9,13 +9,13 @@ "scene11-gltf-ext-spec-gloss-D60sFZYX.js", "scene11-gltf-feature-animations-CUKuLYEP.js", "scene11-gltf-feature-extras-VHPteGay.js", - "scene11-gltf-feature-registry-cyA5niPB.js", + "scene11-gltf-feature-registry-DRQA1mkV.js", "scene11-gltf-feature-skeleton-Djlep9zW.js", "scene11-gltf-glb-parser-B0CzN8nR.js", "scene11-gltf-sampler-desc-DP9TFn1W.js", - "scene11-pbr-renderable-DHK-X7do.js", + "scene11-pbr-renderable-gYyFsbuC.js", "scene11-singlelight-hemispheric-wgsl-B65r2jYR.js", - "scene11-skeleton-fragment-BzLZRpws.js", + "scene11-skeleton-fragment-CD3HlWuZ.js", "scene11.js" ] } diff --git a/lab/public/bundle/manifest/scene111.json b/lab/public/bundle/manifest/scene111.json index 34e39c2739..578babd589 100644 --- a/lab/public/bundle/manifest/scene111.json +++ b/lab/public/bundle/manifest/scene111.json @@ -18,7 +18,7 @@ "scene111-node-registry-DNi8iW5i.js", "scene111-node-renderable-DsHgimzd.js", "scene111-node-shadow-Ckjwl7eT.js", - "scene111-pbr-renderable-D8S6cd3k.js", + "scene111-pbr-renderable-CxoWEpjm.js", "scene111-pbr-shadow-fragment-CFzpdZcF.js", "scene111-shader-composer-CdmK2apN.js", "scene111-shadow-fragment-core-Dbe1xNkt.js", diff --git a/lab/public/bundle/manifest/scene112.json b/lab/public/bundle/manifest/scene112.json index cf7ebe3d5d..e3c8322e10 100644 --- a/lab/public/bundle/manifest/scene112.json +++ b/lab/public/bundle/manifest/scene112.json @@ -9,11 +9,11 @@ "scene112-generate-mipmaps-r2o5vX6L.js", "scene112-gltf-ext-basisu-CrmhRp7H.js", "scene112-gltf-ext-dielectric-C6hvXXiF.js", - "scene112-gltf-feature-registry-kjsHwLH2.js", + "scene112-gltf-feature-registry-BmUvlgMr.js", "scene112-gltf-json-asset-Cj8WBr3J.js", "scene112-ibl-fragment-CeRrvT96.js", "scene112-pbr-refraction-nAlT2yO6.js", - "scene112-pbr-renderable-UQ-QPbSS.js", + "scene112-pbr-renderable-d4hIowXZ.js", "scene112-pbr-transmission-ext-BqJdYPwP.js", "scene112-rgbd-decode-CCG7-bTA.js", "scene112-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene114.json b/lab/public/bundle/manifest/scene114.json index 6fb3881bf5..2412e7ecc6 100644 --- a/lab/public/bundle/manifest/scene114.json +++ b/lab/public/bundle/manifest/scene114.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene114-morph-fragment-DLjixOml.js", - "scene114-pbr-renderable-Bz1zsS0O.js", + "scene114-pbr-renderable-BX6LhQie.js", "scene114-pbr-template-ext-CiHpOV5G.js", "scene114-singlelight-hemispheric-wgsl-CEVgMDLE.js", "scene114-skeleton-fragment-qheSzR-l.js", diff --git a/lab/public/bundle/manifest/scene115.json b/lab/public/bundle/manifest/scene115.json index d143cac0de..1f4332fe73 100644 --- a/lab/public/bundle/manifest/scene115.json +++ b/lab/public/bundle/manifest/scene115.json @@ -10,17 +10,17 @@ "scene115-gltf-color-normalize-4-WGzPL2.js", "scene115-gltf-feature-animations-DOqxEAHR.js", "scene115-gltf-feature-morph-DLWJRlR1.js", - "scene115-gltf-feature-registry-DOkQefUu.js", + "scene115-gltf-feature-registry-BEvgU6Ic.js", "scene115-gltf-feature-skeleton-BktZKWC9.js", "scene115-gltf-json-asset-DXBgrt5f.js", "scene115-morph-fragment-BbCT_r5m.js", "scene115-morph-fragment-core-CTaOwZva.js", - "scene115-pbr-renderable-CLfKUliX.js", + "scene115-pbr-renderable-DekRijt3.js", "scene115-pbr-template-ext-CiHpOV5G.js", "scene115-shader-composer-DishuYLa.js", "scene115-singlelight-hemispheric-wgsl-Cyo3l2OW.js", "scene115-skeleton-fragment-B0VEBZFR.js", - "scene115-standard-renderable-CBK6ow84.js", + "scene115-standard-renderable-B7MttfBJ.js", "scene115-wgsl-fog-m3PkjS7i.js", "scene115.js" ] diff --git a/lab/public/bundle/manifest/scene116.json b/lab/public/bundle/manifest/scene116.json index d5bfb88181..58742019f9 100644 --- a/lab/public/bundle/manifest/scene116.json +++ b/lab/public/bundle/manifest/scene116.json @@ -3,7 +3,7 @@ "gzipKB": 31.3, "ignoredRawKB": 0, "runtimeChunks": [ - "scene116-pbr-renderable-B_c-26sZ.js", + "scene116-pbr-renderable-oquZ2-UC.js", "scene116-shader-composer-Bga-tI-0.js", "scene116-singlelight-hemispheric-wgsl-hXzjd_qm.js", "scene116-standard-renderable-B6DFgoG2.js", diff --git a/lab/public/bundle/manifest/scene118.json b/lab/public/bundle/manifest/scene118.json index ea57222713..27d8b708ba 100644 --- a/lab/public/bundle/manifest/scene118.json +++ b/lab/public/bundle/manifest/scene118.json @@ -6,7 +6,7 @@ "scene118-billboard-pick-pipeline-efBlU1Ns.js", "scene118-billboard-renderable-BMoinrEu.js", "scene118-scene-uniforms-FTYH6Ct0.js", - "scene118-standard-renderable-IusTupZx.js", + "scene118-standard-renderable-DlgYfS07.js", "scene118.js" ] } diff --git a/lab/public/bundle/manifest/scene12.json b/lab/public/bundle/manifest/scene12.json index 46f01af3f3..0fb91dae18 100644 --- a/lab/public/bundle/manifest/scene12.json +++ b/lab/public/bundle/manifest/scene12.json @@ -1,17 +1,17 @@ { "rawKB": 103.8, - "gzipKB": 42.3, + "gzipKB": 42.4, "ignoredRawKB": 0, "runtimeChunks": [ "scene12-create-skeleton-yE34pa8K.js", "scene12-generate-mipmaps-WJd2dKx2.js", "scene12-gltf-animation-BQj3RAlu.js", "scene12-gltf-feature-animations-Db5mzwHK.js", - "scene12-gltf-feature-registry-Dk_apcT-.js", + "scene12-gltf-feature-registry-LgZ-yr59.js", "scene12-gltf-feature-skeleton-eatI5LFE.js", "scene12-gltf-glb-parser-BrsOlymf.js", "scene12-ibl-fragment-CeRrvT96.js", - "scene12-pbr-renderable-jbhRzGSk.js", + "scene12-pbr-renderable-Bs7yCqAw.js", "scene12-reflectance-fragment-BESl-7E9.js", "scene12-rgbd-decode-BLiTLohS.js", "scene12-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene122.json b/lab/public/bundle/manifest/scene122.json index f31f401b7a..743dbcbcd8 100644 --- a/lab/public/bundle/manifest/scene122.json +++ b/lab/public/bundle/manifest/scene122.json @@ -3,7 +3,7 @@ "gzipKB": 19.5, "ignoredRawKB": 0, "runtimeChunks": [ - "scene122-gaussian-splatting-pipeline-sh-DCM3WMGs.js", + "scene122-gaussian-splatting-pipeline-sh-7Pe9evxc.js", "scene122.js" ] } diff --git a/lab/public/bundle/manifest/scene123.json b/lab/public/bundle/manifest/scene123.json index 63137b666e..65a48aa89f 100644 --- a/lab/public/bundle/manifest/scene123.json +++ b/lab/public/bundle/manifest/scene123.json @@ -3,7 +3,7 @@ "gzipKB": 18.4, "ignoredRawKB": 0, "runtimeChunks": [ - "scene123-gaussian-splatting-pipeline-sh-DqvbGZYF.js", + "scene123-gaussian-splatting-pipeline-sh-VanKw4Jm.js", "scene123.js" ] } diff --git a/lab/public/bundle/manifest/scene124.json b/lab/public/bundle/manifest/scene124.json index 69d2adf6a1..f688f6e2b6 100644 --- a/lab/public/bundle/manifest/scene124.json +++ b/lab/public/bundle/manifest/scene124.json @@ -3,7 +3,7 @@ "gzipKB": 20.5, "ignoredRawKB": 0, "runtimeChunks": [ - "scene124-gaussian-splatting-pipeline-sh-DYZuF8gE.js", + "scene124-gaussian-splatting-pipeline-sh-CoCXUHtI.js", "scene124-splat-ply-compressed-4vnSZMqI.js", "scene124.js" ] diff --git a/lab/public/bundle/manifest/scene127.json b/lab/public/bundle/manifest/scene127.json index 97abd23de6..ad70f1fb58 100644 --- a/lab/public/bundle/manifest/scene127.json +++ b/lab/public/bundle/manifest/scene127.json @@ -3,7 +3,7 @@ "gzipKB": 23.5, "ignoredRawKB": 0, "runtimeChunks": [ - "scene127-shader-renderable-C5moVC12.js", + "scene127-shader-renderable-CRu5OTTS.js", "scene127-uniform-copy-batch-C6lLhyWE.js", "scene127.js" ] diff --git a/lab/public/bundle/manifest/scene128.json b/lab/public/bundle/manifest/scene128.json index 7e119e27b2..1ee97d8a33 100644 --- a/lab/public/bundle/manifest/scene128.json +++ b/lab/public/bundle/manifest/scene128.json @@ -3,7 +3,7 @@ "gzipKB": 23.5, "ignoredRawKB": 0, "runtimeChunks": [ - "scene128-shader-renderable-Cm1fTjZA.js", + "scene128-shader-renderable-BfCRPVDX.js", "scene128-uniform-copy-batch-B2JNqIj3.js", "scene128.js" ] diff --git a/lab/public/bundle/manifest/scene129.json b/lab/public/bundle/manifest/scene129.json index 26f90ee55e..f0bdcd2e02 100644 --- a/lab/public/bundle/manifest/scene129.json +++ b/lab/public/bundle/manifest/scene129.json @@ -1,10 +1,10 @@ { - "rawKB": 84.7, - "gzipKB": 32.1, + "rawKB": 85, + "gzipKB": 32.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene129-gs-picking-pipeline-RhdQBaXc.js", - "scene129-standard-renderable-DvSP5bxO.js", + "scene129-standard-renderable-BxnuGE20.js", "scene129.js" ] } diff --git a/lab/public/bundle/manifest/scene13.json b/lab/public/bundle/manifest/scene13.json index a14502fe8d..73b1875f70 100644 --- a/lab/public/bundle/manifest/scene13.json +++ b/lab/public/bundle/manifest/scene13.json @@ -7,7 +7,7 @@ "scene13-generate-mipmaps-BYu5QYgJ.js", "scene13-gltf-glb-parser-HP2v-3hE.js", "scene13-ibl-fragment-CeRrvT96.js", - "scene13-pbr-renderable-DHp41hqi.js", + "scene13-pbr-renderable-Bkz74J_N.js", "scene13-rgbd-decode-CmtmNnLb.js", "scene13-scene-uniforms-FTYH6Ct0.js", "scene13-singlelight-hemispheric-wgsl-D1uhnT_s.js", diff --git a/lab/public/bundle/manifest/scene14.json b/lab/public/bundle/manifest/scene14.json index 2827bcae76..29f752676d 100644 --- a/lab/public/bundle/manifest/scene14.json +++ b/lab/public/bundle/manifest/scene14.json @@ -9,7 +9,7 @@ "scene14-generate-mipmaps-Drlt5FNB.js", "scene14-gltf-glb-parser-B--vN_CU.js", "scene14-ibl-fragment-CeRrvT96.js", - "scene14-pbr-renderable-BfteWe4X.js", + "scene14-pbr-renderable-DnswXng1.js", "scene14-rgbd-decode-BdaP5z1T.js", "scene14-scene-uniforms-FTYH6Ct0.js", "scene14-singlelight-hemispheric-wgsl-Q-eHTfjt.js", diff --git a/lab/public/bundle/manifest/scene141.json b/lab/public/bundle/manifest/scene141.json index 3e50bcdc93..b1abdcd9fd 100644 --- a/lab/public/bundle/manifest/scene141.json +++ b/lab/public/bundle/manifest/scene141.json @@ -15,7 +15,7 @@ "scene141-node-registry-BHrQQQWh.js", "scene141-node-renderable-BMALB2gu.js", "scene141-node-shadow-BscttRKr.js", - "scene141-pbr-renderable-BbElQtt3.js", + "scene141-pbr-renderable-CC0peA-f.js", "scene141-pbr-shadow-fragment-DHDAZ1HL.js", "scene141-shader-composer-BEJi2Ku9.js", "scene141-shadow-fragment-core-Dbe1xNkt.js", diff --git a/lab/public/bundle/manifest/scene143.json b/lab/public/bundle/manifest/scene143.json index 1e2c9c6f1d..bd066d4e47 100644 --- a/lab/public/bundle/manifest/scene143.json +++ b/lab/public/bundle/manifest/scene143.json @@ -6,7 +6,7 @@ "scene143-generate-mipmaps-CiyhUZPA.js", "scene143-normal-map-fragment-BkmuQI98.js", "scene143-point-light-CHHO5fr1.js", - "scene143-standard-renderable-DDynxUoW.js", + "scene143-standard-renderable-Buds7pHK.js", "scene143-std-ambient-fragment-CU5U7ir2.js", "scene143-std-opacity-fragment-CbaZz9IY.js", "scene143-std-reflection-fragment-BMQE51J5.js", diff --git a/lab/public/bundle/manifest/scene144.json b/lab/public/bundle/manifest/scene144.json index 227ffdffe8..0631532dde 100644 --- a/lab/public/bundle/manifest/scene144.json +++ b/lab/public/bundle/manifest/scene144.json @@ -1,6 +1,6 @@ { "rawKB": 112.7, - "gzipKB": 45.8, + "gzipKB": 45.7, "ignoredRawKB": 0, "runtimeChunks": [ "scene144-create-skeleton-DalWacs1.js", @@ -9,16 +9,16 @@ "scene144-gltf-ext-spec-gloss-D60sFZYX.js", "scene144-gltf-feature-animations-CvLpEQNh.js", "scene144-gltf-feature-extras-VHPteGay.js", - "scene144-gltf-feature-registry-D0rf4k_c.js", + "scene144-gltf-feature-registry-CPT4H0lT.js", "scene144-gltf-feature-skeleton-Dvlpv1pW.js", "scene144-gltf-glb-parser-CXf6hcyj.js", "scene144-gltf-pbr-builder-ext-P-UpNEbO.js", "scene144-ibl-fragment-CeRrvT96.js", - "scene144-pbr-renderable-CSZIkMkr.js", + "scene144-pbr-renderable-CslOxHq9.js", "scene144-pbr-template-ext-CiHpOV5G.js", "scene144-rgbd-decode-DNSQ_egC.js", "scene144-scene-uniforms-FTYH6Ct0.js", - "scene144-skeleton-fragment-CTAGnqcL.js", + "scene144-skeleton-fragment-QEj2Gfvn.js", "scene144-texture-2d-DQo3n8D6.js", "scene144.js" ] diff --git a/lab/public/bundle/manifest/scene145.json b/lab/public/bundle/manifest/scene145.json index 2057d7301c..2bb21b3d2d 100644 --- a/lab/public/bundle/manifest/scene145.json +++ b/lab/public/bundle/manifest/scene145.json @@ -6,7 +6,7 @@ "scene145-bake-local-matrix-67U-IT8C.js", "scene145-cube-texture-vbJP4eNk.js", "scene145-generate-mipmaps-Db4M86C8.js", - "scene145-geometry-view-Cu0n7NOB.js", + "scene145-geometry-view-RVgllDdK.js", "scene145-mesh-features-Dnub8q45.js", "scene145-mip-count-CdXF1U-X.js", "scene145-parse-camera-BXBDIYdM.js", @@ -20,7 +20,7 @@ "scene145-std-opacity-fragment-F2uWm_km.js", "scene145-std-specular-fragment-DBvtJbxm.js", "scene145-thin-instance-fragment-CBn2miAC.js", - "scene145-thin-instance-gpu-C_0KMJ2M.js", + "scene145-thin-instance-gpu-mihMDh58.js", "scene145-ubo-layout-Cs5YvLYZ.js", "scene145-wgsl-fog-m3PkjS7i.js", "scene145.js" diff --git a/lab/public/bundle/manifest/scene146.json b/lab/public/bundle/manifest/scene146.json index c0854c4f98..d9429ecf27 100644 --- a/lab/public/bundle/manifest/scene146.json +++ b/lab/public/bundle/manifest/scene146.json @@ -9,8 +9,8 @@ "scene146-generate-mipmaps-DEgXRuY9.js", "scene146-gltf-json-asset-oCUO4rUK.js", "scene146-ibl-fragment-CeRrvT96.js", - "scene146-pbr-geometry-view-DRije4aO.js", - "scene146-pbr-renderable-CM2-TwHP.js", + "scene146-pbr-geometry-view-DTUjzrDW.js", + "scene146-pbr-renderable-DWEht6kL.js", "scene146-rgbd-decode-DZYt7kUp.js", "scene146-scene-uniforms-FTYH6Ct0.js", "scene146-shader-composer-DOjPUQGI.js", diff --git a/lab/public/bundle/manifest/scene147.json b/lab/public/bundle/manifest/scene147.json index e04af10bf3..526ab1b568 100644 --- a/lab/public/bundle/manifest/scene147.json +++ b/lab/public/bundle/manifest/scene147.json @@ -6,12 +6,12 @@ "scene147-directional-light-Bhj3CbsK.js", "scene147-generate-mipmaps-7FMQd9Gb.js", "scene147-gltf-feature-lights-punctual-CwjriEB4.js", - "scene147-gltf-feature-registry-gvU-AElm.js", + "scene147-gltf-feature-registry-DbI80NZj.js", "scene147-gltf-glb-parser-1VrmmUS3.js", "scene147-gltf-light-pointer-state-B7kFTRxK.js", "scene147-multilight-wgsl-EMm1KlC7.js", - "scene147-pbr-geometry-view-DgZ0CrLc.js", - "scene147-pbr-renderable-CJ6XLXcG.js", + "scene147-pbr-geometry-view-BxfBvhNp.js", + "scene147-pbr-renderable-DQJ9qETI.js", "scene147-shader-composer-B51nefZQ.js", "scene147-ubo-layout-Cs5YvLYZ.js", "scene147.js" diff --git a/lab/public/bundle/manifest/scene148.json b/lab/public/bundle/manifest/scene148.json index 932eed8604..cfc9bff770 100644 --- a/lab/public/bundle/manifest/scene148.json +++ b/lab/public/bundle/manifest/scene148.json @@ -6,11 +6,11 @@ "scene148-directional-light-UoLzTD31.js", "scene148-generate-mipmaps-CfuW6q-h.js", "scene148-gltf-feature-lights-punctual-BTZaThsN.js", - "scene148-gltf-feature-registry-BSpqcacR.js", + "scene148-gltf-feature-registry-DeKAPZ8i.js", "scene148-gltf-glb-parser-BR2EHjxU.js", "scene148-gltf-light-pointer-state-B7kFTRxK.js", - "scene148-pbr-geometry-view-D2cVn9Ln.js", - "scene148-pbr-renderable-DlBJCtKq.js", + "scene148-pbr-geometry-view-D_FvC5hZ.js", + "scene148-pbr-renderable-ydLbSnFq.js", "scene148-shader-composer-CUoJ8Zu3.js", "scene148-singlelight-hemispheric-wgsl-BLueMPfJ.js", "scene148-ubo-layout-Cs5YvLYZ.js", diff --git a/lab/public/bundle/manifest/scene149.json b/lab/public/bundle/manifest/scene149.json index 9579557371..419c329789 100644 --- a/lab/public/bundle/manifest/scene149.json +++ b/lab/public/bundle/manifest/scene149.json @@ -9,7 +9,7 @@ "scene149-generate-mipmaps-gtcbk4S2.js", "scene149-geometry-texture-output-BfeQftmX.js", "scene149-gltf-feature-lights-punctual-D7xjFqdg.js", - "scene149-gltf-feature-registry-Dlc3W09a.js", + "scene149-gltf-feature-registry-SQECedy7.js", "scene149-gltf-glb-parser-CpRY-0L4.js", "scene149-gltf-light-pointer-state-B7kFTRxK.js", "scene149-input-block-BTUtjdzr.js", diff --git a/lab/public/bundle/manifest/scene152.json b/lab/public/bundle/manifest/scene152.json index 4cb690e96a..d87b7a8473 100644 --- a/lab/public/bundle/manifest/scene152.json +++ b/lab/public/bundle/manifest/scene152.json @@ -9,13 +9,13 @@ "scene152-gltf-ext-spec-gloss-D60sFZYX.js", "scene152-gltf-feature-animations-B_C5kwG5.js", "scene152-gltf-feature-extras-VHPteGay.js", - "scene152-gltf-feature-registry-DSkVn3FC.js", + "scene152-gltf-feature-registry-DqnU8Z2K.js", "scene152-gltf-feature-skeleton-CUsQ3cht.js", "scene152-gltf-glb-parser-Dr_B2hXm.js", "scene152-gltf-sampler-desc-D0Q2VM93.js", - "scene152-pbr-renderable-w5bTx4T9.js", + "scene152-pbr-renderable-Mu7xwZuv.js", "scene152-singlelight-hemispheric-wgsl-CG2laBNj.js", - "scene152-skeleton-fragment-BYBIZTbR.js", + "scene152-skeleton-fragment-C6N5jSsD.js", "scene152.js" ] } diff --git a/lab/public/bundle/manifest/scene157.json b/lab/public/bundle/manifest/scene157.json index 9a2ae6411f..a21ca7c60c 100644 --- a/lab/public/bundle/manifest/scene157.json +++ b/lab/public/bundle/manifest/scene157.json @@ -7,12 +7,12 @@ "scene157-generate-mipmaps-BYHWBGCp.js", "scene157-gltf-animation-PnQa26Zh.js", "scene157-gltf-feature-animations-at7-WE4C.js", - "scene157-gltf-feature-registry-0o9PKwj2.js", + "scene157-gltf-feature-registry-BoYSJUOM.js", "scene157-gltf-feature-skeleton-Cxp-WFUs.js", "scene157-gltf-glb-parser-DZ4Xgxsa.js", "scene157-multilight-wgsl-BVR6TJvC.js", - "scene157-pbr-renderable-BV80o8HT.js", - "scene157-skeleton-fragment-DF9JSy2m.js", + "scene157-pbr-renderable-DwnXL0Wo.js", + "scene157-skeleton-fragment-B6SLfVGs.js", "scene157.js" ] } diff --git a/lab/public/bundle/manifest/scene158.json b/lab/public/bundle/manifest/scene158.json index 9cc2dd846b..a153e085f3 100644 --- a/lab/public/bundle/manifest/scene158.json +++ b/lab/public/bundle/manifest/scene158.json @@ -7,12 +7,12 @@ "scene158-generate-mipmaps-bNvmU0dI.js", "scene158-gltf-animation-D4bTYTv1.js", "scene158-gltf-feature-animations-BPofhcKw.js", - "scene158-gltf-feature-registry-I9qW8gaC.js", + "scene158-gltf-feature-registry-BGVQtjQo.js", "scene158-gltf-feature-skeleton-D2diH2X_.js", "scene158-gltf-glb-parser-BhqojsL9.js", "scene158-multilight-wgsl-DtA6qLNj.js", - "scene158-pbr-renderable-Dr78ma9D.js", - "scene158-skeleton-fragment-ICbkGaQ4.js", + "scene158-pbr-renderable-kv2xTOAh.js", + "scene158-skeleton-fragment-XRzh2Dkk.js", "scene158.js" ] } diff --git a/lab/public/bundle/manifest/scene159.json b/lab/public/bundle/manifest/scene159.json index 794dbfe947..af8f68eec4 100644 --- a/lab/public/bundle/manifest/scene159.json +++ b/lab/public/bundle/manifest/scene159.json @@ -3,7 +3,7 @@ "gzipKB": 15.6, "ignoredRawKB": 0, "runtimeChunks": [ - "scene159-shader-renderable-CAII7wq8.js", + "scene159-shader-renderable-BBdRWQTx.js", "scene159.js" ] } diff --git a/lab/public/bundle/manifest/scene160.json b/lab/public/bundle/manifest/scene160.json index e25853cb55..c40584b3cf 100644 --- a/lab/public/bundle/manifest/scene160.json +++ b/lab/public/bundle/manifest/scene160.json @@ -3,7 +3,7 @@ "gzipKB": 16.3, "ignoredRawKB": 0, "runtimeChunks": [ - "scene160-shader-renderable-BHJhdNXp.js", + "scene160-shader-renderable-BrUIqS_X.js", "scene160.js" ] } diff --git a/lab/public/bundle/manifest/scene161.json b/lab/public/bundle/manifest/scene161.json index 2732d6795d..7ffcd74c33 100644 --- a/lab/public/bundle/manifest/scene161.json +++ b/lab/public/bundle/manifest/scene161.json @@ -3,7 +3,7 @@ "gzipKB": 15.8, "ignoredRawKB": 0, "runtimeChunks": [ - "scene161-shader-renderable-C_nLERpD.js", + "scene161-shader-renderable-BuB91IGe.js", "scene161.js" ] } diff --git a/lab/public/bundle/manifest/scene162.json b/lab/public/bundle/manifest/scene162.json index 83148dfb5b..e6b64bfc1a 100644 --- a/lab/public/bundle/manifest/scene162.json +++ b/lab/public/bundle/manifest/scene162.json @@ -3,7 +3,7 @@ "gzipKB": 15.6, "ignoredRawKB": 0, "runtimeChunks": [ - "scene162-shader-renderable-C8bEj6dU.js", + "scene162-shader-renderable-Cif9P8a1.js", "scene162.js" ] } diff --git a/lab/public/bundle/manifest/scene163.json b/lab/public/bundle/manifest/scene163.json index ba5e294ba1..9a042b1fe7 100644 --- a/lab/public/bundle/manifest/scene163.json +++ b/lab/public/bundle/manifest/scene163.json @@ -3,7 +3,7 @@ "gzipKB": 15.4, "ignoredRawKB": 0, "runtimeChunks": [ - "scene163-shader-renderable-DMuvkkvJ.js", + "scene163-shader-renderable-CPckCf4W.js", "scene163.js" ] } diff --git a/lab/public/bundle/manifest/scene164.json b/lab/public/bundle/manifest/scene164.json index 8d6806be26..ec4f95f29e 100644 --- a/lab/public/bundle/manifest/scene164.json +++ b/lab/public/bundle/manifest/scene164.json @@ -10,14 +10,14 @@ "scene164-gltf-color-normalize-4-WGzPL2.js", "scene164-gltf-feature-animations-CoptQtIt.js", "scene164-gltf-feature-morph-Dcma2yce.js", - "scene164-gltf-feature-registry-KiGFfhWk.js", + "scene164-gltf-feature-registry-BdDxXmfV.js", "scene164-gltf-feature-skeleton-DRjn81A9.js", "scene164-gltf-json-asset-B2lSB7pA.js", - "scene164-morph-fragment-DqmjrD9s.js", - "scene164-pbr-renderable-BuCBUDuA.js", + "scene164-morph-fragment-BMIAeeto.js", + "scene164-pbr-renderable-DL0UektJ.js", "scene164-pbr-template-ext-CiHpOV5G.js", "scene164-singlelight-hemispheric-wgsl-L03Bnk5I.js", - "scene164-skeleton-fragment-iGQ0nIRQ.js", + "scene164-skeleton-fragment-DY_ucgJY.js", "scene164.js" ] } diff --git a/lab/public/bundle/manifest/scene165.json b/lab/public/bundle/manifest/scene165.json index 879bbfc66f..679a9f747e 100644 --- a/lab/public/bundle/manifest/scene165.json +++ b/lab/public/bundle/manifest/scene165.json @@ -3,7 +3,7 @@ "gzipKB": 17.9, "ignoredRawKB": 0, "runtimeChunks": [ - "scene165-shader-renderable-B02kzevN.js", + "scene165-shader-renderable-DPbJ5CtJ.js", "scene165-shader-thin-instance-CVyyUQwx.js", "scene165-thin-instance-gpu-BE5k2hGl.js", "scene165.js" diff --git a/lab/public/bundle/manifest/scene17.json b/lab/public/bundle/manifest/scene17.json index ab1c81b34c..c0116a7a71 100644 --- a/lab/public/bundle/manifest/scene17.json +++ b/lab/public/bundle/manifest/scene17.json @@ -5,13 +5,13 @@ "runtimeChunks": [ "scene17-generate-mipmaps-DNSbr_z3.js", "scene17-ibl-fragment-CeRrvT96.js", - "scene17-pbr-renderable-B18-VSSF.js", + "scene17-pbr-renderable-CPGSHa0q.js", "scene17-rgbd-decode-aydmE8bD.js", "scene17-shader-composer-Bga-tI-0.js", "scene17-singlelight-hemispheric-wgsl-DpzOMoaT.js", "scene17-standard-renderable-C1KIECNI.js", "scene17-thin-instance-fragment-CBn2miAC.js", - "scene17-thin-instance-gpu-d94c-_y4.js", + "scene17-thin-instance-gpu-Bex3dl6z.js", "scene17-wgsl-fog-m3PkjS7i.js", "scene17.js" ] diff --git a/lab/public/bundle/manifest/scene171.json b/lab/public/bundle/manifest/scene171.json index 19c3b6a8e5..7a6099d5a4 100644 --- a/lab/public/bundle/manifest/scene171.json +++ b/lab/public/bundle/manifest/scene171.json @@ -5,11 +5,11 @@ "runtimeChunks": [ "scene171-generate-mipmaps-Cpc8PT2N.js", "scene171-gltf-glb-parser-COBAbifQ.js", - "scene171-pbr-renderable-CBudRXIZ.js", + "scene171-pbr-renderable-BJydaPou.js", "scene171-recast-navigation-CYBQI-zY-Dry_z7x_.js", "scene171-shader-composer-DishuYLa.js", "scene171-singlelight-hemispheric-wgsl-BfbzAtlS.js", - "scene171-standard-renderable-DCPQcLjO.js", + "scene171-standard-renderable-2gkHd3VO.js", "scene171-wgsl-fog-m3PkjS7i.js", "scene171.js" ] diff --git a/lab/public/bundle/manifest/scene173.json b/lab/public/bundle/manifest/scene173.json index 92881a46cc..1b2d42c31a 100644 --- a/lab/public/bundle/manifest/scene173.json +++ b/lab/public/bundle/manifest/scene173.json @@ -1,6 +1,6 @@ { - "rawKB": 62.3, - "gzipKB": 24.2, + "rawKB": 62.7, + "gzipKB": 24.3, "ignoredRawKB": 1485.2, "runtimeChunks": [ "scene173-recast-navigation-CYBQI-zY-Dry_z7x_.js", diff --git a/lab/public/bundle/manifest/scene174.json b/lab/public/bundle/manifest/scene174.json index 03bf3271b8..d7bc8807c8 100644 --- a/lab/public/bundle/manifest/scene174.json +++ b/lab/public/bundle/manifest/scene174.json @@ -5,11 +5,11 @@ "runtimeChunks": [ "scene174-generate-mipmaps-CU0CttiE.js", "scene174-gltf-glb-parser-V10AO6Mf.js", - "scene174-pbr-renderable-CSIBKar6.js", + "scene174-pbr-renderable-4xgmtIZQ.js", "scene174-recast-navigation-CYBQI-zY-Dry_z7x_.js", "scene174-shader-composer-DishuYLa.js", "scene174-singlelight-hemispheric-wgsl-DYuLXC4p.js", - "scene174-standard-renderable-hP_Zd-jO.js", + "scene174-standard-renderable-DjCYxIuA.js", "scene174-wgsl-fog-m3PkjS7i.js", "scene174.js" ] diff --git a/lab/public/bundle/manifest/scene175.json b/lab/public/bundle/manifest/scene175.json index d78355af98..852bdc14e3 100644 --- a/lab/public/bundle/manifest/scene175.json +++ b/lab/public/bundle/manifest/scene175.json @@ -5,11 +5,11 @@ "runtimeChunks": [ "scene175-generate-mipmaps-Nb0hz7dp.js", "scene175-gltf-glb-parser-BmlEZ507.js", - "scene175-pbr-renderable-BuP2umVH.js", + "scene175-pbr-renderable-C23vM3Bk.js", "scene175-recast-navigation-CYBQI-zY-Dry_z7x_.js", "scene175-shader-composer-DishuYLa.js", "scene175-singlelight-hemispheric-wgsl-C8FJZIgR.js", - "scene175-standard-renderable-CQ7ELH1w.js", + "scene175-standard-renderable-DbWBVzVf.js", "scene175-wgsl-fog-m3PkjS7i.js", "scene175.js" ] diff --git a/lab/public/bundle/manifest/scene176.json b/lab/public/bundle/manifest/scene176.json index 6dd39a3c61..2348e07aad 100644 --- a/lab/public/bundle/manifest/scene176.json +++ b/lab/public/bundle/manifest/scene176.json @@ -1,17 +1,17 @@ { "rawKB": 104.3, - "gzipKB": 42.1, + "gzipKB": 42, "ignoredRawKB": 0, "runtimeChunks": [ "scene176-generate-mipmaps-LBrT9Lsp.js", "scene176-gltf-ext-dielectric-C6hvXXiF.js", "scene176-gltf-feature-extras-VHPteGay.js", - "scene176-gltf-feature-registry-BqV6SdNM.js", + "scene176-gltf-feature-registry-DfPK9ENs.js", "scene176-gltf-json-asset-C-LYB2bT.js", "scene176-ibl-fragment-CeRrvT96.js", "scene176-ibl-skybox-wgsl-CFIBOHHx.js", "scene176-pbr-refraction-Tg8x1uZU.js", - "scene176-pbr-renderable-DP4X61d0.js", + "scene176-pbr-renderable-CHT60vvA.js", "scene176-pbr-transmission-ext-Dipc1EBq.js", "scene176-reflectance-fragment-X3VnKmHR.js", "scene176-rgbd-decode-BNh5jH-x.js", diff --git a/lab/public/bundle/manifest/scene177.json b/lab/public/bundle/manifest/scene177.json index 691aa533de..759a61718f 100644 --- a/lab/public/bundle/manifest/scene177.json +++ b/lab/public/bundle/manifest/scene177.json @@ -6,7 +6,7 @@ "scene177-ibl-fragment-CeRrvT96.js", "scene177-ibl-skybox-wgsl-CFIBOHHx.js", "scene177-iridescence-fragment-DbYifzec.js", - "scene177-pbr-renderable-buJqqz2v.js", + "scene177-pbr-renderable-72_fjIgp.js", "scene177-rgbd-decode-DgyvGBcz.js", "scene177-scene-uniforms-FTYH6Ct0.js", "scene177.js" diff --git a/lab/public/bundle/manifest/scene178.json b/lab/public/bundle/manifest/scene178.json index fcc59bdedb..e048c6a3b0 100644 --- a/lab/public/bundle/manifest/scene178.json +++ b/lab/public/bundle/manifest/scene178.json @@ -5,12 +5,12 @@ "runtimeChunks": [ "scene178-generate-mipmaps-BGzDQPIy.js", "scene178-gltf-ext-iridescence-b4LVniAM.js", - "scene178-gltf-feature-registry-DARfE0Ov.js", + "scene178-gltf-feature-registry-DHU0AOnk.js", "scene178-gltf-glb-parser-d5msxooD.js", "scene178-ibl-fragment-CeRrvT96.js", "scene178-ibl-skybox-wgsl-CFIBOHHx.js", "scene178-iridescence-fragment-DhGVbSHo.js", - "scene178-pbr-renderable-DPpo1Sqy.js", + "scene178-pbr-renderable-CZ5KzWRl.js", "scene178-rgbd-decode-BsmWs_Sj.js", "scene178-scene-uniforms-FTYH6Ct0.js", "scene178.js" diff --git a/lab/public/bundle/manifest/scene179.json b/lab/public/bundle/manifest/scene179.json index 853a2cc56c..ab74b2f214 100644 --- a/lab/public/bundle/manifest/scene179.json +++ b/lab/public/bundle/manifest/scene179.json @@ -6,7 +6,7 @@ "scene179-alpha-test-fragment-BaX_J1Vy.js", "scene179-generate-mipmaps-5DBI-Po-.js", "scene179-gltf-json-asset-DVQojHWg.js", - "scene179-pbr-renderable-EWUr4TXI.js", + "scene179-pbr-renderable-DD-bI5Bi.js", "scene179.js" ] } diff --git a/lab/public/bundle/manifest/scene18.json b/lab/public/bundle/manifest/scene18.json index 7eb3bec8eb..5b00332728 100644 --- a/lab/public/bundle/manifest/scene18.json +++ b/lab/public/bundle/manifest/scene18.json @@ -1,6 +1,6 @@ { "rawKB": 60.6, - "gzipKB": 24.2, + "gzipKB": 24.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene18-generate-mipmaps-CORvzSVx.js", diff --git a/lab/public/bundle/manifest/scene19.json b/lab/public/bundle/manifest/scene19.json index 4f7dee63d8..f38cbdb787 100644 --- a/lab/public/bundle/manifest/scene19.json +++ b/lab/public/bundle/manifest/scene19.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene19-clearcoat-fragment-BWTsF3_L.js", "scene19-ibl-fragment-CeRrvT96.js", - "scene19-pbr-renderable-hgV1av6g.js", + "scene19-pbr-renderable-CqXASKzC.js", "scene19-rgbd-decode-Bhk0icKs.js", "scene19-singlelight-hemispheric-wgsl-CWifOPl1.js", "scene19.js" diff --git a/lab/public/bundle/manifest/scene20.json b/lab/public/bundle/manifest/scene20.json index 34150323fa..4a35c392b9 100644 --- a/lab/public/bundle/manifest/scene20.json +++ b/lab/public/bundle/manifest/scene20.json @@ -8,7 +8,7 @@ "scene20-cubemap-skybox-material-B495OxWx.js", "scene20-emissive-fragment-B_pIHOBu.js", "scene20-ibl-fragment-CeRrvT96.js", - "scene20-pbr-renderable-DCcgiJR2.js", + "scene20-pbr-renderable-Dy4Qt4XV.js", "scene20-rgbd-decode-ChiiPMbr.js", "scene20-scene-uniforms-FTYH6Ct0.js", "scene20-singlelight-hemispheric-wgsl-TLZaCFzg.js", diff --git a/lab/public/bundle/manifest/scene205.json b/lab/public/bundle/manifest/scene205.json index 39a7ab4ad3..ffd546ecec 100644 --- a/lab/public/bundle/manifest/scene205.json +++ b/lab/public/bundle/manifest/scene205.json @@ -1,5 +1,5 @@ { - "rawKB": 62.1, + "rawKB": 62.2, "gzipKB": 25.4, "ignoredRawKB": 0, "runtimeChunks": [ @@ -8,7 +8,7 @@ "scene205-floating-origin-BpHUAtCM.js", "scene205-pack-mat4-with-offset-BS-Lz8k7.js", "scene205-scene-uniforms-FTYH6Ct0.js", - "scene205-standard-renderable-7LzvJzOL.js", + "scene205-standard-renderable-B6Epry6m.js", "scene205.js" ] } diff --git a/lab/public/bundle/manifest/scene206.json b/lab/public/bundle/manifest/scene206.json index 169f6fd9a7..6931d82683 100644 --- a/lab/public/bundle/manifest/scene206.json +++ b/lab/public/bundle/manifest/scene206.json @@ -8,7 +8,7 @@ "scene206-floating-origin-B3Cmn-Fn.js", "scene206-pack-mat4-with-offset-BS-Lz8k7.js", "scene206-scene-uniforms-FTYH6Ct0.js", - "scene206-standard-renderable-Dwsxsr4C.js", + "scene206-standard-renderable-ClYZ5I4J.js", "scene206.js" ] } diff --git a/lab/public/bundle/manifest/scene21.json b/lab/public/bundle/manifest/scene21.json index b11e2a4805..1ac229d40b 100644 --- a/lab/public/bundle/manifest/scene21.json +++ b/lab/public/bundle/manifest/scene21.json @@ -8,7 +8,7 @@ "scene21-generate-mipmaps-C6GFB7Gd.js", "scene21-gltf-glb-parser-GCKDEofq.js", "scene21-ibl-fragment-CeRrvT96.js", - "scene21-pbr-renderable-D_epEzEb.js", + "scene21-pbr-renderable-kY7Ha_0H.js", "scene21-rgbd-decode-9DTol7mq.js", "scene21-scene-uniforms-FTYH6Ct0.js", "scene21-sheen-fragment-D_beAYtD.js", diff --git a/lab/public/bundle/manifest/scene210.json b/lab/public/bundle/manifest/scene210.json index bc06d90d94..3efa39e666 100644 --- a/lab/public/bundle/manifest/scene210.json +++ b/lab/public/bundle/manifest/scene210.json @@ -4,12 +4,12 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene210-generate-mipmaps-BeLXoJVY.js", - "scene210-gltf-feature-registry-8A8TfG8a.js", + "scene210-gltf-feature-registry-CMDmy6PO.js", "scene210-gltf-feature-xmp-S8Tl-XNQ.js", "scene210-gltf-glb-parser-fJOQf1ny.js", "scene210-gltf-interleave-D_KaKI90.js", "scene210-gltf-normals-DYhLwXNX.js", - "scene210-pbr-renderable-DP0dtM69.js", + "scene210-pbr-renderable-DBG2IBxf.js", "scene210-singlelight-hemispheric-wgsl-CBBkrTfR.js", "scene210.js" ] diff --git a/lab/public/bundle/manifest/scene211.json b/lab/public/bundle/manifest/scene211.json index 33e41a93d3..458e2ee6d1 100644 --- a/lab/public/bundle/manifest/scene211.json +++ b/lab/public/bundle/manifest/scene211.json @@ -9,14 +9,14 @@ "scene211-gltf-ext-quantization-DTtElLs7.js", "scene211-gltf-feature-animations-CsN6DvHw.js", "scene211-gltf-feature-meshopt-Dt65KW5n.js", - "scene211-gltf-feature-registry-BG_ahr16.js", + "scene211-gltf-feature-registry-1aP5CJx2.js", "scene211-gltf-feature-skeleton-DpuvZ9VR.js", "scene211-gltf-json-asset-CHRQZ19d.js", "scene211-gltf-multi-buffer-Cwm5u1Qq.js", "scene211-gltf-sampler-denorm-LVUVn5SV.js", - "scene211-pbr-renderable-DGabL0fg.js", + "scene211-pbr-renderable-CT68LQR6.js", "scene211-singlelight-hemispheric-wgsl-BuokTPjH.js", - "scene211-skeleton-fragment-BHJcfpd3.js", + "scene211-skeleton-fragment-DqQvfyn0.js", "scene211.js" ] } diff --git a/lab/public/bundle/manifest/scene212.json b/lab/public/bundle/manifest/scene212.json index 7251e54692..2dd17cb888 100644 --- a/lab/public/bundle/manifest/scene212.json +++ b/lab/public/bundle/manifest/scene212.json @@ -5,12 +5,12 @@ "runtimeChunks": [ "scene212-generate-mipmaps-ffZ1zox-.js", "scene212-gltf-ext-dielectric-C6hvXXiF.js", - "scene212-gltf-feature-registry-CNbzPeCE.js", + "scene212-gltf-feature-registry-appQfBmK.js", "scene212-gltf-glb-parser-DpbXuAuC.js", "scene212-ibl-fragment-CeRrvT96.js", "scene212-ibl-skybox-wgsl-CFIBOHHx.js", "scene212-pbr-refraction-DQZVKvEq.js", - "scene212-pbr-renderable-CQrrv8e7.js", + "scene212-pbr-renderable-BsAEWQz2.js", "scene212-pbr-transmission-ext-C0Ra7jTr.js", "scene212-reflectance-fragment-BdYei-sw.js", "scene212-refraction-dispersion-wgsl-BKvBCmZU.js", diff --git a/lab/public/bundle/manifest/scene213.json b/lab/public/bundle/manifest/scene213.json index 18e7335a5a..e53599dd5a 100644 --- a/lab/public/bundle/manifest/scene213.json +++ b/lab/public/bundle/manifest/scene213.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene213-shader-pipeline-cache-Dga-Orlp.js", - "scene213-shader-renderable-DyZNJexF.js", + "scene213-shader-renderable-BTQJ-RWB.js", "scene213-uniform-copy-batch-Be4x2TDU.js", "scene213.js" ] diff --git a/lab/public/bundle/manifest/scene214.json b/lab/public/bundle/manifest/scene214.json index 379d19adb7..a3b11f71cd 100644 --- a/lab/public/bundle/manifest/scene214.json +++ b/lab/public/bundle/manifest/scene214.json @@ -1,5 +1,5 @@ { - "rawKB": 68.4, + "rawKB": 68.5, "gzipKB": 27.1, "ignoredRawKB": 0, "runtimeChunks": [ diff --git a/lab/public/bundle/manifest/scene215.json b/lab/public/bundle/manifest/scene215.json index de08433808..691296b377 100644 --- a/lab/public/bundle/manifest/scene215.json +++ b/lab/public/bundle/manifest/scene215.json @@ -1,12 +1,12 @@ { - "rawKB": 80, - "gzipKB": 32.2, + "rawKB": 80.1, + "gzipKB": 32.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene215-material-view-Dua1bl7W.js", "scene215-multilight-wgsl-DjVmEAHV.js", "scene215-no-color-view-DS70AbGo.js", - "scene215-pbr-renderable-EjsAGxuk.js", + "scene215-pbr-renderable-DYHxNm0u.js", "scene215-pbr-shadow-fragment-CoRW6XHj.js", "scene215-shadow-task-CPnE-PjD.js", "scene215-singlelight-directional-wgsl-DzV54v_g.js", diff --git a/lab/public/bundle/manifest/scene216.json b/lab/public/bundle/manifest/scene216.json index d4627c9299..25e10edd28 100644 --- a/lab/public/bundle/manifest/scene216.json +++ b/lab/public/bundle/manifest/scene216.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene216-pbr-fog-wgsl-BuLKZvgv.js", - "scene216-pbr-renderable-Dn1fMSg4.js", + "scene216-pbr-renderable-BB1NgsPW.js", "scene216-singlelight-hemispheric-wgsl-BXqEjdZQ.js", "scene216.js" ] diff --git a/lab/public/bundle/manifest/scene217.json b/lab/public/bundle/manifest/scene217.json index c1c4b45807..d67191faf9 100644 --- a/lab/public/bundle/manifest/scene217.json +++ b/lab/public/bundle/manifest/scene217.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene217-multilight-wgsl-hb_hGU0D.js", - "scene217-pbr-renderable-CvchOzGq.js", + "scene217-pbr-renderable-DZAu6fjI.js", "scene217-shader-composer-3MUGhbIu.js", "scene217-standard-renderable-DO07_p5s.js", "scene217-wgsl-fog-m3PkjS7i.js", diff --git a/lab/public/bundle/manifest/scene218.json b/lab/public/bundle/manifest/scene218.json index 364a0c80f8..076fb0b7a9 100644 --- a/lab/public/bundle/manifest/scene218.json +++ b/lab/public/bundle/manifest/scene218.json @@ -9,11 +9,11 @@ "scene218-gltf-ext-spec-gloss-D60sFZYX.js", "scene218-gltf-feature-animations-u2H7ejDl.js", "scene218-gltf-feature-extras-VHPteGay.js", - "scene218-gltf-feature-registry-DtTeXbcA.js", + "scene218-gltf-feature-registry-DD6PgX3-.js", "scene218-gltf-feature-skeleton-n7Xbyk0V.js", "scene218-gltf-glb-parser-BXHcqEF1.js", "scene218-gltf-sampler-desc-CS2bAJuk.js", - "scene218-pbr-renderable-B9shVjr7.js", + "scene218-pbr-renderable-B5MQTLvN.js", "scene218-singlelight-hemispheric-wgsl-D4ReIS7e.js", "scene218.js" ] diff --git a/lab/public/bundle/manifest/scene219.json b/lab/public/bundle/manifest/scene219.json index cbebb6a8d1..2974130d75 100644 --- a/lab/public/bundle/manifest/scene219.json +++ b/lab/public/bundle/manifest/scene219.json @@ -13,7 +13,7 @@ "scene219-gltf-feature-skeleton-CpmBaHpL.js", "scene219-gltf-glb-parser-PDyH09Yg.js", "scene219-gltf-sampler-desc-CKWNJQQv.js", - "scene219-pbr-renderable-WUkS1xKq.js", + "scene219-pbr-renderable-oGlQbjux.js", "scene219-singlelight-hemispheric-wgsl-DDoH6CHa.js", "scene219-thin-instance-fragment-CBn2miAC.js", "scene219-thin-instance-gpu-BgRIhZge.js", diff --git a/lab/public/bundle/manifest/scene22.json b/lab/public/bundle/manifest/scene22.json index 0ef1217f32..9e6278819a 100644 --- a/lab/public/bundle/manifest/scene22.json +++ b/lab/public/bundle/manifest/scene22.json @@ -8,7 +8,7 @@ "scene22-material-view-Dua1bl7W.js", "scene22-multilight-wgsl-My9Iptth.js", "scene22-no-color-view-DHPAYos4.js", - "scene22-pbr-renderable-Zulj0hz7.js", + "scene22-pbr-renderable-Bf_neMUz.js", "scene22-pbr-shadow-fragment-CSEDArnD.js", "scene22-pbr-template-gamma-CVmXdy8B.js", "scene22-shader-composer-Bga-tI-0.js", diff --git a/lab/public/bundle/manifest/scene221.json b/lab/public/bundle/manifest/scene221.json index e1fd8afe05..b7dcdf6474 100644 --- a/lab/public/bundle/manifest/scene221.json +++ b/lab/public/bundle/manifest/scene221.json @@ -3,8 +3,8 @@ "gzipKB": 38.6, "ignoredRawKB": 0, "runtimeChunks": [ - "scene221-shader-renderable-e40HDD0m.js", - "scene221-standard-renderable-DpESG6NP.js", + "scene221-shader-renderable-DpGITp4F.js", + "scene221-standard-renderable-DedDiJB_.js", "scene221-swapchain-overlay-ilhN4El_.js", "scene221-ubo-layout-CnrP4RZD.js", "scene221.js" diff --git a/lab/public/bundle/manifest/scene222.json b/lab/public/bundle/manifest/scene222.json index 9becb6c6bb..4d0a54c2b3 100644 --- a/lab/public/bundle/manifest/scene222.json +++ b/lab/public/bundle/manifest/scene222.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene222-index-FlPA6jzr.js", "scene222-shader-pipeline-cache-CEwSrI-G.js", - "scene222-shader-renderable-CDZpSjhF.js", + "scene222-shader-renderable-BljVCk02.js", "scene222-standard-renderable-V34lXsdm.js", "scene222-swapchain-overlay-ilhN4El_.js", "scene222-ubo-layout-Cs5YvLYZ.js", diff --git a/lab/public/bundle/manifest/scene223.json b/lab/public/bundle/manifest/scene223.json index 2d811f389b..f4a2160a58 100644 --- a/lab/public/bundle/manifest/scene223.json +++ b/lab/public/bundle/manifest/scene223.json @@ -1,6 +1,6 @@ { - "rawKB": 64.2, - "gzipKB": 24.3, + "rawKB": 64.6, + "gzipKB": 24.5, "ignoredRawKB": 0, "runtimeChunks": [ "scene223-standard-renderable-W0J5Mh6Q.js", diff --git a/lab/public/bundle/manifest/scene229.json b/lab/public/bundle/manifest/scene229.json index ae63624565..c5486ff97a 100644 --- a/lab/public/bundle/manifest/scene229.json +++ b/lab/public/bundle/manifest/scene229.json @@ -7,7 +7,7 @@ "scene229-generate-mipmaps-Bd46GUr9.js", "scene229-gltf-json-asset-DodZb4Wf.js", "scene229-gltf-normals-DYhLwXNX.js", - "scene229-pbr-renderable-Dx4_fUjm.js", + "scene229-pbr-renderable-YAUP8Tjz.js", "scene229-unlit-fragment-DWtvt7yQ.js", "scene229.js" ] diff --git a/lab/public/bundle/manifest/scene23.json b/lab/public/bundle/manifest/scene23.json index ed74528cb2..d862292b28 100644 --- a/lab/public/bundle/manifest/scene23.json +++ b/lab/public/bundle/manifest/scene23.json @@ -1,6 +1,6 @@ { "rawKB": 78.2, - "gzipKB": 32.4, + "gzipKB": 32.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene23-anisotropy-fragment-CbNkTpSR.js", @@ -8,7 +8,7 @@ "scene23-background-ground-DmmKHN9b.js", "scene23-cubemap-skybox-material-sjF5EPm1.js", "scene23-ibl-fragment-CeRrvT96.js", - "scene23-pbr-renderable-Cl6UhMRU.js", + "scene23-pbr-renderable-CV7Rakha.js", "scene23-rgbd-decode-BeChsxxY.js", "scene23-scene-uniforms-FTYH6Ct0.js", "scene23-wgsl-helpers-BgB2AJrF.js", diff --git a/lab/public/bundle/manifest/scene24.json b/lab/public/bundle/manifest/scene24.json index 63512fcfc4..580025bb03 100644 --- a/lab/public/bundle/manifest/scene24.json +++ b/lab/public/bundle/manifest/scene24.json @@ -8,7 +8,7 @@ "scene24-generate-mipmaps-BaRC2la_.js", "scene24-parse-camera-D4iQaH48.js", "scene24-point-light-Dzvg6X6L.js", - "scene24-standard-renderable-COSPHzrf.js", + "scene24-standard-renderable-DVwP7F_D.js", "scene24-std-ambient-fragment-qLXGuA_5.js", "scene24-std-cube-reflection-fragment-B-EokvPj.js", "scene24-std-opacity-fragment-BMTURaOK.js", diff --git a/lab/public/bundle/manifest/scene240.json b/lab/public/bundle/manifest/scene240.json index f9d318b9f8..b584d13217 100644 --- a/lab/public/bundle/manifest/scene240.json +++ b/lab/public/bundle/manifest/scene240.json @@ -7,12 +7,12 @@ "scene240-generate-mipmaps-BQZ9ivTX.js", "scene240-gltf-animation-aBVjSk7N.js", "scene240-gltf-feature-animations-Cm8MU3RN.js", - "scene240-gltf-feature-registry-L__OYK3a.js", + "scene240-gltf-feature-registry-B-hF6wVk.js", "scene240-gltf-json-asset-C-b8cTJb.js", "scene240-gltf-multi-buffer-Cwm5u1Qq.js", "scene240-gltf-normals-DYhLwXNX.js", "scene240-ibl-fragment-CeRrvT96.js", - "scene240-pbr-renderable-DvLsriQm.js", + "scene240-pbr-renderable-xz_deyJi.js", "scene240-rgbd-decode-2FzzQzZ2.js", "scene240-scene-uniforms-FTYH6Ct0.js", "scene240.js" diff --git a/lab/public/bundle/manifest/scene241.json b/lab/public/bundle/manifest/scene241.json index e900e9e9df..62deeb96d4 100644 --- a/lab/public/bundle/manifest/scene241.json +++ b/lab/public/bundle/manifest/scene241.json @@ -21,7 +21,7 @@ "scene241-gltf-feature-animation-pointer-Ch_iHImp.js", "scene241-gltf-feature-animations-DEv1D7Id.js", "scene241-gltf-feature-lights-punctual-BYIHIGm7.js", - "scene241-gltf-feature-registry-Dhd9yQ5P.js", + "scene241-gltf-feature-registry-IfnG_-ih.js", "scene241-gltf-json-asset-Dn-uYlO4.js", "scene241-gltf-light-pointer-state-B7kFTRxK.js", "scene241-gltf-pbr-builder-ext-B6jL1YDV.js", @@ -30,7 +30,7 @@ "scene241-light-base-BaUj6FJ5.js", "scene241-light-matrix-CtsX4GmG.js", "scene241-pbr-refraction-BhnHiaxK.js", - "scene241-pbr-renderable-19cLGNX3.js", + "scene241-pbr-renderable-4vQdi2vM.js", "scene241-pbr-template-ext-CiHpOV5G.js", "scene241-pbr-transmission-ext-C1CsruQy.js", "scene241-reflectance-fragment-AymjkEuD.js", diff --git a/lab/public/bundle/manifest/scene242.json b/lab/public/bundle/manifest/scene242.json index 060e391aec..ae325bf2f5 100644 --- a/lab/public/bundle/manifest/scene242.json +++ b/lab/public/bundle/manifest/scene242.json @@ -10,10 +10,10 @@ "scene242-gltf-ext-emissive-strength-CButLZnK.js", "scene242-gltf-feature-animation-pointer-tuVf2ksY.js", "scene242-gltf-feature-animations-c59Hsp8m.js", - "scene242-gltf-feature-registry-CFzXPHvI.js", + "scene242-gltf-feature-registry-Djj6uv54.js", "scene242-gltf-json-asset-89J8Oj0_.js", "scene242-ibl-fragment-CeRrvT96.js", - "scene242-pbr-renderable-tK8PEdbm.js", + "scene242-pbr-renderable-CUhjoZLz.js", "scene242-rgbd-decode-DapdOyyV.js", "scene242-scene-uniforms-FTYH6Ct0.js", "scene242-visibility-J3hTZChg.js", diff --git a/lab/public/bundle/manifest/scene243.json b/lab/public/bundle/manifest/scene243.json index f17d4d7caa..d7580b3196 100644 --- a/lab/public/bundle/manifest/scene243.json +++ b/lab/public/bundle/manifest/scene243.json @@ -9,12 +9,12 @@ "scene243-gltf-feature-animations-CX3jw94s.js", "scene243-gltf-feature-extras-VHPteGay.js", "scene243-gltf-feature-morph-BCUQi4W_.js", - "scene243-gltf-feature-registry-BnUIj1bS.js", + "scene243-gltf-feature-registry-CF87d1Fk.js", "scene243-gltf-json-asset-Cgf3mpUp.js", "scene243-gltf-pbr-builder-ext-CxghLmO7.js", "scene243-ibl-fragment-CeRrvT96.js", - "scene243-morph-fragment-C7xsFBq_.js", - "scene243-pbr-renderable-DjDLAo4b.js", + "scene243-morph-fragment-Xu6YsxOl.js", + "scene243-pbr-renderable-DuRK3C-7.js", "scene243-pbr-template-ext-CiHpOV5G.js", "scene243-rgbd-decode-CtyuiJdH.js", "scene243-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene244.json b/lab/public/bundle/manifest/scene244.json index d46edbbd95..fe54cb77e8 100644 --- a/lab/public/bundle/manifest/scene244.json +++ b/lab/public/bundle/manifest/scene244.json @@ -12,12 +12,12 @@ "scene244-gltf-ext-uv-transform-YGWG8v_J.js", "scene244-gltf-feature-animation-pointer-IIOn7FNX.js", "scene244-gltf-feature-animations-C9Kp7IJh.js", - "scene244-gltf-feature-registry-BcRWAwOd.js", + "scene244-gltf-feature-registry-DAycddVV.js", "scene244-gltf-json-asset-BJhL5QXR.js", "scene244-gltf-pbr-builder-ext-FQk9L_MM.js", "scene244-ibl-fragment-CeRrvT96.js", "scene244-pbr-refraction-Clcnd8cy.js", - "scene244-pbr-renderable-BeyPkXIH.js", + "scene244-pbr-renderable-UmgqhXLM.js", "scene244-pbr-template-ext-CiHpOV5G.js", "scene244-pbr-transmission-ext-DI1Jfp2a.js", "scene244-reflectance-fragment-jGOq-qd3.js", diff --git a/lab/public/bundle/manifest/scene245.json b/lab/public/bundle/manifest/scene245.json index c9880568fe..2d6080c5a8 100644 --- a/lab/public/bundle/manifest/scene245.json +++ b/lab/public/bundle/manifest/scene245.json @@ -8,18 +8,18 @@ "scene245-generate-mipmaps-WM6bVf2v.js", "scene245-gltf-animation-l40-wjM0.js", "scene245-gltf-feature-animations-JDRZSSUs.js", - "scene245-gltf-feature-registry-sYLLZPDM.js", + "scene245-gltf-feature-registry-D_BksC2-.js", "scene245-gltf-feature-skeleton-D19NTiji.js", "scene245-gltf-interleave-7NBE5rBz.js", "scene245-gltf-json-asset-D_ddxWqm.js", "scene245-gltf-normals-DYhLwXNX.js", "scene245-gltf-strided-attribute-CGKc0Sx6.js", "scene245-ibl-fragment-CeRrvT96.js", - "scene245-pbr-renderable-CXtAq-cT.js", + "scene245-pbr-renderable-CvVLXtff.js", "scene245-pbr-template-ext-CiHpOV5G.js", "scene245-rgbd-decode-CKe7_W5g.js", "scene245-scene-uniforms-FTYH6Ct0.js", - "scene245-skeleton-fragment-By0Y5aLx.js", + "scene245-skeleton-fragment-B7lINqwc.js", "scene245.js" ] } diff --git a/lab/public/bundle/manifest/scene246.json b/lab/public/bundle/manifest/scene246.json index a25e072021..058f3c367b 100644 --- a/lab/public/bundle/manifest/scene246.json +++ b/lab/public/bundle/manifest/scene246.json @@ -8,7 +8,7 @@ "scene246-generate-mipmaps-DzKvtUax.js", "scene246-gltf-animation-Bg1KL3K9.js", "scene246-gltf-feature-animations-DxZ057Zf.js", - "scene246-gltf-feature-registry-D8d_ZlRy.js", + "scene246-gltf-feature-registry-BVlU_J5R.js", "scene246-gltf-feature-skeleton-CD4nSwQl.js", "scene246-gltf-interleave-ZNL-XZje.js", "scene246-gltf-json-asset-BLYKbDyg.js", @@ -16,10 +16,10 @@ "scene246-gltf-normals-DYhLwXNX.js", "scene246-gltf-strided-attribute-R3NdoKEL.js", "scene246-ibl-fragment-CeRrvT96.js", - "scene246-pbr-renderable-CA_JxnpX.js", + "scene246-pbr-renderable-CtrF5AjX.js", "scene246-rgbd-decode-DQhLUjb0.js", "scene246-scene-uniforms-FTYH6Ct0.js", - "scene246-skeleton-fragment-CsNvtv95.js", + "scene246-skeleton-fragment-DlkGZn1W.js", "scene246.js" ] } diff --git a/lab/public/bundle/manifest/scene247.json b/lab/public/bundle/manifest/scene247.json index f26590d29e..eb92ad016c 100644 --- a/lab/public/bundle/manifest/scene247.json +++ b/lab/public/bundle/manifest/scene247.json @@ -5,12 +5,12 @@ "runtimeChunks": [ "scene247-generate-mipmaps-Crazqgzv.js", "scene247-gltf-feature-extras-VHPteGay.js", - "scene247-gltf-feature-gpu-instancing-BQnnR0-8.js", - "scene247-gltf-feature-registry-BRI88AOY.js", + "scene247-gltf-feature-gpu-instancing-BN6QBVDw.js", + "scene247-gltf-feature-registry-WNS01r1L.js", "scene247-gltf-json-asset-CILluwFg.js", "scene247-gltf-multi-buffer-Cwm5u1Qq.js", "scene247-ibl-fragment-CeRrvT96.js", - "scene247-pbr-renderable-9WBHQegD.js", + "scene247-pbr-renderable-CiMKPsHy.js", "scene247-rgbd-decode-ClWWmAM6.js", "scene247-scene-uniforms-FTYH6Ct0.js", "scene247-thin-instance-fragment-CBn2miAC.js", diff --git a/lab/public/bundle/manifest/scene248.json b/lab/public/bundle/manifest/scene248.json index 3e8fcd2f3f..e05d59eaba 100644 --- a/lab/public/bundle/manifest/scene248.json +++ b/lab/public/bundle/manifest/scene248.json @@ -7,7 +7,7 @@ "scene248-gltf-json-asset-BxrCZM5A.js", "scene248-gltf-sampler-desc-CdC-ieMf.js", "scene248-ibl-fragment-CeRrvT96.js", - "scene248-pbr-renderable-fTXI0LOl.js", + "scene248-pbr-renderable-DCf2VQnm.js", "scene248-rgbd-decode-CLeq7oRj.js", "scene248-scene-uniforms-FTYH6Ct0.js", "scene248.js" diff --git a/lab/public/bundle/manifest/scene249.json b/lab/public/bundle/manifest/scene249.json index 671502b317..f731ca4c79 100644 --- a/lab/public/bundle/manifest/scene249.json +++ b/lab/public/bundle/manifest/scene249.json @@ -8,7 +8,7 @@ "scene249-gltf-color-normalize-4-WGzPL2.js", "scene249-gltf-json-asset-tL8UliXg.js", "scene249-ibl-fragment-CeRrvT96.js", - "scene249-pbr-renderable-uDRv_ihz.js", + "scene249-pbr-renderable-CYQmpzKb.js", "scene249-pbr-template-ext-CiHpOV5G.js", "scene249-rgbd-decode-BRTm0Szn.js", "scene249-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene251.json b/lab/public/bundle/manifest/scene251.json index 46390364af..adff0f682f 100644 --- a/lab/public/bundle/manifest/scene251.json +++ b/lab/public/bundle/manifest/scene251.json @@ -7,12 +7,12 @@ "scene251-generate-mipmaps-QjbHE23R.js", "scene251-gltf-animation-Cn2klEOG.js", "scene251-gltf-feature-animations-BBOyTs9Y.js", - "scene251-gltf-feature-registry-BuPXgxGD.js", + "scene251-gltf-feature-registry-BIA5Ctik.js", "scene251-gltf-feature-skeleton-C4O8Sov-.js", "scene251-gltf-glb-parser-8AD91ACR.js", "scene251-multilight-wgsl-C4_Zdm3l.js", - "scene251-pbr-renderable-CUoU6ST_.js", - "scene251-skeleton-fragment-CnwCxKHG.js", + "scene251-pbr-renderable-CXpB0NkR.js", + "scene251-skeleton-fragment-BkJhdhbE.js", "scene251.js" ] } diff --git a/lab/public/bundle/manifest/scene253.json b/lab/public/bundle/manifest/scene253.json index 08bd2a3aa3..bec151acd2 100644 --- a/lab/public/bundle/manifest/scene253.json +++ b/lab/public/bundle/manifest/scene253.json @@ -24,7 +24,7 @@ "scene253-gltf-feature-extras-VHPteGay.js", "scene253-gltf-feature-lights-punctual-BD0mrLuD.js", "scene253-gltf-feature-morph-OgvZWqfo.js", - "scene253-gltf-feature-registry-Ba7sf2h1.js", + "scene253-gltf-feature-registry-B5KyFrKk.js", "scene253-gltf-feature-skeleton-BYmZVC5E.js", "scene253-gltf-json-asset-CPydyTRL.js", "scene253-gltf-light-pointer-state-B7kFTRxK.js", @@ -34,16 +34,16 @@ "scene253-iridescence-fragment-T_Mzh3Ja.js", "scene253-light-base-DXdV2nlj.js", "scene253-light-matrix-BpE3lP6k.js", - "scene253-morph-fragment-C-hvw7SR.js", + "scene253-morph-fragment-B8EdH0ay.js", "scene253-multilight-wgsl-D9gPIJS-.js", "scene253-pbr-refraction-BbfkJ5jS.js", - "scene253-pbr-renderable-Bjul0AFo.js", + "scene253-pbr-renderable-RsqdCQDt.js", "scene253-pbr-template-ext-CiHpOV5G.js", "scene253-pbr-transmission-ext-BIxyFQt5.js", "scene253-reflectance-fragment-CC-U--Xr.js", "scene253-rgbd-decode-Cz9OR-T-.js", "scene253-scene-uniforms-FTYH6Ct0.js", - "scene253-skeleton-fragment-Dm7Z9U6B.js", + "scene253-skeleton-fragment-Dr_tRlmx.js", "scene253-spot-light-DxkNmtrY.js", "scene253-texture-2d-DQo3n8D6.js", "scene253-unlit-fragment-DWtvt7yQ.js", diff --git a/lab/public/bundle/manifest/scene254.json b/lab/public/bundle/manifest/scene254.json index 6f7a7f3f4a..5e8b45d4a6 100644 --- a/lab/public/bundle/manifest/scene254.json +++ b/lab/public/bundle/manifest/scene254.json @@ -6,11 +6,11 @@ "scene254-generate-mipmaps-CYwo5pXb.js", "scene254-gltf-animation-BROLRbEL.js", "scene254-gltf-feature-animations-XsSEzprH.js", - "scene254-gltf-feature-registry-9OXzaY_m.js", + "scene254-gltf-feature-registry-D0o8cU1Y.js", "scene254-gltf-json-asset-CpRpkx7j.js", "scene254-gltf-sampler-denorm-Bui_WQIw.js", "scene254-ibl-fragment-CeRrvT96.js", - "scene254-pbr-renderable-BRNiuBx9.js", + "scene254-pbr-renderable-sRm20zgl.js", "scene254-rgbd-decode-JnEXktCX.js", "scene254-scene-uniforms-FTYH6Ct0.js", "scene254-singlelight-hemispheric-wgsl-Dvj4Fh1w.js", diff --git a/lab/public/bundle/manifest/scene255.json b/lab/public/bundle/manifest/scene255.json index a04427eae2..0536c961ff 100644 --- a/lab/public/bundle/manifest/scene255.json +++ b/lab/public/bundle/manifest/scene255.json @@ -8,15 +8,15 @@ "scene255-generate-mipmaps-B7LdcXNp.js", "scene255-gltf-animation-BbNxs5BN.js", "scene255-gltf-feature-animations-Cd2IBM3n.js", - "scene255-gltf-feature-registry-Mn1kFHql.js", + "scene255-gltf-feature-registry-BS1WS7IW.js", "scene255-gltf-feature-skeleton-DHaKW6Xm.js", "scene255-gltf-json-asset-vxujGMAS.js", "scene255-gltf-normals-DYhLwXNX.js", "scene255-ibl-fragment-CeRrvT96.js", - "scene255-pbr-renderable-qZ7DJHy1.js", + "scene255-pbr-renderable-BTVQocvZ.js", "scene255-rgbd-decode-CfRM19UA.js", "scene255-scene-uniforms-FTYH6Ct0.js", - "scene255-skeleton-fragment-ECVoDkLm.js", + "scene255-skeleton-fragment-CFkBz7aE.js", "scene255.js" ] } diff --git a/lab/public/bundle/manifest/scene257.json b/lab/public/bundle/manifest/scene257.json index ea8060f302..675a9df53b 100644 --- a/lab/public/bundle/manifest/scene257.json +++ b/lab/public/bundle/manifest/scene257.json @@ -6,11 +6,11 @@ "scene257-flat-normal-wgsl-Cm9mM4tC.js", "scene257-generate-mipmaps-C2jNwHZ4.js", "scene257-gltf-feature-primitive-BMqT3MMH.js", - "scene257-gltf-feature-registry-b5VBsm7E.js", + "scene257-gltf-feature-registry-Ds1z_3Mp.js", "scene257-gltf-json-asset-DAx_-Tyr.js", "scene257-gltf-normals-DYhLwXNX.js", "scene257-ibl-fragment-CeRrvT96.js", - "scene257-pbr-renderable-Bfh8IaGu.js", + "scene257-pbr-renderable-BqGYwH0i.js", "scene257-rgbd-decode-D-PyAZjs.js", "scene257-scene-uniforms-FTYH6Ct0.js", "scene257.js" diff --git a/lab/public/bundle/manifest/scene258.json b/lab/public/bundle/manifest/scene258.json index 670375d134..71d59a5c00 100644 --- a/lab/public/bundle/manifest/scene258.json +++ b/lab/public/bundle/manifest/scene258.json @@ -10,7 +10,7 @@ "scene258-gltf-normals-DYhLwXNX.js", "scene258-gltf-uv-denorm-BTmmiHmh.js", "scene258-ibl-fragment-CeRrvT96.js", - "scene258-pbr-renderable-BhMf-EQe.js", + "scene258-pbr-renderable-3H89IEDf.js", "scene258-pbr-template-ext-CiHpOV5G.js", "scene258-rgbd-decode-DCksh81N.js", "scene258-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene259.json b/lab/public/bundle/manifest/scene259.json index c17d244395..3d620f46d7 100644 --- a/lab/public/bundle/manifest/scene259.json +++ b/lab/public/bundle/manifest/scene259.json @@ -9,7 +9,7 @@ "scene259-gltf-json-asset-DmawqpbX.js", "scene259-gltf-normals-DYhLwXNX.js", "scene259-ibl-fragment-CeRrvT96.js", - "scene259-pbr-renderable-hbY_bLnp.js", + "scene259-pbr-renderable-CpHGvhA9.js", "scene259-rgbd-decode-lYDuKaAU.js", "scene259-scene-uniforms-FTYH6Ct0.js", "scene259.js" diff --git a/lab/public/bundle/manifest/scene26.json b/lab/public/bundle/manifest/scene26.json index 3fb920362e..7f6db0e1ad 100644 --- a/lab/public/bundle/manifest/scene26.json +++ b/lab/public/bundle/manifest/scene26.json @@ -8,7 +8,7 @@ "scene26-gltf-glb-parser-wcPvSq_F.js", "scene26-ibl-fragment-CeRrvT96.js", "scene26-ibl-skybox-wgsl-CFIBOHHx.js", - "scene26-pbr-renderable-H0-g9-Fg.js", + "scene26-pbr-renderable-CrPA1RZY.js", "scene26-rgbd-decode-Y-O5hyxs.js", "scene26-singlelight-point-wgsl-BFpv7Wxi.js", "scene26-subsurface-fragment-kEd_nF4G.js", diff --git a/lab/public/bundle/manifest/scene260.json b/lab/public/bundle/manifest/scene260.json index 284c2fb006..d1bf8955eb 100644 --- a/lab/public/bundle/manifest/scene260.json +++ b/lab/public/bundle/manifest/scene260.json @@ -6,11 +6,11 @@ "scene260-flat-normal-wgsl-Cm9mM4tC.js", "scene260-generate-mipmaps-BDaYF730.js", "scene260-gltf-feature-primitive-BMqT3MMH.js", - "scene260-gltf-feature-registry-BbMGmG76.js", + "scene260-gltf-feature-registry-XyL-tg8J.js", "scene260-gltf-json-asset-B1CNeG6-.js", "scene260-gltf-normals-DYhLwXNX.js", "scene260-ibl-fragment-CeRrvT96.js", - "scene260-pbr-renderable-D7Df2DyA.js", + "scene260-pbr-renderable-Yof9yX2p.js", "scene260-rgbd-decode-DrU2yE9J.js", "scene260-scene-uniforms-FTYH6Ct0.js", "scene260.js" diff --git a/lab/public/bundle/manifest/scene265.json b/lab/public/bundle/manifest/scene265.json index 7e3ee43fee..73349b7176 100644 --- a/lab/public/bundle/manifest/scene265.json +++ b/lab/public/bundle/manifest/scene265.json @@ -5,12 +5,12 @@ "runtimeChunks": [ "scene265-generate-mipmaps-B94XvMrD.js", "scene265-gltf-ext-lights-image-based-Cy0y6Rcm.js", - "scene265-gltf-feature-registry-BupwipfZ.js", + "scene265-gltf-feature-registry-BCINoENi.js", "scene265-gltf-json-asset-D5Ypa449.js", "scene265-ibl-cubemap-upload-aeOmgodq.js", "scene265-ibl-env-assembly-c2UHDMKi.js", "scene265-ibl-fragment-CeRrvT96.js", - "scene265-pbr-renderable-8tA3k2Md.js", + "scene265-pbr-renderable-B1QOFzM3.js", "scene265.js" ] } diff --git a/lab/public/bundle/manifest/scene27.json b/lab/public/bundle/manifest/scene27.json index 8472d1e7c9..819a5baf96 100644 --- a/lab/public/bundle/manifest/scene27.json +++ b/lab/public/bundle/manifest/scene27.json @@ -1,16 +1,16 @@ { "rawKB": 83, - "gzipKB": 33.9, + "gzipKB": 34, "ignoredRawKB": 0, "runtimeChunks": [ "scene27-generate-mipmaps-CaLzDGPp.js", "scene27-gltf-ext-dielectric-C6hvXXiF.js", - "scene27-gltf-feature-registry-Cw3ZYKOU.js", + "scene27-gltf-feature-registry-CcX8EJ5r.js", "scene27-gltf-feature-variants-DaDkL0ns.js", "scene27-gltf-glb-parser-By8noE3G.js", "scene27-gltf-pbr-builder-ext-DRuiCPJl.js", "scene27-gltf-variants-D0uPiv4k.js", - "scene27-pbr-renderable-C3dYtQmZ.js", + "scene27-pbr-renderable-B8xh0xuA.js", "scene27-reflectance-fragment-BUy2V3-P.js", "scene27-singlelight-hemispheric-wgsl-BOG9p7K3.js", "scene27-texture-2d-DQo3n8D6.js", diff --git a/lab/public/bundle/manifest/scene28.json b/lab/public/bundle/manifest/scene28.json index 91ff7a7321..306e52eb89 100644 --- a/lab/public/bundle/manifest/scene28.json +++ b/lab/public/bundle/manifest/scene28.json @@ -6,10 +6,10 @@ "scene28-clearcoat-fragment-DOT6cYUU.js", "scene28-generate-mipmaps-7w1hCgCw.js", "scene28-gltf-ext-clearcoat-BgaLgSgD.js", - "scene28-gltf-feature-registry-DOn7lKJZ.js", + "scene28-gltf-feature-registry-D6BbZKZ7.js", "scene28-gltf-json-asset-BJmxzYNI.js", "scene28-ibl-fragment-CeRrvT96.js", - "scene28-pbr-renderable-ByBxAG11.js", + "scene28-pbr-renderable-Cyb0SRXH.js", "scene28-rgbd-decode-wUvHpHPX.js", "scene28-scene-uniforms-FTYH6Ct0.js", "scene28.js" diff --git a/lab/public/bundle/manifest/scene29.json b/lab/public/bundle/manifest/scene29.json index 439bbf4359..4b6d2cd851 100644 --- a/lab/public/bundle/manifest/scene29.json +++ b/lab/public/bundle/manifest/scene29.json @@ -1,16 +1,16 @@ { - "rawKB": 90.8, + "rawKB": 90.7, "gzipKB": 37.6, "ignoredRawKB": 0, "runtimeChunks": [ "scene29-generate-mipmaps-DJCYSY1_.js", "scene29-gltf-ext-sheen-Dv23b_3T.js", "scene29-gltf-ext-uv-transform-tUSEOqA7.js", - "scene29-gltf-feature-registry-BBlGSmz5.js", + "scene29-gltf-feature-registry-CkMliJ1g.js", "scene29-gltf-json-asset-BCPycsr9.js", "scene29-gltf-pbr-builder-ext-BS75XjMB.js", "scene29-ibl-fragment-CeRrvT96.js", - "scene29-pbr-renderable-Br3tz0Wi.js", + "scene29-pbr-renderable-Qh9aIn1a.js", "scene29-pbr-template-ext-CiHpOV5G.js", "scene29-rgbd-decode-DZaCQfRj.js", "scene29-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene30.json b/lab/public/bundle/manifest/scene30.json index 13cee4584e..8633095bd5 100644 --- a/lab/public/bundle/manifest/scene30.json +++ b/lab/public/bundle/manifest/scene30.json @@ -8,12 +8,12 @@ "scene30-gltf-ext-dielectric-C6hvXXiF.js", "scene30-gltf-ext-uv-transform-DQN5DAiE.js", "scene30-gltf-feature-draco-BSbw1ZBU.js", - "scene30-gltf-feature-registry-CK1Gb-mk.js", + "scene30-gltf-feature-registry-BRU3VpOu.js", "scene30-gltf-glb-parser-DJ0cTJ-4.js", "scene30-gltf-pbr-builder-ext-a6q_Jsam.js", "scene30-ibl-fragment-CeRrvT96.js", "scene30-pbr-refraction-BZI7joFF.js", - "scene30-pbr-renderable-C0nNN8KI.js", + "scene30-pbr-renderable-HY1VWxMD.js", "scene30-pbr-template-ext-CiHpOV5G.js", "scene30-pbr-transmission-ext-BWXOxiAJ.js", "scene30-rgbd-decode-RQYTGqhL.js", diff --git a/lab/public/bundle/manifest/scene31.json b/lab/public/bundle/manifest/scene31.json index 9a4ce434cc..131bbd4123 100644 --- a/lab/public/bundle/manifest/scene31.json +++ b/lab/public/bundle/manifest/scene31.json @@ -6,10 +6,10 @@ "scene31-emissive-fragment-C1TNSo0w.js", "scene31-generate-mipmaps-CoCbB-UD.js", "scene31-gltf-ext-emissive-strength-CButLZnK.js", - "scene31-gltf-feature-registry-DvtgQyqM.js", + "scene31-gltf-feature-registry-hxmbjDTN.js", "scene31-gltf-glb-parser-CzRZVo57.js", "scene31-ibl-fragment-CeRrvT96.js", - "scene31-pbr-renderable-BtZdTFrP.js", + "scene31-pbr-renderable-Ve1SYEH6.js", "scene31-rgbd-decode-C5kd6U3R.js", "scene31-scene-uniforms-FTYH6Ct0.js", "scene31.js" diff --git a/lab/public/bundle/manifest/scene32.json b/lab/public/bundle/manifest/scene32.json index 508afa3014..1787215ea7 100644 --- a/lab/public/bundle/manifest/scene32.json +++ b/lab/public/bundle/manifest/scene32.json @@ -5,10 +5,10 @@ "runtimeChunks": [ "scene32-generate-mipmaps-DdnhjgK3.js", "scene32-gltf-ext-unlit-1vUYfF7v.js", - "scene32-gltf-feature-registry-mmoWs9VW.js", + "scene32-gltf-feature-registry-DFIIDW7I.js", "scene32-gltf-glb-parser-BX2AS-bF.js", "scene32-ibl-fragment-CeRrvT96.js", - "scene32-pbr-renderable-D6ARAOMy.js", + "scene32-pbr-renderable-CrG-VTk_.js", "scene32-rgbd-decode-3FngBUdB.js", "scene32-scene-uniforms-FTYH6Ct0.js", "scene32-unlit-fragment-DWtvt7yQ.js", diff --git a/lab/public/bundle/manifest/scene33.json b/lab/public/bundle/manifest/scene33.json index 72055c09a9..d94155bde2 100644 --- a/lab/public/bundle/manifest/scene33.json +++ b/lab/public/bundle/manifest/scene33.json @@ -6,7 +6,7 @@ "scene33-generate-mipmaps-Z_0Gnbub.js", "scene33-gltf-ext-dielectric-C6hvXXiF.js", "scene33-gltf-feature-lights-punctual-Bo3_Vssl.js", - "scene33-gltf-feature-registry-BXAbDsrK.js", + "scene33-gltf-feature-registry-D5Nzvvln.js", "scene33-gltf-glb-parser-DMBUHtjv.js", "scene33-gltf-light-pointer-state-B7kFTRxK.js", "scene33-gltf-sampler-desc-ChMUJ3dG.js", @@ -14,7 +14,7 @@ "scene33-light-base-D0CungYX.js", "scene33-multilight-wgsl-D3rF1wIQ.js", "scene33-pbr-refraction-DI8CMlke.js", - "scene33-pbr-renderable-BBqtDwqU.js", + "scene33-pbr-renderable-DlsU8Q4S.js", "scene33-pbr-transmission-ext-tcZohVgy.js", "scene33-point-light-CDLqYT3h.js", "scene33-rgbd-decode-3bEbw5la.js", diff --git a/lab/public/bundle/manifest/scene34.json b/lab/public/bundle/manifest/scene34.json index d84114282d..ee29dd6baa 100644 --- a/lab/public/bundle/manifest/scene34.json +++ b/lab/public/bundle/manifest/scene34.json @@ -8,11 +8,11 @@ "scene34-gltf-ext-node-visibility-DWsJZC5Q.js", "scene34-gltf-feature-animation-pointer-CE1BnN9s.js", "scene34-gltf-feature-animations-BoKR3Ok5.js", - "scene34-gltf-feature-registry-bcYZK_JW.js", + "scene34-gltf-feature-registry-BaYttjiZ.js", "scene34-gltf-glb-parser-BxoFTGKn.js", "scene34-gltf-sampler-denorm-C_vs8OEN.js", "scene34-ibl-fragment-CeRrvT96.js", - "scene34-pbr-renderable-BWMpYg-a.js", + "scene34-pbr-renderable-Dx4vvxmY.js", "scene34-rgbd-decode-ByPAsdES.js", "scene34-scene-uniforms-FTYH6Ct0.js", "scene34-visibility-8Ff_Kr9x.js", diff --git a/lab/public/bundle/manifest/scene35.json b/lab/public/bundle/manifest/scene35.json index cd2ede1703..ae6494e92a 100644 --- a/lab/public/bundle/manifest/scene35.json +++ b/lab/public/bundle/manifest/scene35.json @@ -4,11 +4,11 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene35-generate-mipmaps-Dw2dswCB.js", - "scene35-gltf-feature-gpu-instancing-D1uNjxTA.js", - "scene35-gltf-feature-registry-DmpwKxiD.js", + "scene35-gltf-feature-gpu-instancing-CfHSR4Pp.js", + "scene35-gltf-feature-registry-B2cBIBnw.js", "scene35-gltf-glb-parser-BJyme4Vk.js", "scene35-ibl-fragment-CeRrvT96.js", - "scene35-pbr-renderable-CSwk7IQe.js", + "scene35-pbr-renderable-DKgUPTlX.js", "scene35-rgbd-decode-U56TFVGU.js", "scene35-scene-uniforms-FTYH6Ct0.js", "scene35-thin-instance-fragment-CBn2miAC.js", diff --git a/lab/public/bundle/manifest/scene37.json b/lab/public/bundle/manifest/scene37.json index ec7d97c82b..80becf013b 100644 --- a/lab/public/bundle/manifest/scene37.json +++ b/lab/public/bundle/manifest/scene37.json @@ -8,11 +8,11 @@ "scene37-gltf-ext-dielectric-C6hvXXiF.js", "scene37-gltf-ext-sheen-Dv23b_3T.js", "scene37-gltf-ext-uv-transform-hVTzf5Q9.js", - "scene37-gltf-feature-registry-DkJr-P9O.js", + "scene37-gltf-feature-registry-CB4f47ve.js", "scene37-gltf-glb-parser-CkbGcOCI.js", "scene37-gltf-pbr-builder-ext-D7wSTRfQ.js", "scene37-ibl-fragment-CeRrvT96.js", - "scene37-pbr-renderable-DzmmuWTS.js", + "scene37-pbr-renderable-B1c47N3j.js", "scene37-pbr-template-ext-CiHpOV5G.js", "scene37-reflectance-fragment-CcPSWeb4.js", "scene37-rgbd-decode-D3VLnI-E.js", diff --git a/lab/public/bundle/manifest/scene39.json b/lab/public/bundle/manifest/scene39.json index 2951f82beb..6d26bae106 100644 --- a/lab/public/bundle/manifest/scene39.json +++ b/lab/public/bundle/manifest/scene39.json @@ -12,7 +12,7 @@ "scene39-gltf-feature-animation-pointer-CLES_-mr.js", "scene39-gltf-feature-animations-eOJFbLQK.js", "scene39-gltf-feature-lights-punctual-5HuRS6Ip.js", - "scene39-gltf-feature-registry-BHengqLs.js", + "scene39-gltf-feature-registry-CkFOhzef.js", "scene39-gltf-json-asset-BoHskl0t.js", "scene39-gltf-light-pointer-state-B7kFTRxK.js", "scene39-gltf-pbr-builder-ext-C39ednG5.js", @@ -20,7 +20,7 @@ "scene39-ibl-fragment-CeRrvT96.js", "scene39-light-base-DmItN1i-.js", "scene39-light-matrix-CBTBdGsb.js", - "scene39-pbr-renderable-BPFnAfJ5.js", + "scene39-pbr-renderable-vhFWMsTu.js", "scene39-pbr-template-ext-CiHpOV5G.js", "scene39-rgbd-decode-YUi8rAlU.js", "scene39-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene41.json b/lab/public/bundle/manifest/scene41.json index 1fac34e1d3..52c77c0f2c 100644 --- a/lab/public/bundle/manifest/scene41.json +++ b/lab/public/bundle/manifest/scene41.json @@ -1,16 +1,16 @@ { "rawKB": 93.3, - "gzipKB": 37.1, + "gzipKB": 37.2, "ignoredRawKB": 0, "runtimeChunks": [ "scene41-generate-mipmaps-CaoM8To3.js", "scene41-gltf-ext-spec-gloss-D60sFZYX.js", - "scene41-gltf-feature-registry-hv8F_es0.js", + "scene41-gltf-feature-registry-nGeE6GJh.js", "scene41-gltf-glb-parser-CkOP9avo.js", "scene41-gltf-sampler-desc-E_7RaQlV.js", "scene41-point-light-CnkkP9oB.js", "scene41-shader-composer-DishuYLa.js", - "scene41-standard-renderable-CRg1QH5T.js", + "scene41-standard-renderable-DGTNeky8.js", "scene41-wgsl-fog-m3PkjS7i.js", "scene41.js" ] diff --git a/lab/public/bundle/manifest/scene47.json b/lab/public/bundle/manifest/scene47.json index d7d59fcba1..4505d3cf7e 100644 --- a/lab/public/bundle/manifest/scene47.json +++ b/lab/public/bundle/manifest/scene47.json @@ -5,11 +5,11 @@ "runtimeChunks": [ "scene47-generate-mipmaps-DOnKpLaE.js", "scene47-gltf-ext-spec-gloss-D60sFZYX.js", - "scene47-gltf-feature-registry-BVf_FYMt.js", + "scene47-gltf-feature-registry-CQXDlXIU.js", "scene47-gltf-glb-parser-6-tsF2C2.js", "scene47-gltf-sampler-desc-BJMmnJDF.js", "scene47-shader-composer-DishuYLa.js", - "scene47-standard-renderable-Dt5f9wgQ.js", + "scene47-standard-renderable-_WORg7a1.js", "scene47-wgsl-fog-m3PkjS7i.js", "scene47.js" ] diff --git a/lab/public/bundle/manifest/scene49.json b/lab/public/bundle/manifest/scene49.json index c410f1d188..bbe9b13fdb 100644 --- a/lab/public/bundle/manifest/scene49.json +++ b/lab/public/bundle/manifest/scene49.json @@ -3,7 +3,7 @@ "gzipKB": 35.2, "ignoredRawKB": 0, "runtimeChunks": [ - "scene49-standard-renderable-Bq1pTdOZ.js", + "scene49-standard-renderable-DH4fsXXy.js", "scene49-swapchain-overlay-ilhN4El_.js", "scene49-ubo-layout-CnrP4RZD.js", "scene49.js" diff --git a/lab/public/bundle/manifest/scene5.json b/lab/public/bundle/manifest/scene5.json index e79a06fb75..a80579a078 100644 --- a/lab/public/bundle/manifest/scene5.json +++ b/lab/public/bundle/manifest/scene5.json @@ -10,14 +10,14 @@ "scene5-gltf-color-normalize-4-WGzPL2.js", "scene5-gltf-feature-animations-Db-wL1ps.js", "scene5-gltf-feature-morph-BNcyLEaQ.js", - "scene5-gltf-feature-registry-C96LOoV9.js", + "scene5-gltf-feature-registry-D-ZmGwgF.js", "scene5-gltf-feature-skeleton-BYbOC8HD.js", "scene5-gltf-json-asset-DXOveXY1.js", - "scene5-morph-fragment-qc6YRxFj.js", - "scene5-pbr-renderable-CuvmiQH_.js", + "scene5-morph-fragment-5OczccqE.js", + "scene5-pbr-renderable-B8AzJjV3.js", "scene5-pbr-template-ext-CiHpOV5G.js", "scene5-singlelight-hemispheric-wgsl-Cu_KX2Nl.js", - "scene5-skeleton-fragment-FgKJRZtZ.js", + "scene5-skeleton-fragment-CZD-Wybb.js", "scene5.js" ] } diff --git a/lab/public/bundle/manifest/scene54.json b/lab/public/bundle/manifest/scene54.json index ea662f4bb1..f9bf698987 100644 --- a/lab/public/bundle/manifest/scene54.json +++ b/lab/public/bundle/manifest/scene54.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene54-billboard-renderable-D8TZU5pi.js", "scene54-scene-uniforms-FTYH6Ct0.js", - "scene54-standard-renderable-CmLRsEFS.js", + "scene54-standard-renderable-DGAG0eqv.js", "scene54.js" ] } diff --git a/lab/public/bundle/manifest/scene56.json b/lab/public/bundle/manifest/scene56.json index 49a6a6e1af..7d62d4645f 100644 --- a/lab/public/bundle/manifest/scene56.json +++ b/lab/public/bundle/manifest/scene56.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene56-billboard-renderable-DvUBDyDr.js", "scene56-scene-uniforms-FTYH6Ct0.js", - "scene56-standard-renderable-C_erBbhE.js", + "scene56-standard-renderable-Begr0fCz.js", "scene56.js" ] } diff --git a/lab/public/bundle/manifest/scene57.json b/lab/public/bundle/manifest/scene57.json index d67f649e06..7c85799ee2 100644 --- a/lab/public/bundle/manifest/scene57.json +++ b/lab/public/bundle/manifest/scene57.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene57-billboard-renderable-CutxZjMd.js", "scene57-scene-uniforms-FTYH6Ct0.js", - "scene57-standard-renderable-Cx4nBC4e.js", + "scene57-standard-renderable-BSoxEW0k.js", "scene57.js" ] } diff --git a/lab/public/bundle/manifest/scene59.json b/lab/public/bundle/manifest/scene59.json index 430b7ed57b..924f54e1f0 100644 --- a/lab/public/bundle/manifest/scene59.json +++ b/lab/public/bundle/manifest/scene59.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene59-billboard-renderable-CWKRAyxb.js", "scene59-scene-uniforms-FTYH6Ct0.js", - "scene59-standard-renderable-DSbHZBBT.js", + "scene59-standard-renderable-BqE0cLTR.js", "scene59.js" ] } diff --git a/lab/public/bundle/manifest/scene6.json b/lab/public/bundle/manifest/scene6.json index b4d0632e39..25b222ea70 100644 --- a/lab/public/bundle/manifest/scene6.json +++ b/lab/public/bundle/manifest/scene6.json @@ -7,7 +7,7 @@ "scene6-background-ground-O1nPh-aw.js", "scene6-cubemap-skybox-material-VKHB2CT9.js", "scene6-ibl-fragment-CeRrvT96.js", - "scene6-pbr-renderable-5QjMpYOO.js", + "scene6-pbr-renderable-BRi8Zj8C.js", "scene6-rgbd-decode-DltNvrvh.js", "scene6-scene-uniforms-FTYH6Ct0.js", "scene6-wgsl-helpers-BgB2AJrF.js", diff --git a/lab/public/bundle/manifest/scene7.json b/lab/public/bundle/manifest/scene7.json index a15b21706e..e8c878b6cc 100644 --- a/lab/public/bundle/manifest/scene7.json +++ b/lab/public/bundle/manifest/scene7.json @@ -10,15 +10,15 @@ "scene7-gltf-animation-RP7A9LpW.js", "scene7-gltf-feature-animations-C9A-KG60.js", "scene7-gltf-feature-extras-VHPteGay.js", - "scene7-gltf-feature-registry-Dm5A4Gkw.js", + "scene7-gltf-feature-registry-qFhxtXWB.js", "scene7-gltf-feature-skeleton-DaFXylO1.js", "scene7-gltf-json-asset-DelZuh7Y.js", "scene7-ibl-fragment-CeRrvT96.js", - "scene7-pbr-renderable-weEQGDoa.js", + "scene7-pbr-renderable-3faTikar.js", "scene7-rgbd-decode-C9OJGqTP.js", "scene7-scene-uniforms-FTYH6Ct0.js", "scene7-singlelight-hemispheric-wgsl-DbXoSTyY.js", - "scene7-skeleton-fragment-AJqg2Ax9.js", + "scene7-skeleton-fragment-fpmLTQd6.js", "scene7-skybox.vertex-Ban7q6IM-D1KSfOUq.js", "scene7-wgsl-helpers-BgB2AJrF.js", "scene7.js" diff --git a/lab/public/bundle/manifest/scene73.json b/lab/public/bundle/manifest/scene73.json index fdc655242b..8271756fdd 100644 --- a/lab/public/bundle/manifest/scene73.json +++ b/lab/public/bundle/manifest/scene73.json @@ -10,14 +10,14 @@ "scene73-fragment-output-CfRmfbPv.js", "scene73-generate-mipmaps-DcnO72FN.js", "scene73-gltf-ext-clearcoat-BgaLgSgD.js", - "scene73-gltf-feature-registry-CE7chzXt.js", + "scene73-gltf-feature-registry-CWKqO_I4.js", "scene73-gltf-glb-parser-Bm1IrZjT.js", "scene73-ibl-fragment-CeRrvT96.js", "scene73-input-block-lrbZBZJO.js", "scene73-node-env-2j3ozSNZ.js", "scene73-node-renderable-Ik7zhWQu.js", "scene73-pbr-metallic-roughness-block-full-CgKHgEzu.js", - "scene73-pbr-renderable-ByHW-E0t.js", + "scene73-pbr-renderable-GTc3OZaD.js", "scene73-perturb-normal-Bqx-TYaB.js", "scene73-reflection-block-CIXuH-z0.js", "scene73-rgbd-decode-Bpa-sBKE.js", diff --git a/lab/public/bundle/manifest/scene8.json b/lab/public/bundle/manifest/scene8.json index 311de1754f..0259843c65 100644 --- a/lab/public/bundle/manifest/scene8.json +++ b/lab/public/bundle/manifest/scene8.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene8-background-hdr-skybox-FeMZnJ2H.js", "scene8-ibl-fragment-CeRrvT96.js", - "scene8-pbr-renderable-B1Em0ul0.js", + "scene8-pbr-renderable-4RYLL_xe.js", "scene8-scene-size-CBRA6KcU.js", "scene8-scene-uniforms-FTYH6Ct0.js", "scene8-singlelight-point-wgsl-2QlXbmIF.js", diff --git a/lab/public/bundle/manifest/scene9.json b/lab/public/bundle/manifest/scene9.json index 2173306dbb..8d461df355 100644 --- a/lab/public/bundle/manifest/scene9.json +++ b/lab/public/bundle/manifest/scene9.json @@ -6,7 +6,7 @@ "scene9-generate-mipmaps-xw5BdfZd.js", "scene9-normal-map-fragment-EnSQYVZ8.js", "scene9-point-light-ESYN5_k0.js", - "scene9-standard-renderable-DO9SAzPg.js", + "scene9-standard-renderable-H66bBEdG.js", "scene9-std-ambient-fragment-BT8qGSob.js", "scene9-std-opacity-fragment-Dwz7NrjB.js", "scene9-std-reflection-fragment-Cb2Nq9Do.js", diff --git a/lab/public/bundle/manifest/scene94.json b/lab/public/bundle/manifest/scene94.json index 40e0bab765..ba8f81e365 100644 --- a/lab/public/bundle/manifest/scene94.json +++ b/lab/public/bundle/manifest/scene94.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene94-billboard-renderable-BlUVAJut.js", - "scene94-standard-renderable-DWu49Znz.js", + "scene94-standard-renderable-DO3KlBh_.js", "scene94.js" ] } diff --git a/lab/public/bundle/manifest/scene95.json b/lab/public/bundle/manifest/scene95.json index d94b02fa5a..838ad94a8a 100644 --- a/lab/public/bundle/manifest/scene95.json +++ b/lab/public/bundle/manifest/scene95.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene95-billboard-renderable-BxuhE6OT.js", - "scene95-standard-renderable--BLgRvH3.js", + "scene95-standard-renderable-B5Ui0Ke3.js", "scene95.js" ] } diff --git a/lab/public/bundle/manifest/scene98.json b/lab/public/bundle/manifest/scene98.json index 2e2ee2cfcd..4283f2b9ba 100644 --- a/lab/public/bundle/manifest/scene98.json +++ b/lab/public/bundle/manifest/scene98.json @@ -1,11 +1,11 @@ { - "rawKB": 60.2, + "rawKB": 60.3, "gzipKB": 24.4, "ignoredRawKB": 0, "runtimeChunks": [ "scene98-billboard-renderable-C-kyjRwT.js", "scene98-scene-uniforms-FTYH6Ct0.js", - "scene98-standard-renderable-CeJ5VwgI.js", + "scene98-standard-renderable--UE4Nr0P.js", "scene98.js" ] } diff --git a/lab/public/bundle/manifest/scene99.json b/lab/public/bundle/manifest/scene99.json index ea1eee1203..46d7d8b6ac 100644 --- a/lab/public/bundle/manifest/scene99.json +++ b/lab/public/bundle/manifest/scene99.json @@ -8,12 +8,12 @@ "scene99-generate-mipmaps-BFyY2NRa.js", "scene99-gltf-animation-T1Ovedwq.js", "scene99-gltf-feature-animations-CIfvZioj.js", - "scene99-gltf-feature-registry-CblQlEku.js", + "scene99-gltf-feature-registry-DKNhiimw.js", "scene99-gltf-feature-skeleton-BUITWXe0.js", "scene99-gltf-glb-parser-BtneRupN.js", "scene99-multilight-wgsl-Cni0HYFi.js", - "scene99-pbr-renderable-J44pMMSM.js", - "scene99-skeleton-fragment-B0lr-4bX.js", + "scene99-pbr-renderable-CIdgLGFf.js", + "scene99-skeleton-fragment-Elu_cEOi.js", "scene99-types-BVAXdTdf.js", "scene99.js" ] diff --git a/packages/babylon-lite/src/frame-graph/shadow-inputs.ts b/packages/babylon-lite/src/frame-graph/shadow-inputs.ts index 1e037f881f..42769c8c0d 100644 --- a/packages/babylon-lite/src/frame-graph/shadow-inputs.ts +++ b/packages/babylon-lite/src/frame-graph/shadow-inputs.ts @@ -22,6 +22,28 @@ export function _getShadowTaskCasterMeshes(shadowGenerator: ShadowGenerator): re return shadowTaskInputs?.get(shadowGenerator); } +/** + * Cap the cascades a caster mesh renders into: the mesh casts only into cascade layers `0..maxCascade` + * (0 = the nearest). Lets a scene keep small casters out of the far cascades, where their shadows are + * sub-texel anyway — each excluded layer saves that caster's draw + pipeline switch per frame. + * + * Unset (the default) casts into every cascade. Pass `Infinity` to restore that. + * The cap is read when a generator's caster set is (re)supplied through + * {@link setShadowTaskCasterMeshes}; to change it for an already-registered caster, re-supply the + * caster list (a new array) afterwards. Non-cascaded (single-map) generators ignore the cap. + */ +export function setShadowCasterMaxCascade(mesh: Mesh, maxCascade: number): void { + if (maxCascade !== Infinity && (!Number.isInteger(maxCascade) || maxCascade < 0)) { + throw new RangeError("setShadowCasterMaxCascade requires a non-negative integer or Infinity"); + } + mesh._shadowMaxCascade = maxCascade === Infinity ? undefined : maxCascade; +} + +/** @internal Cascade cap for a caster mesh (see {@link setShadowCasterMaxCascade}); Infinity when unset. */ +export function _getShadowCasterMaxCascade(mesh: Mesh): number { + return mesh._shadowMaxCascade ?? Infinity; +} + /** @internal */ export function _setShadowTaskInputPreloader(preloader: (shadowGenerator: ShadowGenerator, casterMeshes: readonly Mesh[]) => Promise): void { shadowTaskInputPreloader = preloader; diff --git a/packages/babylon-lite/src/index.ts b/packages/babylon-lite/src/index.ts index 4f408007f2..d6633fac58 100644 --- a/packages/babylon-lite/src/index.ts +++ b/packages/babylon-lite/src/index.ts @@ -292,7 +292,7 @@ export { createEsmDirectionalShadowGenerator } from "./shadow/esm-directional-sh export { createPcfSpotlightShadowGenerator } from "./shadow/pcf-spotlight-shadow-generator.js"; export { createPcfDirectionalShadowGenerator } from "./shadow/pcf-directional-shadow-generator.js"; export { createCsmDirectionalShadowGenerator, getCsmReceiverTexture, onCsmReceiverUpdate } from "./shadow/csm-directional-shadow-generator.js"; -export { setShadowTaskCasterMeshes } from "./frame-graph/shadow-inputs.js"; +export { setShadowTaskCasterMeshes, setShadowCasterMaxCascade } from "./frame-graph/shadow-inputs.js"; // ─── Animation ─────────────────────────────────────────────────────── export { createAnimationController } from "./skeleton/skeleton-updater.js"; @@ -395,6 +395,8 @@ export { setThinInstanceColor, enableThinInstanceGpuCulling, setThinInstanceCullBoundsPad, + setThinInstanceLodPartner, + clearThinInstanceLodPartner, } from "./mesh/thin-instance.js"; export { addHierarchyInstance, @@ -404,7 +406,7 @@ export { setHierarchyInstanceMatrix, } from "./mesh/hierarchy-instance-pool.js"; export type { HierarchyInstancePool } from "./mesh/hierarchy-instance-pool.js"; -export type { ThinInstanceData } from "./mesh/thin-instance.js"; +export type { ThinInstanceData, ThinInstanceLodPartnerOptions } from "./mesh/thin-instance.js"; // ─── Types ─────────────────────────────────────────────────────────── export type { SceneContext, ImageProcessingConfig, ClipPlane } from "./scene/scene.js"; diff --git a/packages/babylon-lite/src/mesh/mesh-dispose.ts b/packages/babylon-lite/src/mesh/mesh-dispose.ts index 97d8ff0e7f..8432746869 100644 --- a/packages/babylon-lite/src/mesh/mesh-dispose.ts +++ b/packages/babylon-lite/src/mesh/mesh-dispose.ts @@ -1,5 +1,6 @@ import type { Mesh } from "./mesh.js"; import { release } from "../resource/ref-count.js"; +import { _detachThinInstanceLodMesh } from "./thin-instance.js"; /** Destroy all GPU resources owned by a mesh (vertex buffers, skeleton, morph targets). * `_gpu`/`skeleton`/`morphTargets`/`thinInstances` may be SHARED with a clone made via @@ -19,6 +20,7 @@ export function disposeMeshGpu(mesh: Mesh): void { } const ti = mesh.thinInstances; if (ti && release(ti)) { + _detachThinInstanceLodMesh(mesh); ti._gpuBuffer?.destroy(); ti._colorGpuBuffer?.destroy(); ti._drawArgsBuffer?.destroy(); diff --git a/packages/babylon-lite/src/mesh/mesh.ts b/packages/babylon-lite/src/mesh/mesh.ts index b2e3b9d744..08aa86fcce 100644 --- a/packages/babylon-lite/src/mesh/mesh.ts +++ b/packages/babylon-lite/src/mesh/mesh.ts @@ -120,6 +120,10 @@ export interface Mesh extends SceneNode { /** @internal */ _gpu: MeshGPU; + /** @internal Reason cloning this mesh is currently forbidden. */ + _clone?: string; + /** @internal Highest CSM cascade this mesh casts into; undefined means all cascades. */ + _shadowMaxCascade?: number; /** @internal */ _cpuPositions?: Float32Array; /** @internal */ diff --git a/packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts b/packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts index 4a3cf4858b..3a429f197e 100644 --- a/packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts +++ b/packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts @@ -19,12 +19,14 @@ import type { RenderTargetSignature } from "../engine/render-target.js"; import type { SceneContext } from "../scene/scene.js"; import type { DrawUpdateContext, Renderable } from "../render/renderable.js"; import type { Mesh } from "./mesh.js"; +import type { ThinInstanceData } from "./thin-instance.js"; import type { ThinInstanceDrawBuffers } from "./thin-instance-gpu.js"; import { createTiCullState, destroyTiCullState, getComputeDispatchBatch, prepareTiCull, + publishTiLodBucket, type ComputeDispatchBatch, type ThinInstanceGpuCullState, } from "./thin-instance-gpu-culling.js"; @@ -76,7 +78,21 @@ export function tryBind( signature: RenderTargetSignature ): TiCullBinding | undefined { const ti = mesh.thinInstances; - if (excluded || !ti?._gpuCullingEnabled) { + if (!ti) { + return undefined; + } + if (ti._lodSource) { + if (excluded) { + throw new Error("Thin-instance LOD partners require an opaque, non-transmissive material"); + } + // The partner consumes buffers published by another renderable's update, so its vertex/indirect + // handles must be resolved at draw time rather than captured in an opaque render bundle. + (renderable as { _direct?: boolean })._direct = true; + // This mesh is the LOD partner of a GPU-culled mesh: it consumes the far bucket that mesh's + // culling compacts for this pass and never draws its own instance count (see bindLodPartner). + return bindLodPartner(ti, signature, hasColor, baseUpdate); + } + if (excluded || !ti._gpuCullingEnabled) { return undefined; } const holder = renderable as CullCachingRenderable; @@ -101,9 +117,13 @@ export function tryBind( _updateBatch: updateBatch, update(context: DrawUpdateContext): void { baseUpdate?.(context); - const res = prepareTiCull(engine, state, mesh, mesh._gpu, ti, hasColor, context, updateBatch); + const partner = ti._lodPartner ?? null; + const res = prepareTiCull(engine, state, mesh, mesh._gpu, ti, hasColor, context, updateBatch, partner); binding.cullDrawBufs = res?.drawBuffers ?? null; binding._args = res?.argsBuffer ?? null; + if (ti._lodBuckets) { + publishTiLodBucket(ti, signature, res); + } }, draw(pass: GPURenderPassEncoder | GPURenderBundleEncoder, indexCount: number, instanceCount: number): void { if (binding._args) { @@ -117,3 +137,45 @@ export function tryBind( }; return binding; } + +/** @internal Binding for the LOD partner of a GPU-culled mesh (`setThinInstanceLodPartner`). Its update + * looks up the far bucket the source mesh's culling published for this pass's signature; its draw issues + * ONLY that bucket's indirect draw — with no bucket (source not culling this pass, or culling fell back) + * it draws nothing, so the partner can never fall back to its own instance count and double-draw. */ +function bindLodPartner(ti: ThinInstanceData, signature: RenderTargetSignature, hasColor: boolean, baseUpdate: ((context: DrawUpdateContext) => void) | undefined): TiCullBinding { + const currentBucket = () => { + const bucket = ti._lodBuckets?.get(signature); + if (!bucket?.active) { + return null; + } + if (hasColor && !bucket.colorBuffer) { + throw new Error("Thin-instance LOD partner requires the source draw to provide instance colors"); + } + return bucket; + }; + const binding: TiCullBinding = { + get cullDrawBufs() { + return currentBucket(); + }, + get _args() { + return currentBucket()?.argsBuffer ?? null; + }, + _updateBatch: getComputeDispatchBatch(signature), + update(context: DrawUpdateContext): void { + baseUpdate?.(context); + }, + draw(pass: GPURenderPassEncoder | GPURenderBundleEncoder, indexCount: number, instanceCount: number): void { + const bucket = currentBucket(); + if (bucket) { + pass.drawIndexedIndirect(bucket.argsBuffer, 0); + } else if (!ti._lodSource) { + if (ti._drawArgsBuffer) { + pass.drawIndexedIndirect(ti._drawArgsBuffer, 0); + } else { + pass.drawIndexed(indexCount, instanceCount); + } + } + }, + }; + return binding; +} diff --git a/packages/babylon-lite/src/mesh/thin-instance-gpu-culling.ts b/packages/babylon-lite/src/mesh/thin-instance-gpu-culling.ts index 9036c74db2..a9dc908cb1 100644 --- a/packages/babylon-lite/src/mesh/thin-instance-gpu-culling.ts +++ b/packages/babylon-lite/src/mesh/thin-instance-gpu-culling.ts @@ -18,13 +18,19 @@ import type { ThinInstanceData } from "./thin-instance.js"; import { syncThinInstanceGpuData } from "./thin-instance-gpu.js"; import type { ThinInstanceDrawBuffers } from "./thin-instance-gpu.js"; import { bumpVisibilityEpoch } from "../engine/engine.js"; +import { retireGpuResources } from "../engine/gpu-resource-retirement.js"; const WORKGROUP_SIZE = 64; const PARAM_BYTES = 192; +// Two-bucket (LOD partner) params append camPosDist (vec4: camera xyz + threshold distance) +// and lodBand after the base struct's implicit 16-byte tail padding. +const LOD_PARAM_BYTES = 224; const COUNT_U32_OFFSET = 44; const MESH_WORLD_FLOAT_OFFSET = 24; const LOCAL_SPHERE_FLOAT_OFFSET = 40; const BOUNDS_PAD_F32_OFFSET = 45; +const CAM_POS_DIST_F32_OFFSET = 48; +const LOD_BAND_F32_OFFSET = 52; const INDIRECT_ARGS_BYTES = 20; const CULL_WGSL_NO_COLOR = /* wgsl */ ` @@ -69,6 +75,74 @@ dstMatrices[outIndex]=srcMatrices[i]; dstColors[outIndex]=srcColors[i]; }`; +// Two-bucket variant used when the mesh has a LOD partner: in-frustum instances partition by camera +// distance — near keeps the mesh's own compacted bucket, far fills the partner's bucket. `isNear` +// dithers the threshold per instance by ±lodBand/2 via a pure hash of the instance index (PCG), so +// the split is deterministic frame-to-frame with no time or randomness input. +const CULL_WGSL_LOD_NO_COLOR = /* wgsl */ ` +struct CullParams{planes:array,6>,meshWorld:mat4x4,localSphere:vec4,count:u32,boundsPad:f32,camPosDist:vec4,lodBand:f32}; +@group(0)@binding(0)var srcMatrices:array>; +@group(0)@binding(1)var dstMatrices:array>; +@group(0)@binding(2)var args:array>; +@group(0)@binding(3)var params:CullParams; +@group(0)@binding(6)var lodMatrices:array>; +@group(0)@binding(7)var lodArgs:array>; +fn visible(world:mat4x4)->bool{ +let center=(world*vec4(params.localSphere.xyz,1.0)).xyz; +let sx=length(world[0].xyz); +let sy=length(world[1].xyz); +let sz=length(world[2].xyz); +let radius=params.localSphere.w*max(max(sx,sy),sz)+params.boundsPad+0.0001; +for(var i=0u;i<6u;i++){ +let p=params.planes[i]; +if(dot(p.xyz,center)+p.w < -radius){return false;} +} +return true; +} +fn isNear(world:mat4x4,i:u32)->bool{ +let center=(world*vec4(params.localSphere.xyz,1.0)).xyz; +var h=i*747796405u+2891336453u; +h=((h>>((h>>28u)+4u))^h)*277803737u; +h=(h>>22u)^h; +let dither=(f32(h)*(1.0/4294967295.0)-0.5)*params.lodBand; +return distance(center,params.camPosDist.xyz)){ +let i=gid.x; +if(i>=params.count){return;} +let world=params.meshWorld*srcMatrices[i]; +if(!visible(world)){return;} +if(isNear(world,i)){ +let outIndex=atomicAdd(&args[1],1u); +dstMatrices[outIndex]=srcMatrices[i]; +}else{ +let outIndex=atomicAdd(&lodArgs[1],1u); +lodMatrices[outIndex]=srcMatrices[i]; +} +}`; + +const CULL_WGSL_LOD_COLOR = `${CULL_WGSL_LOD_NO_COLOR} +@group(0)@binding(4)var srcColors:array>; +@group(0)@binding(5)var dstColors:array>; +@group(0)@binding(8)var lodColors:array>; +@compute @workgroup_size(64) +fn mainLodColor(@builtin(global_invocation_id) gid:vec3){ +let i=gid.x; +if(i>=params.count){return;} +let world=params.meshWorld*srcMatrices[i]; +if(!visible(world)){return;} +if(isNear(world,i)){ +let outIndex=atomicAdd(&args[1],1u); +dstMatrices[outIndex]=srcMatrices[i]; +dstColors[outIndex]=srcColors[i]; +}else{ +let outIndex=atomicAdd(&lodArgs[1],1u); +lodMatrices[outIndex]=srcMatrices[i]; +lodColors[outIndex]=srcColors[i]; +} +}`; + /** Per-render-binding GPU culling state. */ export interface ThinInstanceGpuCullState { /** @internal */ @@ -113,12 +187,28 @@ export interface ThinInstanceGpuCullState { _indexCount: number; /** @internal */ _active: boolean; + /** @internal Whether the current pipeline/bind-group/params target the two-bucket LOD variant. */ + _lodActive: boolean; + /** @internal Far-bucket compacted matrices (two-bucket variant only). */ + _lodMatrixBuffer: GPUBuffer | null; + /** @internal Far-bucket compacted colors (two-bucket variant with instance colors only). */ + _lodColorBuffer: GPUBuffer | null; + /** @internal Far-bucket indirect draw args consumed by the LOD partner's draw. */ + _lodArgsBuffer: GPUBuffer | null; + /** @internal Last index count (the partner mesh's) written to `_lodArgsBuffer`. */ + _lodIndexCount: number; + /** @internal True once the far-bucket args were zeroed for a fallback frame (reset when culling runs). */ + _lodArgsZeroed: boolean; } /** Result consumed by a material draw closure after culling has run for the active pass. */ export interface ThinInstanceGpuCullResult { readonly drawBuffers: ThinInstanceDrawBuffers; readonly argsBuffer: GPUBuffer; + /** Far-bucket compacted instance buffers for the LOD partner's draw (two-bucket variant only). */ + readonly lodDrawBuffers?: ThinInstanceDrawBuffers; + /** Far-bucket indirect args for the LOD partner's draw (two-bucket variant only). */ + readonly lodArgsBuffer?: GPUBuffer; } interface ComputeDispatch { @@ -135,6 +225,8 @@ export interface ComputeDispatchBatch extends DrawUpdateBatch { let _cachedDevice: GPUDevice | null = null; let _pipelineNoColor: GPUComputePipeline | null = null; let _pipelineColor: GPUComputePipeline | null = null; +let _pipelineLodNoColor: GPUComputePipeline | null = null; +let _pipelineLodColor: GPUComputePipeline | null = null; let _dispatchBatches: WeakMap | null = null; /** @internal Return the compute batch associated with one render task. */ @@ -182,7 +274,9 @@ export function getComputeDispatchBatch(signature: RenderTargetSignature): Compu /** Create per-binding culling state. */ export function createTiCullState(): ThinInstanceGpuCullState { - const paramsBytes = new ArrayBuffer(PARAM_BYTES); + // CPU scratch is sized for the larger two-bucket variant; single-bucket states + // only ever upload the first PARAM_BYTES of it. + const paramsBytes = new ArrayBuffer(LOD_PARAM_BYTES); return { _capacity: 0, _visibleMatrixBuffer: null, @@ -202,6 +296,12 @@ export function createTiCullState(): ThinInstanceGpuCullState { _drawBuffers: null, _indexCount: -1, _active: false, + _lodActive: false, + _lodMatrixBuffer: null, + _lodColorBuffer: null, + _lodArgsBuffer: null, + _lodIndexCount: -1, + _lodArgsZeroed: false, }; } @@ -211,10 +311,16 @@ export function destroyTiCullState(state: ThinInstanceGpuCullState): void { state._visibleColorBuffer?.destroy(); state._argsBuffer?.destroy(); state._paramsBuffer?.destroy(); + state._lodMatrixBuffer?.destroy(); + state._lodColorBuffer?.destroy(); + state._lodArgsBuffer?.destroy(); state._visibleMatrixBuffer = null; state._visibleColorBuffer = null; state._argsBuffer = null; state._paramsBuffer = null; + state._lodMatrixBuffer = null; + state._lodColorBuffer = null; + state._lodArgsBuffer = null; state._bindGroup = null; state._drawBuffers = null; } @@ -228,25 +334,20 @@ export function prepareTiCull( ti: ThinInstanceData, hasColor: boolean, context: DrawUpdateContext, - dispatchBatch?: ComputeDispatchBatch + dispatchBatch?: ComputeDispatchBatch, + lodMesh?: Mesh | null ): ThinInstanceGpuCullResult | null { const camera = context._camera; if (!ti._gpuCullingEnabled || !camera || mesh.visible === false || ti.count === 0) { - setCullActive(state, false); - state._drawBuffers = null; - return null; + return cullFallback(engine, state); } if (hasColor && !ti.colors) { - setCullActive(state, false); - state._drawBuffers = null; - return null; + return cullFallback(engine, state); } const positions = mesh._cpuPositions; if (!state._localSphereReady || state._localPositions !== positions || state._localBoundMin !== mesh.boundMin || state._localBoundMax !== mesh.boundMax) { if (!computeLocalSphere(mesh as Mesh, state._localSphere)) { - setCullActive(state, false); - state._drawBuffers = null; - return null; + return cullFallback(engine, state); } state._localSphereReady = true; state._localPositions = positions; @@ -258,17 +359,16 @@ export function prepareTiCull( const sourceMatrixBuffer = ti._gpuBuffer; const sourceColorBuffer = hasColor ? ti._colorGpuBuffer : null; if (!sourceMatrixBuffer || (hasColor && !sourceColorBuffer)) { - setCullActive(state, false); - state._drawBuffers = null; - return null; + return cullFallback(engine, state); } - ensureCullBuffers(engine, state, ti._capacity, hasColor); + const lod = lodMesh ?? null; + ensureCullBuffers(engine, state, ti._capacity, hasColor, lod !== null); const visibleMatrixBuffer = state._visibleMatrixBuffer!; const visibleColorBuffer = hasColor ? state._visibleColorBuffer! : null; const argsBuffer = state._argsBuffer!; const paramsBuffer = state._paramsBuffer!; - const pipeline = getCullPipeline(engine, hasColor); + const pipeline = getCullPipeline(engine, hasColor, lod !== null); if (state._bindGroup === null || state._srcMatrixBuffer !== sourceMatrixBuffer || state._srcColorBuffer !== sourceColorBuffer || state._hasColor !== hasColor) { const entries: GPUBindGroupEntry[] = [ @@ -280,6 +380,12 @@ export function prepareTiCull( if (hasColor) { entries.push({ binding: 4, resource: { buffer: sourceColorBuffer! } }, { binding: 5, resource: { buffer: visibleColorBuffer! } }); } + if (lod) { + entries.push({ binding: 6, resource: { buffer: state._lodMatrixBuffer! } }, { binding: 7, resource: { buffer: state._lodArgsBuffer! } }); + if (hasColor) { + entries.push({ binding: 8, resource: { buffer: state._lodColorBuffer! } }); + } + } state._bindGroup = engine._device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries }); state._srcMatrixBuffer = sourceMatrixBuffer; state._srcColorBuffer = sourceColorBuffer; @@ -288,7 +394,7 @@ export function prepareTiCull( const v = camera.viewport; const aspect = (context.targetWidth / context.targetHeight) * (v ? v.width / v.height : 1); - writeCullParams(engine, state, mesh, gpu.indexCount, ti.count, camera, aspect); + writeCullParams(engine, state, mesh, gpu.indexCount, ti.count, camera, aspect, lod); const dispatch = { pipeline, @@ -307,11 +413,87 @@ export function prepareTiCull( state._drawBuffers = { matrixBuffer: visibleMatrixBuffer, colorBuffer: visibleColorBuffer }; setCullActive(state, true); + if (lod) { + return { + drawBuffers: state._drawBuffers, + argsBuffer, + lodDrawBuffers: { matrixBuffer: state._lodMatrixBuffer!, colorBuffer: hasColor ? state._lodColorBuffer : null }, + lodArgsBuffer: state._lodArgsBuffer!, + }; + } return { drawBuffers: state._drawBuffers, argsBuffer }; } -function ensureCullBuffers(engine: EngineContext, state: ThinInstanceGpuCullState, capacity: number, hasColor: boolean): void { +/** Shared early-out: deactivate culling for this pass. When the mesh has (or had) a LOD partner, the + * partner's far-bucket args are zeroed so even a stale recorded indirect draw renders zero instances — + * the full mesh's non-culled fallback then draws ALL instances without ever double-drawing the far set. */ +function cullFallback(engine: EngineContext, state: ThinInstanceGpuCullState): null { + if (state._lodArgsBuffer && !state._lodArgsZeroed) { + engine._currentEncoder.clearBuffer(state._lodArgsBuffer, 4, 4); + state._lodArgsZeroed = true; + } + setCullActive(state, false); + state._drawBuffers = null; + return null; +} + +/** @internal Publish (or withdraw) one pass's far-bucket outputs for the LOD partner's binding. */ +export function publishTiLodBucket(ti: ThinInstanceData, signature: RenderTargetSignature, result: ThinInstanceGpuCullResult | null): void { + const lodDrawBuffers = result?.lodDrawBuffers; + const lodArgsBuffer = result?.lodArgsBuffer; + const buckets = ti._lodBuckets; + if (lodDrawBuffers && lodArgsBuffer && buckets) { + let bucket = buckets.get(signature); + if (!bucket) { + bucket = { matrixBuffer: lodDrawBuffers.matrixBuffer, colorBuffer: lodDrawBuffers.colorBuffer, argsBuffer: lodArgsBuffer, active: true }; + buckets.set(signature, bucket); + return; + } + bucket.matrixBuffer = lodDrawBuffers.matrixBuffer; + bucket.colorBuffer = lodDrawBuffers.colorBuffer; + bucket.argsBuffer = lodArgsBuffer; + bucket.active = true; + return; + } + const bucket = ti._lodBuckets?.get(signature); + if (bucket) { + bucket.active = false; + } +} + +function ensureCullBuffers(engine: EngineContext, state: ThinInstanceGpuCullState, capacity: number, hasColor: boolean, lod: boolean): void { const device = engine._device; + if (state._lodActive !== lod) { + // The params uniform size and bind-group layout differ between the single-bucket and + // two-bucket pipeline variants — rebuild both on a pairing transition. + state._paramsBuffer?.destroy(); + state._paramsBuffer = null; + state._bindGroup = null; + if (!lod) { + const lodMat = state._lodMatrixBuffer; + const lodCol = state._lodColorBuffer; + const lodArgs = state._lodArgsBuffer; + if (lodArgs && !state._lodArgsZeroed) { + // A stale partner bundle may still reference these args this frame — make it draw zero instances. + engine._currentEncoder.clearBuffer(lodArgs, 4, 4); + } + if (lodMat || lodArgs) { + // Retire (not destroy): the partner's binding may hold last frame's handles until it re-records. + retireGpuResources(engine, () => { + lodMat?.destroy(); + lodCol?.destroy(); + lodArgs?.destroy(); + }); + } + state._lodMatrixBuffer = null; + state._lodColorBuffer = null; + state._lodArgsBuffer = null; + state._lodIndexCount = -1; + state._lodArgsZeroed = false; + } + state._lodActive = lod; + bumpVisibilityEpoch(); + } if (state._capacity < capacity) { state._visibleMatrixBuffer?.destroy(); state._visibleColorBuffer?.destroy(); @@ -338,6 +520,37 @@ function ensureCullBuffers(engine: EngineContext, state: ThinInstanceGpuCullStat state._drawBuffers = null; bumpVisibilityEpoch(); } + if (lod && (!state._lodMatrixBuffer || state._lodMatrixBuffer.size < state._capacity * 64 || (hasColor && !state._lodColorBuffer))) { + const oldMat = state._lodMatrixBuffer; + const oldCol = state._lodColorBuffer; + if (oldMat) { + // Retire (not destroy): the partner's binding may hold last frame's handles until it re-records. + retireGpuResources(engine, () => { + oldMat.destroy(); + oldCol?.destroy(); + }); + } + state._lodMatrixBuffer = device.createBuffer({ + size: Math.max(state._capacity * 64, 4), + usage: BU.VERTEX | BU.STORAGE, + }); + state._lodColorBuffer = hasColor + ? device.createBuffer({ + size: Math.max(state._capacity * 16, 4), + usage: BU.VERTEX | BU.STORAGE, + }) + : null; + state._bindGroup = null; + bumpVisibilityEpoch(); + } + if (lod && !state._lodArgsBuffer) { + state._lodArgsBuffer = device.createBuffer({ + size: INDIRECT_ARGS_BYTES, + usage: BU.INDIRECT | BU.STORAGE | BU.COPY_DST, + }); + state._lodIndexCount = -1; + state._lodArgsZeroed = false; + } if (!state._argsBuffer) { state._argsBuffer = device.createBuffer({ size: INDIRECT_ARGS_BYTES, @@ -346,18 +559,34 @@ function ensureCullBuffers(engine: EngineContext, state: ThinInstanceGpuCullStat } if (!state._paramsBuffer) { state._paramsBuffer = device.createBuffer({ - size: PARAM_BYTES, + size: lod ? LOD_PARAM_BYTES : PARAM_BYTES, usage: BU.UNIFORM | BU.COPY_DST, }); } } -function getCullPipeline(engine: EngineContext, hasColor: boolean): GPUComputePipeline { +function getCullPipeline(engine: EngineContext, hasColor: boolean, lod: boolean): GPUComputePipeline { const device = engine._device; if (_cachedDevice !== device) { _cachedDevice = device; _pipelineNoColor = null; _pipelineColor = null; + _pipelineLodNoColor = null; + _pipelineLodColor = null; + } + if (lod) { + if (hasColor) { + _pipelineLodColor ??= device.createComputePipeline({ + layout: "auto", + compute: { module: device.createShaderModule({ code: CULL_WGSL_LOD_COLOR }), entryPoint: "mainLodColor" }, + }); + return _pipelineLodColor; + } + _pipelineLodNoColor ??= device.createComputePipeline({ + layout: "auto", + compute: { module: device.createShaderModule({ code: CULL_WGSL_LOD_NO_COLOR }), entryPoint: "mainLod" }, + }); + return _pipelineLodNoColor; } if (hasColor) { _pipelineColor ??= device.createComputePipeline({ @@ -373,7 +602,16 @@ function getCullPipeline(engine: EngineContext, hasColor: boolean): GPUComputePi return _pipelineNoColor; } -function writeCullParams(engine: EngineContext, state: ThinInstanceGpuCullState, mesh: Mesh, indexCount: number, instanceCount: number, camera: Camera, aspect: number): void { +function writeCullParams( + engine: EngineContext, + state: ThinInstanceGpuCullState, + mesh: Mesh, + indexCount: number, + instanceCount: number, + camera: Camera, + aspect: number, + lodMesh: Mesh | null +): void { const params = state._paramsF32; const viewProjection = getViewProjectionMatrix(camera, aspect); writeFrustumPlanes(params, viewProjection); @@ -381,6 +619,15 @@ function writeCullParams(engine: EngineContext, state: ThinInstanceGpuCullState, params.set(state._localSphere, LOCAL_SPHERE_FLOAT_OFFSET); state._paramsU32[COUNT_U32_OFFSET] = instanceCount; params[BOUNDS_PAD_F32_OFFSET] = mesh.thinInstances?._cullBoundsPad ?? 0; + if (lodMesh) { + // Distance test uses the culling pass's own camera (same one the frustum planes come from). + const cw = camera.worldMatrix; + params[CAM_POS_DIST_F32_OFFSET] = cw[12]!; + params[CAM_POS_DIST_F32_OFFSET + 1] = cw[13]!; + params[CAM_POS_DIST_F32_OFFSET + 2] = cw[14]!; + params[CAM_POS_DIST_F32_OFFSET + 3] = mesh.thinInstances?._lodDistance ?? 0; + params[LOD_BAND_F32_OFFSET] = mesh.thinInstances?._lodBand ?? 0; + } if (state._indexCount !== indexCount) { const args = state._argsData; @@ -394,7 +641,25 @@ function writeCullParams(engine: EngineContext, state: ThinInstanceGpuCullState, } else { engine._currentEncoder.clearBuffer(state._argsBuffer!, 4, 4); } - engine._device.queue.writeBuffer(state._paramsBuffer!, 0, state._paramsBytes); + if (lodMesh) { + // Far-bucket args carry the PARTNER mesh's index count; the compute pass fills its instance count. + const lodGpu = lodMesh._gpu as MeshGPU | undefined; + const lodIndexCount = lodGpu ? lodGpu.indexCount : 0; + if (state._lodIndexCount !== lodIndexCount) { + const args = state._argsData; + args[0] = lodIndexCount; + args[1] = 0; + args[2] = 0; + args[3] = 0; + args[4] = 0; + engine._device.queue.writeBuffer(state._lodArgsBuffer!, 0, args.buffer, args.byteOffset, args.byteLength); + state._lodIndexCount = lodIndexCount; + } else { + engine._currentEncoder.clearBuffer(state._lodArgsBuffer!, 4, 4); + } + state._lodArgsZeroed = false; + } + engine._device.queue.writeBuffer(state._paramsBuffer!, 0, state._paramsBytes, 0, lodMesh ? LOD_PARAM_BYTES : PARAM_BYTES); } function setCullActive(state: ThinInstanceGpuCullState, active: boolean): void { diff --git a/packages/babylon-lite/src/mesh/thin-instance.ts b/packages/babylon-lite/src/mesh/thin-instance.ts index 1618e71905..11bae2dfef 100644 --- a/packages/babylon-lite/src/mesh/thin-instance.ts +++ b/packages/babylon-lite/src/mesh/thin-instance.ts @@ -6,6 +6,19 @@ import { F32 } from "../engine/typed-arrays.js"; import type { Mat4 } from "../math/types.js"; import type { Mesh } from "./mesh.js"; +import type { RenderTargetSignature } from "../engine/render-target.js"; + +/** @internal One render pass's far-bucket output published by the GPU culler for the LOD partner's draw. */ +export interface ThinInstanceLodBucket { + /** Compacted far-bucket instance matrices (vertex + storage usage). */ + matrixBuffer: GPUBuffer; + /** Compacted far-bucket instance colors, when the culled mesh compacts colors. */ + colorBuffer: GPUBuffer | null; + /** Indirect draw args for the far bucket (partner indexCount, visible far count). */ + argsBuffer: GPUBuffer; + /** False while the source mesh's culling is not running — the partner must draw nothing. */ + active: boolean; +} /** CPU-side data backing a thin-instanced mesh: world matrices, optional colors, and GPU sync state. */ export interface ThinInstanceData { @@ -67,6 +80,18 @@ export interface ThinInstanceData { /** @internal Extra world-space radius added to every instance's culling sphere * (see `setThinInstanceCullBoundsPad`). Undefined reads as 0. */ _cullBoundsPad?: number; + /** @internal LOD partner mesh receiving this mesh's far bucket (set on the full-detail side). */ + _lodPartner?: Mesh | null; + /** @internal Camera distance (world units) at which an in-frustum instance moves to the LOD partner. */ + _lodDistance?: number; + /** @internal Width (world units) of the per-instance threshold dither window (0 = hard cut). */ + _lodBand?: number; + /** @internal Set on the LOD side: mesh whose culling produces this mesh's drawn instances. */ + _lodSource?: Mesh | null; + /** @internal Shared per-pass far-bucket outputs for an LOD pair. Its presence also marks both meshes as paired. */ + _lodBuckets?: WeakMap | null; + /** @internal True when pairing auto-enabled GPU culling on the LOD side (clearing restores it). */ + _lodAutoCull?: boolean; /** @internal Extra-owner count when shared with a clone via `cloneTransformNode` — see * resource/ref-count.ts. Absent/undefined means exactly one (implicit) owner. */ _refCount?: number; @@ -299,3 +324,141 @@ export function setThinInstanceCullBoundsPad(mesh: Mesh, pad: number): void { } ti._cullBoundsPad = pad; } + +/** Options for `setThinInstanceLodPartner`. */ +export interface ThinInstanceLodPartnerOptions { + /** Camera distance (world units) at which an in-frustum instance switches from `fullMesh` to the LOD partner. */ + distance: number; + /** Width (world units) of a per-instance threshold dither window centered on `distance` (default 0 — hard cut). */ + band?: number; +} + +/** Pair a GPU-culled thin-instanced mesh with a lower-detail partner: when `fullMesh`'s compute cull runs, + * in-frustum instances closer than `options.distance` keep drawing through `fullMesh` while farther ones are + * compacted into a second bucket drawn by `lodMesh` instead. `lodMesh` must be a normal scene mesh sharing the + * same thin-instance matrix layout (it may reference the same matrices array); while paired it draws ONLY the + * far bucket — never its own instance count — so when culling is disabled, unavailable, or the instance count + * is 0, `fullMesh` falls back to drawing ALL instances and `lodMesh` draws nothing. `options.band` dithers the + * threshold per instance by ±band/2 via a deterministic hash of the instance index. Like + * `enableThinInstanceGpuCulling`, call after `setThinInstances()` and before `registerScene()`; + * `distance`/`band` may be re-set live by calling again with the same pair. */ +export function setThinInstanceLodPartner(fullMesh: Mesh, lodMesh: Mesh, options: ThinInstanceLodPartnerOptions): void { + const ti = fullMesh.thinInstances; + const lodTi = lodMesh.thinInstances; + if (!ti || !lodTi) { + throw new Error("setThinInstanceLodPartner requires thinInstances on both meshes"); + } + if (fullMesh === lodMesh) { + throw new Error("setThinInstanceLodPartner requires two distinct meshes"); + } + if ((ti._refCount ?? 1) > 1 || (lodTi._refCount ?? 1) > 1) { + throw new Error("setThinInstanceLodPartner does not support thin-instance data shared by mesh clones"); + } + if (!Number.isFinite(options.distance) || options.distance < 0) { + throw new RangeError("setThinInstanceLodPartner distance must be a finite non-negative number"); + } + const band = options.band ?? 0; + if (!Number.isFinite(band) || band < 0) { + throw new RangeError("setThinInstanceLodPartner band must be a finite non-negative number"); + } + if (ti._lodSource) { + throw new Error("setThinInstanceLodPartner fullMesh cannot already be an LOD partner"); + } + if (lodTi._lodPartner) { + throw new Error("setThinInstanceLodPartner lodMesh cannot also own an LOD partner"); + } + if (lodTi._lodSource && lodTi._lodSource !== fullMesh) { + throw new Error("setThinInstanceLodPartner lodMesh is already paired with another source mesh"); + } + if (lodTi.colors && !ti.colors) { + throw new Error("setThinInstanceLodPartner requires source instance colors when the LOD partner uses instance colors"); + } + const previous = ti._lodPartner; + if (previous && previous !== lodMesh) { + const prevTi = previous.thinInstances; + if (prevTi && prevTi._lodSource === fullMesh) { + releaseLodConsumer(prevTi); + } + previous._clone = undefined; + ti._lodBuckets = null; + } + ti._lodPartner = lodMesh; + ti._lodDistance = options.distance; + ti._lodBand = band; + lodTi._lodSource = fullMesh; + fullMesh._clone = lodMesh._clone = "Cannot clone LOD-paired mesh"; + const buckets = ti._lodBuckets ?? new WeakMap(); + ti._lodBuckets = buckets; + lodTi._lodBuckets = buckets; + if (!lodTi._gpuCullingEnabled) { + // Route the partner through the culling draw path (so it can consume the far bucket and skip + // its own count) and make sure the cull module is fetched for its material group. + lodTi._lodAutoCull = true; + lodTi._gpuCullingEnabled = true; + lodTi._gpuVersion = -1; + lodTi._colorGpuVersion = -1; + } +} + +/** Dissolve a `setThinInstanceLodPartner` pairing: `fullMesh` culling reverts to its single-bucket output and + * the former partner returns to independent rendering (drawing its own instance count again after the next + * renderable rebuild; GPU culling auto-enabled by the pairing is switched back off). */ +export function clearThinInstanceLodPartner(fullMesh: Mesh): void { + const ti = fullMesh.thinInstances; + if (!ti?._lodPartner) { + return; + } + const lodMesh = ti._lodPartner; + const lodTi = lodMesh.thinInstances; + ti._lodPartner = null; + ti._lodDistance = undefined; + ti._lodBand = undefined; + ti._lodBuckets = null; + fullMesh._clone = undefined; + lodMesh._clone = undefined; + if (lodTi && lodTi._lodSource === fullMesh) { + releaseLodConsumer(lodTi); + } +} + +/** @internal Break any incoming or outgoing LOD pairing before a mesh's GPU resources are disposed. */ +export function _detachThinInstanceLodMesh(mesh: Mesh): void { + const ti = mesh.thinInstances; + if (!ti) { + return; + } + const source = ti._lodSource; + const sourceTi = source?.thinInstances; + if (source && sourceTi?._lodPartner === mesh) { + sourceTi._lodPartner = null; + sourceTi._lodDistance = undefined; + sourceTi._lodBand = undefined; + sourceTi._lodBuckets = null; + source._clone = undefined; + } + if (ti._lodSource) { + releaseLodConsumer(ti); + } + const partner = ti._lodPartner; + const partnerTi = partner?.thinInstances; + ti._lodPartner = null; + ti._lodDistance = undefined; + ti._lodBand = undefined; + ti._lodBuckets = null; + mesh._clone = undefined; + if (partner && partnerTi?._lodSource === mesh) { + partner._clone = undefined; + releaseLodConsumer(partnerTi); + } +} + +function releaseLodConsumer(lodTi: ThinInstanceData): void { + lodTi._lodSource = null; + lodTi._lodBuckets = null; + if (lodTi._lodAutoCull) { + lodTi._lodAutoCull = false; + lodTi._gpuCullingEnabled = false; + lodTi._gpuVersion = -1; + lodTi._colorGpuVersion = -1; + } +} diff --git a/packages/babylon-lite/src/picking/gpu-picker.ts b/packages/babylon-lite/src/picking/gpu-picker.ts index 2903c661b8..d2fa14ba87 100644 --- a/packages/babylon-lite/src/picking/gpu-picker.ts +++ b/packages/babylon-lite/src/picking/gpu-picker.ts @@ -15,7 +15,7 @@ import { resolveCameraViewport } from "../camera/viewport.js"; import { createEmptyUniformBuffer, createMappedBuffer, createUniformBuffer } from "../resource/gpu-buffers.js"; // ─── Scratch arrays — allocated once, reused across all picks ────── -const _pickVP = new F32(16); +const _pickVP = new F32(20); const PICK_MESH_UBO_BYTES = 80; const PICK_TI_UBO_BYTES = 16; const _uboScratch = new ArrayBuffer(PICK_MESH_UBO_BYTES); @@ -34,7 +34,7 @@ export interface GpuPicker { _scene: SceneContext; /** @internal 1×1 render targets (lazily created). */ _rt: PickTargets1x1 | null; - /** @internal Reusable scene UBO (64 bytes). */ + /** @internal Reusable scene UBO (80 bytes: pick VP + original framebuffer fragment coordinate). */ _sceneUbo: GPUBuffer | null; /** @internal Reusable scene bind group. */ _sceneBG: GPUBindGroup | null; @@ -99,7 +99,7 @@ function ensureTargets(engine: EngineContext, picker: GpuPicker): PickTargets1x1 function ensureSceneUbo(engine: EngineContext, picker: GpuPicker): GPUBuffer { const device = engine._device; if (!picker._sceneUbo) { - picker._sceneUbo = createEmptyUniformBuffer(engine, 64, "pick-scene-ubo"); + picker._sceneUbo = createEmptyUniformBuffer(engine, 80, "pick-scene-ubo"); const sceneBGL = getPickingSceneBGL(engine); picker._sceneBG = device.createBindGroup({ label: "pick-scene-bg", layout: sceneBGL, entries: [{ binding: 0, resource: { buffer: picker._sceneUbo } }] }); } @@ -136,8 +136,9 @@ export interface PickOptions { * * `fn shouldDiscardPick(input: PickDiscardInput) -> bool` * - * The input exposes only generic picker data: `worldPos`, `fragmentCoord` (framebuffer pixel - * coordinates), `pickId`, `thinInstanceIndex`, `hasThinInstance`, and `instanceExtras` (the + * The input exposes only generic picker data: `worldPos`, `fragmentCoord` (the selected pixel + * centre in the original backing framebuffer), `pickId`, `thinInstanceIndex`, + * `hasThinInstance`, and `instanceExtras` (the * original thin-instance matrix w lanes, zero for non-instanced meshes). Storage entries are * uploaded and bound by Lite for the current pick only. */ discard?: PickDiscardRule; @@ -271,6 +272,12 @@ async function pickAsyncImpl(picker: GpuPicker, x: number, y: number, options?: // ── Compute pick-zoomed VP (renders single pixel to 1×1 target) ── computePickVP(_pickVP, vp as unknown as Float32Array, px, py, w, h); + // The pick renders into a 1x1 target, whose fragment position is always (0.5, 0.5). Preserve the + // selected pixel's coordinate in the original backing framebuffer so consumer discard rules can + // reproduce screen-space dithering exactly. Include the camera viewport origin because visible + // material fragment coordinates are surface-wide, while px/py above are viewport-local. + _pickVP[16] = viewport.x + px + 0.5; + _pickVP[17] = viewport.y + py + 0.5; const rt = ensureTargets(engine, picker); const sceneUbo = ensureSceneUbo(engine, picker); diff --git a/packages/babylon-lite/src/picking/picking-pipeline.ts b/packages/babylon-lite/src/picking/picking-pipeline.ts index e46d9cf59a..a884817543 100644 --- a/packages/babylon-lite/src/picking/picking-pipeline.ts +++ b/packages/babylon-lite/src/picking/picking-pipeline.ts @@ -40,7 +40,7 @@ function invalidateIfNeeded(engine: EngineContext): void { export function getPickingSceneBGL(engine: EngineContext): GPUBindGroupLayout { invalidateIfNeeded(engine); if (!_sceneBGL) { - _sceneBGL = createSingleUniformBGL(engine, "picking-scene-bgl", SS.VERTEX); + _sceneBGL = createSingleUniformBGL(engine, "picking-scene-bgl", SS.VERTEX | SS.FRAGMENT); } return _sceneBGL; } diff --git a/packages/babylon-lite/src/picking/picking-shader.ts b/packages/babylon-lite/src/picking/picking-shader.ts index a07af981a2..4f53a56d39 100644 --- a/packages/babylon-lite/src/picking/picking-shader.ts +++ b/packages/babylon-lite/src/picking/picking-shader.ts @@ -37,6 +37,14 @@ function pickDiscardStorageDecls(opts?: PickingShaderOptions): string { // ─── Shared structs + fragment shader ─────────────────────────────── +const PICK_SCENE = /* wgsl */ ` +struct SceneUniforms { +viewProjection: mat4x4f, +fragmentCoord: vec2f, +}; +@group(0) @binding(0) var scene: SceneUniforms; +`; + const PICK_FS = /* wgsl */ ` struct VsOut { @builtin(position) p: vec4f, @@ -48,7 +56,7 @@ struct VsOut { }; struct FsOut { @location(0) color: vec4f, @location(1) depth: vec4f }; @fragment fn fs(input: VsOut) -> FsOut { -if (shouldDiscardPick(PickDiscardInput(input.worldPos, input.p.xy, input.pickId, input.thinInstanceIndex, input.hasThinInstance, input.instanceExtras))) { discard; } +if (shouldDiscardPick(PickDiscardInput(input.worldPos, scene.fragmentCoord, input.pickId, input.thinInstanceIndex, input.hasThinInstance, input.instanceExtras))) { discard; } let id = input.pickId; let r = f32((id >> 16u) & 0xFFu) / 255.0; let g = f32((id >> 8u) & 0xFFu) / 255.0; @@ -61,12 +69,11 @@ return FsOut(vec4f(r, g, b, 1.0), vec4f(input.p.z, 0.0, 0.0, 0.0)); export function pickingShaderSource(opts?: PickingShaderOptions): string { return /* wgsl */ ` -struct SceneUniforms { viewProjection: mat4x4f }; +${PICK_SCENE} struct MeshUniforms { world: mat4x4f, pickId: u32, }; -@group(0) @binding(0) var scene: SceneUniforms; @group(1) @binding(0) var mesh: MeshUniforms; ${PICK_DISCARD_INPUT} ${pickDiscardStorageDecls(opts)} @@ -90,11 +97,10 @@ return out; export function pickingThinInstanceShaderSource(opts?: PickingShaderOptions): string { return /* wgsl */ ` -struct SceneUniforms { viewProjection: mat4x4f }; +${PICK_SCENE} struct TIMeshUniforms { baseMeshPickId: u32, }; -@group(0) @binding(0) var scene: SceneUniforms; @group(1) @binding(0) var tiMesh: TIMeshUniforms; @group(1) @binding(1) var instances: array; ${PICK_DISCARD_INPUT} diff --git a/packages/babylon-lite/src/scene/transform-node.ts b/packages/babylon-lite/src/scene/transform-node.ts index 3997380728..a456b4335e 100644 --- a/packages/babylon-lite/src/scene/transform-node.ts +++ b/packages/babylon-lite/src/scene/transform-node.ts @@ -59,6 +59,9 @@ export function cloneTransformNode(src: SceneNode): SceneNode { } function cloneMeshNode(mesh: Mesh): Mesh { + if (mesh._clone) { + throw Error(mesh._clone); + } const meshClone = { ...mesh, name: mesh.name + "_clone", diff --git a/packages/babylon-lite/src/shadow/csm-shadow-task-hooks.ts b/packages/babylon-lite/src/shadow/csm-shadow-task-hooks.ts index ed0ef3dbdf..a40694b6fc 100644 --- a/packages/babylon-lite/src/shadow/csm-shadow-task-hooks.ts +++ b/packages/babylon-lite/src/shadow/csm-shadow-task-hooks.ts @@ -95,6 +95,8 @@ export interface CsmTaskState extends ShadowTaskInternalState { * leave its cached no-color view dangling). This is precise, unlike the global `_materialEpoch` which also * bumps for swaps of unrelated (non-caster) materials. */ _casterMatGens: Map; + /** @internal Per-caster cascade-cap snapshot used to update task membership incrementally. */ + _casterMaxCascades: Map; } export const preloadCsmShadowTaskState = preloadPcfShadowTaskState; @@ -149,28 +151,34 @@ export function ensureCsmShadowTaskState( } } if (!casterMatChanged) { - const prevSet = new Set(existing._casterMeshes); const nextSet = new Set(casterMeshes); const views = existing._materialViews; const gens = existing._casterMatGens; + const caps = existing._casterMaxCascades; + const tasks = existing._tasks; for (const m of existing._casterMeshes) { - if (!nextSet.has(m)) { - for (const t of existing._tasks) { + if (!nextSet.has(m) || m._shadowMaxCascade !== caps.get(m)) { + caps.delete(m); + for (const t of tasks) { removeMeshFromTask(t, m); } } } for (const m of casterMeshes) { - if (!prevSet.has(m) && m.material) { + const maxCascade = m._shadowMaxCascade; + if (!caps.has(m) && m.material) { const view = getNoColorView(m.material, views); - for (const t of existing._tasks) { - t.addMesh(m, { material: view }); + for (let c = 0; c < tasks.length; c++) { + if (c <= (maxCascade ?? c)) { + tasks[c]!.addMesh(m, { material: view }); + } } gens.set(m.material, effectiveCasterGen(m.material)); } + caps.set(m, maxCascade); } // Force each cascade to re-resolve its newly-added pending casters + re-bucket its binding lists. - for (const t of existing._tasks) { + for (const t of tasks) { t._lastVersion = -1; } existing._casterMeshes = casterMeshes; @@ -184,8 +192,7 @@ export function ensureCsmShadowTaskState( // state — the caller swaps to it, so the OLD task is never recorded again. Its GPU buffers may still // be referenced by the next frame command buffer, especially during async pre-first-frame construction, // so retire it only after that frame has submitted and drained. Mirrors resizeMeshGeometry. - const old = existing._task; - retireGpuResources(engine, () => old.dispose()); + retireGpuResources(engine, existing._task.dispose); } const materialViews = new Map(); @@ -215,7 +222,9 @@ export function ensureCsmShadowTaskState( const task = createRenderTask({ name: `csm${i}`, rt, clr: true, cam: camera, _skipClusteredLights: true }, engine, scene); for (const mesh of casterMeshes) { const material = mesh.material; - if (material) { + // Per-caster cascade cap: a capped caster renders only into layers 0..maxCascade (its far-layer + // shadow is sub-texel anyway), saving the excluded layers' draws + pipeline switches. + if (material && i <= (mesh._shadowMaxCascade ?? i)) { task.addMesh(mesh, { material: getNoColorView(material, materialViews) }); } } @@ -246,7 +255,9 @@ export function ensureCsmShadowTaskState( // Snapshot each caster material's gen so the next caster-set change can tell whether a CASTER material was // rebuilt (→ full rebuild) or only the set changed (→ incremental, keeping unchanged casters' packets). const casterMatGens = new Map(); + const casterMaxCascades = new Map(); for (const m of casterMeshes) { + casterMaxCascades.set(m, m._shadowMaxCascade); if (m.material) { casterMatGens.set(m.material, effectiveCasterGen(m.material)); } @@ -266,6 +277,7 @@ export function ensureCsmShadowTaskState( _materialEpoch: scene._materialEpoch, _materialViews: materialViews, _casterMatGens: casterMatGens, + _casterMaxCascades: casterMaxCascades, }; } diff --git a/tests/lite/unit/audit/no-direct-mat4-writes.test.ts b/tests/lite/unit/audit/no-direct-mat4-writes.test.ts index c66c103291..f88bb855eb 100644 --- a/tests/lite/unit/audit/no-direct-mat4-writes.test.ts +++ b/tests/lite/unit/audit/no-direct-mat4-writes.test.ts @@ -78,7 +78,7 @@ const LINE_ALLOWLIST = new Set([ // produces the same F32 bytes packMat4IntoF32 would write for offset 0. // TODO(HPM-followup): collapse into packMat4IntoF32 to fully retire the // opaque-Mat4-as-array-like dependency. - "packages/babylon-lite/src/mesh/thin-instance.ts:167", + "packages/babylon-lite/src/mesh/thin-instance.ts:192", ]); /** Variable-name heuristic: matrix-like identifiers. */ diff --git a/tests/lite/unit/mesh-clone-gpu-dispose.test.ts b/tests/lite/unit/mesh-clone-gpu-dispose.test.ts index 4a32d0dc56..e3f6ab84f7 100644 --- a/tests/lite/unit/mesh-clone-gpu-dispose.test.ts +++ b/tests/lite/unit/mesh-clone-gpu-dispose.test.ts @@ -9,6 +9,7 @@ import type { SkeletonData } from "../../../packages/babylon-lite/src/animation/ import type { EngineContext } from "../../../packages/babylon-lite/src/engine/engine"; import { ObservableVec3 } from "../../../packages/babylon-lite/src/math/observable-vec3"; import { ObservableQuat } from "../../../packages/babylon-lite/src/math/observable-quat"; +import { setThinInstanceLodPartner, type ThinInstanceData } from "../../../packages/babylon-lite/src/mesh/thin-instance"; function fakeBuffer(): GPUBuffer { return { destroy: vi.fn() } as unknown as GPUBuffer; @@ -41,6 +42,44 @@ function makeMesh(gpu: MeshGPU): Mesh { } describe("mesh clone GPU buffer ownership", () => { + it("rejects cloning a mesh while its thin-instance data participates in an LOD pair", () => { + const gpu: MeshGPU = { + positionBuffer: fakeBuffer(), + normalBuffer: fakeBuffer(), + uvBuffer: fakeBuffer(), + indexBuffer: fakeBuffer(), + indexCount: 3, + indexFormat: "uint16", + }; + const makeTi = (): ThinInstanceData => + ({ + matrices: new Float32Array(16), + count: 1, + _capacity: 1, + _version: 1, + _gpuBuffer: null, + _gpuBufferStorage: false, + _gpuVersion: 0, + _dirtyMin: 0, + _dirtyMax: 1, + _colorVersion: 0, + _colorDirtyMin: 0, + _colorDirtyMax: 0, + _colorGpuBuffer: null, + _colorGpuBufferStorage: false, + _colorGpuVersion: 0, + _gpuCullingEnabled: false, + }) satisfies ThinInstanceData; + const src = makeMesh(gpu); + const lod = makeMesh(gpu); + src.thinInstances = makeTi(); + lod.thinInstances = makeTi(); + setThinInstanceLodPartner(src, lod, { distance: 10 }); + + expect(() => cloneTransformNode(src)).toThrow("LOD-paired"); + expect(() => cloneTransformNode(lod)).toThrow("LOD-paired"); + }); + it("resizing one clone releases its claim without retiring geometry still owned by its sibling", () => { const gpu: MeshGPU = { positionBuffer: fakeBuffer(), diff --git a/tests/lite/unit/picking-discard-api.test.ts b/tests/lite/unit/picking-discard-api.test.ts index 15b00f99d7..424134682e 100644 --- a/tests/lite/unit/picking-discard-api.test.ts +++ b/tests/lite/unit/picking-discard-api.test.ts @@ -63,9 +63,12 @@ interface MockBufferRecord { function makePickerEngine(): ReturnType & { pass: { drawCalls: { group2Bound: boolean }[] }; buffers: MockBufferRecord[]; + writes: { label: string | undefined; data: Float32Array }[]; } { const base = makeEngine(); const buffers: MockBufferRecord[] = []; + const labels = new WeakMap(); + const writes: { label: string | undefined; data: Float32Array }[] = []; const passState = { drawCalls: [] as { group2Bound: boolean }[], pipeline: null as (GPURenderPipeline & { _bindGroupLayoutCount?: number }) | null, @@ -96,7 +99,13 @@ function makePickerEngine(): ReturnType & { queue: { writeBuffer: GPUQueue["writeBuffer"]; submit: GPUQueue["submit"] }; }; device.queue = { - writeBuffer() {}, + writeBuffer(buffer, _offset, data, dataOffset, size) { + const source = ArrayBuffer.isView(data) + ? new Uint8Array(data.buffer, data.byteOffset + (dataOffset ?? 0), size ?? data.byteLength - (dataOffset ?? 0)) + : new Uint8Array(data, dataOffset ?? 0, size); + const copy = source.slice().buffer; + writes.push({ label: labels.get(buffer as object), data: new Float32Array(copy) }); + }, submit() {}, }; device.createTexture = () => @@ -133,6 +142,7 @@ function makePickerEngine(): ReturnType & { mapped = false; }, } as unknown as GPUBuffer; + labels.set(buffer as object, descriptor.label); buffers.push({ descriptor: { ...descriptor }, destroy }); return buffer; }; @@ -146,7 +156,7 @@ function makePickerEngine(): ReturnType & { (globalThis as unknown as { GPUMapMode: { READ: number } }).GPUMapMode ??= { READ: 1 }; base.engine._retirements = []; - return { ...base, pass: passState, buffers }; + return { ...base, pass: passState, buffers, writes }; } function makePickScene(engine: EngineContext): { scene: Parameters[0]; mesh: Mesh; discardBuffer: GPUBuffer } { @@ -203,12 +213,14 @@ describe("picking discard shader API", () => { expect(regular).toContain("return false;"); expect(regular).toContain("out.hasThinInstance = 0u;"); expect(regular).toContain("out.thinInstanceIndex = 0xffffffffu;"); + expect(regular).toContain("PickDiscardInput(input.worldPos, scene.fragmentCoord"); expect(thin).toContain("fn shouldDiscardPick(input: PickDiscardInput) -> bool"); expect(thin).toContain("return false;"); expect(thin).toContain("out.hasThinInstance = 1u;"); expect(thin).toContain("out.thinInstanceIndex = instanceIndex;"); expect(thin).toContain("out.instanceExtras = vec4f(m[0].w, m[1].w, m[2].w, m[3].w);"); + expect(thin).toContain("PickDiscardInput(input.worldPos, scene.fragmentCoord"); }); it("injects a custom discard rule into regular and thin-instance picking shaders", () => { @@ -223,7 +235,7 @@ return input.hasThinInstance == 1u && input.instanceExtras.x > 4.0; expect(regular).toContain(discardWgsl); expect(thin).toContain(discardWgsl); const discardCall = - "if (shouldDiscardPick(PickDiscardInput(input.worldPos, input.p.xy, input.pickId, input.thinInstanceIndex, input.hasThinInstance, input.instanceExtras))) { discard; }"; + "if (shouldDiscardPick(PickDiscardInput(input.worldPos, scene.fragmentCoord, input.pickId, input.thinInstanceIndex, input.hasThinInstance, input.instanceExtras))) { discard; }"; expect(regular).toContain(discardCall); expect(thin).toContain(discardCall); expect(thin).toContain("let world = mat4x4f("); @@ -295,6 +307,40 @@ fn shouldDiscardPick(input: PickDiscardInput) -> bool { return data[0].x > 1.0 & expect(pass.drawCalls).toEqual([{ group2Bound: true }]); }); + it("uploads the selected pixel center in original framebuffer coordinates", async () => { + const { engine } = makePickerEngine(); + const { scene } = makePickScene(engine); + scene.camera!.viewport = { x: 0.25, y: 0.25, width: 0.5, height: 0.5 }; + const writeBuffer = vi.spyOn(engine._device.queue, "writeBuffer"); + const picker = createGpuPicker(scene); + + await pickAsync(picker, 20, 20); + + const sceneWrite = writeBuffer.mock.calls.find((call) => call[2] instanceof Float32Array && call[2].length === 20); + expect(sceneWrite).toBeDefined(); + expect(Array.from((sceneWrite![2] as Float32Array).subarray(16, 18))).toEqual([20.5, 20.5]); + }); + + it("publishes the selected original framebuffer pixel under DPR and a nonzero viewport", async () => { + const { engine, writes } = makePickerEngine(); + const { scene } = makePickScene(engine); + const surface = scene.surface as unknown as { canvas: { width: number; height: number; clientWidth: number; clientHeight: number } }; + surface.canvas = { width: 128, height: 96, clientWidth: 64, clientHeight: 48 }; + (scene.camera as unknown as { viewport: { x: number; y: number; width: number; height: number } }).viewport = { + x: 0.25, + y: 0.25, + width: 0.5, + height: 0.5, + }; + + await pickAsync(createGpuPicker(scene), 20, 15); + + const sceneWrite = writes.find((write) => write.label === "pick-scene-ubo"); + expect(sceneWrite?.data).toHaveLength(20); + expect(sceneWrite?.data[16]).toBe(40.5); + expect(sceneWrite?.data[17]).toBe(30.5); + }); + it("destroys temporary pick buffers after the pick submission readback completes", async () => { const { engine, buffers } = makePickerEngine(); const { scene } = makePickScene(engine); diff --git a/tests/lite/unit/shadow-caster-max-cascade.test.ts b/tests/lite/unit/shadow-caster-max-cascade.test.ts new file mode 100644 index 0000000000..9b6ccf343a --- /dev/null +++ b/tests/lite/unit/shadow-caster-max-cascade.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, it, vi } from "vitest"; + +import type { EngineContext } from "../../../packages/babylon-lite/src/engine/engine"; +import type { RenderTask } from "../../../packages/babylon-lite/src/frame-graph/render-task"; +import type { Material, MaterialView } from "../../../packages/babylon-lite/src/material/material"; +import type { Mesh } from "../../../packages/babylon-lite/src/mesh/mesh"; +import type { SceneContext } from "../../../packages/babylon-lite/src/scene/scene-core"; +import { _getShadowCasterMaxCascade, setShadowCasterMaxCascade } from "../../../packages/babylon-lite/src/frame-graph/shadow-inputs"; +import { ensureCsmShadowTaskState, type CsmConfig, type CsmTaskState } from "../../../packages/babylon-lite/src/shadow/csm-shadow-task-hooks"; +import type { ShadowGenerator } from "../../../packages/babylon-lite/src/shadow/shadow-generator"; + +function makeMesh(material?: Material): Mesh { + return { material } as unknown as Mesh; +} + +function makeTask(mesh: Mesh): RenderTask { + const task = { + _pendingMeshes: [{ mesh, material: mesh.material }], + _renderables: [], + _opaqueBindings: [], + _directBindings: [], + _transparentBindings: [], + _opaqueBundles: [], + _lastVersion: 0, + addMesh: vi.fn((added: Mesh, opts?: { material?: Material }) => { + task._pendingMeshes.push({ mesh: added, material: opts?.material ?? added.material }); + }), + }; + return task as unknown as RenderTask; +} + +describe("setShadowCasterMaxCascade", () => { + it("defaults every mesh to casting into all cascades (Infinity)", () => { + expect(_getShadowCasterMaxCascade(makeMesh())).toBe(Infinity); + }); + + it("stores a per-mesh cap without affecting other meshes", () => { + const capped = makeMesh(); + const other = makeMesh(); + setShadowCasterMaxCascade(capped, 0); + expect(_getShadowCasterMaxCascade(capped)).toBe(0); + expect(_getShadowCasterMaxCascade(other)).toBe(Infinity); + }); + + it("rejects caps that are not non-negative integer cascade indexes", () => { + const mesh = makeMesh(); + expect(() => setShadowCasterMaxCascade(mesh, -1)).toThrow(RangeError); + expect(() => setShadowCasterMaxCascade(mesh, 0.5)).toThrow(RangeError); + expect(() => setShadowCasterMaxCascade(mesh, Number.NaN)).toThrow(RangeError); + }); + + it("restores all-cascade casting when re-set to Infinity", () => { + const mesh = makeMesh(); + setShadowCasterMaxCascade(mesh, 0); + setShadowCasterMaxCascade(mesh, Infinity); + expect(_getShadowCasterMaxCascade(mesh)).toBe(Infinity); + }); + + it("reassigns an existing caster when a re-supplied list changes its cap", () => { + const material = { _uboVersion: 0 } as Material; + const view = {} as MaterialView; + const mesh = makeMesh(material); + const tasks = [makeTask(mesh), makeTask(mesh), makeTask(mesh)]; + const state = { + _tasks: tasks, + _casterMeshes: [mesh], + _renderableVersion: 1, + _materialEpoch: 1, + _materialViews: new Map([[material, view]]), + _casterMatGens: new Map([[material, 0]]), + _casterMaxCascades: new Map([[mesh, undefined]]), + } as unknown as CsmTaskState; + const scene = { _renderableVersion: 2, _materialEpoch: 1 } as SceneContext; + + setShadowCasterMaxCascade(mesh, 0); + const result = ensureCsmShadowTaskState({} as EngineContext, scene, {} as ShadowGenerator, {} as CsmConfig, [mesh], state); + + expect(result).toBe(state); + expect(tasks[0]!.addMesh).toHaveBeenCalledOnce(); + expect(tasks[1]!.addMesh).not.toHaveBeenCalled(); + expect(tasks[2]!.addMesh).not.toHaveBeenCalled(); + expect(tasks[0]!._pendingMeshes.map((entry) => entry.mesh)).toEqual([mesh]); + expect(tasks[1]!._pendingMeshes).toHaveLength(0); + expect(tasks[2]!._pendingMeshes).toHaveLength(0); + expect(state._casterMaxCascades.get(mesh)).toBe(0); + + ensureCsmShadowTaskState({} as EngineContext, scene, {} as ShadowGenerator, {} as CsmConfig, [], state); + expect(state._casterMaxCascades.has(mesh)).toBe(false); + ensureCsmShadowTaskState({} as EngineContext, scene, {} as ShadowGenerator, {} as CsmConfig, [mesh], state); + expect(tasks[0]!.addMesh).toHaveBeenCalledTimes(2); + }); +}); diff --git a/tests/lite/unit/thin-instance-gpu-culling.test.ts b/tests/lite/unit/thin-instance-gpu-culling.test.ts index d63e08cc8f..c68ae366b0 100644 --- a/tests/lite/unit/thin-instance-gpu-culling.test.ts +++ b/tests/lite/unit/thin-instance-gpu-culling.test.ts @@ -6,9 +6,11 @@ import type { RenderTargetSignature } from "../../../packages/babylon-lite/src/e import type { Mat4 } from "../../../packages/babylon-lite/src/math/types"; import type { Mesh, MeshGPU } from "../../../packages/babylon-lite/src/mesh/mesh"; import { updateMeshGeometry } from "../../../packages/babylon-lite/src/mesh/mesh-factories"; -import { createTiCullState, getComputeDispatchBatch, prepareTiCull } from "../../../packages/babylon-lite/src/mesh/thin-instance-gpu-culling"; -import type { ThinInstanceData } from "../../../packages/babylon-lite/src/mesh/thin-instance"; -import type { DrawUpdateContext } from "../../../packages/babylon-lite/src/render/renderable"; +import { tryBind } from "../../../packages/babylon-lite/src/mesh/thin-instance-cull-binding"; +import { createTiCullState, getComputeDispatchBatch, prepareTiCull, publishTiLodBucket } from "../../../packages/babylon-lite/src/mesh/thin-instance-gpu-culling"; +import { clearThinInstanceLodPartner, setThinInstanceLodPartner, type ThinInstanceData } from "../../../packages/babylon-lite/src/mesh/thin-instance"; +import type { DrawUpdateContext, Renderable } from "../../../packages/babylon-lite/src/render/renderable"; +import type { SceneContext } from "../../../packages/babylon-lite/src/scene/scene"; function identity(): Mat4 { return new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) as unknown as Mat4; @@ -145,4 +147,152 @@ describe("thin-instance GPU culling submission", () => { expect(writeBuffer.mock.calls.filter((call) => call[0] === state._argsBuffer && call[4] === 20)).toHaveLength(1); expect(buffers.some((buffer) => (buffer.descriptor.usage & GPUBufferUsage.INDIRECT) !== 0)).toBe(true); }); + + it("allocates and publishes a second compacted bucket for an LOD partner", () => { + const buffers: (GPUBuffer & { descriptor: GPUBufferDescriptor })[] = []; + const writeBuffer = vi.fn(); + const pipeline = { + getBindGroupLayout: vi.fn(() => ({}) as GPUBindGroupLayout), + } as unknown as GPUComputePipeline; + const device = { + createBuffer: vi.fn((descriptor: GPUBufferDescriptor) => { + const buffer = { descriptor, size: descriptor.size, destroy: vi.fn() } as unknown as GPUBuffer & { descriptor: GPUBufferDescriptor }; + buffers.push(buffer); + return buffer; + }), + createShaderModule: vi.fn(() => ({}) as GPUShaderModule), + createComputePipeline: vi.fn(() => pipeline), + createBindGroup: vi.fn(() => ({}) as GPUBindGroup), + queue: { writeBuffer }, + } as unknown as GPUDevice; + const engine = { + _device: device, + _currentEncoder: { + beginComputePass: () => + ({ + setPipeline() {}, + setBindGroup() {}, + dispatchWorkgroups() {}, + end() {}, + }) as unknown as GPUComputePassEncoder, + clearBuffer: vi.fn(), + } as unknown as GPUCommandEncoder, + } as unknown as EngineContext; + const ti = makeThinInstances(8); + const mesh = { + visible: true, + worldMatrix: identity(), + _cpuPositions: new Float32Array([-1, -1, -1, 1, 1, 1]), + boundMin: [-1, -1, -1], + boundMax: [1, 1, 1], + thinInstances: ti, + } as unknown as Mesh; + const gpu = { + positionBuffer: {} as GPUBuffer, + normalBuffer: {} as GPUBuffer, + uvBuffer: {} as GPUBuffer, + indexBuffer: {} as GPUBuffer, + indexCount: 3, + indexFormat: "uint32", + hasUv: false, + hasUv2: false, + hasTangent: false, + hasColor: false, + } satisfies MeshGPU; + mesh._gpu = gpu; + const lodMesh = { _gpu: { ...gpu, indexCount: 12 } } as unknown as Mesh; + const state = createTiCullState(); + + const result = prepareTiCull(engine, state, mesh, gpu, ti, false, { targetWidth: 800, targetHeight: 600, _camera: makeCamera() }, undefined, lodMesh); + + expect(result?.lodDrawBuffers?.matrixBuffer).toBe(state._lodMatrixBuffer); + expect(result?.lodDrawBuffers?.colorBuffer).toBeNull(); + expect(result?.lodArgsBuffer).toBe(state._lodArgsBuffer); + expect(state._paramsBuffer?.size).toBe(224); + expect(buffers.filter((buffer) => (buffer.descriptor.usage & GPUBufferUsage.INDIRECT) !== 0)).toHaveLength(2); + const lodArgsWrite = writeBuffer.mock.calls.find((call) => call[0] === state._lodArgsBuffer); + expect(lodArgsWrite).toBeDefined(); + expect(Array.from(new Uint32Array(lodArgsWrite![2] as ArrayBuffer, lodArgsWrite![3] as number, 5))).toEqual([12, 0, 0, 0, 0]); + }); +}); + +describe("thin-instance LOD cull binding", () => { + it("reads the current bucket at draw time and falls back after the pairing is cleared", () => { + const source = { thinInstances: makeThinInstances(2) } as unknown as Mesh; + const partner = { thinInstances: makeThinInstances(2) } as unknown as Mesh; + setThinInstanceLodPartner(source, partner, { distance: 10 }); + const signature = {} as RenderTargetSignature; + const scene = { _meshDisposables: new Map([[partner, []]]) } as unknown as SceneContext; + const renderable = {} as Renderable; + const binding = tryBind(renderable, scene, partner, {} as EngineContext, false, false, undefined, signature)!; + + expect(renderable._direct).toBe(true); + binding.update({ targetWidth: 1, targetHeight: 1 }); + expect(binding.cullDrawBufs).toBeNull(); + + const matrixBuffer = {} as GPUBuffer; + const argsBuffer = {} as GPUBuffer; + publishTiLodBucket(source.thinInstances!, signature, { + drawBuffers: { matrixBuffer: {} as GPUBuffer, colorBuffer: null }, + argsBuffer: {} as GPUBuffer, + lodDrawBuffers: { matrixBuffer, colorBuffer: null }, + lodArgsBuffer: argsBuffer, + }); + + expect(binding.cullDrawBufs).toMatchObject({ matrixBuffer, colorBuffer: null }); + const culledPass = { drawIndexedIndirect: vi.fn(), drawIndexed: vi.fn() }; + binding.draw(culledPass as unknown as GPURenderPassEncoder, 36, 2); + expect(culledPass.drawIndexedIndirect).toHaveBeenCalledWith(argsBuffer, 0); + + clearThinInstanceLodPartner(source); + const fallbackPass = { drawIndexedIndirect: vi.fn(), drawIndexed: vi.fn() }; + binding.draw(fallbackPass as unknown as GPURenderPassEncoder, 36, 2); + expect(fallbackPass.drawIndexed).toHaveBeenCalledWith(36, 2); + }); + + it("rejects transparent partners and missing compacted color data", () => { + const source = { thinInstances: makeThinInstances(1) } as unknown as Mesh; + const partner = { thinInstances: makeThinInstances(1) } as unknown as Mesh; + source.thinInstances!.colors = new Float32Array(4); + partner.thinInstances!.colors = new Float32Array(4); + setThinInstanceLodPartner(source, partner, { distance: 10 }); + const signature = {} as RenderTargetSignature; + const scene = { _meshDisposables: new Map([[partner, []]]) } as unknown as SceneContext; + + expect(() => tryBind({} as Renderable, scene, partner, {} as EngineContext, false, true, undefined, signature)).toThrow("opaque"); + + const binding = tryBind({} as Renderable, scene, partner, {} as EngineContext, true, false, undefined, signature)!; + publishTiLodBucket(source.thinInstances!, signature, { + drawBuffers: { matrixBuffer: {} as GPUBuffer, colorBuffer: null }, + argsBuffer: {} as GPUBuffer, + lodDrawBuffers: { matrixBuffer: {} as GPUBuffer, colorBuffer: null }, + lodArgsBuffer: {} as GPUBuffer, + }); + expect(() => binding.cullDrawBufs).toThrow("provide instance colors"); + }); + + it("reuses source cull-state disposal and does not add partner cleanup callbacks on rebind", () => { + const source = { thinInstances: makeThinInstances(1) } as unknown as Mesh; + const partner = { thinInstances: makeThinInstances(1) } as unknown as Mesh; + source._gpu = { indexCount: 3 } as MeshGPU; + setThinInstanceLodPartner(source, partner, { distance: 10 }); + const sourceDisposers: Array<() => void> = []; + const partnerDisposers: Array<() => void> = []; + const scene = { + _meshDisposables: new Map([ + [source, sourceDisposers], + [partner, partnerDisposers], + ]), + } as unknown as SceneContext; + const signature = {} as RenderTargetSignature; + const sourceRenderable = {} as Renderable; + + expect(tryBind(sourceRenderable, scene, source, {} as EngineContext, false, false, undefined, signature)).toBeDefined(); + expect(tryBind(sourceRenderable, scene, source, {} as EngineContext, false, false, undefined, signature)).toBeDefined(); + expect(sourceDisposers).toHaveLength(1); + + expect(tryBind({} as Renderable, scene, partner, {} as EngineContext, false, false, undefined, signature)).toBeDefined(); + expect(tryBind({} as Renderable, scene, partner, {} as EngineContext, false, false, undefined, signature)).toBeDefined(); + expect(partnerDisposers).toHaveLength(0); + }); }); diff --git a/tests/lite/unit/thin-instance-lod.test.ts b/tests/lite/unit/thin-instance-lod.test.ts new file mode 100644 index 0000000000..a3308f4dee --- /dev/null +++ b/tests/lite/unit/thin-instance-lod.test.ts @@ -0,0 +1,148 @@ +import { describe, expect, it } from "vitest"; + +import type { Mesh } from "../../../packages/babylon-lite/src/mesh/mesh"; +import { disposeMeshGpu } from "../../../packages/babylon-lite/src/mesh/mesh-dispose"; +import { _detachThinInstanceLodMesh, clearThinInstanceLodPartner, setThinInstanceLodPartner, type ThinInstanceData } from "../../../packages/babylon-lite/src/mesh/thin-instance"; + +function makeThinInstances(withColors = false): ThinInstanceData { + return { + matrices: new Float32Array(16), + count: 1, + _capacity: 1, + _version: 1, + _gpuBuffer: null, + _gpuBufferStorage: false, + _gpuVersion: 0, + _dirtyMin: 0, + _dirtyMax: 1, + colors: withColors ? new Float32Array([1, 1, 1, 1]) : null, + _colorVersion: 0, + _colorDirtyMin: 0, + _colorDirtyMax: 0, + _colorGpuBuffer: null, + _colorGpuBufferStorage: false, + _colorGpuVersion: 0, + _gpuCullingEnabled: false, + }; +} + +function makeMesh(withColors = false): Mesh { + return { thinInstances: makeThinInstances(withColors) } as unknown as Mesh; +} + +function addDisposableGpu(mesh: Mesh): void { + const buffer = () => ({ destroy() {} }) as unknown as GPUBuffer; + mesh._gpu = { + positionBuffer: buffer(), + normalBuffer: buffer(), + uvBuffer: buffer(), + indexBuffer: buffer(), + indexCount: 3, + indexFormat: "uint16", + }; +} + +describe("thin-instance LOD pairing", () => { + it("pairs, updates, and clears a partner while restoring auto-enabled culling", () => { + const full = makeMesh(true); + const lod = makeMesh(true); + + setThinInstanceLodPartner(full, lod, { distance: 20, band: 4 }); + + expect(full.thinInstances!._lodPartner).toBe(lod); + expect(full.thinInstances!._lodDistance).toBe(20); + expect(full.thinInstances!._lodBand).toBe(4); + expect(lod.thinInstances!._lodSource).toBe(full); + expect(lod.thinInstances!._lodBuckets).toBe(full.thinInstances!._lodBuckets); + expect(full._clone).toContain("LOD-paired"); + expect(lod._clone).toBe(full._clone); + expect(lod.thinInstances!._lodAutoCull).toBe(true); + expect(lod.thinInstances!._gpuCullingEnabled).toBe(true); + + setThinInstanceLodPartner(full, lod, { distance: 30 }); + expect(full.thinInstances!._lodDistance).toBe(30); + expect(full.thinInstances!._lodBand).toBe(0); + + clearThinInstanceLodPartner(full); + + expect(full.thinInstances!._lodPartner).toBeNull(); + expect(full.thinInstances!._lodDistance).toBeUndefined(); + expect(full.thinInstances!._lodBand).toBeUndefined(); + expect(lod.thinInstances!._lodSource).toBeNull(); + expect(full._clone).toBeUndefined(); + expect(lod._clone).toBeUndefined(); + expect(lod.thinInstances!._lodAutoCull).toBe(false); + expect(lod.thinInstances!._gpuCullingEnabled).toBe(false); + }); + + it("cleans the previous partner when the source is re-paired", () => { + const full = makeMesh(); + const first = makeMesh(); + const second = makeMesh(); + + setThinInstanceLodPartner(full, first, { distance: 10 }); + setThinInstanceLodPartner(full, second, { distance: 20 }); + + expect(first.thinInstances!._lodSource).toBeNull(); + expect(first._clone).toBeUndefined(); + expect(first.thinInstances!._gpuCullingEnabled).toBe(false); + expect(full.thinInstances!._lodPartner).toBe(second); + expect(second.thinInstances!._lodSource).toBe(full); + }); + + it("rejects invalid options, role conflicts, reused partners, and missing source colors", () => { + const full = makeMesh(); + const lod = makeMesh(); + + expect(() => setThinInstanceLodPartner(full, lod, { distance: -1 })).toThrow(RangeError); + expect(() => setThinInstanceLodPartner(full, lod, { distance: Number.NaN })).toThrow(RangeError); + expect(() => setThinInstanceLodPartner(full, lod, { distance: 1, band: -1 })).toThrow(RangeError); + expect(() => setThinInstanceLodPartner(full, full, { distance: 1 })).toThrow("two distinct meshes"); + expect(() => setThinInstanceLodPartner(full, makeMesh(true), { distance: 1 })).toThrow("source instance colors"); + full.thinInstances!._refCount = 2; + expect(() => setThinInstanceLodPartner(full, lod, { distance: 1 })).toThrow("shared by mesh clones"); + full.thinInstances!._refCount = 1; + + setThinInstanceLodPartner(full, lod, { distance: 1 }); + expect(() => setThinInstanceLodPartner(makeMesh(), lod, { distance: 1 })).toThrow("already paired"); + expect(() => setThinInstanceLodPartner(lod, makeMesh(), { distance: 1 })).toThrow("cannot already be an LOD partner"); + + const lodOwner = makeMesh(); + const chained = makeMesh(); + setThinInstanceLodPartner(lodOwner, chained, { distance: 1 }); + expect(() => setThinInstanceLodPartner(makeMesh(), lodOwner, { distance: 1 })).toThrow("cannot also own an LOD partner"); + }); + + it("detaches both sides when either mesh is disposed", () => { + const full = makeMesh(); + const lod = makeMesh(); + setThinInstanceLodPartner(full, lod, { distance: 10 }); + + _detachThinInstanceLodMesh(lod); + expect(full.thinInstances!._lodPartner).toBeNull(); + expect(lod.thinInstances!._lodSource).toBeNull(); + expect(full._clone).toBeUndefined(); + expect(lod._clone).toBeUndefined(); + + setThinInstanceLodPartner(full, lod, { distance: 10 }); + _detachThinInstanceLodMesh(full); + expect(full.thinInstances!._lodPartner).toBeNull(); + expect(lod.thinInstances!._lodSource).toBeNull(); + expect(full._clone).toBeUndefined(); + expect(lod._clone).toBeUndefined(); + expect(lod.thinInstances!._gpuCullingEnabled).toBe(false); + }); + + it("detaches the pairing when the last thin-instance owner is disposed", () => { + const full = makeMesh(); + const lod = makeMesh(); + addDisposableGpu(full); + setThinInstanceLodPartner(full, lod, { distance: 10 }); + + disposeMeshGpu(full); + + expect(full.thinInstances!._lodPartner).toBeNull(); + expect(lod.thinInstances!._lodSource).toBeNull(); + expect(lod.thinInstances!._gpuCullingEnabled).toBe(false); + }); +}); From b34c0e45e82286d11184810d963a854f710c6d19 Mon Sep 17 00:00:00 2001 From: David Catuhe Date: Thu, 16 Jul 2026 15:39:59 -0700 Subject: [PATCH 2/7] chore: retry PR validation after GitHub 503 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 956e9bbb-057c-48ee-87ad-0e25093fd1ce From 19814cd578e82605ab91dd949c3c895b6d62b98d Mon Sep 17 00:00:00 2001 From: David Catuhe Date: Thu, 16 Jul 2026 15:55:04 -0700 Subject: [PATCH 3/7] chore: restart Azure PR validation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 956e9bbb-057c-48ee-87ad-0e25093fd1ce From ab5f8cda75ff05fa01bba4b3b52e4e6a5c30f26e Mon Sep 17 00:00:00 2001 From: David Catuhe Date: Thu, 16 Jul 2026 19:19:20 -0700 Subject: [PATCH 4/7] test: cover transient release marker API failures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 956e9bbb-057c-48ee-87ad-0e25093fd1ce --- .../unit/validate-pr-release-markers.test.ts | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/lite/unit/validate-pr-release-markers.test.ts diff --git a/tests/lite/unit/validate-pr-release-markers.test.ts b/tests/lite/unit/validate-pr-release-markers.test.ts new file mode 100644 index 0000000000..3001f8a6b5 --- /dev/null +++ b/tests/lite/unit/validate-pr-release-markers.test.ts @@ -0,0 +1,55 @@ +import { spawnSync } from "child_process"; +import { mkdtempSync, rmSync, writeFileSync } from "fs"; +import { tmpdir } from "os"; +import { join, resolve } from "path"; +import { pathToFileURL } from "url"; + +import { describe, expect, it } from "vitest"; + +describe("release marker PR metadata fetch", () => { + it("retries a transient GitHub server error before validating the PR", () => { + const tempDir = mkdtempSync(join(tmpdir(), "release-marker-retry-")); + const preloadPath = join(tempDir, "mock-github-fetch.mjs"); + writeFileSync( + preloadPath, + ` +import { createRequire, syncBuiltinESMExports } from "node:module"; +const require = createRequire(import.meta.url); +require("node:child_process").execFileSync = () => "feat: test"; +syncBuiltinESMExports(); +let attempts = 0; +globalThis.fetch = async () => { + attempts++; + console.log("MOCK_GITHUB_ATTEMPT=" + attempts); + if (attempts === 1) { + return new Response(null, { status: 503, statusText: "Service Unavailable" }); + } + return new Response(JSON.stringify({ title: "feat: test", body: "", labels: [] }), { + status: 200, + headers: { "content-type": "application/json" }, + }); +}; +` + ); + + try { + const result = spawnSync(process.execPath, ["--import", "tsx", "--import", pathToFileURL(preloadPath).href, resolve("scripts/validate-pr-release-markers.ts")], { + cwd: resolve("."), + encoding: "utf8", + env: { + ...process.env, + GITHUB_REPOSITORY: "BabylonJS/Babylon-Lite", + PR_NUMBER: "421", + GITHUB_TOKEN: "test-token", + }, + }); + + expect(result.status, result.stderr).toBe(0); + expect(result.stdout).toContain("MOCK_GITHUB_ATTEMPT=1"); + expect(result.stdout).toContain("MOCK_GITHUB_ATTEMPT=2"); + expect(result.stdout).toContain("No breaking-change marker detected"); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } + }); +}); From 3d70d2fb127edff66fb9956ef33f82b12b2b0c71 Mon Sep 17 00:00:00 2001 From: David Catuhe Date: Thu, 16 Jul 2026 19:21:11 -0700 Subject: [PATCH 5/7] fix(ci): retry transient GitHub metadata fetches Retry GitHub 429 and 5xx responses before failing release-marker validation, preventing short API outages from failing required PR checks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 956e9bbb-057c-48ee-87ad-0e25093fd1ce --- scripts/validate-pr-release-markers.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/scripts/validate-pr-release-markers.ts b/scripts/validate-pr-release-markers.ts index bb6c7f9309..3da0ee44f4 100644 --- a/scripts/validate-pr-release-markers.ts +++ b/scripts/validate-pr-release-markers.ts @@ -14,6 +14,20 @@ type GitHubPullRequestResponse = { const BREAKING_MARKER = /^(?:BREAKING[ -]CHANGE:|[a-z][a-z0-9-]*(?:\([^)]+\))?!:)/m; const MALFORMED_BREAKING_CHANGE = /^\s*BREAKING[ -]CHANGE(?!:)/im; +const GITHUB_FETCH_ATTEMPTS = 3; + +async function fetchGitHubPullRequest(url: string, headers: Record): Promise { + let response: Response | undefined; + for (let attempt = 1; attempt <= GITHUB_FETCH_ATTEMPTS; attempt++) { + response = await fetch(url, { headers }); + if (response.ok || (response.status !== 429 && response.status < 500) || attempt === GITHUB_FETCH_ATTEMPTS) { + return response; + } + console.warn(`GitHub PR metadata request returned ${response.status} ${response.statusText}; retrying (${attempt}/${GITHUB_FETCH_ATTEMPTS}).`); + await new Promise((resolve) => setTimeout(resolve, attempt * 250)); + } + return response!; +} function cleanAzureValue(value: string | undefined): string | undefined { if (!value || value.startsWith("$(")) { @@ -85,7 +99,7 @@ async function getPullRequestFromGitHub(): Promise }; headers.Authorization = `Bearer ${githubToken}`; - const response = await fetch(`https://api.github.com/repos/${repository}/pulls/${pullRequestNumber}`, { headers }); + const response = await fetchGitHubPullRequest(`https://api.github.com/repos/${repository}/pulls/${pullRequestNumber}`, headers); if (!response.ok) { fail(`Could not read GitHub PR metadata (${response.status} ${response.statusText}); refusing to skip breaking-label enforcement.`); } From 3fb2032e940f013030c4c56ba037bf6ad23ae0a4 Mon Sep 17 00:00:00 2001 From: David Catuhe Date: Thu, 16 Jul 2026 20:27:29 -0700 Subject: [PATCH 6/7] test: require release marker response cleanup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 956e9bbb-057c-48ee-87ad-0e25093fd1ce --- tests/lite/unit/validate-pr-release-markers.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/lite/unit/validate-pr-release-markers.test.ts b/tests/lite/unit/validate-pr-release-markers.test.ts index 3001f8a6b5..9adbae8cf2 100644 --- a/tests/lite/unit/validate-pr-release-markers.test.ts +++ b/tests/lite/unit/validate-pr-release-markers.test.ts @@ -22,7 +22,14 @@ globalThis.fetch = async () => { attempts++; console.log("MOCK_GITHUB_ATTEMPT=" + attempts); if (attempts === 1) { - return new Response(null, { status: 503, statusText: "Service Unavailable" }); + return new Response( + new ReadableStream({ + cancel() { + console.log("MOCK_GITHUB_BODY_CANCELLED"); + }, + }), + { status: 503, statusText: "Service Unavailable" } + ); } return new Response(JSON.stringify({ title: "feat: test", body: "", labels: [] }), { status: 200, @@ -47,6 +54,7 @@ globalThis.fetch = async () => { expect(result.status, result.stderr).toBe(0); expect(result.stdout).toContain("MOCK_GITHUB_ATTEMPT=1"); expect(result.stdout).toContain("MOCK_GITHUB_ATTEMPT=2"); + expect(result.stdout).toContain("MOCK_GITHUB_BODY_CANCELLED"); expect(result.stdout).toContain("No breaking-change marker detected"); } finally { rmSync(tempDir, { recursive: true, force: true }); From bbc6ab952a9613ee154eeafd9db790a9a165ab2f Mon Sep 17 00:00:00 2001 From: David Catuhe Date: Thu, 16 Jul 2026 20:28:45 -0700 Subject: [PATCH 7/7] fix(ci): cancel transient GitHub response bodies Cancel retryable response streams before the next metadata request so undici can release and reuse connections during GitHub API outages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 956e9bbb-057c-48ee-87ad-0e25093fd1ce --- scripts/validate-pr-release-markers.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/validate-pr-release-markers.ts b/scripts/validate-pr-release-markers.ts index 3da0ee44f4..b72c7234c1 100644 --- a/scripts/validate-pr-release-markers.ts +++ b/scripts/validate-pr-release-markers.ts @@ -23,6 +23,7 @@ async function fetchGitHubPullRequest(url: string, headers: Record setTimeout(resolve, attempt * 250)); }