@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
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { inspectVolumetricFog, resolveSsrAdmissionGeneration, inspectLodOcclusion, zeroSsrAdmissionWork, projectSsrSpatialInspection, createDynamicGeometryLifecycle, inspectReflectionFallback, admitReflectionProbe, DEFAULT_REFLECTION_PROBE_LIMITS, buildReflectionProbeTable, advanceProbeFilter, commitProbeFilterStep, resolveReflectionFallbackSource, deriveReflectionFallbackProjection, reflectionFallbackCoverage, reflectionFallbackSourceKey, reflectionFallbackProjectionSignature, createProbeFilterState, ReflectionProbeProjection, admitPointsLines, projectSsrDependencies, admitSsrSpatial, inspectPointShadow, DynamicGeometryError, sameCandidateCredential } from './chunk-
|
|
2
|
-
import { createExtendedLightingState, promoteExtendedLightingCandidate, glyphTextLayoutSystem, projectExtendedLightingInspection, OcclusionRenderRuntime, extractFrames, viewKey, primitiveKey, viewKeyId, primitiveKeyId, ProbeBlendSceneProjection,
|
|
1
|
+
import { inspectVolumetricFog, resolveSsrAdmissionGeneration, inspectLodOcclusion, zeroSsrAdmissionWork, projectSsrSpatialInspection, createDynamicGeometryLifecycle, inspectReflectionFallback, admitReflectionProbe, DEFAULT_REFLECTION_PROBE_LIMITS, buildReflectionProbeTable, advanceProbeFilter, commitProbeFilterStep, resolveReflectionFallbackSource, deriveReflectionFallbackProjection, reflectionFallbackCoverage, reflectionFallbackSourceKey, reflectionFallbackProjectionSignature, createProbeFilterState, ReflectionProbeProjection, admitPointsLines, projectSsrDependencies, admitSsrSpatial, inspectPointShadow, DynamicGeometryError, sameCandidateCredential } from './chunk-NNQL7CB3.mjs';
|
|
2
|
+
import { createExtendedLightingState, promoteExtendedLightingCandidate, glyphTextLayoutSystem, emptyBloomInspection, projectExtendedLightingInspection, OcclusionRenderRuntime, extractFrames, viewKey, primitiveKey, viewKeyId, primitiveKeyId, ProbeBlendSceneProjection, InstanceBoundsCache, fingerprintNumericArray, buildShadowFrusta, createSsrHistoryOwner, createSsrSpatialResources, createCookieProjectionMatrixData } from './chunk-OO3DRYK7.mjs';
|
|
3
3
|
import './chunk-ILQ5MEZF.mjs';
|
|
4
|
-
import { DEFAULT_DEPTH_OF_FIELD_PARAMS, MeshFilter, MeshRenderer, Instances, Layer, Visibility, Points, Lines, SortKey, projectMeshMaterialBindingObservation, resolveDepthOfFieldFrameParams, buildCubeCameraFaceViews, hasVolumetricFogCapability, packDepthOfFieldParams, Camera, CAMERA_PROJECTION_ORTHOGRAPHIC, CUBE_CAMERA_FACE_ORDER } from './chunk-
|
|
4
|
+
import { DEFAULT_DEPTH_OF_FIELD_PARAMS, MeshFilter, MeshRenderer, Instances, Layer, Visibility, Points, Lines, SortKey, projectMeshMaterialBindingObservation, resolveDepthOfFieldFrameParams, buildCubeCameraFaceViews, SKYLIGHT_RECOVERY_FALLBACK, hasVolumetricFogCapability, packDepthOfFieldParams, Camera, CAMERA_PROJECTION_ORTHOGRAPHIC, CUBE_CAMERA_FACE_ORDER } from './chunk-PLAXOOX5.mjs';
|
|
5
5
|
import { SpriteInstances, SpriteRegionOverride, TileLayer, decodeSortScope, Tilemap } from './chunk-PA5DRJNI.mjs';
|
|
6
|
-
import { GPU_SCENE_LAYOUTS, deviceOptionsForAdapter, PipelineSpecError, assertStorageBufferCap, STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES, GpuBuffer, GpuTexture, deriveRenderDataTexture, deriveRenderDataMesh, deriveRenderDataCubemap, createIblPipelines, CUBEMAP_FACE_VERTICES, runIblPrecompute, createClusterBinScratch, resetHdrpBuffers, retireTemporalGpuState, PostProcessError, entryHasDepthRead, DEPTH_MIN_PARAMS_BYTE_SIZE, postProcessShaderModuleLabel, resolveSsaoParameters, buildPerFrameBindGroups, resolveMaterialSkylight, postProcessShaderEntrySignature, selectSwapChainFormat, setIblComposedShaders, POINTS_LINES_MATERIAL_SHADER_ID, selectStandardPbrTransmissionPrewarmVariants, selectHdrpPbrPrewarmVariants, prewarmMaterialShaderVariants, selectProbePrewarmVariants, createHdrpClusterMembershipBindGroupLayoutDescriptor, createSkylightFallback, buildSpecConstTable, buildLinearLdrMaterialSpecTable, getOrBuildPipeline, cacheKeyOf, configureSurface, CAPTURE_VIEW_PROJS, PREFILTER_MIP_LEVELS, REFLECTION_PROBE_VIEW_SLOT_BASE, VIEW_UNIFORM_SLOT_STRIDE, getOrCreateIblCache, decideVisibility, BatchTopology, postProcessShaderPipelineLabel, createPointsLinesLaneAdapter, computeViewMatrix, computeProjectionMatrix, prepareFrameLighting, warnMultiSkylight, warnMultiSkybox, selectLazyEquirectHandle, driveLazyEquirectProjection, warnZeroLightStandard, writeShadowParamsBuffer, deriveRenderExtent, writeSpotModifierTextures, writeHdrpClusterAndSsaoBuffers, resolveSkyboxActive, computeSplitLdrSprite, writeViewUbo, writePointsLinesViewUbo, writeShadowCasterUniforms, uploadMeshSsboBatch, passKindPolicyTable, colorFormatsForPassKind, GPU_DRIVEN_VIEW_WGSL, selectGpuDrivenSceneIndexVariant, selectSkinPrewarmVariants, transmissionBackdropAvailable,
|
|
6
|
+
import { GPU_SCENE_LAYOUTS, deviceOptionsForAdapter, PipelineSpecError, assertStorageBufferCap, STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES, GpuBuffer, GpuTexture, deriveRenderDataTexture, deriveRenderDataMesh, deriveRenderDataCubemap, createIblPipelines, CUBEMAP_FACE_VERTICES, runIblPrecompute, createClusterBinScratch, resetHdrpBuffers, retireTemporalGpuState, PostProcessError, entryHasDepthRead, DEPTH_MIN_PARAMS_BYTE_SIZE, postProcessShaderModuleLabel, resolveSsaoParameters, buildPerFrameBindGroups, resolveMaterialSkylight, postProcessShaderEntrySignature, standardBloomAdmitted, selectSwapChainFormat, setIblComposedShaders, POINTS_LINES_MATERIAL_SHADER_ID, selectStandardPbrTransmissionPrewarmVariants, selectHdrpPbrPrewarmVariants, prewarmMaterialShaderVariants, selectProbePrewarmVariants, createHdrpClusterMembershipBindGroupLayoutDescriptor, createSkylightFallback, buildSpecConstTable, buildLinearLdrMaterialSpecTable, getOrBuildPipeline, cacheKeyOf, HDR_COLOR_ATTACHMENT_FORMAT, configureSurface, CAPTURE_VIEW_PROJS, PREFILTER_MIP_LEVELS, REFLECTION_PROBE_VIEW_SLOT_BASE, VIEW_UNIFORM_SLOT_STRIDE, getOrCreateIblCache, decideVisibility, BatchTopology, postProcessShaderPipelineLabel, createPointsLinesLaneAdapter, computeViewMatrix, computeProjectionMatrix, prepareFrameLighting, warnMultiSkylight, warnMultiSkybox, selectLazyEquirectHandle, driveLazyEquirectProjection, warnZeroLightStandard, writeShadowParamsBuffer, deriveRenderExtent, writeSpotModifierTextures, writeHdrpClusterAndSsaoBuffers, resolveSkyboxActive, computeSplitLdrSprite, writeViewUbo, writePointsLinesViewUbo, writeShadowCasterUniforms, uploadMeshSsboBatch, passKindPolicyTable, colorFormatsForPassKind, GPU_DRIVEN_VIEW_WGSL, selectGpuDrivenSceneIndexVariant, selectSkinPrewarmVariants, transmissionBackdropAvailable, VIEW_UBO_BYTES, POINTS_LINES_VIEW_BUFFER_SIZE, SHADOW_CASTER_BUFFER_SIZE, FALLBACK_BYTES_PER_ROW, MIPMAP_PREWARM_FORMATS, getIblProbeBackgroundSource, OcclusionConfidenceScheduler, shouldIssueRetest, selectGpuLodLane, ShadowViewStatePool, GpuDrivenView, MATERIAL_PER_ENTITY_STRIDE, shadowViewIdentityKey, createPointsLinesLaneContract, foldDispatchBuckets, CUBE_CAPTURE_VIEW_SLOT_BASE, variantSetFromDefines, resolveReflectionProbeBinding, cleanPerEntityCache, ensureMeshSsboCapacity, buildFoldDispatchPlan, evaluateFoldBucketUniformCap, incrementFoldedDrawsMetric, writePbrMaterialUboPayload, applyParamSnapshotToUbo, applyMaterialTextureUvScales, residentTextureView, detectNineSliceScaleTooSmall, DEPTH_TEXTURE_FORMAT, PREFILTER_SIZE, gpuSceneFieldOffset, projectedHeightForCandidate, geometryRenderStateForTopology, packInstanceStorageBuffer, MESH_SSBO_BYTES, buildFullscreenPostProcessPass, getTemporalGpuState, getTemporalParamsBuffer, getTemporalBindGroupResources, TEMPORAL_HISTORY_FORMATS, resolveSurfaceFormatPair, resolveSurfaceProfile, createRenderPipelineTarget, encodeMainPass, STANDARD_PBR_UBO_SIZE, abortTemporalGpuSubmit, commitTemporalGpuSubmit, retireTemporalGpuStateAfterFence, hasPendingTemporalGpuSubmit, importRenderPipelineSurface, renderExtentSize, DEFERRED_COLOR_FORMATS, aggregateTemporalDemand, standardTemporalLaneAdmission, addTypedScenePass, addTypedSsaoPasses, addTypedSkyboxPass, createStandardSceneDataTarget, addStandardSceneDataPass, VIEW_UNIFORM_BYTES, addTypedFrameObservationPass, addReflectionFallbackObservationPass, selectLod, temporalReadIndex, temporalWriteIndex, prepareMaterialSkylight, BYTES_PER_DIRECT_LIGHT_SLOT, shadowViewRasterAccesses, encodeDirectionalShadowPass, encodePointShadowPass, encodeSpotShadowPass, getOrCreateFromChain, resolveOutputDither, addTypedFullscreenPass, addTypedTemporalResolvePass, addTypedBloomPasses, addTypedOutputTransformPass, addTypedCompositePostEffects, standardTopologyVariantSet, geometryRenderStateForPass, standardStorageVariantSet, getOrCreateHdrpBuffers, variantSetFromVertexLayoutProjection, createStandardSurfaceLightingBindGroup, BLOOM_DOWNSAMPLE_PARAMS_BYTES, BLOOM_UPSAMPLE_PARAMS_BYTES, BLOOM_COMPOSITE_PARAMS_BYTES, isTemporalFullscreenBinding, createFullscreenBindGroup, applyParamSchemaDefaultsToUbo, defaultViewForUserRegionField, assembleMaterialWithSkylightEntries, TYPED_BLOOM_PASS_NAMES } from './chunk-VOZWDM3L.mjs';
|
|
7
7
|
import { getTransparentSortConfig, TRANSPARENT_SORT_MODE_DISTANCE, TRANSPARENT_SORT_MODE_LAYER_Z, TRANSPARENT_SORT_MODE_LAYER_Y, TRANSPARENT_SORT_MODE_LAYER_YZ } from './chunk-RXZ3RGOU.mjs';
|
|
8
|
-
import { DEFAULT_STANDARD_PROFILE, DEFAULT_CLUSTER_GRID, STANDARD_PIPELINE_ID, STANDARD_LIGHT_COUNTS, STANDARD_POST_STAGE_NAMES, createSceneDataCatalog, resolveVolumetricFogProfile,
|
|
8
|
+
import { DEFAULT_STANDARD_PROFILE, DEFAULT_CLUSTER_GRID, STANDARD_PIPELINE_ID, STANDARD_LIGHT_COUNTS, STANDARD_POST_STAGE_NAMES, createSceneDataCatalog, isSceneDataTarget, resolveVolumetricFogProfile, SCENE_DATA_TEMPORAL_V1_SCHEMA } from './chunk-LEAXOMYJ.mjs';
|
|
9
9
|
import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './chunk-BLXGONR2.mjs';
|
|
10
|
-
import { DeviceScope, SKIN_MATERIAL_SHADER_ID, EXTENDED_LIGHTING_TOPOLOGY, getTextureIdentity, GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_MAP_READ, resolveRenderTargetMaterialSource, createRenderTargetMaterialSource, deriveExtendedLightingCapability, GPU_BUFFER_USAGE_VERTEX, GPU_BUFFER_USAGE_INDEX, IES_SLICE_WIDTH, IES_SLICE_HEIGHT, COOKIE_SLICE_SIZE, COOKIE_MATRIX_BYTES, GPU_BUFFER_USAGE_UNIFORM, worldEntityKey, createHdrpBindGroupLayoutDescriptor, createHdrpSkinBindGroupLayoutDescriptor, GPU_BUFFER_USAGE_QUERY_RESOLVE, GPU_BUFFER_USAGE_COPY_SRC, gpuDrivenShadowDrawKey, makeZeroCameraFallbackSnapshot, buildPbrPipelineLayouts, buildPbrSkinLayouts, buildGpuDrivenPbrInstancesBindGroupLayout, buildGpuDrivenPbrPipelineLayout, buildGpuDrivenPbrSkinPipelineLayout, GPU_BUFFER_USAGE_STORAGE, resolvePipelineGroup2Contract, GPU_SHADER_STAGE_FRAGMENT, gpuDrivenDrawKey, gpuDrivenSourceDrawItemIndex, instanceCollectionCacheKey, validateGraphTargetCaptureReadback, createTemporalCoverage, isCanonicalStandardPbrMaterialShader, isStandardPbrMaterialShader, buildBindGroupLayoutDescriptor, gpuDrivenMaterialArtifactKey, buildPbrMaterialUserRegionEntries, extendedLightingSampledTextureCapacityAvailable, GPU_SHADER_STAGE_VERTEX, SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET, SHADOW_CASTER_SHADER_ID, shadowCasterVariantSet, GPU_SHADER_STAGE_COMPUTE, LifecycleTransaction } from './chunk-
|
|
11
|
-
import { RendererContractFailureError, RendererOperationError, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, FrameReceiptStaleError, RenderFeatureStageFailedError, RenderFeatureCapabilityMissingError, RenderTargetStateInvalidError, RenderTargetOperationFailedError, RenderTargetCapabilityMissingError, RenderTargetDescriptorInvalidError, GPU_TEXTURE_USAGE_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_DST, RENDER_PHASE_CATALOG, InstanceCollectionStore, createAutoExposureInspection, RenderFeatureRegistrationConflictError, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_SRC, createVisibilityBudget, EnvironmentGenerationFailedError, PointsLinesMaterialUnsupportedError, StandardProfileInvalidError, TransmissionCapabilityMissingError, SHADOW_ATLAS_DEFAULT_LAYERS, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT, GpuDrivenPreparationError, TemporalFrameSubmitError, ObservationUnavailableError, RenderFeaturePreparationFailedError, AutoExposureInvalidParameterError, PointsLinesPrepareFailedError, SHADOW_ATLAS_DEFAULT_FACE_SIZE, SceneDataUnavailableError, SkinPaletteOverflowError, GPU_TEXTURE_USAGE_STORAGE_BINDING, RenderFeatureDrawRecordingFailedError, PointShadowAtlasUninitializedError, RenderFeaturePreparedStateMismatchError, FXAA_POST_PROCESS_ID } from './chunk-
|
|
10
|
+
import { DeviceScope, SKIN_MATERIAL_SHADER_ID, EXTENDED_LIGHTING_TOPOLOGY, getTextureIdentity, GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_MAP_READ, resolveRenderTargetMaterialSource, createRenderTargetMaterialSource, deriveExtendedLightingCapability, GPU_BUFFER_USAGE_VERTEX, GPU_BUFFER_USAGE_INDEX, IES_SLICE_WIDTH, IES_SLICE_HEIGHT, COOKIE_SLICE_SIZE, COOKIE_MATRIX_BYTES, GPU_BUFFER_USAGE_UNIFORM, worldEntityKey, createHdrpBindGroupLayoutDescriptor, createHdrpSkinBindGroupLayoutDescriptor, GPU_BUFFER_USAGE_QUERY_RESOLVE, GPU_BUFFER_USAGE_COPY_SRC, gpuDrivenShadowDrawKey, makeZeroCameraFallbackSnapshot, buildPbrPipelineLayouts, buildPbrSkinLayouts, buildGpuDrivenPbrInstancesBindGroupLayout, buildGpuDrivenPbrPipelineLayout, buildGpuDrivenPbrSkinPipelineLayout, GPU_BUFFER_USAGE_STORAGE, resolvePipelineGroup2Contract, GPU_SHADER_STAGE_FRAGMENT, gpuDrivenDrawKey, gpuDrivenSourceDrawItemIndex, instanceCollectionCacheKey, validateGraphTargetCaptureReadback, createTemporalCoverage, isCanonicalStandardPbrMaterialShader, isStandardPbrMaterialShader, buildBindGroupLayoutDescriptor, gpuDrivenMaterialArtifactKey, hasRequiredVertexInputs, buildPbrMaterialUserRegionEntries, extendedLightingSampledTextureCapacityAvailable, GPU_SHADER_STAGE_VERTEX, SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET, SHADOW_CASTER_SHADER_ID, shadowCasterVariantSet, GPU_SHADER_STAGE_COMPUTE, LifecycleTransaction } from './chunk-3RTCJTH4.mjs';
|
|
11
|
+
import { RendererContractFailureError, RendererOperationError, MotionBlurValidationError, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, FrameReceiptStaleError, RenderFeatureStageFailedError, RenderFeatureCapabilityMissingError, RenderTargetStateInvalidError, RenderTargetOperationFailedError, RenderTargetCapabilityMissingError, RenderTargetDescriptorInvalidError, GPU_TEXTURE_USAGE_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_DST, RENDER_PHASE_CATALOG, InstanceCollectionStore, motionBlurTemporalDemand, createAutoExposureInspection, RenderFeatureRegistrationConflictError, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_SRC, createVisibilityBudget, EnvironmentGenerationFailedError, PointsLinesMaterialUnsupportedError, StandardProfileInvalidError, TransmissionCapabilityMissingError, motionBlurSampleDelta, isMotionBlurIntervalValid, MOTION_BLUR_PARAMS_BYTE_SIZE, effectiveMotionBlurSampleCount, motionBlurExposureScale, SHADOW_ATLAS_DEFAULT_LAYERS, DEFAULT_MOTION_BLUR_PARAMS, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT, GpuDrivenPreparationError, TemporalFrameSubmitError, ObservationUnavailableError, RenderFeaturePreparationFailedError, AutoExposureInvalidParameterError, PointsLinesPrepareFailedError, validateMotionBlurParams, SHADOW_ATLAS_DEFAULT_FACE_SIZE, SceneDataUnavailableError, SkinPaletteOverflowError, GPU_TEXTURE_USAGE_STORAGE_BINDING, RenderFeatureDrawRecordingFailedError, PointShadowAtlasUninitializedError, RenderFeaturePreparedStateMismatchError, FXAA_POST_PROCESS_ID } from './chunk-NB2S7ZRX.mjs';
|
|
12
12
|
import { ok, err, RhiError, validateDrawArgs } from '@forgeax/engine-rhi';
|
|
13
13
|
import { AssetRegistry, DynamicTextureStore, adaptDynamicTextureDevice, getOrCreateMipmapPipeline, prepareMipmaps, blitMipmapsSync, HANDLE_CUBE, HANDLE_TRIANGLE, HANDLE_QUAD, HANDLE_SPHERE, HANDLE_NINESLICE_QUAD, BuiltinAssetRegistry, resolveAssetHandle, numMipLevels, encodeMipmapLevel, resolveTilesetRuntime, MeshSsboCeilingReachedError, MeshSsboCapacityExceededError } from '@forgeax/engine-assets-runtime';
|
|
14
14
|
import { audioLoader } from '@forgeax/engine-audio-webaudio';
|
|
@@ -18,7 +18,7 @@ import { buildMeshAttributeMapForUvSets, deriveVertexBufferLayout, DEFAULT_VERTE
|
|
|
18
18
|
import { GlobalTransform, ChildOf, MorphWeights, registerPropagateTransforms, collectSubtree, Name, Children, Transform } from '@forgeax/engine-scene';
|
|
19
19
|
import { STANDARD_PIPELINE_PARAM_SCHEMA, createStandardPbrArtifactReceipt, ShaderCatalog, findVariantByKey, LTC_TABLES, standardPhysicalTextureFields, LTC_TABLE_HEIGHT, LTC_TABLE_WIDTH, DEFAULT_STANDARD_PBR_PARAM_SCHEMA, AUTO_EXPOSURE_METER_WGSL } from '@forgeax/engine-shader';
|
|
20
20
|
import { halfFloat, frustum, mat4, vec3, box3, mat3 } from '@forgeax/engine-math';
|
|
21
|
-
import { derive, err as err$1, ok as ok$1, handleSlot,
|
|
21
|
+
import { derive, err as err$1, ok as ok$1, handleSlot, deriveTextureLayout, ASSET_ERROR_HINTS, countExtraUvSets, toShared, RenderQueue, IMAGE_ERROR_HINTS, BUILTIN_BASE, unwrapHandle, materialValuesToLinearRuntime } from '@forgeax/engine-types';
|
|
22
22
|
import { createCurrentFrameObservationLease, RenderGraphBuilder, RenderGraphError } from '@forgeax/engine-render-graph';
|
|
23
23
|
import { componentId } from '@forgeax/engine-ecs/internal';
|
|
24
24
|
import { Skin } from '@forgeax/engine-skinning';
|
|
@@ -254,6 +254,7 @@ function exposeRenderer(renderer) {
|
|
|
254
254
|
cause: structuredRendererCause(cause, "renderer-event")
|
|
255
255
|
});
|
|
256
256
|
const drawError = (cause) => {
|
|
257
|
+
if (cause instanceof MotionBlurValidationError) return cause;
|
|
257
258
|
if (cause instanceof RendererOperationError) return cause;
|
|
258
259
|
if (cause instanceof RendererContractFailureError) {
|
|
259
260
|
return new RendererOperationError("device-operation-failed", {
|
|
@@ -849,6 +850,12 @@ var GpuResidencyCache = class {
|
|
|
849
850
|
// equirect source always resolves to the same cubemap (idempotent, A2).
|
|
850
851
|
cubemapIdempotentMap = /* @__PURE__ */ new Map();
|
|
851
852
|
meshGpuHandles = /* @__PURE__ */ new Map();
|
|
853
|
+
/**
|
|
854
|
+
* Lower-detail POD identities packed into each resident mesh entry. The
|
|
855
|
+
* GPU entry only carries byte ranges; keeping the source identities beside
|
|
856
|
+
* it lets a later draw upgrade a root-only upload without hashing vertices.
|
|
857
|
+
*/
|
|
858
|
+
meshLodAssets = /* @__PURE__ */ new WeakMap();
|
|
852
859
|
meshLifetimes = /* @__PURE__ */ new WeakMap();
|
|
853
860
|
retiredMeshes = /* @__PURE__ */ new Set();
|
|
854
861
|
meshLifetime(entry) {
|
|
@@ -1063,7 +1070,7 @@ var GpuResidencyCache = class {
|
|
|
1063
1070
|
return { freed, errors };
|
|
1064
1071
|
}
|
|
1065
1072
|
evictMesh(handle, worldId = 0) {
|
|
1066
|
-
const id = this.worldKey(
|
|
1073
|
+
const id = this.worldKey(Number(handle), worldId);
|
|
1067
1074
|
const entry = this.meshGpuHandles.get(id);
|
|
1068
1075
|
if (entry === void 0) return { freed: 0, errors: [] };
|
|
1069
1076
|
if (this.meshLifetime(entry).owners > 0) return { freed: 0, errors: [] };
|
|
@@ -1271,11 +1278,11 @@ var GpuResidencyCache = class {
|
|
|
1271
1278
|
* as the canonical "mesh asset has GPU residency" probe.
|
|
1272
1279
|
*/
|
|
1273
1280
|
getMeshGpuHandles(handle, worldId = 0) {
|
|
1274
|
-
return this.meshGpuHandles.get(this.worldKey(
|
|
1281
|
+
return this.meshGpuHandles.get(this.worldKey(Number(handle), worldId));
|
|
1275
1282
|
}
|
|
1276
1283
|
/** Candidate leases retain the concrete allocation even after cache invalidation. */
|
|
1277
1284
|
retainMeshResidency(handle, worldId = 0) {
|
|
1278
|
-
const key = this.worldKey(
|
|
1285
|
+
const key = this.worldKey(Number(handle), worldId);
|
|
1279
1286
|
const entry = this.meshGpuHandles.get(key);
|
|
1280
1287
|
return entry === void 0 ? void 0 : this.meshLifetime(entry).retain(() => {
|
|
1281
1288
|
if (this.meshGpuHandles.get(key) === entry) this.invalidateMesh(handle, worldId);
|
|
@@ -1284,7 +1291,7 @@ var GpuResidencyCache = class {
|
|
|
1284
1291
|
}
|
|
1285
1292
|
/** In-place author changes immediately miss the cache; old submissions keep their allocation. */
|
|
1286
1293
|
invalidateMesh(handle, worldId = 0) {
|
|
1287
|
-
const key = this.worldKey(
|
|
1294
|
+
const key = this.worldKey(handle, worldId);
|
|
1288
1295
|
const entry = this.meshGpuHandles.get(key);
|
|
1289
1296
|
if (entry !== void 0) this.retireMeshEntry(key, entry);
|
|
1290
1297
|
}
|
|
@@ -1292,9 +1299,20 @@ var GpuResidencyCache = class {
|
|
|
1292
1299
|
const id = handleSlot(handle);
|
|
1293
1300
|
switch (pod.kind) {
|
|
1294
1301
|
case "mesh": {
|
|
1295
|
-
const key = this.worldKey(
|
|
1302
|
+
const key = this.worldKey(Number(handle), worldId);
|
|
1296
1303
|
const existing = this.meshGpuHandles.get(key);
|
|
1297
|
-
if (existing !== void 0)
|
|
1304
|
+
if (existing !== void 0) {
|
|
1305
|
+
if (lodMeshes.length === 0) return ok(existing);
|
|
1306
|
+
const previousLodAssets = this.meshLodAssets.get(existing);
|
|
1307
|
+
const sameLodChain = previousLodAssets !== void 0 && previousLodAssets.length === lodMeshes.length && previousLodAssets.every((asset, index) => asset === lodMeshes[index]);
|
|
1308
|
+
if (sameLodChain) return ok(existing);
|
|
1309
|
+
const projected2 = deriveRenderDataMesh(pod);
|
|
1310
|
+
if (!projected2.ok) return projected2;
|
|
1311
|
+
const upgraded = this.uploadMeshById(key, pod, projected2.value, lodMeshes);
|
|
1312
|
+
if (!upgraded.ok) return upgraded;
|
|
1313
|
+
this.retireMeshEntry(key, existing);
|
|
1314
|
+
return upgraded;
|
|
1315
|
+
}
|
|
1298
1316
|
const projected = deriveRenderDataMesh(pod);
|
|
1299
1317
|
if (!projected.ok) return projected;
|
|
1300
1318
|
return this.uploadMeshById(key, pod, projected.value, lodMeshes);
|
|
@@ -1319,7 +1337,7 @@ var GpuResidencyCache = class {
|
|
|
1319
1337
|
prepareResidentForRecovery(handle, pod, worldId = 0) {
|
|
1320
1338
|
const id = handleSlot(handle);
|
|
1321
1339
|
if (pod.kind === "mesh") {
|
|
1322
|
-
const key = this.worldKey(
|
|
1340
|
+
const key = this.worldKey(Number(handle), worldId);
|
|
1323
1341
|
if (this.meshGpuHandles.has(key)) return ok({});
|
|
1324
1342
|
const projected2 = deriveRenderDataMesh(pod);
|
|
1325
1343
|
if (!projected2.ok) return projected2;
|
|
@@ -1965,6 +1983,10 @@ var GpuResidencyCache = class {
|
|
|
1965
1983
|
if (!vboResult.ok) return vboResult;
|
|
1966
1984
|
const vbo = vboResult.value;
|
|
1967
1985
|
let ibo = null;
|
|
1986
|
+
const cleanupUpload = () => {
|
|
1987
|
+
if (ibo !== null) device.destroyBuffer(ibo);
|
|
1988
|
+
device.destroyBuffer(vbo);
|
|
1989
|
+
};
|
|
1968
1990
|
if (indices !== void 0) {
|
|
1969
1991
|
const iboResult = device.createBuffer({
|
|
1970
1992
|
label: `mesh-${id}-ibo`,
|
|
@@ -1972,15 +1994,22 @@ var GpuResidencyCache = class {
|
|
|
1972
1994
|
usage: renderData.indexUsage,
|
|
1973
1995
|
mappedAtCreation: false
|
|
1974
1996
|
});
|
|
1975
|
-
if (!iboResult.ok)
|
|
1997
|
+
if (!iboResult.ok) {
|
|
1998
|
+
cleanupUpload();
|
|
1999
|
+
return iboResult;
|
|
2000
|
+
}
|
|
1976
2001
|
ibo = iboResult.value;
|
|
1977
2002
|
}
|
|
1978
2003
|
const vboWriteResult = device.queue.writeBuffer(vbo, 0, vertices);
|
|
1979
|
-
if (!vboWriteResult.ok)
|
|
2004
|
+
if (!vboWriteResult.ok) {
|
|
2005
|
+
cleanupUpload();
|
|
2006
|
+
return err(vboWriteResult.error);
|
|
2007
|
+
}
|
|
1980
2008
|
if (indices !== void 0) {
|
|
1981
2009
|
const indexSrc = new Uint8Array(indexBytes);
|
|
1982
2010
|
indexSrc.set(new Uint8Array(indices.buffer, indices.byteOffset, indexBytesUnpadded));
|
|
1983
|
-
if (ibo === null)
|
|
2011
|
+
if (ibo === null) {
|
|
2012
|
+
cleanupUpload();
|
|
1984
2013
|
return err(
|
|
1985
2014
|
new RhiError({
|
|
1986
2015
|
code: "webgpu-runtime-error",
|
|
@@ -1988,8 +2017,12 @@ var GpuResidencyCache = class {
|
|
|
1988
2017
|
hint: "the indexed mesh upload lost its RHI index-buffer owner"
|
|
1989
2018
|
})
|
|
1990
2019
|
);
|
|
2020
|
+
}
|
|
1991
2021
|
const iboWriteResult = device.queue.writeBuffer(ibo, 0, indexSrc);
|
|
1992
|
-
if (!iboWriteResult.ok)
|
|
2022
|
+
if (!iboWriteResult.ok) {
|
|
2023
|
+
cleanupUpload();
|
|
2024
|
+
return err(iboWriteResult.error);
|
|
2025
|
+
}
|
|
1993
2026
|
}
|
|
1994
2027
|
const extraUvSets = countExtraUvSets(mesh.attributes);
|
|
1995
2028
|
const entry = {
|
|
@@ -2008,6 +2041,7 @@ var GpuResidencyCache = class {
|
|
|
2008
2041
|
...lodRanges === void 0 ? {} : { lodRanges }
|
|
2009
2042
|
};
|
|
2010
2043
|
this.meshGpuHandles.set(id, entry);
|
|
2044
|
+
this.meshLodAssets.set(entry, lodMeshes);
|
|
2011
2045
|
this.meshResidencyEpoch += 1;
|
|
2012
2046
|
return ok(entry);
|
|
2013
2047
|
}
|
|
@@ -2739,7 +2773,7 @@ function freezeRenderFeaturePlan(identity, plan, logicalTargets2 = []) {
|
|
|
2739
2773
|
for (const resource of plan.resources) {
|
|
2740
2774
|
if (!validName(resource.name) || resources.has(resource.name))
|
|
2741
2775
|
return err$1(stageFailure(identity));
|
|
2742
|
-
if (resource.kind === "prepared-gpu-resource" && resource.logicalTarget !== void 0 && !targets.has(resource.logicalTarget)) {
|
|
2776
|
+
if (resource.kind === "prepared-gpu-resource" && resource.logicalTarget !== void 0 && !(typeof resource.logicalTarget === "string" ? targets.has(resource.logicalTarget) : isSceneDataTarget(resource.logicalTarget))) {
|
|
2743
2777
|
return err$1(stageFailure(identity));
|
|
2744
2778
|
}
|
|
2745
2779
|
resources.set(resource.name, resource);
|
|
@@ -2761,7 +2795,7 @@ function freezeRenderFeaturePlan(identity, plan, logicalTargets2 = []) {
|
|
|
2761
2795
|
);
|
|
2762
2796
|
const resource = resources.get(entry.resource);
|
|
2763
2797
|
const validBuffer = resource?.kind === "buffer" && layout?.buffer !== void 0;
|
|
2764
|
-
const validPrepared = resource?.kind === "prepared-gpu-resource" && (resource.resource.kind === "buffer" && layout?.buffer !== void 0 || resource.resource.kind === "texture-view" && layout?.texture !== void 0 || resource.resource.kind === "sampler" && layout?.sampler !== void 0);
|
|
2798
|
+
const validPrepared = resource?.kind === "prepared-gpu-resource" && (resource.resource.kind === "buffer" && layout?.buffer !== void 0 || resource.resource.kind === "texture-view" && (layout?.texture !== void 0 || layout?.storageTexture !== void 0) || resource.resource.kind === "sampler" && layout?.sampler !== void 0);
|
|
2765
2799
|
if (!validBuffer && !validPrepared) {
|
|
2766
2800
|
return err$1(stageFailure(identity));
|
|
2767
2801
|
}
|
|
@@ -3394,7 +3428,7 @@ function asFeatureError(error, identity, order, stage) {
|
|
|
3394
3428
|
if (error instanceof Error && typeof error.code === "string") {
|
|
3395
3429
|
return error;
|
|
3396
3430
|
}
|
|
3397
|
-
return new RenderFeatureStageFailedError(identity, order, stage, "next-frame");
|
|
3431
|
+
return new RenderFeatureStageFailedError(identity, order, stage, "next-frame", error);
|
|
3398
3432
|
}
|
|
3399
3433
|
function featureErrorForSlot(slot, error) {
|
|
3400
3434
|
switch (error.code) {
|
|
@@ -3409,14 +3443,16 @@ function featureErrorForSlot(slot, error) {
|
|
|
3409
3443
|
slot.feature.identity,
|
|
3410
3444
|
slot.order,
|
|
3411
3445
|
"plan",
|
|
3412
|
-
"next-frame"
|
|
3446
|
+
"next-frame",
|
|
3447
|
+
error
|
|
3413
3448
|
);
|
|
3414
3449
|
default:
|
|
3415
3450
|
return new RenderFeatureStageFailedError(
|
|
3416
3451
|
slot.feature.identity,
|
|
3417
3452
|
slot.order,
|
|
3418
3453
|
"plan",
|
|
3419
|
-
"next-frame"
|
|
3454
|
+
"next-frame",
|
|
3455
|
+
error
|
|
3420
3456
|
);
|
|
3421
3457
|
}
|
|
3422
3458
|
}
|
|
@@ -3444,7 +3480,8 @@ function errorDescriptor(error) {
|
|
|
3444
3480
|
featureIdentity: "unknown",
|
|
3445
3481
|
order: -1,
|
|
3446
3482
|
stage: "plan",
|
|
3447
|
-
recovery: "next-frame"
|
|
3483
|
+
recovery: "next-frame",
|
|
3484
|
+
cause: error
|
|
3448
3485
|
}
|
|
3449
3486
|
};
|
|
3450
3487
|
}
|
|
@@ -3532,13 +3569,13 @@ function preparePlanResources(slot, plan, graphics, gpu, targets) {
|
|
|
3532
3569
|
}
|
|
3533
3570
|
if (resource.resource.kind === "texture-view") {
|
|
3534
3571
|
const logical = resource.logicalTarget === void 0 ? void 0 : targetHandle(resource.logicalTarget, targets);
|
|
3535
|
-
if (resource.logicalTarget !== void 0 && (logical === void 0 || !isRenderFeatureTargetHandle(logical))) {
|
|
3572
|
+
if (resource.logicalTarget !== void 0 && (logical === void 0 || typeof logical !== "string" && !isRenderFeatureTargetHandle(logical) && !isSceneDataTarget(logical))) {
|
|
3536
3573
|
return err$1(planFailure(slot));
|
|
3537
3574
|
}
|
|
3538
3575
|
const prepared2 = gpu.prepareTextureView(
|
|
3539
3576
|
resource.name,
|
|
3540
3577
|
resource.resource.value,
|
|
3541
|
-
logical === void 0
|
|
3578
|
+
logical === void 0 ? void 0 : logical
|
|
3542
3579
|
);
|
|
3543
3580
|
if (!prepared2.ok) return prepared2;
|
|
3544
3581
|
preparedGpuResources.set(resource.name, {
|
|
@@ -4184,7 +4221,10 @@ function runRenderFeatureFrame(host, input) {
|
|
|
4184
4221
|
"plan",
|
|
4185
4222
|
() => feature.plan(extracted.value, {
|
|
4186
4223
|
caps: input.caps,
|
|
4187
|
-
frame: {
|
|
4224
|
+
frame: {
|
|
4225
|
+
frameNumber: input.frameNumber,
|
|
4226
|
+
...input.frameSize === void 0 ? {} : input.frameSize
|
|
4227
|
+
},
|
|
4188
4228
|
generation: transaction.generation,
|
|
4189
4229
|
targets,
|
|
4190
4230
|
sceneData,
|
|
@@ -4241,9 +4281,7 @@ function runRenderFeatureFrame(host, input) {
|
|
|
4241
4281
|
};
|
|
4242
4282
|
const resolver = input.createPreparedGraphicsResolver?.(resolverInput);
|
|
4243
4283
|
const resolveGraphics = resolver === void 0 ? void 0 : (descriptor) => {
|
|
4244
|
-
|
|
4245
|
-
if (!resolved2.ok) resolver.release();
|
|
4246
|
-
return resolved2;
|
|
4284
|
+
return resolveGraphicsSnapshot(resolver, descriptor, transaction.generation);
|
|
4247
4285
|
};
|
|
4248
4286
|
const releaseResolver = () => {
|
|
4249
4287
|
if (resolver !== void 0) resolver.release();
|
|
@@ -4271,9 +4309,7 @@ function runRenderFeatureFrame(host, input) {
|
|
|
4271
4309
|
host.setStatus(identity, "failed", slot.latestError);
|
|
4272
4310
|
continue;
|
|
4273
4311
|
}
|
|
4274
|
-
const graphicsLeases = projected.value.passes.flatMap(
|
|
4275
|
-
(pass) => pass.resolvedGraphics?.leases ?? []
|
|
4276
|
-
);
|
|
4312
|
+
const graphicsLeases = resolver?.leases ?? projected.value.passes.flatMap((pass) => pass.resolvedGraphics?.leases ?? []);
|
|
4277
4313
|
if (graphicsLeases.length > 0) requiresPreparedResourceKey = true;
|
|
4278
4314
|
const leases = [...new Set(graphicsLeases), ...gpu?.retireUntouched() ?? []];
|
|
4279
4315
|
const retained = host.retainPreparedGraphics(identity, leases);
|
|
@@ -4357,6 +4393,66 @@ function createRenderFeatureHost(features, _caps) {
|
|
|
4357
4393
|
return ok$1(new FeatureHostImpl(slots));
|
|
4358
4394
|
}
|
|
4359
4395
|
|
|
4396
|
+
// src/features/motion-blur/motion-blur-runtime.ts
|
|
4397
|
+
function motionBlurDrawOptions(input) {
|
|
4398
|
+
return {
|
|
4399
|
+
...input.sampleTimeSeconds === void 0 ? {} : { sampleTimeSeconds: input.sampleTimeSeconds },
|
|
4400
|
+
...input.temporalReset === true ? { temporalReset: true } : {}
|
|
4401
|
+
};
|
|
4402
|
+
}
|
|
4403
|
+
function planMotionBlurFrame(input) {
|
|
4404
|
+
const rawSampleDelta = motionBlurSampleDelta(
|
|
4405
|
+
input.sampleTimeSeconds,
|
|
4406
|
+
input.acceptedSampleTimeSeconds,
|
|
4407
|
+
input.fallbackDeltaSeconds
|
|
4408
|
+
);
|
|
4409
|
+
const accepted = input.acceptedTemporal;
|
|
4410
|
+
const reset = input.temporalReset || accepted === void 0 || accepted.historyValid === false && accepted.resetReason === void 0 || !isMotionBlurIntervalValid(rawSampleDelta) || accepted.input.width !== input.width || accepted.input.height !== input.height || accepted.input.viewIdentity !== input.cameraViewIdentity || accepted.input.historyVersion !== input.historyVersion || accepted.input.environmentSignature !== input.environmentSignature || accepted.input.fogSignature !== input.fogSignature || accepted.input.deviceGeneration !== input.deviceGeneration;
|
|
4411
|
+
const params = input.params === void 0 ? void 0 : { ...input.params, targetFps: input.params.targetFps ?? 60 };
|
|
4412
|
+
const demanded = motionBlurTemporalDemand(params);
|
|
4413
|
+
return {
|
|
4414
|
+
rawSampleDelta,
|
|
4415
|
+
demanded,
|
|
4416
|
+
featureInput: params === void 0 ? void 0 : {
|
|
4417
|
+
params,
|
|
4418
|
+
frameDeltaSeconds: rawSampleDelta,
|
|
4419
|
+
reset: demanded && reset
|
|
4420
|
+
}
|
|
4421
|
+
};
|
|
4422
|
+
}
|
|
4423
|
+
function projectMotionBlurInspection(input) {
|
|
4424
|
+
if (input.camera?.motionBlur === void 0 && input.invalidParams === void 0) {
|
|
4425
|
+
return void 0;
|
|
4426
|
+
}
|
|
4427
|
+
const motionBlur = input.camera?.motionBlur === void 0 ? DEFAULT_MOTION_BLUR_PARAMS : { ...input.camera.motionBlur, targetFps: input.camera.motionBlur.targetFps ?? 60 };
|
|
4428
|
+
const demanded = motionBlurTemporalDemand(motionBlur);
|
|
4429
|
+
const unavailable3 = demanded && !input.capabilities.rgba16floatRenderable;
|
|
4430
|
+
const lane = input.capabilities.compute && input.capabilities.storageBuffer && input.capabilities.storageTexture && input.capabilities.rgba16floatRenderable ? "compute" : "raster-limited";
|
|
4431
|
+
const accepted = input.acceptedTemporal;
|
|
4432
|
+
const submitFailure = input.transaction.lastFailure === "queue-submit-failed" || accepted?.resetReason === "submit-failure";
|
|
4433
|
+
const execution = input.execution;
|
|
4434
|
+
const executionAccepted = execution !== void 0 && input.temporalFrameId === execution.frameId && execution.deviceGeneration === input.deviceGeneration && execution.graphGeneration === input.graphGeneration;
|
|
4435
|
+
const active = demanded && !unavailable3 && lane === "compute" && executionAccepted && accepted?.historyValid === true && accepted.resetReason === void 0;
|
|
4436
|
+
return {
|
|
4437
|
+
enabled: demanded,
|
|
4438
|
+
status: input.invalidParams !== void 0 ? "invalid" : !demanded ? "off" : active ? "active" : !unavailable3 && lane === "raster-limited" ? "limited" : "reset",
|
|
4439
|
+
lane,
|
|
4440
|
+
shutterAngle: motionBlur.shutterAngle,
|
|
4441
|
+
maxRadiusPixels: motionBlur.maxRadiusPixels,
|
|
4442
|
+
sampleCount: motionBlur.sampleCount,
|
|
4443
|
+
targetFps: motionBlur.targetFps ?? 60,
|
|
4444
|
+
effectiveSampleCount: !demanded ? 0 : effectiveMotionBlurSampleCount(motionBlur.sampleCount),
|
|
4445
|
+
temporalDemand: demanded ? "scene-data-temporal-v1" : "none",
|
|
4446
|
+
passName: "motion-blur",
|
|
4447
|
+
historyWrites: 0,
|
|
4448
|
+
passCount: !demanded ? 0 : executionAccepted ? 2 : lane === "raster-limited" ? 1 : 0,
|
|
4449
|
+
tapBudget: !demanded ? 0 : effectiveMotionBlurSampleCount(motionBlur.sampleCount),
|
|
4450
|
+
...executionAccepted ? { execution } : {},
|
|
4451
|
+
...accepted?.resetReason === void 0 ? {} : { resetReason: accepted.resetReason },
|
|
4452
|
+
...input.invalidParams !== void 0 ? { lastFailure: "invalid-params" } : unavailable3 ? { lastFailure: "scene-data-unavailable" } : submitFailure ? { lastFailure: "submit-failure" } : {}
|
|
4453
|
+
};
|
|
4454
|
+
}
|
|
4455
|
+
|
|
4360
4456
|
// src/lifecycle.ts
|
|
4361
4457
|
var LostListenerRegistry = class {
|
|
4362
4458
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -4715,8 +4811,8 @@ function addAtmosphereBackground(graph, color) {
|
|
|
4715
4811
|
settings.mieDirectionalG,
|
|
4716
4812
|
settings.sunAngularRadius,
|
|
4717
4813
|
settings.sunAngularRadius > 0 ? 1 : 0,
|
|
4718
|
-
|
|
4719
|
-
|
|
4814
|
+
settings.circumsolarStrength,
|
|
4815
|
+
settings.circumsolarWidth
|
|
4720
4816
|
]);
|
|
4721
4817
|
frame.runtime.device.queue.writeBuffer(resources.buffer(params.value).unwrap(), 0, values).unwrap();
|
|
4722
4818
|
const triangles = new Float32Array(6 * 9);
|
|
@@ -6185,7 +6281,7 @@ var VOLUME_FROXEL_FORMAT = "rgba8unorm";
|
|
|
6185
6281
|
var VOLUME_HISTORY_FORMAT = "rgba16float";
|
|
6186
6282
|
var VOLUME_RESOLVED_FORMAT = "rgba16float";
|
|
6187
6283
|
var VOLUME_PACKING_Z = 4;
|
|
6188
|
-
var VOLUMETRIC_FOG_PARAMS_BYTES =
|
|
6284
|
+
var VOLUMETRIC_FOG_PARAMS_BYTES = 9 * 16;
|
|
6189
6285
|
function deriveVolumetricFogExtent(surface, depth = 64, tileSize = 4) {
|
|
6190
6286
|
const physicalTileSize = Math.max(2, Math.floor(tileSize / 2));
|
|
6191
6287
|
return {
|
|
@@ -6404,7 +6500,10 @@ function importVolumeInputs(graph, topology, linearHdr, sceneDepth, directionalS
|
|
|
6404
6500
|
if (!density.ok) return density;
|
|
6405
6501
|
const params = graph.importBuffer(
|
|
6406
6502
|
"volume-params",
|
|
6407
|
-
{
|
|
6503
|
+
{
|
|
6504
|
+
size: VOLUMETRIC_FOG_PARAMS_BYTES,
|
|
6505
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST
|
|
6506
|
+
},
|
|
6408
6507
|
(frame) => {
|
|
6409
6508
|
const context = frame.volumetricFog;
|
|
6410
6509
|
if (context === void 0) throw pipelineError2("parameter residency");
|
|
@@ -6640,9 +6739,9 @@ function addVolumetricFogPasses(graph, inputs) {
|
|
|
6640
6739
|
const densitySampler = frame.runtime.device.createSampler({
|
|
6641
6740
|
minFilter: "linear",
|
|
6642
6741
|
magFilter: "linear",
|
|
6643
|
-
addressModeU: "
|
|
6644
|
-
addressModeV: "
|
|
6645
|
-
addressModeW: "
|
|
6742
|
+
addressModeU: "repeat",
|
|
6743
|
+
addressModeV: "repeat",
|
|
6744
|
+
addressModeW: "repeat"
|
|
6646
6745
|
});
|
|
6647
6746
|
if (!densitySampler.ok) throw densitySampler.error;
|
|
6648
6747
|
const bindings = volumeBindGroup(frame, integrate.layout, [
|
|
@@ -6973,6 +7072,1546 @@ function addMotionBlurPass(graph, input, temporal, output) {
|
|
|
6973
7072
|
output
|
|
6974
7073
|
});
|
|
6975
7074
|
}
|
|
7075
|
+
var MOTION_BLUR_COMPUTE_WGSL = `struct MotionBlurComputeParams {
|
|
7076
|
+
shutterAngle : f32,
|
|
7077
|
+
maxRadiusPixels : f32,
|
|
7078
|
+
sampleCount : u32,
|
|
7079
|
+
reset : u32,
|
|
7080
|
+
targetFps : f32,
|
|
7081
|
+
exposureScale : f32,
|
|
7082
|
+
frameDeltaSeconds : f32,
|
|
7083
|
+
flags : u32,
|
|
7084
|
+
};
|
|
7085
|
+
|
|
7086
|
+
struct MotionBlurTileSummary {
|
|
7087
|
+
// The two motion lanes retain the source pixel that supplied the direction.
|
|
7088
|
+
// Those anchors let a bounded midpoint tap hit a sparse source without
|
|
7089
|
+
// adding a color read or growing the 32-byte per-tile summary.
|
|
7090
|
+
maxMotionAndLength : vec4<f32>,
|
|
7091
|
+
secondaryMotionAndDepth : vec4<f32>,
|
|
7092
|
+
};
|
|
7093
|
+
|
|
7094
|
+
struct TemporalSample {
|
|
7095
|
+
motion : vec2<f32>,
|
|
7096
|
+
depth : f32,
|
|
7097
|
+
reactive : f32,
|
|
7098
|
+
validDepth : bool,
|
|
7099
|
+
motionValid : bool,
|
|
7100
|
+
};
|
|
7101
|
+
|
|
7102
|
+
struct MotionBlurSupport {
|
|
7103
|
+
primaryMotion : vec2<f32>,
|
|
7104
|
+
secondaryMotion : vec2<f32>,
|
|
7105
|
+
primaryAnchor : vec2<i32>,
|
|
7106
|
+
secondaryAnchor : vec2<i32>,
|
|
7107
|
+
primaryRadius : f32,
|
|
7108
|
+
secondaryRadius : f32,
|
|
7109
|
+
valid : bool,
|
|
7110
|
+
longVector : bool,
|
|
7111
|
+
};
|
|
7112
|
+
|
|
7113
|
+
struct MotionBlurTap {
|
|
7114
|
+
pixel : vec2<i32>,
|
|
7115
|
+
fallbackPixel : vec2<i32>,
|
|
7116
|
+
valid : bool,
|
|
7117
|
+
fallbackValid : bool,
|
|
7118
|
+
};
|
|
7119
|
+
|
|
7120
|
+
struct MotionBlurFilter {
|
|
7121
|
+
color0 : vec4<f32>,
|
|
7122
|
+
color1 : vec4<f32>,
|
|
7123
|
+
color2 : vec4<f32>,
|
|
7124
|
+
color3 : vec4<f32>,
|
|
7125
|
+
depth0 : f32,
|
|
7126
|
+
depth1 : f32,
|
|
7127
|
+
depth2 : f32,
|
|
7128
|
+
depth3 : f32,
|
|
7129
|
+
coverage0 : f32,
|
|
7130
|
+
coverage1 : f32,
|
|
7131
|
+
coverage2 : f32,
|
|
7132
|
+
coverage3 : f32,
|
|
7133
|
+
receiver : vec2<i32>,
|
|
7134
|
+
motion : vec2<f32>,
|
|
7135
|
+
radius : f32,
|
|
7136
|
+
receiverMask : u32,
|
|
7137
|
+
validMask : u32,
|
|
7138
|
+
valid : bool,
|
|
7139
|
+
};
|
|
7140
|
+
|
|
7141
|
+
struct MotionBlurCacheSample {
|
|
7142
|
+
color : vec4<f32>,
|
|
7143
|
+
coverage : f32,
|
|
7144
|
+
valid : bool,
|
|
7145
|
+
};
|
|
7146
|
+
|
|
7147
|
+
struct MotionBlurSource {
|
|
7148
|
+
pixel : vec2<i32>,
|
|
7149
|
+
motion : vec2<f32>,
|
|
7150
|
+
depth : f32,
|
|
7151
|
+
radius : f32,
|
|
7152
|
+
coverage0 : f32,
|
|
7153
|
+
coverage1 : f32,
|
|
7154
|
+
coverage2 : f32,
|
|
7155
|
+
coverage3 : f32,
|
|
7156
|
+
receiverMask : u32,
|
|
7157
|
+
valid : bool,
|
|
7158
|
+
};
|
|
7159
|
+
|
|
7160
|
+
@group(0) @binding(0) var currentColor : texture_2d<f32>;
|
|
7161
|
+
@group(0) @binding(1) var sceneTemporal : texture_2d<f32>;
|
|
7162
|
+
@group(0) @binding(2) var<storage, read_write> tileSummary : array<MotionBlurTileSummary>;
|
|
7163
|
+
@group(0) @binding(3) var outputColor : texture_storage_2d<rgba16float, write>;
|
|
7164
|
+
@group(0) @binding(4) var<uniform> params : MotionBlurComputeParams;
|
|
7165
|
+
|
|
7166
|
+
// Reconstruct a bounded part of a partially covered opaque edge and fund it
|
|
7167
|
+
// by attenuating the matching empty-background trail. The fixed factor keeps
|
|
7168
|
+
// the work and the energy adjustment independent of the blur radius.
|
|
7169
|
+
const EDGE_FILL_FACTOR : f32 = 0.99;
|
|
7170
|
+
|
|
7171
|
+
// One 8x8 workgroup covers a 16x16 tile. Each invocation reads a 2x2
|
|
7172
|
+
// footprint, then the workgroup reduction combines the values without
|
|
7173
|
+
// write contention or a serial 256-pixel loop.
|
|
7174
|
+
var<workgroup> tileMotionScratch : array<vec2<f32>, 64>;
|
|
7175
|
+
var<workgroup> tileLengthScratch : array<f32, 64>;
|
|
7176
|
+
var<workgroup> tileSourceScratch : array<vec2<i32>, 64>;
|
|
7177
|
+
var<workgroup> tileSecondaryMotionScratch : array<vec2<f32>, 64>;
|
|
7178
|
+
var<workgroup> tileSecondaryLengthScratch : array<f32, 64>;
|
|
7179
|
+
var<workgroup> tileSecondarySourceScratch : array<vec2<i32>, 64>;
|
|
7180
|
+
// The reconstruction workgroup owns a 4x4 half-resolution result cache for
|
|
7181
|
+
// its 8x8 output footprint. Each owner computes the complete bounded support
|
|
7182
|
+
// filter once. Full-resolution lanes only select a depth-compatible cached
|
|
7183
|
+
// result after the uniform barrier, with no cross-workgroup dependency.
|
|
7184
|
+
// Four quadrants are kept per half-cell. Each owner pays four receiver
|
|
7185
|
+
// center-color reads and at most supportCount (<=15) accepted source-color
|
|
7186
|
+
// reads; midpoint anchor recovery reuses an existing slot and adds no read
|
|
7187
|
+
// budget. The shared result retains odd/even coverage instead of spreading an
|
|
7188
|
+
// odd row across both rows.
|
|
7189
|
+
var<workgroup> halfFilteredColor : array<vec4<f32>, 64>;
|
|
7190
|
+
var<workgroup> halfFilteredDepth : array<f32, 64>;
|
|
7191
|
+
var<workgroup> halfFilteredCoverage : array<f32, 64>;
|
|
7192
|
+
var<workgroup> halfFilteredReceiver : array<vec2<i32>, 16>;
|
|
7193
|
+
var<workgroup> halfFilteredMotion : array<vec2<f32>, 16>;
|
|
7194
|
+
var<workgroup> halfFilteredRadius : array<f32, 16>;
|
|
7195
|
+
var<workgroup> halfFilteredValid : array<u32, 64>;
|
|
7196
|
+
var<workgroup> longVectorScratch : array<u32, 64>;
|
|
7197
|
+
// A reconstruct workgroup covers one 8x8 output footprint. Its first lane
|
|
7198
|
+
// derives the complete bounded tile neighbourhood once and publishes the top
|
|
7199
|
+
// two source directions for all lanes and all designated half-resolution
|
|
7200
|
+
// owners. This keeps summary work O(P/T^2), rather than repeating a tile walk
|
|
7201
|
+
// per output pixel.
|
|
7202
|
+
var<workgroup> neighbourhoodPrimaryMotion : vec2<f32>;
|
|
7203
|
+
var<workgroup> neighbourhoodPrimaryLength : f32;
|
|
7204
|
+
var<workgroup> neighbourhoodPrimaryAnchor : vec2<i32>;
|
|
7205
|
+
var<workgroup> neighbourhoodSecondaryMotion : vec2<f32>;
|
|
7206
|
+
var<workgroup> neighbourhoodSecondaryLength : f32;
|
|
7207
|
+
var<workgroup> neighbourhoodSecondaryAnchor : vec2<i32>;
|
|
7208
|
+
|
|
7209
|
+
// maxRadiusPixels is authored at [0,64], so a 16-pixel tile support reaches
|
|
7210
|
+
// at most four tiles. The fixed 9x9 lattice is geometrically derived from its
|
|
7211
|
+
// index, then intersected with the circular/AABB support. It is a constant
|
|
7212
|
+
// bounded metadata walk: no loop scales with the requested radius and no
|
|
7213
|
+
// radius-squared per-pixel storage or write path is introduced.
|
|
7214
|
+
const MAX_TILE_REACH : i32 = 4;
|
|
7215
|
+
const TILE_NEIGHBORHOOD_SIDE : u32 = 9u;
|
|
7216
|
+
const TILE_NEIGHBORHOOD_COUNT : u32 = 81u;
|
|
7217
|
+
|
|
7218
|
+
fn finite(value : f32) -> bool {
|
|
7219
|
+
return value == value && abs(value) < 3.402823e+38;
|
|
7220
|
+
}
|
|
7221
|
+
|
|
7222
|
+
fn pixelInBounds(pixel : vec2<i32>, dimensions : vec2<i32>) -> bool {
|
|
7223
|
+
return pixel.x >= 0 && pixel.y >= 0 && pixel.x < dimensions.x && pixel.y < dimensions.y;
|
|
7224
|
+
}
|
|
7225
|
+
|
|
7226
|
+
fn decodeDepth(packed : f32) -> f32 {
|
|
7227
|
+
if (packed < 0.0) { return 0.0; }
|
|
7228
|
+
return exp2(packed) - 1.0;
|
|
7229
|
+
}
|
|
7230
|
+
|
|
7231
|
+
fn temporalAt(pixel : vec2<i32>, dimensions : vec2<i32>) -> TemporalSample {
|
|
7232
|
+
let samplePixel = clamp(pixel, vec2<i32>(0), dimensions - vec2<i32>(1));
|
|
7233
|
+
let packed = textureLoad(sceneTemporal, samplePixel, 0);
|
|
7234
|
+
let invalidMotion = packed.w >= 2.0;
|
|
7235
|
+
return TemporalSample(
|
|
7236
|
+
packed.xy,
|
|
7237
|
+
decodeDepth(packed.z),
|
|
7238
|
+
clamp(packed.w - select(0.0, 2.0, invalidMotion), 0.0, 1.0),
|
|
7239
|
+
packed.z >= 0.0,
|
|
7240
|
+
!invalidMotion,
|
|
7241
|
+
);
|
|
7242
|
+
}
|
|
7243
|
+
|
|
7244
|
+
fn depthReject(center : f32, sample : f32) -> bool {
|
|
7245
|
+
return sample > center + max(0.01, center * 0.01);
|
|
7246
|
+
}
|
|
7247
|
+
|
|
7248
|
+
fn distinctDirection(a : vec2<f32>, b : vec2<f32>) -> bool {
|
|
7249
|
+
let aLength = length(a);
|
|
7250
|
+
let bLength = length(b);
|
|
7251
|
+
if (aLength <= 1e-5 || bLength <= 1e-5) { return false; }
|
|
7252
|
+
// Keep only genuinely different directions. Opposite signs still carry
|
|
7253
|
+
// separate source segments, while nearly parallel candidates do not spend
|
|
7254
|
+
// the bounded second-direction slot.
|
|
7255
|
+
return abs(dot(a / aLength, b / bLength)) < 0.94;
|
|
7256
|
+
}
|
|
7257
|
+
|
|
7258
|
+
fn sourceVelocitySegmentCovers(
|
|
7259
|
+
receiverPixel : vec2<i32>,
|
|
7260
|
+
sourcePixel : vec2<i32>,
|
|
7261
|
+
motionPixels : vec2<f32>,
|
|
7262
|
+
radius : f32,
|
|
7263
|
+
) -> bool {
|
|
7264
|
+
let delta = vec2<f32>(receiverPixel - sourcePixel);
|
|
7265
|
+
let motionLength = length(motionPixels);
|
|
7266
|
+
if (motionLength <= 1e-5 || radius <= 1e-5) { return false; }
|
|
7267
|
+
let direction = motionPixels / motionLength;
|
|
7268
|
+
let along = dot(delta, direction);
|
|
7269
|
+
let perpendicular = abs(delta.x * direction.y - delta.y * direction.x);
|
|
7270
|
+
// A source segment owns a one-pixel coverage tube. This is a bounded
|
|
7271
|
+
// geometric test, not a radius-sized source scan, and lets a sparse mover
|
|
7272
|
+
// cover a static receiver without accepting a far/orthogonal source.
|
|
7273
|
+
return abs(along) <= radius + 0.75 && perpendicular <= 0.75;
|
|
7274
|
+
}
|
|
7275
|
+
|
|
7276
|
+
fn symmetricOffset(index : u32, count : u32, motion : vec2<f32>) -> vec2<f32> {
|
|
7277
|
+
let denominator = max(f32(count), 1.0);
|
|
7278
|
+
return motion * (2.0 * (f32(index) + 0.5) / denominator - 1.0);
|
|
7279
|
+
}
|
|
7280
|
+
|
|
7281
|
+
fn tileNeighborhoodOffset(index : u32) -> vec2<i32> {
|
|
7282
|
+
let x = i32(index % TILE_NEIGHBORHOOD_SIDE) - MAX_TILE_REACH;
|
|
7283
|
+
let y = i32(index / TILE_NEIGHBORHOOD_SIDE) - MAX_TILE_REACH;
|
|
7284
|
+
return vec2<i32>(x, y);
|
|
7285
|
+
}
|
|
7286
|
+
|
|
7287
|
+
fn tileOffsetWithinSupport(offset : vec2<i32>, tileReach : i32) -> bool {
|
|
7288
|
+
if (offset.x == 0 && offset.y == 0) { return false; }
|
|
7289
|
+
let boundedReach = min(max(tileReach, 1), MAX_TILE_REACH);
|
|
7290
|
+
// The bounded lattice is a square around the receiver tile. The precise
|
|
7291
|
+
// AABB/circle intersection below decides whether a diagonal tile really
|
|
7292
|
+
// intersects the support; a circular pre-filter here would drop the
|
|
7293
|
+
// (\xB12,\xB12) and boundary cases before that exact test can run.
|
|
7294
|
+
return abs(offset.x) <= boundedReach && abs(offset.y) <= boundedReach;
|
|
7295
|
+
}
|
|
7296
|
+
|
|
7297
|
+
fn tileIntersectsSupport(
|
|
7298
|
+
receiverPixel : vec2<i32>,
|
|
7299
|
+
candidateTile : vec2<i32>,
|
|
7300
|
+
dimensions : vec2<i32>,
|
|
7301
|
+
supportRadius : f32,
|
|
7302
|
+
) -> bool {
|
|
7303
|
+
// Use the receiver-to-tile-AABB lower bound. This includes every tile whose
|
|
7304
|
+
// contents can intersect the support, including signs and diagonals, while
|
|
7305
|
+
// rejecting an outside-support tile before loading its summary.
|
|
7306
|
+
let tileMin = vec2<f32>(candidateTile * 16);
|
|
7307
|
+
let tileMax = min(tileMin + vec2<f32>(16.0), vec2<f32>(dimensions));
|
|
7308
|
+
let receiver = vec2<f32>(receiverPixel) + vec2<f32>(0.5);
|
|
7309
|
+
let dx = max(max(tileMin.x - receiver.x, 0.0), receiver.x - tileMax.x);
|
|
7310
|
+
let dy = max(max(tileMin.y - receiver.y, 0.0), receiver.y - tileMax.y);
|
|
7311
|
+
let distance = vec2<f32>(dx, dy);
|
|
7312
|
+
return dot(distance, distance) <= supportRadius * supportRadius + 1.0;
|
|
7313
|
+
}
|
|
7314
|
+
|
|
7315
|
+
fn tileIntersectsWorkgroupSupport(
|
|
7316
|
+
workgroupBase : vec2<i32>,
|
|
7317
|
+
candidateTile : vec2<i32>,
|
|
7318
|
+
dimensions : vec2<i32>,
|
|
7319
|
+
supportRadius : f32,
|
|
7320
|
+
) -> bool {
|
|
7321
|
+
// A workgroup's 8x8 rectangle is the conservative receiver domain for one
|
|
7322
|
+
// shared metadata walk. The AABB lower bound includes every pixel in that
|
|
7323
|
+
// rectangle that can reach the candidate tile, including diagonal/sign
|
|
7324
|
+
// variants, while rejecting a tile fully outside the support.
|
|
7325
|
+
let tileMin = vec2<f32>(candidateTile * 16);
|
|
7326
|
+
let tileMax = min(tileMin + vec2<f32>(16.0), vec2<f32>(dimensions));
|
|
7327
|
+
let groupMin = vec2<f32>(workgroupBase) + vec2<f32>(0.5);
|
|
7328
|
+
let groupMax = min(groupMin + vec2<f32>(8.0), vec2<f32>(dimensions));
|
|
7329
|
+
let dx = max(max(tileMin.x - groupMax.x, 0.0), groupMin.x - tileMax.x);
|
|
7330
|
+
let dy = max(max(tileMin.y - groupMax.y, 0.0), groupMin.y - tileMax.y);
|
|
7331
|
+
let distance = vec2<f32>(dx, dy);
|
|
7332
|
+
return dot(distance, distance) <= supportRadius * supportRadius + 1.0;
|
|
7333
|
+
}
|
|
7334
|
+
|
|
7335
|
+
fn scanTileNeighbourhood(
|
|
7336
|
+
workgroupBase : vec2<i32>,
|
|
7337
|
+
dimensions : vec2<i32>,
|
|
7338
|
+
) {
|
|
7339
|
+
let tiles = (vec2<u32>(dimensions) + vec2<u32>(15)) / vec2<u32>(16);
|
|
7340
|
+
let tileCoordinate = workgroupBase / vec2<i32>(16);
|
|
7341
|
+
let currentIndex = u32(tileCoordinate.y) * tiles.x + u32(tileCoordinate.x);
|
|
7342
|
+
let current = tileSummary[currentIndex];
|
|
7343
|
+
var primary = current.maxMotionAndLength.xy * vec2<f32>(dimensions) * params.exposureScale;
|
|
7344
|
+
var primaryLength = length(primary);
|
|
7345
|
+
var primaryAnchor = vec2<i32>(
|
|
7346
|
+
i32(current.maxMotionAndLength.z),
|
|
7347
|
+
i32(current.maxMotionAndLength.w),
|
|
7348
|
+
);
|
|
7349
|
+
var secondary = current.secondaryMotionAndDepth.xy * vec2<f32>(dimensions) * params.exposureScale;
|
|
7350
|
+
var secondaryLength = length(secondary);
|
|
7351
|
+
var secondaryAnchor = vec2<i32>(
|
|
7352
|
+
i32(current.secondaryMotionAndDepth.z),
|
|
7353
|
+
i32(current.secondaryMotionAndDepth.w),
|
|
7354
|
+
);
|
|
7355
|
+
let supportRadius = min(max(params.maxRadiusPixels, 1.0), 64.0);
|
|
7356
|
+
let tileReach = min(MAX_TILE_REACH, max(1, i32(ceil(supportRadius / 16.0))));
|
|
7357
|
+
for (var tileCandidateIndex = 0u; tileCandidateIndex < TILE_NEIGHBORHOOD_COUNT; tileCandidateIndex += 1u) {
|
|
7358
|
+
let offset = tileNeighborhoodOffset(tileCandidateIndex);
|
|
7359
|
+
if (!tileOffsetWithinSupport(offset, tileReach)) { continue; }
|
|
7360
|
+
let candidateTile = tileCoordinate + offset;
|
|
7361
|
+
if (
|
|
7362
|
+
candidateTile.x < 0 ||
|
|
7363
|
+
candidateTile.y < 0 ||
|
|
7364
|
+
candidateTile.x >= i32(tiles.x) ||
|
|
7365
|
+
candidateTile.y >= i32(tiles.y) ||
|
|
7366
|
+
!tileIntersectsWorkgroupSupport(workgroupBase, candidateTile, dimensions, supportRadius)
|
|
7367
|
+
) { continue; }
|
|
7368
|
+
let candidate = tileSummary[u32(candidateTile.y) * tiles.x + u32(candidateTile.x)];
|
|
7369
|
+
let candidatePrimary = candidate.maxMotionAndLength.xy * vec2<f32>(dimensions) * params.exposureScale;
|
|
7370
|
+
let candidatePrimaryLength = length(candidatePrimary);
|
|
7371
|
+
let candidatePrimaryAnchor = vec2<i32>(
|
|
7372
|
+
i32(candidate.maxMotionAndLength.z),
|
|
7373
|
+
i32(candidate.maxMotionAndLength.w),
|
|
7374
|
+
);
|
|
7375
|
+
if (candidatePrimaryLength > primaryLength) {
|
|
7376
|
+
let previousPrimary = primary;
|
|
7377
|
+
let previousLength = primaryLength;
|
|
7378
|
+
let previousAnchor = primaryAnchor;
|
|
7379
|
+
primary = candidatePrimary;
|
|
7380
|
+
primaryLength = candidatePrimaryLength;
|
|
7381
|
+
primaryAnchor = candidatePrimaryAnchor;
|
|
7382
|
+
if (previousLength > 1e-5 && distinctDirection(primary, previousPrimary) && previousLength > secondaryLength) {
|
|
7383
|
+
secondary = previousPrimary;
|
|
7384
|
+
secondaryLength = previousLength;
|
|
7385
|
+
secondaryAnchor = previousAnchor;
|
|
7386
|
+
}
|
|
7387
|
+
} else if (
|
|
7388
|
+
candidatePrimaryLength > secondaryLength &&
|
|
7389
|
+
distinctDirection(primary, candidatePrimary)
|
|
7390
|
+
) {
|
|
7391
|
+
secondary = candidatePrimary;
|
|
7392
|
+
secondaryLength = candidatePrimaryLength;
|
|
7393
|
+
secondaryAnchor = candidatePrimaryAnchor;
|
|
7394
|
+
}
|
|
7395
|
+
let candidateSecondary = candidate.secondaryMotionAndDepth.xy * vec2<f32>(dimensions) * params.exposureScale;
|
|
7396
|
+
let candidateSecondaryLength = length(candidateSecondary);
|
|
7397
|
+
let candidateSecondaryAnchor = vec2<i32>(
|
|
7398
|
+
i32(candidate.secondaryMotionAndDepth.z),
|
|
7399
|
+
i32(candidate.secondaryMotionAndDepth.w),
|
|
7400
|
+
);
|
|
7401
|
+
if (candidateSecondaryLength > secondaryLength && distinctDirection(primary, candidateSecondary)) {
|
|
7402
|
+
secondary = candidateSecondary;
|
|
7403
|
+
secondaryLength = candidateSecondaryLength;
|
|
7404
|
+
secondaryAnchor = candidateSecondaryAnchor;
|
|
7405
|
+
}
|
|
7406
|
+
}
|
|
7407
|
+
neighbourhoodPrimaryMotion = primary;
|
|
7408
|
+
neighbourhoodPrimaryLength = primaryLength;
|
|
7409
|
+
neighbourhoodPrimaryAnchor = primaryAnchor;
|
|
7410
|
+
neighbourhoodSecondaryMotion = secondary;
|
|
7411
|
+
neighbourhoodSecondaryLength = secondaryLength;
|
|
7412
|
+
neighbourhoodSecondaryAnchor = secondaryAnchor;
|
|
7413
|
+
}
|
|
7414
|
+
|
|
7415
|
+
fn validMotionParameters() -> bool {
|
|
7416
|
+
return
|
|
7417
|
+
finite(params.frameDeltaSeconds) &&
|
|
7418
|
+
params.frameDeltaSeconds > 0.0 &&
|
|
7419
|
+
params.frameDeltaSeconds <= 0.1 &&
|
|
7420
|
+
finite(params.exposureScale) &&
|
|
7421
|
+
params.exposureScale > 0.0 &&
|
|
7422
|
+
finite(params.targetFps) &&
|
|
7423
|
+
params.targetFps >= 0.0 &&
|
|
7424
|
+
params.targetFps <= 240.0 &&
|
|
7425
|
+
params.targetFps == floor(params.targetFps) &&
|
|
7426
|
+
params.sampleCount >= 4u &&
|
|
7427
|
+
params.maxRadiusPixels > 0.0 &&
|
|
7428
|
+
finite(params.maxRadiusPixels) &&
|
|
7429
|
+
params.shutterAngle > 0.0;
|
|
7430
|
+
}
|
|
7431
|
+
|
|
7432
|
+
fn sourceMotionPixels(sample : TemporalSample, dimensions : vec2<i32>) -> vec2<f32> {
|
|
7433
|
+
return sample.motion * vec2<f32>(dimensions) * params.exposureScale;
|
|
7434
|
+
}
|
|
7435
|
+
|
|
7436
|
+
fn sourceRadius(motionPixels : vec2<f32>, shutter : f32) -> f32 {
|
|
7437
|
+
return min(length(motionPixels) * shutter * 0.5, params.maxRadiusPixels);
|
|
7438
|
+
}
|
|
7439
|
+
|
|
7440
|
+
fn resolveMotionBlurSupport(
|
|
7441
|
+
pixel : vec2<i32>,
|
|
7442
|
+
dimensions : vec2<i32>,
|
|
7443
|
+
) -> MotionBlurSupport {
|
|
7444
|
+
var empty = MotionBlurSupport(
|
|
7445
|
+
vec2<f32>(0.0),
|
|
7446
|
+
vec2<f32>(0.0),
|
|
7447
|
+
vec2<i32>(0),
|
|
7448
|
+
vec2<i32>(0),
|
|
7449
|
+
0.0,
|
|
7450
|
+
0.0,
|
|
7451
|
+
false,
|
|
7452
|
+
false,
|
|
7453
|
+
);
|
|
7454
|
+
if (!pixelInBounds(pixel, dimensions) || !validMotionParameters() || params.reset != 0u) {
|
|
7455
|
+
return empty;
|
|
7456
|
+
}
|
|
7457
|
+
let center = temporalAt(pixel, dimensions);
|
|
7458
|
+
if (!center.validDepth || !center.motionValid) { return empty; }
|
|
7459
|
+
let shutter = clamp(params.shutterAngle / 360.0, 0.0, 1.0);
|
|
7460
|
+
let centerMotion = sourceMotionPixels(center, dimensions);
|
|
7461
|
+
let centerLength = length(centerMotion);
|
|
7462
|
+
var primaryMotion = centerMotion;
|
|
7463
|
+
var primaryLength = centerLength;
|
|
7464
|
+
var primaryAnchor = pixel;
|
|
7465
|
+
var secondaryMotion = neighbourhoodSecondaryMotion;
|
|
7466
|
+
var secondaryLength = neighbourhoodSecondaryLength;
|
|
7467
|
+
var secondaryAnchor = neighbourhoodSecondaryAnchor;
|
|
7468
|
+
if (neighbourhoodPrimaryLength > primaryLength) {
|
|
7469
|
+
if (distinctDirection(primaryMotion, neighbourhoodPrimaryMotion)) {
|
|
7470
|
+
secondaryMotion = primaryMotion;
|
|
7471
|
+
secondaryLength = primaryLength;
|
|
7472
|
+
secondaryAnchor = pixel;
|
|
7473
|
+
}
|
|
7474
|
+
primaryMotion = neighbourhoodPrimaryMotion;
|
|
7475
|
+
primaryLength = neighbourhoodPrimaryLength;
|
|
7476
|
+
primaryAnchor = neighbourhoodPrimaryAnchor;
|
|
7477
|
+
} else if (
|
|
7478
|
+
neighbourhoodPrimaryLength > secondaryLength &&
|
|
7479
|
+
distinctDirection(primaryMotion, neighbourhoodPrimaryMotion)
|
|
7480
|
+
) {
|
|
7481
|
+
secondaryMotion = neighbourhoodPrimaryMotion;
|
|
7482
|
+
secondaryLength = neighbourhoodPrimaryLength;
|
|
7483
|
+
secondaryAnchor = neighbourhoodPrimaryAnchor;
|
|
7484
|
+
}
|
|
7485
|
+
if (
|
|
7486
|
+
secondaryLength <= 1e-5 ||
|
|
7487
|
+
!distinctDirection(primaryMotion, secondaryMotion)
|
|
7488
|
+
) {
|
|
7489
|
+
secondaryMotion = vec2<f32>(0.0);
|
|
7490
|
+
secondaryLength = 0.0;
|
|
7491
|
+
}
|
|
7492
|
+
empty.primaryMotion = primaryMotion;
|
|
7493
|
+
empty.secondaryMotion = secondaryMotion;
|
|
7494
|
+
empty.primaryAnchor = primaryAnchor;
|
|
7495
|
+
empty.secondaryAnchor = secondaryAnchor;
|
|
7496
|
+
empty.primaryRadius = sourceRadius(primaryMotion, shutter);
|
|
7497
|
+
empty.secondaryRadius = sourceRadius(secondaryMotion, shutter);
|
|
7498
|
+
empty.valid = empty.primaryRadius > 1e-5 || empty.secondaryRadius > 1e-5;
|
|
7499
|
+
empty.longVector = max(empty.primaryRadius, empty.secondaryRadius) > 8.0;
|
|
7500
|
+
return empty;
|
|
7501
|
+
}
|
|
7502
|
+
|
|
7503
|
+
fn sourceAnchorForReceiver(
|
|
7504
|
+
receiverPixel : vec2<i32>,
|
|
7505
|
+
anchor : vec2<i32>,
|
|
7506
|
+
motion : vec2<f32>,
|
|
7507
|
+
dimensions : vec2<i32>,
|
|
7508
|
+
) -> vec2<i32> {
|
|
7509
|
+
// A tile summary has one source anchor, while a horizontal source may span
|
|
7510
|
+
// the tile's perpendicular axis. Project that anchor onto the receiver's
|
|
7511
|
+
// motion line before the midpoint read. The following source footprint
|
|
7512
|
+
// validates the projected pixel, so a thin or off-axis source cannot be
|
|
7513
|
+
// invented by the projection.
|
|
7514
|
+
let motionLength = length(motion);
|
|
7515
|
+
if (motionLength <= 1e-5) { return anchor; }
|
|
7516
|
+
let direction = motion / motionLength;
|
|
7517
|
+
let delta = vec2<f32>(receiverPixel - anchor);
|
|
7518
|
+
let perpendicular = delta - direction * dot(delta, direction);
|
|
7519
|
+
let projected = vec2<f32>(anchor) + perpendicular;
|
|
7520
|
+
let candidate = vec2<i32>(floor(projected + vec2<f32>(0.5)));
|
|
7521
|
+
return clamp(candidate, vec2<i32>(0), dimensions - vec2<i32>(1));
|
|
7522
|
+
}
|
|
7523
|
+
|
|
7524
|
+
fn supportTap(
|
|
7525
|
+
receiverPixel : vec2<i32>,
|
|
7526
|
+
index : u32,
|
|
7527
|
+
count : u32,
|
|
7528
|
+
motion : vec2<f32>,
|
|
7529
|
+
radius : f32,
|
|
7530
|
+
anchor : vec2<i32>,
|
|
7531
|
+
dimensions : vec2<i32>,
|
|
7532
|
+
) -> MotionBlurTap {
|
|
7533
|
+
// Preserve the geometric midpoint as the primary sample. The source anchor
|
|
7534
|
+
// is a recovery coordinate for a sparse tile summary, not a replacement for
|
|
7535
|
+
// the receiver's authored support. It is tried only when this midpoint has
|
|
7536
|
+
// no valid source, and therefore never adds a color read or a tap.
|
|
7537
|
+
var fallbackPixel = vec2<i32>(0);
|
|
7538
|
+
var fallbackValid = false;
|
|
7539
|
+
if (index == count / 2u) {
|
|
7540
|
+
fallbackPixel = sourceAnchorForReceiver(receiverPixel, anchor, motion, dimensions);
|
|
7541
|
+
fallbackValid = pixelInBounds(fallbackPixel, dimensions);
|
|
7542
|
+
}
|
|
7543
|
+
let direction = motion / max(length(motion), 1e-5);
|
|
7544
|
+
let offset = symmetricOffset(index, count, direction * radius);
|
|
7545
|
+
let dimensionsF = vec2<f32>(dimensions);
|
|
7546
|
+
let uv = (vec2<f32>(receiverPixel) + vec2<f32>(0.5) + offset) / dimensionsF;
|
|
7547
|
+
if (uv.x < 0.0 || uv.y < 0.0 || uv.x >= 1.0 || uv.y >= 1.0) {
|
|
7548
|
+
return MotionBlurTap(vec2<i32>(0), fallbackPixel, false, fallbackValid);
|
|
7549
|
+
}
|
|
7550
|
+
let candidate = vec2<i32>(floor(uv * dimensionsF));
|
|
7551
|
+
return MotionBlurTap(candidate, fallbackPixel, pixelInBounds(candidate, dimensions), fallbackValid);
|
|
7552
|
+
}
|
|
7553
|
+
|
|
7554
|
+
fn sourceAt(
|
|
7555
|
+
receiverPixel : vec2<i32>,
|
|
7556
|
+
sourcePixel : vec2<i32>,
|
|
7557
|
+
centerDepth : f32,
|
|
7558
|
+
shutter : f32,
|
|
7559
|
+
dimensions : vec2<i32>,
|
|
7560
|
+
) -> MotionBlurSource {
|
|
7561
|
+
let empty = MotionBlurSource(
|
|
7562
|
+
vec2<i32>(0),
|
|
7563
|
+
vec2<f32>(0.0),
|
|
7564
|
+
0.0,
|
|
7565
|
+
0.0,
|
|
7566
|
+
0.0,
|
|
7567
|
+
0.0,
|
|
7568
|
+
0.0,
|
|
7569
|
+
0.0,
|
|
7570
|
+
0u,
|
|
7571
|
+
false,
|
|
7572
|
+
);
|
|
7573
|
+
if (!pixelInBounds(sourcePixel, dimensions)) { return empty; }
|
|
7574
|
+
let source = temporalAt(sourcePixel, dimensions);
|
|
7575
|
+
if (!source.validDepth || !source.motionValid || depthReject(centerDepth, source.depth)) {
|
|
7576
|
+
return empty;
|
|
7577
|
+
}
|
|
7578
|
+
let motionPixels = sourceMotionPixels(source, dimensions);
|
|
7579
|
+
let radius = sourceRadius(motionPixels, shutter);
|
|
7580
|
+
if (!sourceVelocitySegmentCovers(receiverPixel, sourcePixel, motionPixels, radius)) {
|
|
7581
|
+
return empty;
|
|
7582
|
+
}
|
|
7583
|
+
return MotionBlurSource(
|
|
7584
|
+
sourcePixel,
|
|
7585
|
+
motionPixels,
|
|
7586
|
+
source.depth,
|
|
7587
|
+
radius,
|
|
7588
|
+
1.0,
|
|
7589
|
+
1.0,
|
|
7590
|
+
1.0,
|
|
7591
|
+
1.0,
|
|
7592
|
+
0u,
|
|
7593
|
+
true,
|
|
7594
|
+
);
|
|
7595
|
+
}
|
|
7596
|
+
|
|
7597
|
+
// A half-resolution owner represents a complete 2x2 receiving footprint. The
|
|
7598
|
+
// source motion test is evaluated against every receiving pixel in that
|
|
7599
|
+
// footprint, so odd/even rows and columns remain visible without a second
|
|
7600
|
+
// filter pass or an unbounded neighbourhood walk.
|
|
7601
|
+
fn sourceAtFootprint(
|
|
7602
|
+
receiverPixel : vec2<i32>,
|
|
7603
|
+
sourcePixel : vec2<i32>,
|
|
7604
|
+
centerDepth : f32,
|
|
7605
|
+
shutter : f32,
|
|
7606
|
+
dimensions : vec2<i32>,
|
|
7607
|
+
) -> MotionBlurSource {
|
|
7608
|
+
var selected = MotionBlurSource(
|
|
7609
|
+
vec2<i32>(0),
|
|
7610
|
+
vec2<f32>(0.0),
|
|
7611
|
+
0.0,
|
|
7612
|
+
0.0,
|
|
7613
|
+
0.0,
|
|
7614
|
+
0.0,
|
|
7615
|
+
0.0,
|
|
7616
|
+
0.0,
|
|
7617
|
+
0u,
|
|
7618
|
+
false,
|
|
7619
|
+
);
|
|
7620
|
+
var selectedDepth = 3.402823e+38;
|
|
7621
|
+
var coverage0 = 0.0;
|
|
7622
|
+
var coverage1 = 0.0;
|
|
7623
|
+
var coverage2 = 0.0;
|
|
7624
|
+
var coverage3 = 0.0;
|
|
7625
|
+
var selectedReceiverMask = 0u;
|
|
7626
|
+
for (var y = 0u; y < 2u; y += 1u) {
|
|
7627
|
+
for (var x = 0u; x < 2u; x += 1u) {
|
|
7628
|
+
let candidatePixel = sourcePixel + vec2<i32>(i32(x), i32(y));
|
|
7629
|
+
if (!pixelInBounds(candidatePixel, dimensions)) { continue; }
|
|
7630
|
+
let temporal = temporalAt(candidatePixel, dimensions);
|
|
7631
|
+
if (!temporal.validDepth || !temporal.motionValid || depthReject(centerDepth, temporal.depth)) {
|
|
7632
|
+
continue;
|
|
7633
|
+
}
|
|
7634
|
+
let motionPixels = sourceMotionPixels(temporal, dimensions);
|
|
7635
|
+
let radius = sourceRadius(motionPixels, shutter);
|
|
7636
|
+
var receiverMask = 0u;
|
|
7637
|
+
// A source half-cell and a receiver half-cell share the same integer
|
|
7638
|
+
// displacement. This one-to-one correspondence keeps a diagonal source
|
|
7639
|
+
// from being counted for all four receiver quadrants just because its
|
|
7640
|
+
// tube crosses the quadrant. The segment test still supplies the motion
|
|
7641
|
+
// support and depth/validity checks remain source-owned.
|
|
7642
|
+
let receiver = receiverPixel + vec2<i32>(i32(x), i32(y));
|
|
7643
|
+
if (
|
|
7644
|
+
pixelInBounds(receiver, dimensions) &&
|
|
7645
|
+
sourceVelocitySegmentCovers(receiver, candidatePixel, motionPixels, radius)
|
|
7646
|
+
) {
|
|
7647
|
+
receiverMask = receiverMask | (1u << (y * 2u + x));
|
|
7648
|
+
}
|
|
7649
|
+
if (receiverMask == 0u) { continue; }
|
|
7650
|
+
if (!selected.valid || temporal.depth < selectedDepth) {
|
|
7651
|
+
selected = MotionBlurSource(
|
|
7652
|
+
candidatePixel,
|
|
7653
|
+
motionPixels,
|
|
7654
|
+
temporal.depth,
|
|
7655
|
+
radius,
|
|
7656
|
+
0.0,
|
|
7657
|
+
0.0,
|
|
7658
|
+
0.0,
|
|
7659
|
+
0.0,
|
|
7660
|
+
receiverMask,
|
|
7661
|
+
true,
|
|
7662
|
+
);
|
|
7663
|
+
selectedDepth = temporal.depth;
|
|
7664
|
+
coverage0 = 0.0;
|
|
7665
|
+
coverage1 = 0.0;
|
|
7666
|
+
coverage2 = 0.0;
|
|
7667
|
+
coverage3 = 0.0;
|
|
7668
|
+
selectedReceiverMask = receiverMask;
|
|
7669
|
+
}
|
|
7670
|
+
if (abs(temporal.depth - selectedDepth) <= 1e-4) {
|
|
7671
|
+
// The common integer displacement above gives each source cell one
|
|
7672
|
+
// receiver quadrant. The matching footprint therefore contributes its
|
|
7673
|
+
// full unit overlap to that one owner.
|
|
7674
|
+
if ((receiverMask & 1u) != 0u) { coverage0 += 1.0; }
|
|
7675
|
+
if ((receiverMask & 2u) != 0u) { coverage1 += 1.0; }
|
|
7676
|
+
if ((receiverMask & 4u) != 0u) { coverage2 += 1.0; }
|
|
7677
|
+
if ((receiverMask & 8u) != 0u) { coverage3 += 1.0; }
|
|
7678
|
+
selectedReceiverMask = selectedReceiverMask | receiverMask;
|
|
7679
|
+
}
|
|
7680
|
+
}
|
|
7681
|
+
}
|
|
7682
|
+
if (selected.valid) {
|
|
7683
|
+
// The owner samples a 2x2 source footprint but pays for one color read.
|
|
7684
|
+
// Each receiving quadrant keeps the fraction of that footprint that is
|
|
7685
|
+
// covered at the selected depth. This preserves energy at boundaries and
|
|
7686
|
+
// carries a one-pixel odd-row feature into its real row only.
|
|
7687
|
+
selected.coverage0 = min(coverage0, 1.0) * select(0.0, 1.0, (selectedReceiverMask & 1u) != 0u);
|
|
7688
|
+
selected.coverage1 = min(coverage1, 1.0) * select(0.0, 1.0, (selectedReceiverMask & 2u) != 0u);
|
|
7689
|
+
selected.coverage2 = min(coverage2, 1.0) * select(0.0, 1.0, (selectedReceiverMask & 4u) != 0u);
|
|
7690
|
+
selected.coverage3 = min(coverage3, 1.0) * select(0.0, 1.0, (selectedReceiverMask & 8u) != 0u);
|
|
7691
|
+
selected.receiverMask = selectedReceiverMask;
|
|
7692
|
+
}
|
|
7693
|
+
return selected;
|
|
7694
|
+
}
|
|
7695
|
+
|
|
7696
|
+
fn midpointTileOwnership(
|
|
7697
|
+
receiverPixel : vec2<i32>,
|
|
7698
|
+
source : MotionBlurSource,
|
|
7699
|
+
) -> f32 {
|
|
7700
|
+
// A recovered tile anchor represents one two-pixel source footprint. The
|
|
7701
|
+
// footprint is split across the finite, geometrically derived tile bands of
|
|
7702
|
+
// the support segment. One half-cell owner per band claims the represented
|
|
7703
|
+
// midpoint slot, rather than painting every receiver in the interval.
|
|
7704
|
+
let sourcePixel = source.pixel;
|
|
7705
|
+
let motionLength = length(source.motion);
|
|
7706
|
+
if (motionLength <= 1e-5 || source.radius <= 1e-5) { return 0.0; }
|
|
7707
|
+
let receiverTile = receiverPixel / vec2<i32>(16);
|
|
7708
|
+
let sourceTile = sourcePixel / vec2<i32>(16);
|
|
7709
|
+
let tileDelta = vec2<f32>(sourceTile - receiverTile);
|
|
7710
|
+
let direction = source.motion / motionLength;
|
|
7711
|
+
let along = abs(dot(tileDelta, direction));
|
|
7712
|
+
let perpendicular = abs(tileDelta.x * direction.y - tileDelta.y * direction.x);
|
|
7713
|
+
let tileReach = min(4, max(1, i32(ceil(source.radius / 16.0))));
|
|
7714
|
+
if (along < 1.0 || ceil(along) > f32(tileReach) || perpendicular > 1.0) { return 0.0; }
|
|
7715
|
+
let groupBase = (receiverPixel / vec2<i32>(8)) * vec2<i32>(8);
|
|
7716
|
+
let local = receiverPixel - groupBase;
|
|
7717
|
+
let ownerX = select(
|
|
7718
|
+
clamp(((sourcePixel.x - groupBase.x) / 2) * 2, 0, 6),
|
|
7719
|
+
select(0, 6, sourcePixel.x > receiverPixel.x),
|
|
7720
|
+
sourcePixel.x != receiverPixel.x,
|
|
7721
|
+
);
|
|
7722
|
+
let ownerY = select(
|
|
7723
|
+
clamp(((sourcePixel.y - groupBase.y) / 2) * 2, 0, 6),
|
|
7724
|
+
select(0, 6, sourcePixel.y > receiverPixel.y),
|
|
7725
|
+
sourcePixel.y != receiverPixel.y,
|
|
7726
|
+
);
|
|
7727
|
+
if (local.x != ownerX || local.y != ownerY) { return 0.0; }
|
|
7728
|
+
let sourceFootprintLength = 2.0;
|
|
7729
|
+
let supportLength = max(2.0 * source.radius, sourceFootprintLength);
|
|
7730
|
+
// Divide the finite footprint ownership over every reachable tile band.
|
|
7731
|
+
// This keeps the total recovery mass bounded when the support grows from
|
|
7732
|
+
// 32 to 64 pixels while retaining a nonzero owner at any in-view band.
|
|
7733
|
+
return min(1.0, sourceFootprintLength / (supportLength * f32(tileReach)));
|
|
7734
|
+
}
|
|
7735
|
+
|
|
7736
|
+
fn integrateLongColor(
|
|
7737
|
+
filtered : vec4<f32>,
|
|
7738
|
+
validCount : f32,
|
|
7739
|
+
hasAcceptedSource : bool,
|
|
7740
|
+
uncovered : f32,
|
|
7741
|
+
count : u32,
|
|
7742
|
+
supportCount : u32,
|
|
7743
|
+
centerColor : vec4<f32>,
|
|
7744
|
+
centerMoving : bool,
|
|
7745
|
+
) -> vec4<f32> {
|
|
7746
|
+
// Uncovered ownership is decremented at the same time as validCount is incremented
|
|
7747
|
+
// for both geometric and midpoint-recovered samples. Keep the derived cap
|
|
7748
|
+
// as a guard against floating point accumulation and make the ownership
|
|
7749
|
+
// transfer explicit instead of adding an independent brightness term.
|
|
7750
|
+
let conservedUncovered = min(
|
|
7751
|
+
max(uncovered, 0.0),
|
|
7752
|
+
max(f32(supportCount) - validCount, 0.0),
|
|
7753
|
+
);
|
|
7754
|
+
// A moving receiver can be partially covered at a silhouette edge. Use the
|
|
7755
|
+
// accepted foreground only for the receiver's missing current coverage; a
|
|
7756
|
+
// fully covered receiver keeps the original center contribution and the
|
|
7757
|
+
// Dawn energy contract remains unchanged for uniform opaque surfaces.
|
|
7758
|
+
let acceptedColor = select(
|
|
7759
|
+
vec3<f32>(0.0),
|
|
7760
|
+
filtered.rgb / max(validCount, 1.0),
|
|
7761
|
+
hasAcceptedSource && validCount > 0.0,
|
|
7762
|
+
);
|
|
7763
|
+
// Preserve the color target's authored alpha as the output floor. It is
|
|
7764
|
+
// separate from the RGB test below, which identifies an empty background
|
|
7765
|
+
// receiver without treating alpha as a scene-coverage attachment.
|
|
7766
|
+
let missingForeground = 1.0 - clamp(centerColor.a, 0.0, 1.0);
|
|
7767
|
+
let edgeCoverage = select(missingForeground, 1.0, centerMoving);
|
|
7768
|
+
let edgeFill = acceptedColor * edgeCoverage * EDGE_FILL_FACTOR;
|
|
7769
|
+
let movingFallback = select(centerColor.rgb, edgeFill, hasAcceptedSource && validCount > 0.0);
|
|
7770
|
+
let background = select(vec3<f32>(0.0), movingFallback, centerMoving);
|
|
7771
|
+
// The accepted colour can come from a transparent receiver's moving
|
|
7772
|
+
// foreground. Carry the same bounded support coverage into alpha so that
|
|
7773
|
+
// RGB written outside the current silhouette is not composited as black.
|
|
7774
|
+
// Preserve the receiver's authored coverage as a floor: blur must not make
|
|
7775
|
+
// an already visible pixel disappear.
|
|
7776
|
+
let acceptedAlpha = select(
|
|
7777
|
+
0.0,
|
|
7778
|
+
filtered.a / max(validCount, 1.0),
|
|
7779
|
+
validCount > 0.0,
|
|
7780
|
+
);
|
|
7781
|
+
let acceptedCoverage = validCount + missingForeground * conservedUncovered;
|
|
7782
|
+
let reconstructedAlpha = acceptedAlpha * acceptedCoverage / max(f32(count), 1.0);
|
|
7783
|
+
let outputAlpha = clamp(max(centerColor.a, reconstructedAlpha), 0.0, 1.0);
|
|
7784
|
+
let emptyReceiver =
|
|
7785
|
+
!centerMoving &&
|
|
7786
|
+
hasAcceptedSource &&
|
|
7787
|
+
max(max(abs(centerColor.r), abs(centerColor.g)), abs(centerColor.b)) < 1e-4;
|
|
7788
|
+
let sourceScale = select(1.0, 1.0 - EDGE_FILL_FACTOR, emptyReceiver);
|
|
7789
|
+
let sourceContribution = filtered.rgb * sourceScale;
|
|
7790
|
+
let trailScale = select(1.0, 1.0 - EDGE_FILL_FACTOR, emptyReceiver);
|
|
7791
|
+
let trailContribution = background * conservedUncovered * trailScale;
|
|
7792
|
+
return vec4<f32>(
|
|
7793
|
+
(sourceContribution + centerColor.rgb + trailContribution) / max(f32(count), 1.0),
|
|
7794
|
+
outputAlpha,
|
|
7795
|
+
);
|
|
7796
|
+
}
|
|
7797
|
+
|
|
7798
|
+
fn longSupportFilter(
|
|
7799
|
+
receiverPixel : vec2<i32>,
|
|
7800
|
+
support : MotionBlurSupport,
|
|
7801
|
+
centerDepth : f32,
|
|
7802
|
+
dimensions : vec2<i32>,
|
|
7803
|
+
) -> MotionBlurFilter {
|
|
7804
|
+
var empty = MotionBlurFilter(
|
|
7805
|
+
vec4<f32>(0.0),
|
|
7806
|
+
vec4<f32>(0.0),
|
|
7807
|
+
vec4<f32>(0.0),
|
|
7808
|
+
vec4<f32>(0.0),
|
|
7809
|
+
0.0,
|
|
7810
|
+
0.0,
|
|
7811
|
+
0.0,
|
|
7812
|
+
0.0,
|
|
7813
|
+
0.0,
|
|
7814
|
+
0.0,
|
|
7815
|
+
0.0,
|
|
7816
|
+
0.0,
|
|
7817
|
+
receiverPixel,
|
|
7818
|
+
vec2<f32>(0.0),
|
|
7819
|
+
0.0,
|
|
7820
|
+
0u,
|
|
7821
|
+
0u,
|
|
7822
|
+
false,
|
|
7823
|
+
);
|
|
7824
|
+
if (!support.valid) { return empty; }
|
|
7825
|
+
let count = min(params.sampleCount, 16u);
|
|
7826
|
+
// The authored tier is the total source colour-read budget. Reserve one slot
|
|
7827
|
+
// for the receiver/background contribution; the remaining bounded taps
|
|
7828
|
+
// (at most 15) are split between the two source directions when needed.
|
|
7829
|
+
let supportCount = max(count - 1u, 1u);
|
|
7830
|
+
let useSecondDirection = support.secondaryRadius > 1e-5 && distinctDirection(support.primaryMotion, support.secondaryMotion);
|
|
7831
|
+
let primaryCount = select(supportCount, max(supportCount / 2u, 1u), useSecondDirection);
|
|
7832
|
+
let secondaryCount = max(supportCount - primaryCount, 1u);
|
|
7833
|
+
let shutter = clamp(params.shutterAngle / 360.0, 0.0, 1.0);
|
|
7834
|
+
let center = temporalAt(receiverPixel, dimensions);
|
|
7835
|
+
// Keep each receiving quadrant's own center color. Reusing the owner
|
|
7836
|
+
// pixel for all four quadrants causes a one-pixel parity shift to duplicate
|
|
7837
|
+
// a bright source (or erase a bright receiver) in the half-resolution cache.
|
|
7838
|
+
var centerColor0 = colorAt(receiverPixel, dimensions);
|
|
7839
|
+
var centerColor1 = colorAt(receiverPixel + vec2<i32>(1, 0), dimensions);
|
|
7840
|
+
var centerColor2 = colorAt(receiverPixel + vec2<i32>(0, 1), dimensions);
|
|
7841
|
+
var centerColor3 = colorAt(receiverPixel + vec2<i32>(1, 1), dimensions);
|
|
7842
|
+
var filtered0 = vec4<f32>(0.0);
|
|
7843
|
+
var filtered1 = vec4<f32>(0.0);
|
|
7844
|
+
var filtered2 = vec4<f32>(0.0);
|
|
7845
|
+
var filtered3 = vec4<f32>(0.0);
|
|
7846
|
+
var validCount0 = 0.0;
|
|
7847
|
+
var validCount1 = 0.0;
|
|
7848
|
+
var validCount2 = 0.0;
|
|
7849
|
+
var validCount3 = 0.0;
|
|
7850
|
+
var hasAcceptedSource0 = false;
|
|
7851
|
+
var hasAcceptedSource1 = false;
|
|
7852
|
+
var hasAcceptedSource2 = false;
|
|
7853
|
+
var hasAcceptedSource3 = false;
|
|
7854
|
+
var uncovered0 = f32(supportCount);
|
|
7855
|
+
var uncovered1 = f32(supportCount);
|
|
7856
|
+
var uncovered2 = f32(supportCount);
|
|
7857
|
+
var uncovered3 = f32(supportCount);
|
|
7858
|
+
var midpointFallbackPixel = vec2<i32>(0);
|
|
7859
|
+
var midpointFallbackValid = false;
|
|
7860
|
+
var midpointFallbackRadius = 0.0;
|
|
7861
|
+
var nearestDepth0 = 3.402823e+38;
|
|
7862
|
+
var nearestDepth1 = 3.402823e+38;
|
|
7863
|
+
var nearestDepth2 = 3.402823e+38;
|
|
7864
|
+
var nearestDepth3 = 3.402823e+38;
|
|
7865
|
+
for (var index = 0u; index < 16u; index += 1u) {
|
|
7866
|
+
if (index >= supportCount) { break; }
|
|
7867
|
+
let secondDirection = useSecondDirection && index >= primaryCount;
|
|
7868
|
+
let localIndex = select(index, index - primaryCount, secondDirection);
|
|
7869
|
+
let localCount = select(primaryCount, secondaryCount, secondDirection);
|
|
7870
|
+
let selectedMotion = select(support.primaryMotion, support.secondaryMotion, secondDirection);
|
|
7871
|
+
let selectedRadius = select(support.primaryRadius, support.secondaryRadius, secondDirection);
|
|
7872
|
+
let tap = supportTap(
|
|
7873
|
+
receiverPixel,
|
|
7874
|
+
localIndex,
|
|
7875
|
+
localCount,
|
|
7876
|
+
selectedMotion,
|
|
7877
|
+
selectedRadius,
|
|
7878
|
+
select(support.primaryAnchor, support.secondaryAnchor, secondDirection),
|
|
7879
|
+
dimensions,
|
|
7880
|
+
);
|
|
7881
|
+
if (!tap.valid) {
|
|
7882
|
+
if (tap.fallbackValid && selectedRadius > midpointFallbackRadius) {
|
|
7883
|
+
midpointFallbackPixel = tap.fallbackPixel;
|
|
7884
|
+
midpointFallbackValid = true;
|
|
7885
|
+
midpointFallbackRadius = selectedRadius;
|
|
7886
|
+
}
|
|
7887
|
+
continue;
|
|
7888
|
+
}
|
|
7889
|
+
let source = sourceAtFootprint(
|
|
7890
|
+
receiverPixel,
|
|
7891
|
+
tap.pixel,
|
|
7892
|
+
centerDepth,
|
|
7893
|
+
shutter,
|
|
7894
|
+
dimensions,
|
|
7895
|
+
);
|
|
7896
|
+
if (!source.valid) {
|
|
7897
|
+
if (tap.fallbackValid && selectedRadius > midpointFallbackRadius) {
|
|
7898
|
+
midpointFallbackPixel = tap.fallbackPixel;
|
|
7899
|
+
midpointFallbackValid = true;
|
|
7900
|
+
midpointFallbackRadius = selectedRadius;
|
|
7901
|
+
}
|
|
7902
|
+
continue;
|
|
7903
|
+
}
|
|
7904
|
+
let sampleColor = colorAt(source.pixel, dimensions);
|
|
7905
|
+
// Keep the receiver's own color as the center contribution. A source
|
|
7906
|
+
// footprint may cover an adjacent receiver quadrant without being the
|
|
7907
|
+
// receiver itself; substituting its color here creates odd/even-alignment
|
|
7908
|
+
// brightening at thin-object boundaries.
|
|
7909
|
+
if ((source.receiverMask & 1u) != 0u) {
|
|
7910
|
+
filtered0 += sampleColor * source.coverage0;
|
|
7911
|
+
validCount0 += source.coverage0;
|
|
7912
|
+
hasAcceptedSource0 = true;
|
|
7913
|
+
uncovered0 = max(uncovered0 - source.coverage0, 0.0);
|
|
7914
|
+
nearestDepth0 = min(nearestDepth0, source.depth);
|
|
7915
|
+
}
|
|
7916
|
+
if ((source.receiverMask & 2u) != 0u) {
|
|
7917
|
+
filtered1 += sampleColor * source.coverage1;
|
|
7918
|
+
validCount1 += source.coverage1;
|
|
7919
|
+
hasAcceptedSource1 = true;
|
|
7920
|
+
uncovered1 = max(uncovered1 - source.coverage1, 0.0);
|
|
7921
|
+
nearestDepth1 = min(nearestDepth1, source.depth);
|
|
7922
|
+
}
|
|
7923
|
+
if ((source.receiverMask & 4u) != 0u) {
|
|
7924
|
+
filtered2 += sampleColor * source.coverage2;
|
|
7925
|
+
validCount2 += source.coverage2;
|
|
7926
|
+
hasAcceptedSource2 = true;
|
|
7927
|
+
uncovered2 = max(uncovered2 - source.coverage2, 0.0);
|
|
7928
|
+
nearestDepth2 = min(nearestDepth2, source.depth);
|
|
7929
|
+
}
|
|
7930
|
+
if ((source.receiverMask & 8u) != 0u) {
|
|
7931
|
+
filtered3 += sampleColor * source.coverage3;
|
|
7932
|
+
validCount3 += source.coverage3;
|
|
7933
|
+
hasAcceptedSource3 = true;
|
|
7934
|
+
uncovered3 = max(uncovered3 - source.coverage3, 0.0);
|
|
7935
|
+
nearestDepth3 = min(nearestDepth3, source.depth);
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
// A tile anchor recovers a sparse source only for the widest support tier,
|
|
7939
|
+
// where the fixed geometric lattice can leave a source between taps. It
|
|
7940
|
+
// spends the represented midpoint ownership of the finite support segment;
|
|
7941
|
+
// every accepted colour, coverage and depth contribution uses that same
|
|
7942
|
+
// bounded ownership, so the fallback cannot create an opaque stripe. The
|
|
7943
|
+
// shorter tiers keep their exact geometric energy and do not need recovery.
|
|
7944
|
+
if (midpointFallbackValid && max(support.primaryRadius, support.secondaryRadius) > 32.0) {
|
|
7945
|
+
let fallbackSource = sourceAtFootprint(
|
|
7946
|
+
receiverPixel,
|
|
7947
|
+
midpointFallbackPixel,
|
|
7948
|
+
centerDepth,
|
|
7949
|
+
shutter,
|
|
7950
|
+
dimensions,
|
|
7951
|
+
);
|
|
7952
|
+
if (fallbackSource.valid) {
|
|
7953
|
+
let midpointWeight = midpointTileOwnership(receiverPixel, fallbackSource);
|
|
7954
|
+
if (
|
|
7955
|
+
midpointWeight > 0.0
|
|
7956
|
+
) {
|
|
7957
|
+
let fallbackColor = colorAt(fallbackSource.pixel, dimensions);
|
|
7958
|
+
let fallbackWeight0 = fallbackSource.coverage0 * midpointWeight;
|
|
7959
|
+
let fallbackWeight1 = fallbackSource.coverage1 * midpointWeight;
|
|
7960
|
+
let fallbackWeight2 = fallbackSource.coverage2 * midpointWeight;
|
|
7961
|
+
let fallbackWeight3 = fallbackSource.coverage3 * midpointWeight;
|
|
7962
|
+
if (validCount0 <= 0.0 && (fallbackSource.receiverMask & 1u) != 0u) {
|
|
7963
|
+
let ownedWeight0 = min(fallbackWeight0, uncovered0);
|
|
7964
|
+
if (ownedWeight0 > 0.0) {
|
|
7965
|
+
filtered0 += fallbackColor * ownedWeight0;
|
|
7966
|
+
validCount0 += ownedWeight0;
|
|
7967
|
+
hasAcceptedSource0 = true;
|
|
7968
|
+
uncovered0 = max(uncovered0 - ownedWeight0, 0.0);
|
|
7969
|
+
nearestDepth0 = min(nearestDepth0, fallbackSource.depth);
|
|
7970
|
+
}
|
|
7971
|
+
}
|
|
7972
|
+
if (validCount1 <= 0.0 && (fallbackSource.receiverMask & 2u) != 0u) {
|
|
7973
|
+
let ownedWeight1 = min(fallbackWeight1, uncovered1);
|
|
7974
|
+
if (ownedWeight1 > 0.0) {
|
|
7975
|
+
filtered1 += fallbackColor * ownedWeight1;
|
|
7976
|
+
validCount1 += ownedWeight1;
|
|
7977
|
+
hasAcceptedSource1 = true;
|
|
7978
|
+
uncovered1 = max(uncovered1 - ownedWeight1, 0.0);
|
|
7979
|
+
nearestDepth1 = min(nearestDepth1, fallbackSource.depth);
|
|
7980
|
+
}
|
|
7981
|
+
}
|
|
7982
|
+
if (validCount2 <= 0.0 && (fallbackSource.receiverMask & 4u) != 0u) {
|
|
7983
|
+
let ownedWeight2 = min(fallbackWeight2, uncovered2);
|
|
7984
|
+
if (ownedWeight2 > 0.0) {
|
|
7985
|
+
filtered2 += fallbackColor * ownedWeight2;
|
|
7986
|
+
validCount2 += ownedWeight2;
|
|
7987
|
+
hasAcceptedSource2 = true;
|
|
7988
|
+
uncovered2 = max(uncovered2 - ownedWeight2, 0.0);
|
|
7989
|
+
nearestDepth2 = min(nearestDepth2, fallbackSource.depth);
|
|
7990
|
+
}
|
|
7991
|
+
}
|
|
7992
|
+
if (validCount3 <= 0.0 && (fallbackSource.receiverMask & 8u) != 0u) {
|
|
7993
|
+
let ownedWeight3 = min(fallbackWeight3, uncovered3);
|
|
7994
|
+
if (ownedWeight3 > 0.0) {
|
|
7995
|
+
filtered3 += fallbackColor * ownedWeight3;
|
|
7996
|
+
validCount3 += ownedWeight3;
|
|
7997
|
+
hasAcceptedSource3 = true;
|
|
7998
|
+
uncovered3 = max(uncovered3 - ownedWeight3, 0.0);
|
|
7999
|
+
nearestDepth3 = min(nearestDepth3, fallbackSource.depth);
|
|
8000
|
+
}
|
|
8001
|
+
}
|
|
8002
|
+
}
|
|
8003
|
+
}
|
|
8004
|
+
}
|
|
8005
|
+
let moving0 = temporalAt(receiverPixel, dimensions);
|
|
8006
|
+
let moving1 = temporalAt(receiverPixel + vec2<i32>(1, 0), dimensions);
|
|
8007
|
+
let moving2 = temporalAt(receiverPixel + vec2<i32>(0, 1), dimensions);
|
|
8008
|
+
let moving3 = temporalAt(receiverPixel + vec2<i32>(1, 1), dimensions);
|
|
8009
|
+
let motion0 = sourceMotionPixels(moving0, dimensions);
|
|
8010
|
+
let motion1 = sourceMotionPixels(moving1, dimensions);
|
|
8011
|
+
let motion2 = sourceMotionPixels(moving2, dimensions);
|
|
8012
|
+
let motion3 = sourceMotionPixels(moving3, dimensions);
|
|
8013
|
+
let centerMoving0 = moving0.validDepth && moving0.motionValid && sourceRadius(motion0, shutter) > 1e-5;
|
|
8014
|
+
let centerMoving1 = moving1.validDepth && moving1.motionValid && sourceRadius(motion1, shutter) > 1e-5;
|
|
8015
|
+
let centerMoving2 = moving2.validDepth && moving2.motionValid && sourceRadius(motion2, shutter) > 1e-5;
|
|
8016
|
+
let centerMoving3 = moving3.validDepth && moving3.motionValid && sourceRadius(motion3, shutter) > 1e-5;
|
|
8017
|
+
empty.color0 = integrateLongColor(filtered0, validCount0, hasAcceptedSource0, uncovered0, count, supportCount, centerColor0, centerMoving0);
|
|
8018
|
+
empty.color1 = integrateLongColor(filtered1, validCount1, hasAcceptedSource1, uncovered1, count, supportCount, centerColor1, centerMoving1);
|
|
8019
|
+
empty.color2 = integrateLongColor(filtered2, validCount2, hasAcceptedSource2, uncovered2, count, supportCount, centerColor2, centerMoving2);
|
|
8020
|
+
empty.color3 = integrateLongColor(filtered3, validCount3, hasAcceptedSource3, uncovered3, count, supportCount, centerColor3, centerMoving3);
|
|
8021
|
+
empty.depth0 = select(centerDepth, nearestDepth0, validCount0 > 0.0);
|
|
8022
|
+
empty.depth1 = select(centerDepth, nearestDepth1, validCount1 > 0.0);
|
|
8023
|
+
empty.depth2 = select(centerDepth, nearestDepth2, validCount2 > 0.0);
|
|
8024
|
+
empty.depth3 = select(centerDepth, nearestDepth3, validCount3 > 0.0);
|
|
8025
|
+
empty.coverage0 = validCount0 / max(f32(supportCount), 1.0);
|
|
8026
|
+
empty.coverage1 = validCount1 / max(f32(supportCount), 1.0);
|
|
8027
|
+
empty.coverage2 = validCount2 / max(f32(supportCount), 1.0);
|
|
8028
|
+
empty.coverage3 = validCount3 / max(f32(supportCount), 1.0);
|
|
8029
|
+
empty.motion = select(support.primaryMotion, support.secondaryMotion, support.primaryRadius <= 1e-5);
|
|
8030
|
+
empty.radius = max(support.primaryRadius, support.secondaryRadius);
|
|
8031
|
+
empty.receiverMask =
|
|
8032
|
+
select(0u, 1u, validCount0 > 0.0 || centerMoving0) |
|
|
8033
|
+
select(0u, 2u, validCount1 > 0.0 || centerMoving1) |
|
|
8034
|
+
select(0u, 4u, validCount2 > 0.0 || centerMoving2) |
|
|
8035
|
+
select(0u, 8u, validCount3 > 0.0 || centerMoving3);
|
|
8036
|
+
empty.validMask =
|
|
8037
|
+
select(0u, 1u, validCount0 > 0.0 || centerMoving0) |
|
|
8038
|
+
select(0u, 2u, validCount1 > 0.0 || centerMoving1) |
|
|
8039
|
+
select(0u, 4u, validCount2 > 0.0 || centerMoving2) |
|
|
8040
|
+
select(0u, 8u, validCount3 > 0.0 || centerMoving3);
|
|
8041
|
+
empty.valid = empty.validMask != 0u;
|
|
8042
|
+
return empty;
|
|
8043
|
+
}
|
|
8044
|
+
|
|
8045
|
+
fn colorAt(pixel : vec2<i32>, dimensions : vec2<i32>) -> vec4<f32> {
|
|
8046
|
+
return textureLoad(currentColor, clamp(pixel, vec2<i32>(0), dimensions - vec2<i32>(1)), 0);
|
|
8047
|
+
}
|
|
8048
|
+
|
|
8049
|
+
fn cacheFootprintContains(receiverPixel : vec2<i32>, ownerPixel : vec2<i32>) -> bool {
|
|
8050
|
+
return
|
|
8051
|
+
receiverPixel.x >= ownerPixel.x && receiverPixel.x <= ownerPixel.x + 1 &&
|
|
8052
|
+
receiverPixel.y >= ownerPixel.y && receiverPixel.y <= ownerPixel.y + 1;
|
|
8053
|
+
}
|
|
8054
|
+
|
|
8055
|
+
fn cachedSupportCovers(
|
|
8056
|
+
receiverPixel : vec2<i32>,
|
|
8057
|
+
ownerPixel : vec2<i32>,
|
|
8058
|
+
motion : vec2<f32>,
|
|
8059
|
+
radius : f32,
|
|
8060
|
+
) -> bool {
|
|
8061
|
+
if (!cacheFootprintContains(receiverPixel, ownerPixel)) { return false; }
|
|
8062
|
+
let motionLength = length(motion);
|
|
8063
|
+
if (motionLength <= 1e-5 || radius <= 1e-5) { return false; }
|
|
8064
|
+
let direction = motion / motionLength;
|
|
8065
|
+
let delta = vec2<f32>(receiverPixel - ownerPixel);
|
|
8066
|
+
let along = dot(delta, direction);
|
|
8067
|
+
let perpendicular = abs(delta.x * direction.y - delta.y * direction.x);
|
|
8068
|
+
return abs(along) <= radius + 1.0 && perpendicular <= 1.0;
|
|
8069
|
+
}
|
|
8070
|
+
|
|
8071
|
+
fn cachedDirectionMatches(support : MotionBlurSupport, motion : vec2<f32>) -> bool {
|
|
8072
|
+
let motionLength = length(motion);
|
|
8073
|
+
if (motionLength <= 1e-5) { return false; }
|
|
8074
|
+
let direction = motion / motionLength;
|
|
8075
|
+
if (support.primaryRadius > 1e-5 && abs(dot(normalize(support.primaryMotion), direction)) >= 0.94) {
|
|
8076
|
+
return true;
|
|
8077
|
+
}
|
|
8078
|
+
return support.secondaryRadius > 1e-5 && abs(dot(normalize(support.secondaryMotion), direction)) >= 0.94;
|
|
8079
|
+
}
|
|
8080
|
+
|
|
8081
|
+
fn depthGuidedHalfResolutionColor(
|
|
8082
|
+
localId : vec3<u32>,
|
|
8083
|
+
receiverPixel : vec2<i32>,
|
|
8084
|
+
support : MotionBlurSupport,
|
|
8085
|
+
centerDepth : f32,
|
|
8086
|
+
) -> MotionBlurCacheSample {
|
|
8087
|
+
let cell = vec2<i32>(localId.xy / vec2<u32>(2));
|
|
8088
|
+
let quadrant = (localId.y % 2u) * 2u + (localId.x % 2u);
|
|
8089
|
+
var selected = MotionBlurCacheSample(vec4<f32>(0.0), 0.0, false);
|
|
8090
|
+
var selectedDelta = 3.402823e+38;
|
|
8091
|
+
// Search the bounded adjacent cache cells, but accept only a cell whose
|
|
8092
|
+
// authored 2x2 footprint and direction actually cover this receiver.
|
|
8093
|
+
for (var y = -1; y <= 1; y += 1) {
|
|
8094
|
+
for (var x = -1; x <= 1; x += 1) {
|
|
8095
|
+
let candidateCell = cell + vec2<i32>(x, y);
|
|
8096
|
+
if (candidateCell.x < 0 || candidateCell.y < 0 || candidateCell.x >= 4 || candidateCell.y >= 4) { continue; }
|
|
8097
|
+
let cacheCellIndex = u32(candidateCell.y * 4 + candidateCell.x);
|
|
8098
|
+
let index = cacheCellIndex * 4u + quadrant;
|
|
8099
|
+
if (
|
|
8100
|
+
halfFilteredValid[index] == 0u ||
|
|
8101
|
+
depthReject(centerDepth, halfFilteredDepth[index]) ||
|
|
8102
|
+
!cachedDirectionMatches(support, halfFilteredMotion[cacheCellIndex]) ||
|
|
8103
|
+
!cachedSupportCovers(
|
|
8104
|
+
receiverPixel,
|
|
8105
|
+
halfFilteredReceiver[cacheCellIndex],
|
|
8106
|
+
halfFilteredMotion[cacheCellIndex],
|
|
8107
|
+
halfFilteredRadius[cacheCellIndex],
|
|
8108
|
+
)
|
|
8109
|
+
) { continue; }
|
|
8110
|
+
let delta = abs(halfFilteredDepth[index] - centerDepth);
|
|
8111
|
+
if (!selected.valid || delta < selectedDelta) {
|
|
8112
|
+
selected.color = halfFilteredColor[index];
|
|
8113
|
+
selected.coverage = halfFilteredCoverage[index];
|
|
8114
|
+
selectedDelta = delta;
|
|
8115
|
+
selected.valid = true;
|
|
8116
|
+
}
|
|
8117
|
+
}
|
|
8118
|
+
}
|
|
8119
|
+
return selected;
|
|
8120
|
+
}
|
|
8121
|
+
|
|
8122
|
+
@compute @workgroup_size(8, 8)
|
|
8123
|
+
fn tile_summary(
|
|
8124
|
+
@builtin(global_invocation_id) id : vec3<u32>,
|
|
8125
|
+
@builtin(local_invocation_id) localId : vec3<u32>,
|
|
8126
|
+
@builtin(local_invocation_index) localIndex : u32,
|
|
8127
|
+
@builtin(workgroup_id) workgroupId : vec3<u32>,
|
|
8128
|
+
) {
|
|
8129
|
+
let dimensions = textureDimensions(sceneTemporal);
|
|
8130
|
+
let tiles = (dimensions + vec2<u32>(15)) / vec2<u32>(16);
|
|
8131
|
+
let tile = workgroupId.xy;
|
|
8132
|
+
let inTile = tile.x < tiles.x && tile.y < tiles.y;
|
|
8133
|
+
var maxMotion = vec2<f32>(0.0);
|
|
8134
|
+
var maxLength = 0.0;
|
|
8135
|
+
var maxSource = vec2<i32>(0);
|
|
8136
|
+
var secondaryMotion = vec2<f32>(0.0);
|
|
8137
|
+
var secondaryLength = 0.0;
|
|
8138
|
+
var secondarySource = vec2<i32>(0);
|
|
8139
|
+
for (var localY = 0u; localY < 2u; localY += 1u) {
|
|
8140
|
+
for (var localX = 0u; localX < 2u; localX += 1u) {
|
|
8141
|
+
let pixel = tile * vec2<u32>(16) + localId.xy * vec2<u32>(2) + vec2<u32>(localX, localY);
|
|
8142
|
+
if (!inTile || pixel.x >= dimensions.x || pixel.y >= dimensions.y) { continue; }
|
|
8143
|
+
let sample = temporalAt(vec2<i32>(pixel), vec2<i32>(dimensions));
|
|
8144
|
+
if (!sample.validDepth || !sample.motionValid) { continue; }
|
|
8145
|
+
let motionLength = length(sample.motion * vec2<f32>(dimensions));
|
|
8146
|
+
if (motionLength > maxLength) {
|
|
8147
|
+
if (distinctDirection(maxMotion, sample.motion)) {
|
|
8148
|
+
secondaryMotion = maxMotion;
|
|
8149
|
+
secondaryLength = maxLength;
|
|
8150
|
+
secondarySource = maxSource;
|
|
8151
|
+
}
|
|
8152
|
+
maxLength = motionLength;
|
|
8153
|
+
maxMotion = sample.motion;
|
|
8154
|
+
maxSource = vec2<i32>(pixel);
|
|
8155
|
+
} else if (
|
|
8156
|
+
motionLength > secondaryLength &&
|
|
8157
|
+
distinctDirection(maxMotion, sample.motion)
|
|
8158
|
+
) {
|
|
8159
|
+
secondaryLength = motionLength;
|
|
8160
|
+
secondaryMotion = sample.motion;
|
|
8161
|
+
secondarySource = vec2<i32>(pixel);
|
|
8162
|
+
}
|
|
8163
|
+
}
|
|
8164
|
+
}
|
|
8165
|
+
tileMotionScratch[localIndex] = maxMotion;
|
|
8166
|
+
tileLengthScratch[localIndex] = maxLength;
|
|
8167
|
+
tileSourceScratch[localIndex] = maxSource;
|
|
8168
|
+
tileSecondaryMotionScratch[localIndex] = secondaryMotion;
|
|
8169
|
+
tileSecondaryLengthScratch[localIndex] = secondaryLength;
|
|
8170
|
+
tileSecondarySourceScratch[localIndex] = secondarySource;
|
|
8171
|
+
workgroupBarrier();
|
|
8172
|
+
for (var stride = 32u; stride > 0u; stride = stride / 2u) {
|
|
8173
|
+
if (localIndex < stride) {
|
|
8174
|
+
let otherIndex = localIndex + stride;
|
|
8175
|
+
let otherLength = tileLengthScratch[otherIndex];
|
|
8176
|
+
if (otherLength > tileLengthScratch[localIndex]) {
|
|
8177
|
+
let oldMotion = tileMotionScratch[localIndex];
|
|
8178
|
+
let oldLength = tileLengthScratch[localIndex];
|
|
8179
|
+
let oldSource = tileSourceScratch[localIndex];
|
|
8180
|
+
tileLengthScratch[localIndex] = otherLength;
|
|
8181
|
+
tileMotionScratch[localIndex] = tileMotionScratch[otherIndex];
|
|
8182
|
+
tileSourceScratch[localIndex] = tileSourceScratch[otherIndex];
|
|
8183
|
+
if (
|
|
8184
|
+
oldLength > 1e-5 &&
|
|
8185
|
+
distinctDirection(tileMotionScratch[localIndex], oldMotion) &&
|
|
8186
|
+
oldLength > tileSecondaryLengthScratch[localIndex]
|
|
8187
|
+
) {
|
|
8188
|
+
tileSecondaryLengthScratch[localIndex] = oldLength;
|
|
8189
|
+
tileSecondaryMotionScratch[localIndex] = oldMotion;
|
|
8190
|
+
tileSecondarySourceScratch[localIndex] = oldSource;
|
|
8191
|
+
}
|
|
8192
|
+
}
|
|
8193
|
+
if (
|
|
8194
|
+
tileSecondaryLengthScratch[otherIndex] > tileSecondaryLengthScratch[localIndex] &&
|
|
8195
|
+
distinctDirection(
|
|
8196
|
+
tileMotionScratch[localIndex],
|
|
8197
|
+
tileSecondaryMotionScratch[otherIndex],
|
|
8198
|
+
)
|
|
8199
|
+
) {
|
|
8200
|
+
tileSecondaryLengthScratch[localIndex] = tileSecondaryLengthScratch[otherIndex];
|
|
8201
|
+
tileSecondaryMotionScratch[localIndex] = tileSecondaryMotionScratch[otherIndex];
|
|
8202
|
+
tileSecondarySourceScratch[localIndex] = tileSecondarySourceScratch[otherIndex];
|
|
8203
|
+
}
|
|
8204
|
+
if (
|
|
8205
|
+
tileLengthScratch[otherIndex] > tileSecondaryLengthScratch[localIndex] &&
|
|
8206
|
+
distinctDirection(tileMotionScratch[localIndex], tileMotionScratch[otherIndex])
|
|
8207
|
+
) {
|
|
8208
|
+
tileSecondaryLengthScratch[localIndex] = tileLengthScratch[otherIndex];
|
|
8209
|
+
tileSecondaryMotionScratch[localIndex] = tileMotionScratch[otherIndex];
|
|
8210
|
+
tileSecondarySourceScratch[localIndex] = tileSourceScratch[otherIndex];
|
|
8211
|
+
}
|
|
8212
|
+
}
|
|
8213
|
+
workgroupBarrier();
|
|
8214
|
+
}
|
|
8215
|
+
if (localIndex == 0u && inTile) {
|
|
8216
|
+
let tileIndex = tile.y * tiles.x + tile.x;
|
|
8217
|
+
tileSummary[tileIndex] = MotionBlurTileSummary(
|
|
8218
|
+
vec4<f32>(
|
|
8219
|
+
tileMotionScratch[0],
|
|
8220
|
+
f32(tileSourceScratch[0].x),
|
|
8221
|
+
f32(tileSourceScratch[0].y),
|
|
8222
|
+
),
|
|
8223
|
+
vec4<f32>(
|
|
8224
|
+
tileSecondaryMotionScratch[0],
|
|
8225
|
+
f32(tileSecondarySourceScratch[0].x),
|
|
8226
|
+
f32(tileSecondarySourceScratch[0].y),
|
|
8227
|
+
),
|
|
8228
|
+
);
|
|
8229
|
+
}
|
|
8230
|
+
}
|
|
8231
|
+
|
|
8232
|
+
@compute @workgroup_size(8, 8)
|
|
8233
|
+
fn reconstruct(
|
|
8234
|
+
@builtin(global_invocation_id) id : vec3<u32>,
|
|
8235
|
+
@builtin(local_invocation_id) localId : vec3<u32>,
|
|
8236
|
+
@builtin(workgroup_id) workgroupId : vec3<u32>,
|
|
8237
|
+
) {
|
|
8238
|
+
let dimensions = textureDimensions(currentColor);
|
|
8239
|
+
let colorDimensions = vec2<i32>(dimensions);
|
|
8240
|
+
let pixel = vec2<i32>(id.xy);
|
|
8241
|
+
let inBounds = id.x < dimensions.x && id.y < dimensions.y;
|
|
8242
|
+
let workgroupBase = vec2<i32>(workgroupId.xy * vec2<u32>(8));
|
|
8243
|
+
let ownsHalfCell = (localId.x % 2u == 0u) && (localId.y % 2u == 0u);
|
|
8244
|
+
let halfIndex = (localId.y / 2u) * 4u + (localId.x / 2u);
|
|
8245
|
+
let localIndex = localId.y * 8u + localId.x;
|
|
8246
|
+
if (localIndex == 0u) {
|
|
8247
|
+
scanTileNeighbourhood(workgroupBase, colorDimensions);
|
|
8248
|
+
}
|
|
8249
|
+
workgroupBarrier();
|
|
8250
|
+
let support = resolveMotionBlurSupport(pixel, colorDimensions);
|
|
8251
|
+
let longVector = support.valid && support.longVector;
|
|
8252
|
+
|
|
8253
|
+
longVectorScratch[localId.y * 8u + localId.x] = select(0u, 1u, longVector);
|
|
8254
|
+
workgroupBarrier();
|
|
8255
|
+
for (var longStride = 32u; longStride > 0u; longStride = longStride / 2u) {
|
|
8256
|
+
let localIndex = localId.y * 8u + localId.x;
|
|
8257
|
+
if (localIndex < longStride) {
|
|
8258
|
+
longVectorScratch[localIndex] = max(
|
|
8259
|
+
longVectorScratch[localIndex],
|
|
8260
|
+
longVectorScratch[localIndex + longStride],
|
|
8261
|
+
);
|
|
8262
|
+
}
|
|
8263
|
+
workgroupBarrier();
|
|
8264
|
+
}
|
|
8265
|
+
let anyLongVector = longVectorScratch[0] != 0u;
|
|
8266
|
+
if (anyLongVector && ownsHalfCell) {
|
|
8267
|
+
// Every designated owner evaluates the full support exactly once. The
|
|
8268
|
+
// cached result includes filtered color, nearest accepted depth, and
|
|
8269
|
+
// coverage; no 2x2 prefilter is substituted for the long gather.
|
|
8270
|
+
let sourcePixel = workgroupBase + vec2<i32>(localId.xy);
|
|
8271
|
+
let sourceSupport = resolveMotionBlurSupport(sourcePixel, colorDimensions);
|
|
8272
|
+
let filtered = longSupportFilter(
|
|
8273
|
+
sourcePixel,
|
|
8274
|
+
sourceSupport,
|
|
8275
|
+
temporalAt(sourcePixel, colorDimensions).depth,
|
|
8276
|
+
colorDimensions,
|
|
8277
|
+
);
|
|
8278
|
+
halfFilteredReceiver[halfIndex] = sourcePixel;
|
|
8279
|
+
halfFilteredMotion[halfIndex] = filtered.motion;
|
|
8280
|
+
halfFilteredRadius[halfIndex] = filtered.radius;
|
|
8281
|
+
let cacheBase = halfIndex * 4u;
|
|
8282
|
+
halfFilteredColor[cacheBase] = filtered.color0;
|
|
8283
|
+
halfFilteredColor[cacheBase + 1u] = filtered.color1;
|
|
8284
|
+
halfFilteredColor[cacheBase + 2u] = filtered.color2;
|
|
8285
|
+
halfFilteredColor[cacheBase + 3u] = filtered.color3;
|
|
8286
|
+
halfFilteredDepth[cacheBase] = filtered.depth0;
|
|
8287
|
+
halfFilteredDepth[cacheBase + 1u] = filtered.depth1;
|
|
8288
|
+
halfFilteredDepth[cacheBase + 2u] = filtered.depth2;
|
|
8289
|
+
halfFilteredDepth[cacheBase + 3u] = filtered.depth3;
|
|
8290
|
+
halfFilteredCoverage[cacheBase] = filtered.coverage0;
|
|
8291
|
+
halfFilteredCoverage[cacheBase + 1u] = filtered.coverage1;
|
|
8292
|
+
halfFilteredCoverage[cacheBase + 2u] = filtered.coverage2;
|
|
8293
|
+
halfFilteredCoverage[cacheBase + 3u] = filtered.coverage3;
|
|
8294
|
+
halfFilteredValid[cacheBase] = select(0u, 1u, (filtered.validMask & 1u) != 0u);
|
|
8295
|
+
halfFilteredValid[cacheBase + 1u] = select(0u, 1u, (filtered.validMask & 2u) != 0u);
|
|
8296
|
+
halfFilteredValid[cacheBase + 2u] = select(0u, 1u, (filtered.validMask & 4u) != 0u);
|
|
8297
|
+
halfFilteredValid[cacheBase + 3u] = select(0u, 1u, (filtered.validMask & 8u) != 0u);
|
|
8298
|
+
}
|
|
8299
|
+
workgroupBarrier();
|
|
8300
|
+
// No invocation can return before both uniform barriers above. This keeps
|
|
8301
|
+
// odd extents and out-of-bounds lanes from allowing a long source to read a
|
|
8302
|
+
// cache entry that another invocation has not published yet.
|
|
8303
|
+
if (!inBounds) {
|
|
8304
|
+
return;
|
|
8305
|
+
}
|
|
8306
|
+
if (!support.valid) {
|
|
8307
|
+
textureStore(outputColor, pixel, colorAt(pixel, colorDimensions));
|
|
8308
|
+
return;
|
|
8309
|
+
}
|
|
8310
|
+
if (longVector) {
|
|
8311
|
+
let cached = depthGuidedHalfResolutionColor(
|
|
8312
|
+
localId,
|
|
8313
|
+
pixel,
|
|
8314
|
+
support,
|
|
8315
|
+
temporalAt(pixel, colorDimensions).depth,
|
|
8316
|
+
);
|
|
8317
|
+
if (cached.valid) {
|
|
8318
|
+
textureStore(outputColor, pixel, cached.color);
|
|
8319
|
+
} else {
|
|
8320
|
+
textureStore(outputColor, pixel, colorAt(pixel, colorDimensions));
|
|
8321
|
+
}
|
|
8322
|
+
return;
|
|
8323
|
+
}
|
|
8324
|
+
let useSecondDirection = support.secondaryRadius > 1e-5 && distinctDirection(support.primaryMotion, support.secondaryMotion);
|
|
8325
|
+
let count = min(params.sampleCount, 16u);
|
|
8326
|
+
let supportCount = max(count - 1u, 1u);
|
|
8327
|
+
let primaryCount = select(supportCount, max(supportCount / 2u, 1u), useSecondDirection);
|
|
8328
|
+
let secondaryCount = max(supportCount - primaryCount, 1u);
|
|
8329
|
+
let shutter = clamp(params.shutterAngle / 360.0, 0.0, 1.0);
|
|
8330
|
+
let center = temporalAt(pixel, colorDimensions);
|
|
8331
|
+
let centerMotion = sourceMotionPixels(center, colorDimensions);
|
|
8332
|
+
let centerMoving = center.validDepth && center.motionValid && sourceRadius(centerMotion, shutter) > 1e-5;
|
|
8333
|
+
let centerColor = colorAt(pixel, colorDimensions);
|
|
8334
|
+
var accum = vec4<f32>(0.0);
|
|
8335
|
+
var weight = 0.0;
|
|
8336
|
+
for (var index = 0u; index < 16u; index += 1u) {
|
|
8337
|
+
if (index >= supportCount) { break; }
|
|
8338
|
+
let secondDirection = useSecondDirection && index >= primaryCount;
|
|
8339
|
+
let localIndex = select(index, index - primaryCount, secondDirection);
|
|
8340
|
+
let localCount = select(primaryCount, secondaryCount, secondDirection);
|
|
8341
|
+
let selectedMotion = select(support.primaryMotion, support.secondaryMotion, secondDirection);
|
|
8342
|
+
let selectedRadius = select(support.primaryRadius, support.secondaryRadius, secondDirection);
|
|
8343
|
+
let tap = supportTap(
|
|
8344
|
+
pixel,
|
|
8345
|
+
localIndex,
|
|
8346
|
+
localCount,
|
|
8347
|
+
selectedMotion,
|
|
8348
|
+
selectedRadius,
|
|
8349
|
+
select(support.primaryAnchor, support.secondaryAnchor, secondDirection),
|
|
8350
|
+
colorDimensions,
|
|
8351
|
+
);
|
|
8352
|
+
if (!tap.valid) { continue; }
|
|
8353
|
+
let source = sourceAt(pixel, tap.pixel, center.depth, shutter, colorDimensions);
|
|
8354
|
+
if (!source.valid) { continue; }
|
|
8355
|
+
let sampleColor = colorAt(source.pixel, colorDimensions);
|
|
8356
|
+
accum += sampleColor;
|
|
8357
|
+
weight += 1.0;
|
|
8358
|
+
}
|
|
8359
|
+
let uncovered = max(f32(supportCount) - weight, 0.0);
|
|
8360
|
+
let acceptedColor = select(vec3<f32>(0.0), accum.rgb / max(weight, 1.0), weight > 0.0);
|
|
8361
|
+
let missingForeground = 1.0 - clamp(centerColor.a, 0.0, 1.0);
|
|
8362
|
+
let edgeCoverage = select(missingForeground, 1.0, centerMoving);
|
|
8363
|
+
let edgeFill = acceptedColor * edgeCoverage * EDGE_FILL_FACTOR;
|
|
8364
|
+
let movingFallback = select(centerColor.rgb, edgeFill, weight > 0.0);
|
|
8365
|
+
let uncoveredColor = select(vec3<f32>(0.0), movingFallback, centerMoving);
|
|
8366
|
+
let acceptedAlpha = select(0.0, accum.a / max(weight, 1.0), weight > 0.0);
|
|
8367
|
+
let acceptedCoverage = weight + missingForeground * uncovered;
|
|
8368
|
+
let reconstructedAlpha = acceptedAlpha * acceptedCoverage / max(f32(count), 1.0);
|
|
8369
|
+
let outputAlpha = clamp(max(centerColor.a, reconstructedAlpha), 0.0, 1.0);
|
|
8370
|
+
let emptyReceiver =
|
|
8371
|
+
!centerMoving &&
|
|
8372
|
+
weight > 0.0 &&
|
|
8373
|
+
max(max(abs(centerColor.r), abs(centerColor.g)), abs(centerColor.b)) < 1e-4;
|
|
8374
|
+
let sourceScale = select(1.0, 1.0 - EDGE_FILL_FACTOR, emptyReceiver);
|
|
8375
|
+
let sourceContribution = accum.rgb * sourceScale;
|
|
8376
|
+
let trailScale = select(1.0, 1.0 - EDGE_FILL_FACTOR, emptyReceiver);
|
|
8377
|
+
let trailContribution = uncoveredColor * uncovered * trailScale;
|
|
8378
|
+
textureStore(
|
|
8379
|
+
outputColor,
|
|
8380
|
+
pixel,
|
|
8381
|
+
vec4<f32>((sourceContribution + centerColor.rgb + trailContribution) / max(f32(count), 1.0), outputAlpha),
|
|
8382
|
+
);
|
|
8383
|
+
}
|
|
8384
|
+
`;
|
|
8385
|
+
function encodeMotionBlurComputeParams(params, frameDeltaSeconds = 1 / 60, reset = false) {
|
|
8386
|
+
const bytes2 = new Uint8Array(MOTION_BLUR_PARAMS_BYTE_SIZE);
|
|
8387
|
+
const view = new DataView(bytes2.buffer);
|
|
8388
|
+
const uniformDeltaSeconds = Number.isFinite(frameDeltaSeconds) ? frameDeltaSeconds : 0;
|
|
8389
|
+
view.setFloat32(0, params.shutterAngle, true);
|
|
8390
|
+
view.setFloat32(4, params.maxRadiusPixels, true);
|
|
8391
|
+
view.setUint32(8, effectiveMotionBlurSampleCount(params.sampleCount), true);
|
|
8392
|
+
view.setUint32(12, reset ? 1 : 0, true);
|
|
8393
|
+
view.setFloat32(16, params.targetFps, true);
|
|
8394
|
+
view.setFloat32(20, motionBlurExposureScale(uniformDeltaSeconds, params.targetFps), true);
|
|
8395
|
+
view.setFloat32(24, uniformDeltaSeconds, true);
|
|
8396
|
+
view.setUint32(28, 0, true);
|
|
8397
|
+
return bytes2;
|
|
8398
|
+
}
|
|
8399
|
+
function positiveDimension(value) {
|
|
8400
|
+
return Number.isFinite(value) && value > 0 ? Math.ceil(value) : 1;
|
|
8401
|
+
}
|
|
8402
|
+
function planMotionBlur(params, context, temporal = {
|
|
8403
|
+
frameDeltaSeconds: 1 / 60,
|
|
8404
|
+
reset: false
|
|
8405
|
+
}) {
|
|
8406
|
+
if (!motionBlurTemporalDemand(params)) return ok$1({ resources: [], passes: [] });
|
|
8407
|
+
try {
|
|
8408
|
+
const sceneTemporal = context.sceneData.require(SCENE_DATA_TEMPORAL_V1_SCHEMA);
|
|
8409
|
+
const outputFormat = context.targets.find((target5) => target5.name === "motion-output")?.format ?? "rgba16float";
|
|
8410
|
+
const fullscreen = {
|
|
8411
|
+
kind: "fullscreen-program",
|
|
8412
|
+
name: "motion-blur",
|
|
8413
|
+
source: "forgeax::shader::motion-blur",
|
|
8414
|
+
reads: ["scene-color", "scene-temporal"],
|
|
8415
|
+
params: {
|
|
8416
|
+
byteSize: MOTION_BLUR_PARAMS_BYTE_SIZE,
|
|
8417
|
+
defaultValue: new Uint8Array(MOTION_BLUR_PARAMS_BYTE_SIZE)
|
|
8418
|
+
}
|
|
8419
|
+
};
|
|
8420
|
+
const computeCapable = context.caps.compute === true && context.caps.storageBuffer === true && context.caps.storageTexture === true && context.caps.rgba16floatRenderable === true && outputFormat === "rgba16float";
|
|
8421
|
+
if (computeCapable) {
|
|
8422
|
+
const width = positiveDimension(context.frame.width);
|
|
8423
|
+
const height = positiveDimension(context.frame.height);
|
|
8424
|
+
const tileWidth = Math.max(1, Math.ceil(width / 16));
|
|
8425
|
+
const tileHeight = Math.max(1, Math.ceil(height / 16));
|
|
8426
|
+
return ok$1({
|
|
8427
|
+
resources: [
|
|
8428
|
+
{
|
|
8429
|
+
kind: "compute-program",
|
|
8430
|
+
name: "motion-blur-compute",
|
|
8431
|
+
program: {
|
|
8432
|
+
wgsl: MOTION_BLUR_COMPUTE_WGSL,
|
|
8433
|
+
entryPoints: ["tile_summary", "reconstruct"],
|
|
8434
|
+
bindings: [
|
|
8435
|
+
{
|
|
8436
|
+
entries: [
|
|
8437
|
+
{
|
|
8438
|
+
binding: 0,
|
|
8439
|
+
visibility: 4,
|
|
8440
|
+
texture: { sampleType: "float", viewDimension: "2d" }
|
|
8441
|
+
},
|
|
8442
|
+
{
|
|
8443
|
+
binding: 1,
|
|
8444
|
+
visibility: 4,
|
|
8445
|
+
texture: { sampleType: "float", viewDimension: "2d" }
|
|
8446
|
+
},
|
|
8447
|
+
{ binding: 2, visibility: 4, buffer: { type: "storage" } },
|
|
8448
|
+
{
|
|
8449
|
+
binding: 3,
|
|
8450
|
+
visibility: 4,
|
|
8451
|
+
storageTexture: {
|
|
8452
|
+
access: "write-only",
|
|
8453
|
+
format: "rgba16float",
|
|
8454
|
+
viewDimension: "2d"
|
|
8455
|
+
}
|
|
8456
|
+
},
|
|
8457
|
+
{ binding: 4, visibility: 4, buffer: { type: "uniform" } }
|
|
8458
|
+
]
|
|
8459
|
+
}
|
|
8460
|
+
]
|
|
8461
|
+
}
|
|
8462
|
+
},
|
|
8463
|
+
{
|
|
8464
|
+
kind: "buffer",
|
|
8465
|
+
name: "motion-blur-tile-summary",
|
|
8466
|
+
size: tileWidth * tileHeight * 32,
|
|
8467
|
+
usage: ["storage"]
|
|
8468
|
+
},
|
|
8469
|
+
{
|
|
8470
|
+
kind: "buffer",
|
|
8471
|
+
name: "motion-blur-compute-params",
|
|
8472
|
+
size: MOTION_BLUR_PARAMS_BYTE_SIZE,
|
|
8473
|
+
usage: ["uniform"],
|
|
8474
|
+
data: encodeMotionBlurComputeParams(params, temporal.frameDeltaSeconds, temporal.reset)
|
|
8475
|
+
},
|
|
8476
|
+
{
|
|
8477
|
+
kind: "prepared-gpu-resource",
|
|
8478
|
+
name: "motion-blur-input",
|
|
8479
|
+
resource: { kind: "texture-view", value: void 0 },
|
|
8480
|
+
logicalTarget: "motion-input"
|
|
8481
|
+
},
|
|
8482
|
+
{
|
|
8483
|
+
kind: "prepared-gpu-resource",
|
|
8484
|
+
name: "motion-blur-temporal",
|
|
8485
|
+
resource: { kind: "texture-view", value: void 0 },
|
|
8486
|
+
logicalTarget: sceneTemporal
|
|
8487
|
+
},
|
|
8488
|
+
{
|
|
8489
|
+
kind: "prepared-gpu-resource",
|
|
8490
|
+
name: "motion-blur-output",
|
|
8491
|
+
resource: { kind: "texture-view", value: void 0 },
|
|
8492
|
+
logicalTarget: "motion-output"
|
|
8493
|
+
},
|
|
8494
|
+
{
|
|
8495
|
+
kind: "compute-bindings",
|
|
8496
|
+
name: "motion-blur-compute-bindings",
|
|
8497
|
+
program: "motion-blur-compute",
|
|
8498
|
+
entries: [
|
|
8499
|
+
{ binding: 0, resource: "motion-blur-input" },
|
|
8500
|
+
{ binding: 1, resource: "motion-blur-temporal" },
|
|
8501
|
+
{ binding: 2, resource: "motion-blur-tile-summary" },
|
|
8502
|
+
{ binding: 3, resource: "motion-blur-output" },
|
|
8503
|
+
{ binding: 4, resource: "motion-blur-compute-params" }
|
|
8504
|
+
]
|
|
8505
|
+
}
|
|
8506
|
+
],
|
|
8507
|
+
passes: [
|
|
8508
|
+
{
|
|
8509
|
+
kind: "compute",
|
|
8510
|
+
name: "motion-blur-tile-summary",
|
|
8511
|
+
program: "motion-blur-compute",
|
|
8512
|
+
bindings: "motion-blur-compute-bindings",
|
|
8513
|
+
dispatches: [
|
|
8514
|
+
{
|
|
8515
|
+
kind: "direct",
|
|
8516
|
+
entryPoint: "tile_summary",
|
|
8517
|
+
workgroups: [tileWidth, tileHeight, 1]
|
|
8518
|
+
}
|
|
8519
|
+
]
|
|
8520
|
+
},
|
|
8521
|
+
{
|
|
8522
|
+
kind: "compute",
|
|
8523
|
+
// Keep the public post-process identity stable while the compute
|
|
8524
|
+
// lane adds its private tile-summary producer before it.
|
|
8525
|
+
name: "motion-blur",
|
|
8526
|
+
program: "motion-blur-compute",
|
|
8527
|
+
bindings: "motion-blur-compute-bindings",
|
|
8528
|
+
dispatches: [
|
|
8529
|
+
{
|
|
8530
|
+
kind: "direct",
|
|
8531
|
+
entryPoint: "reconstruct",
|
|
8532
|
+
workgroups: [
|
|
8533
|
+
Math.max(1, Math.ceil(width / 8)),
|
|
8534
|
+
Math.max(1, Math.ceil(height / 8)),
|
|
8535
|
+
1
|
|
8536
|
+
]
|
|
8537
|
+
}
|
|
8538
|
+
]
|
|
8539
|
+
}
|
|
8540
|
+
]
|
|
8541
|
+
});
|
|
8542
|
+
}
|
|
8543
|
+
return ok$1({
|
|
8544
|
+
resources: [
|
|
8545
|
+
fullscreen,
|
|
8546
|
+
{
|
|
8547
|
+
kind: "graphics-program",
|
|
8548
|
+
name: "motion-blur-pipeline",
|
|
8549
|
+
program: {
|
|
8550
|
+
shader: MOTION_BLUR_FEATURE_IDENTITY,
|
|
8551
|
+
vertexLayout: "none",
|
|
8552
|
+
colorFormats: [outputFormat],
|
|
8553
|
+
sampleCount: 1
|
|
8554
|
+
}
|
|
8555
|
+
},
|
|
8556
|
+
{
|
|
8557
|
+
kind: "graphics-bindings",
|
|
8558
|
+
name: "motion-blur-bindings",
|
|
8559
|
+
program: "motion-blur-pipeline",
|
|
8560
|
+
values: {
|
|
8561
|
+
group: 1,
|
|
8562
|
+
fullscreen: true,
|
|
8563
|
+
shader: MOTION_BLUR_FEATURE_IDENTITY,
|
|
8564
|
+
input: "motion-input",
|
|
8565
|
+
temporal: sceneTemporal
|
|
8566
|
+
},
|
|
8567
|
+
logicalTargets: { input: "motion-input" }
|
|
8568
|
+
}
|
|
8569
|
+
],
|
|
8570
|
+
passes: [
|
|
8571
|
+
{
|
|
8572
|
+
kind: "raster",
|
|
8573
|
+
name: "motion-blur",
|
|
8574
|
+
colorAttachments: [{ target: "motion-output", loadOp: "clear", storeOp: "store" }],
|
|
8575
|
+
sampledTargets: ["motion-input", sceneTemporal],
|
|
8576
|
+
draws: [
|
|
8577
|
+
{
|
|
8578
|
+
program: "motion-blur-pipeline",
|
|
8579
|
+
bindings: ["motion-blur-bindings"],
|
|
8580
|
+
vertexData: [],
|
|
8581
|
+
vertexLayout: "none",
|
|
8582
|
+
draw: { kind: "draw", vertexCount: 3, instanceCount: 1 }
|
|
8583
|
+
}
|
|
8584
|
+
]
|
|
8585
|
+
}
|
|
8586
|
+
]
|
|
8587
|
+
});
|
|
8588
|
+
} catch (cause) {
|
|
8589
|
+
return err$1(cause);
|
|
8590
|
+
}
|
|
8591
|
+
}
|
|
8592
|
+
function createMotionBlurFeature(read) {
|
|
8593
|
+
return Object.freeze({
|
|
8594
|
+
identity: MOTION_BLUR_FEATURE_IDENTITY,
|
|
8595
|
+
// The program is an Engine-owned static WGSL source. Immediate creation
|
|
8596
|
+
// lets a newly admitted frame use the same graph/dispatch receipt instead
|
|
8597
|
+
// of silently falling back to the raster post effect while the validated
|
|
8598
|
+
// manifest adapter is still warming.
|
|
8599
|
+
shaderModuleMode: "immediate",
|
|
8600
|
+
extract: (_context) => {
|
|
8601
|
+
const input = read();
|
|
8602
|
+
const validated = validateMotionBlurParams(input?.params);
|
|
8603
|
+
if (!validated.ok) return err$1(validated.error);
|
|
8604
|
+
const params = input?.params === void 0 ? void 0 : validated.value;
|
|
8605
|
+
return ok$1({
|
|
8606
|
+
params,
|
|
8607
|
+
demanded: motionBlurTemporalDemand(params),
|
|
8608
|
+
frameDeltaSeconds: input?.frameDeltaSeconds === void 0 ? 1 / 60 : input.frameDeltaSeconds,
|
|
8609
|
+
reset: input?.reset === true
|
|
8610
|
+
});
|
|
8611
|
+
},
|
|
8612
|
+
plan: (frame, context) => frame.params === void 0 ? ok$1({ resources: [], passes: [] }) : planMotionBlur(frame.params, context, frame)
|
|
8613
|
+
});
|
|
8614
|
+
}
|
|
6976
8615
|
var AUTO_EXPOSURE_FUSED_GRAPH_PASS = "auto-exposure-meter";
|
|
6977
8616
|
var AUTO_EXPOSURE_HISTOGRAM_DISPATCH = Object.freeze({
|
|
6978
8617
|
x: 4,
|
|
@@ -7834,60 +9473,183 @@ function addStandardColorLutPass(graph, input, output, lut) {
|
|
|
7834
9473
|
}
|
|
7835
9474
|
|
|
7836
9475
|
// src/pipeline/standard-post.ts
|
|
9476
|
+
var STANDARD_BLOOM_MAX_LEVELS = 5;
|
|
7837
9477
|
var STANDARD_BLOOM_TARGET_SPEC = {
|
|
7838
|
-
composited: { label: "bloom-composited", format: "rgba16float"
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
blurV: { label: "bloom-blur-v", format: "rgba16float", size: "half-surface" }
|
|
9478
|
+
composited: { label: "bloom-composited", format: "rgba16float" },
|
|
9479
|
+
downsamplePrefix: "bloom-downsample-",
|
|
9480
|
+
upsamplePrefix: "bloom-upsample-"
|
|
7842
9481
|
};
|
|
9482
|
+
function deriveStandardBloomLevels(outputWidth, outputHeight) {
|
|
9483
|
+
const levels = [];
|
|
9484
|
+
let width = Math.max(1, Math.ceil(outputWidth / 2));
|
|
9485
|
+
let height = Math.max(1, Math.ceil(outputHeight / 2));
|
|
9486
|
+
for (let level = 0; level < STANDARD_BLOOM_MAX_LEVELS; level += 1) {
|
|
9487
|
+
levels.push({ width, height });
|
|
9488
|
+
if (width === 1 && height === 1) break;
|
|
9489
|
+
width = Math.max(1, Math.ceil(width / 2));
|
|
9490
|
+
height = Math.max(1, Math.ceil(height / 2));
|
|
9491
|
+
}
|
|
9492
|
+
return levels;
|
|
9493
|
+
}
|
|
9494
|
+
function standardBloomTargetSpecs(outputWidth, outputHeight) {
|
|
9495
|
+
const levels = deriveStandardBloomLevels(outputWidth, outputHeight);
|
|
9496
|
+
return {
|
|
9497
|
+
composited: {
|
|
9498
|
+
label: STANDARD_BLOOM_TARGET_SPEC.composited.label,
|
|
9499
|
+
format: STANDARD_BLOOM_TARGET_SPEC.composited.format,
|
|
9500
|
+
size: { width: outputWidth, height: outputHeight },
|
|
9501
|
+
domain: "linear-hdr"
|
|
9502
|
+
},
|
|
9503
|
+
downsample: levels.map((size, level) => ({
|
|
9504
|
+
label: `${STANDARD_BLOOM_TARGET_SPEC.downsamplePrefix}${level}`,
|
|
9505
|
+
format: "rgba16float",
|
|
9506
|
+
size,
|
|
9507
|
+
domain: "linear-hdr"
|
|
9508
|
+
})),
|
|
9509
|
+
upsample: levels.slice(0, -1).map((size, level) => ({
|
|
9510
|
+
label: `${STANDARD_BLOOM_TARGET_SPEC.upsamplePrefix}${level}`,
|
|
9511
|
+
format: "rgba16float",
|
|
9512
|
+
size,
|
|
9513
|
+
domain: "linear-hdr"
|
|
9514
|
+
})),
|
|
9515
|
+
levels
|
|
9516
|
+
};
|
|
9517
|
+
}
|
|
7843
9518
|
var emptyBloomGraphInspection = () => ({
|
|
7844
9519
|
status: "empty",
|
|
7845
9520
|
targetCount: 0,
|
|
7846
9521
|
targetBytes: 0,
|
|
7847
|
-
passCount: 0
|
|
9522
|
+
passCount: 0,
|
|
9523
|
+
levelCount: 0,
|
|
9524
|
+
levelDimensions: [],
|
|
9525
|
+
downsamplePassCount: 0,
|
|
9526
|
+
upsamplePassCount: 0
|
|
7848
9527
|
});
|
|
7849
9528
|
var invalidBloomGraphInspection = () => ({
|
|
7850
9529
|
status: "invalid",
|
|
7851
9530
|
targetCount: 0,
|
|
7852
9531
|
targetBytes: 0,
|
|
7853
|
-
passCount: 0
|
|
9532
|
+
passCount: 0,
|
|
9533
|
+
levelCount: 0,
|
|
9534
|
+
levelDimensions: [],
|
|
9535
|
+
downsamplePassCount: 0,
|
|
9536
|
+
upsamplePassCount: 0
|
|
7854
9537
|
});
|
|
7855
|
-
function textureBytes(resource
|
|
9538
|
+
function textureBytes(resource) {
|
|
7856
9539
|
const descriptor = resource.descriptor;
|
|
7857
|
-
if (descriptor === void 0 || descriptor.kind !== "texture" || descriptor.format !==
|
|
7858
|
-
return 0;
|
|
9540
|
+
if (resource.kind !== "texture" || descriptor === void 0 || descriptor.kind !== "texture" || descriptor.format !== "rgba16float")
|
|
9541
|
+
return void 0;
|
|
7859
9542
|
const texelBytes = 8;
|
|
7860
9543
|
let bytes2 = 0;
|
|
7861
9544
|
for (let level = 0; level < descriptor.mipLevelCount; level += 1) {
|
|
7862
9545
|
bytes2 += Math.max(1, descriptor.width >> level) * Math.max(1, descriptor.height >> level) * descriptor.depthOrArrayLayers * texelBytes;
|
|
7863
9546
|
}
|
|
7864
|
-
|
|
9547
|
+
const descriptorBytes = bytes2 * descriptor.sampleCount;
|
|
9548
|
+
return resource.byteSize === descriptorBytes && descriptorBytes > 0 ? descriptorBytes : void 0;
|
|
9549
|
+
}
|
|
9550
|
+
function resourceSize(resource) {
|
|
9551
|
+
const descriptor = resource.descriptor;
|
|
9552
|
+
if (resource.kind !== "texture" || descriptor.kind !== "texture") return void 0;
|
|
9553
|
+
if (typeof descriptor.size === "string") return void 0;
|
|
9554
|
+
if (resource.extent === void 0 || resource.extent.width !== descriptor.width || resource.extent.height !== descriptor.height || resource.extent.depthOrArrayLayers !== descriptor.depthOrArrayLayers) {
|
|
9555
|
+
return void 0;
|
|
9556
|
+
}
|
|
9557
|
+
if (descriptor.format !== "rgba16float" || descriptor.domain !== "linear-hdr" || descriptor.width <= 0 || descriptor.height <= 0 || descriptor.depthOrArrayLayers !== 1 || descriptor.mipLevelCount !== 1 || descriptor.sampleCount !== 1 || resource.dimension !== "2d" || descriptor.size.width !== descriptor.width || descriptor.size.height !== descriptor.height || textureBytes(resource) === void 0) {
|
|
9558
|
+
return void 0;
|
|
9559
|
+
}
|
|
9560
|
+
return { width: descriptor.width, height: descriptor.height };
|
|
9561
|
+
}
|
|
9562
|
+
function hasPassAccesses(pass, accesses) {
|
|
9563
|
+
return pass !== void 0 && pass.kind === "raster" && pass.accesses.length === accesses.length && accesses.every(
|
|
9564
|
+
(access, index) => pass.accesses[index]?.resource === access.resource && pass.accesses[index]?.usage === access.usage
|
|
9565
|
+
);
|
|
7865
9566
|
}
|
|
7866
9567
|
function inspectStandardBloomGraph(graph) {
|
|
7867
9568
|
if (graph === void 0) return emptyBloomGraphInspection();
|
|
7868
|
-
const
|
|
7869
|
-
|
|
9569
|
+
const composited = graph.resources.filter(
|
|
9570
|
+
(resource) => resource.label === STANDARD_BLOOM_TARGET_SPEC.composited.label
|
|
7870
9571
|
);
|
|
7871
|
-
const
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
9572
|
+
const downsample = graph.resources.filter((resource) => resource.label.startsWith(STANDARD_BLOOM_TARGET_SPEC.downsamplePrefix)).sort((a, b) => a.label.localeCompare(b.label, void 0, { numeric: true }));
|
|
9573
|
+
const upsample = graph.resources.filter((resource) => resource.label.startsWith(STANDARD_BLOOM_TARGET_SPEC.upsamplePrefix)).sort((a, b) => a.label.localeCompare(b.label, void 0, { numeric: true }));
|
|
9574
|
+
const targets = [...composited, ...downsample, ...upsample];
|
|
9575
|
+
const passes = graph.passes.filter(
|
|
9576
|
+
(pass) => pass.name === TYPED_BLOOM_PASS_NAMES.composite || pass.name.startsWith(`${TYPED_BLOOM_PASS_NAMES.downsample}-`) || pass.name.startsWith(`${TYPED_BLOOM_PASS_NAMES.upsample}-`)
|
|
9577
|
+
).sort((left, right) => left.executionIndex - right.executionIndex);
|
|
7877
9578
|
if (targets.length === 0 && passes.length === 0) return emptyBloomGraphInspection();
|
|
7878
|
-
|
|
7879
|
-
const
|
|
7880
|
-
if (
|
|
9579
|
+
if (composited.length !== 1) return invalidBloomGraphInspection();
|
|
9580
|
+
const compositedResource = composited[0];
|
|
9581
|
+
if (compositedResource === void 0) return invalidBloomGraphInspection();
|
|
9582
|
+
const compositedSize = resourceSize(compositedResource);
|
|
9583
|
+
if (compositedSize === void 0) return invalidBloomGraphInspection();
|
|
9584
|
+
const expected = standardBloomTargetSpecs(compositedSize.width, compositedSize.height);
|
|
9585
|
+
const expectedDownsampleLabels = expected.downsample.map((entry) => entry.label);
|
|
9586
|
+
const expectedUpsampleLabels = expected.upsample.map((entry) => entry.label);
|
|
9587
|
+
const orderedDownsample = downsample.sort(
|
|
9588
|
+
(left, right) => left.label.localeCompare(right.label, void 0, { numeric: true })
|
|
9589
|
+
);
|
|
9590
|
+
const orderedUpsample = upsample.sort(
|
|
9591
|
+
(left, right) => left.label.localeCompare(right.label, void 0, { numeric: true })
|
|
9592
|
+
);
|
|
9593
|
+
const completeTargetSet = targets.length === 1 + expectedDownsampleLabels.length + expectedUpsampleLabels.length && orderedDownsample.length === expectedDownsampleLabels.length && orderedUpsample.length === expectedUpsampleLabels.length && orderedDownsample.every(
|
|
9594
|
+
(resource, level) => resource.label === expectedDownsampleLabels[level]
|
|
9595
|
+
) && orderedUpsample.every((resource, level) => resource.label === expectedUpsampleLabels[level]) && orderedDownsample.every(
|
|
9596
|
+
(resource, level) => resourceSize(resource)?.width === expected.levels[level]?.width && resourceSize(resource)?.height === expected.levels[level]?.height
|
|
9597
|
+
) && orderedUpsample.every(
|
|
9598
|
+
(resource, level) => resourceSize(resource)?.width === expected.levels[level]?.width && resourceSize(resource)?.height === expected.levels[level]?.height
|
|
9599
|
+
);
|
|
9600
|
+
const expectedPassNames = [
|
|
9601
|
+
...expectedDownsampleLabels,
|
|
9602
|
+
...[...expectedUpsampleLabels].reverse(),
|
|
9603
|
+
TYPED_BLOOM_PASS_NAMES.composite
|
|
9604
|
+
];
|
|
9605
|
+
const completePassSet = passes.length === expected.levels.length + expected.upsample.length + 1 && passes.every((pass, index) => pass.name === expectedPassNames[index]);
|
|
9606
|
+
const sceneLabel = passes[0]?.accesses[0]?.resource;
|
|
9607
|
+
const passTopology = completePassSet && sceneLabel !== void 0 && !sceneLabel.startsWith(STANDARD_BLOOM_TARGET_SPEC.downsamplePrefix) && !sceneLabel.startsWith(STANDARD_BLOOM_TARGET_SPEC.upsamplePrefix) && sceneLabel !== STANDARD_BLOOM_TARGET_SPEC.composited.label && graph.resources.some((resource) => resource.label === sceneLabel) && orderedDownsample.every(
|
|
9608
|
+
(_, level) => hasPassAccesses(passes[level], [
|
|
9609
|
+
{
|
|
9610
|
+
resource: level === 0 ? sceneLabel : `bloom-downsample-${level - 1}`,
|
|
9611
|
+
usage: "sampled-read"
|
|
9612
|
+
},
|
|
9613
|
+
{ resource: `bloom-downsample-${level}`, usage: "color-attachment" }
|
|
9614
|
+
])
|
|
9615
|
+
) && orderedUpsample.every((_, level) => {
|
|
9616
|
+
const passIndex = expected.levels.length + expected.upsample.length - 1 - level;
|
|
9617
|
+
return hasPassAccesses(passes[passIndex], [
|
|
9618
|
+
{ resource: `bloom-downsample-${level}`, usage: "sampled-read" },
|
|
9619
|
+
{
|
|
9620
|
+
resource: level === expected.levels.length - 2 ? `bloom-downsample-${level + 1}` : `bloom-upsample-${level + 1}`,
|
|
9621
|
+
usage: "sampled-read"
|
|
9622
|
+
},
|
|
9623
|
+
{ resource: `bloom-upsample-${level}`, usage: "color-attachment" }
|
|
9624
|
+
]);
|
|
9625
|
+
}) && hasPassAccesses(passes.at(-1), [
|
|
9626
|
+
{ resource: sceneLabel, usage: "sampled-read" },
|
|
9627
|
+
{
|
|
9628
|
+
resource: expected.levels.length === 1 ? "bloom-downsample-0" : "bloom-upsample-0",
|
|
9629
|
+
usage: "sampled-read"
|
|
9630
|
+
},
|
|
9631
|
+
{ resource: STANDARD_BLOOM_TARGET_SPEC.composited.label, usage: "color-attachment" }
|
|
9632
|
+
]);
|
|
9633
|
+
if (!completeTargetSet || !passTopology) {
|
|
7881
9634
|
return invalidBloomGraphInspection();
|
|
7882
9635
|
}
|
|
9636
|
+
const levelDimensions = orderedDownsample.flatMap((resource) => {
|
|
9637
|
+
const size = resourceSize(resource);
|
|
9638
|
+
return size === void 0 ? [] : [size];
|
|
9639
|
+
});
|
|
9640
|
+
if (levelDimensions.length !== orderedDownsample.length) {
|
|
9641
|
+
return invalidBloomGraphInspection();
|
|
9642
|
+
}
|
|
9643
|
+
const targetBytes = targets.reduce((bytes2, resource) => bytes2 + (textureBytes(resource) ?? 0), 0);
|
|
7883
9644
|
return {
|
|
7884
9645
|
status: "valid",
|
|
7885
9646
|
targetCount: targets.length,
|
|
7886
|
-
targetBytes
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
9647
|
+
targetBytes,
|
|
9648
|
+
passCount: passes.length,
|
|
9649
|
+
levelCount: orderedDownsample.length,
|
|
9650
|
+
levelDimensions,
|
|
9651
|
+
downsamplePassCount: orderedDownsample.length,
|
|
9652
|
+
upsamplePassCount: orderedUpsample.length
|
|
7891
9653
|
};
|
|
7892
9654
|
}
|
|
7893
9655
|
function target2(context, label, descriptor) {
|
|
@@ -7910,7 +9672,7 @@ function addStandardPost(context, topology, scene, depth, surface, sceneTemporal
|
|
|
7910
9672
|
lane: topology.standardProfile?.renderPath === "deferred" ? "clustered" : "direct",
|
|
7911
9673
|
temporal: !clearOnly && topology.camera.antialias === "taa",
|
|
7912
9674
|
meter: topology.output?.autoExposure === true,
|
|
7913
|
-
bloom: !clearOnly && topology.camera
|
|
9675
|
+
bloom: !clearOnly && standardBloomAdmitted(topology.camera),
|
|
7914
9676
|
dof: !clearOnly && topology.camera.depthOfField !== void 0,
|
|
7915
9677
|
exposure: topology.output?.autoExposure === true,
|
|
7916
9678
|
whiteBalance: topology.output?.whiteBalance === true,
|
|
@@ -7936,6 +9698,32 @@ function addStandardPost(context, topology, scene, depth, surface, sceneTemporal
|
|
|
7936
9698
|
const finalPresent = surface.storage;
|
|
7937
9699
|
let postInput = scene;
|
|
7938
9700
|
let postTemporal = sceneTemporal;
|
|
9701
|
+
if (!clearOnly && topology.analyticFog === true) {
|
|
9702
|
+
if (depth.sampleCount !== 1)
|
|
9703
|
+
return err$1(
|
|
9704
|
+
new RenderGraphError({
|
|
9705
|
+
code: "resource-resolution-failed",
|
|
9706
|
+
expected: "analytic fog receives single-sample scene depth",
|
|
9707
|
+
hint: "select FXAA, TAA or no AA for analytic fog",
|
|
9708
|
+
detail: { resourceLabel: "scene-depth" }
|
|
9709
|
+
})
|
|
9710
|
+
);
|
|
9711
|
+
const fogged = target2(context, "analytic-fog-color", {
|
|
9712
|
+
format: "rgba16float",
|
|
9713
|
+
size: topology.extent === void 0 ? "surface" : renderExtentSize(topology.extent, "internal"),
|
|
9714
|
+
domain: hdr ? "linear-hdr" : "linear-ldr"
|
|
9715
|
+
});
|
|
9716
|
+
if (!fogged.ok) return fogged;
|
|
9717
|
+
const fog = addTypedFullscreenPass(context.graph, {
|
|
9718
|
+
name: "analytic-fog",
|
|
9719
|
+
shader: "forgeax.analytic-fog",
|
|
9720
|
+
input: postInput,
|
|
9721
|
+
depth,
|
|
9722
|
+
output: fogged.value
|
|
9723
|
+
});
|
|
9724
|
+
if (!fog.ok) return fog;
|
|
9725
|
+
postInput = fogged.value;
|
|
9726
|
+
}
|
|
7939
9727
|
const autoExposure = context.standardOutput?.autoExposure;
|
|
7940
9728
|
if (outputPlan.features.exposure && autoExposure === void 0) {
|
|
7941
9729
|
return err$1(
|
|
@@ -7956,7 +9744,7 @@ function addStandardPost(context, topology, scene, depth, surface, sceneTemporal
|
|
|
7956
9744
|
return err$1(missingTemporalContributor(topology, "forgeax::standard::taa-history"));
|
|
7957
9745
|
}
|
|
7958
9746
|
const resolveTargets = {
|
|
7959
|
-
scene,
|
|
9747
|
+
scene: postInput,
|
|
7960
9748
|
depth,
|
|
7961
9749
|
currentTemporal: sceneTemporal,
|
|
7962
9750
|
...coverage === void 0 ? {} : { coverage },
|
|
@@ -7974,6 +9762,15 @@ function addStandardPost(context, topology, scene, depth, surface, sceneTemporal
|
|
|
7974
9762
|
postInput = history.currentColor;
|
|
7975
9763
|
postTemporal = history.currentTemporal;
|
|
7976
9764
|
}
|
|
9765
|
+
if (outputPlan.features.exposure && autoExposure !== void 0) {
|
|
9766
|
+
const added = addAutoExposureGraphPasses(context, {
|
|
9767
|
+
source: postInput,
|
|
9768
|
+
resources: autoExposure,
|
|
9769
|
+
width: topology.surface.width,
|
|
9770
|
+
height: topology.surface.height
|
|
9771
|
+
});
|
|
9772
|
+
if (!added.ok) return added;
|
|
9773
|
+
}
|
|
7977
9774
|
if (!clearOnly) {
|
|
7978
9775
|
const featureTargets = [
|
|
7979
9776
|
{
|
|
@@ -8023,15 +9820,6 @@ function addStandardPost(context, topology, scene, depth, surface, sceneTemporal
|
|
|
8023
9820
|
if (!features.ok) return features;
|
|
8024
9821
|
}
|
|
8025
9822
|
}
|
|
8026
|
-
if (outputPlan.features.exposure && autoExposure !== void 0) {
|
|
8027
|
-
const added = addAutoExposureGraphPasses(context, {
|
|
8028
|
-
source: postInput,
|
|
8029
|
-
resources: autoExposure,
|
|
8030
|
-
width: topology.surface.width,
|
|
8031
|
-
height: topology.surface.height
|
|
8032
|
-
});
|
|
8033
|
-
if (!added.ok) return added;
|
|
8034
|
-
}
|
|
8035
9823
|
if (outputPlan.features.dof) {
|
|
8036
9824
|
const dof = addDepthOfFieldPasses(
|
|
8037
9825
|
{
|
|
@@ -8049,36 +9837,29 @@ function addStandardPost(context, topology, scene, depth, surface, sceneTemporal
|
|
|
8049
9837
|
postInput = dof.value;
|
|
8050
9838
|
}
|
|
8051
9839
|
if (bloomActive) {
|
|
8052
|
-
const
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
);
|
|
9840
|
+
const outputWidth = topology.extent?.outputWidth ?? topology.surface.width;
|
|
9841
|
+
const outputHeight = topology.extent?.outputHeight ?? topology.surface.height;
|
|
9842
|
+
const bloomSpecs = standardBloomTargetSpecs(outputWidth, outputHeight);
|
|
9843
|
+
const composited = target2(context, bloomSpecs.composited.label, bloomSpecs.composited);
|
|
8057
9844
|
if (!composited.ok) return composited;
|
|
8058
|
-
const
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
const
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
const blurV = target2(
|
|
8071
|
-
context,
|
|
8072
|
-
STANDARD_BLOOM_TARGET_SPEC.blurV.label,
|
|
8073
|
-
STANDARD_BLOOM_TARGET_SPEC.blurV
|
|
8074
|
-
);
|
|
8075
|
-
if (!blurV.ok) return blurV;
|
|
9845
|
+
const downsample = [];
|
|
9846
|
+
for (const spec of bloomSpecs.downsample) {
|
|
9847
|
+
const level = target2(context, spec.label, spec);
|
|
9848
|
+
if (!level.ok) return level;
|
|
9849
|
+
downsample.push(level.value);
|
|
9850
|
+
}
|
|
9851
|
+
const upsample = [];
|
|
9852
|
+
for (const spec of bloomSpecs.upsample) {
|
|
9853
|
+
const level = target2(context, spec.label, spec);
|
|
9854
|
+
if (!level.ok) return level;
|
|
9855
|
+
upsample.push(level.value);
|
|
9856
|
+
}
|
|
8076
9857
|
const bloom = addTypedBloomPasses(context.graph, {
|
|
8077
9858
|
scene: postInput,
|
|
8078
9859
|
composited: composited.value,
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
9860
|
+
downsample,
|
|
9861
|
+
upsample,
|
|
9862
|
+
levelDimensions: bloomSpecs.levels
|
|
8082
9863
|
});
|
|
8083
9864
|
if (!bloom.ok) return bloom;
|
|
8084
9865
|
postInput = composited.value;
|
|
@@ -12082,6 +13863,7 @@ var GpuDrivenProduction = class _GpuDrivenProduction {
|
|
|
12082
13863
|
}
|
|
12083
13864
|
const updated = this.view.update(filtered.plan, scene.scene, activeFrustum(input.camera));
|
|
12084
13865
|
if (!updated.ok) return updated;
|
|
13866
|
+
this.view.setTelemetrySubmit(input.telemetrySubmit);
|
|
12085
13867
|
this.telemetryPrepared = true;
|
|
12086
13868
|
const view = this.view;
|
|
12087
13869
|
const visibleBuffer = view.visibleBuffer;
|
|
@@ -12999,15 +14781,53 @@ var GpuDrivenProduction = class _GpuDrivenProduction {
|
|
|
12999
14781
|
this.laneReason = "overflow";
|
|
13000
14782
|
}
|
|
13001
14783
|
}
|
|
13002
|
-
if (selection === void 0
|
|
14784
|
+
if (selection === void 0) return selection;
|
|
13003
14785
|
if (filtered === void 0) return selection;
|
|
13004
14786
|
const preparedByBatchId = new Map(
|
|
13005
|
-
filtered.batches.map((prepared) => [
|
|
14787
|
+
filtered.batches.map((prepared) => [
|
|
14788
|
+
prepared.batch.batchId,
|
|
14789
|
+
{
|
|
14790
|
+
batch: prepared.batch,
|
|
14791
|
+
worldKey: prepared.worldKey,
|
|
14792
|
+
primitiveSlot: prepared.primitiveSlot,
|
|
14793
|
+
slotGeneration: prepared.slotGeneration
|
|
14794
|
+
}
|
|
14795
|
+
])
|
|
13006
14796
|
);
|
|
14797
|
+
const filteredSlots = this.filteredCache?.slots;
|
|
14798
|
+
const filteredWorldKeys = this.filteredCache?.worldKeys;
|
|
14799
|
+
if (filteredSlots !== void 0) {
|
|
14800
|
+
const slotByPrimitive = new Map(filteredSlots.map((slot) => [slot.slot, slot]));
|
|
14801
|
+
for (const batch of filtered.plan.batches) {
|
|
14802
|
+
if (preparedByBatchId.has(batch.batchId)) continue;
|
|
14803
|
+
const candidate = batch.candidates[0];
|
|
14804
|
+
const slot = candidate === void 0 ? void 0 : slotByPrimitive.get(candidate.primitiveIndex);
|
|
14805
|
+
if (slot === void 0) continue;
|
|
14806
|
+
preparedByBatchId.set(batch.batchId, {
|
|
14807
|
+
batch,
|
|
14808
|
+
worldKey: filteredWorldKeys?.[slot.snapshot.worldId] ?? slot.snapshot.worldId,
|
|
14809
|
+
primitiveSlot: slot.slot,
|
|
14810
|
+
slotGeneration: slot.generation
|
|
14811
|
+
});
|
|
14812
|
+
}
|
|
14813
|
+
}
|
|
13007
14814
|
const worldSelections = /* @__PURE__ */ new Map();
|
|
14815
|
+
const lodHistogram = /* @__PURE__ */ new Map();
|
|
14816
|
+
let lodCandidateCount = 0;
|
|
14817
|
+
let lodVisible = 0;
|
|
13008
14818
|
for (const batchSelection of selection.batches) {
|
|
13009
14819
|
const prepared = preparedByBatchId.get(batchSelection.batchId);
|
|
13010
|
-
if (prepared === void 0) return
|
|
14820
|
+
if (prepared === void 0) return void 0;
|
|
14821
|
+
const lodCandidateRows = prepared.batch.candidates.filter(
|
|
14822
|
+
(candidate) => (candidate.lodCoverages?.length ?? 0) > 1
|
|
14823
|
+
);
|
|
14824
|
+
if (lodCandidateRows.length === 0) continue;
|
|
14825
|
+
if (lodCandidateRows.length !== batchSelection.candidateCount) return void 0;
|
|
14826
|
+
lodCandidateCount += batchSelection.candidateCount;
|
|
14827
|
+
lodVisible += batchSelection.visible;
|
|
14828
|
+
for (const row of batchSelection.lodHistogram) {
|
|
14829
|
+
lodHistogram.set(row.level, (lodHistogram.get(row.level) ?? 0) + row.count);
|
|
14830
|
+
}
|
|
13011
14831
|
let aggregate = worldSelections.get(prepared.worldKey);
|
|
13012
14832
|
if (aggregate === void 0) {
|
|
13013
14833
|
aggregate = {
|
|
@@ -13033,8 +14853,12 @@ var GpuDrivenProduction = class _GpuDrivenProduction {
|
|
|
13033
14853
|
}
|
|
13034
14854
|
return {
|
|
13035
14855
|
...selection,
|
|
13036
|
-
candidateCount:
|
|
13037
|
-
|
|
14856
|
+
candidateCount: lodCandidateCount,
|
|
14857
|
+
visible: lodVisible,
|
|
14858
|
+
occluded: Math.max(0, lodCandidateCount - lodVisible),
|
|
14859
|
+
lodHistogram: Object.freeze(
|
|
14860
|
+
[...lodHistogram.entries()].sort(([left], [right]) => left - right).map(([level, count]) => Object.freeze({ level, count }))
|
|
14861
|
+
),
|
|
13038
14862
|
worldSelections: Object.freeze(
|
|
13039
14863
|
[...worldSelections.values()].map(
|
|
13040
14864
|
(aggregate) => Object.freeze({
|
|
@@ -13594,6 +15418,49 @@ function projectDepthOfFieldInspection(input) {
|
|
|
13594
15418
|
function requestedEqual(requested, params) {
|
|
13595
15419
|
return requested.focusDistance === params.focusDistance && requested.fStop === params.fStop && requested.sensorHeight === params.sensorHeight && requested.maxRadiusPixels === params.maxRadiusPixels && requested.quality === params.quality && requested.blurSide === params.blurSide;
|
|
13596
15420
|
}
|
|
15421
|
+
|
|
15422
|
+
// src/features/error-reporting.ts
|
|
15423
|
+
var PENDING_RENDER_FEATURE_ERROR_REPORT_INTERVAL_FRAMES = 60;
|
|
15424
|
+
function shouldReportPendingRenderFeatureError(lastReportedFrame, key, frameNumber, intervalFrames = PENDING_RENDER_FEATURE_ERROR_REPORT_INTERVAL_FRAMES) {
|
|
15425
|
+
const lastReported = lastReportedFrame.get(key);
|
|
15426
|
+
if (lastReported !== void 0 && frameNumber - lastReported < intervalFrames) return false;
|
|
15427
|
+
lastReportedFrame.set(key, frameNumber);
|
|
15428
|
+
return true;
|
|
15429
|
+
}
|
|
15430
|
+
function reportPendingRenderFeatureErrors(errors, frameNumber, lastReportedFrame, errorRegistry) {
|
|
15431
|
+
for (const error of errors) {
|
|
15432
|
+
if (!isPendingRenderFeaturePreparation(error)) {
|
|
15433
|
+
errorRegistry.fire(error);
|
|
15434
|
+
continue;
|
|
15435
|
+
}
|
|
15436
|
+
if (isSilentRenderFeatureWarmup(error)) continue;
|
|
15437
|
+
const key = pendingRenderFeatureErrorKey(error);
|
|
15438
|
+
if (shouldReportPendingRenderFeatureError(lastReportedFrame, key, frameNumber)) {
|
|
15439
|
+
errorRegistry.fire(error);
|
|
15440
|
+
}
|
|
15441
|
+
}
|
|
15442
|
+
}
|
|
15443
|
+
function isPendingRenderFeaturePreparation(error) {
|
|
15444
|
+
if (!("detail" in error)) return false;
|
|
15445
|
+
switch (error.code) {
|
|
15446
|
+
case "render-feature-stage-failed":
|
|
15447
|
+
case "render-feature-preparation-failed":
|
|
15448
|
+
case "render-feature-prepared-state-mismatch":
|
|
15449
|
+
case "render-feature-draw-recording-failed":
|
|
15450
|
+
return error.detail.recovery === "next-frame";
|
|
15451
|
+
default:
|
|
15452
|
+
return false;
|
|
15453
|
+
}
|
|
15454
|
+
}
|
|
15455
|
+
function isSilentRenderFeatureWarmup(error) {
|
|
15456
|
+
return error.code === "render-feature-preparation-failed" && error.detail.reason.startsWith("rhi-not-available:");
|
|
15457
|
+
}
|
|
15458
|
+
function pendingRenderFeatureErrorKey(error) {
|
|
15459
|
+
const detail = "detail" in error ? error.detail : void 0;
|
|
15460
|
+
const featureIdentity = detail !== void 0 && "featureIdentity" in detail && typeof detail.featureIdentity === "string" ? detail.featureIdentity : "";
|
|
15461
|
+
const order = detail !== void 0 && "order" in detail && typeof detail.order === "number" ? detail.order : -1;
|
|
15462
|
+
return `${error.code}\0${featureIdentity}\0${order}\0${error.expected}\0${error.hint}`;
|
|
15463
|
+
}
|
|
13597
15464
|
function createFeatureNoisePixels() {
|
|
13598
15465
|
const pixels = new Uint8Array(64 * 64 * 4);
|
|
13599
15466
|
let state = 1831565813;
|
|
@@ -13969,6 +15836,17 @@ function createRenderFeatureGpuWorkSession(input) {
|
|
|
13969
15836
|
pipelineMap.set(entryPoint, pipeline.value);
|
|
13970
15837
|
}
|
|
13971
15838
|
const reference = Object.freeze({ generation: input.generation });
|
|
15839
|
+
const textureBindings = /* @__PURE__ */ new Map();
|
|
15840
|
+
for (const entry of descriptor.bindings?.[0]?.entries ?? []) {
|
|
15841
|
+
if (entry.texture !== void 0) {
|
|
15842
|
+
textureBindings.set(entry.binding, "sampled");
|
|
15843
|
+
} else if (entry.storageTexture !== void 0) {
|
|
15844
|
+
textureBindings.set(
|
|
15845
|
+
entry.binding,
|
|
15846
|
+
entry.storageTexture.access === "write-only" ? "storage-write" : entry.storageTexture.access === "read-only" ? "storage-read" : "storage-read-write"
|
|
15847
|
+
);
|
|
15848
|
+
}
|
|
15849
|
+
}
|
|
13972
15850
|
const item = {
|
|
13973
15851
|
name,
|
|
13974
15852
|
descriptor: cloneProgramDescriptor(descriptor),
|
|
@@ -13980,6 +15858,7 @@ function createRenderFeatureGpuWorkSession(input) {
|
|
|
13980
15858
|
(entry) => entry.buffer === void 0 ? [] : [[entry.binding, entry.buffer.type ?? "uniform"]]
|
|
13981
15859
|
)
|
|
13982
15860
|
),
|
|
15861
|
+
textureBindings,
|
|
13983
15862
|
...bindGroupLayout === void 0 ? {} : { bindGroupLayout }
|
|
13984
15863
|
};
|
|
13985
15864
|
programs.set(name, item);
|
|
@@ -14277,7 +16156,7 @@ function createRenderFeatureGpuWorkSession(input) {
|
|
|
14277
16156
|
}
|
|
14278
16157
|
entryItems.push({
|
|
14279
16158
|
binding: entry.binding,
|
|
14280
|
-
resource: { kind: "textureView", value: item3.resource },
|
|
16159
|
+
...item3.resource === void 0 ? {} : { resource: { kind: "textureView", value: item3.resource } },
|
|
14281
16160
|
textureView: item3
|
|
14282
16161
|
});
|
|
14283
16162
|
continue;
|
|
@@ -14305,7 +16184,7 @@ function createRenderFeatureGpuWorkSession(input) {
|
|
|
14305
16184
|
entries: entryItems.map((entry) => [
|
|
14306
16185
|
entry.binding,
|
|
14307
16186
|
entry.buffer?.name ?? entry.textureView?.name ?? entry.sampler?.name,
|
|
14308
|
-
entry.resource
|
|
16187
|
+
entry.resource?.kind ?? "late-texture"
|
|
14309
16188
|
])
|
|
14310
16189
|
});
|
|
14311
16190
|
const prior = bindings.get(name) ?? parkedBindings.get(name)?.item;
|
|
@@ -14335,7 +16214,22 @@ function createRenderFeatureGpuWorkSession(input) {
|
|
|
14335
16214
|
)
|
|
14336
16215
|
);
|
|
14337
16216
|
}
|
|
14338
|
-
const
|
|
16217
|
+
const hasLateTextures = entryItems.some(
|
|
16218
|
+
(entry) => entry.textureView?.logicalTarget !== void 0
|
|
16219
|
+
);
|
|
16220
|
+
const hasMissingResource = entryItems.some((entry) => entry.resource === void 0);
|
|
16221
|
+
if (hasMissingResource && !hasLateTextures) {
|
|
16222
|
+
return err$1(
|
|
16223
|
+
failure4(
|
|
16224
|
+
input.featureIdentity,
|
|
16225
|
+
"prepare-gpu-bindings",
|
|
16226
|
+
"bindings",
|
|
16227
|
+
name,
|
|
16228
|
+
"texture-view-unavailable"
|
|
16229
|
+
)
|
|
16230
|
+
);
|
|
16231
|
+
}
|
|
16232
|
+
const created = hasLateTextures ? void 0 : input.device.createBindGroup({
|
|
14339
16233
|
label: name,
|
|
14340
16234
|
layout,
|
|
14341
16235
|
entries: entryItems.map((entry) => ({
|
|
@@ -14343,7 +16237,7 @@ function createRenderFeatureGpuWorkSession(input) {
|
|
|
14343
16237
|
resource: entry.resource
|
|
14344
16238
|
}))
|
|
14345
16239
|
});
|
|
14346
|
-
if (!created.ok) {
|
|
16240
|
+
if (created !== void 0 && !created.ok) {
|
|
14347
16241
|
return err$1(
|
|
14348
16242
|
failure4(
|
|
14349
16243
|
input.featureIdentity,
|
|
@@ -14366,7 +16260,10 @@ function createRenderFeatureGpuWorkSession(input) {
|
|
|
14366
16260
|
buffers: entryItems.flatMap(
|
|
14367
16261
|
(entry) => entry.buffer === void 0 ? [] : [{ binding: entry.binding, item: entry.buffer }]
|
|
14368
16262
|
),
|
|
14369
|
-
|
|
16263
|
+
// A bind group with semantic targets is created once the graph resolves
|
|
16264
|
+
// the current generation's physical views. The placeholder is never
|
|
16265
|
+
// encoded because resolveComputePass installs resolveBindGroup.
|
|
16266
|
+
bindGroup: created?.value
|
|
14370
16267
|
};
|
|
14371
16268
|
bindings.set(name, item);
|
|
14372
16269
|
bindingRefs.set(reference, item);
|
|
@@ -14552,16 +16449,20 @@ function createRenderFeatureGpuWorkSession(input) {
|
|
|
14552
16449
|
accesses.add("indirect-read");
|
|
14553
16450
|
resolvedBuffers.set(item, accesses);
|
|
14554
16451
|
}
|
|
16452
|
+
const sampledTargets = /* @__PURE__ */ new Set();
|
|
16453
|
+
const storageTargets = /* @__PURE__ */ new Set();
|
|
16454
|
+
for (const item of dispatchBindings) {
|
|
16455
|
+
for (const entry of item?.entries ?? []) {
|
|
16456
|
+
const target5 = entry.textureView?.logicalTarget;
|
|
16457
|
+
if (target5 === void 0) continue;
|
|
16458
|
+
const role = program.textureBindings.get(entry.binding) ?? "sampled";
|
|
16459
|
+
if (role === "sampled") sampledTargets.add(target5);
|
|
16460
|
+
else storageTargets.add(target5);
|
|
16461
|
+
}
|
|
16462
|
+
}
|
|
14555
16463
|
return ok$1({
|
|
14556
|
-
sampledTargets: [
|
|
14557
|
-
|
|
14558
|
-
dispatchBindings.flatMap(
|
|
14559
|
-
(item) => item?.entries.flatMap(
|
|
14560
|
-
(entry) => entry.textureView?.logicalTarget === void 0 ? [] : [entry.textureView.logicalTarget]
|
|
14561
|
-
) ?? []
|
|
14562
|
-
)
|
|
14563
|
-
)
|
|
14564
|
-
],
|
|
16464
|
+
sampledTargets: [...sampledTargets],
|
|
16465
|
+
storageTargets: [...storageTargets],
|
|
14565
16466
|
buffers: [...resolvedBuffers].flatMap(
|
|
14566
16467
|
([item, accesses]) => [...accesses].map((access) => ({
|
|
14567
16468
|
name: item.name,
|
|
@@ -15782,9 +17683,7 @@ function collectGpuDrivenMaterialArtifacts({
|
|
|
15782
17683
|
specializationKey: sceneIndexProgramKey
|
|
15783
17684
|
};
|
|
15784
17685
|
const receipt = selectedIdentityArtifact?.receipt;
|
|
15785
|
-
const shadowVertexInputsMatch = pass !== "shadow" || receipt !== void 0 && receipt.vertexInputs
|
|
15786
|
-
(input, index) => input.semantic === prepared.vertexInputs[index]?.semantic && input.location === prepared.vertexInputs[index]?.location && input.format === prepared.vertexInputs[index]?.format
|
|
15787
|
-
);
|
|
17686
|
+
const shadowVertexInputsMatch = pass !== "shadow" || receipt !== void 0 && hasRequiredVertexInputs(receipt.vertexInputs, prepared.vertexInputs);
|
|
15788
17687
|
if (selectedIdentityArtifact === void 0 || receipt === void 0 || !shadowVertexInputsMatch || pass !== "shadow" && (receipt.receiptIdentity !== prepared.receiptIdentity || receipt.generation !== prepared.receiptGeneration)) {
|
|
15789
17688
|
continue;
|
|
15790
17689
|
}
|
|
@@ -15853,6 +17752,29 @@ function inspectStandardLighting(input) {
|
|
|
15853
17752
|
}
|
|
15854
17753
|
return inspectStandardClusterTransport(input.transport, input.prepared);
|
|
15855
17754
|
}
|
|
17755
|
+
function encodeAnalyticFogParams(fog, camera) {
|
|
17756
|
+
const projection = computeProjectionMatrix(camera);
|
|
17757
|
+
const jitter = camera.temporal?.currentJitterUv;
|
|
17758
|
+
if (jitter !== void 0) {
|
|
17759
|
+
for (let column = 0; column < 4; column++) {
|
|
17760
|
+
projection[column * 4] = (projection[column * 4] ?? 0) + jitter[0] * 2 * (projection[column * 4 + 3] ?? 0);
|
|
17761
|
+
projection[column * 4 + 1] = (projection[column * 4 + 1] ?? 0) - jitter[1] * 2 * (projection[column * 4 + 3] ?? 0);
|
|
17762
|
+
}
|
|
17763
|
+
}
|
|
17764
|
+
const inverse = mat4.invert(
|
|
17765
|
+
mat4.create(),
|
|
17766
|
+
mat4.multiply(mat4.create(), projection, computeViewMatrix(camera))
|
|
17767
|
+
);
|
|
17768
|
+
const data = new Float32Array(28);
|
|
17769
|
+
data.set(inverse, 0);
|
|
17770
|
+
data.set(camera.position, 16);
|
|
17771
|
+
data.set([...fog.color, fog.density], 20);
|
|
17772
|
+
data.set(
|
|
17773
|
+
[fog.heightFalloff, fog.maxOpacity, camera.near, camera.projection === "orthographic" ? 1 : 0],
|
|
17774
|
+
24
|
|
17775
|
+
);
|
|
17776
|
+
return new Uint8Array(data.buffer);
|
|
17777
|
+
}
|
|
15856
17778
|
function uploadMaterialUniforms(c) {
|
|
15857
17779
|
const {
|
|
15858
17780
|
runtime,
|
|
@@ -16015,6 +17937,12 @@ function resolveVolumeTemporalReset(previous, next, options = {}) {
|
|
|
16015
17937
|
if (previous.lightShadowRevision !== next.lightShadowRevision) {
|
|
16016
17938
|
return { reset: true, reason: "light-revision" };
|
|
16017
17939
|
}
|
|
17940
|
+
if (previous.worldTimeSeconds !== next.worldTimeSeconds) {
|
|
17941
|
+
const delta = (next.worldTimeSeconds ?? NaN) - (previous.worldTimeSeconds ?? NaN);
|
|
17942
|
+
if (!Number.isFinite(delta) || delta < 0 || delta > 0.1) {
|
|
17943
|
+
return { reset: true, reason: "simulation-time" };
|
|
17944
|
+
}
|
|
17945
|
+
}
|
|
16018
17946
|
if (previous.densityGeneration !== next.densityGeneration) {
|
|
16019
17947
|
return { reset: true, reason: "density-generation" };
|
|
16020
17948
|
}
|
|
@@ -16073,6 +18001,7 @@ function createTemporalView(input) {
|
|
|
16073
18001
|
previousUnjitteredViewProjection: input.previousUnjitteredViewProjection,
|
|
16074
18002
|
currentCameraPosition: input.currentCameraPosition,
|
|
16075
18003
|
previousCameraPosition: input.previousCameraPosition,
|
|
18004
|
+
sampleTimeSeconds: input.sampleTimeSeconds,
|
|
16076
18005
|
resetReason: input.resetReason
|
|
16077
18006
|
});
|
|
16078
18007
|
const taa = input.antialias === "taa";
|
|
@@ -16090,6 +18019,7 @@ function createTemporalView(input) {
|
|
|
16090
18019
|
previousUnjitteredViewProjection: input.previousUnjitteredViewProjection,
|
|
16091
18020
|
currentCameraPosition: input.currentCameraPosition,
|
|
16092
18021
|
previousCameraPosition: input.previousCameraPosition,
|
|
18022
|
+
sampleTimeSeconds: input.sampleTimeSeconds,
|
|
16093
18023
|
currentProjection: taa ? "jittered" : "unjittered",
|
|
16094
18024
|
previousProjection: "unjittered",
|
|
16095
18025
|
historyRequired: taa,
|
|
@@ -16097,8 +18027,8 @@ function createTemporalView(input) {
|
|
|
16097
18027
|
});
|
|
16098
18028
|
}
|
|
16099
18029
|
function resolveTemporalReset(previous, next) {
|
|
16100
|
-
if (
|
|
16101
|
-
if (
|
|
18030
|
+
if (previous === void 0) return "first-frame";
|
|
18031
|
+
if (next.mode === "taa" && previous.mode !== "taa") return "first-frame";
|
|
16102
18032
|
const before = previous.input;
|
|
16103
18033
|
const after = next.input;
|
|
16104
18034
|
if (before.width !== after.width || before.height !== after.height) return "resize";
|
|
@@ -16125,6 +18055,12 @@ function graphExecutionPhase(passName) {
|
|
|
16125
18055
|
return "record/graph-execute/ssr-reflection-mip";
|
|
16126
18056
|
}
|
|
16127
18057
|
if (passName === "ssr-compose") return "record/graph-execute/ssr-compose";
|
|
18058
|
+
if (passName.startsWith("bloom-downsample-")) {
|
|
18059
|
+
return "record/graph-execute/bloom-downsample";
|
|
18060
|
+
}
|
|
18061
|
+
if (passName.startsWith("bloom-upsample-")) {
|
|
18062
|
+
return "record/graph-execute/bloom-upsample";
|
|
18063
|
+
}
|
|
16128
18064
|
switch (passName) {
|
|
16129
18065
|
case "cluster-binner-upload":
|
|
16130
18066
|
case "cluster-membership-producer":
|
|
@@ -16154,12 +18090,10 @@ function graphExecutionPhase(passName) {
|
|
|
16154
18090
|
return "record/graph-execute/main";
|
|
16155
18091
|
case "fxaa":
|
|
16156
18092
|
return "record/graph-execute/fxaa";
|
|
16157
|
-
case "bloom-
|
|
16158
|
-
return "record/graph-execute/bloom-
|
|
16159
|
-
case "bloom-
|
|
16160
|
-
return "record/graph-execute/bloom-
|
|
16161
|
-
case "bloom-blur-v":
|
|
16162
|
-
return "record/graph-execute/bloom-blur-v";
|
|
18093
|
+
case "bloom-downsample":
|
|
18094
|
+
return "record/graph-execute/bloom-downsample";
|
|
18095
|
+
case "bloom-upsample":
|
|
18096
|
+
return "record/graph-execute/bloom-upsample";
|
|
16163
18097
|
case "bloom-composite":
|
|
16164
18098
|
return "record/graph-execute/bloom-composite";
|
|
16165
18099
|
default:
|
|
@@ -16331,19 +18265,27 @@ var RenderFeatureComputeGraphProjection = class {
|
|
|
16331
18265
|
addPass(name, featureIdentity, order, work) {
|
|
16332
18266
|
const accesses = [];
|
|
16333
18267
|
const targets = /* @__PURE__ */ new Map();
|
|
16334
|
-
|
|
18268
|
+
const sampledTargets = new Set(work.sampledTargets ?? []);
|
|
18269
|
+
const storageTargets = new Set(work.storageTargets ?? []);
|
|
18270
|
+
for (const handle of [...sampledTargets, ...storageTargets]) {
|
|
16335
18271
|
let target5 = this.resolveTarget?.(handle);
|
|
16336
18272
|
if (target5 === void 0)
|
|
16337
18273
|
return err$1(
|
|
16338
18274
|
new RenderFeatureStageFailedError(featureIdentity, order, "record", "renderer-recover")
|
|
16339
18275
|
);
|
|
16340
|
-
if (handle.kind === "scene-depth") {
|
|
18276
|
+
if (typeof handle !== "string" && handle.kind === "scene-depth") {
|
|
16341
18277
|
const view = this.builder.view(target5.texture, { aspect: "depth-only" });
|
|
16342
18278
|
if (!view.ok) return view;
|
|
16343
18279
|
target5 = { ...target5, view: view.value };
|
|
16344
18280
|
}
|
|
16345
18281
|
targets.set(handle, target5);
|
|
16346
|
-
|
|
18282
|
+
if (sampledTargets.has(handle) && storageTargets.has(handle)) {
|
|
18283
|
+
accesses.push({ resource: target5.view, usage: "sampled-storage-read-write" });
|
|
18284
|
+
} else if (storageTargets.has(handle)) {
|
|
18285
|
+
accesses.push({ resource: target5.view, usage: "storage-write" });
|
|
18286
|
+
} else {
|
|
18287
|
+
accesses.push({ resource: target5.view, usage: "sampled-read" });
|
|
18288
|
+
}
|
|
16347
18289
|
}
|
|
16348
18290
|
for (const resource of work.buffers) {
|
|
16349
18291
|
const imported = importRenderFeatureGraphBuffer(
|
|
@@ -16796,6 +18738,7 @@ function projectRenderFeaturePlans(builder, executions, options) {
|
|
|
16796
18738
|
pass.resolvedGpuCompute
|
|
16797
18739
|
);
|
|
16798
18740
|
if (!added.ok) return added;
|
|
18741
|
+
state.projected.add(pass);
|
|
16799
18742
|
continue;
|
|
16800
18743
|
}
|
|
16801
18744
|
if (pass.graphics !== void 0 && pass.graphicsState !== void 0 && pass.resolvedGraphics !== void 0) {
|
|
@@ -16808,6 +18751,7 @@ function projectRenderFeaturePlans(builder, executions, options) {
|
|
|
16808
18751
|
pass.resolvedGraphics
|
|
16809
18752
|
);
|
|
16810
18753
|
if (!added.ok) return added;
|
|
18754
|
+
state.projected.add(pass);
|
|
16811
18755
|
continue;
|
|
16812
18756
|
}
|
|
16813
18757
|
return err$1(
|
|
@@ -17073,7 +19017,12 @@ function importSsrHistoryTargets(graph) {
|
|
|
17073
19017
|
};
|
|
17074
19018
|
}
|
|
17075
19019
|
function isMotionBlurTemporalDemand(motionBlur) {
|
|
17076
|
-
return motionBlur
|
|
19020
|
+
return motionBlur !== void 0 && motionBlurTemporalDemand({
|
|
19021
|
+
shutterAngle: motionBlur.shutterAngle,
|
|
19022
|
+
maxRadiusPixels: motionBlur.maxRadiusPixels,
|
|
19023
|
+
sampleCount: motionBlur.sampleCount,
|
|
19024
|
+
targetFps: motionBlur.targetFps ?? 60
|
|
19025
|
+
});
|
|
17077
19026
|
}
|
|
17078
19027
|
function renderTargetMipCount(work) {
|
|
17079
19028
|
const { descriptor } = work.physical;
|
|
@@ -17644,7 +19593,7 @@ function commitFeatureCandidate(internals, candidate) {
|
|
|
17644
19593
|
state.plans = candidate.plans;
|
|
17645
19594
|
state.fullscreenEffects = candidate.fullscreenEffects;
|
|
17646
19595
|
}
|
|
17647
|
-
function topologyOf(internals, frameState, pipelineState, camera, clearOnly, lights, width, height, shadowMapSize, gpuDriven, featureGraphCandidate, featurePlanRevision, cubeCaptureState, transmissionDemand, volumetricFog, standardLighting, ssr, renderExtent) {
|
|
19596
|
+
function topologyOf(internals, frameState, pipelineState, camera, clearOnly, lights, width, height, shadowMapSize, gpuDriven, featureGraphCandidate, featurePlanRevision, cubeCaptureState, transmissionDemand, volumetricFog, standardLighting, ssr, renderExtent, analyticFog) {
|
|
17648
19597
|
const cascadeCount = Math.max(1, Math.min(4, Math.round(lights.cascadeCount ?? 1)));
|
|
17649
19598
|
const surfaceFormats = resolveSurfaceFormatPair(
|
|
17650
19599
|
internals.device.caps.backendKind,
|
|
@@ -17688,6 +19637,7 @@ function topologyOf(internals, frameState, pipelineState, camera, clearOnly, lig
|
|
|
17688
19637
|
reflectionFallback: { enabled: frameState.reflectionFallbackDemand === true },
|
|
17689
19638
|
ssr,
|
|
17690
19639
|
...renderExtent === void 0 ? {} : { extent: renderExtent },
|
|
19640
|
+
analyticFog: !clearOnly && analyticFog,
|
|
17691
19641
|
surface: {
|
|
17692
19642
|
width,
|
|
17693
19643
|
height,
|
|
@@ -17698,7 +19648,11 @@ function topologyOf(internals, frameState, pipelineState, camera, clearOnly, lig
|
|
|
17698
19648
|
camera: {
|
|
17699
19649
|
tonemap: camera.tonemap,
|
|
17700
19650
|
antialias: camera.antialias,
|
|
17701
|
-
|
|
19651
|
+
// Bloom intensity is a frame parameter, not a graph identity. Fold
|
|
19652
|
+
// only its zero/non-zero admission into the closed topology switch so
|
|
19653
|
+
// positive intensity changes update the recorder UBO in place.
|
|
19654
|
+
bloom: standardBloomAdmitted(camera) ? "on" : "off",
|
|
19655
|
+
bloomIntensity: camera.bloomIntensity,
|
|
17702
19656
|
...depthOfField === void 0 ? {} : { depthOfField }
|
|
17703
19657
|
},
|
|
17704
19658
|
atmosphere: !clearOnly && frameState.environmentFrame?.source.kind === "atmosphere",
|
|
@@ -17976,7 +19930,7 @@ function settleDepthOfFieldGraphCandidate(frameState, submitted) {
|
|
|
17976
19930
|
frameState.depthOfFieldPreviousStandardLightingGraphSignature = "";
|
|
17977
19931
|
frameState.depthOfFieldCandidateGraph = null;
|
|
17978
19932
|
}
|
|
17979
|
-
function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera, lights, width, height, shadowMapSize, gpuDriven, featureGraphCandidate, cubeCaptureState, clearOnly = false, transmissionDemand, occlusion, volumetricFog, volumeTopologyCandidate = false, standardLighting, onRecoveryError, ssr, renderExtent) {
|
|
19933
|
+
function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera, lights, width, height, shadowMapSize, gpuDriven, featureGraphCandidate, cubeCaptureState, clearOnly = false, transmissionDemand, occlusion, volumetricFog, volumeTopologyCandidate = false, standardLighting, onRecoveryError, ssr, renderExtent, analyticFog = false) {
|
|
17980
19934
|
const featureMetrics = mutableFeatureGraphInspection(internals);
|
|
17981
19935
|
featureMetrics.ensureCalls += 1;
|
|
17982
19936
|
const requestedStandardLightingSignature = standardLightingTopologySignature(standardLighting);
|
|
@@ -18027,7 +19981,8 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
18027
19981
|
volumetricFog,
|
|
18028
19982
|
standardLighting,
|
|
18029
19983
|
ssr,
|
|
18030
|
-
renderExtent
|
|
19984
|
+
renderExtent,
|
|
19985
|
+
analyticFog
|
|
18031
19986
|
);
|
|
18032
19987
|
if (!topologyResult.ok) {
|
|
18033
19988
|
featureMetrics.buildFailures += 1;
|
|
@@ -18040,6 +19995,10 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
18040
19995
|
}
|
|
18041
19996
|
const requestedPostEffects = topologyResult.value.config?.postEffects ?? [];
|
|
18042
19997
|
const featurePostProcessEntries = featureGraphCandidate?.fullscreenEffects ?? getRenderFeatureGraphState(internals).fullscreenEffects;
|
|
19998
|
+
const fullscreenFeatureIds = /* @__PURE__ */ new Set([
|
|
19999
|
+
...featurePostProcessEntries.keys(),
|
|
20000
|
+
"forgeax.motion-blur"
|
|
20001
|
+
]);
|
|
18043
20002
|
const postProcessAdmission = resolvePostProcessChainAdmission(
|
|
18044
20003
|
requestedPostEffects,
|
|
18045
20004
|
(identity) => {
|
|
@@ -18076,8 +20035,15 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
18076
20035
|
postEffects: []
|
|
18077
20036
|
}
|
|
18078
20037
|
} : topologyResult.value;
|
|
20038
|
+
const topologyIdentity = {
|
|
20039
|
+
...topology,
|
|
20040
|
+
camera: {
|
|
20041
|
+
...topology.camera,
|
|
20042
|
+
bloomIntensity: void 0
|
|
20043
|
+
}
|
|
20044
|
+
};
|
|
18079
20045
|
const key = JSON.stringify({
|
|
18080
|
-
topology,
|
|
20046
|
+
topology: topologyIdentity,
|
|
18081
20047
|
// Observation domains are an explicit per-frame graph demand. They are
|
|
18082
20048
|
// part of the cache identity so a request made after the ordinary graph
|
|
18083
20049
|
// was compiled can admit the matching tone/output capture boundaries on
|
|
@@ -18174,7 +20140,6 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
18174
20140
|
return lastKnownGood();
|
|
18175
20141
|
}
|
|
18176
20142
|
const ssrHistory = ssr?.status === "admitted" ? importSsrHistoryTargets(builder) : void 0;
|
|
18177
|
-
let projectedFeatures = false;
|
|
18178
20143
|
const featureProjection = createRenderFeatureProjectionState();
|
|
18179
20144
|
let featureShadows;
|
|
18180
20145
|
let projectedGpuDriven = false;
|
|
@@ -18227,11 +20192,12 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
18227
20192
|
featureProjection
|
|
18228
20193
|
),
|
|
18229
20194
|
contributeFeatures: (targets, semanticTargets = [], namedTargets = {}, standardSurfaceAccesses = []) => {
|
|
18230
|
-
|
|
18231
|
-
|
|
18232
|
-
|
|
20195
|
+
const plans = Object.keys(namedTargets).length === 0 ? projectedFeaturePlans.value.filter(
|
|
20196
|
+
(execution) => !fullscreenFeatureIds.has(execution.featureIdentity)
|
|
20197
|
+
) : projectedFeaturePlans.value;
|
|
20198
|
+
if (plans.length === 0) return ok$1(void 0);
|
|
18233
20199
|
const resolveTarget = targetResolver(targets, semanticTargets, namedTargets);
|
|
18234
|
-
const projected = projectRenderFeaturePlans(builder,
|
|
20200
|
+
const projected = projectRenderFeaturePlans(builder, plans, {
|
|
18235
20201
|
state: featureProjection,
|
|
18236
20202
|
resolveTarget,
|
|
18237
20203
|
standardSurfaceAccesses,
|
|
@@ -18348,6 +20314,70 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
18348
20314
|
}
|
|
18349
20315
|
return compiled.value;
|
|
18350
20316
|
}
|
|
20317
|
+
function deriveMotionBlurExecutionReceipt(internals, frameState) {
|
|
20318
|
+
const graph = frameState.compiledFrameGraph;
|
|
20319
|
+
const deviceGeneration = internals.deviceScope?.generation;
|
|
20320
|
+
if (graph === null || deviceGeneration === void 0) return void 0;
|
|
20321
|
+
const info = graph.inspect();
|
|
20322
|
+
const graphPasses = new Map(info.passes.map((pass) => [pass.name, pass]));
|
|
20323
|
+
const summaryGraphPass = graphPasses.get("motion-blur-tile-summary");
|
|
20324
|
+
const reconstructGraphPass = graphPasses.get("motion-blur");
|
|
20325
|
+
if (summaryGraphPass?.kind !== "compute" || reconstructGraphPass?.kind !== "compute") {
|
|
20326
|
+
return void 0;
|
|
20327
|
+
}
|
|
20328
|
+
const planned = getRenderFeatureGraphState(internals).plans.find(
|
|
20329
|
+
(candidate) => candidate.featureIdentity === "forgeax.motion-blur"
|
|
20330
|
+
);
|
|
20331
|
+
const execution = planned === void 0 ? void 0 : getRenderFeaturePlanExecutionProjection(planned);
|
|
20332
|
+
if (execution === void 0 || execution.featureIdentity !== "forgeax.motion-blur") {
|
|
20333
|
+
return void 0;
|
|
20334
|
+
}
|
|
20335
|
+
const executionPass = (name) => execution.passes.find((pass) => pass.name === name);
|
|
20336
|
+
const summary = executionPass("motion-blur-tile-summary");
|
|
20337
|
+
const reconstruct = executionPass("motion-blur");
|
|
20338
|
+
if (summary?.gpuCompute === void 0 || reconstruct?.gpuCompute === void 0 || summary.resolvedGpuCompute === void 0 || reconstruct.resolvedGpuCompute === void 0) {
|
|
20339
|
+
return void 0;
|
|
20340
|
+
}
|
|
20341
|
+
const directWorkgroups = (pass) => {
|
|
20342
|
+
const dispatch = pass.dispatches[0];
|
|
20343
|
+
if (dispatch === void 0 || !("workgroups" in dispatch)) return void 0;
|
|
20344
|
+
return dispatch.workgroups;
|
|
20345
|
+
};
|
|
20346
|
+
const summaryWorkgroups = directWorkgroups(summary.resolvedGpuCompute);
|
|
20347
|
+
const reconstructWorkgroups = directWorkgroups(reconstruct.resolvedGpuCompute);
|
|
20348
|
+
if (summaryWorkgroups === void 0 || reconstructWorkgroups === void 0) return void 0;
|
|
20349
|
+
const outputResource = info.resources.find(
|
|
20350
|
+
(resource) => resource.label === "motion-blurred-color" || reconstructGraphPass.accesses.some(
|
|
20351
|
+
(access) => access.resource === resource.label && access.usage === "storage-write"
|
|
20352
|
+
)
|
|
20353
|
+
);
|
|
20354
|
+
if (outputResource?.descriptor.kind !== "texture" || outputResource.descriptor.format !== "rgba16float") {
|
|
20355
|
+
return void 0;
|
|
20356
|
+
}
|
|
20357
|
+
const summaryReceipt = {
|
|
20358
|
+
name: "motion-blur-tile-summary",
|
|
20359
|
+
kind: "compute",
|
|
20360
|
+
entryPoint: "tile_summary",
|
|
20361
|
+
workgroups: summaryWorkgroups
|
|
20362
|
+
};
|
|
20363
|
+
const reconstructReceipt = {
|
|
20364
|
+
name: "motion-blur",
|
|
20365
|
+
kind: "compute",
|
|
20366
|
+
entryPoint: "reconstruct",
|
|
20367
|
+
workgroups: reconstructWorkgroups
|
|
20368
|
+
};
|
|
20369
|
+
return Object.freeze({
|
|
20370
|
+
frameId: frameState.frameNumber,
|
|
20371
|
+
deviceGeneration,
|
|
20372
|
+
// RenderGraphBuilder generations are process-global. The renderer's
|
|
20373
|
+
// frame-state generation is the identity used by inspection and by the
|
|
20374
|
+
// same-frame admission check below, so retain that owner-scoped value.
|
|
20375
|
+
graphGeneration: frameState.graphGeneration,
|
|
20376
|
+
program: "motion-blur-compute",
|
|
20377
|
+
outputFormat: "rgba16float",
|
|
20378
|
+
passes: [summaryReceipt, reconstructReceipt]
|
|
20379
|
+
});
|
|
20380
|
+
}
|
|
18351
20381
|
function executeCompiledFrameGraph(internals, frameState, frame, encoder, runPass, frameHooks, readbackFaces, onSubmitted, timingCapture) {
|
|
18352
20382
|
const legacySubmitCommit = typeof frameHooks === "function" ? frameHooks : void 0;
|
|
18353
20383
|
const resolvedFrameHooks = typeof frameHooks === "function" ? void 0 : frameHooks;
|
|
@@ -18366,6 +20396,7 @@ function executeCompiledFrameGraph(internals, frameState, frame, encoder, runPas
|
|
|
18366
20396
|
rejectTemporalFrame();
|
|
18367
20397
|
return false;
|
|
18368
20398
|
}
|
|
20399
|
+
const motionBlurExecution = deriveMotionBlurExecutionReceipt(internals, frameState);
|
|
18369
20400
|
const legacyTimingInstrumentation = timingCapture === void 0 ? void 0 : createTimingInstrumentation(timingCapture);
|
|
18370
20401
|
const timingHost = internals;
|
|
18371
20402
|
timingHost.gpuPassTimingCapture = void 0;
|
|
@@ -18894,6 +20925,11 @@ function executeCompiledFrameGraph(internals, frameState, frame, encoder, runPas
|
|
|
18894
20925
|
commit: () => {
|
|
18895
20926
|
resolvedFrameHooks?.onSubmitted?.();
|
|
18896
20927
|
legacySubmitCommit?.();
|
|
20928
|
+
if (motionBlurExecution === void 0) {
|
|
20929
|
+
delete frameState.motionBlurExecution;
|
|
20930
|
+
} else {
|
|
20931
|
+
frameState.motionBlurExecution = motionBlurExecution;
|
|
20932
|
+
}
|
|
18897
20933
|
if (frameState.temporalFrameInput !== void 0) {
|
|
18898
20934
|
const temporal = frameState.temporalFrameTransaction.commit({ accepted: true });
|
|
18899
20935
|
if (temporal.ok) {
|
|
@@ -19663,6 +21699,7 @@ function volumeTemporalSignature(fog, prepared, camera, lights, width, height) {
|
|
|
19663
21699
|
densityGeneration: prepared.densityGeneration,
|
|
19664
21700
|
width,
|
|
19665
21701
|
height,
|
|
21702
|
+
...fog.worldTimeSeconds === void 0 ? {} : { worldTimeSeconds: fog.worldTimeSeconds },
|
|
19666
21703
|
...fog.lightKind === void 0 ? {} : { lightKind: fog.lightKind },
|
|
19667
21704
|
...fog.lightEntity === void 0 ? {} : { lightEntity: fog.lightEntity },
|
|
19668
21705
|
...fog.pointLightEntity === void 0 ? {} : { pointLightEntity: fog.pointLightEntity },
|
|
@@ -19721,7 +21758,7 @@ function prepareVolumetricFogFrame(internals, frameState, world, worlds, fog, li
|
|
|
19721
21758
|
}
|
|
19722
21759
|
return void 0;
|
|
19723
21760
|
}
|
|
19724
|
-
const params = new Float32Array(
|
|
21761
|
+
const params = new Float32Array(36);
|
|
19725
21762
|
params.set(fog.fog.bounds.min, 0);
|
|
19726
21763
|
params.set(fog.fog.bounds.max, 4);
|
|
19727
21764
|
params.set(fog.fog.extinction, 8);
|
|
@@ -19733,7 +21770,7 @@ function prepareVolumetricFogFrame(internals, frameState, world, worlds, fog, li
|
|
|
19733
21770
|
const lightColor = selectedSpot?.color ?? lights.directional?.color ?? vec3.create(0, 0, 0);
|
|
19734
21771
|
if (lightDirection === void 0 || lightColor === void 0) return void 0;
|
|
19735
21772
|
params.set(lightDirection, 20);
|
|
19736
|
-
params[23] =
|
|
21773
|
+
params[23] = 0;
|
|
19737
21774
|
const selectedPointIndex = selectedPoint === void 0 ? -1 : lights.point.findIndex((light) => light.entity === selectedPoint.entity);
|
|
19738
21775
|
const selectedSpotIndex = selectedSpot === void 0 ? -1 : lights.spot.findIndex((light) => light.entity === selectedSpot.entity);
|
|
19739
21776
|
const selectedSpotClusterIndex = selectedSpotIndex < 0 ? -1 : lights.point.length + selectedSpotIndex;
|
|
@@ -19744,6 +21781,7 @@ function prepareVolumetricFogFrame(internals, frameState, world, worlds, fog, li
|
|
|
19744
21781
|
params[29] = fog.fog.anisotropy;
|
|
19745
21782
|
params[30] = selectedPoint !== void 0 && selectedSpot !== void 0 ? 3 : selectedPoint !== void 0 ? 1 : selectedSpot !== void 0 ? 2 : 0;
|
|
19746
21783
|
params[31] = frameState.temporalFrame !== void 0 && frameState.temporalFrame.resetReason === "none" ? 1 : 0;
|
|
21784
|
+
params[32] = fog.worldTimeSeconds ?? 0;
|
|
19747
21785
|
const paramsBuffer = stageVolumetricFogParams(internals, frameState, params);
|
|
19748
21786
|
if (paramsBuffer === void 0) return void 0;
|
|
19749
21787
|
return {
|
|
@@ -19847,6 +21885,8 @@ function mapSsrHistoryResetReason(reason2) {
|
|
|
19847
21885
|
case "environment-change":
|
|
19848
21886
|
case "fog-change":
|
|
19849
21887
|
return "reflection-generation";
|
|
21888
|
+
case "time-discontinuity":
|
|
21889
|
+
return "camera-cut";
|
|
19850
21890
|
case "device-recover":
|
|
19851
21891
|
return "device-recovery";
|
|
19852
21892
|
case "submit-failure":
|
|
@@ -19854,6 +21894,27 @@ function mapSsrHistoryResetReason(reason2) {
|
|
|
19854
21894
|
return void 0;
|
|
19855
21895
|
}
|
|
19856
21896
|
}
|
|
21897
|
+
function frameClockSample(world, worlds, camera, sampleTimeSeconds) {
|
|
21898
|
+
const owner = worlds[camera.worldId ?? 0] ?? world;
|
|
21899
|
+
const invalidSampleTime = sampleTimeSeconds !== void 0 && !Number.isFinite(sampleTimeSeconds);
|
|
21900
|
+
try {
|
|
21901
|
+
const clock = owner.getResource(Time);
|
|
21902
|
+
return {
|
|
21903
|
+
deltaSeconds: Number.isFinite(clock.delta) ? Math.max(0, clock.delta) : 0,
|
|
21904
|
+
// Keep the raw host sample even when ECS accepted only a clamped
|
|
21905
|
+
// simulation delta. Temporal admission compares this value with the
|
|
21906
|
+
// last successfully submitted sample and therefore sees real hitches.
|
|
21907
|
+
elapsedSeconds: sampleTimeSeconds === void 0 ? Number.isFinite(clock.elapsed) ? clock.elapsed : void 0 : Number.isFinite(sampleTimeSeconds) ? sampleTimeSeconds : void 0,
|
|
21908
|
+
invalidSampleTime
|
|
21909
|
+
};
|
|
21910
|
+
} catch {
|
|
21911
|
+
return {
|
|
21912
|
+
deltaSeconds: 0,
|
|
21913
|
+
elapsedSeconds: Number.isFinite(sampleTimeSeconds) ? sampleTimeSeconds : void 0,
|
|
21914
|
+
invalidSampleTime
|
|
21915
|
+
};
|
|
21916
|
+
}
|
|
21917
|
+
}
|
|
19857
21918
|
function stageSsrHistoryCandidate(internals, frameState, camera, width, height, admission, temporalView) {
|
|
19858
21919
|
const previousAdmission = frameState.ssrSpatialAdmission;
|
|
19859
21920
|
frameState.ssrSpatialAdmission = admission;
|
|
@@ -20081,7 +22142,7 @@ function updateVolumetricFogInspection(frameState, fog, lights, prepared, submit
|
|
|
20081
22142
|
memoryBytes: 0
|
|
20082
22143
|
});
|
|
20083
22144
|
}
|
|
20084
|
-
function recordFrame(internals, world, cameras, lights, renderables, transparentDispatch, frameState, dispatchCounts, bindGroupCounts, skylight, skylightCount, skybox, skyboxCount, postProcessParams, worlds = [world], profilePhase, gpuDriven, renderReadLeases, featureGraphCandidate, pointsLinesOwner, cubeCapture, environmentSignature = "", fogSignature = "", environmentReady = true, transmissionDemand, shadowCasterEntityKeys = EMPTY_ENTITY_KEYS, shadowCasterDrawKeys = void 0, volumetricFog, timingCapture, shadowCasterMembership, shadowCasterProjection, shadowCasterWorldKeys, preparedLighting, ssrDependencies, onRenderableDraw) {
|
|
22145
|
+
function recordFrame(internals, world, cameras, lights, renderables, transparentDispatch, frameState, dispatchCounts, bindGroupCounts, skylight, skylightCount, skybox, skyboxCount, postProcessParams, worlds = [world], profilePhase, gpuDriven, renderReadLeases, featureGraphCandidate, pointsLinesOwner, cubeCapture, environmentSignature = "", fogSignature = "", environmentReady = true, transmissionDemand, shadowCasterEntityKeys = EMPTY_ENTITY_KEYS, shadowCasterDrawKeys = void 0, volumetricFog, timingCapture, shadowCasterMembership, shadowCasterProjection, shadowCasterWorldKeys, preparedLighting, ssrDependencies, onRenderableDraw, analyticFog, sampleTimeSeconds, temporalReset = false) {
|
|
20085
22146
|
frameState.reflectionFallbackObservationSource = void 0;
|
|
20086
22147
|
frameState.reflectionFallbackCompletion = void 0;
|
|
20087
22148
|
let activeCameras = cameras;
|
|
@@ -20321,6 +22382,7 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
20321
22382
|
...frameGpuDriven.activeEntityKeys === void 0 ? {} : { activeEntityKeys: frameGpuDriven.activeEntityKeys },
|
|
20322
22383
|
...frameGpuDriven.activeEntityRevision === void 0 ? {} : { activeEntityRevision: frameGpuDriven.activeEntityRevision },
|
|
20323
22384
|
...frameGpuDriven.telemetryCandidateCount === void 0 ? {} : { telemetryCandidateCount: frameGpuDriven.telemetryCandidateCount },
|
|
22385
|
+
...frameGpuDriven.telemetrySubmit === void 0 ? {} : { telemetrySubmit: frameGpuDriven.telemetrySubmit },
|
|
20324
22386
|
...frameGpuDriven.occlusion === void 0 ? {} : { occlusion: frameGpuDriven.occlusion },
|
|
20325
22387
|
...frameGpuDriven.scene?.structureMetrics === void 0 ? {} : {
|
|
20326
22388
|
structureMetrics: {
|
|
@@ -20424,7 +22486,8 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
20424
22486
|
standard,
|
|
20425
22487
|
void 0,
|
|
20426
22488
|
ssrAdmission,
|
|
20427
|
-
renderExtent
|
|
22489
|
+
renderExtent,
|
|
22490
|
+
analyticFog !== void 0 && analyticFog.density > 0 && analyticFog.maxOpacity > 0
|
|
20428
22491
|
)
|
|
20429
22492
|
);
|
|
20430
22493
|
if (preflightGraph === null) {
|
|
@@ -20487,6 +22550,21 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
20487
22550
|
const targetW = swapTarget.targetW;
|
|
20488
22551
|
const targetH = swapTarget.targetH;
|
|
20489
22552
|
const previousTemporalView = frameState.lastSuccessfulTemporalView;
|
|
22553
|
+
const motionBlurDemand = camera.motionBlur === void 0 ? false : motionBlurTemporalDemand({
|
|
22554
|
+
shutterAngle: camera.motionBlur.shutterAngle,
|
|
22555
|
+
maxRadiusPixels: camera.motionBlur.maxRadiusPixels,
|
|
22556
|
+
sampleCount: camera.motionBlur.sampleCount,
|
|
22557
|
+
targetFps: camera.motionBlur.targetFps ?? 60
|
|
22558
|
+
});
|
|
22559
|
+
const temporalDemand = camera.antialias === "taa" || motionBlurDemand;
|
|
22560
|
+
const clock = frameClockSample(world, worlds, camera, sampleTimeSeconds);
|
|
22561
|
+
const deltaSeconds = temporalReset || clock.invalidSampleTime ? Number.NaN : motionBlurSampleDelta(
|
|
22562
|
+
clock.elapsedSeconds,
|
|
22563
|
+
previousTemporalView?.sampleTimeSeconds,
|
|
22564
|
+
clock.deltaSeconds
|
|
22565
|
+
);
|
|
22566
|
+
const intervalValid = isMotionBlurIntervalValid(deltaSeconds);
|
|
22567
|
+
const motionBlurFirstFrame = motionBlurDemand && camera.antialias !== "taa" && (previousTemporalView === void 0 || previousTemporalView.mode === "off" && !previousTemporalView.historyValid && previousTemporalView.resetReason === void 0);
|
|
20490
22568
|
const currentUnjitteredViewProjection = mat4.multiply(
|
|
20491
22569
|
mat4.create(),
|
|
20492
22570
|
computeProjectionMatrix(camera),
|
|
@@ -20511,16 +22589,20 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
20511
22589
|
internalHeight: renderExtent.internalHeight
|
|
20512
22590
|
},
|
|
20513
22591
|
...camera.historyVersion === void 0 ? {} : { historyVersion: camera.historyVersion },
|
|
22592
|
+
...clock.elapsedSeconds === void 0 ? {} : { sampleTimeSeconds: clock.elapsedSeconds },
|
|
20514
22593
|
historyValid: false,
|
|
20515
22594
|
currentUnjitteredViewProjection,
|
|
20516
22595
|
currentCameraPosition
|
|
20517
22596
|
});
|
|
20518
|
-
const resetReason =
|
|
22597
|
+
const resetReason = temporalDemand && environmentReady ? resolveTemporalReset(previousTemporalView, temporalCandidate) ?? (temporalReset || motionBlurDemand && !intervalValid ? "time-discontinuity" : motionBlurFirstFrame ? "first-frame" : void 0) : void 0;
|
|
20519
22598
|
const canReusePrevious = previousTemporalView !== void 0 && resetReason === void 0;
|
|
20520
22599
|
const temporalView = createTemporalView({
|
|
20521
22600
|
...temporalCandidate.input,
|
|
20522
22601
|
frameIndex: resetReason === void 0 ? temporalCandidate.input.frameIndex : 0,
|
|
20523
|
-
|
|
22602
|
+
// Motion Blur needs a finite positive pair before it can read the
|
|
22603
|
+
// baseline. TAA-only frames retain their existing temporal contract and
|
|
22604
|
+
// do not depend on the simulation clock being present in a test host.
|
|
22605
|
+
historyValid: temporalDemand && environmentReady && canReusePrevious && (!motionBlurDemand || intervalValid),
|
|
20524
22606
|
resetReason,
|
|
20525
22607
|
...canReusePrevious && previousTemporalView.currentUnjitteredViewProjection !== void 0 ? {
|
|
20526
22608
|
previousUnjitteredViewProjection: previousTemporalView.currentUnjitteredViewProjection
|
|
@@ -20559,17 +22641,23 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
20559
22641
|
);
|
|
20560
22642
|
activeVolumetricFogContext = resolvedHistory.context;
|
|
20561
22643
|
activeVolumetricFogSignature = resolvedHistory.signature;
|
|
20562
|
-
const
|
|
22644
|
+
const previousTime = frameState.volumetricFogHistorySignature?.worldTimeSeconds;
|
|
22645
|
+
const currentTime = resolvedHistory.signature.worldTimeSeconds;
|
|
22646
|
+
const historyParams = new Float32Array([
|
|
22647
|
+
activeVolumetricFogContext.historyValid ? 1 : 0,
|
|
22648
|
+
currentTime ?? 0,
|
|
22649
|
+
previousTime === void 0 || currentTime === void 0 ? 0 : Math.max(0, currentTime - previousTime)
|
|
22650
|
+
]);
|
|
20563
22651
|
if (frameState.volumetricFogPendingParams !== void 0) {
|
|
20564
|
-
frameState.volumetricFogPendingParams
|
|
22652
|
+
frameState.volumetricFogPendingParams.set(historyParams, 31);
|
|
20565
22653
|
}
|
|
20566
|
-
const
|
|
22654
|
+
const historyParamsWritten = internals.device.queue.writeBuffer(
|
|
20567
22655
|
activeVolumetricFogContext.paramsBuffer,
|
|
20568
22656
|
31 * Float32Array.BYTES_PER_ELEMENT,
|
|
20569
|
-
|
|
22657
|
+
historyParams
|
|
20570
22658
|
);
|
|
20571
|
-
if (!
|
|
20572
|
-
internals.errorRegistry.fire(
|
|
22659
|
+
if (!historyParamsWritten.ok) {
|
|
22660
|
+
internals.errorRegistry.fire(historyParamsWritten.error);
|
|
20573
22661
|
abortPreparedGpuDriven();
|
|
20574
22662
|
return false;
|
|
20575
22663
|
}
|
|
@@ -20759,14 +22847,33 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
20759
22847
|
);
|
|
20760
22848
|
}
|
|
20761
22849
|
const framePostProcessParams = new Map(postProcessParams);
|
|
22850
|
+
if (analyticFog !== void 0)
|
|
22851
|
+
framePostProcessParams.set(
|
|
22852
|
+
"forgeax.analytic-fog",
|
|
22853
|
+
encodeAnalyticFogParams(analyticFog, recordCamera)
|
|
22854
|
+
);
|
|
20762
22855
|
if (camera.motionBlur !== void 0) {
|
|
20763
|
-
|
|
20764
|
-
|
|
20765
|
-
|
|
20766
|
-
|
|
20767
|
-
|
|
20768
|
-
|
|
20769
|
-
|
|
22856
|
+
if (motionBlurDemand) {
|
|
22857
|
+
const motionBlurParams = new Uint8Array(MOTION_BLUR_PARAMS_BYTE_SIZE);
|
|
22858
|
+
const motionBlurView = new DataView(motionBlurParams.buffer);
|
|
22859
|
+
const uniformDeltaSeconds = Number.isFinite(deltaSeconds) ? deltaSeconds : 0;
|
|
22860
|
+
motionBlurView.setFloat32(0, camera.motionBlur.shutterAngle, true);
|
|
22861
|
+
motionBlurView.setFloat32(4, camera.motionBlur.maxRadiusPixels, true);
|
|
22862
|
+
motionBlurView.setUint32(
|
|
22863
|
+
8,
|
|
22864
|
+
effectiveMotionBlurSampleCount(camera.motionBlur.sampleCount),
|
|
22865
|
+
true
|
|
22866
|
+
);
|
|
22867
|
+
motionBlurView.setUint32(12, temporalView?.historyValid === true ? 0 : 1, true);
|
|
22868
|
+
motionBlurView.setFloat32(16, camera.motionBlur.targetFps ?? 60, true);
|
|
22869
|
+
motionBlurView.setFloat32(
|
|
22870
|
+
20,
|
|
22871
|
+
motionBlurExposureScale(uniformDeltaSeconds, camera.motionBlur.targetFps ?? 60),
|
|
22872
|
+
true
|
|
22873
|
+
);
|
|
22874
|
+
motionBlurView.setFloat32(24, uniformDeltaSeconds, true);
|
|
22875
|
+
framePostProcessParams.set("forgeax.motion-blur", motionBlurParams);
|
|
22876
|
+
}
|
|
20770
22877
|
}
|
|
20771
22878
|
if (camera.depthOfField !== void 0) {
|
|
20772
22879
|
const dofParams = packDepthOfFieldParams(camera.depthOfField, {
|
|
@@ -21939,6 +24046,7 @@ function createTemporalFrameTransaction(options) {
|
|
|
21939
24046
|
let staged;
|
|
21940
24047
|
let historyEpoch = 0;
|
|
21941
24048
|
let pendingResetReason;
|
|
24049
|
+
let lastFailure;
|
|
21942
24050
|
return {
|
|
21943
24051
|
stage(input) {
|
|
21944
24052
|
staged = copyInput(input);
|
|
@@ -21946,9 +24054,11 @@ function createTemporalFrameTransaction(options) {
|
|
|
21946
24054
|
commit(submit) {
|
|
21947
24055
|
if (!submit.accepted) {
|
|
21948
24056
|
staged = void 0;
|
|
24057
|
+
lastFailure = submit.reason ?? "queue-submit-failed";
|
|
21949
24058
|
return err$1(new TemporalFrameSubmitError(submit.reason ?? "queue-submit-failed"));
|
|
21950
24059
|
}
|
|
21951
24060
|
if (staged === void 0) {
|
|
24061
|
+
lastFailure = "queue-submit-failed";
|
|
21952
24062
|
return err$1(new TemporalFrameSubmitError("queue-submit-failed"));
|
|
21953
24063
|
}
|
|
21954
24064
|
const resetReason = submit.resetReason ?? staged.resetReason ?? pendingResetReason ?? "none";
|
|
@@ -21969,6 +24079,7 @@ function createTemporalFrameTransaction(options) {
|
|
|
21969
24079
|
accepted = next;
|
|
21970
24080
|
staged = void 0;
|
|
21971
24081
|
pendingResetReason = void 0;
|
|
24082
|
+
lastFailure = void 0;
|
|
21972
24083
|
return ok$1(copyFrame(next));
|
|
21973
24084
|
},
|
|
21974
24085
|
snapshot() {
|
|
@@ -21976,11 +24087,19 @@ function createTemporalFrameTransaction(options) {
|
|
|
21976
24087
|
throw new Error("TemporalFrame snapshot is unavailable before commit");
|
|
21977
24088
|
return copyFrame(accepted);
|
|
21978
24089
|
},
|
|
24090
|
+
inspect() {
|
|
24091
|
+
return {
|
|
24092
|
+
accepted: accepted !== void 0,
|
|
24093
|
+
staged: staged !== void 0,
|
|
24094
|
+
...lastFailure === void 0 ? {} : { lastFailure }
|
|
24095
|
+
};
|
|
24096
|
+
},
|
|
21979
24097
|
reset(reason2 = "signature-change") {
|
|
21980
24098
|
accepted = void 0;
|
|
21981
24099
|
staged = void 0;
|
|
21982
24100
|
historyEpoch += 1;
|
|
21983
24101
|
pendingResetReason = reason2;
|
|
24102
|
+
lastFailure = void 0;
|
|
21984
24103
|
}
|
|
21985
24104
|
};
|
|
21986
24105
|
}
|
|
@@ -22311,6 +24430,10 @@ function createRenderSystemRecovery(deps) {
|
|
|
22311
24430
|
worlds: seed.worlds,
|
|
22312
24431
|
owner: seed.resourceOwner,
|
|
22313
24432
|
frameNumber: candidateFrameState.frameNumber,
|
|
24433
|
+
frameSize: {
|
|
24434
|
+
width: runtime.internals.canvas.width,
|
|
24435
|
+
height: runtime.internals.canvas.height
|
|
24436
|
+
},
|
|
22314
24437
|
visibilitySnapshots: seed.frame.featureVisibilitySnapshots,
|
|
22315
24438
|
hiddenEntityReports: seed.frame.hiddenEntityReports,
|
|
22316
24439
|
targets: featureTargets,
|
|
@@ -22538,16 +24661,21 @@ function createRenderSystemRecovery(deps) {
|
|
|
22538
24661
|
throw new Error("recovery TAA pipeline is unavailable");
|
|
22539
24662
|
}
|
|
22540
24663
|
}
|
|
22541
|
-
if (camera.motionBlur
|
|
24664
|
+
if (camera.motionBlur !== void 0 && motionBlurTemporalDemand({
|
|
24665
|
+
shutterAngle: camera.motionBlur.shutterAngle,
|
|
24666
|
+
maxRadiusPixels: camera.motionBlur.maxRadiusPixels,
|
|
24667
|
+
sampleCount: camera.motionBlur.sampleCount,
|
|
24668
|
+
targetFps: camera.motionBlur.targetFps ?? 60
|
|
24669
|
+
})) {
|
|
22542
24670
|
warmPostProcessPipeline("forgeax.motion-blur", "rgba16float");
|
|
22543
24671
|
}
|
|
22544
24672
|
for (const id of candidatePostProcessIds) {
|
|
22545
24673
|
warmPostProcessPipeline(id, runtime.pipelineState.format);
|
|
22546
24674
|
}
|
|
22547
|
-
if (camera
|
|
24675
|
+
if (standardBloomAdmitted(camera)) {
|
|
22548
24676
|
runtime.pipelineState.perPassResources.ensureBloomResources?.();
|
|
22549
24677
|
const bloom = runtime.pipelineState.perPassResources.getBloomResources?.();
|
|
22550
|
-
if (bloom === null || bloom === void 0 || bloom.
|
|
24678
|
+
if (bloom === null || bloom === void 0 || bloom.bloomDownsamplePipeline === null || bloom.bloomUpsamplePipeline === null || bloom.bloomCompositePipeline === null || bloom.bloomDownsampleBindGroupLayout === null || bloom.bloomUpsampleBindGroupLayout === null || bloom.bloomCompositeBindGroupLayout === null || bloom.bloomSampler === null || bloom.bloomDownsampleParamsBuffer === null || bloom.bloomUpsampleParamsBuffer === null || bloom.bloomCompositeParamsBuffer === null) {
|
|
22551
24679
|
throw new Error("recovery Bloom resources are incomplete");
|
|
22552
24680
|
}
|
|
22553
24681
|
}
|
|
@@ -24819,6 +26947,30 @@ function coalesceSlots(slots) {
|
|
|
24819
26947
|
}
|
|
24820
26948
|
return ranges;
|
|
24821
26949
|
}
|
|
26950
|
+
function previousInstanceOrdinals(current, temporal) {
|
|
26951
|
+
const previous = temporal?.previousInstances;
|
|
26952
|
+
const currentGenerations = current?.generations;
|
|
26953
|
+
const previousGenerations = previous?.generations;
|
|
26954
|
+
if (temporal?.motionValid !== true || current === void 0 || previous === void 0 || currentGenerations === void 0 || previousGenerations === void 0 || currentGenerations.length !== current.instanceCount || previousGenerations.length !== previous.instanceCount || current.transforms.length < current.instanceCount * 16 || previous.transforms.length < previous.instanceCount * 16) {
|
|
26955
|
+
return void 0;
|
|
26956
|
+
}
|
|
26957
|
+
const currentSet = /* @__PURE__ */ new Set();
|
|
26958
|
+
for (const generation of currentGenerations) {
|
|
26959
|
+
if (generation === 0 || currentSet.has(generation)) return void 0;
|
|
26960
|
+
currentSet.add(generation);
|
|
26961
|
+
}
|
|
26962
|
+
const ordinals = /* @__PURE__ */ new Map();
|
|
26963
|
+
for (let ordinal = 0; ordinal < previousGenerations.length; ordinal += 1) {
|
|
26964
|
+
const generation = previousGenerations[ordinal] ?? 0;
|
|
26965
|
+
if (generation === 0 || ordinals.has(generation)) return void 0;
|
|
26966
|
+
ordinals.set(generation, ordinal);
|
|
26967
|
+
}
|
|
26968
|
+
return ordinals;
|
|
26969
|
+
}
|
|
26970
|
+
function instanceIdentityChanged(resident, current, ordinal) {
|
|
26971
|
+
if (resident === void 0 || current === void 0) return resident !== current;
|
|
26972
|
+
return resident[ordinal] !== current[ordinal];
|
|
26973
|
+
}
|
|
24822
26974
|
var GpuScene = class _GpuScene {
|
|
24823
26975
|
constructor(device, capacity, buffers) {
|
|
24824
26976
|
this.device = device;
|
|
@@ -24849,6 +27001,8 @@ var GpuScene = class _GpuScene {
|
|
|
24849
27001
|
sceneContentRevision = 0;
|
|
24850
27002
|
identityUploaded = false;
|
|
24851
27003
|
allocations = /* @__PURE__ */ new Map();
|
|
27004
|
+
/** Current stable instance identities retained beside compact GPU rows. */
|
|
27005
|
+
instanceGenerations = /* @__PURE__ */ new Map();
|
|
24852
27006
|
instances = new StableRangeAllocator();
|
|
24853
27007
|
transforms = new StableRangeAllocator();
|
|
24854
27008
|
draws = new StableRangeAllocator();
|
|
@@ -24904,7 +27058,7 @@ var GpuScene = class _GpuScene {
|
|
|
24904
27058
|
}
|
|
24905
27059
|
return allocation.materialStart + materialSlot;
|
|
24906
27060
|
}
|
|
24907
|
-
sync(delta) {
|
|
27061
|
+
sync(delta, temporalResolver) {
|
|
24908
27062
|
for (const transformIndex of this.pendingTemporalUploads) {
|
|
24909
27063
|
this.writesByTable.transform.push(transformIndex);
|
|
24910
27064
|
}
|
|
@@ -24962,11 +27116,11 @@ var GpuScene = class _GpuScene {
|
|
|
24962
27116
|
for (const record of publications) {
|
|
24963
27117
|
const reset = newSlots.has(record.slot) || allocationResets.has(record.slot);
|
|
24964
27118
|
if (reset || contentUpdatedSlotIds.has(record.slot)) {
|
|
24965
|
-
this.writeSlot(record, reset, writesByTable);
|
|
27119
|
+
this.writeSlot(record, reset, writesByTable, temporalResolver?.(record));
|
|
24966
27120
|
continue;
|
|
24967
27121
|
}
|
|
24968
27122
|
if (instanceUpdatedSlotIds.has(record.slot))
|
|
24969
|
-
this.writeInstanceTransforms(record, writesByTable);
|
|
27123
|
+
this.writeInstanceTransforms(record, writesByTable, temporalResolver?.(record));
|
|
24970
27124
|
this.writeRootTransform(record, false, writesByTable);
|
|
24971
27125
|
}
|
|
24972
27126
|
const uploaded = this.uploadRows(writesByTable);
|
|
@@ -25026,6 +27180,7 @@ var GpuScene = class _GpuScene {
|
|
|
25026
27180
|
}
|
|
25027
27181
|
rebuild(slots) {
|
|
25028
27182
|
this.allocations.clear();
|
|
27183
|
+
this.instanceGenerations.clear();
|
|
25029
27184
|
this.instances.reset();
|
|
25030
27185
|
this.transforms.reset(1);
|
|
25031
27186
|
this.draws.reset();
|
|
@@ -25111,7 +27266,7 @@ var GpuScene = class _GpuScene {
|
|
|
25111
27266
|
for (const buffer of this.retiredBuffers) this.device.destroyBuffer(buffer);
|
|
25112
27267
|
for (const buffer of Object.values(this.buffers)) this.device.destroyBuffer(buffer);
|
|
25113
27268
|
}
|
|
25114
|
-
writeSlot(record, resetPrevious, writes) {
|
|
27269
|
+
writeSlot(record, resetPrevious, writes, temporal) {
|
|
25115
27270
|
const allocation = this.allocations.get(record.slot);
|
|
25116
27271
|
if (allocation === void 0) throw new RangeError("GPU Scene allocation unavailable");
|
|
25117
27272
|
const primitiveOffset = record.slot * PRIMITIVE.stride;
|
|
@@ -25167,6 +27322,11 @@ var GpuScene = class _GpuScene {
|
|
|
25167
27322
|
]);
|
|
25168
27323
|
const instance = new DataView(this.tableBytes.instance);
|
|
25169
27324
|
const transform = this.transformView;
|
|
27325
|
+
const instances = record.snapshot.instances;
|
|
27326
|
+
const previousInstances = temporal?.previousInstances;
|
|
27327
|
+
const previousOrdinals = previousInstanceOrdinals(instances, temporal);
|
|
27328
|
+
const currentGenerations = instances?.generations;
|
|
27329
|
+
const residentGenerations = this.instanceGenerations.get(record.slot);
|
|
25170
27330
|
for (let ordinal = 0; ordinal < allocation.instanceCount; ordinal += 1) {
|
|
25171
27331
|
const instanceIndex = allocation.instanceStart + ordinal;
|
|
25172
27332
|
const instanceOffset = instanceIndex * INSTANCE.stride;
|
|
@@ -25188,7 +27348,7 @@ var GpuScene = class _GpuScene {
|
|
|
25188
27348
|
) || metadataChanged;
|
|
25189
27349
|
metadataChanged = writeChangedU32(instance, instanceOffset + offset(INSTANCE, "flags"), PRIMITIVE_ACTIVE) || metadataChanged;
|
|
25190
27350
|
if (allocation.instanceTransformCount > 0) {
|
|
25191
|
-
const transforms =
|
|
27351
|
+
const transforms = instances?.transforms;
|
|
25192
27352
|
const hasLocal = transforms !== void 0 && transforms.length >= (ordinal + 1) * 16;
|
|
25193
27353
|
const local = hasLocal ? transforms : IDENTITY_MATRIX;
|
|
25194
27354
|
const sourceOffset = hasLocal ? ordinal * 16 : 0;
|
|
@@ -25199,18 +27359,55 @@ var GpuScene = class _GpuScene {
|
|
|
25199
27359
|
local,
|
|
25200
27360
|
sourceOffset
|
|
25201
27361
|
);
|
|
25202
|
-
|
|
25203
|
-
|
|
25204
|
-
|
|
25205
|
-
|
|
25206
|
-
|
|
25207
|
-
|
|
25208
|
-
);
|
|
25209
|
-
|
|
25210
|
-
|
|
27362
|
+
const identityChanged = instanceIdentityChanged(
|
|
27363
|
+
residentGenerations,
|
|
27364
|
+
currentGenerations,
|
|
27365
|
+
ordinal
|
|
27366
|
+
);
|
|
27367
|
+
if (resetPrevious) {
|
|
27368
|
+
const previousOrdinal = previousOrdinals?.get(currentGenerations?.[ordinal] ?? 0);
|
|
27369
|
+
const previousSource = previousOrdinal === void 0 || previousInstances === void 0 ? void 0 : previousInstances.transforms;
|
|
27370
|
+
if (previousSource === void 0 || previousOrdinal === void 0) {
|
|
27371
|
+
writeChangedMat4(
|
|
27372
|
+
transform,
|
|
27373
|
+
localOffset + offset(TRANSFORM, "previousWorld"),
|
|
27374
|
+
local,
|
|
27375
|
+
sourceOffset
|
|
27376
|
+
);
|
|
27377
|
+
} else {
|
|
27378
|
+
writeChangedMat4(
|
|
27379
|
+
transform,
|
|
27380
|
+
localOffset + offset(TRANSFORM, "previousWorld"),
|
|
27381
|
+
previousSource,
|
|
27382
|
+
previousOrdinal * 16
|
|
27383
|
+
);
|
|
27384
|
+
this.pendingTemporalTransforms.add(transformIndex);
|
|
27385
|
+
}
|
|
27386
|
+
} else if (changed || identityChanged) {
|
|
27387
|
+
const previousOrdinal = previousOrdinals?.get(currentGenerations?.[ordinal] ?? 0);
|
|
27388
|
+
const previousSource = previousOrdinal === void 0 || previousInstances === void 0 ? void 0 : previousInstances.transforms;
|
|
27389
|
+
if (previousSource === void 0 || previousOrdinal === void 0) {
|
|
27390
|
+
writeChangedMat4(
|
|
27391
|
+
transform,
|
|
27392
|
+
localOffset + offset(TRANSFORM, "previousWorld"),
|
|
27393
|
+
local,
|
|
27394
|
+
sourceOffset
|
|
27395
|
+
);
|
|
27396
|
+
} else {
|
|
27397
|
+
writeChangedMat4(
|
|
27398
|
+
transform,
|
|
27399
|
+
localOffset + offset(TRANSFORM, "previousWorld"),
|
|
27400
|
+
previousSource,
|
|
27401
|
+
previousOrdinal * 16
|
|
27402
|
+
);
|
|
27403
|
+
}
|
|
27404
|
+
this.pendingTemporalTransforms.add(transformIndex);
|
|
27405
|
+
}
|
|
27406
|
+
if (changed || identityChanged || resetPrevious) writes.transform.push(transformIndex);
|
|
25211
27407
|
}
|
|
25212
27408
|
if (metadataChanged || resetPrevious) writes.instance.push(instanceIndex);
|
|
25213
27409
|
}
|
|
27410
|
+
this.rememberInstanceGenerations(record.slot, currentGenerations, allocation.instanceCount);
|
|
25214
27411
|
this.writeRootTransform(record, resetPrevious, writes);
|
|
25215
27412
|
writes.primitive.push(record.slot);
|
|
25216
27413
|
const draw = new DataView(this.tableBytes.drawTemplate);
|
|
@@ -25270,7 +27467,7 @@ var GpuScene = class _GpuScene {
|
|
|
25270
27467
|
}
|
|
25271
27468
|
}
|
|
25272
27469
|
/** Consume changed instance fields independently of root or material changes. */
|
|
25273
|
-
writeInstanceTransforms(record, writes) {
|
|
27470
|
+
writeInstanceTransforms(record, writes, temporal) {
|
|
25274
27471
|
const allocation = this.allocations.get(record.slot);
|
|
25275
27472
|
const instances = record.snapshot.instances;
|
|
25276
27473
|
if (allocation === void 0 || instances === void 0) {
|
|
@@ -25279,19 +27476,62 @@ var GpuScene = class _GpuScene {
|
|
|
25279
27476
|
if (allocation.instanceTransformCount !== instances.instanceCount) {
|
|
25280
27477
|
throw new RangeError("GPU Scene instance update changed instance count");
|
|
25281
27478
|
}
|
|
27479
|
+
const previousOrdinals = previousInstanceOrdinals(instances, temporal);
|
|
27480
|
+
const previousInstances = temporal?.previousInstances;
|
|
27481
|
+
const currentGenerations = instances.generations;
|
|
27482
|
+
const residentGenerations = this.instanceGenerations.get(record.slot);
|
|
25282
27483
|
for (let ordinal = 0; ordinal < allocation.instanceTransformCount; ordinal += 1) {
|
|
25283
27484
|
const transformIndex = allocation.instanceTransformStart + ordinal;
|
|
25284
27485
|
const transformOffset = transformIndex * TRANSFORM.stride;
|
|
25285
|
-
|
|
27486
|
+
const changed = writeChangedMat4(
|
|
25286
27487
|
this.transformView,
|
|
25287
27488
|
transformOffset + offset(TRANSFORM, "currentWorld"),
|
|
25288
27489
|
instances.transforms,
|
|
25289
27490
|
ordinal * 16
|
|
25290
|
-
)
|
|
27491
|
+
);
|
|
27492
|
+
const identityChanged = instanceIdentityChanged(
|
|
27493
|
+
residentGenerations,
|
|
27494
|
+
currentGenerations,
|
|
27495
|
+
ordinal
|
|
27496
|
+
);
|
|
27497
|
+
if (changed || identityChanged) {
|
|
27498
|
+
const previousOrdinal = previousOrdinals?.get(currentGenerations?.[ordinal] ?? 0);
|
|
27499
|
+
const previousSource = previousOrdinal === void 0 || previousInstances === void 0 ? void 0 : previousInstances.transforms;
|
|
27500
|
+
if (previousSource === void 0 || previousOrdinal === void 0) {
|
|
27501
|
+
writeChangedMat4(
|
|
27502
|
+
this.transformView,
|
|
27503
|
+
transformOffset + offset(TRANSFORM, "previousWorld"),
|
|
27504
|
+
instances.transforms,
|
|
27505
|
+
ordinal * 16
|
|
27506
|
+
);
|
|
27507
|
+
} else {
|
|
27508
|
+
writeChangedMat4(
|
|
27509
|
+
this.transformView,
|
|
27510
|
+
transformOffset + offset(TRANSFORM, "previousWorld"),
|
|
27511
|
+
previousSource,
|
|
27512
|
+
previousOrdinal * 16
|
|
27513
|
+
);
|
|
27514
|
+
}
|
|
25291
27515
|
this.pendingTemporalTransforms.add(transformIndex);
|
|
25292
27516
|
writes.transform.push(transformIndex);
|
|
25293
27517
|
}
|
|
25294
27518
|
}
|
|
27519
|
+
this.rememberInstanceGenerations(
|
|
27520
|
+
record.slot,
|
|
27521
|
+
currentGenerations,
|
|
27522
|
+
allocation.instanceTransformCount
|
|
27523
|
+
);
|
|
27524
|
+
}
|
|
27525
|
+
rememberInstanceGenerations(slot, generations2, instanceCount) {
|
|
27526
|
+
if (generations2 === void 0 || generations2.length !== instanceCount) {
|
|
27527
|
+
this.instanceGenerations.delete(slot);
|
|
27528
|
+
return;
|
|
27529
|
+
}
|
|
27530
|
+
const resident = this.instanceGenerations.get(slot);
|
|
27531
|
+
if (resident !== void 0 && resident.length === generations2.length && resident.every((generation, ordinal) => generation === generations2[ordinal])) {
|
|
27532
|
+
return;
|
|
27533
|
+
}
|
|
27534
|
+
this.instanceGenerations.set(slot, generations2.slice());
|
|
25295
27535
|
}
|
|
25296
27536
|
writeRootTransform(record, resetPrevious, writes) {
|
|
25297
27537
|
const allocation = this.allocations.get(record.slot);
|
|
@@ -25317,6 +27557,7 @@ var GpuScene = class _GpuScene {
|
|
|
25317
27557
|
}
|
|
25318
27558
|
const allocation = this.allocations.get(slot);
|
|
25319
27559
|
if (allocation === void 0) return;
|
|
27560
|
+
this.instanceGenerations.delete(slot);
|
|
25320
27561
|
this.clearAllocation(allocation, writes);
|
|
25321
27562
|
this.allocations.delete(slot);
|
|
25322
27563
|
}
|
|
@@ -25798,6 +28039,25 @@ var VisibilityFacetStore = class {
|
|
|
25798
28039
|
};
|
|
25799
28040
|
|
|
25800
28041
|
// src/scene/render-scene.ts
|
|
28042
|
+
function sameInstanceIdentitySet(current, previous) {
|
|
28043
|
+
if (current === void 0 || previous === void 0) return false;
|
|
28044
|
+
const currentSet = new Set(current);
|
|
28045
|
+
const previousSet = new Set(previous);
|
|
28046
|
+
if (currentSet.size !== current.length || previousSet.size !== previous.length || current.some((identity) => identity === 0) || previous.some((identity) => identity === 0)) {
|
|
28047
|
+
return false;
|
|
28048
|
+
}
|
|
28049
|
+
if (current.length === previous.length) {
|
|
28050
|
+
let sameOrder = true;
|
|
28051
|
+
for (let index = 0; index < current.length; index += 1) {
|
|
28052
|
+
if (current[index] !== previous[index]) {
|
|
28053
|
+
sameOrder = false;
|
|
28054
|
+
break;
|
|
28055
|
+
}
|
|
28056
|
+
}
|
|
28057
|
+
if (sameOrder) return true;
|
|
28058
|
+
}
|
|
28059
|
+
return current.every((identity) => previousSet.has(identity));
|
|
28060
|
+
}
|
|
25801
28061
|
function identityKey(worldId, entityKey) {
|
|
25802
28062
|
return `${worldId}:${entityKey}`;
|
|
25803
28063
|
}
|
|
@@ -25837,7 +28097,8 @@ function ownSnapshot(snapshot) {
|
|
|
25837
28097
|
...snapshot.instances === void 0 ? {} : {
|
|
25838
28098
|
instances: {
|
|
25839
28099
|
...snapshot.instances,
|
|
25840
|
-
transforms: new Float32Array(snapshot.instances.transforms)
|
|
28100
|
+
transforms: new Float32Array(snapshot.instances.transforms),
|
|
28101
|
+
...snapshot.instances.generations === void 0 ? {} : { generations: new Uint32Array(snapshot.instances.generations) }
|
|
25841
28102
|
}
|
|
25842
28103
|
},
|
|
25843
28104
|
...snapshot.spriteInstances === void 0 ? {} : {
|
|
@@ -26002,7 +28263,8 @@ var RenderScene = class {
|
|
|
26002
28263
|
...pending.current,
|
|
26003
28264
|
instances: {
|
|
26004
28265
|
...operation.instances,
|
|
26005
|
-
transforms: new Float32Array(operation.instances.transforms)
|
|
28266
|
+
transforms: new Float32Array(operation.instances.transforms),
|
|
28267
|
+
...operation.instances.generations === void 0 ? {} : { generations: new Uint32Array(operation.instances.generations) }
|
|
26006
28268
|
}
|
|
26007
28269
|
};
|
|
26008
28270
|
pending.instanceChanged = true;
|
|
@@ -26424,13 +28686,23 @@ var RenderScene = class {
|
|
|
26424
28686
|
const seedCurrent = !sameGeneration || !wasVisible || reasons.includes("geometry-revision") || reasons.includes("material-revision");
|
|
26425
28687
|
const previous = seedCurrent || submitted === void 0 ? slot.snapshot : submitted.snapshot;
|
|
26426
28688
|
const previousWorld = this.previousWorldBySlot[slot.slot];
|
|
28689
|
+
const currentInstances = slot.snapshot.instances;
|
|
28690
|
+
const submittedInstances = submitted?.snapshot.instances;
|
|
28691
|
+
const instancesCompatible = currentInstances === void 0 && submittedInstances === void 0 ? true : currentInstances !== void 0 && submittedInstances !== void 0 && (currentInstances.collectionId === void 0 || submittedInstances.collectionId === void 0 || currentInstances.collectionId === submittedInstances.collectionId) && currentInstances.transforms.length >= currentInstances.instanceCount * 16 && submittedInstances.transforms.length >= submittedInstances.instanceCount * 16 && sameInstanceIdentitySet(currentInstances.generations, submittedInstances.generations);
|
|
28692
|
+
const motionValid = !seedCurrent && previousWorld !== void 0 && instancesCompatible;
|
|
26427
28693
|
return {
|
|
26428
28694
|
previousEpoch: seedCurrent ? void 0 : this.submittedEpoch,
|
|
26429
28695
|
previousSource: seedCurrent ? "current-seed" : "last-submitted",
|
|
28696
|
+
motionValid,
|
|
26430
28697
|
reactive: reasons.length > 0,
|
|
26431
28698
|
reactiveReasons: reasons,
|
|
26432
28699
|
previousTransform: seedCurrent || previousWorld === void 0 ? previous.transform : { ...previous.transform, world: previousWorld },
|
|
26433
|
-
|
|
28700
|
+
// A collection identity/count break is a temporal cut for that
|
|
28701
|
+
// renderable. The record stage receives the current snapshot as its
|
|
28702
|
+
// baseline, while stable collections retain the submitted column and
|
|
28703
|
+
// use identity generations to map reordered entries and seed replaced
|
|
28704
|
+
// entries.
|
|
28705
|
+
previousInstances: seedCurrent ? slot.snapshot.instances : previous.instances,
|
|
26434
28706
|
previousSkin: previous.skin,
|
|
26435
28707
|
previousMorphWeights: previous.morph?.weights
|
|
26436
28708
|
};
|
|
@@ -26618,7 +28890,13 @@ function topologyDeltaForAvailable(delta, unavailableSlots, newlyUnavailable) {
|
|
|
26618
28890
|
}
|
|
26619
28891
|
function frameDemandsTemporal(frame) {
|
|
26620
28892
|
const camera = frame.cameras[0];
|
|
26621
|
-
|
|
28893
|
+
const motionBlur = camera?.motionBlur;
|
|
28894
|
+
return camera?.antialias === "taa" || motionBlur !== void 0 && motionBlurTemporalDemand({
|
|
28895
|
+
shutterAngle: motionBlur.shutterAngle,
|
|
28896
|
+
maxRadiusPixels: motionBlur.maxRadiusPixels,
|
|
28897
|
+
sampleCount: motionBlur.sampleCount,
|
|
28898
|
+
targetFps: motionBlur.targetFps ?? 60
|
|
28899
|
+
});
|
|
26622
28900
|
}
|
|
26623
28901
|
function cameraFrusta(cameras) {
|
|
26624
28902
|
const planes = [];
|
|
@@ -27625,12 +29903,16 @@ var PersistentRenderScene = class {
|
|
|
27625
29903
|
/** Publish renderer-owned previous transforms after queue submission. */
|
|
27626
29904
|
commitTemporalFrame() {
|
|
27627
29905
|
const gpuCommit = this.gpuScene?.commitTemporalFrame(this.temporalCapture !== void 0);
|
|
27628
|
-
if (gpuCommit !== void 0 && !gpuCommit.ok)
|
|
29906
|
+
if (gpuCommit !== void 0 && !gpuCommit.ok) {
|
|
29907
|
+
this.temporalCapture = void 0;
|
|
29908
|
+
return gpuCommit;
|
|
29909
|
+
}
|
|
27629
29910
|
const temporalCapture = this.temporalCapture;
|
|
27630
29911
|
if (temporalCapture !== void 0) {
|
|
27631
29912
|
try {
|
|
27632
29913
|
temporalCapture.projection.commitSubmission(temporalCapture.capture);
|
|
27633
29914
|
} catch (cause) {
|
|
29915
|
+
this.temporalCapture = void 0;
|
|
27634
29916
|
return err$1(
|
|
27635
29917
|
new RhiError({
|
|
27636
29918
|
code: "internal-error",
|
|
@@ -27643,6 +29925,10 @@ var PersistentRenderScene = class {
|
|
|
27643
29925
|
}
|
|
27644
29926
|
return ok$1(void 0);
|
|
27645
29927
|
}
|
|
29928
|
+
/** Drop a staged temporal capture when the owning frame did not submit. */
|
|
29929
|
+
discardTemporalFrame() {
|
|
29930
|
+
this.temporalCapture = void 0;
|
|
29931
|
+
}
|
|
27646
29932
|
/** Capture current CPU scene facts; commitTemporalFrame publishes them only after submit. */
|
|
27647
29933
|
prepareTemporalFrame(visible) {
|
|
27648
29934
|
const projection = this.composition?.projection;
|
|
@@ -27934,7 +30220,7 @@ var PersistentRenderScene = class {
|
|
|
27934
30220
|
removedSlots: [],
|
|
27935
30221
|
recreatedSlots: [],
|
|
27936
30222
|
resynced: 0
|
|
27937
|
-
}) : acquired.scene.sync(delta);
|
|
30223
|
+
}) : acquired.scene.sync(delta, (slot) => projection.temporalSnapshotBySlot(slot.slot));
|
|
27938
30224
|
if (!result.ok) {
|
|
27939
30225
|
this.sceneTableUploadBytes = 0;
|
|
27940
30226
|
this.failGpu(result.error);
|
|
@@ -28344,9 +30630,6 @@ function reportPreparedGraphicsCompletionError(internals, error) {
|
|
|
28344
30630
|
})
|
|
28345
30631
|
);
|
|
28346
30632
|
}
|
|
28347
|
-
function isPendingRenderFeaturePreparation(error) {
|
|
28348
|
-
return error.code === "render-feature-preparation-failed" && error.detail.reason.startsWith("rhi-not-available:");
|
|
28349
|
-
}
|
|
28350
30633
|
function makePreparedPipelinePendingError() {
|
|
28351
30634
|
return new RhiError({
|
|
28352
30635
|
code: "rhi-not-available",
|
|
@@ -28471,6 +30754,19 @@ function createRenderSystem(internals) {
|
|
|
28471
30754
|
let preparedFeatureSkylightResources;
|
|
28472
30755
|
const dynamicGeometryFrames = createDynamicGeometryFrameBindings();
|
|
28473
30756
|
let latestCamera;
|
|
30757
|
+
let lastMotionBlurInvalidParams;
|
|
30758
|
+
let motionBlurFeatureInput;
|
|
30759
|
+
const motionBlurFeature = createMotionBlurFeature(() => motionBlurFeatureInput);
|
|
30760
|
+
const ensureMotionBlurFeature = () => {
|
|
30761
|
+
const host = internals.featureHost;
|
|
30762
|
+
if (host === void 0 || host.features.some((feature) => feature.identity === "forgeax.motion-blur")) {
|
|
30763
|
+
return;
|
|
30764
|
+
}
|
|
30765
|
+
const installed = host.install(
|
|
30766
|
+
motionBlurFeature
|
|
30767
|
+
);
|
|
30768
|
+
if (!installed.ok) internals.errorRegistry.fire(installed.error);
|
|
30769
|
+
};
|
|
28474
30770
|
const pointsLinesOwner = new StandardPointsLinesOwner(internals);
|
|
28475
30771
|
const instanceCollections = new InstanceCollectionStore();
|
|
28476
30772
|
const reflectionProbeOwner = new ReflectionProbeRecordOwner(internals);
|
|
@@ -28818,6 +31114,7 @@ function createRenderSystem(internals) {
|
|
|
28818
31114
|
// tiles until the first frame with a castShadow spot (AC-03).
|
|
28819
31115
|
spotShadowSnapshots: []
|
|
28820
31116
|
};
|
|
31117
|
+
const pendingFeatureErrorLastReportedFrame = /* @__PURE__ */ new Map();
|
|
28821
31118
|
const resolveSsrDependencies = (requested) => {
|
|
28822
31119
|
const owner = reflectionProbeOwner.inspect();
|
|
28823
31120
|
const temporal = frameState.lastSuccessfulTemporalView === void 0 ? void 0 : {
|
|
@@ -29162,16 +31459,16 @@ function createRenderSystem(internals) {
|
|
|
29162
31459
|
let lodCandidateCountCache;
|
|
29163
31460
|
let lodInspectionRenderablesCache;
|
|
29164
31461
|
let orderedDispatchCache;
|
|
29165
|
-
const applyLodSelectionTelemetry = (selection) => {
|
|
29166
|
-
if (selection === void 0 ||
|
|
29167
|
-
const inspection =
|
|
31462
|
+
const applyLodSelectionTelemetry = (selection, targetInspection = lastLodOcclusionInspection, targetWorldAttachments = lastLodWorldAttachments) => {
|
|
31463
|
+
if (selection === void 0 || targetInspection === void 0) return;
|
|
31464
|
+
const inspection = targetInspection;
|
|
29168
31465
|
let worlds = inspection.worlds;
|
|
29169
31466
|
const worldSelections = selection.worldSelections;
|
|
29170
31467
|
if (worldSelections !== void 0 && worldSelections.length > 0) {
|
|
29171
31468
|
const selectionsByAttachment = /* @__PURE__ */ new Map();
|
|
29172
31469
|
let attributionValid = true;
|
|
29173
31470
|
for (const selected of worldSelections) {
|
|
29174
|
-
const attachmentId =
|
|
31471
|
+
const attachmentId = targetWorldAttachments.get(selected.worldKey);
|
|
29175
31472
|
if (attachmentId === void 0) {
|
|
29176
31473
|
attributionValid = false;
|
|
29177
31474
|
break;
|
|
@@ -29184,15 +31481,7 @@ function createRenderSystem(internals) {
|
|
|
29184
31481
|
const nextWorlds = worlds.map((world) => {
|
|
29185
31482
|
const selected = selectionsByAttachment.get(world.attachmentId);
|
|
29186
31483
|
const baseRow = world.rows[0];
|
|
29187
|
-
if (selected === void 0 || baseRow === void 0)
|
|
29188
|
-
if (world.rows.length > 0) attributionValid = false;
|
|
29189
|
-
return world;
|
|
29190
|
-
}
|
|
29191
|
-
const candidateCount = selected.reduce((sum, row) => sum + row.candidateCount, 0);
|
|
29192
|
-
if (candidateCount !== baseRow.count.candidates) {
|
|
29193
|
-
attributionValid = false;
|
|
29194
|
-
return world;
|
|
29195
|
-
}
|
|
31484
|
+
if (selected === void 0 || baseRow === void 0) return world;
|
|
29196
31485
|
return Object.freeze({
|
|
29197
31486
|
...world,
|
|
29198
31487
|
attribution: Object.freeze({
|
|
@@ -29222,7 +31511,7 @@ function createRenderSystem(internals) {
|
|
|
29222
31511
|
lastLodOcclusionInspection = inspectLodOcclusion({
|
|
29223
31512
|
...inspection,
|
|
29224
31513
|
count: {
|
|
29225
|
-
...
|
|
31514
|
+
...inspection.count,
|
|
29226
31515
|
candidates: selection.candidateCount,
|
|
29227
31516
|
visible: selection.visible,
|
|
29228
31517
|
occluded: selection.occluded
|
|
@@ -29562,22 +31851,20 @@ function createRenderSystem(internals) {
|
|
|
29562
31851
|
});
|
|
29563
31852
|
},
|
|
29564
31853
|
get motionBlurInspection() {
|
|
29565
|
-
|
|
29566
|
-
|
|
29567
|
-
|
|
29568
|
-
|
|
29569
|
-
|
|
29570
|
-
|
|
29571
|
-
|
|
29572
|
-
|
|
29573
|
-
|
|
29574
|
-
|
|
29575
|
-
|
|
29576
|
-
|
|
29577
|
-
|
|
29578
|
-
|
|
29579
|
-
...unavailable3 ? { lastFailure: "scene-data-unavailable" } : {}
|
|
29580
|
-
};
|
|
31854
|
+
return projectMotionBlurInspection({
|
|
31855
|
+
camera: latestCamera,
|
|
31856
|
+
invalidParams: lastMotionBlurInvalidParams,
|
|
31857
|
+
capabilities: internals.device.caps,
|
|
31858
|
+
acceptedTemporal: frameState.lastSuccessfulTemporalView,
|
|
31859
|
+
transaction: frameState.temporalFrameTransaction.inspect(),
|
|
31860
|
+
execution: frameState.motionBlurExecution,
|
|
31861
|
+
temporalFrameId: frameState.temporalFrame?.frameId,
|
|
31862
|
+
deviceGeneration: internals.deviceScope.generation,
|
|
31863
|
+
graphGeneration: frameState.graphGeneration
|
|
31864
|
+
});
|
|
31865
|
+
},
|
|
31866
|
+
get motionBlurInvalidParams() {
|
|
31867
|
+
return lastMotionBlurInvalidParams;
|
|
29581
31868
|
},
|
|
29582
31869
|
get depthOfFieldInspection() {
|
|
29583
31870
|
const camera = latestCamera;
|
|
@@ -29635,22 +31922,7 @@ function createRenderSystem(internals) {
|
|
|
29635
31922
|
const lifecycle = resources?.inspectBloomResources?.(
|
|
29636
31923
|
frameState.compiledFrameGraph?.inspect()
|
|
29637
31924
|
);
|
|
29638
|
-
return Object.freeze(
|
|
29639
|
-
lifecycle ?? {
|
|
29640
|
-
graphStatus: "empty",
|
|
29641
|
-
enabled: false,
|
|
29642
|
-
targetCount: 0,
|
|
29643
|
-
targetBytes: 0,
|
|
29644
|
-
resourceCount: 0,
|
|
29645
|
-
passCount: 0,
|
|
29646
|
-
encodeCount: 0,
|
|
29647
|
-
bindGroupCount: 0,
|
|
29648
|
-
uploadCount: 0,
|
|
29649
|
-
residentChildBytes: 0,
|
|
29650
|
-
generation: 0,
|
|
29651
|
-
state: "off"
|
|
29652
|
-
}
|
|
29653
|
-
);
|
|
31925
|
+
return Object.freeze(lifecycle ?? emptyBloomInspection());
|
|
29654
31926
|
},
|
|
29655
31927
|
get transmission() {
|
|
29656
31928
|
return lastTransmissionInspection;
|
|
@@ -29714,6 +31986,7 @@ function createRenderSystem(internals) {
|
|
|
29714
31986
|
const profileSession = internals.profiler?.activeSession();
|
|
29715
31987
|
let ownsProfileFrame = false;
|
|
29716
31988
|
let submitted = false;
|
|
31989
|
+
persistentRenderScene.discardTemporalFrame();
|
|
29717
31990
|
dynamicGeometryFrames.begin();
|
|
29718
31991
|
if (profileSession !== void 0 && opts.profileFrame === void 0) {
|
|
29719
31992
|
try {
|
|
@@ -29733,44 +32006,56 @@ function createRenderSystem(internals) {
|
|
|
29733
32006
|
}
|
|
29734
32007
|
dispatchCounts.unlit = 0;
|
|
29735
32008
|
const resourceWorld = compositionWorlds[resourceOwner];
|
|
29736
|
-
|
|
29737
|
-
|
|
29738
|
-
|
|
29739
|
-
|
|
29740
|
-
internals.assets.catalogEpoch,
|
|
29741
|
-
(renderables2) => extractFrames(
|
|
32009
|
+
let frame;
|
|
32010
|
+
try {
|
|
32011
|
+
frame = runProfiledRenderPhase(profileSession, "extract", () => {
|
|
32012
|
+
return persistentRenderScene.extractComposition(
|
|
29742
32013
|
compositionWorlds,
|
|
29743
|
-
{
|
|
29744
|
-
|
|
29745
|
-
|
|
29746
|
-
|
|
29747
|
-
|
|
29748
|
-
|
|
29749
|
-
|
|
29750
|
-
|
|
29751
|
-
{
|
|
29752
|
-
...internals.device.caps.backendKind === "null" ? {} : {
|
|
29753
|
-
materialContext: {
|
|
29754
|
-
backend: internals.device.caps.backendKind === "wgpu-webgl2" ? "webgl2" : internals.device.caps.backendKind,
|
|
29755
|
-
capability: internals.device.caps.storageBuffer ? "storage-buffer" : "uniform-fallback",
|
|
29756
|
-
pipeline: "forward",
|
|
29757
|
-
geometry: "mesh",
|
|
29758
|
-
pass: "forward",
|
|
29759
|
-
profile: "forgeax-material-wgsl-v1",
|
|
29760
|
-
toolchain: "naga-oil",
|
|
29761
|
-
instrumentation: "none"
|
|
29762
|
-
}
|
|
32014
|
+
{ cameraOwner, resourceOwner },
|
|
32015
|
+
internals.assets.catalogEpoch,
|
|
32016
|
+
(renderables2) => extractFrames(
|
|
32017
|
+
compositionWorlds,
|
|
32018
|
+
{
|
|
32019
|
+
cameraOwner,
|
|
32020
|
+
resourceOwner,
|
|
32021
|
+
...cameraEntityKey === void 0 ? {} : { cameraEntityKey }
|
|
29763
32022
|
},
|
|
29764
|
-
|
|
29765
|
-
|
|
29766
|
-
|
|
29767
|
-
|
|
29768
|
-
|
|
29769
|
-
|
|
29770
|
-
|
|
29771
|
-
|
|
29772
|
-
|
|
29773
|
-
|
|
32023
|
+
internals.assets,
|
|
32024
|
+
internals.getPipelineState(),
|
|
32025
|
+
persistentRenderScene.materialSnapshotCacheStore(),
|
|
32026
|
+
{
|
|
32027
|
+
...internals.device.caps.backendKind === "null" ? {} : {
|
|
32028
|
+
materialContext: {
|
|
32029
|
+
backend: internals.device.caps.backendKind === "wgpu-webgl2" ? "webgl2" : internals.device.caps.backendKind,
|
|
32030
|
+
capability: internals.device.caps.storageBuffer ? "storage-buffer" : "uniform-fallback",
|
|
32031
|
+
pipeline: "forward",
|
|
32032
|
+
geometry: "mesh",
|
|
32033
|
+
pass: "forward",
|
|
32034
|
+
profile: "forgeax-material-wgsl-v1",
|
|
32035
|
+
toolchain: "naga-oil",
|
|
32036
|
+
instrumentation: "none"
|
|
32037
|
+
}
|
|
32038
|
+
},
|
|
32039
|
+
cull: "none",
|
|
32040
|
+
retainHidden: true,
|
|
32041
|
+
renderables: renderables2,
|
|
32042
|
+
instanceCollections,
|
|
32043
|
+
...!internals.device.caps.storageBuffer || internals.getMaterialShaderArtifact === void 0 ? {} : { getMaterialShaderArtifact: internals.getMaterialShaderArtifact }
|
|
32044
|
+
}
|
|
32045
|
+
),
|
|
32046
|
+
compositionLeases
|
|
32047
|
+
);
|
|
32048
|
+
});
|
|
32049
|
+
} catch (cause) {
|
|
32050
|
+
if (cause instanceof MotionBlurValidationError) {
|
|
32051
|
+
lastMotionBlurInvalidParams = cause;
|
|
32052
|
+
latestCamera = void 0;
|
|
32053
|
+
internals.errorRegistry.fire(cause);
|
|
32054
|
+
return false;
|
|
32055
|
+
}
|
|
32056
|
+
throw cause;
|
|
32057
|
+
}
|
|
32058
|
+
lastMotionBlurInvalidParams = void 0;
|
|
29774
32059
|
const {
|
|
29775
32060
|
cameras: extractedCameras,
|
|
29776
32061
|
cubeCameras,
|
|
@@ -29976,6 +32261,7 @@ function createRenderSystem(internals) {
|
|
|
29976
32261
|
const submissionRenderables = visibilityProjection.renderables;
|
|
29977
32262
|
const submissionDispatch = visibilityProjection.dispatch;
|
|
29978
32263
|
const inspectionCamera = latestCamera;
|
|
32264
|
+
const lodSubmitFrameId = internals.observationFrameId ?? frameState.frameNumber;
|
|
29979
32265
|
let lodCandidateCountForFrame = 0;
|
|
29980
32266
|
if (inspectionCamera !== void 0) {
|
|
29981
32267
|
const firstRenderable = renderables[0];
|
|
@@ -30007,7 +32293,7 @@ function createRenderSystem(internals) {
|
|
|
30007
32293
|
renderables,
|
|
30008
32294
|
submissionRenderables,
|
|
30009
32295
|
inspectionCamera,
|
|
30010
|
-
|
|
32296
|
+
lodSubmitFrameId,
|
|
30011
32297
|
worldKeys,
|
|
30012
32298
|
slotByEntity,
|
|
30013
32299
|
{
|
|
@@ -30032,7 +32318,7 @@ function createRenderSystem(internals) {
|
|
|
30032
32318
|
lastLodOcclusionInspection = inspectLodOcclusion({
|
|
30033
32319
|
root: {
|
|
30034
32320
|
guid: firstRenderable === void 0 ? "none" : `asset-handle:${firstRenderable.assetHandle}`,
|
|
30035
|
-
sourceKey: `render-frame:${
|
|
32321
|
+
sourceKey: `render-frame:${lodSubmitFrameId}`
|
|
30036
32322
|
},
|
|
30037
32323
|
view: {
|
|
30038
32324
|
attachmentId: compositionWorlds[inspectionCamera.worldId ?? cameraOwner]?.identity ?? "none",
|
|
@@ -30044,7 +32330,7 @@ function createRenderSystem(internals) {
|
|
|
30044
32330
|
primitiveSlot: 0,
|
|
30045
32331
|
slotGeneration: persistentRenderScene.inspect().topology.revision
|
|
30046
32332
|
},
|
|
30047
|
-
generation:
|
|
32333
|
+
generation: lodSubmitFrameId,
|
|
30048
32334
|
count: {
|
|
30049
32335
|
candidates: candidateCount,
|
|
30050
32336
|
visible: inspectionRenderables.length,
|
|
@@ -30061,7 +32347,7 @@ function createRenderSystem(internals) {
|
|
|
30061
32347
|
visible: true
|
|
30062
32348
|
})),
|
|
30063
32349
|
submit: {
|
|
30064
|
-
frameId:
|
|
32350
|
+
frameId: lodSubmitFrameId,
|
|
30065
32351
|
build: resolveInspectionBuild(internals),
|
|
30066
32352
|
deviceGeneration: internals.deviceScope.generation
|
|
30067
32353
|
},
|
|
@@ -30072,7 +32358,12 @@ function createRenderSystem(internals) {
|
|
|
30072
32358
|
lastLodOcclusionInspection = void 0;
|
|
30073
32359
|
lastLodWorldAttachments = /* @__PURE__ */ new Map();
|
|
30074
32360
|
}
|
|
30075
|
-
const temporalDemanded = cameras[0]?.antialias === "taa" ||
|
|
32361
|
+
const temporalDemanded = cameras[0]?.antialias === "taa" || cameras[0]?.motionBlur !== void 0 && motionBlurTemporalDemand({
|
|
32362
|
+
shutterAngle: cameras[0].motionBlur.shutterAngle,
|
|
32363
|
+
maxRadiusPixels: cameras[0].motionBlur.maxRadiusPixels,
|
|
32364
|
+
sampleCount: cameras[0].motionBlur.sampleCount,
|
|
32365
|
+
targetFps: cameras[0].motionBlur.targetFps ?? 60
|
|
32366
|
+
});
|
|
30076
32367
|
if (temporalDemanded) {
|
|
30077
32368
|
persistentRenderScene.prepareTemporalFrame(submissionRenderables);
|
|
30078
32369
|
}
|
|
@@ -30163,6 +32454,38 @@ function createRenderSystem(internals) {
|
|
|
30163
32454
|
storageBuffer: internals.device.caps.storageBuffer,
|
|
30164
32455
|
multisample: internals.device.caps.backendKind !== "wgpu-webgl2"
|
|
30165
32456
|
});
|
|
32457
|
+
const motionBlurParams = cameras[0]?.motionBlur;
|
|
32458
|
+
let motionBlurDelta = 0;
|
|
32459
|
+
try {
|
|
32460
|
+
const delta = compositionWorlds[cameraOwner].getResource(Time).delta;
|
|
32461
|
+
motionBlurDelta = Number.isFinite(delta) ? Math.max(0, delta) : 0;
|
|
32462
|
+
} catch {
|
|
32463
|
+
motionBlurDelta = 0;
|
|
32464
|
+
}
|
|
32465
|
+
const acceptedSampleTime = frameState.lastSuccessfulTemporalView?.sampleTimeSeconds;
|
|
32466
|
+
const cameraViewIdentity = `camera:${cameras[0]?.entityKey ?? 0}`;
|
|
32467
|
+
const canvasWidth = Math.max(1, internals.canvas.width);
|
|
32468
|
+
const canvasHeight = Math.max(1, internals.canvas.height);
|
|
32469
|
+
const motionBlurPlan = planMotionBlurFrame({
|
|
32470
|
+
params: motionBlurParams,
|
|
32471
|
+
sampleTimeSeconds: opts.sampleTimeSeconds,
|
|
32472
|
+
acceptedSampleTimeSeconds: acceptedSampleTime,
|
|
32473
|
+
fallbackDeltaSeconds: motionBlurDelta,
|
|
32474
|
+
acceptedTemporal: frameState.lastSuccessfulTemporalView,
|
|
32475
|
+
temporalReset: opts.temporalReset === true,
|
|
32476
|
+
width: canvasWidth,
|
|
32477
|
+
height: canvasHeight,
|
|
32478
|
+
cameraViewIdentity,
|
|
32479
|
+
historyVersion: cameras[0]?.historyVersion ?? 0,
|
|
32480
|
+
environmentSignature: environment?.environmentSignature ?? "",
|
|
32481
|
+
fogSignature: environment?.fogSignature ?? "",
|
|
32482
|
+
deviceGeneration: internals.deviceScope?.generation ?? 0
|
|
32483
|
+
});
|
|
32484
|
+
const demandedMotionBlur = motionBlurPlan.demanded;
|
|
32485
|
+
motionBlurFeatureInput = motionBlurPlan.featureInput;
|
|
32486
|
+
if (demandedMotionBlur && internals.device.caps.compute === true && internals.device.caps.storageBuffer === true && internals.device.caps.storageTexture === true && internals.device.caps.rgba16floatRenderable === true) {
|
|
32487
|
+
ensureMotionBlurFeature();
|
|
32488
|
+
}
|
|
30166
32489
|
preparedFeatureSkylightResources = preparedPipelineState === null ? void 0 : resolveMaterialSkylight(internals, preparedPipelineState, skylight, skylightCount).skylightResources;
|
|
30167
32490
|
let featureGraphCandidate;
|
|
30168
32491
|
const preparedResourceBatches = runProfiledRenderPhase(profileSession, "features", () => {
|
|
@@ -30171,6 +32494,7 @@ function createRenderSystem(internals) {
|
|
|
30171
32494
|
worlds: compositionWorlds,
|
|
30172
32495
|
owner: resourceOwner,
|
|
30173
32496
|
frameNumber: frameState.frameNumber,
|
|
32497
|
+
frameSize: { width: internals.canvas.width, height: internals.canvas.height },
|
|
30174
32498
|
visibilitySnapshots: frame.featureVisibilitySnapshots,
|
|
30175
32499
|
hiddenEntityReports: frame.hiddenEntityReports,
|
|
30176
32500
|
targets: featureTargets,
|
|
@@ -30199,11 +32523,12 @@ function createRenderSystem(internals) {
|
|
|
30199
32523
|
gpuWork: featureGpuWork
|
|
30200
32524
|
});
|
|
30201
32525
|
lastVisibilityStats.explicitlyHidden = featureFrame.hiddenEntityReports.length;
|
|
30202
|
-
|
|
30203
|
-
|
|
30204
|
-
|
|
30205
|
-
|
|
30206
|
-
|
|
32526
|
+
reportPendingRenderFeatureErrors(
|
|
32527
|
+
featureFrame.errors,
|
|
32528
|
+
frameState.frameNumber,
|
|
32529
|
+
pendingFeatureErrorLastReportedFrame,
|
|
32530
|
+
internals.errorRegistry
|
|
32531
|
+
);
|
|
30207
32532
|
const postProcessParamsCandidate = preparePostProcessParamsResources(
|
|
30208
32533
|
featureFrame.fullscreenEffects
|
|
30209
32534
|
);
|
|
@@ -30287,7 +32612,8 @@ function createRenderSystem(internals) {
|
|
|
30287
32612
|
}
|
|
30288
32613
|
return runProfiledRenderPhase(profileSession, phase, action);
|
|
30289
32614
|
};
|
|
30290
|
-
|
|
32615
|
+
const bloomAdmitted = standardBloomAdmitted(cameras[0]);
|
|
32616
|
+
if (bloomAdmitted) {
|
|
30291
32617
|
internals.getPipelineState()?.perPassResources.ensureBloomResources?.();
|
|
30292
32618
|
}
|
|
30293
32619
|
const liveOutput = cameras[0]?.output;
|
|
@@ -30330,6 +32656,10 @@ function createRenderSystem(internals) {
|
|
|
30330
32656
|
activeEntityKeys: visibilityProjection.activeEntityKeys,
|
|
30331
32657
|
activeEntityRevision: visibilityProjection.activeEntityRevision,
|
|
30332
32658
|
telemetryCandidateCount: lodCandidateCountForFrame,
|
|
32659
|
+
telemetrySubmit: {
|
|
32660
|
+
frameId: lodSubmitFrameId,
|
|
32661
|
+
deviceGeneration: internals.deviceScope.generation
|
|
32662
|
+
},
|
|
30333
32663
|
...occlusionProjection === void 0 ? {} : { occlusion: occlusionProjection }
|
|
30334
32664
|
},
|
|
30335
32665
|
compositionLeases,
|
|
@@ -30355,7 +32685,10 @@ function createRenderSystem(internals) {
|
|
|
30355
32685
|
worldKeys,
|
|
30356
32686
|
void 0,
|
|
30357
32687
|
ssrDependencies,
|
|
30358
|
-
dynamicGeometryFrames.onRenderableDraw(compositionWorlds)
|
|
32688
|
+
dynamicGeometryFrames.onRenderableDraw(compositionWorlds),
|
|
32689
|
+
environment?.fog,
|
|
32690
|
+
opts.sampleTimeSeconds,
|
|
32691
|
+
opts.temporalReset === true
|
|
30359
32692
|
)
|
|
30360
32693
|
);
|
|
30361
32694
|
dynamicGeometryFrames.commit(submitted);
|
|
@@ -30433,7 +32766,7 @@ function createRenderSystem(internals) {
|
|
|
30433
32766
|
frameState.depthOfFieldLastSubmitFailed = true;
|
|
30434
32767
|
}
|
|
30435
32768
|
internals.getPipelineState()?.perPassResources.commitBloomResources?.();
|
|
30436
|
-
const nextBloom =
|
|
32769
|
+
const nextBloom = bloomAdmitted ? "on" : "off";
|
|
30437
32770
|
if (frameState.lastSuccessfulBloom === "on" && nextBloom !== "on") {
|
|
30438
32771
|
internals.getPipelineState()?.perPassResources.retireBloomResources?.(
|
|
30439
32772
|
internals.device.queue.onSubmittedWorkDone()
|
|
@@ -30491,6 +32824,7 @@ function createRenderSystem(internals) {
|
|
|
30491
32824
|
})
|
|
30492
32825
|
);
|
|
30493
32826
|
} finally {
|
|
32827
|
+
if (!submitted) persistentRenderScene.discardTemporalFrame();
|
|
30494
32828
|
internals.recoveryColdWorkGuard?.endFrame();
|
|
30495
32829
|
if (ownsProfileFrame) {
|
|
30496
32830
|
try {
|
|
@@ -30507,13 +32841,26 @@ function createRenderSystem(internals) {
|
|
|
30507
32841
|
const currentFrameId = frameState.frameNumber - 1;
|
|
30508
32842
|
return observeCurrentFrame(options, frameState.currentFrameObservationSource, currentFrameId);
|
|
30509
32843
|
},
|
|
30510
|
-
async observeLodOcclusion() {
|
|
32844
|
+
async observeLodOcclusion(receipt) {
|
|
32845
|
+
const capturedInspection = lastLodOcclusionInspection;
|
|
32846
|
+
const capturedWorldAttachments = lastLodWorldAttachments;
|
|
32847
|
+
if (capturedInspection === void 0) return;
|
|
32848
|
+
if (receipt !== void 0 && (capturedInspection.submit.frameId !== receipt.frameId || capturedInspection.submit.deviceGeneration !== receipt.deviceGeneration))
|
|
32849
|
+
return;
|
|
30511
32850
|
await ensureOcclusionRuntime().waitForCompletions();
|
|
30512
|
-
|
|
30513
|
-
|
|
32851
|
+
if (lastLodOcclusionInspection !== capturedInspection) return;
|
|
32852
|
+
const selection = await gpuDrivenProduction.readLodSelection();
|
|
32853
|
+
if (lastLodOcclusionInspection !== capturedInspection || selection === void 0) return;
|
|
32854
|
+
if (selection.submit !== void 0 && (selection.submit.frameId !== capturedInspection.submit.frameId || selection.submit.deviceGeneration !== capturedInspection.submit.deviceGeneration))
|
|
32855
|
+
return;
|
|
32856
|
+
if (receipt !== void 0 && (selection.submit === void 0 || selection.submit.frameId !== receipt.frameId || selection.submit.deviceGeneration !== receipt.deviceGeneration))
|
|
32857
|
+
return;
|
|
32858
|
+
applyLodSelectionTelemetry(selection, capturedInspection, capturedWorldAttachments);
|
|
32859
|
+
const published = lastLodOcclusionInspection;
|
|
32860
|
+
if (published !== void 0 && published.submit.frameId === capturedInspection.submit.frameId && published.submit.deviceGeneration === capturedInspection.submit.deviceGeneration) {
|
|
30514
32861
|
const queryLatency = ensureOcclusionRuntime().inspectQueryLatency();
|
|
30515
32862
|
lastLodOcclusionInspection = inspectLodOcclusion({
|
|
30516
|
-
...
|
|
32863
|
+
...published,
|
|
30517
32864
|
queryLatencyUs: {
|
|
30518
32865
|
median: queryLatency.median,
|
|
30519
32866
|
p95: queryLatency.p95,
|
|
@@ -31727,6 +34074,16 @@ function tilemapChunkExtractSystem(world, lookup = () => void 0) {
|
|
|
31727
34074
|
}
|
|
31728
34075
|
}
|
|
31729
34076
|
}
|
|
34077
|
+
|
|
34078
|
+
// src/assembly/analytic-fog-registration.ts
|
|
34079
|
+
function registerAnalyticFogPostProcess(renderSystem, source, candidate) {
|
|
34080
|
+
if (candidate) return;
|
|
34081
|
+
renderSystem.registerBuiltinPostProcess("forgeax.analytic-fog", {
|
|
34082
|
+
source,
|
|
34083
|
+
params: { byteSize: 112, defaultValue: new Uint8Array(112) },
|
|
34084
|
+
reads: ["scene-color", { key: "scene-depth", sampleType: "depth" }]
|
|
34085
|
+
});
|
|
34086
|
+
}
|
|
31730
34087
|
function fixedStepOf(world) {
|
|
31731
34088
|
try {
|
|
31732
34089
|
return world.getResource(FixedTime).tick;
|
|
@@ -32433,6 +34790,25 @@ function createRendererDynamicGeometryController(options) {
|
|
|
32433
34790
|
host: createRendererDynamicGeometryHost({ ...options, lifecycle })
|
|
32434
34791
|
};
|
|
32435
34792
|
}
|
|
34793
|
+
|
|
34794
|
+
// src/assembly/fxaa-registration.ts
|
|
34795
|
+
function registerFxaaPostProcess(renderSystem, source, registered) {
|
|
34796
|
+
if (registered) return true;
|
|
34797
|
+
renderSystem.registerBuiltinPostProcess(FXAA_POST_PROCESS_ID, {
|
|
34798
|
+
source,
|
|
34799
|
+
params: { byteSize: 16, defaultValue: new Uint8Array(16) },
|
|
34800
|
+
reads: ["ldrColor"]
|
|
34801
|
+
});
|
|
34802
|
+
return true;
|
|
34803
|
+
}
|
|
34804
|
+
|
|
34805
|
+
// src/assembly/lod-observation.ts
|
|
34806
|
+
function observeLodOcclusionForReceipt(renderSystem, receipt) {
|
|
34807
|
+
return renderSystem.observeLodOcclusion({
|
|
34808
|
+
frameId: receipt.frameId,
|
|
34809
|
+
deviceGeneration: receipt.deviceGeneration
|
|
34810
|
+
});
|
|
34811
|
+
}
|
|
32436
34812
|
function makeShaderDeviceAdapter(rhiDevice, _errorRegistry, asyncCreateShaderModule, immediateCreateShaderModule, mode = "validated") {
|
|
32437
34813
|
const moduleCache = /* @__PURE__ */ new Map();
|
|
32438
34814
|
const errorCache = /* @__PURE__ */ new Map();
|
|
@@ -33076,6 +35452,18 @@ function resolveMaterialPipelineBindGroups(layoutKind, state, clustered = false)
|
|
|
33076
35452
|
function isEngineOwnedMaterialShader(materialShaderId) {
|
|
33077
35453
|
return materialShaderId === "forgeax::default-unlit" || materialShaderId === "forgeax::default-shadow-caster" || materialShaderId === "forgeax::sprite" || materialShaderId === "forgeax::sprite-lit" || materialShaderId === "forgeax::default-sprite" || materialShaderId === "forgeax::msdf-text" || materialShaderId === "forgeax::default-standard-pbr" || materialShaderId === "forgeax::pbr-skin" || materialShaderId === "forgeax::default-standard-pbr-skin";
|
|
33078
35454
|
}
|
|
35455
|
+
|
|
35456
|
+
// src/assembly/motion-blur-registration.ts
|
|
35457
|
+
function registerMotionBlurPostProcess(renderSystem, source) {
|
|
35458
|
+
renderSystem.registerBuiltinPostProcess("forgeax.motion-blur", {
|
|
35459
|
+
source,
|
|
35460
|
+
params: {
|
|
35461
|
+
byteSize: MOTION_BLUR_PARAMS_BYTE_SIZE,
|
|
35462
|
+
defaultValue: new Uint8Array(MOTION_BLUR_PARAMS_BYTE_SIZE)
|
|
35463
|
+
},
|
|
35464
|
+
reads: ["scene-color", "scene-temporal"]
|
|
35465
|
+
});
|
|
35466
|
+
}
|
|
33079
35467
|
var ROOT_ORDER = /* @__PURE__ */ new Map([
|
|
33080
35468
|
["listener", 0],
|
|
33081
35469
|
["surface", 1],
|
|
@@ -36384,6 +38772,27 @@ function createMeshSsboGrowController(init) {
|
|
|
36384
38772
|
};
|
|
36385
38773
|
return { state, initialBuild, growMeshSsbo };
|
|
36386
38774
|
}
|
|
38775
|
+
|
|
38776
|
+
// src/assembly/temporal-post-process-prewarm.ts
|
|
38777
|
+
function createTemporalShaderPrewarmer(input) {
|
|
38778
|
+
return async (entry, id) => {
|
|
38779
|
+
if (entry === void 0) return;
|
|
38780
|
+
const prewarm = await runShimStep(
|
|
38781
|
+
() => input.asyncCreateShaderModule ? input.asyncCreateShaderModule(input.device, {
|
|
38782
|
+
code: entry.wgsl,
|
|
38783
|
+
label: postProcessShaderModuleLabel(id, entry.wgsl)
|
|
38784
|
+
}) : invokeDeviceCreateShaderModule(input.device, {
|
|
38785
|
+
code: entry.wgsl,
|
|
38786
|
+
label: postProcessShaderModuleLabel(id, entry.wgsl)
|
|
38787
|
+
}),
|
|
38788
|
+
"shader-compile-failed",
|
|
38789
|
+
`${id} shader module compiled (unified post-process prewarm)`,
|
|
38790
|
+
`inspect manifest ${id} entry composed wgsl; check device.features`
|
|
38791
|
+
);
|
|
38792
|
+
if (!prewarm.ok) throw prewarm.error;
|
|
38793
|
+
input.seedShaderModule(postProcessShaderModuleLabel(id, entry.wgsl), prewarm.value);
|
|
38794
|
+
};
|
|
38795
|
+
}
|
|
36387
38796
|
function createShader(input, code, label) {
|
|
36388
38797
|
return input.asyncCreateShaderModule ? input.asyncCreateShaderModule(input.device, { code, label }) : Promise.resolve(invokeDeviceCreateShaderModule(input.device, { code, label }));
|
|
36389
38798
|
}
|
|
@@ -36516,19 +38925,6 @@ function buildGpuDrivenPbrReadyLayouts(device, storageBufferCapable, pbrLayouts)
|
|
|
36516
38925
|
};
|
|
36517
38926
|
}
|
|
36518
38927
|
|
|
36519
|
-
// src/assembly/webgpu-ready-contract.ts
|
|
36520
|
-
var VIEW_UBO_BYTES = VIEW_UNIFORM_BUFFER_SIZE;
|
|
36521
|
-
var MIPMAP_PREWARM_FORMATS = [
|
|
36522
|
-
"rgba8unorm-srgb",
|
|
36523
|
-
"rgba8unorm",
|
|
36524
|
-
"rgba16float"
|
|
36525
|
-
];
|
|
36526
|
-
var BRIGHT_PARAMS_BYTES = 16;
|
|
36527
|
-
var BLUR_PARAMS_BYTES = 16;
|
|
36528
|
-
var COMPOSITE_PARAMS_BYTES = 16;
|
|
36529
|
-
var HDR_COLOR_ATTACHMENT_FORMAT = "rgba16float";
|
|
36530
|
-
var DEPTH_TEXTURE_FORMAT = "depth24plus-stencil8";
|
|
36531
|
-
|
|
36532
38928
|
// src/assembly/webgpu-ready-mipmap.ts
|
|
36533
38929
|
function adaptMipmapShaderModuleFactory(factory) {
|
|
36534
38930
|
if (factory === void 0) return void 0;
|
|
@@ -36541,7 +38937,7 @@ async function prewarmMipmapPipeline(rhiDevice, gpuStore, hasMaterialManifest) {
|
|
|
36541
38937
|
}
|
|
36542
38938
|
|
|
36543
38939
|
// src/assembly/webgpu-ready.ts
|
|
36544
|
-
async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, asyncCreateShaderModule, errorRegistry, requiredMaterialShaders, standardPbrShaderUvSetCount, requiredFullscreenPostProcesses, setGrowMeshSsboHook, seedShaderModule, seedMaterialShaderPipelineCache, registerBuiltinTonemap, registerBuiltinFxaa, registerBuiltinTemporalPostProcesses, registerBuiltinDepthOfField, setVolumetricFogShaderSources, setSsrShaderSources, setAtmosphereShaderSources) {
|
|
38940
|
+
async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, asyncCreateShaderModule, errorRegistry, requiredMaterialShaders, standardPbrShaderUvSetCount, requiredFullscreenPostProcesses, setGrowMeshSsboHook, seedShaderModule, seedMaterialShaderPipelineCache, registerBuiltinTonemap, registerBuiltinFxaa, registerBuiltinAnalyticFog, registerBuiltinTemporalPostProcesses, registerBuiltinDepthOfField, setVolumetricFogShaderSources, setSsrShaderSources, setAtmosphereShaderSources) {
|
|
36545
38941
|
const extendedLightingShaderAvailable = deriveExtendedLightingCapability(rhiDevice).admitted;
|
|
36546
38942
|
const capValue = rhiDevice.limits.maxStorageBuffersPerShaderStage;
|
|
36547
38943
|
const webgl2Downlevel = rhiDevice.caps.backendKind === "wgpu-webgl2";
|
|
@@ -36576,8 +38972,8 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
36576
38972
|
let spriteLitModule = null;
|
|
36577
38973
|
let fxaaModule = null;
|
|
36578
38974
|
let skyboxModule = null;
|
|
36579
|
-
let
|
|
36580
|
-
let
|
|
38975
|
+
let bloomDownsampleModule = null;
|
|
38976
|
+
let bloomUpsampleModule = null;
|
|
36581
38977
|
let bloomCompositeModule = null;
|
|
36582
38978
|
let ssaoModule = null;
|
|
36583
38979
|
const findEngineManifestEntry = (identifier) => {
|
|
@@ -36608,8 +39004,8 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
36608
39004
|
let probeBackgroundEntry;
|
|
36609
39005
|
let fxaaEntry;
|
|
36610
39006
|
let skyboxEntry;
|
|
36611
|
-
let
|
|
36612
|
-
let
|
|
39007
|
+
let bloomDownsampleEntry;
|
|
39008
|
+
let bloomUpsampleEntry;
|
|
36613
39009
|
let bloomCompositeEntry;
|
|
36614
39010
|
let ssaoEntry;
|
|
36615
39011
|
let shadowCasterEntry = findEngineManifestEntry("forgeax::default-shadow-caster");
|
|
@@ -36666,12 +39062,12 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
36666
39062
|
skyboxEntry ??= entry;
|
|
36667
39063
|
continue;
|
|
36668
39064
|
}
|
|
36669
|
-
if (entry.wgsl.includes("
|
|
36670
|
-
|
|
39065
|
+
if (entry.wgsl.includes("BloomDownsampleParams")) {
|
|
39066
|
+
bloomDownsampleEntry ??= entry;
|
|
36671
39067
|
continue;
|
|
36672
39068
|
}
|
|
36673
|
-
if (entry.wgsl.includes("
|
|
36674
|
-
|
|
39069
|
+
if (entry.wgsl.includes("BloomUpsampleParams")) {
|
|
39070
|
+
bloomUpsampleEntry ??= entry;
|
|
36675
39071
|
continue;
|
|
36676
39072
|
}
|
|
36677
39073
|
if (entry.wgsl.includes("BloomCompositeParams")) {
|
|
@@ -37127,23 +39523,14 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
37127
39523
|
tonemapPrewarm.value
|
|
37128
39524
|
);
|
|
37129
39525
|
registerBuiltinTonemap(tonemapEntry.wgsl);
|
|
37130
|
-
const prewarmTemporal =
|
|
37131
|
-
|
|
37132
|
-
|
|
37133
|
-
|
|
37134
|
-
|
|
37135
|
-
|
|
37136
|
-
|
|
37137
|
-
|
|
37138
|
-
label: postProcessShaderModuleLabel(id, entry.wgsl)
|
|
37139
|
-
}),
|
|
37140
|
-
"shader-compile-failed",
|
|
37141
|
-
`${id} shader module compiled (unified post-process prewarm)`,
|
|
37142
|
-
`inspect manifest ${id} entry composed wgsl; check device.features`
|
|
37143
|
-
);
|
|
37144
|
-
if (!prewarm.ok) throw prewarm.error;
|
|
37145
|
-
seedShaderModule(postProcessShaderModuleLabel(id, entry.wgsl), prewarm.value);
|
|
37146
|
-
};
|
|
39526
|
+
const prewarmTemporal = createTemporalShaderPrewarmer({
|
|
39527
|
+
device: rhiDevice,
|
|
39528
|
+
asyncCreateShaderModule,
|
|
39529
|
+
seedShaderModule
|
|
39530
|
+
});
|
|
39531
|
+
const analyticFogEntry = findEngineManifestEntry("forgeax::analytic-fog");
|
|
39532
|
+
await prewarmTemporal(analyticFogEntry, "forgeax.analytic-fog");
|
|
39533
|
+
if (analyticFogEntry !== void 0) registerBuiltinAnalyticFog(analyticFogEntry.wgsl);
|
|
37147
39534
|
await prewarmTemporal(motionBlurEntry, "forgeax.motion-blur");
|
|
37148
39535
|
await prewarmTemporal(taaResolveEntry, "forgeax.taa-resolve");
|
|
37149
39536
|
await prewarmDepthOfField(manifestEntries, prewarmTemporal, registerBuiltinDepthOfField);
|
|
@@ -37264,34 +39651,37 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
37264
39651
|
if (!skyboxShaderResult.ok) throw skyboxShaderResult.error;
|
|
37265
39652
|
skyboxModule = skyboxShaderResult.value;
|
|
37266
39653
|
}
|
|
37267
|
-
if (
|
|
39654
|
+
if (bloomDownsampleEntry !== void 0) {
|
|
37268
39655
|
const result = await runShimStep(
|
|
37269
39656
|
() => asyncCreateShaderModule ? asyncCreateShaderModule(rhiDevice, {
|
|
37270
|
-
code:
|
|
37271
|
-
label: "bloom-
|
|
39657
|
+
code: bloomDownsampleEntry.wgsl,
|
|
39658
|
+
label: "bloom-downsample"
|
|
37272
39659
|
}) : invokeDeviceCreateShaderModule(rhiDevice, {
|
|
37273
|
-
code:
|
|
37274
|
-
label: "bloom-
|
|
39660
|
+
code: bloomDownsampleEntry.wgsl,
|
|
39661
|
+
label: "bloom-downsample"
|
|
37275
39662
|
}),
|
|
37276
39663
|
"shader-compile-failed",
|
|
37277
|
-
"bloom-
|
|
37278
|
-
"inspect manifest bloom-
|
|
39664
|
+
"bloom-downsample shader module compiled",
|
|
39665
|
+
"inspect manifest bloom-downsample entry composed wgsl; check device.features"
|
|
37279
39666
|
);
|
|
37280
39667
|
if (!result.ok) throw result.error;
|
|
37281
|
-
|
|
39668
|
+
bloomDownsampleModule = result.value;
|
|
37282
39669
|
}
|
|
37283
|
-
if (
|
|
39670
|
+
if (bloomUpsampleEntry !== void 0) {
|
|
37284
39671
|
const result = await runShimStep(
|
|
37285
|
-
() => asyncCreateShaderModule ? asyncCreateShaderModule(rhiDevice, {
|
|
37286
|
-
code:
|
|
37287
|
-
label: "bloom-
|
|
39672
|
+
() => asyncCreateShaderModule ? asyncCreateShaderModule(rhiDevice, {
|
|
39673
|
+
code: bloomUpsampleEntry.wgsl,
|
|
39674
|
+
label: "bloom-upsample"
|
|
39675
|
+
}) : invokeDeviceCreateShaderModule(rhiDevice, {
|
|
39676
|
+
code: bloomUpsampleEntry.wgsl,
|
|
39677
|
+
label: "bloom-upsample"
|
|
37288
39678
|
}),
|
|
37289
39679
|
"shader-compile-failed",
|
|
37290
|
-
"bloom-
|
|
37291
|
-
"inspect manifest bloom-
|
|
39680
|
+
"bloom-upsample shader module compiled",
|
|
39681
|
+
"inspect manifest bloom-upsample entry composed wgsl; check device.features"
|
|
37292
39682
|
);
|
|
37293
39683
|
if (!result.ok) throw result.error;
|
|
37294
|
-
|
|
39684
|
+
bloomUpsampleModule = result.value;
|
|
37295
39685
|
}
|
|
37296
39686
|
if (bloomCompositeEntry !== void 0) {
|
|
37297
39687
|
const result = await runShimStep(
|
|
@@ -37979,10 +40369,10 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
37979
40369
|
} else if (fragModule === skyboxModule) {
|
|
37980
40370
|
const msaa = d.multisample;
|
|
37981
40371
|
label = msaa !== void 0 ? "skybox-pipeline-msaa" : "skybox-pipeline";
|
|
37982
|
-
} else if (fragModule ===
|
|
37983
|
-
label = "bloom-
|
|
37984
|
-
} else if (fragModule ===
|
|
37985
|
-
label =
|
|
40372
|
+
} else if (fragModule === bloomDownsampleModule) {
|
|
40373
|
+
label = "bloom-downsample-pipeline";
|
|
40374
|
+
} else if (fragModule === bloomUpsampleModule) {
|
|
40375
|
+
label = "bloom-upsample-pipeline";
|
|
37986
40376
|
} else if (fragModule === bloomCompositeModule) {
|
|
37987
40377
|
label = "bloom-composite-pipeline";
|
|
37988
40378
|
} else if (fragModule === ssaoModule) {
|
|
@@ -38065,17 +40455,15 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38065
40455
|
let skyboxSamplerHandle = null;
|
|
38066
40456
|
let skyboxRotationBufferHandle = null;
|
|
38067
40457
|
let skyboxPipelineMsaaHandle = null;
|
|
38068
|
-
let
|
|
38069
|
-
let
|
|
38070
|
-
let bloomBlurVPipelineHandle = null;
|
|
40458
|
+
let bloomDownsamplePipelineHandle = null;
|
|
40459
|
+
let bloomUpsamplePipelineHandle = null;
|
|
38071
40460
|
let bloomCompositePipelineHandle = null;
|
|
38072
|
-
let
|
|
38073
|
-
let
|
|
40461
|
+
let bloomDownsampleBglHandle = null;
|
|
40462
|
+
let bloomUpsampleBglHandle = null;
|
|
38074
40463
|
let bloomCompositeBglHandle = null;
|
|
38075
40464
|
let bloomSamplerHandle = null;
|
|
38076
|
-
let
|
|
38077
|
-
let
|
|
38078
|
-
let bloomBlurVParamsBufferHandle = null;
|
|
40465
|
+
let bloomDownsampleParamsBufferHandle = null;
|
|
40466
|
+
let bloomUpsampleParamsBufferHandle = null;
|
|
38079
40467
|
let bloomCompositeParamsBufferHandle = null;
|
|
38080
40468
|
let bloomResourcesReady = false;
|
|
38081
40469
|
let ensureBloomResources;
|
|
@@ -38106,20 +40494,7 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38106
40494
|
drainBloomResources?.();
|
|
38107
40495
|
};
|
|
38108
40496
|
const inspectBloomResourcesStable = (graph) => {
|
|
38109
|
-
return inspectBloomResources?.(graph) ??
|
|
38110
|
-
graphStatus: "empty",
|
|
38111
|
-
enabled: false,
|
|
38112
|
-
targetCount: 0,
|
|
38113
|
-
targetBytes: 0,
|
|
38114
|
-
resourceCount: 0,
|
|
38115
|
-
passCount: 0,
|
|
38116
|
-
encodeCount: 0,
|
|
38117
|
-
bindGroupCount: 0,
|
|
38118
|
-
uploadCount: 0,
|
|
38119
|
-
residentChildBytes: 0,
|
|
38120
|
-
generation: 0,
|
|
38121
|
-
state: "off"
|
|
38122
|
-
};
|
|
40497
|
+
return inspectBloomResources?.(graph) ?? emptyBloomInspection();
|
|
38123
40498
|
};
|
|
38124
40499
|
let ssaoCalcPipelineHandle = null;
|
|
38125
40500
|
let ssaoBlurPipelineHandle = null;
|
|
@@ -38356,7 +40731,7 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38356
40731
|
return value;
|
|
38357
40732
|
};
|
|
38358
40733
|
try {
|
|
38359
|
-
if (
|
|
40734
|
+
if (bloomDownsampleModule !== null || bloomUpsampleModule !== null || bloomCompositeModule !== null) {
|
|
38360
40735
|
const bloomSamplerResult = runShimSyncStep(
|
|
38361
40736
|
() => rhiDevice.createSampler({
|
|
38362
40737
|
label: "bloom-sampler",
|
|
@@ -38373,10 +40748,10 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38373
40748
|
if (!bloomSamplerResult.ok) throw bloomSamplerResult.error;
|
|
38374
40749
|
bloomSamplerHandle = adopt("binding", bloomSamplerResult.value, () => void 0);
|
|
38375
40750
|
}
|
|
38376
|
-
if (
|
|
38377
|
-
const
|
|
40751
|
+
if (bloomDownsampleModule !== null && bloomSamplerHandle !== null) {
|
|
40752
|
+
const downsampleBglResult = runShimSyncStep(
|
|
38378
40753
|
() => rhiDevice.createBindGroupLayout({
|
|
38379
|
-
label: "bloom-
|
|
40754
|
+
label: "bloom-downsample-bgl",
|
|
38380
40755
|
entries: [
|
|
38381
40756
|
{
|
|
38382
40757
|
binding: 0,
|
|
@@ -38396,26 +40771,26 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38396
40771
|
]
|
|
38397
40772
|
}),
|
|
38398
40773
|
"webgpu-runtime-error",
|
|
38399
|
-
"createBindGroupLayout(bloom-
|
|
40774
|
+
"createBindGroupLayout(bloom-downsample) succeeded",
|
|
38400
40775
|
"check device.limits.maxBindGroupsPerPipelineLayout"
|
|
38401
40776
|
);
|
|
38402
|
-
if (!
|
|
38403
|
-
|
|
38404
|
-
const
|
|
40777
|
+
if (!downsampleBglResult.ok) throw downsampleBglResult.error;
|
|
40778
|
+
bloomDownsampleBglHandle = adopt("binding", downsampleBglResult.value, () => void 0);
|
|
40779
|
+
const downsamplePlResult = runShimSyncStep(
|
|
38405
40780
|
() => rhiDevice.createPipelineLayout({
|
|
38406
|
-
label: "bloom-
|
|
38407
|
-
bindGroupLayouts: [
|
|
40781
|
+
label: "bloom-downsample-pl",
|
|
40782
|
+
bindGroupLayouts: [downsampleBglResult.value]
|
|
38408
40783
|
}),
|
|
38409
40784
|
"webgpu-runtime-error",
|
|
38410
|
-
"createPipelineLayout(bloom-
|
|
38411
|
-
"verify bloom-
|
|
40785
|
+
"createPipelineLayout(bloom-downsample) succeeded",
|
|
40786
|
+
"verify bloom-downsample BindGroupLayout matches shader @group(0) bindings"
|
|
38412
40787
|
);
|
|
38413
|
-
if (!
|
|
38414
|
-
adopt("binding",
|
|
40788
|
+
if (!downsamplePlResult.ok) throw downsamplePlResult.error;
|
|
40789
|
+
adopt("binding", downsamplePlResult.value, () => void 0);
|
|
38415
40790
|
{
|
|
38416
|
-
const
|
|
40791
|
+
const downsampleSpec = {
|
|
38417
40792
|
shader: {
|
|
38418
|
-
id: "forgeax::post::bloom-
|
|
40793
|
+
id: "forgeax::post::bloom-downsample",
|
|
38419
40794
|
passKind: "post-process",
|
|
38420
40795
|
variantSet: void 0
|
|
38421
40796
|
},
|
|
@@ -38432,20 +40807,21 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38432
40807
|
renderState: { cullMode: "none" }
|
|
38433
40808
|
};
|
|
38434
40809
|
const modules = {
|
|
38435
|
-
vertex:
|
|
38436
|
-
fragment:
|
|
38437
|
-
layout:
|
|
40810
|
+
vertex: bloomDownsampleModule,
|
|
40811
|
+
fragment: bloomDownsampleModule,
|
|
40812
|
+
layout: downsamplePlResult.value,
|
|
40813
|
+
label: "bloom-downsample-pipeline"
|
|
38438
40814
|
};
|
|
38439
40815
|
try {
|
|
38440
|
-
|
|
38441
|
-
|
|
40816
|
+
bloomDownsamplePipelineHandle = getOrBuildPipeline(
|
|
40817
|
+
downsampleSpec,
|
|
38442
40818
|
pipelineDeviceProvider,
|
|
38443
40819
|
pipelineCache,
|
|
38444
40820
|
modules
|
|
38445
40821
|
);
|
|
38446
|
-
|
|
40822
|
+
bloomDownsamplePipelineHandle = adopt(
|
|
38447
40823
|
"pipeline",
|
|
38448
|
-
|
|
40824
|
+
bloomDownsamplePipelineHandle,
|
|
38449
40825
|
() => void 0
|
|
38450
40826
|
);
|
|
38451
40827
|
} catch (err54) {
|
|
@@ -38453,30 +40829,34 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38453
40829
|
throw new PipelineSpecError({
|
|
38454
40830
|
code: "pipeline-build-failed",
|
|
38455
40831
|
detail: { cause: err54 },
|
|
38456
|
-
hint: "createRenderPipeline (bloom-
|
|
40832
|
+
hint: "createRenderPipeline (bloom-downsample fullscreen) failed; inspect gpuMessage"
|
|
38457
40833
|
});
|
|
38458
40834
|
}
|
|
38459
40835
|
}
|
|
38460
|
-
const
|
|
40836
|
+
const downsampleParamsResult = runShimSyncStep(
|
|
38461
40837
|
() => rhiDevice.createBuffer({
|
|
38462
|
-
label: "bloom-
|
|
38463
|
-
size:
|
|
40838
|
+
label: "bloom-downsample-params-ubo",
|
|
40839
|
+
size: BLOOM_DOWNSAMPLE_PARAMS_BYTES,
|
|
38464
40840
|
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
38465
40841
|
mappedAtCreation: false
|
|
38466
40842
|
}),
|
|
38467
40843
|
"webgpu-runtime-error",
|
|
38468
|
-
"createBuffer (bloom-
|
|
40844
|
+
"createBuffer (bloom-downsample params UBO) succeeded",
|
|
38469
40845
|
"check device.limits.maxUniformBufferBindingSize"
|
|
38470
40846
|
);
|
|
38471
|
-
if (!
|
|
38472
|
-
|
|
38473
|
-
|
|
38474
|
-
|
|
40847
|
+
if (!downsampleParamsResult.ok) throw downsampleParamsResult.error;
|
|
40848
|
+
bloomDownsampleParamsBufferHandle = adopt(
|
|
40849
|
+
"buffer",
|
|
40850
|
+
downsampleParamsResult.value,
|
|
40851
|
+
(value) => {
|
|
40852
|
+
rhiDevice.destroyBuffer(value);
|
|
40853
|
+
}
|
|
40854
|
+
);
|
|
38475
40855
|
}
|
|
38476
|
-
if (
|
|
38477
|
-
const
|
|
40856
|
+
if (bloomUpsampleModule !== null && bloomSamplerHandle !== null) {
|
|
40857
|
+
const upsampleBglResult = runShimSyncStep(
|
|
38478
40858
|
() => rhiDevice.createBindGroupLayout({
|
|
38479
|
-
label: "bloom-
|
|
40859
|
+
label: "bloom-upsample-bgl",
|
|
38480
40860
|
entries: [
|
|
38481
40861
|
{
|
|
38482
40862
|
binding: 0,
|
|
@@ -38486,36 +40866,41 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38486
40866
|
{
|
|
38487
40867
|
binding: 1,
|
|
38488
40868
|
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
38489
|
-
|
|
40869
|
+
texture: { sampleType: "float", viewDimension: "2d" }
|
|
38490
40870
|
},
|
|
38491
40871
|
{
|
|
38492
40872
|
binding: 2,
|
|
38493
40873
|
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
40874
|
+
sampler: { type: "filtering" }
|
|
40875
|
+
},
|
|
40876
|
+
{
|
|
40877
|
+
binding: 3,
|
|
40878
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
38494
40879
|
buffer: { type: "uniform" }
|
|
38495
40880
|
}
|
|
38496
40881
|
]
|
|
38497
40882
|
}),
|
|
38498
40883
|
"webgpu-runtime-error",
|
|
38499
|
-
"createBindGroupLayout(bloom-
|
|
40884
|
+
"createBindGroupLayout(bloom-upsample) succeeded",
|
|
38500
40885
|
"check device.limits.maxBindGroupsPerPipelineLayout"
|
|
38501
40886
|
);
|
|
38502
|
-
if (!
|
|
38503
|
-
|
|
38504
|
-
const
|
|
40887
|
+
if (!upsampleBglResult.ok) throw upsampleBglResult.error;
|
|
40888
|
+
bloomUpsampleBglHandle = adopt("binding", upsampleBglResult.value, () => void 0);
|
|
40889
|
+
const upsamplePlResult = runShimSyncStep(
|
|
38505
40890
|
() => rhiDevice.createPipelineLayout({
|
|
38506
|
-
label: "bloom-
|
|
38507
|
-
bindGroupLayouts: [
|
|
40891
|
+
label: "bloom-upsample-pl",
|
|
40892
|
+
bindGroupLayouts: [upsampleBglResult.value]
|
|
38508
40893
|
}),
|
|
38509
40894
|
"webgpu-runtime-error",
|
|
38510
|
-
"createPipelineLayout(bloom-
|
|
38511
|
-
"verify bloom-
|
|
40895
|
+
"createPipelineLayout(bloom-upsample) succeeded",
|
|
40896
|
+
"verify bloom-upsample BindGroupLayout matches shader @group(0) bindings"
|
|
38512
40897
|
);
|
|
38513
|
-
if (!
|
|
38514
|
-
adopt("binding",
|
|
40898
|
+
if (!upsamplePlResult.ok) throw upsamplePlResult.error;
|
|
40899
|
+
adopt("binding", upsamplePlResult.value, () => void 0);
|
|
38515
40900
|
{
|
|
38516
|
-
const
|
|
40901
|
+
const upsampleSpec = {
|
|
38517
40902
|
shader: {
|
|
38518
|
-
id: "forgeax::post::bloom-
|
|
40903
|
+
id: "forgeax::post::bloom-upsample",
|
|
38519
40904
|
passKind: "post-process",
|
|
38520
40905
|
variantSet: void 0
|
|
38521
40906
|
},
|
|
@@ -38532,21 +40917,21 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38532
40917
|
renderState: { cullMode: "none" }
|
|
38533
40918
|
};
|
|
38534
40919
|
const modules = {
|
|
38535
|
-
vertex:
|
|
38536
|
-
fragment:
|
|
38537
|
-
layout:
|
|
38538
|
-
label: "bloom-
|
|
40920
|
+
vertex: bloomUpsampleModule,
|
|
40921
|
+
fragment: bloomUpsampleModule,
|
|
40922
|
+
layout: upsamplePlResult.value,
|
|
40923
|
+
label: "bloom-upsample-pipeline"
|
|
38539
40924
|
};
|
|
38540
40925
|
try {
|
|
38541
|
-
|
|
38542
|
-
|
|
40926
|
+
bloomUpsamplePipelineHandle = getOrBuildPipeline(
|
|
40927
|
+
upsampleSpec,
|
|
38543
40928
|
pipelineDeviceProvider,
|
|
38544
40929
|
pipelineCache,
|
|
38545
40930
|
modules
|
|
38546
40931
|
);
|
|
38547
|
-
|
|
40932
|
+
bloomUpsamplePipelineHandle = adopt(
|
|
38548
40933
|
"pipeline",
|
|
38549
|
-
|
|
40934
|
+
bloomUpsamplePipelineHandle,
|
|
38550
40935
|
() => void 0
|
|
38551
40936
|
);
|
|
38552
40937
|
} catch (err54) {
|
|
@@ -38554,39 +40939,23 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38554
40939
|
throw new PipelineSpecError({
|
|
38555
40940
|
code: "pipeline-build-failed",
|
|
38556
40941
|
detail: { cause: err54 },
|
|
38557
|
-
hint: "createRenderPipeline (bloom-
|
|
40942
|
+
hint: "createRenderPipeline (bloom-upsample fullscreen) failed; inspect gpuMessage"
|
|
38558
40943
|
});
|
|
38559
40944
|
}
|
|
38560
|
-
bloomBlurVPipelineHandle = bloomBlurHPipelineHandle;
|
|
38561
40945
|
}
|
|
38562
|
-
const
|
|
38563
|
-
() => rhiDevice.createBuffer({
|
|
38564
|
-
label: "bloom-blur-h-params-ubo",
|
|
38565
|
-
size: BLUR_PARAMS_BYTES,
|
|
38566
|
-
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
38567
|
-
mappedAtCreation: false
|
|
38568
|
-
}),
|
|
38569
|
-
"webgpu-runtime-error",
|
|
38570
|
-
"createBuffer (bloom-blur-h params UBO) succeeded",
|
|
38571
|
-
"check device.limits.maxUniformBufferBindingSize"
|
|
38572
|
-
);
|
|
38573
|
-
if (!blurHParamsResult.ok) throw blurHParamsResult.error;
|
|
38574
|
-
bloomBlurHParamsBufferHandle = adopt("buffer", blurHParamsResult.value, (value) => {
|
|
38575
|
-
rhiDevice.destroyBuffer(value);
|
|
38576
|
-
});
|
|
38577
|
-
const blurVParamsResult = runShimSyncStep(
|
|
40946
|
+
const upsampleParamsResult = runShimSyncStep(
|
|
38578
40947
|
() => rhiDevice.createBuffer({
|
|
38579
|
-
label: "bloom-
|
|
38580
|
-
size:
|
|
40948
|
+
label: "bloom-upsample-params-ubo",
|
|
40949
|
+
size: BLOOM_UPSAMPLE_PARAMS_BYTES,
|
|
38581
40950
|
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
38582
40951
|
mappedAtCreation: false
|
|
38583
40952
|
}),
|
|
38584
40953
|
"webgpu-runtime-error",
|
|
38585
|
-
"createBuffer (bloom-
|
|
40954
|
+
"createBuffer (bloom-upsample params UBO) succeeded",
|
|
38586
40955
|
"check device.limits.maxUniformBufferBindingSize"
|
|
38587
40956
|
);
|
|
38588
|
-
if (!
|
|
38589
|
-
|
|
40957
|
+
if (!upsampleParamsResult.ok) throw upsampleParamsResult.error;
|
|
40958
|
+
bloomUpsampleParamsBufferHandle = adopt("buffer", upsampleParamsResult.value, (value) => {
|
|
38590
40959
|
rhiDevice.destroyBuffer(value);
|
|
38591
40960
|
});
|
|
38592
40961
|
}
|
|
@@ -38682,7 +41051,7 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38682
41051
|
const compositeParamsResult = runShimSyncStep(
|
|
38683
41052
|
() => rhiDevice.createBuffer({
|
|
38684
41053
|
label: "bloom-composite-params-ubo",
|
|
38685
|
-
size:
|
|
41054
|
+
size: BLOOM_COMPOSITE_PARAMS_BYTES,
|
|
38686
41055
|
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
38687
41056
|
mappedAtCreation: false
|
|
38688
41057
|
}),
|
|
@@ -38702,17 +41071,15 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38702
41071
|
bloomCandidateBundle = {
|
|
38703
41072
|
scope: candidateScope,
|
|
38704
41073
|
generation: bloomGeneration + 1,
|
|
38705
|
-
|
|
38706
|
-
|
|
38707
|
-
bloomBlurVPipeline: bloomBlurVPipelineHandle,
|
|
41074
|
+
bloomDownsamplePipeline: bloomDownsamplePipelineHandle,
|
|
41075
|
+
bloomUpsamplePipeline: bloomUpsamplePipelineHandle,
|
|
38708
41076
|
bloomCompositePipeline: bloomCompositePipelineHandle,
|
|
38709
|
-
|
|
38710
|
-
|
|
41077
|
+
bloomDownsampleBindGroupLayout: bloomDownsampleBglHandle,
|
|
41078
|
+
bloomUpsampleBindGroupLayout: bloomUpsampleBglHandle,
|
|
38711
41079
|
bloomCompositeBindGroupLayout: bloomCompositeBglHandle,
|
|
38712
41080
|
bloomSampler: bloomSamplerHandle,
|
|
38713
|
-
|
|
38714
|
-
|
|
38715
|
-
bloomBlurVParamsBuffer: bloomBlurVParamsBufferHandle,
|
|
41081
|
+
bloomDownsampleParamsBuffer: bloomDownsampleParamsBufferHandle,
|
|
41082
|
+
bloomUpsampleParamsBuffer: bloomUpsampleParamsBufferHandle,
|
|
38716
41083
|
bloomCompositeParamsBuffer: bloomCompositeParamsBufferHandle
|
|
38717
41084
|
};
|
|
38718
41085
|
inspectBloomResources = (graph) => {
|
|
@@ -38726,6 +41093,10 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38726
41093
|
return {
|
|
38727
41094
|
graphStatus: "empty",
|
|
38728
41095
|
enabled: false,
|
|
41096
|
+
levelCount: 0,
|
|
41097
|
+
levelDimensions: [],
|
|
41098
|
+
downsamplePassCount: 0,
|
|
41099
|
+
upsamplePassCount: 0,
|
|
38729
41100
|
targetCount: 0,
|
|
38730
41101
|
targetBytes: 0,
|
|
38731
41102
|
resourceCount: 0,
|
|
@@ -38749,6 +41120,10 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38749
41120
|
return {
|
|
38750
41121
|
graphStatus: active === void 0 ? "empty" : graphInspection.status,
|
|
38751
41122
|
enabled: active !== void 0,
|
|
41123
|
+
levelCount: active === void 0 ? 0 : graphInspection.levelCount,
|
|
41124
|
+
levelDimensions: active === void 0 ? [] : graphInspection.levelDimensions,
|
|
41125
|
+
downsamplePassCount: active === void 0 ? 0 : graphInspection.downsamplePassCount,
|
|
41126
|
+
upsamplePassCount: active === void 0 ? 0 : graphInspection.upsamplePassCount,
|
|
38752
41127
|
targetCount: active === void 0 ? 0 : graphInspection.targetCount,
|
|
38753
41128
|
targetBytes: active === void 0 ? 0 : graphInspection.targetBytes,
|
|
38754
41129
|
resourceCount: ownedBundles.reduce(
|
|
@@ -38759,7 +41134,11 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38759
41134
|
encodeCount: active === void 0 ? 0 : receipts.encodeCount,
|
|
38760
41135
|
bindGroupCount: active === void 0 ? 0 : receipts.bindGroupCount,
|
|
38761
41136
|
uploadCount: active === void 0 ? 0 : receipts.uploadCount,
|
|
38762
|
-
|
|
41137
|
+
// The graph owns the target allocations. Use its validated
|
|
41138
|
+
// descriptor byte sum for the active child; zero is reserved for
|
|
41139
|
+
// an absent/invalid current graph, never a placeholder for a
|
|
41140
|
+
// live Bloom allocation.
|
|
41141
|
+
residentChildBytes: active === void 0 || graphInspection.status !== "valid" ? 0 : graphInspection.targetBytes,
|
|
38763
41142
|
generation: selected?.generation ?? 0,
|
|
38764
41143
|
state
|
|
38765
41144
|
};
|
|
@@ -38836,17 +41215,15 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
38836
41215
|
commitBloomResources = void 0;
|
|
38837
41216
|
discardBloomResources = void 0;
|
|
38838
41217
|
drainBloomResources = void 0;
|
|
38839
|
-
|
|
38840
|
-
|
|
38841
|
-
bloomBlurVPipelineHandle = null;
|
|
41218
|
+
bloomDownsamplePipelineHandle = null;
|
|
41219
|
+
bloomUpsamplePipelineHandle = null;
|
|
38842
41220
|
bloomCompositePipelineHandle = null;
|
|
38843
|
-
|
|
38844
|
-
|
|
41221
|
+
bloomDownsampleBglHandle = null;
|
|
41222
|
+
bloomUpsampleBglHandle = null;
|
|
38845
41223
|
bloomCompositeBglHandle = null;
|
|
38846
41224
|
bloomSamplerHandle = null;
|
|
38847
|
-
|
|
38848
|
-
|
|
38849
|
-
bloomBlurVParamsBufferHandle = null;
|
|
41225
|
+
bloomDownsampleParamsBufferHandle = null;
|
|
41226
|
+
bloomUpsampleParamsBufferHandle = null;
|
|
38850
41227
|
bloomCompositeParamsBufferHandle = null;
|
|
38851
41228
|
throw cause;
|
|
38852
41229
|
}
|
|
@@ -39160,24 +41537,17 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
39160
41537
|
shadowLightSpaceMatrix: null,
|
|
39161
41538
|
shadowCsmLightViewProj: null,
|
|
39162
41539
|
shadowCsmSelection: null,
|
|
39163
|
-
//
|
|
39164
|
-
//
|
|
39165
|
-
|
|
39166
|
-
|
|
39167
|
-
// (ensureLazyTexture, slot width/height tracking for size-drift rebuild).
|
|
39168
|
-
// BindGroup caches survive until the intermediate view is invalidated
|
|
39169
|
-
// by a resize (width/height drift forces null).
|
|
39170
|
-
bloomBrightPipeline: null,
|
|
39171
|
-
bloomBlurHPipeline: null,
|
|
39172
|
-
bloomBlurVPipeline: null,
|
|
41540
|
+
// Multiscale Bloom pipeline handles are generation-owned by the ready
|
|
41541
|
+
// builder; graph targets are frame-owned and are not stored here.
|
|
41542
|
+
bloomDownsamplePipeline: null,
|
|
41543
|
+
bloomUpsamplePipeline: null,
|
|
39173
41544
|
bloomCompositePipeline: null,
|
|
39174
|
-
|
|
39175
|
-
|
|
41545
|
+
bloomDownsampleBindGroupLayout: null,
|
|
41546
|
+
bloomUpsampleBindGroupLayout: null,
|
|
39176
41547
|
bloomCompositeBindGroupLayout: null,
|
|
39177
41548
|
bloomSampler: null,
|
|
39178
|
-
|
|
39179
|
-
|
|
39180
|
-
bloomBlurVParamsBuffer: null,
|
|
41549
|
+
bloomDownsampleParamsBuffer: null,
|
|
41550
|
+
bloomUpsampleParamsBuffer: null,
|
|
39181
41551
|
bloomCompositeParamsBuffer: null,
|
|
39182
41552
|
ensureBloomResources: ensureBloomResourcesStable,
|
|
39183
41553
|
getBloomResources: getBloomResourcesStable,
|
|
@@ -39189,18 +41559,6 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
39189
41559
|
retireBloomResources: retireBloomResourcesStable,
|
|
39190
41560
|
drainBloomResources: drainBloomResourcesStable,
|
|
39191
41561
|
inspectBloomResources: inspectBloomResourcesStable,
|
|
39192
|
-
bloomBrightTexture: null,
|
|
39193
|
-
bloomBrightView: null,
|
|
39194
|
-
bloomBrightWidth: 0,
|
|
39195
|
-
bloomBrightHeight: 0,
|
|
39196
|
-
bloomBlurHTexture: null,
|
|
39197
|
-
bloomBlurHView: null,
|
|
39198
|
-
bloomBlurHWidth: 0,
|
|
39199
|
-
bloomBlurHHeight: 0,
|
|
39200
|
-
bloomBlurVTexture: null,
|
|
39201
|
-
bloomBlurVView: null,
|
|
39202
|
-
bloomBlurVWidth: 0,
|
|
39203
|
-
bloomBlurVHeight: 0,
|
|
39204
41562
|
// feat-20260612-hdrp-ssao M6 / w26 + M8 / w38: SSAO pipeline slots.
|
|
39205
41563
|
ssaoCalcPipeline: ssaoCalcPipelineHandle,
|
|
39206
41564
|
ssaoBlurPipeline: ssaoBlurPipelineHandle,
|
|
@@ -40246,22 +42604,13 @@ async function makeWebGPURenderer(internals) {
|
|
|
40246
42604
|
tonemapRegistered = true;
|
|
40247
42605
|
},
|
|
40248
42606
|
(source) => {
|
|
40249
|
-
|
|
40250
|
-
renderSystem.registerBuiltinPostProcess(FXAA_POST_PROCESS_ID, {
|
|
40251
|
-
source,
|
|
40252
|
-
params: { byteSize: 16, defaultValue: new Uint8Array(16) },
|
|
40253
|
-
reads: ["ldrColor"]
|
|
40254
|
-
});
|
|
40255
|
-
fxaaRegistered = true;
|
|
42607
|
+
fxaaRegistered = registerFxaaPostProcess(renderSystem, source, fxaaRegistered);
|
|
40256
42608
|
},
|
|
42609
|
+
(source) => registerAnalyticFogPostProcess(renderSystem, source, candidateBuildState !== void 0),
|
|
40257
42610
|
(entries) => {
|
|
40258
42611
|
if (candidateBuildState !== void 0 || temporalPostProcessesRegistered) return;
|
|
40259
42612
|
if (entries.motionBlur !== void 0) {
|
|
40260
|
-
renderSystem
|
|
40261
|
-
source: entries.motionBlur,
|
|
40262
|
-
params: { byteSize: 16, defaultValue: new Uint8Array(16) },
|
|
40263
|
-
reads: ["scene-color", "scene-temporal"]
|
|
40264
|
-
});
|
|
42613
|
+
registerMotionBlurPostProcess(renderSystem, entries.motionBlur);
|
|
40265
42614
|
}
|
|
40266
42615
|
if (entries.taaResolve !== void 0) {
|
|
40267
42616
|
renderSystem.registerBuiltinPostProcess("forgeax.taa-resolve", {
|
|
@@ -41168,20 +43517,7 @@ async function makeWebGPURenderer(internals) {
|
|
|
41168
43517
|
const state = disposed ? "disposed" : deviceLost ? "device-lost" : health.reason === "internal-fault" || !readySettled ? "faulted" : "alive";
|
|
41169
43518
|
const graph = deviceLost ? void 0 : renderSystem.perFrameGraphInfo;
|
|
41170
43519
|
const standardLighting = deviceLost ? void 0 : renderSystem.standardLightingInspection;
|
|
41171
|
-
const bloom = deviceLost ?
|
|
41172
|
-
graphStatus: "empty",
|
|
41173
|
-
enabled: false,
|
|
41174
|
-
targetCount: 0,
|
|
41175
|
-
targetBytes: 0,
|
|
41176
|
-
resourceCount: 0,
|
|
41177
|
-
passCount: 0,
|
|
41178
|
-
encodeCount: 0,
|
|
41179
|
-
bindGroupCount: 0,
|
|
41180
|
-
uploadCount: 0,
|
|
41181
|
-
residentChildBytes: 0,
|
|
41182
|
-
generation: 0,
|
|
41183
|
-
state: "off"
|
|
41184
|
-
} : renderSystem.bloom;
|
|
43520
|
+
const bloom = deviceLost ? emptyBloomInspection() : renderSystem.bloom;
|
|
41185
43521
|
const surfaceStorage = pipelineState?.format ?? "rgba8unorm";
|
|
41186
43522
|
const surfaceDisplay = pipelineState?.colorAttachmentFormat ?? "rgba8unorm";
|
|
41187
43523
|
const observationId = `${STANDARD_OUTPUT_TRANSFORM_FEATURE_ID}:frame-${frameId}`;
|
|
@@ -41600,6 +43936,7 @@ async function makeWebGPURenderer(internals) {
|
|
|
41600
43936
|
cameraOwner,
|
|
41601
43937
|
resourceOwner,
|
|
41602
43938
|
...frameRequest.camera.entityKey === void 0 ? {} : { cameraEntityKey: frameRequest.camera.entityKey },
|
|
43939
|
+
...motionBlurDrawOptions(frameRequest),
|
|
41603
43940
|
...frameRequest.profileFrame === void 0 ? {} : { profileFrame: frameRequest.profileFrame }
|
|
41604
43941
|
} : options ?? { cameraOwner: 0, resourceOwner: 0 };
|
|
41605
43942
|
if (disposed) {
|
|
@@ -41691,6 +44028,8 @@ async function makeWebGPURenderer(internals) {
|
|
|
41691
44028
|
const cleanupFailure = disposeOwnedObservationCaptures(observationCaptureOwner.drain());
|
|
41692
44029
|
volumeTimingCapture?.discard();
|
|
41693
44030
|
if (cleanupFailure !== void 0) return err(cleanupFailure);
|
|
44031
|
+
const invalidMotionBlur = renderSystem.motionBlurInvalidParams;
|
|
44032
|
+
if (invalidMotionBlur !== void 0) return err(invalidMotionBlur);
|
|
41694
44033
|
return err(
|
|
41695
44034
|
new RendererContractFailureError(
|
|
41696
44035
|
"draw",
|
|
@@ -41981,7 +44320,7 @@ async function makeWebGPURenderer(internals) {
|
|
|
41981
44320
|
receiptObservationBuffers.delete(receipt);
|
|
41982
44321
|
}
|
|
41983
44322
|
}
|
|
41984
|
-
await renderSystem
|
|
44323
|
+
await observeLodOcclusionForReceipt(renderSystem, receipt);
|
|
41985
44324
|
const targetReadbacks = request.targetReadbacks === void 0 || request.targetReadbacks.length === 0 ? void 0 : await renderTargetHost.observeTargetReadbacks(receipt, request.targetReadbacks);
|
|
41986
44325
|
if (targetReadbacks !== void 0 && !targetReadbacks.ok) return targetReadbacks;
|
|
41987
44326
|
const volumeTimings = request.include.includes("timings") ? await (receiptTimings.get(receipt) ?? Promise.resolve({
|