fix(plugin-trees): consume dirty marks so scene-ready fires for plant scenes (#476)
Instanced plant kinds (trees/grass/flowers) never cleared their dirty marks: FloorElevationSystem deliberately leaves the mark for kinds with a def.system, expecting that system to clear it after its own work, but InstancedKindSystem never participated in the dirty protocol. The marks lived forever, hasPendingSceneBuildWork() never went false, and the Viewer's scene-ready signal stalled at SCENE_READY_MAX_WAIT_FRAMES on every plant-containing scene — measured on the headless bake worker as ~190s (180 frames x ~1s SwiftShader frames) of pure cap-wait per bake. Clear the marks in a priority-2 useFrame pass: after the priority-1 floor-elevation lift in the same frame, and only for nodes whose proxy is registered (instances rebuild synchronously from the store, so a rendered node is already built). Validated by baking Wawa House locally: settle 22.5s -> 11.6s (the remainder is genuine asset loading), exported GLB byte-identical with and without the fix. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a477c4ade3
commit
58339c64f5
@@ -9,6 +9,7 @@ import {
|
|||||||
useScene,
|
useScene,
|
||||||
} from '@pascal-app/core'
|
} from '@pascal-app/core'
|
||||||
import { useNodeEvents, useViewer } from '@pascal-app/viewer'
|
import { useNodeEvents, useViewer } from '@pascal-app/viewer'
|
||||||
|
import { useFrame } from '@react-three/fiber'
|
||||||
import { useLayoutEffect, useMemo, useRef } from 'react'
|
import { useLayoutEffect, useMemo, useRef } from 'react'
|
||||||
import { type BufferGeometry, type InstancedMesh, type Material, Matrix4, Object3D } from 'three'
|
import { type BufferGeometry, type InstancedMesh, type Material, Matrix4, Object3D } from 'three'
|
||||||
import { toStaticMaterial } from './wind-node'
|
import { toStaticMaterial } from './wind-node'
|
||||||
@@ -73,6 +74,26 @@ export function InstancedKindSystem<N extends Placeable>({
|
|||||||
(n) => (n.type as string) === kind && !active.has(n.id as string),
|
(n) => (n.type as string) === kind && !active.has(n.id as string),
|
||||||
) as unknown as N[]
|
) as unknown as N[]
|
||||||
}, [scene, kind, activeKey])
|
}, [scene, kind, activeKey])
|
||||||
|
|
||||||
|
// Consume the dirty marks for this kind. Instances rebuild synchronously
|
||||||
|
// from the store (the memos above), so a rendered node is already "built" —
|
||||||
|
// but `FloorElevationSystem` deliberately leaves the mark for kinds with a
|
||||||
|
// `def.system`, expecting that system to clear it. Without this pass the
|
||||||
|
// marks live forever: `hasPendingSceneBuildWork` never goes false, so the
|
||||||
|
// scene-ready signal (and every headless bake) stalls at its frame cap.
|
||||||
|
// Priority 2 = after the priority-1 floor-elevation lift in the same frame;
|
||||||
|
// clearing only registered nodes leaves unmounted proxies for a later frame.
|
||||||
|
useFrame(() => {
|
||||||
|
const { dirtyNodes, nodes: sceneNodes, clearDirty } = useScene.getState()
|
||||||
|
if (dirtyNodes.size === 0) return
|
||||||
|
for (const id of dirtyNodes) {
|
||||||
|
const node = sceneNodes[id]
|
||||||
|
if (!node || (node.type as string) !== kind) continue
|
||||||
|
if (!sceneRegistry.nodes.has(id)) continue
|
||||||
|
clearDirty(id)
|
||||||
|
}
|
||||||
|
}, 2)
|
||||||
|
|
||||||
return <InstancedNodes getVariant={getVariant} nodes={nodes} variantKeyOf={variantKeyOf} />
|
return <InstancedNodes getVariant={getVariant} nodes={nodes} variantKeyOf={variantKeyOf} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user