@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/features/host.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EntityHandle } from '@forgeax/engine-ecs';
|
|
2
2
|
import type { RhiCaps, TextureFormat } from '@forgeax/engine-rhi';
|
|
3
3
|
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
4
|
+
import type { DeviceScope, LifecycleResourceSpec } from '../device/device-scope';
|
|
4
5
|
import {
|
|
5
6
|
type RenderError,
|
|
6
7
|
RenderFeatureCapabilityMissingError,
|
|
@@ -175,6 +176,7 @@ export interface RenderFeatureHost {
|
|
|
175
176
|
batches: readonly RenderFeaturePreparedResourceBatch[],
|
|
176
177
|
): Result<void, RenderError>;
|
|
177
178
|
recover(input: RenderFeatureRecoverInput): Result<void, RenderError>;
|
|
179
|
+
createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown>;
|
|
178
180
|
diagnostics(): readonly RenderFeatureDiagnostics[];
|
|
179
181
|
/** Subscribe to lifecycle projection changes; unchanged active frames are silent. */
|
|
180
182
|
subscribeDiagnostics(listener: () => void): () => void;
|
|
@@ -915,6 +917,20 @@ class FeatureHostImpl implements RenderFeatureHost {
|
|
|
915
917
|
return this.slots.map((slot) => slot.feature);
|
|
916
918
|
}
|
|
917
919
|
|
|
920
|
+
createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown> {
|
|
921
|
+
return {
|
|
922
|
+
kind: 'feature',
|
|
923
|
+
create: () => {
|
|
924
|
+
if (!scope.isAlive()) throw new Error('Feature candidate scope is not active.');
|
|
925
|
+
// The root carries the detached host owner itself. A scalar marker is
|
|
926
|
+
// not evidence that feature planning/preparation actually ran; the
|
|
927
|
+
// candidate graph and host own the prepared resources.
|
|
928
|
+
return this;
|
|
929
|
+
},
|
|
930
|
+
cleanup: () => undefined,
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
|
|
918
934
|
private publishDiagnosticsChanged(): void {
|
|
919
935
|
for (const listener of this.diagnosticsListeners) {
|
|
920
936
|
try {
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
ShaderModule,
|
|
12
12
|
} from '@forgeax/engine-rhi';
|
|
13
13
|
import { type BindGroupLayoutDescriptor, err, ok, type Result } from '@forgeax/engine-types';
|
|
14
|
+
import type { DeviceScope, LifecycleResourceSpec } from '../device/device-scope';
|
|
14
15
|
import { type RenderError, RenderFeaturePreparationFailedError } from '../errors/render';
|
|
15
16
|
import type { PipelineBuilderShaderModuleFactory } from '../pipeline-builder';
|
|
16
17
|
import type { RenderFeatureShaderModuleMode } from './types';
|
|
@@ -910,6 +911,8 @@ function createRenderFeatureGpuWorkSession(input: {
|
|
|
910
911
|
* retains the per-feature prepared GPU state and recreates it after recovery.
|
|
911
912
|
*/
|
|
912
913
|
export interface RenderFeatureGpuWorkOwner {
|
|
914
|
+
/** Candidate root carries this detached GPU-work owner itself. */
|
|
915
|
+
createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown>;
|
|
913
916
|
beginFeature(
|
|
914
917
|
featureIdentity: string,
|
|
915
918
|
generation: number,
|
|
@@ -935,9 +938,21 @@ export function createRenderFeatureGpuWorkOwner(input: {
|
|
|
935
938
|
session: RenderFeatureGpuPrepareSession;
|
|
936
939
|
}
|
|
937
940
|
>();
|
|
938
|
-
|
|
939
|
-
|
|
941
|
+
let disposed = false;
|
|
942
|
+
|
|
943
|
+
const owner: RenderFeatureGpuWorkOwner = {
|
|
944
|
+
createRecoveryRoot: (scope) => ({
|
|
945
|
+
kind: 'feature',
|
|
946
|
+
create: () => {
|
|
947
|
+
if (!scope.isAlive()) throw new Error('Feature GPU candidate scope is not active.');
|
|
948
|
+
return owner;
|
|
949
|
+
},
|
|
950
|
+
cleanup: () => undefined,
|
|
951
|
+
}),
|
|
940
952
|
beginFeature: (featureIdentity, generation, requestedShaderModuleMode = 'validated') => {
|
|
953
|
+
if (disposed) {
|
|
954
|
+
throw new Error('Render feature GPU work owner is disposed.');
|
|
955
|
+
}
|
|
941
956
|
const shaderModuleMode = requestedShaderModuleMode;
|
|
942
957
|
const existing = sessions.get(featureIdentity);
|
|
943
958
|
if (existing?.generation === generation && existing.shaderModuleMode === shaderModuleMode) {
|
|
@@ -962,6 +977,8 @@ export function createRenderFeatureGpuWorkOwner(input: {
|
|
|
962
977
|
resolveBuffer: (featureIdentity, reference) =>
|
|
963
978
|
sessions.get(featureIdentity)?.session.resolveBuffer(reference),
|
|
964
979
|
dispose: () => {
|
|
980
|
+
if (disposed) return ok(undefined);
|
|
981
|
+
disposed = true;
|
|
965
982
|
let first: RenderError | undefined;
|
|
966
983
|
for (const { session } of sessions.values()) {
|
|
967
984
|
const disposed = session.dispose();
|
|
@@ -971,4 +988,5 @@ export function createRenderFeatureGpuWorkOwner(input: {
|
|
|
971
988
|
return first === undefined ? ok(undefined) : err(first);
|
|
972
989
|
},
|
|
973
990
|
};
|
|
991
|
+
return owner;
|
|
974
992
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TextureFormat } from '@forgeax/engine-rhi';
|
|
2
2
|
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
3
|
+
import type { DeviceScope, LifecycleResourceSpec } from '../device/device-scope';
|
|
3
4
|
import { type RenderError, RenderFeaturePreparationFailedError } from '../errors/render';
|
|
4
5
|
import type {
|
|
5
6
|
PreparedKind,
|
|
@@ -81,6 +82,7 @@ export interface PreparedGraphicsStore {
|
|
|
81
82
|
beginFrame(featureIdentity: string, generation: number): PreparedGraphicsTransaction;
|
|
82
83
|
snapshot(featureIdentity: string): PreparedGraphicsStoreSnapshot;
|
|
83
84
|
invalidate(featureIdentity: string, generation: number): void;
|
|
85
|
+
createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown>;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
interface Slot {
|
|
@@ -418,6 +420,21 @@ class PreparedGraphicsStoreImpl implements PreparedGraphicsStore {
|
|
|
418
420
|
}
|
|
419
421
|
}
|
|
420
422
|
|
|
423
|
+
createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown> {
|
|
424
|
+
return {
|
|
425
|
+
kind: 'buffer',
|
|
426
|
+
create: () => {
|
|
427
|
+
if (!scope.isAlive()) throw new Error('Prepared graphics candidate scope is not active.');
|
|
428
|
+
const itemCount = [...this.slots.values()].reduce(
|
|
429
|
+
(count, slot) => count + slot.items.size,
|
|
430
|
+
0,
|
|
431
|
+
);
|
|
432
|
+
return Object.freeze({ generation: scope.generation, itemCount });
|
|
433
|
+
},
|
|
434
|
+
cleanup: () => undefined,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
421
438
|
registerReference(reference: object, item: PreparedGraphicsItem): void {
|
|
422
439
|
this.references.set(reference, item);
|
|
423
440
|
}
|
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
import { err, ok, RhiError } from '@forgeax/engine-rhi';
|
|
21
21
|
import type { MaterialShaderArtifact } from '@forgeax/engine-shader';
|
|
22
22
|
import type { MaterialRenderState } from '@forgeax/engine-types';
|
|
23
|
+
import type { DeviceScope, LifecycleResourceSpec } from '../device/device-scope';
|
|
23
24
|
import type { MeshGpuHandles } from '../device/gpu-residency';
|
|
24
25
|
import { GPU_SCENE_WGSL } from '../gpu-scene-schema';
|
|
25
26
|
import {
|
|
@@ -48,7 +49,12 @@ import {
|
|
|
48
49
|
projectedHeightForCandidate,
|
|
49
50
|
type SubmissionPlan,
|
|
50
51
|
} from './batch-topology';
|
|
51
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
GPU_DRIVEN_VIEW_WGSL,
|
|
54
|
+
type GpuDrivenLodSelectionInspection,
|
|
55
|
+
GpuDrivenView,
|
|
56
|
+
selectGpuLodLane,
|
|
57
|
+
} from './view-gpu';
|
|
52
58
|
|
|
53
59
|
const VERTEX_STAGE = 0x1;
|
|
54
60
|
const FRAGMENT_STAGE = 0x2;
|
|
@@ -261,8 +267,8 @@ function activeFrustum(camera: CameraSnapshot): Float32Array {
|
|
|
261
267
|
projection,
|
|
262
268
|
camera.orthoLeft,
|
|
263
269
|
camera.orthoRight,
|
|
264
|
-
camera.orthoBottom,
|
|
265
270
|
camera.orthoTop,
|
|
271
|
+
camera.orthoBottom,
|
|
266
272
|
camera.near,
|
|
267
273
|
camera.far,
|
|
268
274
|
);
|
|
@@ -297,6 +303,9 @@ function productionEligible(snapshot: RenderableSnapshot, batch: GpuDrivenBatch)
|
|
|
297
303
|
const shader = material.materialShaderId ?? 'forgeax::default-unlit';
|
|
298
304
|
return (
|
|
299
305
|
shader === 'forgeax::default-unlit' &&
|
|
306
|
+
// The production rigid shader owns triangle-list raster only. Lines and
|
|
307
|
+
// points stay with their CPU/specialized topology owners.
|
|
308
|
+
batch.key.topology === 'triangle-list' &&
|
|
300
309
|
batch.key.materialResourceClass === EMPTY_RESOURCE_CLASS &&
|
|
301
310
|
material.baseColorTexture === undefined &&
|
|
302
311
|
material.transparent !== true
|
|
@@ -928,7 +937,62 @@ export interface PreparedGpuDrivenFrame {
|
|
|
928
937
|
): Result<RenderPipelineGpuDrivenProjection, RenderGraphError | RhiError>;
|
|
929
938
|
}
|
|
930
939
|
|
|
940
|
+
export interface RecoveryCapabilityFacts {
|
|
941
|
+
readonly generation: number;
|
|
942
|
+
readonly shaderCompilation: boolean;
|
|
943
|
+
readonly compute: boolean;
|
|
944
|
+
readonly storageBuffer: boolean;
|
|
945
|
+
readonly indirectDrawing: boolean;
|
|
946
|
+
readonly multisample: boolean;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
export type RecoveryCapabilityOutcome =
|
|
950
|
+
| {
|
|
951
|
+
readonly status: 'ready' | 'fallback';
|
|
952
|
+
readonly generation: number;
|
|
953
|
+
readonly disabled: readonly string[];
|
|
954
|
+
}
|
|
955
|
+
| {
|
|
956
|
+
readonly status: 'disabled';
|
|
957
|
+
readonly generation: number;
|
|
958
|
+
readonly disabled: readonly string[];
|
|
959
|
+
readonly allocations: 0;
|
|
960
|
+
}
|
|
961
|
+
| {
|
|
962
|
+
readonly status: 'refused';
|
|
963
|
+
readonly generation: number;
|
|
964
|
+
readonly failedOwner: 'shader-material-pipeline';
|
|
965
|
+
readonly resourceKind: 'pipeline';
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
export function classifyRecoveryCapability(
|
|
969
|
+
facts: RecoveryCapabilityFacts,
|
|
970
|
+
): RecoveryCapabilityOutcome {
|
|
971
|
+
if (!facts.shaderCompilation) {
|
|
972
|
+
return {
|
|
973
|
+
status: 'refused',
|
|
974
|
+
generation: facts.generation,
|
|
975
|
+
failedOwner: 'shader-material-pipeline',
|
|
976
|
+
resourceKind: 'pipeline',
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
if (!facts.compute || !facts.storageBuffer || !facts.indirectDrawing) {
|
|
980
|
+
return {
|
|
981
|
+
status: 'disabled',
|
|
982
|
+
generation: facts.generation,
|
|
983
|
+
disabled: ['gpu-driven'],
|
|
984
|
+
allocations: 0,
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
return {
|
|
988
|
+
status: facts.multisample ? 'ready' : 'fallback',
|
|
989
|
+
generation: facts.generation,
|
|
990
|
+
disabled: [],
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
|
|
931
994
|
export class GpuDrivenProduction {
|
|
995
|
+
private disposed = false;
|
|
932
996
|
private view: GpuDrivenView | undefined;
|
|
933
997
|
private raster: GpuDrivenRigidRaster | undefined;
|
|
934
998
|
private standardRaster: StandardPbrRasterAdapter | undefined;
|
|
@@ -972,6 +1036,55 @@ export class GpuDrivenProduction {
|
|
|
972
1036
|
private readonly shaderModuleFactory: PipelineBuilderShaderModuleFactory,
|
|
973
1037
|
) {}
|
|
974
1038
|
|
|
1039
|
+
static forDevice(
|
|
1040
|
+
device: RhiDevice,
|
|
1041
|
+
shaderModuleFactory: PipelineBuilderShaderModuleFactory,
|
|
1042
|
+
): GpuDrivenProduction {
|
|
1043
|
+
return new GpuDrivenProduction(device, shaderModuleFactory);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/** Recovery has no frame retry: await generated programs before preparing the graph. */
|
|
1047
|
+
static async forRecoveryDevice(
|
|
1048
|
+
device: RhiDevice,
|
|
1049
|
+
factory: PipelineBuilderShaderModuleFactory,
|
|
1050
|
+
createShaderModule: (descriptor: {
|
|
1051
|
+
code: string;
|
|
1052
|
+
label?: string;
|
|
1053
|
+
}) => Promise<Result<ShaderModule, RhiError>>,
|
|
1054
|
+
): Promise<Result<GpuDrivenProduction, RhiError>> {
|
|
1055
|
+
const modules = new Map<string, ShaderModule>();
|
|
1056
|
+
for (const descriptor of [
|
|
1057
|
+
{ label: 'gpu-driven-view', code: GPU_DRIVEN_VIEW_WGSL },
|
|
1058
|
+
{ label: 'gpu-driven-rigid-unlit', code: GPU_DRIVEN_RIGID_UNLIT_WGSL },
|
|
1059
|
+
]) {
|
|
1060
|
+
const module = await createShaderModule(descriptor);
|
|
1061
|
+
if (!module.ok) return module;
|
|
1062
|
+
modules.set(descriptor.code, module.value);
|
|
1063
|
+
}
|
|
1064
|
+
return ok(
|
|
1065
|
+
new GpuDrivenProduction(device, {
|
|
1066
|
+
createShaderModule: (descriptor) => {
|
|
1067
|
+
const module = modules.get(descriptor.code);
|
|
1068
|
+
return module === undefined ? factory.createShaderModule(descriptor) : ok(module);
|
|
1069
|
+
},
|
|
1070
|
+
}),
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown> {
|
|
1075
|
+
return {
|
|
1076
|
+
kind: 'scene-table',
|
|
1077
|
+
create: () => {
|
|
1078
|
+
if (!scope.isAlive()) throw new Error('GPU-driven candidate scope is not active.');
|
|
1079
|
+
// Keep the actual detached production owner in the generation
|
|
1080
|
+
// aggregate. The recovery transaction, not a marker object, is the
|
|
1081
|
+
// owner that is published or discarded exactly once.
|
|
1082
|
+
return this;
|
|
1083
|
+
},
|
|
1084
|
+
cleanup: () => undefined,
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
|
|
975
1088
|
prepare(input: {
|
|
976
1089
|
readonly scene: PersistentGpuDrivenState | undefined;
|
|
977
1090
|
readonly camera: CameraSnapshot;
|
|
@@ -1000,7 +1113,20 @@ export class GpuDrivenProduction {
|
|
|
1000
1113
|
this.telemetryGeometryWork = 0;
|
|
1001
1114
|
this.telemetryRootGeometryWork = 0;
|
|
1002
1115
|
this.indirectDrawCount = 0;
|
|
1003
|
-
|
|
1116
|
+
const capability = classifyRecoveryCapability({
|
|
1117
|
+
generation: 0,
|
|
1118
|
+
shaderCompilation: true,
|
|
1119
|
+
compute: this.device.caps.compute,
|
|
1120
|
+
storageBuffer: this.device.caps.storageBuffer,
|
|
1121
|
+
indirectDrawing: this.device.caps.indirectDrawing,
|
|
1122
|
+
multisample: true,
|
|
1123
|
+
});
|
|
1124
|
+
if (
|
|
1125
|
+
input.hdrp ||
|
|
1126
|
+
input.scene === undefined ||
|
|
1127
|
+
capability.status === 'disabled' ||
|
|
1128
|
+
selectGpuLodLane(this.device.caps) === 'cpu'
|
|
1129
|
+
) {
|
|
1004
1130
|
this.gpuOwnedEntityCount = 0;
|
|
1005
1131
|
this.sceneRowsRequired = false;
|
|
1006
1132
|
return ok(undefined);
|
|
@@ -1457,6 +1583,8 @@ export class GpuDrivenProduction {
|
|
|
1457
1583
|
}
|
|
1458
1584
|
|
|
1459
1585
|
dispose(): void {
|
|
1586
|
+
if (this.disposed) return;
|
|
1587
|
+
this.disposed = true;
|
|
1460
1588
|
this.view?.dispose();
|
|
1461
1589
|
this.view = undefined;
|
|
1462
1590
|
this.raster = undefined;
|
package/src/gpu-scene.ts
CHANGED
|
@@ -201,6 +201,7 @@ function coalesceSlots(
|
|
|
201
201
|
|
|
202
202
|
/** Persistent GPU tables derived only from renderer projection slots. */
|
|
203
203
|
export class GpuScene {
|
|
204
|
+
private disposed = false;
|
|
204
205
|
private tableBytes: Record<GpuSceneTableName, ArrayBuffer>;
|
|
205
206
|
private transformBytes: Uint8Array;
|
|
206
207
|
private transformView: DataView;
|
|
@@ -224,6 +225,10 @@ export class GpuScene {
|
|
|
224
225
|
drawTemplate: [],
|
|
225
226
|
material: [],
|
|
226
227
|
};
|
|
228
|
+
/** Transform rows whose previous value advances only after submit. */
|
|
229
|
+
private readonly pendingTemporalTransforms = new Set<number>();
|
|
230
|
+
/** Previous rows whose post-submit upload must be retried before the next draw. */
|
|
231
|
+
private readonly pendingTemporalUploads = new Set<number>();
|
|
227
232
|
|
|
228
233
|
private constructor(
|
|
229
234
|
private readonly device: RhiDevice,
|
|
@@ -272,31 +277,47 @@ export class GpuScene {
|
|
|
272
277
|
}
|
|
273
278
|
|
|
274
279
|
sync(delta: RenderSceneApplyResult): Result<GpuSceneSyncResult, RhiError> {
|
|
280
|
+
for (const transformIndex of this.pendingTemporalUploads) {
|
|
281
|
+
this.writesByTable.transform.push(transformIndex);
|
|
282
|
+
}
|
|
283
|
+
const pendingWrites = TABLE_NAMES.some((name) => this.writesByTable[name].length > 0);
|
|
275
284
|
if (
|
|
276
285
|
delta.createdSlots.length === 0 &&
|
|
277
286
|
delta.updatedSlots.length === 0 &&
|
|
278
287
|
delta.recreatedSlots.length === 0 &&
|
|
279
|
-
delta.removedSlots.length === 0
|
|
288
|
+
delta.removedSlots.length === 0 &&
|
|
289
|
+
!pendingWrites
|
|
280
290
|
) {
|
|
281
291
|
this.noChangeFrames += 1;
|
|
282
292
|
return ok({ ranges: 0, bytes: 0, grew: false, cleared: 0 });
|
|
283
293
|
}
|
|
284
294
|
const writesByTable = this.writesByTable;
|
|
285
|
-
|
|
286
|
-
|
|
295
|
+
if (!this.identityUploaded && !writesByTable.transform.includes(0)) {
|
|
296
|
+
writesByTable.transform.push(0);
|
|
297
|
+
}
|
|
287
298
|
let highestChangedSlot = -1;
|
|
288
|
-
|
|
299
|
+
const contentUpdatedSlots = delta.contentUpdatedSlots ?? [];
|
|
300
|
+
const contentUpdatedSlotIds = new Set(contentUpdatedSlots.map((record) => record.slot));
|
|
301
|
+
const allocationResets = new Set<number>();
|
|
302
|
+
// RenderScene may recycle a released CPU slot for a different identity in
|
|
303
|
+
// the same delta. Clear the old GPU allocation before ensuring the new
|
|
304
|
+
// record; otherwise the later removal pass clears the newly allocated
|
|
305
|
+
// record and the next transform-only update sees no allocation.
|
|
306
|
+
for (const record of delta.removedSlots) {
|
|
307
|
+
if (record.slot > highestChangedSlot) highestChangedSlot = record.slot;
|
|
308
|
+
this.clearSlot(record.slot, writesByTable);
|
|
309
|
+
}
|
|
310
|
+
for (const records of [delta.createdSlots, contentUpdatedSlots, delta.recreatedSlots]) {
|
|
289
311
|
for (const record of records) {
|
|
312
|
+
const previous = this.allocations[record.slot];
|
|
290
313
|
this.ensureAllocation(record, writesByTable);
|
|
314
|
+
if (previous !== this.allocations[record.slot]) allocationResets.add(record.slot);
|
|
291
315
|
if (record.slot > highestChangedSlot) highestChangedSlot = record.slot;
|
|
292
316
|
}
|
|
293
317
|
}
|
|
294
318
|
for (const record of delta.updatedSlots) {
|
|
295
319
|
if (record.slot > highestChangedSlot) highestChangedSlot = record.slot;
|
|
296
320
|
}
|
|
297
|
-
for (const record of delta.removedSlots) {
|
|
298
|
-
if (record.slot > highestChangedSlot) highestChangedSlot = record.slot;
|
|
299
|
-
}
|
|
300
321
|
const requiredCapacity = Math.max(
|
|
301
322
|
highestChangedSlot + 1,
|
|
302
323
|
this.instances.requiredCapacity(),
|
|
@@ -311,11 +332,18 @@ export class GpuScene {
|
|
|
311
332
|
}
|
|
312
333
|
|
|
313
334
|
for (const record of delta.createdSlots) this.writeSlot(record, true, writesByTable);
|
|
314
|
-
for (const record of
|
|
335
|
+
for (const record of contentUpdatedSlots) {
|
|
336
|
+
this.writeSlot(record, allocationResets.has(record.slot), writesByTable);
|
|
337
|
+
}
|
|
338
|
+
for (const record of delta.updatedSlots) {
|
|
339
|
+
if (contentUpdatedSlotIds.has(record.slot)) continue;
|
|
340
|
+
this.writeRootTransform(record, false, writesByTable);
|
|
341
|
+
}
|
|
315
342
|
for (const record of delta.recreatedSlots) this.writeSlot(record, true, writesByTable);
|
|
316
|
-
for (const record of delta.removedSlots) this.clearSlot(record.slot, writesByTable);
|
|
317
343
|
const uploaded = this.uploadRows(writesByTable);
|
|
318
344
|
if (!uploaded.ok) return uploaded;
|
|
345
|
+
this.pendingTemporalUploads.clear();
|
|
346
|
+
for (const name of TABLE_NAMES) writesByTable[name].length = 0;
|
|
319
347
|
this.identityUploaded = true;
|
|
320
348
|
this.clearedSlots += delta.removedSlots.length;
|
|
321
349
|
return ok({
|
|
@@ -328,27 +356,47 @@ export class GpuScene {
|
|
|
328
356
|
|
|
329
357
|
/** Publish current transforms as previous only after a successful submit. */
|
|
330
358
|
commitTemporalFrame(enabled = true): Result<void, RhiError> {
|
|
331
|
-
if (!enabled)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
359
|
+
if (!enabled) {
|
|
360
|
+
this.pendingTemporalTransforms.clear();
|
|
361
|
+
this.pendingTemporalUploads.clear();
|
|
362
|
+
return ok(undefined);
|
|
363
|
+
}
|
|
364
|
+
if (this.pendingTemporalTransforms.size === 0) return ok(undefined);
|
|
335
365
|
const currentOffset = offset(TRANSFORM, 'currentWorld');
|
|
336
366
|
const previousOffset = offset(TRANSFORM, 'previousWorld');
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
367
|
+
const ranges = coalesceSlots([...this.pendingTemporalTransforms]);
|
|
368
|
+
for (const range of ranges) {
|
|
369
|
+
for (let index = range.start; index < range.end; index += 1) {
|
|
370
|
+
this.pendingTemporalUploads.add(index);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
for (const range of ranges) {
|
|
374
|
+
for (let index = range.start; index < range.end; index += 1) {
|
|
375
|
+
this.transformBytes.copyWithin(
|
|
376
|
+
index * TRANSFORM.stride + previousOffset,
|
|
340
377
|
index * TRANSFORM.stride + currentOffset,
|
|
341
378
|
index * TRANSFORM.stride + currentOffset + 64,
|
|
342
|
-
)
|
|
343
|
-
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
for (const range of ranges) {
|
|
383
|
+
const firstOffset = range.start * TRANSFORM.stride;
|
|
384
|
+
const lastOffset = range.end * TRANSFORM.stride;
|
|
385
|
+
const uploaded = this.device.queue.writeBuffer(
|
|
386
|
+
this.buffers.transform,
|
|
387
|
+
firstOffset,
|
|
388
|
+
this.transformBytes.subarray(firstOffset, lastOffset),
|
|
344
389
|
);
|
|
390
|
+
if (!uploaded.ok) return uploaded;
|
|
345
391
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
392
|
+
this.pendingTemporalTransforms.clear();
|
|
393
|
+
this.pendingTemporalUploads.clear();
|
|
394
|
+
const bytes = ranges.reduce(
|
|
395
|
+
(total, range) => total + (range.end - range.start) * TRANSFORM.stride,
|
|
396
|
+
0,
|
|
397
|
+
);
|
|
398
|
+
this.uploadRanges += ranges.length;
|
|
399
|
+
this.uploadBytes += bytes;
|
|
352
400
|
return ok(undefined);
|
|
353
401
|
}
|
|
354
402
|
|
|
@@ -358,6 +406,9 @@ export class GpuScene {
|
|
|
358
406
|
this.transforms.reset(1);
|
|
359
407
|
this.draws.reset();
|
|
360
408
|
this.materials.reset();
|
|
409
|
+
this.pendingTemporalTransforms.clear();
|
|
410
|
+
this.pendingTemporalUploads.clear();
|
|
411
|
+
for (const name of TABLE_NAMES) this.writesByTable[name].length = 0;
|
|
361
412
|
const writesByTable: Record<GpuSceneTableName, number[]> = {
|
|
362
413
|
primitive: [],
|
|
363
414
|
instance: [],
|
|
@@ -432,6 +483,8 @@ export class GpuScene {
|
|
|
432
483
|
}
|
|
433
484
|
|
|
434
485
|
dispose(): void {
|
|
486
|
+
if (this.disposed) return;
|
|
487
|
+
this.disposed = true;
|
|
435
488
|
for (const buffer of Object.values(this.buffers)) this.device.destroyBuffer(buffer);
|
|
436
489
|
}
|
|
437
490
|
|
|
@@ -517,7 +570,8 @@ export class GpuScene {
|
|
|
517
570
|
const localCurrentOffset = localOffset + offset(TRANSFORM, 'currentWorld');
|
|
518
571
|
const localPreviousOffset = localOffset + offset(TRANSFORM, 'previousWorld');
|
|
519
572
|
writeMat4(transform, localCurrentOffset, localWorld);
|
|
520
|
-
writeMat4(transform, localPreviousOffset, localWorld);
|
|
573
|
+
if (resetPrevious) writeMat4(transform, localPreviousOffset, localWorld);
|
|
574
|
+
else this.pendingTemporalTransforms.add(transformIndex);
|
|
521
575
|
writes.transform.push(transformIndex);
|
|
522
576
|
}
|
|
523
577
|
writes.instance.push(instanceIndex);
|
|
@@ -583,8 +637,6 @@ export class GpuScene {
|
|
|
583
637
|
writeVec4(material, materialOffset + offset(MATERIAL, 'params1'), [
|
|
584
638
|
snapshot.metallic ?? 0,
|
|
585
639
|
snapshot.roughness ?? 1,
|
|
586
|
-
snapshot.clearcoat ?? 0,
|
|
587
|
-
snapshot.clearcoatRoughness ?? 0,
|
|
588
640
|
]);
|
|
589
641
|
writeVec4(material, materialOffset + offset(MATERIAL, 'params2'), [
|
|
590
642
|
snapshot.emissive?.[0] ?? 0,
|
|
@@ -630,14 +682,16 @@ export class GpuScene {
|
|
|
630
682
|
const allocation = this.allocations[record.slot];
|
|
631
683
|
if (allocation === undefined) throw new RangeError('GPU Scene allocation unavailable');
|
|
632
684
|
const transformOffset = allocation.transformStart * TRANSFORM.stride;
|
|
633
|
-
const currentOffset = transformOffset + offset(TRANSFORM, 'currentWorld');
|
|
634
685
|
const previousOffset = transformOffset + offset(TRANSFORM, 'previousWorld');
|
|
635
686
|
if (resetPrevious) {
|
|
636
687
|
writeMat4(this.transformView, previousOffset, record.snapshot.transform.world);
|
|
637
|
-
} else {
|
|
638
|
-
this.transformBytes.copyWithin(previousOffset, currentOffset, currentOffset + 64);
|
|
639
688
|
}
|
|
640
|
-
writeMat4(
|
|
689
|
+
writeMat4(
|
|
690
|
+
this.transformView,
|
|
691
|
+
transformOffset + offset(TRANSFORM, 'currentWorld'),
|
|
692
|
+
record.snapshot.transform.world,
|
|
693
|
+
);
|
|
694
|
+
if (!resetPrevious) this.pendingTemporalTransforms.add(allocation.transformStart);
|
|
641
695
|
writes.transform.push(allocation.transformStart);
|
|
642
696
|
}
|
|
643
697
|
|
|
@@ -742,6 +796,8 @@ export class GpuScene {
|
|
|
742
796
|
TRANSFORM.stride,
|
|
743
797
|
).fill(0);
|
|
744
798
|
writes.transform.push(transformIndex);
|
|
799
|
+
this.pendingTemporalTransforms.delete(transformIndex);
|
|
800
|
+
this.pendingTemporalUploads.delete(transformIndex);
|
|
745
801
|
}
|
|
746
802
|
for (let ordinal = 0; ordinal < allocation.drawCount; ordinal += 1) {
|
|
747
803
|
const drawIndex = allocation.drawStart + ordinal;
|