@forgeax/engine-render 0.1.23 → 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +231 -10
- package/dist/assembly/bundler-contract.d.ts +9 -0
- package/dist/assembly/bundler-contract.d.ts.map +1 -0
- package/dist/assembly/device-feature-admission.d.ts +8 -0
- package/dist/assembly/device-feature-admission.d.ts.map +1 -1
- package/dist/assembly/extended-lighting-fallback.d.ts +13 -0
- package/dist/assembly/extended-lighting-fallback.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +7 -243
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +3 -2
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/standard-layer-projection.d.ts +10 -0
- package/dist/assembly/material/standard-layer-projection.d.ts.map +1 -0
- package/dist/assembly/material/surface-projection.d.ts +24 -0
- package/dist/assembly/material/surface-projection.d.ts.map +1 -0
- package/dist/assembly/material-shader-policy.d.ts +188 -0
- package/dist/assembly/material-shader-policy.d.ts.map +1 -0
- package/dist/assembly/material-shader-prewarm.d.ts +21 -0
- package/dist/assembly/material-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/mesh-ssbo-grow.d.ts +88 -0
- package/dist/assembly/mesh-ssbo-grow.d.ts.map +1 -0
- package/dist/assembly/recovery/device-loss-fanout.d.ts +14 -0
- package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -0
- package/dist/assembly/recovery/generation.d.ts +46 -0
- package/dist/assembly/recovery/generation.d.ts.map +1 -0
- package/dist/assembly/recovery/recovery-attempt.d.ts +63 -0
- package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -0
- package/dist/assembly/recovery/renderer-recover.d.ts +115 -0
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -0
- package/dist/assembly/renderer-facade.d.ts +10 -0
- package/dist/assembly/renderer-facade.d.ts.map +1 -0
- package/dist/assembly/renderer-frame-transaction.d.ts +17 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-helpers.d.ts +19 -0
- package/dist/assembly/renderer-helpers.d.ts.map +1 -0
- package/dist/assembly/renderer-lifecycle.d.ts +34 -0
- package/dist/assembly/renderer-lifecycle.d.ts.map +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts +15 -0
- package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-contract.d.ts +9 -0
- package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +55 -0
- package/dist/assembly/webgpu-ready.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +152 -0
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -0
- package/dist/assembly/webgpu-vertex-layouts.d.ts +89 -0
- package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -0
- package/dist/assets/asset-decoders.d.ts.map +1 -1
- package/dist/authoring.mjs +1 -1
- package/dist/{chunk-FF7K3FSO.mjs → chunk-ADHHVYLW.mjs} +89 -89
- package/dist/chunk-ADHHVYLW.mjs.map +1 -0
- package/dist/{chunk-YALPS44A.mjs → chunk-E34VL5VI.mjs} +47 -47
- package/dist/chunk-E34VL5VI.mjs.map +1 -0
- package/dist/{chunk-4L3P6DT5.mjs → chunk-GE3SDD72.mjs} +37 -5
- package/dist/chunk-GE3SDD72.mjs.map +1 -0
- package/dist/{chunk-HZUPSYW7.mjs → chunk-HKXTW355.mjs} +105 -15
- package/dist/chunk-HKXTW355.mjs.map +1 -0
- package/dist/chunk-JSEDTZYS.mjs +413 -0
- package/dist/chunk-JSEDTZYS.mjs.map +1 -0
- package/dist/{chunk-FBC4R44J.mjs → chunk-KM2NOX2I.mjs} +57 -57
- package/dist/chunk-KM2NOX2I.mjs.map +1 -0
- package/dist/{chunk-DIYW3M5D.mjs → chunk-N3RT2EUR.mjs} +2288 -2238
- package/dist/chunk-N3RT2EUR.mjs.map +1 -0
- package/dist/{chunk-ZUWKAZ2I.mjs → chunk-TCLDX7SZ.mjs} +1167 -368
- package/dist/chunk-TCLDX7SZ.mjs.map +1 -0
- package/dist/{chunk-QODA5UKU.mjs → chunk-ZZ4YQ474.mjs} +2 -2
- package/dist/chunk-ZZ4YQ474.mjs.map +1 -0
- package/dist/components/instances.d.ts +13 -31
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +20688 -16314
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/device-scope.d.ts +2 -0
- package/dist/device/device-scope.d.ts.map +1 -1
- package/dist/device/gpu-residency.d.ts +24 -4
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/environment/frame.d.ts +5 -0
- package/dist/environment/frame.d.ts.map +1 -1
- package/dist/environment/inspection.d.ts +7 -0
- package/dist/environment/inspection.d.ts.map +1 -1
- package/dist/environment/lifecycle.d.ts +20 -2
- package/dist/environment/lifecycle.d.ts.map +1 -1
- package/dist/errors/recover.d.ts +58 -2
- package/dist/errors/recover.d.ts.map +1 -1
- package/dist/errors/render.d.ts +13 -0
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +899 -0
- package/dist/extract/contracts.d.ts.map +1 -0
- package/dist/features/host.d.ts +2 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +3 -0
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/prepared-graphics-store.d.ts +2 -0
- package/dist/features/prepared-graphics-store.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +34 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +5 -0
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +7 -6
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +7 -1
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +27 -9
- package/dist/index.mjs.map +1 -1
- package/dist/instance-buffer-cache.d.ts +7 -1
- package/dist/instance-buffer-cache.d.ts.map +1 -1
- package/dist/instances.d.ts +101 -0
- package/dist/instances.d.ts.map +1 -0
- package/dist/internal.mjs +6 -6
- package/dist/light-buffer-layout.d.ts +3 -0
- package/dist/light-buffer-layout.d.ts.map +1 -1
- package/dist/materials.d.ts +96 -11
- package/dist/materials.d.ts.map +1 -1
- package/dist/mesh-material-bindings.d.ts +56 -0
- package/dist/mesh-material-bindings.d.ts.map +1 -1
- package/dist/occlusion-proxy.d.ts +4 -0
- package/dist/occlusion-proxy.d.ts.map +1 -0
- package/dist/pbr-pipeline.d.ts +32 -9
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/points-lines/prepare.d.ts +3 -0
- package/dist/points-lines/prepare.d.ts.map +1 -1
- package/dist/points-lines/standard-owner.d.ts +33 -0
- package/dist/points-lines/standard-owner.d.ts.map +1 -0
- package/dist/record/frame-snapshot.d.ts +18 -2
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +14 -2
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +36 -4
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +7 -17
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +11 -3
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/recovery-pipeline.d.ts +30 -0
- package/dist/record/recovery-pipeline.d.ts.map +1 -0
- package/dist/record/render-context.d.ts +28 -5
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +11 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +1 -1
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +65 -0
- package/dist/recovery/render-system-candidate.d.ts.map +1 -0
- package/dist/render-contract.d.ts +68 -7
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +16 -0
- package/dist/render-system-extract-tail.d.ts.map +1 -0
- package/dist/render-system-extract.d.ts +180 -22
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +131 -4
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +2 -0
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +31 -1
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/systems/skin-palette-allocator.d.ts +6 -0
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -6
- package/dist/temporal/taa-history-store.d.ts +15 -1
- package/dist/temporal/taa-history-store.d.ts.map +1 -1
- package/dist/temporal/transaction.d.ts +5 -0
- package/dist/temporal/transaction.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/__tests__/device-feature-admission.unit.test.ts +6 -0
- package/src/__tests__/device-loss-fanout.unit.test.ts +107 -0
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +21 -0
- package/src/__tests__/environment-generation.integration.test.ts +23 -0
- package/src/__tests__/expose-renderer-errors.unit.test.ts +34 -0
- package/src/__tests__/factory-contract.integration.test.ts +122 -7
- package/src/__tests__/fallback-row-stride-surface.unit.test.ts +4 -1
- package/src/__tests__/gpu-scene.unit.test.ts +38 -2
- package/src/__tests__/ibl/face-uniforms.unit.test.ts +2 -0
- package/src/__tests__/ibl-residency-publication.unit.test.ts +113 -0
- package/src/__tests__/instances-store.unit.test.ts +46 -0
- package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +29 -22
- package/src/__tests__/material-cooked-projection.unit.test.ts +19 -1
- package/src/__tests__/material-pass-policy.unit.test.ts +69 -0
- package/src/__tests__/material-snapshot-mutation.integration.test.ts +69 -0
- package/src/__tests__/material-static-dynamic-route.unit.test.ts +29 -0
- package/src/__tests__/material-stride-surface.unit.test.ts +7 -3
- package/src/__tests__/material-surface-authoring.test-d.ts +62 -0
- package/src/__tests__/materials-standard-contract.unit.test.ts +104 -0
- package/src/__tests__/materials-transmission-validation.unit.test.ts +24 -0
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -5
- package/src/__tests__/physical-clearcoat.integration.test.ts +89 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +1 -1
- package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +1 -1
- package/src/__tests__/recover-error-policy-owner.unit.test.ts +6 -3
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +140 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +127 -18
- package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +47 -0
- package/src/__tests__/renderer-generation-assembly.unit.test.ts +77 -0
- package/src/__tests__/renderer-generation-failure.unit.test.ts +108 -0
- package/src/__tests__/renderer-producer-root-matrix.integration.test.ts +33 -0
- package/src/__tests__/renderer-recovery-capability-matrix.unit.test.ts +59 -0
- package/src/__tests__/renderer-recovery-contract.unit.test.ts +73 -0
- package/src/__tests__/renderer-recovery-lifecycle.unit.test.ts +216 -0
- package/src/__tests__/renderer-target-history-recovery.integration.test.ts +80 -0
- package/src/__tests__/shader-manifest-fixture.ts +13 -16
- package/src/__tests__/shader-stage-surface.unit.test.ts +1 -1
- package/src/__tests__/skin-motion-regression.unit.test.ts +33 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +30 -2
- package/src/__tests__/standard-layer-projection.unit.test.ts +23 -0
- package/src/__tests__/surface-adjacent-regressions.integration.test.ts +60 -0
- package/src/__tests__/surface-standard-pipeline.browser.test.ts +20 -0
- package/src/__tests__/surface-standard-pipeline.dawn.test.ts +20 -0
- package/src/__tests__/surface-standard-pipeline.fixture.ts +132 -0
- package/src/__tests__/surface-variant-provenance.integration.test.ts +33 -0
- package/src/__tests__/transmission-prewarm.unit.test.ts +95 -85
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +1 -1
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +3 -1
- package/src/assembly/bundler-contract.ts +9 -0
- package/src/assembly/device-feature-admission.ts +15 -5
- package/src/assembly/extended-lighting-fallback.ts +216 -0
- package/src/assembly/factory.ts +83 -10088
- package/src/assembly/material/__tests__/standard-layer-plan-projection.integration.test.ts +18 -0
- package/src/assembly/material/__tests__/surface-pass-projection.integration.test.ts +62 -0
- package/src/assembly/material/assembly.ts +18 -2
- package/src/assembly/material/standard-layer-projection.ts +16 -0
- package/src/assembly/material/surface-projection.ts +90 -0
- package/src/assembly/material-shader-policy.ts +932 -0
- package/src/assembly/material-shader-prewarm.ts +88 -0
- package/src/assembly/mesh-ssbo-grow.ts +270 -0
- package/src/assembly/recovery/device-loss-fanout.ts +89 -0
- package/src/assembly/recovery/generation.ts +141 -0
- package/src/assembly/recovery/recovery-attempt.ts +800 -0
- package/src/assembly/recovery/renderer-recover.ts +906 -0
- package/src/assembly/renderer-facade.ts +342 -0
- package/src/assembly/renderer-frame-transaction.ts +45 -0
- package/src/assembly/renderer-helpers.ts +99 -0
- package/src/assembly/renderer-lifecycle.ts +159 -0
- package/src/assembly/shader-prewarm-policy.ts +157 -0
- package/src/assembly/webgpu-ready-contract.ts +14 -0
- package/src/assembly/webgpu-ready.ts +4066 -0
- package/src/assembly/webgpu-renderer.ts +4027 -0
- package/src/assembly/webgpu-vertex-layouts.ts +132 -0
- package/src/assets/asset-decoders.ts +27 -4
- package/src/components/camera.ts +1 -1
- package/src/components/instances.ts +14 -197
- package/src/debug-draw-glue.ts +1 -1
- package/src/device/device-scope.ts +35 -6
- package/src/device/gpu-residency.ts +194 -26
- package/src/environment/frame.ts +6 -0
- package/src/environment/inspection.ts +10 -0
- package/src/environment/lifecycle.ts +114 -8
- package/src/errors/recover.ts +76 -5
- package/src/errors/render.ts +17 -0
- package/src/extract/contracts.ts +974 -0
- package/src/features/host.ts +16 -0
- package/src/features/prepared-gpu-work.ts +20 -2
- package/src/features/prepared-graphics-store.ts +17 -0
- package/src/gpu-driven/production-raster.ts +131 -3
- package/src/gpu-scene.ts +87 -31
- package/src/ibl/IblPipelineCache.ts +134 -50
- package/src/ibl/skylight-bind-group.ts +26 -0
- package/src/index.ts +10 -1
- package/src/instance-buffer-cache.ts +14 -1
- package/src/instances.ts +288 -0
- package/src/light-buffer-layout.ts +3 -0
- package/src/materials.ts +365 -53
- package/src/mesh-material-bindings.ts +59 -0
- package/src/occlusion-proxy.ts +64 -0
- package/src/pbr-pipeline.ts +139 -15
- package/src/pipeline-builder.ts +14 -10
- package/src/pipeline-spec.ts +6 -14
- package/src/points-lines/__tests__/cache-recovery.unit.test.ts +22 -0
- package/src/points-lines/prepare.ts +9 -0
- package/src/points-lines/standard-owner.ts +403 -0
- package/src/record/__tests__/frame-targets-recovery.unit.test.ts +60 -0
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +26 -1
- package/src/record/__tests__/mesh-ssbo.unit.test.ts +38 -0
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +124 -0
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +28 -13
- package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +47 -4
- package/src/record/__tests__/temporal-payload.unit.test.ts +22 -43
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -1
- package/src/record/frame-snapshot.ts +34 -2
- package/src/record/frame-targets.ts +11 -1
- package/src/record/frame.ts +37 -4
- package/src/record/helpers.ts +1 -1
- package/src/record/main-pass-geometry.ts +727 -63
- package/src/record/main-pass-material.ts +316 -209
- package/src/record/main-pass-sprite-draws.ts +7 -8
- package/src/record/main-pass.ts +94 -6
- package/src/record/mesh-ssbo.ts +32 -10
- package/src/record/recovery-pipeline.ts +695 -0
- package/src/record/render-context.ts +39 -4
- package/src/record/shadow-pass.ts +46 -99
- package/src/record/typed-frame-graph.ts +45 -1
- package/src/record/view-ubo.ts +3 -3
- package/src/recovery/render-system-candidate.ts +876 -0
- package/src/render-contract.ts +69 -7
- package/src/render-system-extract-tail.ts +3039 -0
- package/src/render-system-extract.ts +177 -3003
- package/src/render-system.ts +906 -662
- package/src/scene/render-scene-types.ts +2 -0
- package/src/scene/render-scene.ts +703 -37
- package/src/systems/skin-palette-allocator.ts +129 -7
- package/src/temporal/frame-coordinator.ts +4 -1
- package/src/temporal/taa-history-store.ts +46 -2
- package/src/temporal/temporal-view.ts +1 -1
- package/src/temporal/transaction.ts +9 -0
- package/src/tilemap-chunk-extract-system.ts +1 -1
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +19 -12
- package/src/transmission/__tests__/surface-compatibility.integration.test.ts +32 -0
- package/dist/chunk-4L3P6DT5.mjs.map +0 -1
- package/dist/chunk-4NPUASZR.mjs +0 -203
- package/dist/chunk-4NPUASZR.mjs.map +0 -1
- package/dist/chunk-DIYW3M5D.mjs.map +0 -1
- package/dist/chunk-FBC4R44J.mjs.map +0 -1
- package/dist/chunk-FF7K3FSO.mjs.map +0 -1
- package/dist/chunk-HZUPSYW7.mjs.map +0 -1
- package/dist/chunk-QODA5UKU.mjs.map +0 -1
- package/dist/chunk-YALPS44A.mjs.map +0 -1
- package/dist/chunk-ZUWKAZ2I.mjs.map +0 -1
package/src/render-system.ts
CHANGED
|
@@ -16,13 +16,22 @@
|
|
|
16
16
|
// .invert` (charter proposition 5: no math reinvention; render-system.test.ts
|
|
17
17
|
// asserts `/@forgeax\/engine-math/` shows up in render-system.ts source).
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
type MipmapEncoderWork,
|
|
21
|
+
numMipLevels,
|
|
22
|
+
resolveAssetHandle,
|
|
23
|
+
} from '@forgeax/engine-assets-runtime';
|
|
20
24
|
import type { World } from '@forgeax/engine-ecs';
|
|
21
25
|
import type { RenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
22
|
-
import { mat4 } from '@forgeax/engine-math';
|
|
23
26
|
import type { RecorderSession } from '@forgeax/engine-profiler';
|
|
24
27
|
import type { CompiledRenderGraphInfo } from '@forgeax/engine-render-graph';
|
|
25
|
-
import type {
|
|
28
|
+
import type {
|
|
29
|
+
BindGroupEntry,
|
|
30
|
+
BindGroupLayout,
|
|
31
|
+
Buffer,
|
|
32
|
+
RenderPipeline,
|
|
33
|
+
RhiDevice,
|
|
34
|
+
} from '@forgeax/engine-rhi';
|
|
26
35
|
import { err, ok, type Result, RhiError } from '@forgeax/engine-rhi';
|
|
27
36
|
import {
|
|
28
37
|
derive,
|
|
@@ -30,7 +39,6 @@ import {
|
|
|
30
39
|
type Handle,
|
|
31
40
|
type MaterialAsset,
|
|
32
41
|
type MaterialTextureValue,
|
|
33
|
-
type MeshAsset,
|
|
34
42
|
type RenderPipelineAsset,
|
|
35
43
|
RenderQueue,
|
|
36
44
|
type SamplerAsset,
|
|
@@ -43,14 +51,16 @@ import {
|
|
|
43
51
|
projectDirectionalShadowInspectionSource,
|
|
44
52
|
} from './assembly/directional-shadow-inspection';
|
|
45
53
|
import { createClusterBinScratch } from './cluster-binner';
|
|
54
|
+
import type { DeviceScope, LifecycleResourceSpec } from './device/device-scope';
|
|
55
|
+
import type { EnvironmentGeneration } from './environment/generation';
|
|
46
56
|
import { EnvironmentLifecycle } from './environment/lifecycle';
|
|
47
57
|
import {
|
|
48
58
|
type ObservationUnavailableError,
|
|
49
|
-
PointsLinesMaterialUnsupportedError,
|
|
50
59
|
type RenderError,
|
|
51
60
|
TransmissionCapabilityMissingError,
|
|
52
61
|
} from './errors/render';
|
|
53
62
|
import {
|
|
63
|
+
type RenderFeatureHost,
|
|
54
64
|
type RenderFeaturePreparedGraphicsResolverInput,
|
|
55
65
|
runRenderFeatureFrame,
|
|
56
66
|
settlePreparedGraphicsCompletion,
|
|
@@ -84,20 +94,14 @@ import type {
|
|
|
84
94
|
TemporalTargetInspection,
|
|
85
95
|
TransmissionInspection,
|
|
86
96
|
} from './inspection-types';
|
|
87
|
-
import {
|
|
88
|
-
import { PointsLinesExpansionCache } from './points-lines/expansion-cache';
|
|
89
|
-
import {
|
|
90
|
-
inspectPointsLines,
|
|
91
|
-
type PointsLinesInspection,
|
|
92
|
-
type PointsLinesSourceError,
|
|
93
|
-
} from './points-lines/inspection';
|
|
94
|
-
import {
|
|
95
|
-
createPointsLinesLanePreparationAdapter,
|
|
96
|
-
type PointsLinesLanePreparationAdapter,
|
|
97
|
-
} from './points-lines/prepare';
|
|
98
|
-
import { createPointsLinesLaneAdapter, type PointsLinesBackend } from './points-lines/record';
|
|
97
|
+
import { buildOcclusionProxyVertices } from './occlusion-proxy';
|
|
99
98
|
import type { PointsLinesRetainedSnapshot } from './points-lines/snapshot';
|
|
100
|
-
import
|
|
99
|
+
import { StandardPointsLinesOwner } from './points-lines/standard-owner';
|
|
100
|
+
import {
|
|
101
|
+
createRenderSystemRecovery,
|
|
102
|
+
materialSamplerHandles,
|
|
103
|
+
materialTextureHandles,
|
|
104
|
+
} from './recovery/render-system-candidate';
|
|
101
105
|
import { ReflectionProbeRecordOwner } from './reflection/record-owner';
|
|
102
106
|
import {
|
|
103
107
|
OcclusionRenderRuntime,
|
|
@@ -108,18 +112,19 @@ import { inspectVolumetricFog } from './volume/inspection';
|
|
|
108
112
|
|
|
109
113
|
export type { RenderSceneInspection } from './inspection-types';
|
|
110
114
|
|
|
111
|
-
import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
112
115
|
import { createCubeCaptureScheduler } from './capture/scheduler';
|
|
116
|
+
import type { GpuResidencyCache } from './device/gpu-residency';
|
|
113
117
|
import type { EnvironmentInspection } from './environment/inspection';
|
|
114
|
-
import {
|
|
115
|
-
GPU_BUFFER_USAGE_COPY_DST,
|
|
116
|
-
GPU_BUFFER_USAGE_INDEX,
|
|
117
|
-
GPU_BUFFER_USAGE_UNIFORM,
|
|
118
|
-
GPU_BUFFER_USAGE_VERTEX,
|
|
119
|
-
} from './gpu-usage';
|
|
118
|
+
import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_UNIFORM } from './gpu-usage';
|
|
120
119
|
import { resetHdrpBuffers } from './hdrp-buffers';
|
|
121
120
|
import { assembleMaterialWithSkylightEntries } from './ibl/skylight-bind-group';
|
|
122
|
-
import {
|
|
121
|
+
import {
|
|
122
|
+
disposeInstanceBufferChunks,
|
|
123
|
+
disposeInstanceBuffers,
|
|
124
|
+
disposeTransientInstanceBuffers,
|
|
125
|
+
} from './instance-buffer-cache';
|
|
126
|
+
import type { InstanceCollectionInspection } from './instances';
|
|
127
|
+
import { InstanceProjectionStore } from './instances';
|
|
123
128
|
import {
|
|
124
129
|
type MeshMaterialBindingObservation,
|
|
125
130
|
type MeshMaterialBindingPreparationFailure,
|
|
@@ -158,9 +163,9 @@ import type {
|
|
|
158
163
|
GpuPassTimingSession,
|
|
159
164
|
} from './record/gpu-pass-timing/session';
|
|
160
165
|
import type { GpuTimingCapture } from './record/gpu-timing';
|
|
161
|
-
import { computeProjectionMatrix, computeViewMatrix } from './record/helpers';
|
|
162
166
|
import { applyParamSnapshotToUbo, residentTextureView } from './record/main-pass-material';
|
|
163
|
-
import type {
|
|
167
|
+
import type { RecoveryPipelineReadiness } from './record/recovery-pipeline';
|
|
168
|
+
import type { PipelineState, RenderSystemInternals } from './record/render-context';
|
|
164
169
|
import type { CubeCaptureGraphState } from './record/typed-frame-graph';
|
|
165
170
|
import {
|
|
166
171
|
type RenderFeatureGraphCandidate,
|
|
@@ -183,7 +188,11 @@ import type {
|
|
|
183
188
|
RenderableSnapshot,
|
|
184
189
|
} from './render-system-extract';
|
|
185
190
|
import { extractFrames } from './render-system-extract';
|
|
186
|
-
import {
|
|
191
|
+
import {
|
|
192
|
+
type PersistentGpuDrivenCandidate,
|
|
193
|
+
type PersistentRenderCandidateRequest,
|
|
194
|
+
PersistentRenderScene,
|
|
195
|
+
} from './scene/render-scene';
|
|
187
196
|
import { inspectLodOcclusion } from './scene/visibility/inspection';
|
|
188
197
|
import { resolveSsaoParameters } from './ssao-config';
|
|
189
198
|
import { projectSsrDependencies, resolveSsrAdmissionGeneration } from './ssr/admission';
|
|
@@ -210,6 +219,34 @@ import {
|
|
|
210
219
|
inspectTransmissionFromAdmission,
|
|
211
220
|
} from './transmission/inspection';
|
|
212
221
|
|
|
222
|
+
function canonicalizeWorldComposition(
|
|
223
|
+
worlds: readonly World[],
|
|
224
|
+
owners: Pick<DrawOwnerOptions, 'cameraOwner' | 'resourceOwner'>,
|
|
225
|
+
leases: readonly RenderReadLease[] | undefined,
|
|
226
|
+
): {
|
|
227
|
+
readonly worlds: readonly World[];
|
|
228
|
+
readonly owners: Pick<DrawOwnerOptions, 'cameraOwner' | 'resourceOwner'>;
|
|
229
|
+
readonly leases: readonly RenderReadLease[] | undefined;
|
|
230
|
+
} {
|
|
231
|
+
const order = worlds
|
|
232
|
+
.map((world, index) => ({ world, index }))
|
|
233
|
+
.sort((left, right) => left.world.identity.localeCompare(right.world.identity));
|
|
234
|
+
const indexByOriginal = new Map(order.map((entry, index) => [entry.index, index]));
|
|
235
|
+
return {
|
|
236
|
+
worlds: Object.freeze(order.map((entry) => entry.world)),
|
|
237
|
+
owners: {
|
|
238
|
+
cameraOwner: indexByOriginal.get(owners.cameraOwner) ?? owners.cameraOwner,
|
|
239
|
+
resourceOwner: indexByOriginal.get(owners.resourceOwner) ?? owners.resourceOwner,
|
|
240
|
+
},
|
|
241
|
+
leases:
|
|
242
|
+
leases === undefined
|
|
243
|
+
? undefined
|
|
244
|
+
: Object.freeze(
|
|
245
|
+
order.map((entry) => leases[entry.index]).filter((lease) => lease !== undefined),
|
|
246
|
+
),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
213
250
|
interface LodInspectionQuerySummary {
|
|
214
251
|
readonly used: number;
|
|
215
252
|
readonly capacity: number;
|
|
@@ -361,6 +398,128 @@ export {
|
|
|
361
398
|
selectSwapChainFormat,
|
|
362
399
|
} from './record/render-context';
|
|
363
400
|
|
|
401
|
+
export type RendererProducerRootKind =
|
|
402
|
+
| 'backend-surface'
|
|
403
|
+
| 'shader-material-pipeline'
|
|
404
|
+
| 'mesh-texture-sampler'
|
|
405
|
+
| 'render-scene'
|
|
406
|
+
| 'gpu-driven'
|
|
407
|
+
| 'feature'
|
|
408
|
+
| 'external-source'
|
|
409
|
+
| 'render-graph'
|
|
410
|
+
| 'target-history'
|
|
411
|
+
| 'observation-lease';
|
|
412
|
+
|
|
413
|
+
export interface RendererProducerRoot {
|
|
414
|
+
readonly kind: RendererProducerRootKind;
|
|
415
|
+
readonly owner: string;
|
|
416
|
+
readonly candidateScope: 'device-scope';
|
|
417
|
+
readonly visibility: 'visible-workset' | 'non-visible-lazy';
|
|
418
|
+
readonly disabledWork: 'zero' | 'lazy';
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** Device-bound post-process resources prepared off to the side of the active renderer. */
|
|
422
|
+
export interface RecoveryPostProcessResources {
|
|
423
|
+
readonly device: RhiDevice;
|
|
424
|
+
readonly paramsBuffers: Map<string, Buffer>;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const PRODUCER_ROOT_MATRIX: readonly RendererProducerRoot[] = Object.freeze([
|
|
428
|
+
{
|
|
429
|
+
kind: 'backend-surface',
|
|
430
|
+
owner: 'backend-surface-owner',
|
|
431
|
+
candidateScope: 'device-scope',
|
|
432
|
+
visibility: 'visible-workset',
|
|
433
|
+
disabledWork: 'zero',
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
kind: 'shader-material-pipeline',
|
|
437
|
+
owner: 'shader-material-pipeline-owner',
|
|
438
|
+
candidateScope: 'device-scope',
|
|
439
|
+
visibility: 'visible-workset',
|
|
440
|
+
disabledWork: 'zero',
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
kind: 'mesh-texture-sampler',
|
|
444
|
+
owner: 'gpu-residency',
|
|
445
|
+
candidateScope: 'device-scope',
|
|
446
|
+
visibility: 'visible-workset',
|
|
447
|
+
disabledWork: 'lazy',
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
kind: 'render-scene',
|
|
451
|
+
owner: 'persistent-render-scene',
|
|
452
|
+
candidateScope: 'device-scope',
|
|
453
|
+
visibility: 'visible-workset',
|
|
454
|
+
disabledWork: 'zero',
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
kind: 'gpu-driven',
|
|
458
|
+
owner: 'gpu-driven-production',
|
|
459
|
+
candidateScope: 'device-scope',
|
|
460
|
+
visibility: 'visible-workset',
|
|
461
|
+
disabledWork: 'zero',
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
kind: 'feature',
|
|
465
|
+
owner: 'render-feature-host',
|
|
466
|
+
candidateScope: 'device-scope',
|
|
467
|
+
visibility: 'visible-workset',
|
|
468
|
+
disabledWork: 'lazy',
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
kind: 'external-source',
|
|
472
|
+
owner: 'external-source-owner',
|
|
473
|
+
candidateScope: 'device-scope',
|
|
474
|
+
visibility: 'non-visible-lazy',
|
|
475
|
+
disabledWork: 'zero',
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
kind: 'render-graph',
|
|
479
|
+
owner: 'typed-frame-graph',
|
|
480
|
+
candidateScope: 'device-scope',
|
|
481
|
+
visibility: 'visible-workset',
|
|
482
|
+
disabledWork: 'zero',
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
kind: 'target-history',
|
|
486
|
+
owner: 'temporal-target-owner',
|
|
487
|
+
candidateScope: 'device-scope',
|
|
488
|
+
visibility: 'visible-workset',
|
|
489
|
+
disabledWork: 'zero',
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
kind: 'observation-lease',
|
|
493
|
+
owner: 'render-observation-owner',
|
|
494
|
+
candidateScope: 'device-scope',
|
|
495
|
+
visibility: 'non-visible-lazy',
|
|
496
|
+
disabledWork: 'zero',
|
|
497
|
+
},
|
|
498
|
+
]);
|
|
499
|
+
|
|
500
|
+
export function createRendererProducerRootMatrix(): readonly RendererProducerRoot[] {
|
|
501
|
+
return PRODUCER_ROOT_MATRIX;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Production-owned recovery facts. These values are projections of the live
|
|
506
|
+
* RenderSystem owners; they are not a test ledger or a second graph model.
|
|
507
|
+
*/
|
|
508
|
+
export interface RecoveryProductionEvidence {
|
|
509
|
+
readonly producerRoots: readonly RendererProducerRoot[];
|
|
510
|
+
readonly graph: {
|
|
511
|
+
readonly ready: boolean;
|
|
512
|
+
readonly generation: number;
|
|
513
|
+
readonly passCount: number;
|
|
514
|
+
readonly resourceCount: number;
|
|
515
|
+
};
|
|
516
|
+
readonly residency: { readonly meshResidencyEpoch: number };
|
|
517
|
+
readonly submissions: {
|
|
518
|
+
readonly count: number;
|
|
519
|
+
readonly lastGeneration: number | undefined;
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
364
523
|
/**
|
|
365
524
|
* Unified transparent-queue sub-sort covering all four
|
|
366
525
|
* {@link TransparentSortConfig} modes. Reorders the
|
|
@@ -508,6 +667,19 @@ export interface RenderSystem {
|
|
|
508
667
|
renderReadLeases?: readonly RenderReadLease[],
|
|
509
668
|
timingCapture?: GpuTimingCapture,
|
|
510
669
|
): boolean;
|
|
670
|
+
/** Prepare a detached graph candidate without entering the frame record path. */
|
|
671
|
+
prepareRecoveryGraphCandidate(
|
|
672
|
+
runtime: RecoveryGraphCandidateRuntime,
|
|
673
|
+
): Promise<RecoveryGraphCandidatePreparation>;
|
|
674
|
+
/** Finish and submit detached candidate setup, never a frame receipt. */
|
|
675
|
+
submitCandidateSetup(
|
|
676
|
+
candidate: RecoveryGraphCandidate,
|
|
677
|
+
isValid: () => boolean,
|
|
678
|
+
): Result<RecoveryGraphSetupSubmission, RhiError>;
|
|
679
|
+
/** Publish a detached graph candidate at the generation publication boundary. */
|
|
680
|
+
publishRecoveryGraphCandidate(candidate: RecoveryGraphCandidate): void;
|
|
681
|
+
/** Discard a detached graph candidate that did not reach publication. */
|
|
682
|
+
discardRecoveryGraphCandidate(candidate: RecoveryGraphCandidate): void;
|
|
511
683
|
/** Release renderer-owned persistent state for one detached World. */
|
|
512
684
|
detachScene(world: World): void;
|
|
513
685
|
/** Release the profiler catalog contribution owned by this RenderSystem. */
|
|
@@ -524,6 +696,8 @@ export interface RenderSystem {
|
|
|
524
696
|
readonly pipelineDispatchCounts: {
|
|
525
697
|
readonly unlit: number;
|
|
526
698
|
};
|
|
699
|
+
/** Detached residency and upload facts for explicit instance collections. */
|
|
700
|
+
readonly instanceCollectionsInspection: readonly InstanceCollectionInspection[];
|
|
527
701
|
/**
|
|
528
702
|
* feat-20260528-frustum-culling M5 / w14: per-frame frustum-culling counters.
|
|
529
703
|
* Updated by `draw([world], { cameraOwner: 0, resourceOwner: 0 })` on every call from the Extract stage.
|
|
@@ -560,6 +734,8 @@ export interface RenderSystem {
|
|
|
560
734
|
readonly pointsLinesSnapshots: readonly PointsLinesRetainedSnapshot[];
|
|
561
735
|
/** Current mesh-slot provenance and active diagnostics from the last frame. */
|
|
562
736
|
readonly meshMaterialBindings: readonly MeshMaterialBindingObservation[];
|
|
737
|
+
/** Final-submit IBL binding-chain receipt for the last diagnostic frame. */
|
|
738
|
+
readonly iblBinding: import('./mesh-material-bindings').IblBindingInspection | undefined;
|
|
563
739
|
/**
|
|
564
740
|
* feat-20260531-bloom-first-declarative-render-graph-pass M4 fix-up w19:
|
|
565
741
|
* per-frame render-graph pass names in declaration order. Empty array
|
|
@@ -573,6 +749,8 @@ export interface RenderSystem {
|
|
|
573
749
|
readonly lastSuccessfulCameraAntialias: CameraSnapshot['antialias'] | undefined;
|
|
574
750
|
/** Detached compiled-graph facts used by the renderer inspection owner. */
|
|
575
751
|
readonly perFrameGraphInfo: CompiledRenderGraphInfo | undefined;
|
|
752
|
+
/** Production-owner recovery facts used by Renderer.inspect(). */
|
|
753
|
+
readonly recoveryEvidence: RecoveryProductionEvidence;
|
|
576
754
|
/**
|
|
577
755
|
* feat-20260531-per-frame-bind-group-cache M1 / w4: per-frame
|
|
578
756
|
* createBindGroup counter. Reset to 0 on every `draw([world], { cameraOwner: 0, resourceOwner: 0 })` entry,
|
|
@@ -600,6 +778,8 @@ export interface RenderSystem {
|
|
|
600
778
|
configureStandard(config: RenderPipelineAsset['config']): void;
|
|
601
779
|
/** Register one engine-owned post-process shader used by the Standard lane. */
|
|
602
780
|
registerBuiltinPostProcess(id: string, entry: PostProcessShaderEntry): () => void;
|
|
781
|
+
/** Resolve one active built-in post-process declaration for a detached candidate. */
|
|
782
|
+
readonly lookupPostProcess: (id: string) => PostProcessShaderEntry | undefined;
|
|
603
783
|
/**
|
|
604
784
|
* feat-20260612-rhi-destroy-renderer-dispose-gpu-lifecycle / M5 / w21:
|
|
605
785
|
* release the per-RenderSystem frame-state GPU bookkeeping during
|
|
@@ -623,9 +803,19 @@ export interface RenderSystem {
|
|
|
623
803
|
* handles and are rebuilt after the replacement device is live.
|
|
624
804
|
* Idempotent and graph-optional (no-op when nothing has compiled yet).
|
|
625
805
|
*/
|
|
626
|
-
resetForRecover(): void;
|
|
806
|
+
resetForRecover(retiringPipelineState?: PipelineState, replacementDevice?: RhiDevice): void;
|
|
807
|
+
/** Build post-process parameter buffers without mutating the active bundle. */
|
|
808
|
+
prepareRecoveryPostProcessResources(
|
|
809
|
+
device: RhiDevice,
|
|
810
|
+
): Result<RecoveryPostProcessResources, RhiError>;
|
|
811
|
+
/** Publish a previously prepared post-process bundle at the recovery boundary. */
|
|
812
|
+
publishRecoveryPostProcessResources(candidate: RecoveryPostProcessResources): void;
|
|
813
|
+
/** Release a candidate bundle that was never published. */
|
|
814
|
+
discardRecoveryPostProcessResources(candidate: RecoveryPostProcessResources): void;
|
|
627
815
|
/** Recreate post-process GPU parameter resources after a device rebuild. */
|
|
628
816
|
restorePostProcessResources(): void;
|
|
817
|
+
/** Build candidate roots from explicit candidate-owned device state. */
|
|
818
|
+
prepareRecoveryRoots(runtime: RecoveryRootRuntime): RecoveryRootBundle;
|
|
629
819
|
}
|
|
630
820
|
|
|
631
821
|
function isStructuredRendererError(error: unknown): error is RhiError | RenderError {
|
|
@@ -681,11 +871,12 @@ function makePreparedPipelinePendingError(): RhiError {
|
|
|
681
871
|
});
|
|
682
872
|
}
|
|
683
873
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
874
|
+
/** The single Points/Lines owner’s staged device-generation handoff. */
|
|
875
|
+
export interface RecoveryPointsLinesCandidate {
|
|
876
|
+
createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown>;
|
|
877
|
+
publish(): void;
|
|
878
|
+
release(): void;
|
|
879
|
+
}
|
|
689
880
|
|
|
690
881
|
function preparationFailureFrom(error: unknown): MeshMaterialBindingPreparationFailure | undefined {
|
|
691
882
|
if (typeof error !== 'object' || error === null) return undefined;
|
|
@@ -714,30 +905,56 @@ function preparationFailureFrom(error: unknown): MeshMaterialBindingPreparationF
|
|
|
714
905
|
};
|
|
715
906
|
}
|
|
716
907
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
const add = (handle: Handle<'TextureAsset', 'shared'> | undefined): void => {
|
|
722
|
-
if (handle !== undefined) handles.set(Number(handle), handle);
|
|
723
|
-
};
|
|
724
|
-
for (const handle of material.textureHandles?.values() ?? []) add(handle);
|
|
725
|
-
add(material.baseColorTexture);
|
|
726
|
-
add(material.metallicRoughnessTexture);
|
|
727
|
-
add(material.normalTexture);
|
|
728
|
-
add(material.emissiveTexture);
|
|
729
|
-
add(material.occlusionTexture);
|
|
730
|
-
return [...handles.values()];
|
|
908
|
+
/** Candidate-only device/runtime inputs used by the recovery prepare seam. */
|
|
909
|
+
export interface RecoveryGraphCandidateRuntime {
|
|
910
|
+
readonly internals: RenderSystemInternals;
|
|
911
|
+
readonly pipelineState: PipelineState;
|
|
731
912
|
}
|
|
732
913
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
914
|
+
/** Detached graph state prepared on a replacement device, before publication. */
|
|
915
|
+
export interface RecoveryGraphCandidate {
|
|
916
|
+
readonly frameState: RenderFrameState;
|
|
917
|
+
readonly setupWorks: readonly MipmapEncoderWork[];
|
|
918
|
+
readonly device: RhiDevice;
|
|
919
|
+
readonly generation: number;
|
|
920
|
+
readonly gpuDrivenProduction?: GpuDrivenProduction;
|
|
921
|
+
readonly gpuDrivenScene?: PersistentGpuDrivenCandidate;
|
|
922
|
+
/** Staged CPU-expanded Points/Lines buffers owned by the same RenderSystem. */
|
|
923
|
+
readonly pointsLines?: RecoveryPointsLinesCandidate;
|
|
924
|
+
readonly featureHost?: RenderFeatureHost;
|
|
925
|
+
readonly featureGpuWork?: RenderFeatureGpuWorkOwner;
|
|
926
|
+
readonly featureGraphCandidate?: RenderFeatureGraphCandidate;
|
|
927
|
+
readonly postProcessPipelines?: ReadonlyMap<string, RenderPipeline>;
|
|
928
|
+
readonly recoveryReadiness: RecoveryPipelineReadiness;
|
|
929
|
+
/** Release all candidate roots before publication; safe to call repeatedly. */
|
|
930
|
+
readonly release: () => void;
|
|
931
|
+
/** Transfer candidate-root ownership to the published RenderSystem. */
|
|
932
|
+
readonly markPublished: () => void;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/** Candidate setup completion; completion is bounded by the recovery owner. */
|
|
936
|
+
export interface RecoveryGraphSetupSubmission {
|
|
937
|
+
readonly completion?: PromiseLike<unknown>;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
export type RecoveryGraphCandidatePreparation =
|
|
941
|
+
| { readonly kind: 'no-seed' }
|
|
942
|
+
| { readonly kind: 'ready'; readonly candidate: RecoveryGraphCandidate }
|
|
943
|
+
| { readonly kind: 'failed'; readonly reason: string; readonly cause?: unknown };
|
|
944
|
+
|
|
945
|
+
/** Explicit candidate-owned inputs for lifecycle-root preparation. */
|
|
946
|
+
export interface RecoveryRootRuntime {
|
|
947
|
+
readonly scope: DeviceScope;
|
|
948
|
+
readonly device: RhiDevice;
|
|
949
|
+
readonly gpuStore: GpuResidencyCache;
|
|
950
|
+
readonly graphCandidate?: RecoveryGraphCandidate | undefined;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/** Candidate roots plus the one synchronous owner switch at publication. */
|
|
954
|
+
export interface RecoveryRootBundle {
|
|
955
|
+
readonly roots: readonly LifecycleResourceSpec<unknown>[];
|
|
956
|
+
publish(): void;
|
|
957
|
+
discard(): void;
|
|
741
958
|
}
|
|
742
959
|
|
|
743
960
|
/**
|
|
@@ -856,267 +1073,6 @@ function observeMaterialResidency(
|
|
|
856
1073
|
};
|
|
857
1074
|
}
|
|
858
1075
|
|
|
859
|
-
/**
|
|
860
|
-
* The Standard renderer's single Points/Lines prepare owner.
|
|
861
|
-
*
|
|
862
|
-
* The retained snapshot is the identity boundary; this owner only resolves
|
|
863
|
-
* the source assets, runs admission, and publishes the existing preparation
|
|
864
|
-
* and record contracts to the main geometry loop. The expansion cache is
|
|
865
|
-
* shared across lanes and this owner keeps the one prepared vertex/index
|
|
866
|
-
* resource pair for each retained identity.
|
|
867
|
-
*/
|
|
868
|
-
class StandardPointsLinesOwner implements PointsLinesRecordOwner {
|
|
869
|
-
private readonly cache = new PointsLinesExpansionCache();
|
|
870
|
-
private readonly preparations = new Map<string, PointsLinesPreparationAdapter>();
|
|
871
|
-
private readonly active = new Map<string, PointsLinesInspection>();
|
|
872
|
-
private readonly layoutProjection = deriveVertexLayoutProjection({
|
|
873
|
-
position: new Float32Array(0),
|
|
874
|
-
normal: new Float32Array(0),
|
|
875
|
-
uv: new Float32Array(0),
|
|
876
|
-
tangent: new Float32Array(0),
|
|
877
|
-
});
|
|
878
|
-
|
|
879
|
-
constructor(private readonly internals: RenderSystemInternals) {}
|
|
880
|
-
|
|
881
|
-
beginFrame(): void {
|
|
882
|
-
this.active.clear();
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
prepare(
|
|
886
|
-
entry: import('./record/frame-snapshot').ValidatedRenderable,
|
|
887
|
-
clustered: boolean,
|
|
888
|
-
): PointsLinesRecordSubmission | undefined {
|
|
889
|
-
const snapshot = entry.source.pointsLines;
|
|
890
|
-
if (snapshot === undefined || snapshot.component === undefined || entry.world === undefined) {
|
|
891
|
-
return undefined;
|
|
892
|
-
}
|
|
893
|
-
const key = `${snapshot.worldId}:${snapshot.entityKey}`;
|
|
894
|
-
const topology = snapshot.component === 'Points' ? 'point-list' : 'line-list';
|
|
895
|
-
const backend = this.backend();
|
|
896
|
-
const lane = backend === 'wgpu-webgl2' ? 'cpu-webgl2' : clustered ? 'clustered' : 'direct';
|
|
897
|
-
const meshResult = resolveAssetHandle<MeshAsset>(
|
|
898
|
-
entry.world,
|
|
899
|
-
toShared<'MeshAsset'>(entry.source.assetHandle),
|
|
900
|
-
);
|
|
901
|
-
const materialResult = resolveAssetHandle<MaterialAsset>(
|
|
902
|
-
entry.world,
|
|
903
|
-
toShared<'MaterialAsset'>(snapshot.materialHandle),
|
|
904
|
-
);
|
|
905
|
-
if (!meshResult.ok || !materialResult.ok) {
|
|
906
|
-
this.publishRefusal(
|
|
907
|
-
snapshot,
|
|
908
|
-
new PointsLinesMaterialUnsupportedError({
|
|
909
|
-
entity: snapshot.entityKey,
|
|
910
|
-
material: 'unresolved',
|
|
911
|
-
pass: 'forward',
|
|
912
|
-
module: 'asset-resolution',
|
|
913
|
-
reason: 'source MeshAsset or MaterialAsset could not be resolved',
|
|
914
|
-
}),
|
|
915
|
-
);
|
|
916
|
-
return undefined;
|
|
917
|
-
}
|
|
918
|
-
const admission = admitPointsLines({
|
|
919
|
-
entity: snapshot.entityKey,
|
|
920
|
-
mesh: meshResult.value,
|
|
921
|
-
material: materialResult.value,
|
|
922
|
-
...(snapshot.style?.kind === 'points'
|
|
923
|
-
? {
|
|
924
|
-
points: {
|
|
925
|
-
sizePx: snapshot.style.sizePx,
|
|
926
|
-
shape: snapshot.style.shape === 'circle' ? 1 : 0,
|
|
927
|
-
},
|
|
928
|
-
}
|
|
929
|
-
: { lines: { widthPx: snapshot.style?.widthPx ?? 1 } }),
|
|
930
|
-
});
|
|
931
|
-
if (!admission.ok) {
|
|
932
|
-
this.publishRefusal(snapshot, admission.error);
|
|
933
|
-
return undefined;
|
|
934
|
-
}
|
|
935
|
-
const geometryKey = this.cache.getOrCreate(snapshot, meshResult.value).key;
|
|
936
|
-
const prepKey = `${geometryKey}:${lane}:${backend}`;
|
|
937
|
-
let preparation = this.preparations.get(prepKey);
|
|
938
|
-
if (preparation === undefined) {
|
|
939
|
-
preparation = createPointsLinesLanePreparationAdapter(lane, backend, {
|
|
940
|
-
cache: this.cache,
|
|
941
|
-
adapter: {
|
|
942
|
-
create: (geometry) => {
|
|
943
|
-
const vertex = this.internals.device.createBuffer({
|
|
944
|
-
label: `points-lines-vertices:${geometryKey}`,
|
|
945
|
-
size: Math.max(4, geometry.vertices.byteLength),
|
|
946
|
-
usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
947
|
-
mappedAtCreation: false,
|
|
948
|
-
});
|
|
949
|
-
if (!vertex.ok) return err(vertex.error);
|
|
950
|
-
const index = this.internals.device.createBuffer({
|
|
951
|
-
label: `points-lines-indices:${geometryKey}`,
|
|
952
|
-
size: Math.max(4, geometry.indices.byteLength),
|
|
953
|
-
usage: GPU_BUFFER_USAGE_INDEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
954
|
-
mappedAtCreation: false,
|
|
955
|
-
});
|
|
956
|
-
if (!index.ok) {
|
|
957
|
-
this.internals.device.destroyBuffer(vertex.value);
|
|
958
|
-
return err(index.error);
|
|
959
|
-
}
|
|
960
|
-
return ok({ vertexBuffer: vertex.value, indexBuffer: index.value });
|
|
961
|
-
},
|
|
962
|
-
upload: (resource, geometry) => {
|
|
963
|
-
if (geometry.vertices.byteLength > 0) {
|
|
964
|
-
const vertexWrite = this.internals.device.queue.writeBuffer(
|
|
965
|
-
resource.vertexBuffer,
|
|
966
|
-
0,
|
|
967
|
-
geometry.vertices,
|
|
968
|
-
);
|
|
969
|
-
if (!vertexWrite.ok) return err(vertexWrite.error);
|
|
970
|
-
}
|
|
971
|
-
if (geometry.indices.byteLength > 0) {
|
|
972
|
-
const indexWrite = this.internals.device.queue.writeBuffer(
|
|
973
|
-
resource.indexBuffer,
|
|
974
|
-
0,
|
|
975
|
-
geometry.indices,
|
|
976
|
-
);
|
|
977
|
-
if (!indexWrite.ok) return err(indexWrite.error);
|
|
978
|
-
}
|
|
979
|
-
return ok(geometry.derivedBytes);
|
|
980
|
-
},
|
|
981
|
-
validate: (_resource, geometry) =>
|
|
982
|
-
geometry.expandedVertexCount > 0 && geometry.expandedIndexCount > 0
|
|
983
|
-
? ok(undefined)
|
|
984
|
-
: err(new Error('Points/Lines expansion contains no drawable triangles')),
|
|
985
|
-
destroy: (resource) => {
|
|
986
|
-
this.internals.device.destroyBuffer(resource.vertexBuffer);
|
|
987
|
-
this.internals.device.destroyBuffer(resource.indexBuffer);
|
|
988
|
-
},
|
|
989
|
-
},
|
|
990
|
-
});
|
|
991
|
-
this.preparations.set(prepKey, preparation);
|
|
992
|
-
}
|
|
993
|
-
const prepared = preparation.prepare(snapshot, meshResult.value);
|
|
994
|
-
if (!prepared.ok) {
|
|
995
|
-
const lkg = preparation.lastKnownGood();
|
|
996
|
-
if (lkg !== undefined) {
|
|
997
|
-
const lkgPlan = createPointsLinesLaneAdapter(lane, backend).createRecordPlan(
|
|
998
|
-
lkg.snapshot,
|
|
999
|
-
lkg.geometry,
|
|
1000
|
-
);
|
|
1001
|
-
const state = preparation.inspect();
|
|
1002
|
-
this.active.set(
|
|
1003
|
-
key,
|
|
1004
|
-
inspectPointsLines({
|
|
1005
|
-
snapshot,
|
|
1006
|
-
topology,
|
|
1007
|
-
lane,
|
|
1008
|
-
pointCount: lkg.geometry.pointCount,
|
|
1009
|
-
segmentCount: lkg.geometry.segmentCount,
|
|
1010
|
-
sourceBytes: lkg.geometry.sourceBytes,
|
|
1011
|
-
derivedBytes: lkg.geometry.derivedBytes,
|
|
1012
|
-
cache: { hit: true, rebuilds: state.rebuilds, evictions: 0 },
|
|
1013
|
-
drawCount: lkgPlan.drawCount,
|
|
1014
|
-
uploadBytes: 0,
|
|
1015
|
-
lastKnownGood: true,
|
|
1016
|
-
refusal: {
|
|
1017
|
-
code: prepared.error.code,
|
|
1018
|
-
expected: prepared.error.expected,
|
|
1019
|
-
hint: prepared.error.hint,
|
|
1020
|
-
detail: prepared.error.detail,
|
|
1021
|
-
generation: prepared.error.detail.generation,
|
|
1022
|
-
lastKnownGood: true,
|
|
1023
|
-
},
|
|
1024
|
-
}),
|
|
1025
|
-
);
|
|
1026
|
-
return {
|
|
1027
|
-
plan: lkgPlan,
|
|
1028
|
-
vertexBuffer: lkg.resource.vertexBuffer,
|
|
1029
|
-
indexBuffer: lkg.resource.indexBuffer,
|
|
1030
|
-
layoutProjection: this.layoutProjection,
|
|
1031
|
-
};
|
|
1032
|
-
}
|
|
1033
|
-
this.publishRefusal(snapshot, prepared.error);
|
|
1034
|
-
return undefined;
|
|
1035
|
-
}
|
|
1036
|
-
const plan = createPointsLinesLaneAdapter(lane, backend).createRecordPlan(
|
|
1037
|
-
snapshot,
|
|
1038
|
-
prepared.value.geometry,
|
|
1039
|
-
);
|
|
1040
|
-
const state = preparation.inspect();
|
|
1041
|
-
this.active.set(
|
|
1042
|
-
key,
|
|
1043
|
-
inspectPointsLines({
|
|
1044
|
-
snapshot,
|
|
1045
|
-
topology,
|
|
1046
|
-
lane,
|
|
1047
|
-
pointCount: prepared.value.geometry.pointCount,
|
|
1048
|
-
segmentCount: prepared.value.geometry.segmentCount,
|
|
1049
|
-
sourceBytes: prepared.value.geometry.sourceBytes,
|
|
1050
|
-
derivedBytes: prepared.value.geometry.derivedBytes,
|
|
1051
|
-
cache: {
|
|
1052
|
-
hit: prepared.value.uploadedBytes === 0,
|
|
1053
|
-
rebuilds: state.rebuilds,
|
|
1054
|
-
evictions: 0,
|
|
1055
|
-
},
|
|
1056
|
-
drawCount: plan.drawCount,
|
|
1057
|
-
uploadBytes: prepared.value.uploadedBytes,
|
|
1058
|
-
lastKnownGood: prepared.value.lastKnownGood,
|
|
1059
|
-
}),
|
|
1060
|
-
);
|
|
1061
|
-
return {
|
|
1062
|
-
plan,
|
|
1063
|
-
vertexBuffer: prepared.value.resource.vertexBuffer,
|
|
1064
|
-
indexBuffer: prepared.value.resource.indexBuffer,
|
|
1065
|
-
layoutProjection: this.layoutProjection,
|
|
1066
|
-
};
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
resetForDeviceLoss(): void {
|
|
1070
|
-
for (const preparation of this.preparations.values()) preparation.resetForDeviceLoss();
|
|
1071
|
-
this.active.clear();
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
inspections(): readonly PointsLinesInspection[] {
|
|
1075
|
-
return [...this.active.values()];
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
private backend(): PointsLinesBackend {
|
|
1079
|
-
switch (this.internals.device.caps.backendKind) {
|
|
1080
|
-
case 'wgpu-webgl2':
|
|
1081
|
-
return 'wgpu-webgl2';
|
|
1082
|
-
case 'null':
|
|
1083
|
-
return 'null';
|
|
1084
|
-
default:
|
|
1085
|
-
return 'webgpu';
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
private publishRefusal(
|
|
1090
|
-
snapshot: PointsLinesRetainedSnapshot,
|
|
1091
|
-
error: PointsLinesSourceError,
|
|
1092
|
-
): void {
|
|
1093
|
-
this.active.set(
|
|
1094
|
-
`${snapshot.worldId}:${snapshot.entityKey}`,
|
|
1095
|
-
inspectPointsLines({
|
|
1096
|
-
snapshot,
|
|
1097
|
-
topology: snapshot.component === 'Points' ? 'point-list' : 'line-list',
|
|
1098
|
-
lane: 'refused',
|
|
1099
|
-
pointCount: 0,
|
|
1100
|
-
segmentCount: 0,
|
|
1101
|
-
sourceBytes: 0,
|
|
1102
|
-
derivedBytes: 0,
|
|
1103
|
-
cache: { hit: false, rebuilds: 0, evictions: 0 },
|
|
1104
|
-
drawCount: 0,
|
|
1105
|
-
uploadBytes: 0,
|
|
1106
|
-
lastKnownGood: false,
|
|
1107
|
-
refusal: {
|
|
1108
|
-
code: error.code,
|
|
1109
|
-
expected: error.expected,
|
|
1110
|
-
hint: error.hint,
|
|
1111
|
-
detail: error.detail,
|
|
1112
|
-
generation: snapshot.meshGeneration,
|
|
1113
|
-
lastKnownGood: false,
|
|
1114
|
-
},
|
|
1115
|
-
}),
|
|
1116
|
-
);
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
1076
|
export function createRenderSystem(internals: RenderSystemInternals): RenderSystem {
|
|
1121
1077
|
const phaseCatalogRegistration = internals.profiler?.registerPhaseCatalog(
|
|
1122
1078
|
'render',
|
|
@@ -1127,6 +1083,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1127
1083
|
let preparedWorlds: readonly World[] = [];
|
|
1128
1084
|
let latestCamera: CameraSnapshot | undefined;
|
|
1129
1085
|
const pointsLinesOwner = new StandardPointsLinesOwner(internals);
|
|
1086
|
+
const instanceCollections = new InstanceProjectionStore();
|
|
1130
1087
|
const reflectionProbeOwner = new ReflectionProbeRecordOwner(internals);
|
|
1131
1088
|
let ssrFormatReceipt: import('@forgeax/engine-rhi').RhiTextureFormatCapabilityReceipt | undefined;
|
|
1132
1089
|
let ssrFormatProbeGeneration = -1;
|
|
@@ -1168,8 +1125,10 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1168
1125
|
onSharedRefMutation: (worldId, handle) => {
|
|
1169
1126
|
internals.gpuStore.invalidateMesh(handle, preparedWorlds[worldId] ?? worldId);
|
|
1170
1127
|
},
|
|
1128
|
+
instanceCollections,
|
|
1129
|
+
getSkinPaletteAllocator: () => internals.getPipelineState()?.skinPaletteAllocator ?? null,
|
|
1171
1130
|
});
|
|
1172
|
-
|
|
1131
|
+
let environmentLifecycle = new EnvironmentLifecycle(internals.deviceScope);
|
|
1173
1132
|
const transmissionAdmission = new TransmissionCandidateAdmission();
|
|
1174
1133
|
let lastTransmissionInspection: TransmissionInspection | undefined;
|
|
1175
1134
|
let lastTransmissionKey = '';
|
|
@@ -1294,8 +1253,13 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1294
1253
|
});
|
|
1295
1254
|
lastTransmissionKey = key;
|
|
1296
1255
|
};
|
|
1256
|
+
// GPU-driven production uses the validated adapter for normal frames. The
|
|
1257
|
+
// assembly-supplied closure resolves its device-bound adapter at call time,
|
|
1258
|
+
// so recovery candidate preparation still follows candidateShaderState
|
|
1259
|
+
// rather than retaining a lost-generation module.
|
|
1297
1260
|
const gpuDrivenShaderFactory =
|
|
1298
1261
|
internals.shaderModuleFactory ??
|
|
1262
|
+
internals.immediateShaderModuleFactory ??
|
|
1299
1263
|
({
|
|
1300
1264
|
createShaderModule: () =>
|
|
1301
1265
|
err(
|
|
@@ -1306,7 +1270,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1306
1270
|
}),
|
|
1307
1271
|
),
|
|
1308
1272
|
} satisfies import('./pipeline-builder').PipelineBuilderShaderModuleFactory);
|
|
1309
|
-
let gpuDrivenProduction =
|
|
1273
|
+
let gpuDrivenProduction = GpuDrivenProduction.forDevice(internals.device, gpuDrivenShaderFactory);
|
|
1310
1274
|
let occlusionRuntime: OcclusionRenderRuntime | undefined = new OcclusionRenderRuntime(
|
|
1311
1275
|
internals.device,
|
|
1312
1276
|
persistentRenderScene.visibilityFacetStore(),
|
|
@@ -1324,34 +1288,36 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1324
1288
|
}
|
|
1325
1289
|
return occlusionRuntime;
|
|
1326
1290
|
};
|
|
1327
|
-
const
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1291
|
+
const createFeatureGpuWorkOwner = (runtime: RenderSystemInternals): RenderFeatureGpuWorkOwner =>
|
|
1292
|
+
createRenderFeatureGpuWorkOwner({
|
|
1293
|
+
getDevice: () => runtime.device,
|
|
1294
|
+
getShaderModuleFactory: () =>
|
|
1295
|
+
runtime.shaderModuleFactory ??
|
|
1296
|
+
({
|
|
1297
|
+
createShaderModule: () =>
|
|
1298
|
+
err(
|
|
1299
|
+
new RhiError({
|
|
1300
|
+
code: 'rhi-not-available',
|
|
1301
|
+
expected: 'the renderer backend exposes shader module creation',
|
|
1302
|
+
hint: 'construct the renderer through the backend pack before activating GPU features',
|
|
1303
|
+
}),
|
|
1304
|
+
),
|
|
1305
|
+
} satisfies import('./pipeline-builder').PipelineBuilderShaderModuleFactory),
|
|
1306
|
+
getImmediateShaderModuleFactory: () =>
|
|
1307
|
+
runtime.immediateShaderModuleFactory ??
|
|
1308
|
+
runtime.shaderModuleFactory ??
|
|
1309
|
+
({
|
|
1310
|
+
createShaderModule: () =>
|
|
1311
|
+
err(
|
|
1312
|
+
new RhiError({
|
|
1313
|
+
code: 'rhi-not-available',
|
|
1314
|
+
expected: 'the renderer backend exposes shader module creation',
|
|
1315
|
+
hint: 'construct the renderer through the backend pack before activating GPU features',
|
|
1316
|
+
}),
|
|
1317
|
+
),
|
|
1318
|
+
} satisfies import('./pipeline-builder').PipelineBuilderShaderModuleFactory),
|
|
1319
|
+
});
|
|
1320
|
+
let featureGpuWork = createFeatureGpuWorkOwner(internals);
|
|
1355
1321
|
const disposeFeatureGpuWork = (): void => {
|
|
1356
1322
|
const disposed = featureGpuWork.dispose();
|
|
1357
1323
|
if (!disposed.ok) internals.errorRegistry.fire(disposed.error);
|
|
@@ -1411,6 +1377,8 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1411
1377
|
currentDirectionalShadowView: null,
|
|
1412
1378
|
currentSpotShadowView: null,
|
|
1413
1379
|
instanceBuffers: new Map(),
|
|
1380
|
+
instanceResidency: new Map(),
|
|
1381
|
+
instanceBufferChunks: new Map(),
|
|
1414
1382
|
probeBlendBuffers: new Map(),
|
|
1415
1383
|
probeBlendRecordBufferCapacity: 0,
|
|
1416
1384
|
morphBuffers: new Map(),
|
|
@@ -1493,6 +1461,8 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1493
1461
|
spotShadowSnapshots: [],
|
|
1494
1462
|
};
|
|
1495
1463
|
let directFrameId = 0;
|
|
1464
|
+
let submittedFrameCount = 0;
|
|
1465
|
+
let lastSubmittedGeneration: number | undefined;
|
|
1496
1466
|
let lastDirectionalShadowLights: ExtractedLights | undefined;
|
|
1497
1467
|
let lastDirectionalShadowCandidate: 'accepted' | 'failed' = 'failed';
|
|
1498
1468
|
let lastDirectionalShadowError: DirectionalShadowInspectionSource['error'];
|
|
@@ -1551,7 +1521,8 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1551
1521
|
// D-3 / D-8: per-shader params UBO resource table (id -> GPU Buffer).
|
|
1552
1522
|
// Eager-created at register time when entry.params is present (byteSize >= 16,
|
|
1553
1523
|
// defaultValue.length === byteSize); reused frame-to-frame via queue.writeBuffer.
|
|
1554
|
-
|
|
1524
|
+
let postProcessParamsBuffers = new Map<string, Buffer>();
|
|
1525
|
+
let postProcessParamsDevice = internals.device;
|
|
1555
1526
|
const builtinPostProcessEntries = new Map<string, PostProcessShaderEntry>();
|
|
1556
1527
|
let activeFeaturePostProcessEntries: ReadonlyMap<string, PostProcessShaderEntry> = new Map();
|
|
1557
1528
|
const lookupPostProcess = (id: string): PostProcessShaderEntry | undefined =>
|
|
@@ -1567,6 +1538,76 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1567
1538
|
// per-frame snapshot + bind it at group(1) binding(2).
|
|
1568
1539
|
const getPostProcessParamsBuffer = (id: string): Buffer | undefined =>
|
|
1569
1540
|
postProcessParamsBuffers.get(id);
|
|
1541
|
+
const postProcessDeclarations = (): readonly (readonly [string, PostProcessShaderEntry])[] => [
|
|
1542
|
+
...builtinPostProcessEntries.entries(),
|
|
1543
|
+
...activeFeaturePostProcessEntries.entries(),
|
|
1544
|
+
];
|
|
1545
|
+
const disposePostProcessResources = (resources: RecoveryPostProcessResources): void => {
|
|
1546
|
+
for (const buffer of resources.paramsBuffers.values()) {
|
|
1547
|
+
const destroyed = resources.device.destroyBuffer(buffer);
|
|
1548
|
+
if (!destroyed.ok) internals.errorRegistry.fire(destroyed.error);
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
const prepareRecoveryPostProcessResources = (
|
|
1552
|
+
device: RhiDevice,
|
|
1553
|
+
): Result<RecoveryPostProcessResources, RhiError> => {
|
|
1554
|
+
const paramsBuffers = new Map<string, Buffer>();
|
|
1555
|
+
try {
|
|
1556
|
+
for (const [id, entry] of postProcessDeclarations()) {
|
|
1557
|
+
if (id === 'forgeax.taa-resolve') continue;
|
|
1558
|
+
let buffer: Buffer | undefined;
|
|
1559
|
+
if (entry.params !== undefined) {
|
|
1560
|
+
const created = device.createBuffer({
|
|
1561
|
+
label: `post-process-params-${id}`,
|
|
1562
|
+
size: entry.params.byteSize,
|
|
1563
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
1564
|
+
mappedAtCreation: false,
|
|
1565
|
+
});
|
|
1566
|
+
if (!created.ok) throw created.error;
|
|
1567
|
+
buffer = created.value;
|
|
1568
|
+
paramsBuffers.set(id, buffer);
|
|
1569
|
+
const written = device.queue.writeBuffer(buffer, 0, entry.params.defaultValue);
|
|
1570
|
+
if (!written.ok) throw written.error;
|
|
1571
|
+
} else if (entryHasDepthRead(entry)) {
|
|
1572
|
+
const created = device.createBuffer({
|
|
1573
|
+
label: `post-process-params-${id}`,
|
|
1574
|
+
size: DEPTH_MIN_PARAMS_BYTE_SIZE,
|
|
1575
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
1576
|
+
mappedAtCreation: false,
|
|
1577
|
+
});
|
|
1578
|
+
if (!created.ok) throw created.error;
|
|
1579
|
+
buffer = created.value;
|
|
1580
|
+
}
|
|
1581
|
+
if (buffer !== undefined) paramsBuffers.set(id, buffer);
|
|
1582
|
+
}
|
|
1583
|
+
} catch (cause) {
|
|
1584
|
+
disposePostProcessResources({ device, paramsBuffers });
|
|
1585
|
+
if (cause instanceof RhiError) return err(cause);
|
|
1586
|
+
return err(
|
|
1587
|
+
new RhiError({
|
|
1588
|
+
code: 'webgpu-runtime-error',
|
|
1589
|
+
expected: 'post-process recovery resources are created on the candidate device',
|
|
1590
|
+
hint: cause instanceof Error ? cause.message : String(cause),
|
|
1591
|
+
}),
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
return ok({ device, paramsBuffers });
|
|
1595
|
+
};
|
|
1596
|
+
const publishRecoveryPostProcessResources = (candidate: RecoveryPostProcessResources): void => {
|
|
1597
|
+
if (candidate.paramsBuffers === postProcessParamsBuffers) return;
|
|
1598
|
+
const previous = { device: postProcessParamsDevice, paramsBuffers: postProcessParamsBuffers };
|
|
1599
|
+
postProcessParamsDevice = candidate.device;
|
|
1600
|
+
postProcessParamsBuffers = candidate.paramsBuffers;
|
|
1601
|
+
disposePostProcessResources(previous);
|
|
1602
|
+
};
|
|
1603
|
+
const discardRecoveryPostProcessResources = (candidate: RecoveryPostProcessResources): void => {
|
|
1604
|
+
if (candidate.paramsBuffers === postProcessParamsBuffers) {
|
|
1605
|
+
postProcessParamsBuffers = new Map();
|
|
1606
|
+
disposePostProcessResources(candidate);
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
disposePostProcessResources(candidate);
|
|
1610
|
+
};
|
|
1570
1611
|
interface PostProcessParamsCandidate {
|
|
1571
1612
|
readonly buffers: ReadonlyMap<string, Buffer>;
|
|
1572
1613
|
readonly created: readonly Buffer[];
|
|
@@ -1666,6 +1707,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1666
1707
|
const key = `${id}|${colorFormat}|${signature}`;
|
|
1667
1708
|
const cached = postProcessPipelineCache.get(key);
|
|
1668
1709
|
if (cached !== undefined) return cached;
|
|
1710
|
+
internals.recoveryColdWorkGuard?.notePipelineColdWork();
|
|
1669
1711
|
const factory = internals.buildPostProcessPipeline;
|
|
1670
1712
|
if (factory === undefined) return null;
|
|
1671
1713
|
const built = factory(
|
|
@@ -1842,44 +1884,60 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1842
1884
|
createBindGroup: 0,
|
|
1843
1885
|
keys: [],
|
|
1844
1886
|
};
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1887
|
+
type PreparedResolverCaches = {
|
|
1888
|
+
readonly preparedPipelineIds: WeakMap<object, string>;
|
|
1889
|
+
readonly preparedMaterialPipelineShaders: WeakMap<object, string>;
|
|
1890
|
+
readonly preparedGroup0Pipelines: WeakSet<object>;
|
|
1891
|
+
readonly preparedViewOnlyPipelines: WeakSet<object>;
|
|
1892
|
+
readonly preparedRenderMaterialPipelines: WeakSet<object>;
|
|
1893
|
+
readonly preparedAssetHandles: WeakMap<World, Map<string, number>>;
|
|
1894
|
+
};
|
|
1895
|
+
const createPreparedResolverCaches = (): PreparedResolverCaches => ({
|
|
1896
|
+
preparedPipelineIds: new WeakMap(),
|
|
1897
|
+
preparedMaterialPipelineShaders: new WeakMap(),
|
|
1898
|
+
preparedGroup0Pipelines: new WeakSet(),
|
|
1899
|
+
preparedViewOnlyPipelines: new WeakSet(),
|
|
1900
|
+
preparedRenderMaterialPipelines: new WeakSet(),
|
|
1901
|
+
preparedAssetHandles: new WeakMap(),
|
|
1902
|
+
});
|
|
1903
|
+
const activePreparedResolverCaches = createPreparedResolverCaches();
|
|
1851
1904
|
const preparedHandle = <Brand extends string>(
|
|
1905
|
+
runtime: RenderSystemInternals,
|
|
1906
|
+
caches: PreparedResolverCaches,
|
|
1852
1907
|
world: World,
|
|
1853
1908
|
guid: string,
|
|
1854
1909
|
brand: Brand,
|
|
1855
1910
|
): Handle<Brand, 'shared'> | undefined => {
|
|
1856
|
-
let handles = preparedAssetHandles.get(world);
|
|
1911
|
+
let handles = caches.preparedAssetHandles.get(world);
|
|
1857
1912
|
if (handles === undefined) {
|
|
1858
1913
|
handles = new Map();
|
|
1859
|
-
preparedAssetHandles.set(world, handles);
|
|
1914
|
+
caches.preparedAssetHandles.set(world, handles);
|
|
1860
1915
|
}
|
|
1861
1916
|
const key = `${brand}:${guid.toLowerCase()}`;
|
|
1862
1917
|
const cached = handles.get(key);
|
|
1863
1918
|
if (cached !== undefined) return cached as Handle<Brand, 'shared'>;
|
|
1864
|
-
const asset =
|
|
1919
|
+
const asset = runtime.assets.lookup(guid);
|
|
1865
1920
|
if (asset === undefined) return undefined;
|
|
1866
1921
|
const handle = world.allocSharedRef(brand, asset);
|
|
1867
1922
|
handles.set(key, handle as number);
|
|
1868
1923
|
return handle;
|
|
1869
1924
|
};
|
|
1870
1925
|
const preparedMaterialBindings = (
|
|
1926
|
+
runtime: RenderSystemInternals,
|
|
1927
|
+
caches: PreparedResolverCaches,
|
|
1928
|
+
worlds: readonly World[],
|
|
1871
1929
|
materialShaderId: string,
|
|
1872
1930
|
worldIndex: number,
|
|
1873
1931
|
materialGuid: string,
|
|
1874
1932
|
layout: BindGroupLayout,
|
|
1875
1933
|
) => {
|
|
1876
|
-
const world =
|
|
1877
|
-
const material =
|
|
1878
|
-
const pipelineState =
|
|
1934
|
+
const world = worlds[worldIndex];
|
|
1935
|
+
const material = runtime.assets.lookup(materialGuid) as MaterialAsset | undefined;
|
|
1936
|
+
const pipelineState = runtime.getPipelineState();
|
|
1879
1937
|
if (world === undefined || material?.kind !== 'material' || pipelineState === null) {
|
|
1880
1938
|
return err(new Error('prepared material asset is unavailable'));
|
|
1881
1939
|
}
|
|
1882
|
-
const schema =
|
|
1940
|
+
const schema = runtime.getParamSchema?.(materialShaderId) ?? [];
|
|
1883
1941
|
const values = material.values ?? {};
|
|
1884
1942
|
const derived = derive(schema);
|
|
1885
1943
|
const fields = [...derived.textureFieldNames];
|
|
@@ -1892,7 +1950,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1892
1950
|
schema,
|
|
1893
1951
|
values as Readonly<Record<string, number | readonly number[]>>,
|
|
1894
1952
|
);
|
|
1895
|
-
const created =
|
|
1953
|
+
const created = runtime.device.createBuffer({
|
|
1896
1954
|
label: `prepared-material:${materialGuid}`,
|
|
1897
1955
|
size: payload.byteLength,
|
|
1898
1956
|
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
@@ -1900,9 +1958,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1900
1958
|
if (!created.ok) return created;
|
|
1901
1959
|
buffer = created.value;
|
|
1902
1960
|
payloadByteLength = payload.byteLength;
|
|
1903
|
-
const written =
|
|
1961
|
+
const written = runtime.device.queue.writeBuffer(buffer, 0, payload);
|
|
1904
1962
|
if (!written.ok) {
|
|
1905
|
-
|
|
1963
|
+
runtime.device.destroyBuffer(buffer);
|
|
1906
1964
|
return written;
|
|
1907
1965
|
}
|
|
1908
1966
|
}
|
|
@@ -1919,20 +1977,32 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1919
1977
|
: undefined;
|
|
1920
1978
|
let sampler = pipelineState.defaultSampler;
|
|
1921
1979
|
if (textureValue?.sampler !== undefined) {
|
|
1922
|
-
const handle = preparedHandle(
|
|
1980
|
+
const handle = preparedHandle(
|
|
1981
|
+
runtime,
|
|
1982
|
+
caches,
|
|
1983
|
+
world,
|
|
1984
|
+
String(textureValue.sampler),
|
|
1985
|
+
'SamplerAsset',
|
|
1986
|
+
);
|
|
1923
1987
|
if (handle !== undefined) {
|
|
1924
1988
|
const asset = resolveAssetHandle<SamplerAsset>(world, handle);
|
|
1925
1989
|
if (asset.ok) {
|
|
1926
|
-
const resident =
|
|
1990
|
+
const resident = runtime.gpuStore.ensureSamplerResident(handle, asset.value, world);
|
|
1927
1991
|
if (resident.ok) sampler = resident.value;
|
|
1928
1992
|
}
|
|
1929
1993
|
}
|
|
1930
1994
|
}
|
|
1931
1995
|
let view = pipelineState.defaultWhiteTextureView;
|
|
1932
1996
|
if (textureValue !== undefined) {
|
|
1933
|
-
const handle = preparedHandle(
|
|
1997
|
+
const handle = preparedHandle(
|
|
1998
|
+
runtime,
|
|
1999
|
+
caches,
|
|
2000
|
+
world,
|
|
2001
|
+
String(textureValue.texture),
|
|
2002
|
+
'TextureAsset',
|
|
2003
|
+
);
|
|
1934
2004
|
if (handle !== undefined) {
|
|
1935
|
-
view = residentTextureView(world,
|
|
2005
|
+
view = residentTextureView(world, runtime.gpuStore, runtime, handle) ?? view;
|
|
1936
2006
|
}
|
|
1937
2007
|
}
|
|
1938
2008
|
textureResources.push({ sampler, view });
|
|
@@ -1968,12 +2038,12 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1968
2038
|
textureIndex += 1;
|
|
1969
2039
|
continue;
|
|
1970
2040
|
}
|
|
1971
|
-
if (buffer !== undefined)
|
|
2041
|
+
if (buffer !== undefined) runtime.device.destroyBuffer(buffer);
|
|
1972
2042
|
return err(new Error('prepared material schema contains unsupported binding kinds'));
|
|
1973
2043
|
}
|
|
1974
2044
|
const fallback = pipelineState.skylightFallback;
|
|
1975
2045
|
if (fallback === null) {
|
|
1976
|
-
if (buffer !== undefined)
|
|
2046
|
+
if (buffer !== undefined) runtime.device.destroyBuffer(buffer);
|
|
1977
2047
|
return err(new Error('prepared material skylight fallback is unavailable'));
|
|
1978
2048
|
}
|
|
1979
2049
|
const completeEntries = assembleMaterialWithSkylightEntries(entries, {
|
|
@@ -1985,207 +2055,268 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1985
2055
|
brdfLutSampler: fallback.sampler,
|
|
1986
2056
|
intensityBuffer: fallback.intensityBuffer,
|
|
1987
2057
|
});
|
|
1988
|
-
const group =
|
|
2058
|
+
const group = runtime.device.createBindGroup({ layout, entries: completeEntries });
|
|
1989
2059
|
if (!group.ok) {
|
|
1990
|
-
if (buffer !== undefined)
|
|
2060
|
+
if (buffer !== undefined) runtime.device.destroyBuffer(buffer);
|
|
1991
2061
|
return group;
|
|
1992
2062
|
}
|
|
1993
2063
|
if (buffer === undefined) return group;
|
|
1994
2064
|
return ok({
|
|
1995
2065
|
handle: group.value,
|
|
1996
2066
|
dynamicOffsets: [0, 0],
|
|
1997
|
-
release: () =>
|
|
2067
|
+
release: () => runtime.device.destroyBuffer(buffer),
|
|
1998
2068
|
});
|
|
1999
2069
|
};
|
|
2000
2070
|
const lastFrustumStats: { culled: number; total: number } = { culled: 0, total: 0 };
|
|
2001
2071
|
const lastVisibilityStats: { explicitlyHidden: number } = { explicitlyHidden: 0 };
|
|
2002
2072
|
let lastMeshMaterialBindings: readonly MeshMaterialBindingObservation[] = [];
|
|
2003
2073
|
let lastMeshMaterialBindingFrame: ExtractedFrame | undefined;
|
|
2004
|
-
const
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2074
|
+
const createPreparedResolverFactory =
|
|
2075
|
+
(
|
|
2076
|
+
runtime: RenderSystemInternals,
|
|
2077
|
+
gpuWork: RenderFeatureGpuWorkOwner,
|
|
2078
|
+
worlds: readonly World[],
|
|
2079
|
+
caches: PreparedResolverCaches,
|
|
2080
|
+
) =>
|
|
2081
|
+
(input: RenderFeaturePreparedGraphicsResolverInput): PreparedGraphicsResolver => {
|
|
2082
|
+
const {
|
|
2083
|
+
preparedPipelineIds,
|
|
2084
|
+
preparedMaterialPipelineShaders,
|
|
2085
|
+
preparedGroup0Pipelines,
|
|
2086
|
+
preparedViewOnlyPipelines,
|
|
2087
|
+
preparedRenderMaterialPipelines,
|
|
2088
|
+
} = caches;
|
|
2089
|
+
return createPreparedGraphicsResolver({
|
|
2090
|
+
device: runtime.device,
|
|
2091
|
+
featureIdentity: input.featureIdentity,
|
|
2092
|
+
generation: input.generation,
|
|
2093
|
+
capabilityAvailable: true,
|
|
2094
|
+
featureOrder: input.order,
|
|
2095
|
+
lookup: input.lookup,
|
|
2096
|
+
resolveGpuBuffer: (reference) => gpuWork.resolveBuffer(input.featureIdentity, reference),
|
|
2097
|
+
resolvePipeline: (descriptor) => {
|
|
2098
|
+
const postProcessEntry =
|
|
2099
|
+
input.fullscreenEffects.get(descriptor.shader) ??
|
|
2100
|
+
builtinPostProcessEntries.get(descriptor.shader);
|
|
2101
|
+
if (postProcessEntry !== undefined) {
|
|
2102
|
+
const fullscreen = buildFullscreenPostProcessPass(
|
|
2103
|
+
{ device: runtime.device, errorRegistry: runtime.errorRegistry },
|
|
2104
|
+
postProcessEntry,
|
|
2105
|
+
);
|
|
2106
|
+
if (fullscreen === null) return err(new Error('prepared post-process layout failed'));
|
|
2107
|
+
const pipeline = runtime.getPostProcessPipeline?.(
|
|
2108
|
+
descriptor.shader,
|
|
2109
|
+
fullscreen.bindGroupLayout,
|
|
2110
|
+
descriptor.colorFormats[0] as GPUTextureFormat,
|
|
2111
|
+
postProcessEntry,
|
|
2112
|
+
);
|
|
2113
|
+
if (pipeline !== null && pipeline !== undefined) {
|
|
2114
|
+
preparedPipelineIds.set(pipeline as object, descriptor.shader);
|
|
2115
|
+
if (runtime.getMaterialShaderBindingContract?.(descriptor.shader) === 'group-0') {
|
|
2116
|
+
preparedGroup0Pipelines.add(pipeline as object);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
return pipeline === null || pipeline === undefined
|
|
2120
|
+
? err(makePreparedPipelinePendingError())
|
|
2121
|
+
: ok(pipeline);
|
|
2122
|
+
}
|
|
2123
|
+
const preparedPipeline = runtime.getMaterialShaderPipeline?.(
|
|
2027
2124
|
descriptor.shader,
|
|
2028
|
-
|
|
2125
|
+
descriptor.colorFormats[0] === 'rgba16float',
|
|
2126
|
+
descriptor.renderState,
|
|
2127
|
+
descriptor.topology,
|
|
2128
|
+
descriptor.indexFormat,
|
|
2129
|
+
undefined,
|
|
2130
|
+
'forward',
|
|
2131
|
+
undefined,
|
|
2132
|
+
descriptor.sampleCount ?? 1,
|
|
2133
|
+
// Prepared graphics own their declared attachment format. Passing
|
|
2134
|
+
// it through keeps the PSO compatible with feature-target views
|
|
2135
|
+
// (the ordinary material path intentionally defaults to the
|
|
2136
|
+
// swap-chain view format).
|
|
2029
2137
|
descriptor.colorFormats[0] as GPUTextureFormat,
|
|
2030
|
-
|
|
2138
|
+
undefined,
|
|
2139
|
+
descriptor.depthFormat === undefined
|
|
2140
|
+
? null
|
|
2141
|
+
: (descriptor.depthFormat as GPUTextureFormat),
|
|
2142
|
+
descriptor.vertexLayout,
|
|
2143
|
+
undefined,
|
|
2144
|
+
input.shaderModuleMode,
|
|
2031
2145
|
);
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2146
|
+
// A requested material shader is an exact pipeline contract. During
|
|
2147
|
+
// async shader warmup, substituting the generic unlit pipeline can
|
|
2148
|
+
// mismatch vertex layouts and HDR attachment formats, turning a
|
|
2149
|
+
// retryable next-frame prepare into an invalid GPU command buffer.
|
|
2150
|
+
const pipeline = preparedPipeline ?? null;
|
|
2151
|
+
if (pipeline !== null) {
|
|
2152
|
+
preparedMaterialPipelineShaders.set(pipeline as object, descriptor.shader);
|
|
2153
|
+
const bindingContract = runtime.getMaterialShaderBindingContract?.(descriptor.shader);
|
|
2154
|
+
if (bindingContract === 'group-0') {
|
|
2035
2155
|
preparedGroup0Pipelines.add(pipeline as object);
|
|
2156
|
+
} else if (bindingContract === 'view-only') {
|
|
2157
|
+
preparedViewOnlyPipelines.add(pipeline as object);
|
|
2158
|
+
} else {
|
|
2159
|
+
preparedRenderMaterialPipelines.add(pipeline as object);
|
|
2036
2160
|
}
|
|
2037
2161
|
}
|
|
2038
|
-
return pipeline === null
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
undefined,
|
|
2051
|
-
descriptor.sampleCount ?? 1,
|
|
2052
|
-
// Prepared graphics own their declared attachment format. Passing
|
|
2053
|
-
// it through keeps the PSO compatible with feature-target views
|
|
2054
|
-
// (the ordinary material path intentionally defaults to the
|
|
2055
|
-
// swap-chain view format).
|
|
2056
|
-
descriptor.colorFormats[0] as GPUTextureFormat,
|
|
2057
|
-
undefined,
|
|
2058
|
-
descriptor.depthFormat === undefined
|
|
2059
|
-
? null
|
|
2060
|
-
: (descriptor.depthFormat as GPUTextureFormat),
|
|
2061
|
-
descriptor.vertexLayout,
|
|
2062
|
-
undefined,
|
|
2063
|
-
input.shaderModuleMode,
|
|
2064
|
-
);
|
|
2065
|
-
// A requested material shader is an exact pipeline contract. During
|
|
2066
|
-
// async shader warmup, substituting the generic unlit pipeline can
|
|
2067
|
-
// mismatch vertex layouts and HDR attachment formats, turning a
|
|
2068
|
-
// retryable next-frame prepare into an invalid GPU command buffer.
|
|
2069
|
-
const pipeline = preparedPipeline ?? null;
|
|
2070
|
-
if (pipeline !== null) {
|
|
2071
|
-
preparedMaterialPipelineShaders.set(pipeline as object, descriptor.shader);
|
|
2072
|
-
const bindingContract = internals.getMaterialShaderBindingContract?.(descriptor.shader);
|
|
2073
|
-
if (bindingContract === 'group-0') {
|
|
2074
|
-
preparedGroup0Pipelines.add(pipeline as object);
|
|
2075
|
-
} else if (bindingContract === 'view-only') {
|
|
2076
|
-
preparedViewOnlyPipelines.add(pipeline as object);
|
|
2077
|
-
} else {
|
|
2078
|
-
preparedRenderMaterialPipelines.add(pipeline as object);
|
|
2162
|
+
return pipeline === null ? err(makePreparedPipelinePendingError()) : ok(pipeline);
|
|
2163
|
+
},
|
|
2164
|
+
resolveBindings: (descriptor, pipeline) => {
|
|
2165
|
+
const materialShaderId = preparedMaterialPipelineShaders.get(pipeline as object);
|
|
2166
|
+
const bindingContract =
|
|
2167
|
+
materialShaderId === undefined
|
|
2168
|
+
? undefined
|
|
2169
|
+
: runtime.getMaterialShaderBindingContract?.(materialShaderId);
|
|
2170
|
+
if (bindingContract === 'group-0-resource') {
|
|
2171
|
+
return descriptor.values.sceneDepth === undefined
|
|
2172
|
+
? err(new Error('prepared group-0 resource pipeline requires a scene target'))
|
|
2173
|
+
: ok(undefined);
|
|
2079
2174
|
}
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2175
|
+
if (bindingContract === 'view-and-scene-depth') {
|
|
2176
|
+
return ok(undefined);
|
|
2177
|
+
}
|
|
2178
|
+
if (preparedGroup0Pipelines.has(pipeline as object)) {
|
|
2179
|
+
const layout =
|
|
2180
|
+
(materialShaderId === undefined
|
|
2181
|
+
? undefined
|
|
2182
|
+
: runtime.getMaterialBindGroupLayout?.(materialShaderId)) ??
|
|
2183
|
+
(
|
|
2184
|
+
pipeline as RenderPipeline & {
|
|
2185
|
+
getBindGroupLayout?: (index: number) => BindGroupLayout;
|
|
2186
|
+
}
|
|
2187
|
+
).getBindGroupLayout?.(0);
|
|
2188
|
+
return layout === undefined
|
|
2189
|
+
? err(new Error('prepared group-0 pipeline bind group layout is unavailable'))
|
|
2190
|
+
: runtime.device.createBindGroup({ layout, entries: [] });
|
|
2191
|
+
}
|
|
2192
|
+
if (
|
|
2193
|
+
preparedViewOnlyPipelines.has(pipeline as object) ||
|
|
2194
|
+
(preparedRenderMaterialPipelines.has(pipeline as object) &&
|
|
2195
|
+
descriptor.values.group === 0) ||
|
|
2196
|
+
pipeline === runtime.getPipelineState()?.unlitPipeline
|
|
2197
|
+
) {
|
|
2198
|
+
return ok(undefined);
|
|
2199
|
+
}
|
|
2200
|
+
const group = descriptor.values.group;
|
|
2098
2201
|
const layout =
|
|
2099
|
-
(materialShaderId
|
|
2100
|
-
?
|
|
2101
|
-
|
|
2202
|
+
(materialShaderId !== undefined
|
|
2203
|
+
? (runtime.getMaterialBindGroupLayout?.(materialShaderId) ??
|
|
2204
|
+
(group === 0 ? runtime.getPipelineState()?.materialBindGroupLayout : undefined))
|
|
2205
|
+
: undefined) ??
|
|
2102
2206
|
(
|
|
2103
2207
|
pipeline as RenderPipeline & {
|
|
2104
2208
|
getBindGroupLayout?: (index: number) => BindGroupLayout;
|
|
2105
2209
|
}
|
|
2106
|
-
).getBindGroupLayout?.(0);
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
pipeline as RenderPipeline & {
|
|
2127
|
-
getBindGroupLayout?: (index: number) => BindGroupLayout;
|
|
2128
|
-
}
|
|
2129
|
-
).getBindGroupLayout?.(group === 1 ? 1 : 0);
|
|
2130
|
-
const material = descriptor.values.material;
|
|
2131
|
-
if (
|
|
2132
|
-
group === 1 &&
|
|
2133
|
-
layout !== undefined &&
|
|
2134
|
-
materialShaderId !== undefined &&
|
|
2135
|
-
typeof material === 'object' &&
|
|
2136
|
-
material !== null &&
|
|
2137
|
-
'world' in material &&
|
|
2138
|
-
'guid' in material &&
|
|
2139
|
-
typeof material.world === 'number' &&
|
|
2140
|
-
typeof material.guid === 'string'
|
|
2141
|
-
) {
|
|
2142
|
-
return preparedMaterialBindings(materialShaderId, material.world, material.guid, layout);
|
|
2143
|
-
}
|
|
2144
|
-
return layout === undefined
|
|
2145
|
-
? err(new Error('prepared pipeline bind group layout is unavailable'))
|
|
2146
|
-
: internals.device.createBindGroup({
|
|
2210
|
+
).getBindGroupLayout?.(group === 1 ? 1 : 0);
|
|
2211
|
+
const material = descriptor.values.material;
|
|
2212
|
+
if (
|
|
2213
|
+
group === 1 &&
|
|
2214
|
+
layout !== undefined &&
|
|
2215
|
+
materialShaderId !== undefined &&
|
|
2216
|
+
typeof material === 'object' &&
|
|
2217
|
+
material !== null &&
|
|
2218
|
+
'world' in material &&
|
|
2219
|
+
'guid' in material &&
|
|
2220
|
+
typeof material.world === 'number' &&
|
|
2221
|
+
typeof material.guid === 'string'
|
|
2222
|
+
) {
|
|
2223
|
+
return preparedMaterialBindings(
|
|
2224
|
+
runtime,
|
|
2225
|
+
caches,
|
|
2226
|
+
worlds,
|
|
2227
|
+
materialShaderId,
|
|
2228
|
+
material.world,
|
|
2229
|
+
material.guid,
|
|
2147
2230
|
layout,
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2231
|
+
);
|
|
2232
|
+
}
|
|
2233
|
+
return layout === undefined
|
|
2234
|
+
? err(new Error('prepared pipeline bind group layout is unavailable'))
|
|
2235
|
+
: runtime.device.createBindGroup({
|
|
2236
|
+
layout,
|
|
2237
|
+
entries:
|
|
2238
|
+
group === 1 && preparedPipelineIds.has(pipeline as object)
|
|
2239
|
+
? [
|
|
2240
|
+
{
|
|
2241
|
+
binding: 0,
|
|
2242
|
+
resource: {
|
|
2243
|
+
kind: 'textureView',
|
|
2244
|
+
value: runtime.getPipelineState()?.fallbackTextureView as never,
|
|
2245
|
+
},
|
|
2156
2246
|
},
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2247
|
+
{
|
|
2248
|
+
binding: 1,
|
|
2249
|
+
resource: {
|
|
2250
|
+
kind: 'sampler',
|
|
2251
|
+
value: runtime.getPipelineState()?.defaultSampler as never,
|
|
2252
|
+
},
|
|
2163
2253
|
},
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2254
|
+
...(runtime.getPostProcessParamsBuffer?.(
|
|
2255
|
+
preparedPipelineIds.get(pipeline as object) ?? '',
|
|
2256
|
+
) === undefined
|
|
2257
|
+
? []
|
|
2258
|
+
: [
|
|
2259
|
+
{
|
|
2260
|
+
binding: 2,
|
|
2261
|
+
resource: {
|
|
2262
|
+
kind: 'buffer' as const,
|
|
2263
|
+
value: {
|
|
2264
|
+
buffer: runtime.getPostProcessParamsBuffer?.(
|
|
2265
|
+
preparedPipelineIds.get(pipeline as object) ?? '',
|
|
2266
|
+
) as never,
|
|
2267
|
+
},
|
|
2178
2268
|
},
|
|
2179
2269
|
},
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
]
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2270
|
+
]),
|
|
2271
|
+
]
|
|
2272
|
+
: [],
|
|
2273
|
+
});
|
|
2274
|
+
},
|
|
2275
|
+
});
|
|
2276
|
+
};
|
|
2277
|
+
const preparedResolverFactory = (
|
|
2278
|
+
input: RenderFeaturePreparedGraphicsResolverInput,
|
|
2279
|
+
): PreparedGraphicsResolver =>
|
|
2280
|
+
createPreparedResolverFactory(
|
|
2281
|
+
internals,
|
|
2282
|
+
featureGpuWork,
|
|
2283
|
+
preparedWorlds,
|
|
2284
|
+
activePreparedResolverCaches,
|
|
2285
|
+
)(input);
|
|
2286
|
+
const recoveryOwner = createRenderSystemRecovery({
|
|
2287
|
+
frameState,
|
|
2288
|
+
internals,
|
|
2289
|
+
getEnvironmentLifecycle: () => environmentLifecycle,
|
|
2290
|
+
getGpuDrivenProduction: () => gpuDrivenProduction,
|
|
2291
|
+
setGpuDrivenProduction: (production) => {
|
|
2292
|
+
gpuDrivenProduction = production;
|
|
2293
|
+
},
|
|
2294
|
+
disposeFeatureGpuWork,
|
|
2295
|
+
setFeatureGpuWork: (owner) => {
|
|
2296
|
+
featureGpuWork = owner;
|
|
2297
|
+
},
|
|
2298
|
+
getActiveFeaturePostProcessEntries: () => activeFeaturePostProcessEntries,
|
|
2299
|
+
setActiveFeaturePostProcessEntries: (entries) => {
|
|
2300
|
+
activeFeaturePostProcessEntries = entries;
|
|
2301
|
+
},
|
|
2302
|
+
postProcessPipelineCache,
|
|
2303
|
+
clearPostProcessPipelineCache,
|
|
2304
|
+
clearPostProcessPipelineEntry,
|
|
2305
|
+
invalidatePostProcessModule,
|
|
2306
|
+
persistentRenderScene,
|
|
2307
|
+
gpuDrivenShaderFactory,
|
|
2308
|
+
createFeatureGpuWorkOwner,
|
|
2309
|
+
pointsLinesOwner,
|
|
2310
|
+
createPreparedResolverFactory,
|
|
2311
|
+
createPreparedResolverCaches,
|
|
2312
|
+
});
|
|
2188
2313
|
return {
|
|
2314
|
+
get instanceCollectionsInspection(): readonly InstanceCollectionInspection[] {
|
|
2315
|
+
return instanceCollections._inspections(
|
|
2316
|
+
frameState.instanceResidency,
|
|
2317
|
+
frameState.frameNumber - 1,
|
|
2318
|
+
);
|
|
2319
|
+
},
|
|
2189
2320
|
releaseProfilerCatalog(): void {
|
|
2190
2321
|
releaseProfilerCatalog?.();
|
|
2191
2322
|
releaseProfilerCatalog = undefined;
|
|
@@ -2387,7 +2518,10 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2387
2518
|
renderReadLeases?: readonly RenderReadLease[],
|
|
2388
2519
|
timingCapture?: GpuTimingCapture,
|
|
2389
2520
|
): boolean {
|
|
2390
|
-
|
|
2521
|
+
const composition = canonicalizeWorldComposition(worlds, opts, renderReadLeases);
|
|
2522
|
+
const compositionWorlds = composition.worlds;
|
|
2523
|
+
const compositionLeases = composition.leases;
|
|
2524
|
+
preparedWorlds = compositionWorlds;
|
|
2391
2525
|
const profileSession = internals.profiler?.activeSession();
|
|
2392
2526
|
let ownsProfileFrame = false;
|
|
2393
2527
|
let submitted = false;
|
|
@@ -2403,7 +2537,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2403
2537
|
// cameraOwner drives the surfaced cameras + frustum
|
|
2404
2538
|
// cull; resourceOwner drives skylight/skybox/postProcess + per-world
|
|
2405
2539
|
// record config.
|
|
2406
|
-
const { cameraOwner, resourceOwner } =
|
|
2540
|
+
const { cameraOwner, resourceOwner } = composition.owners;
|
|
2407
2541
|
// Keep the compiled graph as last-known-good until the candidate topology
|
|
2408
2542
|
// compiles. The failed candidate frame does not execute it; successful
|
|
2409
2543
|
// replacement retires it atomically in ensureCompiledFrameGraph.
|
|
@@ -2438,28 +2572,33 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2438
2572
|
// the dedicated resourceOwner index. The variable is named
|
|
2439
2573
|
// `resourceWorld` so the record-stage reads are self-describing (they
|
|
2440
2574
|
// are resource-owner reads, not camera reads).
|
|
2441
|
-
const resourceWorld =
|
|
2575
|
+
const resourceWorld = compositionWorlds[resourceOwner] as World;
|
|
2442
2576
|
const frame = runProfiledRenderPhase(profileSession, 'extract', () => {
|
|
2443
2577
|
return persistentRenderScene.extractComposition(
|
|
2444
|
-
|
|
2578
|
+
compositionWorlds,
|
|
2445
2579
|
{ cameraOwner, resourceOwner },
|
|
2446
2580
|
internals.assets.catalogEpoch,
|
|
2447
|
-
(
|
|
2581
|
+
(request: PersistentRenderCandidateRequest) =>
|
|
2448
2582
|
extractFrames(
|
|
2449
|
-
|
|
2583
|
+
compositionWorlds,
|
|
2450
2584
|
{ cameraOwner, resourceOwner },
|
|
2451
2585
|
internals.assets,
|
|
2452
2586
|
internals.getPipelineState(),
|
|
2453
2587
|
persistentRenderScene.materialSnapshotCacheStore(),
|
|
2454
2588
|
{
|
|
2455
2589
|
cull: 'none',
|
|
2456
|
-
renderables,
|
|
2590
|
+
renderables: request === 'none' ? 'none' : 'full',
|
|
2591
|
+
...(request === 'none' || request === 'full'
|
|
2592
|
+
? {}
|
|
2593
|
+
: { renderableEntitiesByWorld: request.entitiesByWorld }),
|
|
2594
|
+
retainHidden: true,
|
|
2595
|
+
instanceCollections,
|
|
2457
2596
|
...(internals.getMaterialShaderArtifact === undefined
|
|
2458
2597
|
? {}
|
|
2459
2598
|
: { getMaterialShaderArtifact: internals.getMaterialShaderArtifact }),
|
|
2460
2599
|
},
|
|
2461
2600
|
),
|
|
2462
|
-
|
|
2601
|
+
compositionLeases,
|
|
2463
2602
|
);
|
|
2464
2603
|
});
|
|
2465
2604
|
const {
|
|
@@ -2491,16 +2630,16 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2491
2630
|
skylight,
|
|
2492
2631
|
);
|
|
2493
2632
|
latestCamera = cameras[0];
|
|
2494
|
-
persistentRenderScene.updateVisibilityFacet(
|
|
2633
|
+
persistentRenderScene.updateVisibilityFacet(compositionWorlds, latestCamera, renderables);
|
|
2495
2634
|
let occlusionProjection:
|
|
2496
2635
|
| import('./scene/visibility/occlusion-runtime').OcclusionFrameProjection
|
|
2497
2636
|
| undefined;
|
|
2498
2637
|
let occlusionFallback: LodOcclusionInspection['fallback'] = { active: false };
|
|
2499
|
-
const worldKeys = persistentRenderScene.visibilityWorldKeysFor(
|
|
2638
|
+
const worldKeys = persistentRenderScene.visibilityWorldKeysFor(compositionWorlds);
|
|
2500
2639
|
const slotByEntity = persistentRenderScene.compositionSlotByStableEntity();
|
|
2501
2640
|
if (latestCamera !== undefined) {
|
|
2502
2641
|
const camera = latestCamera;
|
|
2503
|
-
const cameraWorld =
|
|
2642
|
+
const cameraWorld = compositionWorlds[camera.worldId ?? cameraOwner];
|
|
2504
2643
|
const view = viewKey({
|
|
2505
2644
|
attachmentId: cameraWorld?.identity ?? 'missing-camera-world',
|
|
2506
2645
|
cameraEntity: camera.entityKey ?? 0,
|
|
@@ -2514,7 +2653,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2514
2653
|
cachedCandidates.cameraWorldIdentity === (cameraWorld?.identity ?? '')
|
|
2515
2654
|
? cachedCandidates.candidates
|
|
2516
2655
|
: renderables.flatMap((renderable) => {
|
|
2517
|
-
const world =
|
|
2656
|
+
const world = compositionWorlds[renderable.worldId];
|
|
2518
2657
|
const aabb = renderable.localAabb;
|
|
2519
2658
|
// Occlusion is a LOD transport concern. A plain mesh has no
|
|
2520
2659
|
// lower-detail range to select, so keep it on the ordinary CPU
|
|
@@ -2599,7 +2738,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2599
2738
|
latestCamera !== undefined &&
|
|
2600
2739
|
ensureOcclusionRuntime().prepareUnavailable
|
|
2601
2740
|
) {
|
|
2602
|
-
const cameraWorld =
|
|
2741
|
+
const cameraWorld = compositionWorlds[latestCamera.worldId ?? cameraOwner];
|
|
2603
2742
|
if (cameraWorld !== undefined) {
|
|
2604
2743
|
const fallbackView = viewKey({
|
|
2605
2744
|
attachmentId: cameraWorld.identity,
|
|
@@ -2609,7 +2748,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2609
2748
|
});
|
|
2610
2749
|
for (const renderable of renderables) {
|
|
2611
2750
|
if ((renderable.lods?.length ?? 0) === 0) continue;
|
|
2612
|
-
const world =
|
|
2751
|
+
const world = compositionWorlds[renderable.worldId];
|
|
2613
2752
|
if (world === undefined) continue;
|
|
2614
2753
|
const slot = slotByEntity.get(
|
|
2615
2754
|
worldEntityKey(
|
|
@@ -2632,7 +2771,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2632
2771
|
}
|
|
2633
2772
|
}
|
|
2634
2773
|
const visibilityProjection = persistentRenderScene.projectVisibility(
|
|
2635
|
-
|
|
2774
|
+
compositionWorlds,
|
|
2636
2775
|
latestCamera,
|
|
2637
2776
|
renderables,
|
|
2638
2777
|
dispatch,
|
|
@@ -2678,7 +2817,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2678
2817
|
const inspectionQuery = ensureOcclusionRuntime().inspect();
|
|
2679
2818
|
const inspectionBudget = persistentRenderScene.visibilityBudgetValue();
|
|
2680
2819
|
const inspectionWorlds = createLodWorldInspections(
|
|
2681
|
-
|
|
2820
|
+
compositionWorlds,
|
|
2682
2821
|
renderables,
|
|
2683
2822
|
submissionRenderables,
|
|
2684
2823
|
inspectionCamera,
|
|
@@ -2697,8 +2836,8 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2697
2836
|
: { active: false },
|
|
2698
2837
|
);
|
|
2699
2838
|
const attachmentByWorldKey = new Map<number, string>();
|
|
2700
|
-
for (let worldId = 0; worldId <
|
|
2701
|
-
const world =
|
|
2839
|
+
for (let worldId = 0; worldId < compositionWorlds.length; worldId += 1) {
|
|
2840
|
+
const world = compositionWorlds[worldId];
|
|
2702
2841
|
if (world === undefined) continue;
|
|
2703
2842
|
const worldKey = worldKeys[worldId] ?? worldId;
|
|
2704
2843
|
if (attachmentByWorldKey.has(worldKey)) {
|
|
@@ -2717,7 +2856,8 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2717
2856
|
sourceKey: `render-frame:${frameState.frameNumber}`,
|
|
2718
2857
|
},
|
|
2719
2858
|
view: {
|
|
2720
|
-
attachmentId:
|
|
2859
|
+
attachmentId:
|
|
2860
|
+
compositionWorlds[inspectionCamera.worldId ?? cameraOwner]?.identity ?? 'none',
|
|
2721
2861
|
cameraEntity: inspectionCamera.entityKey ?? 0,
|
|
2722
2862
|
viewRole: 'main',
|
|
2723
2863
|
viewGeneration: inspectionCamera.historyVersion ?? 0,
|
|
@@ -2859,7 +2999,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2859
2999
|
const preparedResourceBatches = runProfiledRenderPhase(profileSession, 'features', () => {
|
|
2860
3000
|
if (internals.featureHost === undefined) return [];
|
|
2861
3001
|
const featureFrame = runRenderFeatureFrame(internals.featureHost, {
|
|
2862
|
-
worlds,
|
|
3002
|
+
worlds: compositionWorlds,
|
|
2863
3003
|
owner: resourceOwner,
|
|
2864
3004
|
frameNumber: frameState.frameNumber,
|
|
2865
3005
|
visibilitySnapshots: frame.featureVisibilitySnapshots,
|
|
@@ -3021,7 +3161,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3021
3161
|
skybox,
|
|
3022
3162
|
skyboxCount,
|
|
3023
3163
|
postProcessParams,
|
|
3024
|
-
|
|
3164
|
+
compositionWorlds,
|
|
3025
3165
|
recordProfilePhase,
|
|
3026
3166
|
{
|
|
3027
3167
|
owner: gpuDrivenProduction,
|
|
@@ -3034,7 +3174,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3034
3174
|
telemetryCandidateCount: lodCandidateCountForFrame,
|
|
3035
3175
|
...(occlusionProjection === undefined ? {} : { occlusion: occlusionProjection }),
|
|
3036
3176
|
},
|
|
3037
|
-
|
|
3177
|
+
compositionLeases,
|
|
3038
3178
|
featureGraphCandidate,
|
|
3039
3179
|
pointsLinesOwner,
|
|
3040
3180
|
{
|
|
@@ -3051,6 +3191,14 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3051
3191
|
timingCapture,
|
|
3052
3192
|
),
|
|
3053
3193
|
);
|
|
3194
|
+
if (submitted) {
|
|
3195
|
+
try {
|
|
3196
|
+
internals.recoveryColdWorkGuard?.finish();
|
|
3197
|
+
} catch (cause) {
|
|
3198
|
+
submitted = false;
|
|
3199
|
+
throw cause;
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3054
3202
|
if (submitted && lastLodOcclusionInspection !== undefined) {
|
|
3055
3203
|
const query = ensureOcclusionRuntime().inspect();
|
|
3056
3204
|
lastLodOcclusionInspection = {
|
|
@@ -3090,6 +3238,16 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3090
3238
|
};
|
|
3091
3239
|
}
|
|
3092
3240
|
if (submitted) {
|
|
3241
|
+
recoveryOwner.setLastSuccessfulFrameSeed({
|
|
3242
|
+
frame,
|
|
3243
|
+
worlds: compositionWorlds,
|
|
3244
|
+
cameraOwner: composition.owners.cameraOwner,
|
|
3245
|
+
resourceOwner: composition.owners.resourceOwner,
|
|
3246
|
+
width: Math.max(1, internals.canvas.width),
|
|
3247
|
+
height: Math.max(1, internals.canvas.height),
|
|
3248
|
+
});
|
|
3249
|
+
submittedFrameCount += 1;
|
|
3250
|
+
lastSubmittedGeneration = internals.deviceScope?.generation;
|
|
3093
3251
|
if (occlusionProjection === undefined) {
|
|
3094
3252
|
runProfiledRenderPhase(profileSession, 'record/occlusion-global-advance', () =>
|
|
3095
3253
|
ensureOcclusionRuntime().advanceSuccessfulSubmit(frameState.frameNumber),
|
|
@@ -3226,6 +3384,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3226
3384
|
get meshMaterialBindings(): readonly MeshMaterialBindingObservation[] {
|
|
3227
3385
|
return lastMeshMaterialBindings;
|
|
3228
3386
|
},
|
|
3387
|
+
get iblBinding(): import('./mesh-material-bindings').IblBindingInspection | undefined {
|
|
3388
|
+
return frameState.iblBindingInspection;
|
|
3389
|
+
},
|
|
3229
3390
|
get perFramePassNames(): readonly string[] {
|
|
3230
3391
|
return frameState.compiledFrameGraph?.inspect().passes.map((pass) => pass.name) ?? [];
|
|
3231
3392
|
},
|
|
@@ -3235,6 +3396,25 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3235
3396
|
get perFrameGraphInfo(): CompiledRenderGraphInfo | undefined {
|
|
3236
3397
|
return frameState.compiledFrameGraph?.inspect();
|
|
3237
3398
|
},
|
|
3399
|
+
get recoveryEvidence(): RecoveryProductionEvidence {
|
|
3400
|
+
const graph = frameState.compiledFrameGraph?.inspect();
|
|
3401
|
+
return Object.freeze({
|
|
3402
|
+
producerRoots: createRendererProducerRootMatrix(),
|
|
3403
|
+
graph: Object.freeze({
|
|
3404
|
+
ready: graph !== undefined,
|
|
3405
|
+
generation: frameState.graphGeneration,
|
|
3406
|
+
passCount: graph?.passes.length ?? 0,
|
|
3407
|
+
resourceCount: graph?.resources.length ?? 0,
|
|
3408
|
+
}),
|
|
3409
|
+
residency: Object.freeze({
|
|
3410
|
+
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
3411
|
+
}),
|
|
3412
|
+
submissions: Object.freeze({
|
|
3413
|
+
count: submittedFrameCount,
|
|
3414
|
+
lastGeneration: lastSubmittedGeneration,
|
|
3415
|
+
}),
|
|
3416
|
+
});
|
|
3417
|
+
},
|
|
3238
3418
|
get volumetricFog(): import('./volume/inspection').VolumetricFogInspection {
|
|
3239
3419
|
return frameState.volumetricFogInspection;
|
|
3240
3420
|
},
|
|
@@ -3330,6 +3510,11 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3330
3510
|
throw cause;
|
|
3331
3511
|
}
|
|
3332
3512
|
},
|
|
3513
|
+
lookupPostProcess,
|
|
3514
|
+
prepareRecoveryGraphCandidate: recoveryOwner.prepareRecoveryGraphCandidate,
|
|
3515
|
+
submitCandidateSetup: recoveryOwner.submitCandidateSetup,
|
|
3516
|
+
publishRecoveryGraphCandidate: recoveryOwner.publishRecoveryGraphCandidate,
|
|
3517
|
+
discardRecoveryGraphCandidate: recoveryOwner.discardRecoveryGraphCandidate,
|
|
3333
3518
|
disposeFrameState(): void {
|
|
3334
3519
|
(internals as TimingSessionHost).gpuPassTimingSession?.dispose();
|
|
3335
3520
|
(internals as TimingSessionHost).gpuPassTimingSession = undefined;
|
|
@@ -3337,7 +3522,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3337
3522
|
(internals as TimingSessionHost).gpuPassTimingSubmittedWork = undefined;
|
|
3338
3523
|
(internals as TimingSessionHost).gpuPassTimingFrameIdentity = undefined;
|
|
3339
3524
|
(internals as TimingSessionHost).gpuPassTimingBeginReason = undefined;
|
|
3525
|
+
pointsLinesOwner.dispose();
|
|
3340
3526
|
persistentRenderScene.dispose();
|
|
3527
|
+
instanceCollections.dispose();
|
|
3341
3528
|
gpuDrivenProduction.dispose();
|
|
3342
3529
|
occlusionRuntime?.dispose();
|
|
3343
3530
|
disposeFeatureGpuWork();
|
|
@@ -3407,6 +3594,10 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3407
3594
|
// so destroy failures fire structured errors (unified per-frame +
|
|
3408
3595
|
// dispose error strategy).
|
|
3409
3596
|
disposeInstanceBuffers(frameState.instanceBuffers, internals.errorRegistry);
|
|
3597
|
+
frameState.instanceResidency?.clear();
|
|
3598
|
+
if (frameState.instanceBufferChunks !== undefined) {
|
|
3599
|
+
disposeInstanceBufferChunks(frameState.instanceBufferChunks, internals.errorRegistry);
|
|
3600
|
+
}
|
|
3410
3601
|
disposeTransientInstanceBuffers(frameState.transientInstanceBuffers, internals.errorRegistry);
|
|
3411
3602
|
if (frameState.probeBlendRecordBuffer !== undefined) {
|
|
3412
3603
|
if (!frameState.probeBlendRecordBuffer.isDestroyed) {
|
|
@@ -3436,7 +3627,14 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3436
3627
|
frameState.pointShadowAtlas = null;
|
|
3437
3628
|
}
|
|
3438
3629
|
},
|
|
3439
|
-
resetForRecover(): void {
|
|
3630
|
+
resetForRecover(retiringPipelineState?: PipelineState, replacementDevice?: RhiDevice): void {
|
|
3631
|
+
frameState.lastSuccessfulTemporalView = undefined;
|
|
3632
|
+
frameState.successfulTemporalFrameIndex = 0;
|
|
3633
|
+
frameState.pendingTemporalCommit = { kind: 'none' };
|
|
3634
|
+
// The active expansion buffers belong to the lost generation. Abandon
|
|
3635
|
+
// their handles without invoking destroy on a device that can no longer
|
|
3636
|
+
// service cleanup; the staged candidate owns the replacement buffers.
|
|
3637
|
+
pointsLinesOwner.abandonForDeviceLoss();
|
|
3440
3638
|
ssrFormatReceipt = undefined;
|
|
3441
3639
|
ssrFormatProbeGeneration = -1;
|
|
3442
3640
|
ssrFormatProbe = undefined;
|
|
@@ -3457,18 +3655,28 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3457
3655
|
updateTransmissionInspection(lastTransmissionAntialias, false);
|
|
3458
3656
|
persistentRenderScene.resetGpuForRecover();
|
|
3459
3657
|
gpuDrivenProduction.dispose();
|
|
3460
|
-
|
|
3658
|
+
// The active renderer still points at the lost device until the caller
|
|
3659
|
+
// completes the synchronous publication boundary. Bind the replacement
|
|
3660
|
+
// explicitly so the first published draw never routes through an owner
|
|
3661
|
+
// recreated against the old physical device.
|
|
3662
|
+
gpuDrivenProduction = GpuDrivenProduction.forDevice(
|
|
3663
|
+
replacementDevice ?? internals.device,
|
|
3664
|
+
gpuDrivenShaderFactory,
|
|
3665
|
+
);
|
|
3461
3666
|
occlusionRuntime?.dispose();
|
|
3462
3667
|
// The replacement device is installed by the host after this hook. Do
|
|
3463
3668
|
// not recreate device-bound query resources against the lost device;
|
|
3464
3669
|
// the first post-recovery frame lazily binds the current device.
|
|
3465
3670
|
occlusionRuntime = undefined;
|
|
3466
3671
|
disposeFeatureGpuWork();
|
|
3672
|
+
featureGpuWork = createFeatureGpuWorkOwner(internals);
|
|
3467
3673
|
reflectionProbeOwner.dispose();
|
|
3468
3674
|
// A recovered device cannot retain handles from the lost device. The
|
|
3469
3675
|
// Bloom owner abandons candidates and retires every committed generation
|
|
3470
3676
|
// before the new PipelineState is rebuilt.
|
|
3471
|
-
|
|
3677
|
+
(
|
|
3678
|
+
retiringPipelineState ?? internals.getPipelineState()
|
|
3679
|
+
)?.perPassResources.drainBloomResources?.();
|
|
3472
3680
|
// feat-20260622-s5 M3 / B-2 / w18: recover() rebuild drops device-bound
|
|
3473
3681
|
// state minted by the lost device. The active graph and per-entity caches
|
|
3474
3682
|
// must be discarded, not merely marked for destruction: their opaque
|
|
@@ -3531,6 +3739,8 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3531
3739
|
}
|
|
3532
3740
|
frameState.retiredCompiledFrameGraphs.clear();
|
|
3533
3741
|
frameState.instanceBuffers.clear();
|
|
3742
|
+
frameState.instanceBufferChunks?.clear();
|
|
3743
|
+
frameState.instanceResidency?.clear();
|
|
3534
3744
|
delete frameState.probeBlendRecordBuffer;
|
|
3535
3745
|
frameState.probeBlendRecordBufferCapacity = 0;
|
|
3536
3746
|
frameState.probeBlendBuffers.clear();
|
|
@@ -3554,113 +3764,147 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3554
3764
|
// the lost device, so recovery must invalidate this cache alongside the
|
|
3555
3765
|
// feature-host declarations and UBOs below.
|
|
3556
3766
|
postProcessPipelineCache.clear();
|
|
3557
|
-
// Logical declarations
|
|
3558
|
-
//
|
|
3559
|
-
postProcessParamsBuffers.clear();
|
|
3767
|
+
// Logical declarations and the active params bundle stay live until the
|
|
3768
|
+
// candidate post-process bundle crosses the publication boundary.
|
|
3560
3769
|
resetRenderFeatureGraphState(internals);
|
|
3561
3770
|
},
|
|
3771
|
+
prepareRecoveryPostProcessResources(device: RhiDevice) {
|
|
3772
|
+
return prepareRecoveryPostProcessResources(device);
|
|
3773
|
+
},
|
|
3774
|
+
publishRecoveryPostProcessResources(candidate: RecoveryPostProcessResources): void {
|
|
3775
|
+
publishRecoveryPostProcessResources(candidate);
|
|
3776
|
+
},
|
|
3777
|
+
discardRecoveryPostProcessResources(candidate: RecoveryPostProcessResources): void {
|
|
3778
|
+
discardRecoveryPostProcessResources(candidate);
|
|
3779
|
+
},
|
|
3562
3780
|
restorePostProcessResources(): void {
|
|
3563
|
-
const
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3781
|
+
const prepared = prepareRecoveryPostProcessResources(internals.device);
|
|
3782
|
+
if (!prepared.ok) throw prepared.error;
|
|
3783
|
+
publishRecoveryPostProcessResources(prepared.value);
|
|
3784
|
+
},
|
|
3785
|
+
prepareRecoveryRoots(runtime: RecoveryRootRuntime): RecoveryRootBundle {
|
|
3786
|
+
// Snapshot only CPU projections/declarations here. The root create
|
|
3787
|
+
// callbacks below must never close over a mutable active owner while
|
|
3788
|
+
// LifecycleTransaction is awaiting candidate construction.
|
|
3789
|
+
const candidateEnvironmentLifecycle = environmentLifecycle.createRecoveryCandidate(
|
|
3790
|
+
runtime.scope,
|
|
3791
|
+
);
|
|
3792
|
+
const roots: LifecycleResourceSpec<unknown>[] = [];
|
|
3793
|
+
let candidateEnvironmentGeneration: EnvironmentGeneration | undefined;
|
|
3794
|
+
let environmentRootCleaned = false;
|
|
3795
|
+
let published = false;
|
|
3796
|
+
if (candidateEnvironmentLifecycle.hasRecoveryFrame()) {
|
|
3797
|
+
const environmentRoot = candidateEnvironmentLifecycle.createRecoveryRoot(runtime.scope);
|
|
3798
|
+
roots.push({
|
|
3799
|
+
...environmentRoot,
|
|
3800
|
+
create: async () => {
|
|
3801
|
+
const generation = (await environmentRoot.create()) as EnvironmentGeneration;
|
|
3802
|
+
candidateEnvironmentGeneration = generation;
|
|
3803
|
+
return generation;
|
|
3804
|
+
},
|
|
3805
|
+
cleanup: (value) => {
|
|
3806
|
+
if (environmentRootCleaned) return;
|
|
3807
|
+
environmentRootCleaned = true;
|
|
3808
|
+
if (candidateEnvironmentGeneration === value) {
|
|
3809
|
+
candidateEnvironmentGeneration = undefined;
|
|
3810
|
+
}
|
|
3811
|
+
if (published) {
|
|
3812
|
+
candidateEnvironmentLifecycle.retirePublishedGeneration(
|
|
3813
|
+
value as EnvironmentGeneration,
|
|
3814
|
+
);
|
|
3815
|
+
} else {
|
|
3816
|
+
environmentRoot.cleanup(value);
|
|
3817
|
+
}
|
|
3818
|
+
},
|
|
3819
|
+
});
|
|
3820
|
+
}
|
|
3821
|
+
const graphCandidate = runtime.graphCandidate;
|
|
3822
|
+
if (graphCandidate !== undefined) {
|
|
3823
|
+
const releaseGraphCandidate = (): void => graphCandidate.release();
|
|
3824
|
+
const compiledGraph = graphCandidate.frameState.compiledFrameGraph;
|
|
3825
|
+
if (compiledGraph !== null) {
|
|
3826
|
+
roots.push({
|
|
3827
|
+
kind: 'pipeline',
|
|
3828
|
+
create: () => {
|
|
3829
|
+
if (!runtime.scope.isAlive()) {
|
|
3830
|
+
throw new Error('Recovery graph candidate scope is not active.');
|
|
3831
|
+
}
|
|
3832
|
+
if (graphCandidate.frameState.compiledFrameGraph !== compiledGraph) {
|
|
3833
|
+
throw new Error('Recovery graph candidate was changed before aggregate assembly.');
|
|
3834
|
+
}
|
|
3835
|
+
return compiledGraph;
|
|
3836
|
+
},
|
|
3837
|
+
// All graph/feature/GPU-driven candidate resources share one
|
|
3838
|
+
// owner. The root is a real release seam, not a marker; the
|
|
3839
|
+
// candidate latch makes every root cleanup call harmless after
|
|
3840
|
+
// the first one.
|
|
3841
|
+
cleanup: releaseGraphCandidate,
|
|
3590
3842
|
});
|
|
3591
|
-
if (!created.ok) throw created.error;
|
|
3592
|
-
buffer = created.value;
|
|
3593
3843
|
}
|
|
3594
|
-
if (
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3844
|
+
if (graphCandidate.featureHost !== undefined) {
|
|
3845
|
+
const featureRoot = graphCandidate.featureHost.createRecoveryRoot(runtime.scope);
|
|
3846
|
+
roots.push({ ...featureRoot, cleanup: releaseGraphCandidate });
|
|
3847
|
+
}
|
|
3848
|
+
if (graphCandidate.featureGpuWork !== undefined) {
|
|
3849
|
+
const featureWorkRoot = graphCandidate.featureGpuWork.createRecoveryRoot(runtime.scope);
|
|
3850
|
+
roots.push({ ...featureWorkRoot, cleanup: releaseGraphCandidate });
|
|
3851
|
+
}
|
|
3852
|
+
if (graphCandidate.gpuDrivenProduction !== undefined) {
|
|
3853
|
+
const productionRoot = graphCandidate.gpuDrivenProduction.createRecoveryRoot(
|
|
3854
|
+
runtime.scope,
|
|
3855
|
+
);
|
|
3856
|
+
roots.push({ ...productionRoot, cleanup: releaseGraphCandidate });
|
|
3857
|
+
}
|
|
3858
|
+
if (graphCandidate.gpuDrivenScene?.state !== undefined) {
|
|
3859
|
+
const sceneRoot = graphCandidate.gpuDrivenScene.createRecoveryRoot(runtime.scope);
|
|
3860
|
+
roots.push({ ...sceneRoot, cleanup: releaseGraphCandidate });
|
|
3861
|
+
}
|
|
3862
|
+
if (graphCandidate.pointsLines !== undefined) {
|
|
3863
|
+
const pointsLinesRoot = graphCandidate.pointsLines.createRecoveryRoot(runtime.scope);
|
|
3864
|
+
roots.push({ ...pointsLinesRoot, cleanup: releaseGraphCandidate });
|
|
3865
|
+
}
|
|
3866
|
+
if (runtime.gpuStore.recoveryResourceCount() > 0) {
|
|
3867
|
+
const gpuStoreRoot = runtime.gpuStore.createRecoveryRoot(runtime.scope);
|
|
3868
|
+
roots.push({ ...gpuStoreRoot, cleanup: releaseGraphCandidate });
|
|
3598
3869
|
}
|
|
3599
3870
|
}
|
|
3871
|
+
let discarded = false;
|
|
3872
|
+
return {
|
|
3873
|
+
roots: Object.freeze(roots),
|
|
3874
|
+
publish: () => {
|
|
3875
|
+
if (published || discarded) return;
|
|
3876
|
+
if (
|
|
3877
|
+
candidateEnvironmentGeneration !== undefined &&
|
|
3878
|
+
!candidateEnvironmentGeneration.retired
|
|
3879
|
+
) {
|
|
3880
|
+
environmentLifecycle.retireActiveForReplacement();
|
|
3881
|
+
candidateEnvironmentLifecycle.publish(candidateEnvironmentGeneration);
|
|
3882
|
+
}
|
|
3883
|
+
published = true;
|
|
3884
|
+
environmentLifecycle = candidateEnvironmentLifecycle;
|
|
3885
|
+
Object.assign(frameState, { environmentLifecycle });
|
|
3886
|
+
},
|
|
3887
|
+
discard: () => {
|
|
3888
|
+
if (published || discarded) return;
|
|
3889
|
+
discarded = true;
|
|
3890
|
+
if (candidateEnvironmentGeneration !== undefined) {
|
|
3891
|
+
candidateEnvironmentLifecycle.discard(candidateEnvironmentGeneration);
|
|
3892
|
+
candidateEnvironmentGeneration = undefined;
|
|
3893
|
+
}
|
|
3894
|
+
candidateEnvironmentLifecycle.discardRecoveryCandidates();
|
|
3895
|
+
// The transaction can fail before it reaches any graph root (for
|
|
3896
|
+
// example while the environment root is created). Keep the graph,
|
|
3897
|
+
// feature, GPU-driven, and residency resources on the same real
|
|
3898
|
+
// candidate owner in that case; later scope/factory cleanup is a
|
|
3899
|
+
// harmless repeat through the candidate latch.
|
|
3900
|
+
graphCandidate?.release();
|
|
3901
|
+
runtime.scope.abandon();
|
|
3902
|
+
},
|
|
3903
|
+
};
|
|
3600
3904
|
},
|
|
3601
3905
|
};
|
|
3602
3906
|
}
|
|
3603
3907
|
|
|
3604
|
-
function buildOcclusionProxyVertices(
|
|
3605
|
-
aabb: ArrayLike<number>,
|
|
3606
|
-
world: ArrayLike<number>,
|
|
3607
|
-
camera: import('./render-contract').CameraSnapshot,
|
|
3608
|
-
): Float32Array {
|
|
3609
|
-
const view = computeViewMatrix(camera);
|
|
3610
|
-
const projection = computeProjectionMatrix(camera);
|
|
3611
|
-
const viewProjection = mat4.create();
|
|
3612
|
-
mat4.multiply(viewProjection, projection, view);
|
|
3613
|
-
const corners = [
|
|
3614
|
-
[aabb[0] ?? 0, aabb[1] ?? 0, aabb[2] ?? 0],
|
|
3615
|
-
[aabb[3] ?? 0, aabb[1] ?? 0, aabb[2] ?? 0],
|
|
3616
|
-
[aabb[3] ?? 0, aabb[4] ?? 0, aabb[2] ?? 0],
|
|
3617
|
-
[aabb[0] ?? 0, aabb[4] ?? 0, aabb[2] ?? 0],
|
|
3618
|
-
[aabb[0] ?? 0, aabb[1] ?? 0, aabb[5] ?? 0],
|
|
3619
|
-
[aabb[3] ?? 0, aabb[1] ?? 0, aabb[5] ?? 0],
|
|
3620
|
-
[aabb[3] ?? 0, aabb[4] ?? 0, aabb[5] ?? 0],
|
|
3621
|
-
[aabb[0] ?? 0, aabb[4] ?? 0, aabb[5] ?? 0],
|
|
3622
|
-
];
|
|
3623
|
-
const triangles = [
|
|
3624
|
-
0, 1, 2, 0, 2, 3, 4, 6, 5, 4, 7, 6, 0, 4, 5, 0, 5, 1, 3, 2, 6, 3, 6, 7, 0, 3, 7, 0, 7, 4, 1, 5,
|
|
3625
|
-
6, 1, 6, 2,
|
|
3626
|
-
];
|
|
3627
|
-
const output = new Float32Array(triangles.length * 4);
|
|
3628
|
-
for (let index = 0; index < triangles.length; index += 1) {
|
|
3629
|
-
const corner = corners[triangles[index] ?? 0] ?? [0, 0, 0];
|
|
3630
|
-
const cx = corner[0] ?? 0;
|
|
3631
|
-
const cy = corner[1] ?? 0;
|
|
3632
|
-
const cz = corner[2] ?? 0;
|
|
3633
|
-
const x = (world[0] ?? 0) * cx + (world[4] ?? 0) * cy + (world[8] ?? 0) * cz + (world[12] ?? 0);
|
|
3634
|
-
const y = (world[1] ?? 0) * cx + (world[5] ?? 0) * cy + (world[9] ?? 0) * cz + (world[13] ?? 0);
|
|
3635
|
-
const z =
|
|
3636
|
-
(world[2] ?? 0) * cx + (world[6] ?? 0) * cy + (world[10] ?? 0) * cz + (world[14] ?? 0);
|
|
3637
|
-
const w =
|
|
3638
|
-
(world[3] ?? 0) * cx + (world[7] ?? 0) * cy + (world[11] ?? 0) * cz + (world[15] ?? 1);
|
|
3639
|
-
const base = index * 4;
|
|
3640
|
-
output[base] =
|
|
3641
|
-
(viewProjection[0] ?? 0) * x +
|
|
3642
|
-
(viewProjection[4] ?? 0) * y +
|
|
3643
|
-
(viewProjection[8] ?? 0) * z +
|
|
3644
|
-
(viewProjection[12] ?? 0) * w;
|
|
3645
|
-
output[base + 1] =
|
|
3646
|
-
(viewProjection[1] ?? 0) * x +
|
|
3647
|
-
(viewProjection[5] ?? 0) * y +
|
|
3648
|
-
(viewProjection[9] ?? 0) * z +
|
|
3649
|
-
(viewProjection[13] ?? 0) * w;
|
|
3650
|
-
output[base + 2] =
|
|
3651
|
-
(viewProjection[2] ?? 0) * x +
|
|
3652
|
-
(viewProjection[6] ?? 0) * y +
|
|
3653
|
-
(viewProjection[10] ?? 0) * z +
|
|
3654
|
-
(viewProjection[14] ?? 0) * w;
|
|
3655
|
-
output[base + 3] =
|
|
3656
|
-
(viewProjection[3] ?? 0) * x +
|
|
3657
|
-
(viewProjection[7] ?? 0) * y +
|
|
3658
|
-
(viewProjection[11] ?? 0) * z +
|
|
3659
|
-
(viewProjection[15] ?? 0) * w;
|
|
3660
|
-
}
|
|
3661
|
-
return output;
|
|
3662
|
-
}
|
|
3663
|
-
|
|
3664
3908
|
/** Resolve the build identity used by every inspection submit. */
|
|
3665
3909
|
export function resolveInspectionBuild(internals: Pick<RenderSystemInternals, 'build'>): string {
|
|
3666
3910
|
return internals.build ?? 'render-system';
|