@forgeax/engine-render 0.1.32 → 0.1.33
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 +379 -33
- package/dist/assembly/analytic-fog-registration.d.ts +3 -0
- package/dist/assembly/analytic-fog-registration.d.ts.map +1 -0
- package/dist/assembly/fxaa-registration.d.ts +4 -0
- package/dist/assembly/fxaa-registration.d.ts.map +1 -0
- package/dist/assembly/lod-observation.d.ts +4 -0
- package/dist/assembly/lod-observation.d.ts.map +1 -0
- package/dist/assembly/motion-blur-registration.d.ts +3 -0
- package/dist/assembly/motion-blur-registration.d.ts.map +1 -0
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/temporal-post-process-prewarm.d.ts +13 -0
- package/dist/assembly/temporal-post-process-prewarm.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-contract.d.ts +4 -3
- package/dist/assembly/webgpu-ready-contract.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/bloom-admission.d.ts +13 -0
- package/dist/bloom-admission.d.ts.map +1 -0
- package/dist/{chunk-DRR7D3KX.mjs → chunk-3RTCJTH4.mjs} +12 -9
- package/dist/chunk-3RTCJTH4.mjs.map +1 -0
- package/dist/{chunk-S665NJNF.mjs → chunk-LEAXOMYJ.mjs} +3 -3
- package/dist/{chunk-S665NJNF.mjs.map → chunk-LEAXOMYJ.mjs.map} +1 -1
- package/dist/{chunk-6TFBXLRJ.mjs → chunk-NB2S7ZRX.mjs} +155 -11
- package/dist/chunk-NB2S7ZRX.mjs.map +1 -0
- package/dist/{chunk-LULCA3OF.mjs → chunk-NNQL7CB3.mjs} +4 -4
- package/dist/{chunk-LULCA3OF.mjs.map → chunk-NNQL7CB3.mjs.map} +1 -1
- package/dist/{chunk-4NNZQCL3.mjs → chunk-OO3DRYK7.mjs} +66 -200
- package/dist/chunk-OO3DRYK7.mjs.map +1 -0
- package/dist/{chunk-GWCEMDVR.mjs → chunk-PLAXOOX5.mjs} +232 -7
- package/dist/chunk-PLAXOOX5.mjs.map +1 -0
- package/dist/{chunk-JRIPCMYC.mjs → chunk-VOZWDM3L.mjs} +283 -193
- package/dist/chunk-VOZWDM3L.mjs.map +1 -0
- package/dist/{chunk-JCIRYQ4V.mjs → chunk-ZDQWGQW7.mjs} +10 -4
- package/dist/chunk-ZDQWGQW7.mjs.map +1 -0
- package/dist/components/atmosphere.d.ts +2 -0
- package/dist/components/atmosphere.d.ts.map +1 -1
- package/dist/components/camera.d.ts +20 -4
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/mesh-filter.d.ts +6 -5
- package/dist/components/mesh-filter.d.ts.map +1 -1
- package/dist/components/motion-blur.d.ts +1 -0
- package/dist/components/motion-blur.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +2865 -526
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +6 -0
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/environment/analytic-fog.d.ts +6 -0
- package/dist/environment/analytic-fog.d.ts.map +1 -0
- package/dist/errors/render.d.ts +6 -2
- 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/environment.d.ts +2 -0
- package/dist/extract/environment.d.ts.map +1 -1
- package/dist/features/error-reporting.d.ts +13 -0
- package/dist/features/error-reporting.d.ts.map +1 -0
- package/dist/features/host.d.ts +5 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-feature.d.ts +20 -2
- package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-params.d.ts +21 -0
- package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-runtime.d.ts +54 -0
- package/dist/features/motion-blur/motion-blur-runtime.d.ts.map +1 -0
- package/dist/features/plan.d.ts +7 -4
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +9 -5
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts +7 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +3 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +9 -0
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +6 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +5 -5
- package/dist/inspection-types.d.ts +43 -2
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances.d.ts +8 -1
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +6 -6
- package/dist/pipeline/standard-post.d.ts +28 -19
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +2 -2
- package/dist/record/frame-snapshot.d.ts +3 -0
- 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 +5 -2
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +6 -5
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +12 -28
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +5 -3
- package/dist/record/skybox-post-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.map +1 -1
- package/dist/render-contract.d.ts +24 -4
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +3 -1
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +8 -1
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +7 -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/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/frame.d.ts +7 -0
- package/dist/temporal/frame.d.ts.map +1 -1
- package/dist/temporal/index.mjs +5 -5
- package/dist/temporal/view.d.ts +6 -1
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +10 -7
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/component.d.ts +0 -1
- package/dist/volume/component.d.ts.map +1 -1
- package/dist/volume/passes.d.ts.map +1 -1
- package/dist/volume/temporal.d.ts +3 -1
- package/dist/volume/temporal.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/__tests__/analytic-fog-graph.unit.test.ts +199 -0
- package/src/__tests__/analytic-fog-optics.unit.test.ts +38 -0
- package/src/__tests__/bloom-exact-zero.unit.test.ts +112 -18
- package/src/__tests__/bloom-lifecycle.integration.test.ts +17 -4
- package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +20 -0
- package/src/__tests__/environment-fog-lanes.integration.test.ts +14 -0
- package/src/__tests__/environment-selection.unit.test.ts +12 -0
- package/src/__tests__/factory-contract.integration.test.ts +331 -68
- package/src/__tests__/frame-camera-selection.unit.test.ts +39 -1
- package/src/__tests__/frame-plan-contract.unit.test.ts +2 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +2 -1
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +1 -1
- package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +1 -1
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +11 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +12 -0
- package/src/__tests__/gpu-scene.unit.test.ts +91 -0
- package/src/__tests__/instances-store.unit.test.ts +75 -0
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +31 -0
- package/src/__tests__/motion-blur-compute.dawn.test.ts +583 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +21 -0
- package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +86 -0
- package/src/__tests__/public-surface.test-d.ts +8 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +2 -0
- package/src/__tests__/render-feature-error-reporting.unit.test.ts +24 -0
- package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +23 -0
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +31 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +7 -1
- package/src/__tests__/shader-manifest-fixture.ts +4 -4
- package/src/__tests__/skinned-shadow-caster.test.ts +2 -0
- package/src/__tests__/standard-lighting-write.unit.test.ts +2 -1
- package/src/__tests__/standard-pipeline.dawn.test.ts +1 -1
- package/src/__tests__/standard-pipeline.integration.test.ts +18 -8
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +10 -0
- package/src/__tests__/temporal-view.unit.test.ts +2 -1
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +20 -4
- package/src/__tests__/volume-optics-oracle.unit.test.ts +3 -1
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +177 -13
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +447 -61
- package/src/__tests__/volumetric-fog-temporal.unit.test.ts +27 -0
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +19 -3
- package/src/assembly/analytic-fog-registration.ts +14 -0
- package/src/assembly/fxaa-registration.ts +17 -0
- package/src/assembly/lod-observation.ts +12 -0
- package/src/assembly/motion-blur-registration.ts +17 -0
- package/src/assembly/renderer-facade.ts +2 -0
- package/src/assembly/temporal-post-process-prewarm.ts +40 -0
- package/src/assembly/webgpu-ready-contract.ts +8 -3
- package/src/assembly/webgpu-ready.ts +181 -245
- package/src/assembly/webgpu-renderer.ts +16 -31
- package/src/bloom-admission.ts +21 -0
- package/src/components/__tests__/motion-blur.unit.test.ts +1 -0
- package/src/components/atmosphere.ts +4 -0
- package/src/components/camera.ts +66 -10
- package/src/components/mesh-filter.ts +11 -9
- package/src/components/motion-blur.ts +1 -0
- package/src/device/gpu-residency.ts +56 -12
- package/src/environment/analytic-fog.ts +44 -0
- package/src/environment/background.ts +2 -2
- package/src/errors/render.ts +14 -1
- package/src/extract/camera.ts +20 -7
- package/src/extract/contracts.ts +7 -0
- package/src/extract/environment.ts +4 -0
- package/src/features/error-reporting.ts +78 -0
- package/src/features/host.ts +25 -12
- package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +111 -1
- package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +58 -3
- package/src/features/motion-blur/motion-blur-feature.ts +1519 -15
- package/src/features/motion-blur/motion-blur-params.ts +70 -2
- package/src/features/motion-blur/motion-blur-runtime.ts +176 -0
- package/src/features/plan.ts +25 -8
- package/src/features/prepared-gpu-work.ts +86 -35
- package/src/features/render-graph-compute.ts +13 -5
- package/src/features/render-graph-contribution.ts +2 -0
- package/src/gpu-driven/material-artifacts.ts +2 -7
- package/src/gpu-driven/prepared-draw.ts +15 -10
- package/src/gpu-driven/production-raster.ts +83 -7
- package/src/gpu-driven/view-gpu.ts +17 -0
- package/src/gpu-scene.ts +188 -21
- package/src/index.ts +32 -0
- package/src/inspection-types.ts +60 -2
- package/src/instances.ts +80 -1
- package/src/pipeline/standard-post.ts +307 -78
- package/src/pipeline-spec.ts +5 -5
- package/src/points-lines/__tests__/view-ubo.unit.test.ts +2 -1
- package/src/record/__tests__/temporal-payload.unit.test.ts +2 -1
- package/src/record/frame-snapshot.ts +5 -1
- package/src/record/frame-targets.ts +10 -6
- package/src/record/frame.ts +163 -22
- package/src/record/main-pass-geometry.ts +30 -1
- package/src/record/main-pass-sprite-draws.ts +6 -1
- package/src/record/mesh-ssbo.ts +43 -6
- package/src/record/render-context.ts +12 -28
- package/src/record/shadow-pass.ts +6 -1
- package/src/record/skybox-post-pass.ts +122 -196
- package/src/record/typed-frame-graph.ts +153 -9
- package/src/recovery/render-system-candidate.ts +22 -10
- package/src/render-contract.ts +23 -4
- package/src/render-pipeline.ts +6 -1
- package/src/render-system-extract-tail.ts +16 -0
- package/src/render-system-extract.ts +45 -13
- package/src/render-system.ts +255 -120
- package/src/scene/render-scene.ts +85 -4
- package/src/temporal/__tests__/temporal-frame-transaction.unit.test.ts +36 -0
- package/src/temporal/frame-coordinator.ts +8 -1
- package/src/temporal/frame.ts +20 -0
- package/src/temporal/view.ts +13 -2
- package/src/typed-render-graph-primitives.ts +121 -48
- package/src/volume/component.ts +0 -1
- package/src/volume/passes.ts +8 -4
- package/src/volume/resources.ts +1 -1
- package/src/volume/temporal.ts +11 -0
- package/dist/chunk-4NNZQCL3.mjs.map +0 -1
- package/dist/chunk-6TFBXLRJ.mjs.map +0 -1
- package/dist/chunk-DRR7D3KX.mjs.map +0 -1
- package/dist/chunk-GWCEMDVR.mjs.map +0 -1
- package/dist/chunk-JCIRYQ4V.mjs.map +0 -1
- package/dist/chunk-JRIPCMYC.mjs.map +0 -1
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
registerDepthOfFieldBuiltins,
|
|
59
59
|
} from '../features/depth-of-field/depth-of-field-assembly';
|
|
60
60
|
import { createRenderFeatureHost } from '../features/host';
|
|
61
|
+
import { motionBlurDrawOptions } from '../features/motion-blur/motion-blur-runtime';
|
|
61
62
|
import { RENDER_FEATURE_VERTEX_LAYOUTS } from '../features/prepared-graphics';
|
|
62
63
|
import type { RenderFeature, RenderFeatureShaderModuleMode } from '../features/types';
|
|
63
64
|
import {
|
|
@@ -66,7 +67,7 @@ import {
|
|
|
66
67
|
} from '../fullscreen-post-process-pass';
|
|
67
68
|
import { glyphTextLayoutSystem } from '../glyph-text-layout-system';
|
|
68
69
|
import { GPU_SHADER_STAGE_FRAGMENT, GPU_SHADER_STAGE_VERTEX } from '../gpu-stage';
|
|
69
|
-
import type
|
|
70
|
+
import { type BloomInspection, emptyBloomInspection } from '../inspection-types';
|
|
70
71
|
import { DeviceScope } from '../lifecycle';
|
|
71
72
|
import {
|
|
72
73
|
createHdrpBindGroupLayoutDescriptor,
|
|
@@ -131,7 +132,7 @@ import type {
|
|
|
131
132
|
SsrShaderSources,
|
|
132
133
|
VolumetricFogShaderSources,
|
|
133
134
|
} from '../render-contract';
|
|
134
|
-
import {
|
|
135
|
+
import { STANDARD_OUTPUT_TRANSFORM_FEATURE_ID } from '../render-contract';
|
|
135
136
|
import {
|
|
136
137
|
attachGpuPassTimingSession,
|
|
137
138
|
createRenderSystem,
|
|
@@ -146,10 +147,13 @@ import { postSpawnResolveJoints } from '../scene-instances/post-spawn-resolve-jo
|
|
|
146
147
|
import type { RenderTarget } from '../targets/contracts';
|
|
147
148
|
import { tilemapChunkExtractSystem } from '../tilemap-chunk-extract-system';
|
|
148
149
|
import type { TypedFrameObservationCapture } from '../typed-render-graph-primitives';
|
|
150
|
+
import { registerAnalyticFogPostProcess } from './analytic-fog-registration';
|
|
149
151
|
import type { RhiBackendPack } from './backend-contract';
|
|
150
152
|
import type { BundlerOptions } from './bundler-contract';
|
|
151
153
|
import { createRendererDynamicGeometryController } from './dynamic-geometry-host';
|
|
154
|
+
import { registerFxaaPostProcess } from './fxaa-registration';
|
|
152
155
|
import type { RendererAssemblyImplementation } from './host-contract';
|
|
156
|
+
import { observeLodOcclusionForReceipt } from './lod-observation';
|
|
153
157
|
import {
|
|
154
158
|
isEngineOwnedMaterialShader,
|
|
155
159
|
resolveMaterialPipelineBindGroups,
|
|
@@ -177,6 +181,7 @@ import {
|
|
|
177
181
|
shouldDeferMissingPreparedMaterialShader,
|
|
178
182
|
} from './material-shader-policy';
|
|
179
183
|
import type { MeshSsboGrowResult, MeshSsboState } from './mesh-ssbo-grow';
|
|
184
|
+
import { registerMotionBlurPostProcess } from './motion-blur-registration';
|
|
180
185
|
import {
|
|
181
186
|
type GenerationAggregate,
|
|
182
187
|
type GenerationPublication,
|
|
@@ -1339,22 +1344,14 @@ async function makeWebGPURenderer(
|
|
|
1339
1344
|
tonemapRegistered = true;
|
|
1340
1345
|
},
|
|
1341
1346
|
(source: string) => {
|
|
1342
|
-
|
|
1343
|
-
renderSystem.registerBuiltinPostProcess(FXAA_POST_PROCESS_ID, {
|
|
1344
|
-
source,
|
|
1345
|
-
params: { byteSize: 16, defaultValue: new Uint8Array(16) },
|
|
1346
|
-
reads: ['ldrColor'],
|
|
1347
|
-
});
|
|
1348
|
-
fxaaRegistered = true;
|
|
1347
|
+
fxaaRegistered = registerFxaaPostProcess(renderSystem, source, fxaaRegistered);
|
|
1349
1348
|
},
|
|
1349
|
+
(source: string) =>
|
|
1350
|
+
registerAnalyticFogPostProcess(renderSystem, source, candidateBuildState !== undefined),
|
|
1350
1351
|
(entries: { readonly motionBlur?: string; readonly taaResolve?: string }) => {
|
|
1351
1352
|
if (candidateBuildState !== undefined || temporalPostProcessesRegistered) return;
|
|
1352
1353
|
if (entries.motionBlur !== undefined) {
|
|
1353
|
-
renderSystem
|
|
1354
|
-
source: entries.motionBlur,
|
|
1355
|
-
params: { byteSize: 16, defaultValue: new Uint8Array(16) },
|
|
1356
|
-
reads: ['scene-color', 'scene-temporal'],
|
|
1357
|
-
});
|
|
1354
|
+
registerMotionBlurPostProcess(renderSystem, entries.motionBlur);
|
|
1358
1355
|
}
|
|
1359
1356
|
if (entries.taaResolve !== undefined) {
|
|
1360
1357
|
renderSystem.registerBuiltinPostProcess('forgeax.taa-resolve', {
|
|
@@ -2718,22 +2715,7 @@ async function makeWebGPURenderer(
|
|
|
2718
2715
|
: 'alive';
|
|
2719
2716
|
const graph = deviceLost ? undefined : renderSystem.perFrameGraphInfo;
|
|
2720
2717
|
const standardLighting = deviceLost ? undefined : renderSystem.standardLightingInspection;
|
|
2721
|
-
const bloom: BloomInspection = deviceLost
|
|
2722
|
-
? {
|
|
2723
|
-
graphStatus: 'empty',
|
|
2724
|
-
enabled: false,
|
|
2725
|
-
targetCount: 0,
|
|
2726
|
-
targetBytes: 0,
|
|
2727
|
-
resourceCount: 0,
|
|
2728
|
-
passCount: 0,
|
|
2729
|
-
encodeCount: 0,
|
|
2730
|
-
bindGroupCount: 0,
|
|
2731
|
-
uploadCount: 0,
|
|
2732
|
-
residentChildBytes: 0,
|
|
2733
|
-
generation: 0,
|
|
2734
|
-
state: 'off',
|
|
2735
|
-
}
|
|
2736
|
-
: renderSystem.bloom;
|
|
2718
|
+
const bloom: BloomInspection = deviceLost ? emptyBloomInspection() : renderSystem.bloom;
|
|
2737
2719
|
const surfaceStorage = pipelineState?.format ?? 'rgba8unorm';
|
|
2738
2720
|
const surfaceDisplay = pipelineState?.colorAttachmentFormat ?? 'rgba8unorm';
|
|
2739
2721
|
const observationId = `${STANDARD_OUTPUT_TRANSFORM_FEATURE_ID}:frame-${frameId}`;
|
|
@@ -3226,6 +3208,7 @@ async function makeWebGPURenderer(
|
|
|
3226
3208
|
...(frameRequest.camera.entityKey === undefined
|
|
3227
3209
|
? {}
|
|
3228
3210
|
: { cameraEntityKey: frameRequest.camera.entityKey }),
|
|
3211
|
+
...motionBlurDrawOptions(frameRequest),
|
|
3229
3212
|
...(frameRequest.profileFrame === undefined
|
|
3230
3213
|
? {}
|
|
3231
3214
|
: { profileFrame: frameRequest.profileFrame }),
|
|
@@ -3377,6 +3360,8 @@ async function makeWebGPURenderer(
|
|
|
3377
3360
|
const cleanupFailure = disposeOwnedObservationCaptures(observationCaptureOwner.drain());
|
|
3378
3361
|
volumeTimingCapture?.discard();
|
|
3379
3362
|
if (cleanupFailure !== undefined) return err(cleanupFailure);
|
|
3363
|
+
const invalidMotionBlur = renderSystem.motionBlurInvalidParams;
|
|
3364
|
+
if (invalidMotionBlur !== undefined) return err(invalidMotionBlur);
|
|
3380
3365
|
return err(
|
|
3381
3366
|
new RendererContractFailureError(
|
|
3382
3367
|
'draw',
|
|
@@ -3715,7 +3700,7 @@ async function makeWebGPURenderer(
|
|
|
3715
3700
|
receiptObservationBuffers.delete(receipt);
|
|
3716
3701
|
}
|
|
3717
3702
|
}
|
|
3718
|
-
await renderSystem
|
|
3703
|
+
await observeLodOcclusionForReceipt(renderSystem, receipt);
|
|
3719
3704
|
const targetReadbacks =
|
|
3720
3705
|
request.targetReadbacks === undefined || request.targetReadbacks.length === 0
|
|
3721
3706
|
? undefined
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BloomEnabled, Tonemap } from './components/camera';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The one effective Standard Bloom admission used by graph, resource, and
|
|
5
|
+
* record owners. Authoring remains Camera-owned; this is only the derived
|
|
6
|
+
* zero/non-zero topology decision.
|
|
7
|
+
*/
|
|
8
|
+
export interface BloomAdmissionCamera {
|
|
9
|
+
readonly bloom: BloomEnabled;
|
|
10
|
+
readonly bloomIntensity: number;
|
|
11
|
+
readonly tonemap: Tonemap;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function standardBloomAdmitted(camera: BloomAdmissionCamera | undefined): boolean {
|
|
15
|
+
return (
|
|
16
|
+
camera !== undefined &&
|
|
17
|
+
camera.bloom === 'on' &&
|
|
18
|
+
camera.bloomIntensity > 0 &&
|
|
19
|
+
camera.tonemap !== 'none'
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -8,6 +8,7 @@ describe('MotionBlur component schema', () => {
|
|
|
8
8
|
shutterAngle: { type: 'f32', default: 180 },
|
|
9
9
|
maxRadiusPixels: { type: 'f32', default: 32 },
|
|
10
10
|
sampleCount: { type: 'f32', default: 8 },
|
|
11
|
+
targetFps: { type: 'f32', default: 60 },
|
|
11
12
|
});
|
|
12
13
|
expect(MotionBlur.fields).not.toHaveProperty('enabled');
|
|
13
14
|
});
|
|
@@ -7,4 +7,8 @@ export const Atmosphere = defineComponent('Atmosphere', {
|
|
|
7
7
|
mieCoefficient: { type: 'f32', default: 0.005 },
|
|
8
8
|
mieDirectionalG: { type: 'f32', default: 0.8 },
|
|
9
9
|
sunAngularRadius: { type: 'f32', default: 0.004675 },
|
|
10
|
+
/** Independent multiplier for the analytic circumsolar lobe. */
|
|
11
|
+
circumsolarStrength: { type: 'f32', default: 1 },
|
|
12
|
+
/** Relative angular width of the analytic circumsolar lobe. */
|
|
13
|
+
circumsolarWidth: { type: 'f32', default: 1 },
|
|
10
14
|
});
|
package/src/components/camera.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
// @forgeax/engine-render - Camera (projection variant + ortho extension).
|
|
2
2
|
//
|
|
3
|
-
// Schema:
|
|
4
|
-
//
|
|
5
|
-
// quartet (left + right + bottom + top). The view matrix continues to be
|
|
6
|
-
// derived from the entity's Transform (AC-06 case B fires
|
|
3
|
+
// Schema: projection, output, Bloom, and lifecycle columns. The view matrix
|
|
4
|
+
// continues to be derived from the entity's Transform (AC-06 case B fires
|
|
7
5
|
// 'render-system-no-camera' when 0 such entities exist).
|
|
8
6
|
//
|
|
9
7
|
// Projection discriminator:
|
|
@@ -386,7 +384,7 @@ export function antialiasFromF32(value: number): Antialias {
|
|
|
386
384
|
*
|
|
387
385
|
* Two members:
|
|
388
386
|
* `'off'` - default; no bloom post-processing (zero-overhead opt-out path)
|
|
389
|
-
* `'on'` -
|
|
387
|
+
* `'on'` - five-level HDR extraction/downsample/tent-upsample pipeline
|
|
390
388
|
*
|
|
391
389
|
* Bloom is a discrete enum (0/1) — illegal values fail-fast with the closed
|
|
392
390
|
* CameraError contract (charter P3), matching antialiasFromF32 precedent.
|
|
@@ -398,6 +396,19 @@ export const BLOOM_DISABLED = 0;
|
|
|
398
396
|
/** Numeric encoding of bloom enabled (schema value for `bloom`). */
|
|
399
397
|
export const BLOOM_ENABLED = 1;
|
|
400
398
|
|
|
399
|
+
/** Bloom intensity is a linear HDR add-back multiplier. */
|
|
400
|
+
export const CAMERA_BLOOM_INTENSITY_MIN = 0;
|
|
401
|
+
export const CAMERA_BLOOM_INTENSITY_MAX = 8;
|
|
402
|
+
/** Bloom threshold is expressed in scene-linear Rec.709 luminance. */
|
|
403
|
+
export const CAMERA_BLOOM_THRESHOLD_MIN = 0;
|
|
404
|
+
export const CAMERA_BLOOM_THRESHOLD_MAX = 65504;
|
|
405
|
+
/** Soft-knee transition width relative to the threshold. */
|
|
406
|
+
export const CAMERA_BLOOM_SOFT_KNEE_MIN = 0;
|
|
407
|
+
export const CAMERA_BLOOM_SOFT_KNEE_MAX = 1;
|
|
408
|
+
/** Coarse pyramid contribution. This is a blend, not a blur radius. */
|
|
409
|
+
export const CAMERA_BLOOM_SCATTER_MIN = 0;
|
|
410
|
+
export const CAMERA_BLOOM_SCATTER_MAX = 0.95;
|
|
411
|
+
|
|
401
412
|
/**
|
|
402
413
|
* Map a `camera.bloom` numeric value to the closed `BloomEnabled` string-literal
|
|
403
414
|
* union. Invalid values fail-fast with structured error (charter P3).
|
|
@@ -414,6 +425,49 @@ export function bloomEnabledFromF32(value: number): BloomEnabled {
|
|
|
414
425
|
);
|
|
415
426
|
}
|
|
416
427
|
|
|
428
|
+
function validateBloomRange(field: string, value: number, min: number, max: number): void {
|
|
429
|
+
if (!Number.isFinite(value) || value < min || value > max) {
|
|
430
|
+
throw cameraError(
|
|
431
|
+
'camera-bloom-invalid',
|
|
432
|
+
field,
|
|
433
|
+
value,
|
|
434
|
+
`a finite value in [${min}, ${max}]`,
|
|
435
|
+
`set Camera.${field} to a finite value in [${min}, ${max}]`,
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** Validate the complete Camera-owned Bloom contract at the extract boundary. */
|
|
441
|
+
export function validateCameraBloom(
|
|
442
|
+
bloom: number,
|
|
443
|
+
threshold: number,
|
|
444
|
+
intensity: number,
|
|
445
|
+
softKnee: number,
|
|
446
|
+
scatter: number,
|
|
447
|
+
): BloomEnabled {
|
|
448
|
+
const enabled = bloomEnabledFromF32(bloom);
|
|
449
|
+
validateBloomRange(
|
|
450
|
+
'bloomThreshold',
|
|
451
|
+
threshold,
|
|
452
|
+
CAMERA_BLOOM_THRESHOLD_MIN,
|
|
453
|
+
CAMERA_BLOOM_THRESHOLD_MAX,
|
|
454
|
+
);
|
|
455
|
+
validateBloomRange(
|
|
456
|
+
'bloomIntensity',
|
|
457
|
+
intensity,
|
|
458
|
+
CAMERA_BLOOM_INTENSITY_MIN,
|
|
459
|
+
CAMERA_BLOOM_INTENSITY_MAX,
|
|
460
|
+
);
|
|
461
|
+
validateBloomRange(
|
|
462
|
+
'bloomSoftKnee',
|
|
463
|
+
softKnee,
|
|
464
|
+
CAMERA_BLOOM_SOFT_KNEE_MIN,
|
|
465
|
+
CAMERA_BLOOM_SOFT_KNEE_MAX,
|
|
466
|
+
);
|
|
467
|
+
validateBloomRange('bloomScatter', scatter, CAMERA_BLOOM_SCATTER_MIN, CAMERA_BLOOM_SCATTER_MAX);
|
|
468
|
+
return enabled;
|
|
469
|
+
}
|
|
470
|
+
|
|
417
471
|
/**
|
|
418
472
|
* Camera projection parameters (perspective + orthographic variants) +
|
|
419
473
|
* tonemap trio (mode + exposure + whitePoint).
|
|
@@ -447,8 +501,9 @@ export function bloomEnabledFromF32(value: number): BloomEnabled {
|
|
|
447
501
|
* N>1 fires 'render-system-multi-camera' + uses first). Multi-viewport is
|
|
448
502
|
* OOS (see feat-future-multi-viewport). The orthographic path reuses the
|
|
449
503
|
* same near / far as the perspective path — both variants share the single
|
|
450
|
-
* Camera archetype (
|
|
451
|
-
* the `clearColor` array<f32,4> column
|
|
504
|
+
* Camera archetype (30 schema fields, including the Bloom controls, one
|
|
505
|
+
* historyVersion u32 column, the `clearColor` array<f32,4> column, and the
|
|
506
|
+
* `autoAspect` bool column).
|
|
452
507
|
*
|
|
453
508
|
* @example Perspective camera at (0, 0, 3) looking down -Z (zero-config tonemap):
|
|
454
509
|
* world.spawn(
|
|
@@ -514,7 +569,8 @@ export const Camera = defineComponent('Camera', {
|
|
|
514
569
|
bloom: { type: 'f32', default: 0 },
|
|
515
570
|
bloomThreshold: { type: 'f32', default: 1.0 },
|
|
516
571
|
bloomIntensity: { type: 'f32', default: 1.0 },
|
|
517
|
-
|
|
572
|
+
bloomSoftKnee: { type: 'f32', default: 0.5 },
|
|
573
|
+
bloomScatter: { type: 'f32', default: 0.7 },
|
|
518
574
|
// M1 target camera role: zero means this camera is eligible for display;
|
|
519
575
|
// non-zero shared targets are auxiliary producers whose views and receipt
|
|
520
576
|
// promotion remain owned by Renderer.
|
|
@@ -544,7 +600,7 @@ export const Camera = defineComponent('Camera', {
|
|
|
544
600
|
|
|
545
601
|
// ─── Camera POD type (derived from Camera token — single source, AC-07) ─────
|
|
546
602
|
//
|
|
547
|
-
// ShapeOf<SchemaOf<typeof Camera>> resolves the
|
|
603
|
+
// ShapeOf<SchemaOf<typeof Camera>> resolves the 30-field POD from the Camera
|
|
548
604
|
// token's schema, which is itself derived from Camera.fields[k].type (D-A7).
|
|
549
605
|
// This replaces the hand-maintained CameraDataPod interface — the field set
|
|
550
606
|
// lives exclusively in the Camera component definition above.
|
|
@@ -626,7 +682,7 @@ function cameraColorGradingColumns(
|
|
|
626
682
|
|
|
627
683
|
// ─── Camera factory functions (w13 SSOT refactoring) ─────────────────────
|
|
628
684
|
//
|
|
629
|
-
// Standalone factory functions that return
|
|
685
|
+
// Standalone factory functions that return 29-field CameraPod objects
|
|
630
686
|
// matching the Camera component column shape. Not static methods because
|
|
631
687
|
// TypeScript const-namespace merge is not supported, and Object.assign
|
|
632
688
|
// would break the Camera token's reference identity (archetype columns /
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
//
|
|
11
11
|
// AI users spawn with the assets-runtime constants `HANDLE_CUBE` /
|
|
12
12
|
// `HANDLE_TRIANGLE` (now branded `Handle<'MeshAsset','shared'>` to
|
|
13
|
-
// match the schema-derived shape)
|
|
14
|
-
//
|
|
13
|
+
// match the schema-derived shape). A loaded or custom MeshAsset payload is
|
|
14
|
+
// bound by allocating a World shared reference before publishing this field.
|
|
15
15
|
//
|
|
16
16
|
// Naming flavor: unity-style "MeshFilter / MeshRenderer" pair, but the forgeax
|
|
17
17
|
// pair does NOT mirror Unity's filter-toggle semantics - MeshFilter only
|
|
@@ -22,12 +22,13 @@
|
|
|
22
22
|
// query never matches -> entity is silently absent from the
|
|
23
23
|
// RenderableSnapshot[] (NO default-material fallback, NO onError fire);
|
|
24
24
|
// case B (MeshRenderer.material omitted at spawn) -> mid-grey
|
|
25
|
-
// defaultMaterialSnapshot fallback (no onError);
|
|
25
|
+
// defaultMaterialSnapshot fallback (no onError); an unbound scalar shared<T>
|
|
26
|
+
// field (slot 0) is also not a renderable; case C (non-zero material handle
|
|
26
27
|
// unresolved) -> RhiError 'asset-not-registered' (mirrors the
|
|
27
28
|
// MeshFilter.assetHandle dangling-ref path).
|
|
28
29
|
//
|
|
29
30
|
// charter mapping: proposition 1 (single import) + proposition 4 (explicit
|
|
30
|
-
// failure:
|
|
31
|
+
// failure: a non-zero unregistered handle fires onError 'asset-not-registered'
|
|
31
32
|
// with .detail = { assetHandle } + cross-asset brand mismatch is a TS
|
|
32
33
|
// compile-time error) + proposition 5 (consistent abstraction: the schema
|
|
33
34
|
// vocab `'shared<T>'` is the SSOT for AssetRegistry-owned handles across
|
|
@@ -39,12 +40,13 @@ import { defineComponent } from '@forgeax/engine-ecs';
|
|
|
39
40
|
* Mesh filter (geometry asset reference).
|
|
40
41
|
*
|
|
41
42
|
* `assetHandle` carries a `Handle<'MeshAsset', 'shared'>` (u32-stored)
|
|
42
|
-
* pointing
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
43
|
+
* pointing to a mesh payload in the current World shared-reference / builtin
|
|
44
|
+
* handle namespace. An omitted field defaults to slot `0`, the unbound
|
|
45
|
+
* sentinel; an entity with that value is not emitted as a renderable. Use the
|
|
46
|
+
* predefined constants `HANDLE_CUBE` / `HANDLE_TRIANGLE` for builtins and
|
|
47
|
+
* allocate a World shared reference for a loaded mesh payload.
|
|
46
48
|
*
|
|
47
|
-
* Error path: if
|
|
49
|
+
* Error path: if a non-zero handle is not registered at draw time, RenderSystem
|
|
48
50
|
* fires `Renderer.onError` with
|
|
49
51
|
* `RhiError({ code: 'asset-not-registered', detail: { assetHandle } })`
|
|
50
52
|
* and skips this entity (other entities continue rendering; charter
|
|
@@ -75,7 +75,6 @@ import {
|
|
|
75
75
|
type TextureAsset,
|
|
76
76
|
type TextureShape,
|
|
77
77
|
type MeshAsset as TypesMeshAsset,
|
|
78
|
-
toShared,
|
|
79
78
|
} from '@forgeax/engine-types';
|
|
80
79
|
import { GpuBuffer, GpuTexture } from '../gpu-resource';
|
|
81
80
|
import {
|
|
@@ -570,6 +569,12 @@ export class GpuResidencyCache {
|
|
|
570
569
|
// equirect source always resolves to the same cubemap (idempotent, A2).
|
|
571
570
|
private readonly cubemapIdempotentMap: Map<number, Handle<'EquirectAsset', 'shared'>> = new Map();
|
|
572
571
|
private readonly meshGpuHandles: Map<string | number, MeshGpuHandles> = new Map();
|
|
572
|
+
/**
|
|
573
|
+
* Lower-detail POD identities packed into each resident mesh entry. The
|
|
574
|
+
* GPU entry only carries byte ranges; keeping the source identities beside
|
|
575
|
+
* it lets a later draw upgrade a root-only upload without hashing vertices.
|
|
576
|
+
*/
|
|
577
|
+
private readonly meshLodAssets = new WeakMap<MeshGpuHandles, readonly TypesMeshAsset[]>();
|
|
573
578
|
private readonly meshLifetimes = new WeakMap<MeshGpuHandles, MeshResidencyLifetime>();
|
|
574
579
|
private readonly retiredMeshes = new Set<MeshGpuHandles>();
|
|
575
580
|
|
|
@@ -832,7 +837,7 @@ export class GpuResidencyCache {
|
|
|
832
837
|
handle: Handle<'MeshAsset', 'shared'>,
|
|
833
838
|
worldId: World | number = 0,
|
|
834
839
|
): { freed: number; errors: RhiError[] } {
|
|
835
|
-
const id = this.worldKey(
|
|
840
|
+
const id = this.worldKey(Number(handle), worldId);
|
|
836
841
|
const entry = this.meshGpuHandles.get(id);
|
|
837
842
|
if (entry === undefined) return { freed: 0, errors: [] };
|
|
838
843
|
|
|
@@ -1090,7 +1095,7 @@ export class GpuResidencyCache {
|
|
|
1090
1095
|
handle: Handle<'MeshAsset', 'shared'>,
|
|
1091
1096
|
worldId: World | number = 0,
|
|
1092
1097
|
): MeshGpuHandles | undefined {
|
|
1093
|
-
return this.meshGpuHandles.get(this.worldKey(
|
|
1098
|
+
return this.meshGpuHandles.get(this.worldKey(Number(handle), worldId));
|
|
1094
1099
|
}
|
|
1095
1100
|
|
|
1096
1101
|
/** Candidate leases retain the concrete allocation even after cache invalidation. */
|
|
@@ -1098,7 +1103,7 @@ export class GpuResidencyCache {
|
|
|
1098
1103
|
handle: Handle<'MeshAsset', 'shared'>,
|
|
1099
1104
|
worldId: World | number = 0,
|
|
1100
1105
|
): MeshResidencyLease | undefined {
|
|
1101
|
-
const key = this.worldKey(
|
|
1106
|
+
const key = this.worldKey(Number(handle), worldId);
|
|
1102
1107
|
const entry = this.meshGpuHandles.get(key);
|
|
1103
1108
|
return entry === undefined
|
|
1104
1109
|
? undefined
|
|
@@ -1110,7 +1115,7 @@ export class GpuResidencyCache {
|
|
|
1110
1115
|
|
|
1111
1116
|
/** In-place author changes immediately miss the cache; old submissions keep their allocation. */
|
|
1112
1117
|
invalidateMesh(handle: number, worldId: World | number = 0): void {
|
|
1113
|
-
const key = this.worldKey(
|
|
1118
|
+
const key = this.worldKey(handle, worldId);
|
|
1114
1119
|
const entry = this.meshGpuHandles.get(key);
|
|
1115
1120
|
if (entry !== undefined) this.retireMeshEntry(key, entry);
|
|
1116
1121
|
}
|
|
@@ -1156,9 +1161,32 @@ export class GpuResidencyCache {
|
|
|
1156
1161
|
// path, not routed here).
|
|
1157
1162
|
switch (pod.kind) {
|
|
1158
1163
|
case 'mesh': {
|
|
1159
|
-
|
|
1164
|
+
// Recycled shared slots can coexist with an older GPU submission.
|
|
1165
|
+
// The generation is part of mesh residency identity, not only its slot.
|
|
1166
|
+
const key = this.worldKey(Number(handle), worldId);
|
|
1160
1167
|
const existing = this.meshGpuHandles.get(key);
|
|
1161
|
-
if (existing !== undefined)
|
|
1168
|
+
if (existing !== undefined) {
|
|
1169
|
+
// A root mesh can become resident before its lower-detail assets are
|
|
1170
|
+
// available. Preserve the fast hit for the common case, but publish
|
|
1171
|
+
// a complete chain once the caller supplies one. Never downgrade a
|
|
1172
|
+
// resident chain when a later call has no lower assets yet.
|
|
1173
|
+
if (lodMeshes.length === 0) return ok(existing);
|
|
1174
|
+
const previousLodAssets = this.meshLodAssets.get(existing);
|
|
1175
|
+
const sameLodChain =
|
|
1176
|
+
previousLodAssets !== undefined &&
|
|
1177
|
+
previousLodAssets.length === lodMeshes.length &&
|
|
1178
|
+
previousLodAssets.every((asset, index) => asset === lodMeshes[index]);
|
|
1179
|
+
if (sameLodChain) return ok(existing);
|
|
1180
|
+
|
|
1181
|
+
const projected = deriveRenderDataMesh(pod);
|
|
1182
|
+
if (!projected.ok) return projected;
|
|
1183
|
+
const upgraded = this.uploadMeshById(key, pod, projected.value, lodMeshes);
|
|
1184
|
+
if (!upgraded.ok) return upgraded;
|
|
1185
|
+
// uploadMeshById publishes the new entry before this call so the
|
|
1186
|
+
// old allocation stays available to any already-recorded frame.
|
|
1187
|
+
this.retireMeshEntry(key, existing);
|
|
1188
|
+
return upgraded;
|
|
1189
|
+
}
|
|
1162
1190
|
const projected = deriveRenderDataMesh(pod);
|
|
1163
1191
|
if (!projected.ok) return projected;
|
|
1164
1192
|
return this.uploadMeshById(key, pod, projected.value, lodMeshes);
|
|
@@ -1204,7 +1232,7 @@ export class GpuResidencyCache {
|
|
|
1204
1232
|
): Result<RecoveryResidencyPreparation, RhiError | AssetError | ImageError> {
|
|
1205
1233
|
const id = handleSlot(handle);
|
|
1206
1234
|
if (pod.kind === 'mesh') {
|
|
1207
|
-
const key = this.worldKey(
|
|
1235
|
+
const key = this.worldKey(Number(handle), worldId);
|
|
1208
1236
|
if (this.meshGpuHandles.has(key)) return ok({});
|
|
1209
1237
|
const projected = deriveRenderDataMesh(pod);
|
|
1210
1238
|
if (!projected.ok) return projected;
|
|
@@ -2035,6 +2063,10 @@ export class GpuResidencyCache {
|
|
|
2035
2063
|
// Vertex-only mesh: skip the index buffer entirely. The indexed path below
|
|
2036
2064
|
// is unchanged byte-for-byte when `indices` is present.
|
|
2037
2065
|
let ibo: Buffer | null = null;
|
|
2066
|
+
const cleanupUpload = (): void => {
|
|
2067
|
+
if (ibo !== null) device.destroyBuffer(ibo);
|
|
2068
|
+
device.destroyBuffer(vbo);
|
|
2069
|
+
};
|
|
2038
2070
|
if (indices !== undefined) {
|
|
2039
2071
|
const iboResult = device.createBuffer({
|
|
2040
2072
|
label: `mesh-${id}-ibo`,
|
|
@@ -2042,17 +2074,24 @@ export class GpuResidencyCache {
|
|
|
2042
2074
|
usage: renderData.indexUsage,
|
|
2043
2075
|
mappedAtCreation: false,
|
|
2044
2076
|
});
|
|
2045
|
-
if (!iboResult.ok)
|
|
2077
|
+
if (!iboResult.ok) {
|
|
2078
|
+
cleanupUpload();
|
|
2079
|
+
return iboResult;
|
|
2080
|
+
}
|
|
2046
2081
|
ibo = iboResult.value;
|
|
2047
2082
|
}
|
|
2048
2083
|
|
|
2049
2084
|
const vboWriteResult = device.queue.writeBuffer(vbo, 0, vertices);
|
|
2050
|
-
if (!vboWriteResult.ok)
|
|
2085
|
+
if (!vboWriteResult.ok) {
|
|
2086
|
+
cleanupUpload();
|
|
2087
|
+
return err(vboWriteResult.error);
|
|
2088
|
+
}
|
|
2051
2089
|
|
|
2052
2090
|
if (indices !== undefined) {
|
|
2053
2091
|
const indexSrc = new Uint8Array(indexBytes);
|
|
2054
2092
|
indexSrc.set(new Uint8Array(indices.buffer, indices.byteOffset, indexBytesUnpadded));
|
|
2055
|
-
if (ibo === null)
|
|
2093
|
+
if (ibo === null) {
|
|
2094
|
+
cleanupUpload();
|
|
2056
2095
|
return err(
|
|
2057
2096
|
new RhiError({
|
|
2058
2097
|
code: 'webgpu-runtime-error',
|
|
@@ -2060,8 +2099,12 @@ export class GpuResidencyCache {
|
|
|
2060
2099
|
hint: 'the indexed mesh upload lost its RHI index-buffer owner',
|
|
2061
2100
|
}),
|
|
2062
2101
|
);
|
|
2102
|
+
}
|
|
2063
2103
|
const iboWriteResult = device.queue.writeBuffer(ibo, 0, indexSrc);
|
|
2064
|
-
if (!iboWriteResult.ok)
|
|
2104
|
+
if (!iboWriteResult.ok) {
|
|
2105
|
+
cleanupUpload();
|
|
2106
|
+
return err(iboWriteResult.error);
|
|
2107
|
+
}
|
|
2065
2108
|
}
|
|
2066
2109
|
|
|
2067
2110
|
// The geometry projection already contains every packed attribute,
|
|
@@ -2085,6 +2128,7 @@ export class GpuResidencyCache {
|
|
|
2085
2128
|
...(lodRanges === undefined ? {} : { lodRanges }),
|
|
2086
2129
|
};
|
|
2087
2130
|
this.meshGpuHandles.set(id, entry);
|
|
2131
|
+
this.meshLodAssets.set(entry, lodMeshes);
|
|
2088
2132
|
this.meshResidencyEpoch += 1;
|
|
2089
2133
|
return ok(entry);
|
|
2090
2134
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { mat4 } from '@forgeax/engine-math';
|
|
2
|
+
import type { FogFrame } from '../extract/environment';
|
|
3
|
+
import { computeProjectionMatrix, computeViewMatrix } from '../record/helpers';
|
|
4
|
+
import type { CameraSnapshot } from '../render-contract';
|
|
5
|
+
|
|
6
|
+
/** Numerical reference for the shader's closed-form height-density integral. */
|
|
7
|
+
export function analyticFogOpacity(
|
|
8
|
+
fog: Pick<FogFrame, 'density' | 'heightFalloff' | 'maxOpacity'>,
|
|
9
|
+
originY: number,
|
|
10
|
+
endY: number,
|
|
11
|
+
distance: number,
|
|
12
|
+
): number {
|
|
13
|
+
const rho = fog.density * Math.exp(Math.max(-60, Math.min(60, -fog.heightFalloff * originY)));
|
|
14
|
+
const t = Math.max(-60, Math.min(60, fog.heightFalloff * (endY - originY)));
|
|
15
|
+
const integral = Math.abs(t) < 0.001 ? 1 - t / 2 + (t * t) / 6 : -Math.expm1(-t) / t;
|
|
16
|
+
return fog.maxOpacity * -Math.expm1(-Math.min(80, Math.max(0, rho * distance * integral)));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function encodeAnalyticFogParams(fog: FogFrame, camera: CameraSnapshot): Uint8Array {
|
|
20
|
+
const projection = computeProjectionMatrix(camera);
|
|
21
|
+
// Match the scene depth's jittered projection before temporal resolve.
|
|
22
|
+
const jitter = camera.temporal?.currentJitterUv;
|
|
23
|
+
if (jitter !== undefined) {
|
|
24
|
+
for (let column = 0; column < 4; column++) {
|
|
25
|
+
projection[column * 4] =
|
|
26
|
+
(projection[column * 4] ?? 0) + jitter[0] * 2 * (projection[column * 4 + 3] ?? 0);
|
|
27
|
+
projection[column * 4 + 1] =
|
|
28
|
+
(projection[column * 4 + 1] ?? 0) - jitter[1] * 2 * (projection[column * 4 + 3] ?? 0);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const inverse = mat4.invert(
|
|
32
|
+
mat4.create(),
|
|
33
|
+
mat4.multiply(mat4.create(), projection, computeViewMatrix(camera)),
|
|
34
|
+
);
|
|
35
|
+
const data = new Float32Array(28);
|
|
36
|
+
data.set(inverse, 0);
|
|
37
|
+
data.set(camera.position, 16);
|
|
38
|
+
data.set([...fog.color, fog.density], 20);
|
|
39
|
+
data.set(
|
|
40
|
+
[fog.heightFalloff, fog.maxOpacity, camera.near, camera.projection === 'orthographic' ? 1 : 0],
|
|
41
|
+
24,
|
|
42
|
+
);
|
|
43
|
+
return new Uint8Array(data.buffer);
|
|
44
|
+
}
|
|
@@ -88,8 +88,8 @@ export function addAtmosphereBackground(
|
|
|
88
88
|
settings.mieDirectionalG,
|
|
89
89
|
settings.sunAngularRadius,
|
|
90
90
|
settings.sunAngularRadius > 0 ? 1 : 0,
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
settings.circumsolarStrength,
|
|
92
|
+
settings.circumsolarWidth,
|
|
93
93
|
]);
|
|
94
94
|
frame.runtime.device.queue
|
|
95
95
|
.writeBuffer(resources.buffer(params.value).unwrap(), 0, values)
|
package/src/errors/render.ts
CHANGED
|
@@ -19,6 +19,8 @@ import type {
|
|
|
19
19
|
SurfaceFailureKind,
|
|
20
20
|
} from '@forgeax/engine-render-graph';
|
|
21
21
|
import type { DeviceResourceKind, DeviceScopeReceipt } from '../device/resource-types';
|
|
22
|
+
import type { SunCardinalityError } from '../environment/frame';
|
|
23
|
+
import type { MotionBlurValidationError } from '../features/motion-blur/motion-blur-params';
|
|
22
24
|
import type {
|
|
23
25
|
PreparedKind,
|
|
24
26
|
RenderFeatureCapabilityKey,
|
|
@@ -783,6 +785,8 @@ export interface RenderFeatureStageFailedDetail {
|
|
|
783
785
|
readonly stage: RenderFeatureStage;
|
|
784
786
|
readonly recovery: RenderFeatureRecovery;
|
|
785
787
|
readonly cleanupFailures?: readonly RenderFeatureCleanupFailure[];
|
|
788
|
+
/** Original stage exception when the feature did not return a RenderError. */
|
|
789
|
+
readonly cause?: unknown;
|
|
786
790
|
}
|
|
787
791
|
|
|
788
792
|
export interface RenderFeatureCapabilityMissingDetail {
|
|
@@ -947,6 +951,7 @@ export class RenderFeatureStageFailedError extends Error {
|
|
|
947
951
|
order: number,
|
|
948
952
|
stage: RenderFeatureStage,
|
|
949
953
|
recovery: RenderFeatureRecovery,
|
|
954
|
+
cause?: unknown,
|
|
950
955
|
) {
|
|
951
956
|
const expected = `feature '${featureIdentity}' completes its ${stage} stage without an error`;
|
|
952
957
|
const hint = renderFeatureRecoveryHintByRecovery[recovery](featureIdentity, stage);
|
|
@@ -954,7 +959,13 @@ export class RenderFeatureStageFailedError extends Error {
|
|
|
954
959
|
this.name = 'RenderFeatureStageFailedError';
|
|
955
960
|
this.expected = expected;
|
|
956
961
|
this.hint = hint;
|
|
957
|
-
this.detail = {
|
|
962
|
+
this.detail = {
|
|
963
|
+
featureIdentity,
|
|
964
|
+
order,
|
|
965
|
+
stage,
|
|
966
|
+
recovery,
|
|
967
|
+
...(cause === undefined ? {} : { cause }),
|
|
968
|
+
};
|
|
958
969
|
}
|
|
959
970
|
}
|
|
960
971
|
|
|
@@ -1966,12 +1977,14 @@ export type RenderError =
|
|
|
1966
1977
|
| RenderFeatureDrawRecordingFailedError
|
|
1967
1978
|
| EnvironmentSourceConflictError
|
|
1968
1979
|
| FogCardinalityError
|
|
1980
|
+
| SunCardinalityError
|
|
1969
1981
|
| TaaCapsInsufficientError
|
|
1970
1982
|
| EnvironmentGenerationFailedError
|
|
1971
1983
|
| AtmosphereInvalidParameterError
|
|
1972
1984
|
| DynamicResolutionInvalidParameterError
|
|
1973
1985
|
| DynamicResolutionRequiresTaaError
|
|
1974
1986
|
| DynamicResolutionTimingUnavailableError
|
|
1987
|
+
| MotionBlurValidationError
|
|
1975
1988
|
| OwnerStageFailedError
|
|
1976
1989
|
| PointsLinesInvalidStyleError
|
|
1977
1990
|
| PointsLinesTopologyMismatchError
|