@forgeax/engine-render 0.1.29 → 0.1.31
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 +248 -21
- package/dist/assembly/atmosphere-shader-prewarm.d.ts +1 -1
- package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -1
- package/dist/assembly/device-feature-admission.d.ts +4 -3
- package/dist/assembly/device-feature-admission.d.ts.map +1 -1
- package/dist/assembly/dynamic-geometry-host.d.ts +6 -0
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +6 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +26 -3
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/pipeline-helpers.d.ts +24 -0
- package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -0
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
- package/dist/assembly/recovery/recovery-attempt.d.ts +1 -6
- package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts +1 -0
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready.d.ts +1 -1
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/capture/scene-projection.d.ts +41 -0
- package/dist/capture/scene-projection.d.ts.map +1 -0
- package/dist/{chunk-BQVQOQJH.mjs → chunk-7MQ3XBSP.mjs} +20 -14
- package/dist/chunk-7MQ3XBSP.mjs.map +1 -0
- package/dist/{chunk-S4DNKQYQ.mjs → chunk-C3DDSXYK.mjs} +86 -33
- package/dist/chunk-C3DDSXYK.mjs.map +1 -0
- package/dist/{chunk-BTWH7BP4.mjs → chunk-FUUO4UND.mjs} +73 -29
- package/dist/chunk-FUUO4UND.mjs.map +1 -0
- package/dist/{chunk-NRNXBLIJ.mjs → chunk-JRBDO2KB.mjs} +46 -6
- package/dist/chunk-JRBDO2KB.mjs.map +1 -0
- package/dist/{chunk-NICXSHAT.mjs → chunk-MDQJPY3T.mjs} +8 -6
- package/dist/chunk-MDQJPY3T.mjs.map +1 -0
- package/dist/{chunk-JQTSEI3Z.mjs → chunk-NO4WZCAH.mjs} +3 -3
- package/dist/chunk-NO4WZCAH.mjs.map +1 -0
- package/dist/{chunk-FUODTTIA.mjs → chunk-PSEQ6QPH.mjs} +621 -108
- package/dist/chunk-PSEQ6QPH.mjs.map +1 -0
- package/dist/{chunk-33XZFTQU.mjs → chunk-S5UE2T5P.mjs} +48 -6
- package/dist/chunk-S5UE2T5P.mjs.map +1 -0
- package/dist/components/directional-light.d.ts +2 -2
- package/dist/components/dynamic-resolution.d.ts +15 -0
- package/dist/components/dynamic-resolution.d.ts.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/reflection-probe.d.ts +1 -0
- package/dist/components/reflection-probe.d.ts.map +1 -1
- package/dist/components/skylight.d.ts +4 -1
- package/dist/components/skylight.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +2172 -796
- 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 +2 -2
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/dynamic-geometry.d.ts.map +1 -1
- package/dist/environment/background.d.ts +3 -2
- package/dist/environment/background.d.ts.map +1 -1
- package/dist/environment/ibl.d.ts +11 -0
- package/dist/environment/ibl.d.ts.map +1 -0
- package/dist/environment/storage.d.ts +14 -0
- package/dist/environment/storage.d.ts.map +1 -0
- package/dist/errors/gpu-driven.d.ts +2 -2
- package/dist/errors/gpu-driven.d.ts.map +1 -1
- package/dist/errors/render.d.ts +35 -1
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +7 -0
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +18 -1
- package/dist/extract/gpu-driven.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts +24 -0
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -0
- package/dist/gpu-driven/material-bindings.d.ts +7 -0
- package/dist/gpu-driven/material-bindings.d.ts.map +1 -0
- package/dist/gpu-driven/prepared-draw.d.ts +2 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +27 -6
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-resource.d.ts +5 -2
- package/dist/gpu-resource.d.ts.map +1 -1
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +3 -0
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +2 -0
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +3 -1
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +83 -81
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +7 -5
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +16 -1
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/internal.mjs +6 -6
- package/dist/material-row.d.ts +10 -0
- package/dist/material-row.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +3 -1
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/render-extent.d.ts +51 -0
- package/dist/pipeline/render-extent.d.ts.map +1 -0
- package/dist/pipeline/standard-build-context.d.ts +7 -0
- package/dist/pipeline/standard-build-context.d.ts.map +1 -0
- package/dist/pipeline/standard-forward-lane.d.ts +3 -2
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts +1 -1
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline/standard-profile.d.ts +2 -1
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +3 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/post-process-errors.d.ts +9 -2
- package/dist/post-process-errors.d.ts.map +1 -1
- package/dist/prepare/extended-lighting/resources.d.ts +3 -3
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +3 -8
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame.d.ts +3 -2
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +7 -2
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +6 -3
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +2 -0
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/prepared-material-bindings.d.ts.map +1 -1
- package/dist/record/recovery-pipeline.d.ts +9 -1
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +34 -3
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +2 -2
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +5 -2
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/reflection/background.d.ts +14 -0
- package/dist/reflection/background.d.ts.map +1 -0
- package/dist/reflection/filter.d.ts.map +1 -1
- package/dist/reflection/projection.d.ts +1 -0
- package/dist/reflection/projection.d.ts.map +1 -1
- package/dist/reflection/record-owner.d.ts +4 -2
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +47 -32
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +17 -1
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +9 -2
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +29 -2
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +2 -0
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/ssao-config.d.ts +8 -6
- package/dist/ssao-config.d.ts.map +1 -1
- package/dist/ssr/graph.d.ts.map +1 -1
- package/dist/temporal/gpu.d.ts.map +1 -1
- package/dist/temporal/index.mjs +5 -5
- package/dist/temporal/standard-scene-data.d.ts +2 -1
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/target-coverage-attachment.d.ts +15 -0
- package/dist/temporal/target-coverage-attachment.d.ts.map +1 -0
- package/dist/temporal/temporal-view.d.ts +6 -2
- package/dist/temporal/temporal-view.d.ts.map +1 -1
- package/dist/temporal/view.d.ts +6 -0
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +6 -0
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/component.d.ts +1 -0
- package/dist/volume/component.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/__tests__/capture-scene-projection.unit.test.ts +24 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +15 -24
- package/src/__tests__/device-loss-fanout.unit.test.ts +43 -0
- package/src/__tests__/dynamic-geometry-batch.unit.test.ts +157 -0
- package/src/__tests__/dynamic-geometry.unit.test.ts +36 -0
- package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +81 -0
- package/src/__tests__/factory-contract.integration.test.ts +164 -1
- package/src/__tests__/frame-observation.contract.test.ts +21 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +1 -1
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +20 -1
- package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +0 -3
- package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +0 -3
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +11 -6
- package/src/__tests__/gpu-driven-production.integration.test.ts +13 -25
- package/src/__tests__/gpu-driven-view.unit.test.ts +1 -0
- package/src/__tests__/gpu-pass-timing-browser-runner.ts +15 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +14 -1
- package/src/__tests__/gpu-resource-scope.unit.test.ts +71 -0
- package/src/__tests__/gpu-scene.unit.test.ts +55 -1
- package/src/__tests__/materials-standard-contract.unit.test.ts +5 -1
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +8 -0
- package/src/__tests__/morph-neutral-admission.unit.test.ts +19 -0
- package/src/__tests__/physical-clearcoat.integration.test.ts +6 -1
- package/src/__tests__/post-process-error-policy-owner.unit.test.ts +6 -5
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +216 -5
- package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +68 -1
- package/src/__tests__/reflection-probe-authoring.unit.test.ts +12 -10
- package/src/__tests__/reflection-probe-box.dawn.test.ts +71 -0
- package/src/__tests__/reflection-probe-filter.unit.test.ts +9 -0
- package/src/__tests__/reflection-probe-orientation.dawn.test.ts +294 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +5 -15
- package/src/__tests__/reflection-probe-updates.unit.test.ts +129 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +3 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +5 -1
- package/src/__tests__/render-extent.unit.test.ts +63 -0
- package/src/__tests__/render-scene-projection.unit.test.ts +3 -2
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +173 -9
- package/src/__tests__/shader-manifest-fixture.ts +6 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +8 -1
- package/src/__tests__/specular-environment-scale.dawn.test.ts +4 -2
- package/src/__tests__/ssao-config.unit.test.ts +7 -0
- package/src/__tests__/ssao-surface.dawn.test.ts +252 -0
- package/src/__tests__/ssr-cube-silhouette.dawn.test.ts +270 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +51 -43
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +4 -0
- package/src/__tests__/ssr-gpu-dispatch.ts +10 -5
- package/src/__tests__/ssr-graph-topology.unit.test.ts +2 -2
- package/src/__tests__/ssr-hiz.browser.test.ts +1 -1
- package/src/__tests__/ssr-hiz.dawn.test.ts +1 -1
- package/src/__tests__/ssr-spatial.integration.test.ts +1 -1
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +40 -0
- package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +238 -1
- package/src/__tests__/standard-profile-oracle.unit.test.ts +27 -0
- package/src/__tests__/taa-coverage-edge.dawn.test.ts +6 -0
- package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +5 -1
- package/src/__tests__/taa-stability-feedback.ts +1 -0
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +2 -2
- package/src/__tests__/temporal-view.unit.test.ts +17 -0
- package/src/__tests__/transmission-prewarm.unit.test.ts +57 -0
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +62 -13
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +7 -7
- package/src/assembly/atmosphere-shader-prewarm.ts +3 -2
- package/src/assembly/device-feature-admission.ts +9 -4
- package/src/assembly/dynamic-geometry-host.ts +236 -163
- package/src/assembly/host-contract.ts +9 -0
- package/src/assembly/material/assembly.ts +350 -6
- package/src/assembly/material/pipeline-helpers.ts +100 -0
- package/src/assembly/material-shader-policy.ts +39 -18
- package/src/assembly/recovery/device-loss-fanout.ts +5 -0
- package/src/assembly/recovery/recovery-attempt.ts +2 -29
- package/src/assembly/recovery/renderer-recover.ts +6 -9
- package/src/assembly/renderer-facade.ts +22 -1
- package/src/assembly/shader-prewarm-policy.ts +14 -1
- package/src/assembly/webgpu-ready.ts +28 -19
- package/src/assembly/webgpu-renderer.ts +95 -95
- package/src/capture/scene-projection.ts +37 -0
- package/src/components/__tests__/dynamic-resolution-schema.unit.test.ts +65 -0
- package/src/components/__tests__/light-radius.integration.test.ts +59 -0
- package/src/components/directional-light.ts +3 -3
- package/src/components/dynamic-resolution.ts +67 -0
- package/src/components/index.ts +1 -0
- package/src/components/light-helpers.ts +6 -1
- package/src/components/reflection-probe.ts +1 -0
- package/src/components/skylight.ts +4 -1
- package/src/device/device-scope.ts +13 -7
- package/src/device/gpu-residency.ts +11 -11
- package/src/dynamic-geometry.ts +13 -8
- package/src/environment/background.ts +30 -12
- package/src/environment/ibl.ts +211 -0
- package/src/environment/storage.ts +63 -0
- package/src/errors/gpu-driven.ts +10 -3
- package/src/errors/render.ts +64 -0
- package/src/extract/camera.ts +12 -1
- package/src/extract/contracts.ts +10 -0
- package/src/extract/gpu-driven.ts +72 -17
- package/src/gpu-driven/batch-topology.ts +33 -11
- package/src/gpu-driven/material-artifacts.ts +179 -0
- package/src/gpu-driven/material-bindings.ts +41 -0
- package/src/gpu-driven/prepared-draw.ts +13 -2
- package/src/gpu-driven/production-raster.ts +681 -164
- package/src/gpu-resource.ts +21 -2
- package/src/gpu-scene-schema.ts +11 -2
- package/src/gpu-scene.ts +14 -3
- package/src/hdrp-buffers.ts +69 -0
- package/src/ibl/IblPipelineCache.ts +5 -0
- package/src/ibl/skylight-bind-group.ts +27 -17
- package/src/index.ts +92 -81
- package/src/inspection-types.ts +16 -1
- package/src/material-row.ts +60 -11
- package/src/pbr-pipeline.ts +13 -2
- package/src/pipeline/render-extent.ts +111 -0
- package/src/pipeline/standard-build-context.ts +7 -0
- package/src/pipeline/standard-forward-lane.ts +41 -24
- package/src/pipeline/standard-pipeline.ts +47 -18
- package/src/pipeline/standard-post.ts +4 -0
- package/src/pipeline/standard-profile.ts +2 -1
- package/src/pipeline-builder.ts +12 -3
- package/src/plugin.ts +4 -0
- package/src/post-process-errors.ts +26 -18
- package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +2 -2
- package/src/prepare/extended-lighting/resources.ts +3 -3
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +68 -0
- package/src/record/__tests__/material-bg-cache.unit.test.ts +45 -0
- package/src/record/__tests__/material-slot-plan.unit.test.ts +9 -0
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +47 -1
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +7 -3
- package/src/record/__tests__/temporal-payload.unit.test.ts +2 -0
- package/src/record/frame-lighting.ts +3 -1
- package/src/record/frame-snapshot.ts +13 -11
- package/src/record/frame.ts +150 -74
- package/src/record/main-pass-geometry.ts +60 -9
- package/src/record/main-pass-material.ts +47 -19
- package/src/record/main-pass.ts +63 -4
- package/src/record/prepared-material-bindings.ts +4 -0
- package/src/record/recovery-pipeline.ts +56 -0
- package/src/record/render-context.ts +34 -3
- package/src/record/shadow-pass.ts +187 -50
- package/src/record/typed-frame-graph.ts +60 -3
- package/src/record/view-ubo.ts +1 -1
- package/src/recovery/render-system-candidate.ts +54 -11
- package/src/reflection/background.ts +151 -0
- package/src/reflection/filter.ts +3 -0
- package/src/reflection/projection.ts +16 -6
- package/src/reflection/record-owner.ts +282 -86
- package/src/render-contract.ts +50 -32
- package/src/render-graph-primitives.ts +28 -3
- package/src/render-pipeline.ts +20 -1
- package/src/render-system-extract-tail.ts +23 -3
- package/src/render-system-extract.ts +127 -42
- package/src/render-system.ts +35 -7
- package/src/scene/render-scene.ts +8 -0
- package/src/ssao-config.ts +25 -8
- package/src/ssr/graph.ts +78 -58
- package/src/temporal/gpu.ts +5 -0
- package/src/temporal/standard-scene-data.ts +7 -1
- package/src/temporal/target-coverage-attachment.ts +72 -0
- package/src/temporal/temporal-view.ts +10 -4
- package/src/temporal/view.ts +12 -2
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +24 -9
- package/src/typed-render-graph-primitives.ts +38 -0
- package/src/volume/component.ts +1 -0
- package/src/volume/passes.ts +3 -3
- package/dist/chunk-33XZFTQU.mjs.map +0 -1
- package/dist/chunk-BQVQOQJH.mjs.map +0 -1
- package/dist/chunk-BTWH7BP4.mjs.map +0 -1
- package/dist/chunk-FUODTTIA.mjs.map +0 -1
- package/dist/chunk-JQTSEI3Z.mjs.map +0 -1
- package/dist/chunk-NICXSHAT.mjs.map +0 -1
- package/dist/chunk-NRNXBLIJ.mjs.map +0 -1
- package/dist/chunk-S4DNKQYQ.mjs.map +0 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { defineComponent, type SchemaOf, type ShapeOf } from '@forgeax/engine-ecs';
|
|
2
|
+
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
3
|
+
import {
|
|
4
|
+
type DynamicResolutionError,
|
|
5
|
+
DynamicResolutionInvalidParameterError,
|
|
6
|
+
DynamicResolutionRequiresTaaError,
|
|
7
|
+
} from '../errors/render';
|
|
8
|
+
|
|
9
|
+
/** Optional Camera companion that enables fixed or adaptive internal scaling. */
|
|
10
|
+
export const DynamicResolution = defineComponent('DynamicResolution', {
|
|
11
|
+
targetGpuMs: { type: 'f32', default: 16.67 },
|
|
12
|
+
minScale: { type: 'f32', default: 0.67 },
|
|
13
|
+
maxScale: { type: 'f32', default: 1.0 },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type DynamicResolutionData = ShapeOf<SchemaOf<typeof DynamicResolution>>;
|
|
17
|
+
|
|
18
|
+
const DEFAULT_DYNAMIC_RESOLUTION: DynamicResolutionData = Object.freeze({
|
|
19
|
+
targetGpuMs: 16.67,
|
|
20
|
+
minScale: 0.67,
|
|
21
|
+
maxScale: 1,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function invalid(
|
|
25
|
+
field: 'targetGpuMs' | 'minScale' | 'maxScale',
|
|
26
|
+
value: number,
|
|
27
|
+
expected: string,
|
|
28
|
+
): DynamicResolutionInvalidParameterError {
|
|
29
|
+
return new DynamicResolutionInvalidParameterError({ field, value, expected });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Validate one detached DynamicResolution authoring POD before graph work. */
|
|
33
|
+
export function validateDynamicResolutionParameters(
|
|
34
|
+
input: Partial<DynamicResolutionData> | undefined,
|
|
35
|
+
): Result<DynamicResolutionData, DynamicResolutionInvalidParameterError> {
|
|
36
|
+
const value = input ?? {};
|
|
37
|
+
const targetGpuMs = value.targetGpuMs ?? DEFAULT_DYNAMIC_RESOLUTION.targetGpuMs;
|
|
38
|
+
const minScale = value.minScale ?? DEFAULT_DYNAMIC_RESOLUTION.minScale;
|
|
39
|
+
const maxScale = value.maxScale ?? DEFAULT_DYNAMIC_RESOLUTION.maxScale;
|
|
40
|
+
if (!Number.isFinite(targetGpuMs) || targetGpuMs <= 0) {
|
|
41
|
+
return err(invalid('targetGpuMs', targetGpuMs, 'finite and greater than 0'));
|
|
42
|
+
}
|
|
43
|
+
if (!Number.isFinite(minScale) || minScale < 0.5 || minScale > 1) {
|
|
44
|
+
return err(invalid('minScale', minScale, 'finite and in [0.5, 1.0]'));
|
|
45
|
+
}
|
|
46
|
+
if (!Number.isFinite(maxScale) || maxScale < 0.5 || maxScale > 1) {
|
|
47
|
+
return err(invalid('maxScale', maxScale, 'finite and in [0.5, 1.0]'));
|
|
48
|
+
}
|
|
49
|
+
if (minScale > maxScale) {
|
|
50
|
+
return err(invalid('minScale', minScale, 'less than or equal to maxScale'));
|
|
51
|
+
}
|
|
52
|
+
return ok(Object.freeze({ targetGpuMs, minScale, maxScale }));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Validate the Camera companion relationship at the extraction boundary. */
|
|
56
|
+
export function validateDynamicResolutionCamera(
|
|
57
|
+
input: Partial<DynamicResolutionData> | undefined,
|
|
58
|
+
antialias: 'none' | 'fxaa' | 'msaa' | 'taa',
|
|
59
|
+
): Result<DynamicResolutionData | undefined, DynamicResolutionError> {
|
|
60
|
+
if (input === undefined) return ok(undefined);
|
|
61
|
+
const parameters = validateDynamicResolutionParameters(input);
|
|
62
|
+
if (!parameters.ok) return parameters;
|
|
63
|
+
if (antialias !== 'taa') {
|
|
64
|
+
return err(new DynamicResolutionRequiresTaaError({ antialias }));
|
|
65
|
+
}
|
|
66
|
+
return parameters;
|
|
67
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './atmosphere';
|
|
|
2
2
|
export * from './camera';
|
|
3
3
|
export * from './cube-camera';
|
|
4
4
|
export * from './directional-light';
|
|
5
|
+
export * from './dynamic-resolution';
|
|
5
6
|
export * from './fog';
|
|
6
7
|
export { Instances, type InstancesData } from './instances';
|
|
7
8
|
export { Layer } from './layer';
|
|
@@ -149,7 +149,12 @@ export function validateDirectionalLightData(
|
|
|
149
149
|
}),
|
|
150
150
|
);
|
|
151
151
|
}
|
|
152
|
-
|
|
152
|
+
// ECS stores this field as f32; both authored endpoints round outward.
|
|
153
|
+
// Accept their stored representations, but not the adjacent out-of-range f32.
|
|
154
|
+
if (
|
|
155
|
+
shadowAngularRadius < Math.fround(PCSS_RADIUS_MIN) ||
|
|
156
|
+
shadowAngularRadius > Math.fround(PCSS_RADIUS_MAX)
|
|
157
|
+
) {
|
|
153
158
|
return err(
|
|
154
159
|
new ShadowInvalidConfigError('shadowAngularRadius', shadowAngularRadius, {
|
|
155
160
|
kind: 'range',
|
|
@@ -40,6 +40,7 @@ export function reflectionProbeUpdateIntentToF32(intent: ReflectionProbeUpdateIn
|
|
|
40
40
|
export const ReflectionProbe = defineComponent('ReflectionProbe', {
|
|
41
41
|
halfExtents: { type: 'array<f32, 3>', default: new Float32Array([1, 1, 1]) },
|
|
42
42
|
priority: { type: 'f32', default: 0 },
|
|
43
|
+
boxProjection: { type: 'bool', default: false },
|
|
43
44
|
intensity: { type: 'f32', default: 1 },
|
|
44
45
|
resolution: { type: 'u32', default: 256 },
|
|
45
46
|
updateIntent: { type: 'f32', default: REFLECTION_PROBE_UPDATE_ONCE },
|
|
@@ -40,7 +40,10 @@ import { defineComponent } from '@forgeax/engine-ecs';
|
|
|
40
40
|
* Skylight ambient environment light.
|
|
41
41
|
*
|
|
42
42
|
* A single Skylight entity provides ambient lighting for all StandardMaterial
|
|
43
|
-
* surfaces.
|
|
43
|
+
* surfaces. When Atmosphere is selected and equirect is omitted, the same sky
|
|
44
|
+
* cube supplies diffuse and roughness-filtered specular IBL. Color/intensity
|
|
45
|
+
* still scale the result; without Atmosphere the solid-color behavior applies.
|
|
46
|
+
* Two authored inputs share one component:
|
|
44
47
|
*
|
|
45
48
|
* - **Solid-color ambient (no equirect).** Omit `equirect` for a constant
|
|
46
49
|
* ambient = `color` * `intensity` applied immediately, with no async GPU
|
|
@@ -62,7 +62,7 @@ export class DeviceScope {
|
|
|
62
62
|
readonly owner: string;
|
|
63
63
|
readonly parent: DeviceScope | undefined;
|
|
64
64
|
private lifecycleState: DeviceScopeState = 'active';
|
|
65
|
-
private readonly resources
|
|
65
|
+
private readonly resources = new Map<ResourceRef<unknown>, OwnedResource>();
|
|
66
66
|
private readonly children: DeviceScope[] = [];
|
|
67
67
|
|
|
68
68
|
private constructor(generation: number, owner: string, parent?: DeviceScope) {
|
|
@@ -99,7 +99,7 @@ export class DeviceScope {
|
|
|
99
99
|
|
|
100
100
|
ref<T>(kind: DeviceResourceKind, value: T): ResourceRef<T> {
|
|
101
101
|
const ref = new DeviceResourceRef(kind, this.owner, this.generation, value);
|
|
102
|
-
this.resources.
|
|
102
|
+
this.resources.set(ref, { ref, cleanup: () => undefined });
|
|
103
103
|
return ref;
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -116,7 +116,7 @@ export class DeviceScope {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
resourceDelta(): number {
|
|
119
|
-
return this.resources.
|
|
119
|
+
return this.resources.size;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/** @internal */
|
|
@@ -126,16 +126,22 @@ export class DeviceScope {
|
|
|
126
126
|
cleanup: (value: T) => void | Promise<void>,
|
|
127
127
|
): ResourceRef<T> {
|
|
128
128
|
const ref = new DeviceResourceRef(kind, this.owner, this.generation, value);
|
|
129
|
-
this.resources.
|
|
130
|
-
ref
|
|
129
|
+
this.resources.set(ref, {
|
|
130
|
+
ref,
|
|
131
131
|
cleanup: idempotentCleanup(cleanup) as (resource: unknown) => void | Promise<void>,
|
|
132
132
|
});
|
|
133
133
|
return ref;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/** @internal Remove ownership only after the resource's cleanup succeeded. */
|
|
137
|
+
_release(ref: ResourceRef<unknown>): void {
|
|
138
|
+
this.resources.delete(ref);
|
|
139
|
+
}
|
|
140
|
+
|
|
136
141
|
/** @internal */
|
|
137
142
|
_clearResources(): OwnedResource[] {
|
|
138
|
-
const resources = this.resources.
|
|
143
|
+
const resources = [...this.resources.values()];
|
|
144
|
+
this.resources.clear();
|
|
139
145
|
for (const resource of resources.reverse()) {
|
|
140
146
|
try {
|
|
141
147
|
const pending = resource.cleanup(resource.ref.value);
|
|
@@ -214,7 +220,7 @@ export class DeviceScope {
|
|
|
214
220
|
return {
|
|
215
221
|
owner: this.owner,
|
|
216
222
|
generation: this.generation,
|
|
217
|
-
resourceCount: this.resources.
|
|
223
|
+
resourceCount: this.resources.size,
|
|
218
224
|
};
|
|
219
225
|
}
|
|
220
226
|
}
|
|
@@ -183,6 +183,7 @@ export function writePrefilterUniforms(
|
|
|
183
183
|
roughness: number,
|
|
184
184
|
mipFaceSize: number,
|
|
185
185
|
sourceMipLevelCount = 1,
|
|
186
|
+
sourceSpace: 'image' | 'camera' = 'image',
|
|
186
187
|
): Result<void, RhiError> {
|
|
187
188
|
if (subPassIdx < 0 || subPassIdx >= PREFILTER_SUBPASS_COUNT) {
|
|
188
189
|
return invalidUniformInput(
|
|
@@ -196,7 +197,12 @@ export function writePrefilterUniforms(
|
|
|
196
197
|
`writePrefilterUniforms received sourceMipLevelCount=${sourceMipLevelCount}`,
|
|
197
198
|
);
|
|
198
199
|
}
|
|
199
|
-
const payload = new Float32Array([
|
|
200
|
+
const payload = new Float32Array([
|
|
201
|
+
roughness,
|
|
202
|
+
mipFaceSize,
|
|
203
|
+
sourceMipLevelCount,
|
|
204
|
+
sourceSpace === 'camera' ? 1 : 0,
|
|
205
|
+
]);
|
|
200
206
|
return device.queue.writeBuffer(buffer, subPassIdx * DYNAMIC_OFFSET_STRIDE, payload);
|
|
201
207
|
}
|
|
202
208
|
|
|
@@ -204,6 +210,7 @@ export function writeAllPrefilterUniforms(
|
|
|
204
210
|
device: FaceUniformsDevice,
|
|
205
211
|
buffer: Buffer,
|
|
206
212
|
sourceMipLevelCount = 1,
|
|
213
|
+
sourceSpace: 'image' | 'camera' = 'image',
|
|
207
214
|
): Result<void, RhiError> {
|
|
208
215
|
for (let mip = 0; mip < PREFILTER_MIP_LEVELS; mip++) {
|
|
209
216
|
const roughness = mip / (PREFILTER_MIP_LEVELS - 1);
|
|
@@ -216,6 +223,7 @@ export function writeAllPrefilterUniforms(
|
|
|
216
223
|
roughness,
|
|
217
224
|
mipFaceSize,
|
|
218
225
|
sourceMipLevelCount,
|
|
226
|
+
sourceSpace,
|
|
219
227
|
);
|
|
220
228
|
if (!result.ok) return result;
|
|
221
229
|
}
|
|
@@ -677,11 +685,7 @@ export class GpuResidencyCache {
|
|
|
677
685
|
if (device === undefined) {
|
|
678
686
|
throw new Error('GpuResidencyCache.wrapBuf called before configureGpuDevice');
|
|
679
687
|
}
|
|
680
|
-
|
|
681
|
-
this.deviceScope?._adopt('buffer', resource, (value: GpuBuffer) => {
|
|
682
|
-
if (!value.isDestroyed) value.destroy();
|
|
683
|
-
});
|
|
684
|
-
return resource;
|
|
688
|
+
return new GpuBuffer(device, rawHandle, this.deviceScope);
|
|
685
689
|
}
|
|
686
690
|
|
|
687
691
|
/** Wrap a raw RHI Texture handle into a GpuTexture (mirror of wrapBuf). */
|
|
@@ -690,11 +694,7 @@ export class GpuResidencyCache {
|
|
|
690
694
|
if (device === undefined) {
|
|
691
695
|
throw new Error('GpuResidencyCache.wrapTex called before configureGpuDevice');
|
|
692
696
|
}
|
|
693
|
-
|
|
694
|
-
this.deviceScope?._adopt('texture', resource, (value: GpuTexture) => {
|
|
695
|
-
if (!value.isDestroyed) value.destroy();
|
|
696
|
-
});
|
|
697
|
-
return resource;
|
|
697
|
+
return new GpuTexture(device, rawHandle, this.deviceScope);
|
|
698
698
|
}
|
|
699
699
|
|
|
700
700
|
/**
|
package/src/dynamic-geometry.ts
CHANGED
|
@@ -199,13 +199,11 @@ function cloneCandidate(candidate: DynamicGeometryCandidate): DynamicGeometryCan
|
|
|
199
199
|
|
|
200
200
|
function validateMesh(mesh: MeshAsset): string | undefined {
|
|
201
201
|
if (mesh.kind !== 'mesh') return 'MeshAsset.kind must be mesh';
|
|
202
|
-
if (
|
|
203
|
-
|
|
204
|
-
mesh.vertices.length === 0 ||
|
|
205
|
-
mesh.vertices.length % 3 !== 0
|
|
206
|
-
) {
|
|
207
|
-
return 'MeshAsset.vertices must be a non-empty Float32Array with xyz triples';
|
|
202
|
+
if (!(mesh.vertices instanceof Float32Array) || mesh.vertices.length === 0) {
|
|
203
|
+
return 'MeshAsset.vertices must be a non-empty Float32Array';
|
|
208
204
|
}
|
|
205
|
+
// vertices may be interleaved. Attribute layout validation belongs to the
|
|
206
|
+
// mesh producer/residency path; this gate must not assume an xyz-only stride.
|
|
209
207
|
if (
|
|
210
208
|
mesh.indices !== undefined &&
|
|
211
209
|
!(mesh.indices instanceof Uint16Array || mesh.indices instanceof Uint32Array)
|
|
@@ -214,8 +212,15 @@ function validateMesh(mesh: MeshAsset): string | undefined {
|
|
|
214
212
|
}
|
|
215
213
|
if (mesh.submeshes.length === 0)
|
|
216
214
|
return 'MeshAsset.submeshes must contain at least one topology range';
|
|
217
|
-
if (
|
|
218
|
-
|
|
215
|
+
if (
|
|
216
|
+
mesh.submeshes.some(
|
|
217
|
+
(submesh) =>
|
|
218
|
+
!Number.isSafeInteger(submesh.materialSlot) ||
|
|
219
|
+
submesh.materialSlot < 0 ||
|
|
220
|
+
submesh.materialSlot >= mesh.materialSlots.length,
|
|
221
|
+
)
|
|
222
|
+
) {
|
|
223
|
+
return 'MeshAsset.materialSlots must cover every referenced materialSlot';
|
|
219
224
|
}
|
|
220
225
|
if (mesh.vertices.some((value) => !Number.isFinite(value)))
|
|
221
226
|
return 'MeshAsset.vertices must be finite';
|
|
@@ -5,24 +5,39 @@ import type { Result } from '@forgeax/engine-types';
|
|
|
5
5
|
import type { _InternalRenderPipelineContext } from '../record/render-context';
|
|
6
6
|
import { VIEW_UNIFORM_BYTES } from '../record/view-ubo';
|
|
7
7
|
import type { RenderPipelineFrame, RenderPipelineTarget } from '../render-pipeline';
|
|
8
|
+
import { addAtmosphereIbl, type GraphEnvironment } from './ibl';
|
|
9
|
+
import { atmosphereStorage } from './storage';
|
|
8
10
|
|
|
9
11
|
const CUBE_SIZE = 128;
|
|
10
12
|
|
|
11
|
-
/** One
|
|
13
|
+
/** One device-owned sky cube feeds the graph background; the sun disc is added only there. */
|
|
12
14
|
export function addAtmosphereBackground(
|
|
13
15
|
graph: RenderGraphBuilder<RenderPipelineFrame>,
|
|
14
16
|
color: RenderPipelineTarget,
|
|
15
|
-
): Result<
|
|
16
|
-
const cube = graph.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
): Result<GraphEnvironment, RenderGraphError> {
|
|
18
|
+
const cube = graph.importTexture(
|
|
19
|
+
'atmosphere-sky',
|
|
20
|
+
{
|
|
21
|
+
format: 'rgba16float',
|
|
22
|
+
size: { width: CUBE_SIZE, height: CUBE_SIZE, depthOrArrayLayers: 6 },
|
|
23
|
+
usage: 0x14,
|
|
24
|
+
},
|
|
25
|
+
(frame) => atmosphereStorage(frame).sky,
|
|
26
|
+
);
|
|
20
27
|
if (!cube.ok) return cube;
|
|
21
28
|
const cubeView = graph.view(cube.value, { dimension: 'cube', arrayLayerCount: 6 });
|
|
22
29
|
if (!cubeView.ok) return cubeView;
|
|
23
|
-
const params = graph.
|
|
30
|
+
const params = graph.importBuffer(
|
|
31
|
+
'atmosphere-params',
|
|
32
|
+
{ size: 64, usage: 0x48 },
|
|
33
|
+
(frame) => atmosphereStorage(frame).params,
|
|
34
|
+
);
|
|
24
35
|
if (!params.ok) return params;
|
|
25
|
-
const vertices = graph.
|
|
36
|
+
const vertices = graph.importBuffer(
|
|
37
|
+
'atmosphere-face-vertices',
|
|
38
|
+
{ size: 6 * 3 * 3 * 4, usage: 0x28 },
|
|
39
|
+
(frame) => atmosphereStorage(frame).vertices,
|
|
40
|
+
);
|
|
26
41
|
if (!vertices.ok) return vertices;
|
|
27
42
|
const view = graph.importBuffer(
|
|
28
43
|
'atmosphere-view',
|
|
@@ -30,7 +45,6 @@ export function addAtmosphereBackground(
|
|
|
30
45
|
(frame) => (frame as _InternalRenderPipelineContext).pipelineState.viewUniformBuffer,
|
|
31
46
|
);
|
|
32
47
|
if (!view.ok) return view;
|
|
33
|
-
let submittedSignature: string | undefined;
|
|
34
48
|
let state:
|
|
35
49
|
| {
|
|
36
50
|
cubePipeline: RenderPipeline;
|
|
@@ -44,7 +58,8 @@ export function addAtmosphereBackground(
|
|
|
44
58
|
| undefined;
|
|
45
59
|
const frameOf = (frame: RenderPipelineFrame) => frame as _InternalRenderPipelineContext;
|
|
46
60
|
const dirty = (frame: RenderPipelineFrame) =>
|
|
47
|
-
frameOf(frame).frameState.environmentFrame?.environmentSignature !==
|
|
61
|
+
frameOf(frame).frameState.environmentFrame?.environmentSignature !==
|
|
62
|
+
atmosphereStorage(frame).submittedSignature;
|
|
48
63
|
const upload = graph.addCopyPass('atmosphere-prepare', {
|
|
49
64
|
accesses: [
|
|
50
65
|
{ resource: params.value, usage: 'copy-dst' },
|
|
@@ -206,14 +221,16 @@ export function addAtmosphereBackground(
|
|
|
206
221
|
if (face === 5) {
|
|
207
222
|
const signature = frameOf(frame).frameState.environmentFrame?.environmentSignature;
|
|
208
223
|
frameOf(frame).frameState.pendingAtmospherePublish = () => {
|
|
209
|
-
submittedSignature = signature;
|
|
224
|
+
atmosphereStorage(frame).submittedSignature = signature;
|
|
210
225
|
};
|
|
211
226
|
}
|
|
212
227
|
},
|
|
213
228
|
});
|
|
214
229
|
if (!pass.ok) return pass;
|
|
215
230
|
}
|
|
216
|
-
|
|
231
|
+
const ibl = addAtmosphereIbl(graph, cubeView.value, vertices.value, dirty);
|
|
232
|
+
if (!ibl.ok) return ibl;
|
|
233
|
+
const background = graph.addRasterPass('atmosphere-background', {
|
|
217
234
|
accesses: [
|
|
218
235
|
{ resource: cubeView.value, usage: 'sampled-read' },
|
|
219
236
|
{ resource: params.value, usage: 'uniform-read' },
|
|
@@ -260,4 +277,5 @@ export function addAtmosphereBackground(
|
|
|
260
277
|
pass.draw(3);
|
|
261
278
|
},
|
|
262
279
|
});
|
|
280
|
+
return background.ok ? ibl : background;
|
|
263
281
|
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GraphBuffer,
|
|
3
|
+
GraphTextureView,
|
|
4
|
+
RenderGraphBuilder,
|
|
5
|
+
RenderGraphError,
|
|
6
|
+
} from '@forgeax/engine-render-graph';
|
|
7
|
+
import {
|
|
8
|
+
type BindGroup,
|
|
9
|
+
type BindGroupLayout,
|
|
10
|
+
type RenderPipeline,
|
|
11
|
+
RhiError,
|
|
12
|
+
type Sampler,
|
|
13
|
+
} from '@forgeax/engine-rhi';
|
|
14
|
+
import { ok, type Result } from '@forgeax/engine-types';
|
|
15
|
+
import type { RenderPipelineFrame } from '../render-pipeline';
|
|
16
|
+
import { atmosphereStorage } from './storage';
|
|
17
|
+
|
|
18
|
+
/** Graph views of the shared device-owned sky and its lighting products. */
|
|
19
|
+
export interface GraphEnvironment {
|
|
20
|
+
readonly sky: GraphTextureView;
|
|
21
|
+
readonly irradiance: GraphTextureView;
|
|
22
|
+
readonly prefilter: GraphTextureView;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function addAtmosphereIbl(
|
|
26
|
+
graph: RenderGraphBuilder<RenderPipelineFrame>,
|
|
27
|
+
sky: GraphTextureView,
|
|
28
|
+
vertices: GraphBuffer,
|
|
29
|
+
dirty: (frame: RenderPipelineFrame) => boolean,
|
|
30
|
+
): Result<GraphEnvironment, RenderGraphError> {
|
|
31
|
+
const irradiance = graph.importTexture(
|
|
32
|
+
'atmosphere-irradiance',
|
|
33
|
+
{
|
|
34
|
+
format: 'rgba16float',
|
|
35
|
+
size: { width: 16, height: 16, depthOrArrayLayers: 6 },
|
|
36
|
+
usage: 0x14,
|
|
37
|
+
},
|
|
38
|
+
(frame) => atmosphereStorage(frame).irradiance,
|
|
39
|
+
);
|
|
40
|
+
if (!irradiance.ok) return irradiance;
|
|
41
|
+
const prefilter = graph.importTexture(
|
|
42
|
+
'atmosphere-prefilter',
|
|
43
|
+
{
|
|
44
|
+
format: 'rgba16float',
|
|
45
|
+
size: { width: 64, height: 64, depthOrArrayLayers: 6 },
|
|
46
|
+
mipLevelCount: 5,
|
|
47
|
+
usage: 0x14,
|
|
48
|
+
},
|
|
49
|
+
(frame) => atmosphereStorage(frame).prefilter,
|
|
50
|
+
);
|
|
51
|
+
if (!prefilter.ok) return prefilter;
|
|
52
|
+
const irradianceView = graph.view(irradiance.value, { dimension: 'cube', arrayLayerCount: 6 });
|
|
53
|
+
if (!irradianceView.ok) return irradianceView;
|
|
54
|
+
const prefilterView = graph.view(prefilter.value, { dimension: 'cube', arrayLayerCount: 6 });
|
|
55
|
+
if (!prefilterView.ok) return prefilterView;
|
|
56
|
+
const parameters = graph.importBuffer(
|
|
57
|
+
'atmosphere-ibl-params',
|
|
58
|
+
{ size: 5 * 256, usage: 0x48 },
|
|
59
|
+
(frame) => atmosphereStorage(frame).iblParams,
|
|
60
|
+
);
|
|
61
|
+
if (!parameters.ok) return parameters;
|
|
62
|
+
const upload = graph.addCopyPass('atmosphere-ibl-prepare', {
|
|
63
|
+
accesses: [{ resource: parameters.value, usage: 'copy-dst' }],
|
|
64
|
+
executeIf: dirty,
|
|
65
|
+
encode: ({ frame, resources }) => {
|
|
66
|
+
const values = new Float32Array(5 * 64);
|
|
67
|
+
for (let mip = 0; mip < 5; mip++) values[mip * 64 + 14] = mip / 4;
|
|
68
|
+
frame.runtime.device.queue
|
|
69
|
+
.writeBuffer(resources.buffer(parameters.value).unwrap(), 0, values)
|
|
70
|
+
.unwrap();
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
if (!upload.ok) return upload;
|
|
74
|
+
let state:
|
|
75
|
+
| {
|
|
76
|
+
layout: BindGroupLayout;
|
|
77
|
+
inputLayout: BindGroupLayout;
|
|
78
|
+
sampler: Sampler;
|
|
79
|
+
irradiance: RenderPipeline;
|
|
80
|
+
prefilter: RenderPipeline;
|
|
81
|
+
inputs?: BindGroup;
|
|
82
|
+
parameters: BindGroup[];
|
|
83
|
+
}
|
|
84
|
+
| undefined;
|
|
85
|
+
const ensureState = (frame: RenderPipelineFrame) => {
|
|
86
|
+
if (state !== undefined) return state;
|
|
87
|
+
const device = frame.runtime.device;
|
|
88
|
+
const source = frame.atmosphereShaders?.ibl;
|
|
89
|
+
const factory = frame.runtime.shaderModuleFactory;
|
|
90
|
+
if (source === undefined || factory === undefined)
|
|
91
|
+
throw new RhiError({
|
|
92
|
+
code: 'shader-compile-failed',
|
|
93
|
+
expected: 'the compiled Atmosphere IBL program is available',
|
|
94
|
+
hint: 'rebuild the Engine shader manifest before selecting Atmosphere lighting',
|
|
95
|
+
});
|
|
96
|
+
const module = factory.createShaderModule({ code: source, label: 'atmosphere_ibl' }).unwrap();
|
|
97
|
+
const layout = device
|
|
98
|
+
.createBindGroupLayout({
|
|
99
|
+
entries: [{ binding: 0, visibility: 2, buffer: { type: 'uniform', minBindingSize: 64 } }],
|
|
100
|
+
})
|
|
101
|
+
.unwrap();
|
|
102
|
+
const inputLayout = device
|
|
103
|
+
.createBindGroupLayout({
|
|
104
|
+
entries: [
|
|
105
|
+
{ binding: 0, visibility: 2, texture: { sampleType: 'float', viewDimension: 'cube' } },
|
|
106
|
+
{ binding: 1, visibility: 2, sampler: { type: 'filtering' } },
|
|
107
|
+
],
|
|
108
|
+
})
|
|
109
|
+
.unwrap();
|
|
110
|
+
const pipelineLayout = device
|
|
111
|
+
.createPipelineLayout({ bindGroupLayouts: [layout, inputLayout] })
|
|
112
|
+
.unwrap();
|
|
113
|
+
const pipeline = (entryPoint: string) =>
|
|
114
|
+
device
|
|
115
|
+
.createRenderPipeline({
|
|
116
|
+
label: entryPoint,
|
|
117
|
+
layout: pipelineLayout,
|
|
118
|
+
vertex: {
|
|
119
|
+
module,
|
|
120
|
+
entryPoint: 'atmosphere_ibl_vs',
|
|
121
|
+
buffers: [
|
|
122
|
+
{
|
|
123
|
+
arrayStride: 12,
|
|
124
|
+
attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x3' }],
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
fragment: { module, entryPoint, targets: [{ format: 'rgba16float' }] },
|
|
129
|
+
primitive: { topology: 'triangle-list' },
|
|
130
|
+
})
|
|
131
|
+
.unwrap();
|
|
132
|
+
state = {
|
|
133
|
+
layout,
|
|
134
|
+
inputLayout,
|
|
135
|
+
sampler: device
|
|
136
|
+
.createSampler({ magFilter: 'linear', minFilter: 'linear', mipmapFilter: 'linear' })
|
|
137
|
+
.unwrap(),
|
|
138
|
+
irradiance: pipeline('atmosphere_irradiance_fs'),
|
|
139
|
+
prefilter: pipeline('atmosphere_prefilter_fs'),
|
|
140
|
+
parameters: [],
|
|
141
|
+
};
|
|
142
|
+
return state;
|
|
143
|
+
};
|
|
144
|
+
for (const product of ['irradiance', 'prefilter'] as const) {
|
|
145
|
+
for (let mip = 0; mip < (product === 'irradiance' ? 1 : 5); mip++)
|
|
146
|
+
for (let face = 0; face < 6; face++) {
|
|
147
|
+
const target = graph.view(product === 'irradiance' ? irradiance.value : prefilter.value, {
|
|
148
|
+
dimension: '2d',
|
|
149
|
+
baseArrayLayer: face,
|
|
150
|
+
arrayLayerCount: 1,
|
|
151
|
+
baseMipLevel: mip,
|
|
152
|
+
mipLevelCount: 1,
|
|
153
|
+
});
|
|
154
|
+
if (!target.ok) return target;
|
|
155
|
+
const pass = graph.addRasterPass(`atmosphere-${product}-${mip}-${face}`, {
|
|
156
|
+
accesses: [
|
|
157
|
+
{ resource: sky, usage: 'sampled-read' },
|
|
158
|
+
{ resource: parameters.value, usage: 'uniform-read' },
|
|
159
|
+
{ resource: vertices, usage: 'vertex-read' },
|
|
160
|
+
{ resource: target.value, usage: 'color-attachment' },
|
|
161
|
+
],
|
|
162
|
+
colorAttachments: [{ view: target.value, loadOp: 'clear', storeOp: 'store' }],
|
|
163
|
+
executeIf: dirty,
|
|
164
|
+
encode: ({ frame, resources, pass }) => {
|
|
165
|
+
const current = ensureState(frame);
|
|
166
|
+
current.inputs ??= frame.runtime.device
|
|
167
|
+
.createBindGroup({
|
|
168
|
+
layout: current.inputLayout,
|
|
169
|
+
entries: [
|
|
170
|
+
{
|
|
171
|
+
binding: 0,
|
|
172
|
+
resource: { kind: 'textureView', value: resources.textureView(sky).unwrap() },
|
|
173
|
+
},
|
|
174
|
+
{ binding: 1, resource: { kind: 'sampler', value: current.sampler } },
|
|
175
|
+
],
|
|
176
|
+
})
|
|
177
|
+
.unwrap();
|
|
178
|
+
let parameterGroup = current.parameters[mip];
|
|
179
|
+
if (parameterGroup === undefined) {
|
|
180
|
+
parameterGroup = frame.runtime.device
|
|
181
|
+
.createBindGroup({
|
|
182
|
+
layout: current.layout,
|
|
183
|
+
entries: [
|
|
184
|
+
{
|
|
185
|
+
binding: 0,
|
|
186
|
+
resource: {
|
|
187
|
+
kind: 'buffer',
|
|
188
|
+
value: {
|
|
189
|
+
buffer: resources.buffer(parameters.value).unwrap(),
|
|
190
|
+
offset: mip * 256,
|
|
191
|
+
size: 64,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
})
|
|
197
|
+
.unwrap();
|
|
198
|
+
current.parameters[mip] = parameterGroup;
|
|
199
|
+
}
|
|
200
|
+
pass.setPipeline(current[product]);
|
|
201
|
+
pass.setBindGroup(0, parameterGroup);
|
|
202
|
+
pass.setBindGroup(1, current.inputs);
|
|
203
|
+
pass.setVertexBuffer(0, resources.buffer(vertices).unwrap());
|
|
204
|
+
pass.draw(3, 1, face * 3);
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
if (!pass.ok) return pass;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return ok({ sky, irradiance: irradianceView.value, prefilter: prefilterView.value });
|
|
211
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Buffer, Texture } from '@forgeax/engine-rhi';
|
|
2
|
+
import type { DeviceScope } from '../device/device-scope';
|
|
3
|
+
import type { _InternalRenderPipelineContext } from '../record/render-context';
|
|
4
|
+
import type { RenderPipelineFrame } from '../render-pipeline';
|
|
5
|
+
|
|
6
|
+
/** Device-generation resources survive unrelated probe/scene graph topology changes. */
|
|
7
|
+
export interface AtmosphereStorage {
|
|
8
|
+
readonly sky: Texture;
|
|
9
|
+
readonly irradiance: Texture;
|
|
10
|
+
readonly prefilter: Texture;
|
|
11
|
+
readonly params: Buffer;
|
|
12
|
+
readonly vertices: Buffer;
|
|
13
|
+
readonly iblParams: Buffer;
|
|
14
|
+
submittedSignature?: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
const generations = new WeakMap<DeviceScope, AtmosphereStorage>();
|
|
17
|
+
|
|
18
|
+
export function atmosphereStorage(frame: RenderPipelineFrame): AtmosphereStorage {
|
|
19
|
+
const runtime = (frame as _InternalRenderPipelineContext).runtime;
|
|
20
|
+
const owner = runtime.deviceScope;
|
|
21
|
+
const existing = generations.get(owner);
|
|
22
|
+
if (existing !== undefined) return existing;
|
|
23
|
+
const scope = owner.createChild('atmosphere');
|
|
24
|
+
const device = runtime.device;
|
|
25
|
+
try {
|
|
26
|
+
const texture = (name: string, size: number, mipLevelCount = 1) => {
|
|
27
|
+
const value = device
|
|
28
|
+
.createTexture({
|
|
29
|
+
label: name,
|
|
30
|
+
format: 'rgba16float',
|
|
31
|
+
size: { width: size, height: size, depthOrArrayLayers: 6 },
|
|
32
|
+
mipLevelCount,
|
|
33
|
+
usage: 0x14,
|
|
34
|
+
textureBindingViewDimension: undefined,
|
|
35
|
+
})
|
|
36
|
+
.unwrap();
|
|
37
|
+
scope._adopt('texture', value, (value) => {
|
|
38
|
+
device.destroyTexture(value).unwrap();
|
|
39
|
+
});
|
|
40
|
+
return value;
|
|
41
|
+
};
|
|
42
|
+
const buffer = (name: string, size: number, usage: number) => {
|
|
43
|
+
const value = device.createBuffer({ label: name, size, usage }).unwrap();
|
|
44
|
+
scope._adopt('buffer', value, (value) => {
|
|
45
|
+
device.destroyBuffer(value).unwrap();
|
|
46
|
+
});
|
|
47
|
+
return value;
|
|
48
|
+
};
|
|
49
|
+
const created: AtmosphereStorage = {
|
|
50
|
+
sky: texture('atmosphere-sky', 128),
|
|
51
|
+
irradiance: texture('atmosphere-irradiance', 16),
|
|
52
|
+
prefilter: texture('atmosphere-prefilter', 64, 5),
|
|
53
|
+
params: buffer('atmosphere-params', 64, 0x48),
|
|
54
|
+
vertices: buffer('atmosphere-face-vertices', 216, 0x28),
|
|
55
|
+
iblParams: buffer('atmosphere-ibl-params', 1280, 0x48),
|
|
56
|
+
};
|
|
57
|
+
generations.set(owner, created);
|
|
58
|
+
return created;
|
|
59
|
+
} catch (cause) {
|
|
60
|
+
scope.retire();
|
|
61
|
+
throw cause;
|
|
62
|
+
}
|
|
63
|
+
}
|