@forgeax/engine-render 0.1.19 → 0.1.20
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/dist/.tsbuildinfo +1 -1
- package/dist/assembly/factory.d.ts +16 -5
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/{chunk-RJRUS55L.mjs → chunk-4XINDLAY.mjs} +267 -186
- package/dist/chunk-4XINDLAY.mjs.map +1 -0
- package/dist/{chunk-YTQ5C7YQ.mjs → chunk-CDTWTHZR.mjs} +176 -8
- package/dist/chunk-CDTWTHZR.mjs.map +1 -0
- package/dist/{chunk-IPXCABEP.mjs → chunk-M2MP4ZOU.mjs} +2 -2
- package/dist/chunk-M2MP4ZOU.mjs.map +1 -0
- package/dist/{chunk-LOSO4CTV.mjs → chunk-TBQJOK2L.mjs} +67 -3
- package/dist/chunk-TBQJOK2L.mjs.map +1 -0
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/spot-light.d.ts +12 -0
- package/dist/components/spot-light.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +2281 -164
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +19 -2
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/errors/render.d.ts +69 -1
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/gpu-usage.d.ts +2 -0
- package/dist/gpu-usage.d.ts.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/internal.mjs +1 -1
- package/dist/light-buffer-layout.d.ts +20 -12
- package/dist/light-buffer-layout.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +6 -3
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-profile.d.ts +13 -0
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/record/frame-lighting.d.ts +2 -0
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +37 -1
- 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 +3 -2
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-timing.d.ts +32 -0
- package/dist/record/gpu-timing.d.ts.map +1 -0
- package/dist/record/main-pass-material.d.ts +1 -1
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +5 -1
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +11 -3
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/render-contract.d.ts +66 -1
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-data.d.ts +10 -6
- package/dist/render-data.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +36 -0
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +55 -3
- 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/renderer-inspect.d.ts +29 -0
- package/dist/renderer-inspect.d.ts.map +1 -0
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/temporal/frame.d.ts +44 -0
- package/dist/temporal/frame.d.ts.map +1 -0
- package/dist/temporal/index.mjs +3 -3
- package/dist/typed-render-graph-primitives.d.ts +6 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-atlas.d.ts +10 -0
- package/dist/typed-shadow-atlas.d.ts.map +1 -0
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/capability.d.ts +57 -0
- package/dist/volume/capability.d.ts.map +1 -0
- package/dist/volume/component.d.ts +61 -0
- package/dist/volume/component.d.ts.map +1 -0
- package/dist/volume/extract.d.ts +11 -0
- package/dist/volume/extract.d.ts.map +1 -0
- package/dist/volume/inspection.d.ts +38 -0
- package/dist/volume/inspection.d.ts.map +1 -0
- package/dist/volume/passes.d.ts +38 -0
- package/dist/volume/passes.d.ts.map +1 -0
- package/dist/volume/recovery.d.ts +38 -0
- package/dist/volume/recovery.d.ts.map +1 -0
- package/dist/volume/resources.d.ts +57 -0
- package/dist/volume/resources.d.ts.map +1 -0
- package/dist/volume/temporal.d.ts +35 -0
- package/dist/volume/temporal.d.ts.map +1 -0
- package/package.json +20 -20
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -14
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
- package/src/__tests__/render-error-code-owner.test-d.ts +5 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +28 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +1 -1
- package/src/__tests__/skinned-shadow-caster.test.ts +21 -0
- package/src/__tests__/standard-profile-oracle.unit.test.ts +7 -5
- package/src/__tests__/volume-optics-oracle.unit.test.ts +85 -0
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +655 -0
- package/src/__tests__/volumetric-fog-component.unit.test.ts +71 -0
- package/src/__tests__/volumetric-fog-composite-stage-readback.dawn.test.ts +376 -0
- package/src/__tests__/volumetric-fog-errors.unit.test.ts +51 -0
- package/src/__tests__/volumetric-fog-inspection.unit.test.ts +77 -0
- package/src/__tests__/volumetric-fog-light-capability.unit.test.ts +19 -0
- package/src/__tests__/volumetric-fog-light-consumers.integration.test.ts +18 -0
- package/src/__tests__/volumetric-fog-light-errors.unit.test.ts +17 -0
- package/src/__tests__/volumetric-fog-light-topology.unit.test.ts +21 -0
- package/src/__tests__/volumetric-fog-light-transaction.integration.test.ts +53 -0
- package/src/__tests__/volumetric-fog-m9-watcher.integration.test.ts +27 -0
- package/src/__tests__/volumetric-fog-point-light-owner.integration.test.ts +44 -0
- package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +49 -0
- package/src/__tests__/volumetric-fog-point-spot-contract.unit.test.ts +28 -0
- package/src/__tests__/volumetric-fog-point-spot-lkg.integration.test.ts +65 -0
- package/src/__tests__/volumetric-fog-point-spot-selection.integration.test.ts +49 -0
- package/src/__tests__/volumetric-fog-point-spot-temporal.unit.test.ts +55 -0
- package/src/__tests__/volumetric-fog-projector-contract.unit.test.ts +22 -0
- package/src/__tests__/volumetric-fog-projector-inspection.unit.test.ts +12 -0
- package/src/__tests__/volumetric-fog-projector-surface-volume.integration.test.ts +11 -0
- package/src/__tests__/volumetric-fog-projector-transaction.integration.test.ts +26 -0
- package/src/__tests__/volumetric-fog-recovery.integration.test.ts +47 -0
- package/src/__tests__/volumetric-fog-selected-light-resolution.integration.test.ts +63 -0
- package/src/__tests__/volumetric-fog-spot-closure.integration.test.ts +26 -0
- package/src/__tests__/volumetric-fog-spot-contract.test-d.ts +21 -0
- package/src/__tests__/volumetric-fog-spot-contract.unit.test.ts +27 -0
- package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +14 -0
- package/src/__tests__/volumetric-fog-spot-optics.integration.test.ts +29 -0
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +550 -0
- package/src/__tests__/volumetric-fog-temporal-selected-light.unit.test.ts +33 -0
- package/src/__tests__/volumetric-fog-temporal.unit.test.ts +45 -0
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +237 -0
- package/src/__tests__/volumetric-fog-transparent.integration.test.ts +27 -0
- package/src/assembly/factory.ts +271 -15
- package/src/components/light-helpers.ts +4 -0
- package/src/components/spot-light.ts +22 -3
- package/src/device/gpu-residency.ts +168 -58
- package/src/errors/render.ts +119 -1
- package/src/gpu-usage.ts +2 -0
- package/src/index.ts +38 -1
- package/src/light-buffer-layout.ts +59 -26
- package/src/pbr-pipeline.ts +33 -10
- package/src/pipeline/standard-forward-lane.ts +20 -0
- package/src/pipeline/standard-pipeline.ts +20 -0
- package/src/pipeline/standard-profile.ts +23 -0
- package/src/record/frame-lighting.ts +103 -89
- package/src/record/frame-snapshot.ts +41 -1
- package/src/record/frame-targets.ts +6 -2
- package/src/record/frame.ts +908 -5
- package/src/record/gpu-timing.ts +224 -0
- package/src/record/main-pass-material.ts +6 -2
- package/src/record/render-context.ts +5 -0
- package/src/record/shadow-pass.ts +87 -38
- package/src/record/typed-frame-graph.ts +143 -8
- package/src/render-contract.ts +72 -0
- package/src/render-data.ts +50 -22
- package/src/render-pipeline.ts +44 -0
- package/src/render-system-extract.ts +329 -3
- package/src/render-system.ts +79 -0
- package/src/renderer-inspect.ts +66 -0
- package/src/scene/render-scene.ts +2 -0
- package/src/temporal/frame.ts +128 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +2 -3
- package/src/typed-render-graph-primitives.ts +32 -2
- package/src/typed-shadow-atlas.ts +17 -0
- package/src/typed-shadow-passes.ts +6 -5
- package/src/volume/capability.ts +173 -0
- package/src/volume/component.ts +110 -0
- package/src/volume/extract.ts +21 -0
- package/src/volume/inspection.ts +78 -0
- package/src/volume/passes.ts +680 -0
- package/src/volume/recovery.ts +86 -0
- package/src/volume/resources.ts +236 -0
- package/src/volume/temporal.ts +89 -0
- package/dist/chunk-IPXCABEP.mjs.map +0 -1
- package/dist/chunk-LOSO4CTV.mjs.map +0 -1
- package/dist/chunk-RJRUS55L.mjs.map +0 -1
- package/dist/chunk-YTQ5C7YQ.mjs.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { GlyphText, selectActiveCameraIndex, getActiveCamera } from './chunk-FJ6P52EE.mjs';
|
|
2
|
-
import { MeshFilter, MeshRenderer, Instances, Layer, Visibility, Camera, DirectionalLight, PointLight, PointLightShadow, SpotLight, Skylight, SkyboxBackground, PostProcessParams, admitPointsLines, Fog, resolveVisibility, Atmosphere, Points, Lines, SortKey, pointShapeFromU32, CAMERA_PROJECTION_ORTHOGRAPHIC, MotionBlur, bloomEnabledFromF32, antialiasFromF32, tonemapFromF32, cameraProjectionFromF32, tonemapToU32 } from './chunk-
|
|
2
|
+
import { MeshFilter, MeshRenderer, Instances, Layer, Visibility, Camera, DirectionalLight, PointLight, PointLightShadow, SpotLight, Skylight, SkyboxBackground, PostProcessParams, VolumetricFog, inspectVolumetricFog, admitPointsLines, hasVolumetricFogCapability, Fog, resolveVisibility, Atmosphere, Points, Lines, SortKey, pointShapeFromU32, CAMERA_PROJECTION_ORTHOGRAPHIC, resolveSelectedVolumetricLight, extractVolumetricFog, MotionBlur, bloomEnabledFromF32, antialiasFromF32, tonemapFromF32, cameraProjectionFromF32, tonemapToU32 } from './chunk-CDTWTHZR.mjs';
|
|
3
3
|
import { SpriteInstances, SpriteRegionOverride, TileLayer, decodeSortScope, Tilemap } from './chunk-JDWARUOI.mjs';
|
|
4
|
-
import { SKIN_MATERIAL_SHADER_ID, assertStorageBufferCap, GpuBuffer, GpuTexture, deriveRenderDataTexture, deriveRenderDataMesh,
|
|
4
|
+
import { SKIN_MATERIAL_SHADER_ID, assertStorageBufferCap, GpuBuffer, GpuTexture, deriveRenderDataTexture, deriveRenderDataMesh, deriveRenderDataCubemap, createIblPipelines, GPU_TEXTURE_USAGE_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_DST, CUBEMAP_FACE_VERTICES, GPU_BUFFER_USAGE_VERTEX, GPU_BUFFER_USAGE_COPY_DST, runIblPrecompute, GPU_BUFFER_USAGE_INDEX, createClusterBinScratch, GPU_BUFFER_USAGE_UNIFORM, entryHasDepthRead, DEPTH_MIN_PARAMS_BYTE_SIZE, retireTemporalGpuState, PostProcessError, resolveSsaoParameters, buildPerFrameBindGroups, PipelineSpecError, CAPTURE_VIEW_PROJS, PREFILTER_MIP_LEVELS, selectSwapChainFormat, setIblComposedShaders, POINTS_LINES_MATERIAL_SHADER_ID, createHdrpBindGroupLayoutDescriptor, createHdrpClusterMembershipBindGroupLayoutDescriptor, createSkylightFallback, buildSpecConstTable, buildLinearLdrMaterialSpecTable, getOrBuildPipeline, cacheKeyOf, createPointsLinesLaneAdapter, prepareFrameLighting, warnMultiSkylight, warnMultiSkybox, selectLazyEquirectHandle, driveLazyEquirectProjection, writeHdrpClusterAndSsaoBuffers, warnZeroLightStandard, writeShadowParamsBuffer, computeProjectionMatrix, computeViewMatrix, resolveSkyboxActive, computeSplitLdrSprite, writeViewUbo, writePointsLinesViewUbo, writeShadowCasterUniforms, writePointSpotLightBuffers, uploadMeshSsboBatch, colorFormatsForPassKind, configureSurface, GPU_BUFFER_USAGE_QUERY_RESOLVE, GPU_BUFFER_USAGE_COPY_SRC, GPU_BUFFER_USAGE_MAP_READ, buildPbrPipelineLayouts, buildPbrSkinLayouts, VIEW_UNIFORM_BUFFER_SIZE, POINTS_LINES_VIEW_BUFFER_SIZE, SHADOW_CASTER_BUFFER_SIZE, GPU_BUFFER_USAGE_STORAGE, FALLBACK_BYTES_PER_ROW, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING, GPU_SHADER_STAGE_FRAGMENT, geometryRenderStateForTopology, GPU_TEXTURE_USAGE_COPY_SRC, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT, createPointsLinesLaneContract, foldDispatchBuckets, variantSetFromDefines, cleanPerEntityCache, ensureMeshSsboCapacity, buildFoldDispatchPlan, evaluateFoldBucketUniformCap, incrementFoldedDrawsMetric, buildBindGroupLayoutDescriptor, LIGHT_ARRAY_HEADER_BYTES, POINT_LIGHT_STD430_BYTES, LIGHT_ARRAY_MAX_SLOTS, SPOT_LIGHT_STD430_BYTES, GPU_BUFFER_USAGE_INDIRECT, selectPasses, isStandardPbrMaterialShader, resolveSurfaceFormatPair, resolveSurfaceProfile, abortTemporalGpuSubmit, commitTemporalGpuSubmit, retireTemporalGpuStateAfterFence, hasPendingTemporalGpuSubmit, buildPbrMaterialUserRegionEntries, isSkinnedShadowCasterVariant, getTemporalGpuState, temporalReadIndex, temporalWriteIndex, GPU_SHADER_STAGE_VERTEX, importRenderPipelineSurface, aggregateTemporalDemand, addTypedSkyboxPass, addTypedScenePass, standardTemporalLaneAdmission, createStandardSceneDataTarget, addStandardSceneDataPass, addTypedFrameObservationPass, DEFERRED_COLOR_FORMATS, addTypedSsaoPasses, typedFrameClearColor, createRenderPipelineTarget, addTypedTemporalResolvePass, addTypedBloomPasses, addTypedOutputTransformPass, addTypedFullscreenPass, addTypedCompositePostEffects, encodeDirectionalShadowPass, encodePointShadowPass, encodeSpotShadowPass, VIEW_UNIFORM_BYTES, getOrCreateHdrpBuffers, buildFullscreenPostProcessPass, MATERIAL_PER_ENTITY_STRIDE, PREFILTER_SIZE, isTemporalFullscreenBinding, createFullscreenBindGroup, applyParamSnapshotToUbo, residentTextureView, assembleMaterialWithSkylightEntries, TYPED_BLOOM_PASS_NAMES } from './chunk-4XINDLAY.mjs';
|
|
5
5
|
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';
|
|
6
|
-
import { RendererContractFailureError, RendererOperationError, DEFAULT_STANDARD_PROFILE, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, RENDER_PHASE_CATALOG, DEFAULT_CLUSTER_GRID, resolveStandardLane, FrameReceiptStaleError, RenderFeatureStageFailedError, RenderFeatureCapabilityMissingError, RenderFeatureRegistrationConflictError, LifecycleConstructionError, EnvironmentGenerationFailedError, PointsLinesMaterialUnsupportedError, TransmissionCapabilityMissingError, createSceneDataCatalog, STANDARD_PIPELINE_ID, STANDARD_LIGHT_COUNTS, STANDARD_POST_STAGE_NAMES, ObservationUnavailableError, SkinMaterialMismatchError, MaterialSkinAttrMissingError, RenderFeaturePreparationFailedError, PointsLinesPrepareFailedError, EnvironmentSourceConflictError, FogCardinalityError, ShadowInvalidConfigError, AtmosphereInvalidParameterError, RenderFeaturePreparedStateMismatchError, RenderFeatureDrawRecordingFailedError, SceneDataUnavailableError, HdrpDeferredCapsInsufficientError, SkinPaletteOverflowError, PointShadowAtlasUninitializedError, MAX_LIGHTS, CLUSTER_GRID_STRIDE_U32, LIGHT_INDEX_LIST_CAPACITY, isSceneDataTarget } from './chunk-
|
|
6
|
+
import { RendererContractFailureError, RendererOperationError, DEFAULT_STANDARD_PROFILE, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, RENDER_PHASE_CATALOG, DEFAULT_CLUSTER_GRID, resolveStandardLane, FrameReceiptStaleError, RenderFeatureStageFailedError, RenderFeatureCapabilityMissingError, RenderFeatureRegistrationConflictError, LifecycleConstructionError, EnvironmentGenerationFailedError, PointsLinesMaterialUnsupportedError, TransmissionCapabilityMissingError, createSceneDataCatalog, STANDARD_PIPELINE_ID, STANDARD_LIGHT_COUNTS, STANDARD_POST_STAGE_NAMES, TemporalFrameSubmitError, ObservationUnavailableError, SkinMaterialMismatchError, MaterialSkinAttrMissingError, RenderFeaturePreparationFailedError, PointsLinesPrepareFailedError, EnvironmentSourceConflictError, FogCardinalityError, ShadowInvalidConfigError, resolveVolumetricFogProfile, AtmosphereInvalidParameterError, RenderFeaturePreparedStateMismatchError, RenderFeatureDrawRecordingFailedError, SceneDataUnavailableError, HdrpDeferredCapsInsufficientError, SkinPaletteOverflowError, PointShadowAtlasUninitializedError, MAX_LIGHTS, CLUSTER_GRID_STRIDE_U32, LIGHT_INDEX_LIST_CAPACITY, isSceneDataTarget } from './chunk-TBQJOK2L.mjs';
|
|
7
7
|
import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './chunk-VFPLTKCU.mjs';
|
|
8
|
-
import { getTextureIdentity, worldEntityKey, makeZeroCameraFallbackSnapshot } from './chunk-
|
|
8
|
+
import { getTextureIdentity, worldEntityKey, makeZeroCameraFallbackSnapshot } from './chunk-M2MP4ZOU.mjs';
|
|
9
9
|
import { AssetRegistry, DynamicTextureStore, adaptDynamicTextureDevice, getOrCreateMipmapPipeline, blitMipmapsSync, HANDLE_CUBE, HANDLE_TRIANGLE, HANDLE_QUAD, HANDLE_SPHERE, HANDLE_NINESLICE_QUAD, BuiltinAssetRegistry, numMipLevels, resolveAssetHandle, walkMaterialPassesOverSharedRefs, encodeMipmapLevel, resolveTilesetRuntime, MeshSsboCeilingReachedError, MeshSsboCapacityExceededError } from '@forgeax/engine-assets-runtime';
|
|
10
10
|
import { audioLoader } from '@forgeax/engine-audio-webaudio';
|
|
11
11
|
import { Update } from '@forgeax/engine-ecs';
|
|
@@ -14,8 +14,8 @@ import { PROCEDURAL_FLOATS_PER_VERTEX, deriveVertexLayoutProjection, deriveVerte
|
|
|
14
14
|
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
15
15
|
import { ok, err, RhiError, validateDrawArgs } from '@forgeax/engine-rhi';
|
|
16
16
|
import { Transform, ChildOf, collectSubtree, Name, registerPropagateTransforms, Children, projectHierarchy, MorphWeights } from '@forgeax/engine-scene';
|
|
17
|
-
import { ShaderCatalog, findVariantByKey } from '@forgeax/engine-shader';
|
|
18
|
-
import { err as err$1, ok as ok$1, derive, handleSlot, toShared, ASSET_ERROR_HINTS, countExtraUvSets, RenderQueue, IMAGE_ERROR_HINTS, AssetError, materialValuesToLinearRuntime, unpackSlot, TextError, unwrapHandle, materialGuidText } from '@forgeax/engine-types';
|
|
17
|
+
import { ShaderCatalog, findVariantByKey, DEFAULT_STANDARD_PBR_PARAM_SCHEMA } from '@forgeax/engine-shader';
|
|
18
|
+
import { err as err$1, ok as ok$1, derive, handleSlot, toShared, deriveTextureLayout, ASSET_ERROR_HINTS, countExtraUvSets, RenderQueue, IMAGE_ERROR_HINTS, AssetError, materialValuesToLinearRuntime, unpackSlot, TextError, unwrapHandle, materialGuidText } from '@forgeax/engine-types';
|
|
19
19
|
import { vec3, halfFloat, mat4, frustum, box3 } from '@forgeax/engine-math';
|
|
20
20
|
import { resetFontConcurrency, decodeTileBits, trackFontConcurrency, layoutGlyphText, conservativeCubeAabb, bakeGlyphMesh } from '@forgeax/engine-graphics-extras';
|
|
21
21
|
import { createCurrentFrameObservationLease, RenderGraphError, RenderGraphBuilder } from '@forgeax/engine-render-graph';
|
|
@@ -200,6 +200,8 @@ var GpuResidencyCache = class {
|
|
|
200
200
|
// Hardware-probe caps injected at `configureGpuDevice`; guards the HDR cubemap
|
|
201
201
|
// path (_uploadCubemapFromEquirect) when `rgba16floatRenderable` is false.
|
|
202
202
|
caps = void 0;
|
|
203
|
+
deviceEpoch = 0;
|
|
204
|
+
textureGeneration = 0;
|
|
203
205
|
textureGpuHandles = /* @__PURE__ */ new Map();
|
|
204
206
|
samplerGpuHandles = /* @__PURE__ */ new Map();
|
|
205
207
|
cubemapGpuHandles = /* @__PURE__ */ new Map();
|
|
@@ -235,6 +237,12 @@ var GpuResidencyCache = class {
|
|
|
235
237
|
* the pull model builds resources lazily at first `ensureResident` access.
|
|
236
238
|
*/
|
|
237
239
|
configureGpuDevice(device, asyncCreateShaderModule, registerCube, caps) {
|
|
240
|
+
for (const entry of this.textureGpuHandles.values()) {
|
|
241
|
+
if (!entry.texture.isDestroyed) entry.texture.destroy();
|
|
242
|
+
}
|
|
243
|
+
if (this.textureGpuHandles.size > 0) this.materialResourceEpoch += 1;
|
|
244
|
+
this.textureGpuHandles.clear();
|
|
245
|
+
this.deviceEpoch += 1;
|
|
238
246
|
this.meshResidencyEpoch += 1;
|
|
239
247
|
this.gpuDevice = device;
|
|
240
248
|
this.asyncCreateShaderModule = asyncCreateShaderModule;
|
|
@@ -673,7 +681,7 @@ var GpuResidencyCache = class {
|
|
|
673
681
|
const { tex, levels, gpuTexture } = prepared.value;
|
|
674
682
|
const cacheKey = this.worldKey(handleSlot(handle), worldId);
|
|
675
683
|
if (gpuTexture === void 0) return ok(void 0);
|
|
676
|
-
if (!projected.value.compressed &&
|
|
684
|
+
if (!projected.value.compressed && tex.mips.kind === "generate" && levels > 1) {
|
|
677
685
|
const factory = this.asyncCreateShaderModule;
|
|
678
686
|
if (factory === void 0) {
|
|
679
687
|
return err(
|
|
@@ -696,21 +704,22 @@ var GpuResidencyCache = class {
|
|
|
696
704
|
}
|
|
697
705
|
const blitRes = blitMipmapsSync(device, gpuTexture, {
|
|
698
706
|
format: tex.format,
|
|
699
|
-
width: tex.width,
|
|
700
|
-
height: tex.height,
|
|
707
|
+
width: tex.shape.extent.width,
|
|
708
|
+
height: tex.shape.extent.height,
|
|
701
709
|
levels
|
|
702
710
|
});
|
|
703
711
|
if (!blitRes.ok) return blitRes;
|
|
704
712
|
}
|
|
705
713
|
const viewRes = device.createTextureView(gpuTexture, {
|
|
706
714
|
label: `texture-view-${cacheKey}`,
|
|
707
|
-
dimension:
|
|
715
|
+
dimension: projected.value.shape.viewDimension
|
|
708
716
|
});
|
|
709
717
|
if (!viewRes.ok) return viewRes;
|
|
710
718
|
const previous = this.textureGpuHandles.get(cacheKey);
|
|
711
719
|
const next = {
|
|
712
720
|
texture: this.wrapTex(gpuTexture),
|
|
713
|
-
view: viewRes.value
|
|
721
|
+
view: viewRes.value,
|
|
722
|
+
receipt: this.textureReceipt(pod, projected.value)
|
|
714
723
|
};
|
|
715
724
|
this.textureGpuHandles.set(cacheKey, next);
|
|
716
725
|
this.materialResourceEpoch += 1;
|
|
@@ -740,21 +749,25 @@ var GpuResidencyCache = class {
|
|
|
740
749
|
}
|
|
741
750
|
const { tex, levels, gpuTexture } = prepared.value;
|
|
742
751
|
const cacheKey = this.worldKey(handleSlot(handle), worldId);
|
|
743
|
-
if (!renderData.compressed &&
|
|
752
|
+
if (!renderData.compressed && tex.mips.kind === "generate" && levels > 1) {
|
|
744
753
|
const blitRes = blitMipmapsSync(device, gpuTexture, {
|
|
745
754
|
format: tex.format,
|
|
746
|
-
width: tex.width,
|
|
747
|
-
height: tex.height,
|
|
755
|
+
width: tex.shape.extent.width,
|
|
756
|
+
height: tex.shape.extent.height,
|
|
748
757
|
levels
|
|
749
758
|
});
|
|
750
759
|
if (!blitRes.ok) return blitRes;
|
|
751
760
|
}
|
|
752
761
|
const viewRes = device.createTextureView(gpuTexture, {
|
|
753
762
|
label: `texture-view-${cacheKey}`,
|
|
754
|
-
dimension:
|
|
763
|
+
dimension: renderData.shape.viewDimension
|
|
755
764
|
});
|
|
756
765
|
if (!viewRes.ok) return viewRes;
|
|
757
|
-
const entry = {
|
|
766
|
+
const entry = {
|
|
767
|
+
texture: this.wrapTex(gpuTexture),
|
|
768
|
+
view: viewRes.value,
|
|
769
|
+
receipt: this.textureReceipt(pod, renderData)
|
|
770
|
+
};
|
|
758
771
|
const previous = this.textureGpuHandles.get(cacheKey);
|
|
759
772
|
this.textureGpuHandles.set(cacheKey, entry);
|
|
760
773
|
this.materialResourceEpoch += 1;
|
|
@@ -763,6 +776,21 @@ var GpuResidencyCache = class {
|
|
|
763
776
|
}
|
|
764
777
|
return ok(entry);
|
|
765
778
|
}
|
|
779
|
+
textureReceipt(pod, renderData) {
|
|
780
|
+
return {
|
|
781
|
+
shape: pod.shape,
|
|
782
|
+
format: renderData.format,
|
|
783
|
+
extent: {
|
|
784
|
+
width: renderData.physicalExtent.width,
|
|
785
|
+
height: renderData.physicalExtent.height,
|
|
786
|
+
depthOrArrayLayers: renderData.depthOrArrayLayers
|
|
787
|
+
},
|
|
788
|
+
bytes: pod.data.byteLength,
|
|
789
|
+
view: renderData.shape.viewDimension,
|
|
790
|
+
generation: ++this.textureGeneration,
|
|
791
|
+
deviceEpoch: this.deviceEpoch
|
|
792
|
+
};
|
|
793
|
+
}
|
|
766
794
|
/**
|
|
767
795
|
* Shared texture-upload prelude: format / colorSpace consistency assertion,
|
|
768
796
|
* GPU texture allocation + writeTexture. Returns `gpuTexture: undefined`
|
|
@@ -789,13 +817,17 @@ var GpuResidencyCache = class {
|
|
|
789
817
|
if (device === void 0) {
|
|
790
818
|
return ok({ id, tex, levels, gpuTexture: void 0 });
|
|
791
819
|
}
|
|
792
|
-
const maxDimension = device.limits.maxTextureDimension2D;
|
|
793
|
-
|
|
820
|
+
const maxDimension = tex.shape.viewDimension === "3d" ? device.limits.maxTextureDimension3D : device.limits.maxTextureDimension2D;
|
|
821
|
+
const width = tex.shape.extent.width;
|
|
822
|
+
const height = tex.shape.extent.height;
|
|
823
|
+
const layers = renderData.depthOrArrayLayers;
|
|
824
|
+
const exceedsLayerLimit = tex.shape.viewDimension === "2d-array" && layers > device.limits.maxTextureArrayLayers;
|
|
825
|
+
if (width > maxDimension || height > maxDimension || exceedsLayerLimit) {
|
|
794
826
|
return err(
|
|
795
827
|
makeImageError({
|
|
796
828
|
code: "image-dimension-out-of-bounds",
|
|
797
|
-
requested: { width
|
|
798
|
-
limit: maxDimension
|
|
829
|
+
requested: { width, height },
|
|
830
|
+
limit: exceedsLayerLimit ? device.limits.maxTextureArrayLayers : maxDimension
|
|
799
831
|
})
|
|
800
832
|
);
|
|
801
833
|
}
|
|
@@ -804,11 +836,11 @@ var GpuResidencyCache = class {
|
|
|
804
836
|
size: {
|
|
805
837
|
width: renderData.physicalExtent.width,
|
|
806
838
|
height: renderData.physicalExtent.height,
|
|
807
|
-
depthOrArrayLayers:
|
|
839
|
+
depthOrArrayLayers: renderData.depthOrArrayLayers
|
|
808
840
|
},
|
|
809
841
|
mipLevelCount: levels,
|
|
810
842
|
sampleCount: 1,
|
|
811
|
-
dimension: "2d",
|
|
843
|
+
dimension: tex.shape.viewDimension === "3d" ? "3d" : "2d",
|
|
812
844
|
format: renderData.format,
|
|
813
845
|
usage: renderData.usage,
|
|
814
846
|
viewFormats: [],
|
|
@@ -818,25 +850,74 @@ var GpuResidencyCache = class {
|
|
|
818
850
|
const gpuTexture = createTexRes.value;
|
|
819
851
|
const queue = device.queue;
|
|
820
852
|
if (renderData.compressed) {
|
|
821
|
-
const layout =
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
{
|
|
853
|
+
const layout = deriveTextureLayout({
|
|
854
|
+
shape: tex.shape,
|
|
855
|
+
format: renderData.format,
|
|
856
|
+
mips: tex.mips
|
|
857
|
+
});
|
|
858
|
+
if (!layout.ok) {
|
|
859
|
+
return err(
|
|
860
|
+
makeAssetError({
|
|
861
|
+
code: "invalid-source-format",
|
|
862
|
+
expected: layout.error.expected,
|
|
863
|
+
hint: layout.error.hint
|
|
864
|
+
})
|
|
829
865
|
);
|
|
830
|
-
|
|
866
|
+
}
|
|
867
|
+
for (const level of layout.value.levels) {
|
|
868
|
+
for (let image = 0; image < level.imagesPerMip; image++) {
|
|
869
|
+
const imageBytes = level.bytesPerRow * level.rowsPerImage;
|
|
870
|
+
const offset2 = level.byteOffset + image * imageBytes;
|
|
871
|
+
const slice = decoded.bytes.subarray(offset2, offset2 + imageBytes);
|
|
872
|
+
const writeRes = queue.writeTexture(
|
|
873
|
+
{
|
|
874
|
+
texture: gpuTexture,
|
|
875
|
+
mipLevel: level.level,
|
|
876
|
+
origin: { x: 0, y: 0, z: image }
|
|
877
|
+
},
|
|
878
|
+
slice,
|
|
879
|
+
{ offset: 0, bytesPerRow: level.bytesPerRow, rowsPerImage: level.rowsPerImage },
|
|
880
|
+
{
|
|
881
|
+
width: level.physicalWidth,
|
|
882
|
+
height: level.physicalHeight,
|
|
883
|
+
depthOrArrayLayers: 1
|
|
884
|
+
}
|
|
885
|
+
);
|
|
886
|
+
if (!writeRes.ok) return writeRes;
|
|
887
|
+
}
|
|
831
888
|
}
|
|
832
889
|
return ok({ id, tex, levels, gpuTexture });
|
|
833
890
|
}
|
|
834
|
-
const
|
|
891
|
+
const textureLayout = deriveTextureLayout({
|
|
892
|
+
shape: tex.shape,
|
|
893
|
+
format: renderData.format,
|
|
894
|
+
mips: tex.mips
|
|
895
|
+
});
|
|
896
|
+
if (!textureLayout.ok) {
|
|
897
|
+
return err(
|
|
898
|
+
makeAssetError({
|
|
899
|
+
code: "invalid-source-format",
|
|
900
|
+
expected: textureLayout.error.expected,
|
|
901
|
+
hint: textureLayout.error.hint
|
|
902
|
+
})
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
const baseLevel = textureLayout.value.levels[0];
|
|
906
|
+
if (baseLevel === void 0) {
|
|
907
|
+
return err(
|
|
908
|
+
makeAssetError({
|
|
909
|
+
code: "invalid-source-format",
|
|
910
|
+
expected: "texture layout contains a base mip level",
|
|
911
|
+
hint: ASSET_ERROR_HINTS["invalid-source-format"]
|
|
912
|
+
})
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
const bytesPerRow = baseLevel.bytesPerRow;
|
|
835
916
|
const writeTextureRes = queue.writeTexture(
|
|
836
917
|
{ texture: gpuTexture, mipLevel: 0, origin: { x: 0, y: 0, z: 0 } },
|
|
837
918
|
decoded.bytes,
|
|
838
|
-
{ offset: 0, bytesPerRow, rowsPerImage:
|
|
839
|
-
{ width
|
|
919
|
+
{ offset: 0, bytesPerRow, rowsPerImage: height },
|
|
920
|
+
{ width, height, depthOrArrayLayers: renderData.depthOrArrayLayers }
|
|
840
921
|
);
|
|
841
922
|
if (!writeTextureRes.ok) return writeTextureRes;
|
|
842
923
|
return ok({ id, tex, levels, gpuTexture });
|
|
@@ -913,14 +994,18 @@ var GpuResidencyCache = class {
|
|
|
913
994
|
view: void 0,
|
|
914
995
|
faceViews: []
|
|
915
996
|
});
|
|
997
|
+
const sourceData = sourcePod.data instanceof Uint8ClampedArray ? new Uint8Array(
|
|
998
|
+
sourcePod.data.buffer,
|
|
999
|
+
sourcePod.data.byteOffset,
|
|
1000
|
+
sourcePod.data.byteLength
|
|
1001
|
+
) : sourcePod.data;
|
|
916
1002
|
const sourceTex = {
|
|
917
1003
|
kind: "texture",
|
|
918
|
-
width: sourcePod.width,
|
|
919
|
-
height: sourcePod.height,
|
|
1004
|
+
shape: { viewDimension: "2d", extent: { width: sourcePod.width, height: sourcePod.height } },
|
|
920
1005
|
format: sourcePod.format,
|
|
921
|
-
data:
|
|
1006
|
+
data: sourceData,
|
|
922
1007
|
colorSpace: sourcePod.colorSpace,
|
|
923
|
-
|
|
1008
|
+
mips: { kind: "none" }
|
|
924
1009
|
};
|
|
925
1010
|
const projected = (() => {
|
|
926
1011
|
const p = deriveRenderDataCubemap(sourceTex);
|
|
@@ -936,16 +1021,9 @@ var GpuResidencyCache = class {
|
|
|
936
1021
|
);
|
|
937
1022
|
}
|
|
938
1023
|
const tex = projected.needsHalfConversion ? {
|
|
939
|
-
|
|
940
|
-
height: sourceTex.height,
|
|
1024
|
+
shape: sourceTex.shape,
|
|
941
1025
|
format: projected.outputFormat,
|
|
942
|
-
data: halfFloat.f32ToF16Bytes(
|
|
943
|
-
sourceTex.data instanceof Uint8ClampedArray ? new Uint8Array(
|
|
944
|
-
sourceTex.data.buffer,
|
|
945
|
-
sourceTex.data.byteOffset,
|
|
946
|
-
sourceTex.data.byteLength
|
|
947
|
-
) : sourceTex.data
|
|
948
|
-
)} : sourceTex;
|
|
1026
|
+
data: halfFloat.f32ToF16Bytes(sourceTex.data)} : sourceTex;
|
|
949
1027
|
const cubeFaceSize = projected.cubeFaceSize;
|
|
950
1028
|
const outputFormat = this.caps?.rgba16floatRenderable === false ? "rgba8unorm" : tex.format;
|
|
951
1029
|
const device = this.iblDevice;
|
|
@@ -1034,7 +1112,11 @@ var GpuResidencyCache = class {
|
|
|
1034
1112
|
}
|
|
1035
1113
|
const equirectTextureResult = device.createTexture({
|
|
1036
1114
|
label: `equirect-${sourceId}`,
|
|
1037
|
-
size: {
|
|
1115
|
+
size: {
|
|
1116
|
+
width: tex.shape.extent.width,
|
|
1117
|
+
height: tex.shape.extent.height,
|
|
1118
|
+
depthOrArrayLayers: 1
|
|
1119
|
+
},
|
|
1038
1120
|
mipLevelCount: 1,
|
|
1039
1121
|
sampleCount: 1,
|
|
1040
1122
|
dimension: "2d",
|
|
@@ -1055,8 +1137,15 @@ var GpuResidencyCache = class {
|
|
|
1055
1137
|
const equirectWriteResult = device.queue.writeTexture(
|
|
1056
1138
|
{ texture: equirectGpuTex },
|
|
1057
1139
|
tex.data,
|
|
1058
|
-
{
|
|
1059
|
-
|
|
1140
|
+
{
|
|
1141
|
+
bytesPerRow: tex.shape.extent.width * bytesPerPixel,
|
|
1142
|
+
rowsPerImage: tex.shape.extent.height
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
width: tex.shape.extent.width,
|
|
1146
|
+
height: tex.shape.extent.height,
|
|
1147
|
+
depthOrArrayLayers: 1
|
|
1148
|
+
}
|
|
1060
1149
|
);
|
|
1061
1150
|
if (!equirectWriteResult.ok) return recordFailed(equirectWriteResult.error);
|
|
1062
1151
|
const faceBufferResult = createFaceUniformsBuffer(device);
|
|
@@ -1268,14 +1357,13 @@ var GpuResidencyCache = class {
|
|
|
1268
1357
|
}
|
|
1269
1358
|
};
|
|
1270
1359
|
function decodedFromTexture(tex) {
|
|
1271
|
-
const bytes2 = tex.data instanceof Uint8ClampedArray ? new Uint8Array(tex.data.buffer, tex.data.byteOffset, tex.data.byteLength) : tex.data;
|
|
1272
1360
|
return {
|
|
1273
|
-
bytes:
|
|
1274
|
-
width: tex.width,
|
|
1275
|
-
height: tex.height,
|
|
1361
|
+
bytes: tex.data,
|
|
1362
|
+
width: tex.shape.extent.width,
|
|
1363
|
+
height: tex.shape.extent.height,
|
|
1276
1364
|
mime: "image/png",
|
|
1277
1365
|
colorSpace: tex.colorSpace,
|
|
1278
|
-
mipmap: tex.
|
|
1366
|
+
mipmap: tex.mips.kind === "generate"
|
|
1279
1367
|
};
|
|
1280
1368
|
}
|
|
1281
1369
|
|
|
@@ -3619,6 +3707,16 @@ function addTransmissionBackdropTemporalPass(graph, source) {
|
|
|
3619
3707
|
encode: () => void 0
|
|
3620
3708
|
});
|
|
3621
3709
|
}
|
|
3710
|
+
|
|
3711
|
+
// src/typed-shadow-atlas.ts
|
|
3712
|
+
function resolveDirectionalShadowAtlasGrid(cascadeCount) {
|
|
3713
|
+
const count = Math.max(1, Math.min(4, Math.round(cascadeCount)));
|
|
3714
|
+
if (count === 1) return { columns: 1, rows: 1 };
|
|
3715
|
+
if (count === 2) return { columns: 2, rows: 1 };
|
|
3716
|
+
return { columns: 2, rows: 2 };
|
|
3717
|
+
}
|
|
3718
|
+
|
|
3719
|
+
// src/typed-shadow-passes.ts
|
|
3622
3720
|
function recordShadowPass(graph, name, target4, loadOp, encode, executeIf) {
|
|
3623
3721
|
return graph.addRasterPass(name, {
|
|
3624
3722
|
accesses: [{ resource: target4.view, usage: "depth-stencil-write" }],
|
|
@@ -3634,18 +3732,18 @@ function recordShadowPass(graph, name, target4, loadOp, encode, executeIf) {
|
|
|
3634
3732
|
});
|
|
3635
3733
|
}
|
|
3636
3734
|
function addTypedShadowPasses(graph, topology) {
|
|
3637
|
-
const
|
|
3735
|
+
const atlasGrid = resolveDirectionalShadowAtlasGrid(topology.shadow.cascadeCount);
|
|
3638
3736
|
const directional = createRenderPipelineTarget(graph, "directional-shadow-depth", {
|
|
3639
3737
|
format: "depth32float",
|
|
3640
3738
|
size: {
|
|
3641
|
-
width: topology.shadow.mapSize *
|
|
3642
|
-
height: topology.shadow.mapSize *
|
|
3739
|
+
width: topology.shadow.mapSize * atlasGrid.columns,
|
|
3740
|
+
height: topology.shadow.mapSize * atlasGrid.rows
|
|
3643
3741
|
}
|
|
3644
3742
|
});
|
|
3645
3743
|
if (!directional.ok) return directional;
|
|
3646
3744
|
for (let cascade = 0; cascade < topology.shadow.cascadeCount; cascade += 1) {
|
|
3647
|
-
const col = cascade %
|
|
3648
|
-
const row = Math.floor(cascade /
|
|
3745
|
+
const col = cascade % atlasGrid.columns;
|
|
3746
|
+
const row = Math.floor(cascade / atlasGrid.columns);
|
|
3649
3747
|
const added = recordShadowPass(
|
|
3650
3748
|
graph,
|
|
3651
3749
|
`shadowCascade${cascade}`,
|
|
@@ -3747,6 +3845,656 @@ function addTypedShadowPasses(graph, topology) {
|
|
|
3747
3845
|
}
|
|
3748
3846
|
return ok$1({ directional: directional.value, spot: spot.value, point });
|
|
3749
3847
|
}
|
|
3848
|
+
var VOLUME_FROXEL_FORMAT = "rgba8unorm";
|
|
3849
|
+
var VOLUME_HISTORY_FORMAT = "rgba16float";
|
|
3850
|
+
var VOLUME_RESOLVED_FORMAT = "rgba16float";
|
|
3851
|
+
var VOLUME_PACKING_Z = 4;
|
|
3852
|
+
var VOLUMETRIC_FOG_PARAMS_BYTES = 8 * 16;
|
|
3853
|
+
function deriveVolumetricFogExtent(surface, depth = 64, tileSize = 4) {
|
|
3854
|
+
const physicalTileSize = Math.max(2, Math.floor(tileSize / 2));
|
|
3855
|
+
return {
|
|
3856
|
+
width: Math.max(1, Math.ceil(surface.width / physicalTileSize)),
|
|
3857
|
+
height: Math.max(1, Math.ceil(surface.height / physicalTileSize)),
|
|
3858
|
+
depth: Math.max(1, depth)
|
|
3859
|
+
};
|
|
3860
|
+
}
|
|
3861
|
+
function deriveVolumetricFogResolvedExtent(surface, froxelTileSize = 4) {
|
|
3862
|
+
const resolveTileSize = froxelTileSize === 4 ? 8 : Math.max(1, froxelTileSize);
|
|
3863
|
+
return deriveVolumetricFogExtent(surface, 1, resolveTileSize);
|
|
3864
|
+
}
|
|
3865
|
+
function inspectVolumetricFogResources(graph, options = {}) {
|
|
3866
|
+
const volumeResources = graph.resources.filter(
|
|
3867
|
+
(resource) => resource.label === "volume-froxel" || resource.label === "volume-resolved-current" || resource.label === "volume-history" || resource.label === "volume-temporal" || resource.label === "volume-params"
|
|
3868
|
+
);
|
|
3869
|
+
const bytes2 = (label) => volumeResources.filter((resource) => resource.label === label).reduce((total, resource) => total + (resource.byteSize ?? 0), 0);
|
|
3870
|
+
const graphParameterResources = volumeResources.filter(
|
|
3871
|
+
(resource) => resource.label === "volume-params"
|
|
3872
|
+
);
|
|
3873
|
+
const graphParameterBufferCount = graphParameterResources.length;
|
|
3874
|
+
const parameterBufferCount = Math.max(
|
|
3875
|
+
0,
|
|
3876
|
+
Math.floor(options.parameterBufferCount ?? graphParameterBufferCount)
|
|
3877
|
+
);
|
|
3878
|
+
const froxel = volumeResources.find((resource) => resource.label === "volume-froxel");
|
|
3879
|
+
const froxelExtent = froxel?.extent;
|
|
3880
|
+
const resolvedExtent = volumeResources.find(
|
|
3881
|
+
(resource) => resource.label === "volume-resolved-current"
|
|
3882
|
+
)?.extent;
|
|
3883
|
+
const currentBytes = bytes2("volume-resolved-current");
|
|
3884
|
+
const historyBytes = bytes2("volume-history") + bytes2("volume-temporal");
|
|
3885
|
+
const scratchBytes = bytes2("volume-froxel");
|
|
3886
|
+
const bufferBytes = parameterBufferCount * VOLUMETRIC_FOG_PARAMS_BYTES;
|
|
3887
|
+
const physicalAllocations = /* @__PURE__ */ new Set();
|
|
3888
|
+
for (const resource of volumeResources) {
|
|
3889
|
+
if (resource.label === "volume-params") continue;
|
|
3890
|
+
physicalAllocations.add(resource.physicalAllocationKey ?? resource.label);
|
|
3891
|
+
}
|
|
3892
|
+
const totalBytes = currentBytes + historyBytes + scratchBytes + bufferBytes;
|
|
3893
|
+
return Object.freeze({
|
|
3894
|
+
generation: graph.generation,
|
|
3895
|
+
liveResourceCount: volumeResources.length - graphParameterBufferCount + parameterBufferCount,
|
|
3896
|
+
currentBytes,
|
|
3897
|
+
historyBytes,
|
|
3898
|
+
scratchBytes,
|
|
3899
|
+
bufferBytes,
|
|
3900
|
+
totalBytes,
|
|
3901
|
+
physicalResourceCount: physicalAllocations.size + parameterBufferCount,
|
|
3902
|
+
sampleCount: froxelExtent === void 0 ? 0 : froxelExtent.width * froxelExtent.height * froxelExtent.depthOrArrayLayers * VOLUME_PACKING_Z,
|
|
3903
|
+
resolvedPixelCount: resolvedExtent === void 0 ? 0 : resolvedExtent.width * resolvedExtent.height
|
|
3904
|
+
});
|
|
3905
|
+
}
|
|
3906
|
+
function textureDescriptor(format, extent) {
|
|
3907
|
+
return {
|
|
3908
|
+
format,
|
|
3909
|
+
size: {
|
|
3910
|
+
width: extent.width,
|
|
3911
|
+
height: extent.height,
|
|
3912
|
+
depthOrArrayLayers: Math.ceil(extent.depth / VOLUME_PACKING_Z)
|
|
3913
|
+
},
|
|
3914
|
+
dimension: "2d"
|
|
3915
|
+
};
|
|
3916
|
+
}
|
|
3917
|
+
function resolvedDescriptor(extent) {
|
|
3918
|
+
return {
|
|
3919
|
+
format: VOLUME_RESOLVED_FORMAT,
|
|
3920
|
+
size: { width: extent.width, height: extent.height, depthOrArrayLayers: 1 },
|
|
3921
|
+
dimension: "2d"
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
function createVolumetricFogResources(graph, extent = { width: 64, height: 64, depth: 64 }, resolvedExtent) {
|
|
3925
|
+
const effectiveResolvedExtent = resolvedExtent ?? {
|
|
3926
|
+
width: extent.width,
|
|
3927
|
+
height: extent.height,
|
|
3928
|
+
depth: 1
|
|
3929
|
+
};
|
|
3930
|
+
const froxel = graph.createTexture(
|
|
3931
|
+
"volume-froxel",
|
|
3932
|
+
textureDescriptor(VOLUME_FROXEL_FORMAT, extent)
|
|
3933
|
+
);
|
|
3934
|
+
if (!froxel.ok) return froxel;
|
|
3935
|
+
const froxelView = graph.view(froxel.value, {
|
|
3936
|
+
label: "volume-froxel.view",
|
|
3937
|
+
dimension: "2d-array",
|
|
3938
|
+
baseArrayLayer: 0,
|
|
3939
|
+
arrayLayerCount: Math.ceil(extent.depth / VOLUME_PACKING_Z)
|
|
3940
|
+
});
|
|
3941
|
+
if (!froxelView.ok) return froxelView;
|
|
3942
|
+
const resolved2 = graph.createTexture(
|
|
3943
|
+
"volume-resolved-current",
|
|
3944
|
+
resolvedDescriptor(effectiveResolvedExtent)
|
|
3945
|
+
);
|
|
3946
|
+
if (!resolved2.ok) return resolved2;
|
|
3947
|
+
const resolvedView = graph.view(resolved2.value, {
|
|
3948
|
+
label: "volume-resolved-current.view",
|
|
3949
|
+
dimension: "2d"
|
|
3950
|
+
});
|
|
3951
|
+
if (!resolvedView.ok) return resolvedView;
|
|
3952
|
+
const history = graph.createTexture(
|
|
3953
|
+
"volume-history",
|
|
3954
|
+
resolvedDescriptor(effectiveResolvedExtent)
|
|
3955
|
+
);
|
|
3956
|
+
if (!history.ok) return history;
|
|
3957
|
+
const historyView = graph.view(history.value, { label: "volume-history.view", dimension: "2d" });
|
|
3958
|
+
if (!historyView.ok) return historyView;
|
|
3959
|
+
const temporal = graph.createTexture(
|
|
3960
|
+
"volume-temporal",
|
|
3961
|
+
resolvedDescriptor(effectiveResolvedExtent)
|
|
3962
|
+
);
|
|
3963
|
+
if (!temporal.ok) return temporal;
|
|
3964
|
+
const temporalView = graph.view(temporal.value, {
|
|
3965
|
+
label: "volume-temporal.view",
|
|
3966
|
+
dimension: "2d"
|
|
3967
|
+
});
|
|
3968
|
+
if (!temporalView.ok) return temporalView;
|
|
3969
|
+
return ok$1({
|
|
3970
|
+
extent,
|
|
3971
|
+
resolvedExtent: effectiveResolvedExtent,
|
|
3972
|
+
froxel: froxel.value,
|
|
3973
|
+
froxelView: froxelView.value,
|
|
3974
|
+
resolved: resolved2.value,
|
|
3975
|
+
resolvedView: resolvedView.value,
|
|
3976
|
+
history: history.value,
|
|
3977
|
+
historyView: historyView.value,
|
|
3978
|
+
temporal: temporal.value,
|
|
3979
|
+
temporalView: temporalView.value
|
|
3980
|
+
});
|
|
3981
|
+
}
|
|
3982
|
+
|
|
3983
|
+
// src/volume/passes.ts
|
|
3984
|
+
function pipelineError(stage) {
|
|
3985
|
+
return new RhiError({
|
|
3986
|
+
code: "rhi-not-available",
|
|
3987
|
+
expected: `volumetric fog ${stage} pipeline to be available`,
|
|
3988
|
+
hint: "construct the renderer through a backend pack with shader module support"
|
|
3989
|
+
});
|
|
3990
|
+
}
|
|
3991
|
+
function shaderSource(frame, stage) {
|
|
3992
|
+
const source = frame.volumetricFogShaders?.[stage];
|
|
3993
|
+
if (source === void 0) throw pipelineError(`${stage} shader source`);
|
|
3994
|
+
return source;
|
|
3995
|
+
}
|
|
3996
|
+
function computeState(frame, source, label, entries, entryPoint = label) {
|
|
3997
|
+
const factory = frame.runtime.shaderModuleFactory;
|
|
3998
|
+
if (factory === void 0) throw pipelineError(label);
|
|
3999
|
+
const module = factory.createShaderModule({ code: source, label });
|
|
4000
|
+
if (!module.ok) throw module.error;
|
|
4001
|
+
const layout = frame.runtime.device.createBindGroupLayout({ entries });
|
|
4002
|
+
if (!layout.ok) throw layout.error;
|
|
4003
|
+
const pipelineLayout = frame.runtime.device.createPipelineLayout({
|
|
4004
|
+
label: `${label}.layout`,
|
|
4005
|
+
bindGroupLayouts: [layout.value]
|
|
4006
|
+
});
|
|
4007
|
+
if (!pipelineLayout.ok) throw pipelineLayout.error;
|
|
4008
|
+
const pipeline = frame.runtime.device.createComputePipeline({
|
|
4009
|
+
label,
|
|
4010
|
+
layout: pipelineLayout.value,
|
|
4011
|
+
compute: { module: module.value, entryPoint }
|
|
4012
|
+
});
|
|
4013
|
+
if (!pipeline.ok) throw pipeline.error;
|
|
4014
|
+
return { pipeline: pipeline.value, layout: layout.value };
|
|
4015
|
+
}
|
|
4016
|
+
function volumeBindGroup(frame, layout, entries) {
|
|
4017
|
+
const group = frame.runtime.device.createBindGroup({ layout, entries });
|
|
4018
|
+
if (!group.ok) throw group.error;
|
|
4019
|
+
return group.value;
|
|
4020
|
+
}
|
|
4021
|
+
function importVolumeInputs(graph, topology, linearHdr, sceneDepth, directionalShadow, spotShadow, selectedLightKind) {
|
|
4022
|
+
const volume = topology.volumetricFog;
|
|
4023
|
+
if (volume?.enabled !== true || volume.format === void 0 || volume.extent === void 0)
|
|
4024
|
+
return ok$1(void 0);
|
|
4025
|
+
const texture = graph.importTexture(
|
|
4026
|
+
"volume-density",
|
|
4027
|
+
{
|
|
4028
|
+
format: volume.format,
|
|
4029
|
+
size: {
|
|
4030
|
+
width: volume.extent.width,
|
|
4031
|
+
height: volume.extent.height,
|
|
4032
|
+
depthOrArrayLayers: volume.extent.depth
|
|
4033
|
+
},
|
|
4034
|
+
dimension: "3d",
|
|
4035
|
+
usage: GPU_TEXTURE_USAGE_TEXTURE_BINDING
|
|
4036
|
+
},
|
|
4037
|
+
(frame) => {
|
|
4038
|
+
const context = frame.volumetricFog;
|
|
4039
|
+
if (context === void 0) throw pipelineError("density residency");
|
|
4040
|
+
return context.densityTexture;
|
|
4041
|
+
}
|
|
4042
|
+
);
|
|
4043
|
+
if (!texture.ok) return texture;
|
|
4044
|
+
const density = graph.importView(
|
|
4045
|
+
texture.value,
|
|
4046
|
+
{ label: "volume-density.view", dimension: "3d" },
|
|
4047
|
+
(frame) => {
|
|
4048
|
+
const context = frame.volumetricFog;
|
|
4049
|
+
if (context === void 0) throw pipelineError("density view residency");
|
|
4050
|
+
return context.densityView;
|
|
4051
|
+
}
|
|
4052
|
+
);
|
|
4053
|
+
if (!density.ok) return density;
|
|
4054
|
+
const params = graph.importBuffer(
|
|
4055
|
+
"volume-params",
|
|
4056
|
+
{ size: 128, usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST },
|
|
4057
|
+
(frame) => {
|
|
4058
|
+
const context = frame.volumetricFog;
|
|
4059
|
+
if (context === void 0) throw pipelineError("parameter residency");
|
|
4060
|
+
return context.paramsBuffer;
|
|
4061
|
+
}
|
|
4062
|
+
);
|
|
4063
|
+
if (!params.ok) return params;
|
|
4064
|
+
const view = graph.importBuffer(
|
|
4065
|
+
"volume-view",
|
|
4066
|
+
{ size: VIEW_UNIFORM_BYTES, usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST },
|
|
4067
|
+
(frame) => frame.pipelineState.viewUniformBuffer
|
|
4068
|
+
);
|
|
4069
|
+
if (!view.ok) return view;
|
|
4070
|
+
const spotLights = graph.importBuffer(
|
|
4071
|
+
"volume-spot-lights",
|
|
4072
|
+
{
|
|
4073
|
+
size: LIGHT_ARRAY_HEADER_BYTES + LIGHT_ARRAY_MAX_SLOTS * SPOT_LIGHT_STD430_BYTES,
|
|
4074
|
+
usage: GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST
|
|
4075
|
+
},
|
|
4076
|
+
(frame) => frame.pipelineState.spotLightsBuffer
|
|
4077
|
+
);
|
|
4078
|
+
if (!spotLights.ok) return spotLights;
|
|
4079
|
+
const pointLights = graph.importBuffer(
|
|
4080
|
+
"volume-point-lights",
|
|
4081
|
+
{ size: 16 + 4 * 32, usage: GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST },
|
|
4082
|
+
(frame) => frame.pipelineState.pointLightsBuffer
|
|
4083
|
+
);
|
|
4084
|
+
if (!pointLights.ok) return pointLights;
|
|
4085
|
+
let projector;
|
|
4086
|
+
if (volume.projector !== void 0) {
|
|
4087
|
+
const projectorTexture = graph.importTexture(
|
|
4088
|
+
"spot-projector",
|
|
4089
|
+
{
|
|
4090
|
+
format: "rgba8unorm-srgb",
|
|
4091
|
+
size: { width: 128, height: 128, depthOrArrayLayers: 1 },
|
|
4092
|
+
dimension: "2d",
|
|
4093
|
+
usage: GPU_TEXTURE_USAGE_TEXTURE_BINDING
|
|
4094
|
+
},
|
|
4095
|
+
(frame) => {
|
|
4096
|
+
const context = frame.volumetricFog;
|
|
4097
|
+
if (context?.projectorTexture === void 0) throw pipelineError("projector residency");
|
|
4098
|
+
return context.projectorTexture;
|
|
4099
|
+
}
|
|
4100
|
+
);
|
|
4101
|
+
if (!projectorTexture.ok) return projectorTexture;
|
|
4102
|
+
const projectorView = graph.importView(
|
|
4103
|
+
projectorTexture.value,
|
|
4104
|
+
{ label: "spot-projector.view", dimension: "2d" },
|
|
4105
|
+
(frame) => {
|
|
4106
|
+
const context = frame.volumetricFog;
|
|
4107
|
+
if (context?.projectorView === void 0) throw pipelineError("projector view residency");
|
|
4108
|
+
return context.projectorView;
|
|
4109
|
+
}
|
|
4110
|
+
);
|
|
4111
|
+
if (!projectorView.ok) return projectorView;
|
|
4112
|
+
projector = projectorView.value;
|
|
4113
|
+
}
|
|
4114
|
+
return ok$1({
|
|
4115
|
+
linearHdr,
|
|
4116
|
+
sceneDepth,
|
|
4117
|
+
density: density.value,
|
|
4118
|
+
params: params.value,
|
|
4119
|
+
view: view.value,
|
|
4120
|
+
directionalShadow,
|
|
4121
|
+
spotShadow,
|
|
4122
|
+
pointLights: pointLights.value,
|
|
4123
|
+
spotLights: spotLights.value,
|
|
4124
|
+
selectedLightKind: selectedLightKind ?? "directional",
|
|
4125
|
+
...projector === void 0 ? {} : { projector }
|
|
4126
|
+
});
|
|
4127
|
+
}
|
|
4128
|
+
function addAuthoredVolumetricFogPasses(graph, topology, linearHdr, sceneDepth, directionalShadow, spotShadow) {
|
|
4129
|
+
const shadowLightKind = topology.volumetricFog?.spotLightEntity === void 0 ? topology.volumetricFog?.lightKind : "spot";
|
|
4130
|
+
const imported = importVolumeInputs(
|
|
4131
|
+
graph,
|
|
4132
|
+
topology,
|
|
4133
|
+
linearHdr,
|
|
4134
|
+
sceneDepth,
|
|
4135
|
+
directionalShadow,
|
|
4136
|
+
spotShadow,
|
|
4137
|
+
shadowLightKind
|
|
4138
|
+
);
|
|
4139
|
+
if (!imported.ok) return imported;
|
|
4140
|
+
if (imported.value === void 0) return ok$1(void 0);
|
|
4141
|
+
const resources = createVolumetricFogResources(
|
|
4142
|
+
graph,
|
|
4143
|
+
topology.volumetricFog?.froxelExtent ?? { width: 64, height: 64, depth: 64 },
|
|
4144
|
+
topology.volumetricFog?.resolvedExtent
|
|
4145
|
+
);
|
|
4146
|
+
if (!resources.ok) return resources;
|
|
4147
|
+
return addVolumetricFogPasses(graph, { ...imported.value, resources: resources.value });
|
|
4148
|
+
}
|
|
4149
|
+
function addVolumetricFogPasses(graph, inputs) {
|
|
4150
|
+
const dispatchX = Math.ceil(inputs.resources.extent.width / 8);
|
|
4151
|
+
const dispatchY = Math.ceil(inputs.resources.extent.height / 8);
|
|
4152
|
+
const packedLayers = Math.ceil(inputs.resources.extent.depth / VOLUME_PACKING_Z);
|
|
4153
|
+
const resolvedDispatchX = Math.ceil(inputs.resources.resolvedExtent.width / 8);
|
|
4154
|
+
const resolvedDispatchY = Math.ceil(inputs.resources.resolvedExtent.height / 8);
|
|
4155
|
+
let inject;
|
|
4156
|
+
let integrate;
|
|
4157
|
+
let temporal;
|
|
4158
|
+
let composite;
|
|
4159
|
+
let compositeLayout;
|
|
4160
|
+
const injected = graph.addComputePass("volume-inject", {
|
|
4161
|
+
accesses: [
|
|
4162
|
+
{ resource: inputs.params, usage: "uniform-read" },
|
|
4163
|
+
{ resource: inputs.view, usage: "uniform-read" },
|
|
4164
|
+
{
|
|
4165
|
+
resource: inputs.selectedLightKind === "spot" ? inputs.spotShadow : inputs.directionalShadow,
|
|
4166
|
+
usage: "sampled-read"
|
|
4167
|
+
},
|
|
4168
|
+
{ resource: inputs.spotLights, usage: "storage-read" },
|
|
4169
|
+
{ resource: inputs.pointLights, usage: "storage-read" },
|
|
4170
|
+
...inputs.projector === void 0 ? [] : [{ resource: inputs.projector, usage: "sampled-read" }],
|
|
4171
|
+
{ resource: inputs.resources.froxelView, usage: "storage-write" }
|
|
4172
|
+
],
|
|
4173
|
+
encode: ({ pass, frame, resources }) => {
|
|
4174
|
+
inject ??= computeState(frame, shaderSource(frame, "inject"), "volume_inject", [
|
|
4175
|
+
{ binding: 0, visibility: 4, buffer: { type: "uniform" } },
|
|
4176
|
+
{ binding: 3, visibility: 4, texture: { sampleType: "depth", viewDimension: "2d" } },
|
|
4177
|
+
{ binding: 4, visibility: 4, sampler: { type: "comparison" } },
|
|
4178
|
+
{ binding: 5, visibility: 4, buffer: { type: "uniform" } },
|
|
4179
|
+
{
|
|
4180
|
+
binding: 6,
|
|
4181
|
+
visibility: 4,
|
|
4182
|
+
storageTexture: {
|
|
4183
|
+
access: "write-only",
|
|
4184
|
+
format: VOLUME_FROXEL_FORMAT,
|
|
4185
|
+
viewDimension: "2d-array"
|
|
4186
|
+
}
|
|
4187
|
+
},
|
|
4188
|
+
{ binding: 7, visibility: 4, buffer: { type: "read-only-storage" } },
|
|
4189
|
+
{ binding: 8, visibility: 4, buffer: { type: "read-only-storage" } }
|
|
4190
|
+
]);
|
|
4191
|
+
const shadow = resources.textureView(
|
|
4192
|
+
inputs.selectedLightKind === "spot" ? inputs.spotShadow : inputs.directionalShadow
|
|
4193
|
+
);
|
|
4194
|
+
const params = resources.buffer(inputs.params);
|
|
4195
|
+
const view = resources.buffer(inputs.view);
|
|
4196
|
+
const spotLights = resources.buffer(inputs.spotLights);
|
|
4197
|
+
const pointLights = resources.buffer(inputs.pointLights);
|
|
4198
|
+
const froxel = resources.textureView(inputs.resources.froxelView);
|
|
4199
|
+
if (!shadow.ok) throw shadow.error;
|
|
4200
|
+
if (!params.ok) throw params.error;
|
|
4201
|
+
if (!view.ok) throw view.error;
|
|
4202
|
+
if (!spotLights.ok) throw spotLights.error;
|
|
4203
|
+
if (!pointLights.ok) throw pointLights.error;
|
|
4204
|
+
if (!froxel.ok) throw froxel.error;
|
|
4205
|
+
const shadowSampler = frame.runtime.device.createSampler({ compare: "less" });
|
|
4206
|
+
if (!shadowSampler.ok) throw shadowSampler.error;
|
|
4207
|
+
const bindings = volumeBindGroup(frame, inject.layout, [
|
|
4208
|
+
{ binding: 0, resource: { kind: "buffer", value: { buffer: view.value } } },
|
|
4209
|
+
{ binding: 3, resource: { kind: "textureView", value: shadow.value } },
|
|
4210
|
+
{ binding: 4, resource: { kind: "sampler", value: shadowSampler.value } },
|
|
4211
|
+
{ binding: 5, resource: { kind: "buffer", value: { buffer: params.value } } },
|
|
4212
|
+
{ binding: 6, resource: { kind: "textureView", value: froxel.value } },
|
|
4213
|
+
{ binding: 7, resource: { kind: "buffer", value: { buffer: spotLights.value } } },
|
|
4214
|
+
{ binding: 8, resource: { kind: "buffer", value: { buffer: pointLights.value } } }
|
|
4215
|
+
]);
|
|
4216
|
+
pass.setPipeline(inject.pipeline);
|
|
4217
|
+
pass.setBindGroup(0, bindings);
|
|
4218
|
+
pass.dispatchWorkgroups(dispatchX, dispatchY, packedLayers);
|
|
4219
|
+
}
|
|
4220
|
+
});
|
|
4221
|
+
if (!injected.ok) return injected;
|
|
4222
|
+
const integratePass = graph.addComputePass("volume-integrate", {
|
|
4223
|
+
accesses: [
|
|
4224
|
+
{ resource: inputs.resources.froxelView, usage: "sampled-read" },
|
|
4225
|
+
{ resource: inputs.sceneDepth, usage: "sampled-read" },
|
|
4226
|
+
{ resource: inputs.density, usage: "sampled-read" },
|
|
4227
|
+
{ resource: inputs.resources.resolvedView, usage: "storage-write" },
|
|
4228
|
+
{ resource: inputs.resources.historyView, usage: "storage-write" },
|
|
4229
|
+
{ resource: inputs.resources.temporalView, usage: "storage-write" },
|
|
4230
|
+
{ resource: inputs.params, usage: "uniform-read" },
|
|
4231
|
+
{ resource: inputs.view, usage: "uniform-read" },
|
|
4232
|
+
{ resource: inputs.spotLights, usage: "storage-read" },
|
|
4233
|
+
{ resource: inputs.pointLights, usage: "storage-read" },
|
|
4234
|
+
...inputs.projector === void 0 ? [] : [{ resource: inputs.projector, usage: "sampled-read" }]
|
|
4235
|
+
],
|
|
4236
|
+
encode: ({ pass, frame, resources }) => {
|
|
4237
|
+
integrate ??= computeState(frame, shaderSource(frame, "integrate"), "volume_integrate", [
|
|
4238
|
+
{ binding: 0, visibility: 4, texture: { sampleType: "float", viewDimension: "2d-array" } },
|
|
4239
|
+
{ binding: 1, visibility: 4, texture: { sampleType: "depth", viewDimension: "2d" } },
|
|
4240
|
+
{
|
|
4241
|
+
binding: 2,
|
|
4242
|
+
visibility: 4,
|
|
4243
|
+
storageTexture: {
|
|
4244
|
+
access: "write-only",
|
|
4245
|
+
format: VOLUME_RESOLVED_FORMAT,
|
|
4246
|
+
viewDimension: "2d"
|
|
4247
|
+
}
|
|
4248
|
+
},
|
|
4249
|
+
{ binding: 3, visibility: 4, buffer: { type: "uniform" } },
|
|
4250
|
+
{ binding: 4, visibility: 4, buffer: { type: "uniform" } },
|
|
4251
|
+
{ binding: 5, visibility: 4, sampler: { type: "filtering" } },
|
|
4252
|
+
{ binding: 6, visibility: 4, texture: { sampleType: "float", viewDimension: "3d" } },
|
|
4253
|
+
{ binding: 7, visibility: 4, sampler: { type: "filtering" } },
|
|
4254
|
+
{
|
|
4255
|
+
binding: 8,
|
|
4256
|
+
visibility: 4,
|
|
4257
|
+
storageTexture: {
|
|
4258
|
+
access: "write-only",
|
|
4259
|
+
format: VOLUME_HISTORY_FORMAT,
|
|
4260
|
+
viewDimension: "2d"
|
|
4261
|
+
}
|
|
4262
|
+
},
|
|
4263
|
+
{
|
|
4264
|
+
binding: 9,
|
|
4265
|
+
visibility: 4,
|
|
4266
|
+
storageTexture: {
|
|
4267
|
+
access: "write-only",
|
|
4268
|
+
format: VOLUME_HISTORY_FORMAT,
|
|
4269
|
+
viewDimension: "2d"
|
|
4270
|
+
}
|
|
4271
|
+
},
|
|
4272
|
+
{ binding: 10, visibility: 4, buffer: { type: "read-only-storage" } },
|
|
4273
|
+
{ binding: 11, visibility: 4, buffer: { type: "read-only-storage" } },
|
|
4274
|
+
{ binding: 12, visibility: 4, texture: { sampleType: "float", viewDimension: "2d" } },
|
|
4275
|
+
{ binding: 13, visibility: 4, sampler: { type: "filtering" } }
|
|
4276
|
+
]);
|
|
4277
|
+
const froxel = resources.textureView(inputs.resources.froxelView);
|
|
4278
|
+
const depth = resources.textureView(inputs.sceneDepth);
|
|
4279
|
+
const density = resources.textureView(inputs.density);
|
|
4280
|
+
const resolved2 = resources.textureView(inputs.resources.resolvedView);
|
|
4281
|
+
const historySeed = resources.textureView(inputs.resources.historyView);
|
|
4282
|
+
const temporalSeed = resources.textureView(inputs.resources.temporalView);
|
|
4283
|
+
const params = resources.buffer(inputs.params);
|
|
4284
|
+
const view = resources.buffer(inputs.view);
|
|
4285
|
+
const spotLights = resources.buffer(inputs.spotLights);
|
|
4286
|
+
const pointLights = resources.buffer(inputs.pointLights);
|
|
4287
|
+
const projector = inputs.projector === void 0 ? void 0 : resources.textureView(inputs.projector);
|
|
4288
|
+
if (!froxel.ok) throw froxel.error;
|
|
4289
|
+
if (!depth.ok) throw depth.error;
|
|
4290
|
+
if (!density.ok) throw density.error;
|
|
4291
|
+
if (!resolved2.ok) throw resolved2.error;
|
|
4292
|
+
if (!historySeed.ok) throw historySeed.error;
|
|
4293
|
+
if (!temporalSeed.ok) throw temporalSeed.error;
|
|
4294
|
+
if (!params.ok) throw params.error;
|
|
4295
|
+
if (!view.ok) throw view.error;
|
|
4296
|
+
if (!spotLights.ok) throw spotLights.error;
|
|
4297
|
+
if (!pointLights.ok) throw pointLights.error;
|
|
4298
|
+
if (projector !== void 0 && !projector.ok) throw projector.error;
|
|
4299
|
+
const sampler = frame.runtime.device.createSampler({
|
|
4300
|
+
minFilter: "linear",
|
|
4301
|
+
magFilter: "linear"
|
|
4302
|
+
});
|
|
4303
|
+
if (!sampler.ok) throw sampler.error;
|
|
4304
|
+
const densitySampler = frame.runtime.device.createSampler({
|
|
4305
|
+
minFilter: "linear",
|
|
4306
|
+
magFilter: "linear",
|
|
4307
|
+
addressModeU: "repeat",
|
|
4308
|
+
addressModeV: "repeat",
|
|
4309
|
+
addressModeW: "repeat"
|
|
4310
|
+
});
|
|
4311
|
+
if (!densitySampler.ok) throw densitySampler.error;
|
|
4312
|
+
const bindings = volumeBindGroup(frame, integrate.layout, [
|
|
4313
|
+
{ binding: 0, resource: { kind: "textureView", value: froxel.value } },
|
|
4314
|
+
{ binding: 1, resource: { kind: "textureView", value: depth.value } },
|
|
4315
|
+
{ binding: 2, resource: { kind: "textureView", value: resolved2.value } },
|
|
4316
|
+
{ binding: 3, resource: { kind: "buffer", value: { buffer: params.value } } },
|
|
4317
|
+
{ binding: 4, resource: { kind: "buffer", value: { buffer: view.value } } },
|
|
4318
|
+
{ binding: 5, resource: { kind: "sampler", value: sampler.value } },
|
|
4319
|
+
{ binding: 6, resource: { kind: "textureView", value: density.value } },
|
|
4320
|
+
{ binding: 7, resource: { kind: "sampler", value: densitySampler.value } },
|
|
4321
|
+
{ binding: 8, resource: { kind: "textureView", value: historySeed.value } },
|
|
4322
|
+
{ binding: 9, resource: { kind: "textureView", value: temporalSeed.value } },
|
|
4323
|
+
{ binding: 10, resource: { kind: "buffer", value: { buffer: spotLights.value } } },
|
|
4324
|
+
{ binding: 11, resource: { kind: "buffer", value: { buffer: pointLights.value } } },
|
|
4325
|
+
{
|
|
4326
|
+
binding: 12,
|
|
4327
|
+
resource: {
|
|
4328
|
+
kind: "textureView",
|
|
4329
|
+
value: projector === void 0 ? frame.pipelineState.defaultWhiteTextureView : projector.value
|
|
4330
|
+
}
|
|
4331
|
+
},
|
|
4332
|
+
{
|
|
4333
|
+
binding: 13,
|
|
4334
|
+
resource: {
|
|
4335
|
+
kind: "sampler",
|
|
4336
|
+
value: frame.volumetricFog?.projectorSampler ?? frame.pipelineState.defaultSampler
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
]);
|
|
4340
|
+
pass.setPipeline(integrate.pipeline);
|
|
4341
|
+
pass.setBindGroup(0, bindings);
|
|
4342
|
+
pass.dispatchWorkgroups(resolvedDispatchX, resolvedDispatchY, 1);
|
|
4343
|
+
}
|
|
4344
|
+
});
|
|
4345
|
+
if (!integratePass.ok) return integratePass;
|
|
4346
|
+
const temporalPass = graph.addComputePass("volume-temporal", {
|
|
4347
|
+
accesses: [
|
|
4348
|
+
{ resource: inputs.resources.resolvedView, usage: "sampled-read" },
|
|
4349
|
+
{ resource: inputs.resources.historyView, usage: "sampled-storage-read-write" },
|
|
4350
|
+
{ resource: inputs.resources.temporalView, usage: "sampled-storage-read-write" },
|
|
4351
|
+
{ resource: inputs.params, usage: "uniform-read" },
|
|
4352
|
+
{ resource: inputs.view, usage: "uniform-read" }
|
|
4353
|
+
],
|
|
4354
|
+
encode: ({ pass, frame, resources }) => {
|
|
4355
|
+
temporal ??= computeState(frame, shaderSource(frame, "temporal"), "volume_temporal", [
|
|
4356
|
+
{ binding: 0, visibility: 4, texture: { sampleType: "float", viewDimension: "2d" } },
|
|
4357
|
+
{ binding: 1, visibility: 4, texture: { sampleType: "float", viewDimension: "2d" } },
|
|
4358
|
+
{
|
|
4359
|
+
binding: 2,
|
|
4360
|
+
visibility: 4,
|
|
4361
|
+
storageTexture: {
|
|
4362
|
+
access: "write-only",
|
|
4363
|
+
format: VOLUME_HISTORY_FORMAT,
|
|
4364
|
+
viewDimension: "2d"
|
|
4365
|
+
}
|
|
4366
|
+
},
|
|
4367
|
+
{ binding: 3, visibility: 4, buffer: { type: "uniform" } },
|
|
4368
|
+
{ binding: 4, visibility: 4, buffer: { type: "uniform" } }
|
|
4369
|
+
]);
|
|
4370
|
+
const current = resources.textureView(inputs.resources.resolvedView);
|
|
4371
|
+
const history = resources.textureView(
|
|
4372
|
+
frame.volumetricFog?.historyReadSlot === 0 ? inputs.resources.historyView : inputs.resources.temporalView
|
|
4373
|
+
);
|
|
4374
|
+
const pending = resources.textureView(
|
|
4375
|
+
frame.volumetricFog?.historyWriteSlot === 0 ? inputs.resources.historyView : inputs.resources.temporalView
|
|
4376
|
+
);
|
|
4377
|
+
const params = resources.buffer(inputs.params);
|
|
4378
|
+
const view = resources.buffer(inputs.view);
|
|
4379
|
+
if (!current.ok) throw current.error;
|
|
4380
|
+
if (!history.ok) throw history.error;
|
|
4381
|
+
if (!pending.ok) throw pending.error;
|
|
4382
|
+
if (!params.ok) throw params.error;
|
|
4383
|
+
if (!view.ok) throw view.error;
|
|
4384
|
+
const bindings = volumeBindGroup(frame, temporal.layout, [
|
|
4385
|
+
{ binding: 0, resource: { kind: "textureView", value: current.value } },
|
|
4386
|
+
{ binding: 1, resource: { kind: "textureView", value: history.value } },
|
|
4387
|
+
{ binding: 2, resource: { kind: "textureView", value: pending.value } },
|
|
4388
|
+
{ binding: 3, resource: { kind: "buffer", value: { buffer: params.value } } },
|
|
4389
|
+
{ binding: 4, resource: { kind: "buffer", value: { buffer: view.value } } }
|
|
4390
|
+
]);
|
|
4391
|
+
pass.setPipeline(temporal.pipeline);
|
|
4392
|
+
pass.setBindGroup(0, bindings);
|
|
4393
|
+
pass.dispatchWorkgroups(resolvedDispatchX, resolvedDispatchY, 1);
|
|
4394
|
+
}
|
|
4395
|
+
});
|
|
4396
|
+
if (!temporalPass.ok) return temporalPass;
|
|
4397
|
+
return graph.addRasterPass("volume-composite", {
|
|
4398
|
+
accesses: [
|
|
4399
|
+
{ resource: inputs.resources.historyView, usage: "sampled-read" },
|
|
4400
|
+
{ resource: inputs.resources.temporalView, usage: "sampled-read" },
|
|
4401
|
+
{ resource: inputs.sceneDepth, usage: "sampled-read" },
|
|
4402
|
+
{ resource: inputs.view, usage: "uniform-read" },
|
|
4403
|
+
{ resource: inputs.linearHdr.view, usage: "color-attachment" }
|
|
4404
|
+
],
|
|
4405
|
+
colorAttachments: [
|
|
4406
|
+
{
|
|
4407
|
+
view: inputs.linearHdr.view,
|
|
4408
|
+
loadOp: "load",
|
|
4409
|
+
storeOp: "store",
|
|
4410
|
+
clearValue: { r: 0, g: 0, b: 0, a: 1 }
|
|
4411
|
+
}
|
|
4412
|
+
],
|
|
4413
|
+
encode: ({ pass, frame, resources }) => {
|
|
4414
|
+
composite ??= (() => {
|
|
4415
|
+
const factory = frame.runtime.shaderModuleFactory;
|
|
4416
|
+
if (factory === void 0) throw pipelineError("volume_composite");
|
|
4417
|
+
const module = factory.createShaderModule({
|
|
4418
|
+
code: shaderSource(frame, "composite"),
|
|
4419
|
+
label: "volume_composite"
|
|
4420
|
+
});
|
|
4421
|
+
if (!module.ok) throw module.error;
|
|
4422
|
+
const layout = frame.runtime.device.createBindGroupLayout({
|
|
4423
|
+
label: "volume_composite.bind-group-layout",
|
|
4424
|
+
entries: [
|
|
4425
|
+
{
|
|
4426
|
+
binding: 0,
|
|
4427
|
+
visibility: 2 | 1,
|
|
4428
|
+
texture: { sampleType: "float", viewDimension: "2d" }
|
|
4429
|
+
},
|
|
4430
|
+
{ binding: 1, visibility: 2 | 1, sampler: { type: "filtering" } },
|
|
4431
|
+
{
|
|
4432
|
+
binding: 2,
|
|
4433
|
+
visibility: 2 | 1,
|
|
4434
|
+
texture: { sampleType: "depth", viewDimension: "2d" }
|
|
4435
|
+
},
|
|
4436
|
+
{ binding: 3, visibility: 2 | 1, buffer: { type: "uniform" } }
|
|
4437
|
+
]
|
|
4438
|
+
});
|
|
4439
|
+
if (!layout.ok) throw layout.error;
|
|
4440
|
+
const pipelineLayout = frame.runtime.device.createPipelineLayout({
|
|
4441
|
+
label: "volume_composite.layout",
|
|
4442
|
+
bindGroupLayouts: [layout.value]
|
|
4443
|
+
});
|
|
4444
|
+
if (!pipelineLayout.ok) throw pipelineLayout.error;
|
|
4445
|
+
const created = frame.runtime.device.createRenderPipeline({
|
|
4446
|
+
label: "volume_composite",
|
|
4447
|
+
layout: pipelineLayout.value,
|
|
4448
|
+
vertex: { module: module.value, entryPoint: "volume_vs", buffers: [] },
|
|
4449
|
+
fragment: {
|
|
4450
|
+
module: module.value,
|
|
4451
|
+
entryPoint: "volume_fs",
|
|
4452
|
+
targets: [
|
|
4453
|
+
{
|
|
4454
|
+
format: inputs.linearHdr.format,
|
|
4455
|
+
blend: {
|
|
4456
|
+
color: { srcFactor: "one", dstFactor: "one-minus-src-alpha", operation: "add" },
|
|
4457
|
+
alpha: { srcFactor: "one", dstFactor: "one-minus-src-alpha", operation: "add" }
|
|
4458
|
+
}
|
|
4459
|
+
}
|
|
4460
|
+
]
|
|
4461
|
+
},
|
|
4462
|
+
primitive: { topology: "triangle-list" }
|
|
4463
|
+
});
|
|
4464
|
+
if (!created.ok) throw created.error;
|
|
4465
|
+
compositeLayout = layout.value;
|
|
4466
|
+
return created.value;
|
|
4467
|
+
})();
|
|
4468
|
+
const resolved2 = resources.textureView(
|
|
4469
|
+
frame.volumetricFog?.historyWriteSlot === 0 ? inputs.resources.historyView : inputs.resources.temporalView
|
|
4470
|
+
);
|
|
4471
|
+
const depth = resources.textureView(inputs.sceneDepth);
|
|
4472
|
+
const view = resources.buffer(inputs.view);
|
|
4473
|
+
if (!resolved2.ok) throw resolved2.error;
|
|
4474
|
+
if (!depth.ok) throw depth.error;
|
|
4475
|
+
if (!view.ok) throw view.error;
|
|
4476
|
+
const sampler = frame.runtime.device.createSampler({
|
|
4477
|
+
label: "volume_composite.sampler",
|
|
4478
|
+
minFilter: "linear",
|
|
4479
|
+
magFilter: "linear"
|
|
4480
|
+
});
|
|
4481
|
+
if (!sampler.ok) throw sampler.error;
|
|
4482
|
+
const bindings = frame.runtime.device.createBindGroup({
|
|
4483
|
+
layout: compositeLayout,
|
|
4484
|
+
entries: [
|
|
4485
|
+
{ binding: 0, resource: { kind: "textureView", value: resolved2.value } },
|
|
4486
|
+
{ binding: 1, resource: { kind: "sampler", value: sampler.value } },
|
|
4487
|
+
{ binding: 2, resource: { kind: "textureView", value: depth.value } },
|
|
4488
|
+
{ binding: 3, resource: { kind: "buffer", value: { buffer: view.value } } }
|
|
4489
|
+
]
|
|
4490
|
+
});
|
|
4491
|
+
if (!bindings.ok) throw bindings.error;
|
|
4492
|
+
pass.setPipeline(composite);
|
|
4493
|
+
pass.setBindGroup(0, bindings.value);
|
|
4494
|
+
pass.draw(3);
|
|
4495
|
+
}
|
|
4496
|
+
});
|
|
4497
|
+
}
|
|
3750
4498
|
function addTypedDebugOverlayPass(graph, output) {
|
|
3751
4499
|
return graph.addRasterPass("debug-overlay", {
|
|
3752
4500
|
accesses: [{ resource: output.view, usage: "color-attachment" }],
|
|
@@ -4218,8 +4966,26 @@ function buildUrp(context, topology) {
|
|
|
4218
4966
|
const temporal2 = addTransmissionBackdropTemporalPass(graph, sceneResolved.value);
|
|
4219
4967
|
if (!temporal2.ok) return temporal2;
|
|
4220
4968
|
}
|
|
4969
|
+
if (topology.volumetricFog?.enabled === true) {
|
|
4970
|
+
const depthSample = graph.view(depth.value.texture, {
|
|
4971
|
+
label: "scene-depth.sample",
|
|
4972
|
+
dimension: "2d",
|
|
4973
|
+
aspect: "depth-only"
|
|
4974
|
+
});
|
|
4975
|
+
if (!depthSample.ok) return depthSample;
|
|
4976
|
+
const volume = addAuthoredVolumetricFogPasses(
|
|
4977
|
+
graph,
|
|
4978
|
+
topology,
|
|
4979
|
+
sceneResolved.value,
|
|
4980
|
+
depthSample.value,
|
|
4981
|
+
shadows.value.directional.view,
|
|
4982
|
+
shadows.value.spot.view
|
|
4983
|
+
);
|
|
4984
|
+
if (!volume.ok) return volume;
|
|
4985
|
+
}
|
|
4221
4986
|
const features = context.contributeFeatures([
|
|
4222
4987
|
{
|
|
4988
|
+
name: "linear-hdr",
|
|
4223
4989
|
kind: "scene-color",
|
|
4224
4990
|
texture: scene.value.texture,
|
|
4225
4991
|
view: scene.value.view,
|
|
@@ -4653,8 +5419,26 @@ function buildHdrp(context, topology) {
|
|
|
4653
5419
|
const temporal2 = addTransmissionBackdropTemporalPass(graph, scene.value);
|
|
4654
5420
|
if (!temporal2.ok) return temporal2;
|
|
4655
5421
|
}
|
|
5422
|
+
if (topology.volumetricFog?.enabled === true) {
|
|
5423
|
+
const depthSample = graph.view(depth.value.texture, {
|
|
5424
|
+
label: "scene-depth.sample",
|
|
5425
|
+
dimension: "2d",
|
|
5426
|
+
aspect: "depth-only"
|
|
5427
|
+
});
|
|
5428
|
+
if (!depthSample.ok) return depthSample;
|
|
5429
|
+
const volume = addAuthoredVolumetricFogPasses(
|
|
5430
|
+
graph,
|
|
5431
|
+
topology,
|
|
5432
|
+
scene.value,
|
|
5433
|
+
depthSample.value,
|
|
5434
|
+
shadows.value.directional.view,
|
|
5435
|
+
shadows.value.spot.view
|
|
5436
|
+
);
|
|
5437
|
+
if (!volume.ok) return volume;
|
|
5438
|
+
}
|
|
4656
5439
|
const features = context.contributeFeatures([
|
|
4657
5440
|
{
|
|
5441
|
+
name: "linear-hdr",
|
|
4658
5442
|
kind: "scene-color",
|
|
4659
5443
|
texture: scene.value.texture,
|
|
4660
5444
|
view: scene.value.view,
|
|
@@ -4892,8 +5676,180 @@ function buildPipelineForMaterialShader(id, entry, ctx, renderState, geometry, v
|
|
|
4892
5676
|
})
|
|
4893
5677
|
);
|
|
4894
5678
|
}
|
|
4895
|
-
return pipelineResult;
|
|
4896
|
-
}
|
|
5679
|
+
return pipelineResult;
|
|
5680
|
+
}
|
|
5681
|
+
var MAX_VOLUME_PASSES = 4;
|
|
5682
|
+
var QUERY_COUNT = MAX_VOLUME_PASSES * 2;
|
|
5683
|
+
var QUERY_RESULT_BYTES = 8;
|
|
5684
|
+
var READBACK_BYTES = QUERY_COUNT * QUERY_RESULT_BYTES;
|
|
5685
|
+
var PASS_NAMES = [
|
|
5686
|
+
"volume-inject",
|
|
5687
|
+
"volume-integrate",
|
|
5688
|
+
"volume-temporal",
|
|
5689
|
+
"volume-composite"
|
|
5690
|
+
];
|
|
5691
|
+
function unavailable(reason) {
|
|
5692
|
+
return Object.freeze({ status: "unavailable", reason });
|
|
5693
|
+
}
|
|
5694
|
+
var GpuTimingCapture = class _GpuTimingCapture {
|
|
5695
|
+
#device;
|
|
5696
|
+
#querySet;
|
|
5697
|
+
#resolveBuffer;
|
|
5698
|
+
#readbackBuffer;
|
|
5699
|
+
#periodNanoseconds;
|
|
5700
|
+
#started = /* @__PURE__ */ new Map();
|
|
5701
|
+
#finished = /* @__PURE__ */ new Map();
|
|
5702
|
+
#resolved = false;
|
|
5703
|
+
#submitted = false;
|
|
5704
|
+
#discarded = false;
|
|
5705
|
+
#disposed = false;
|
|
5706
|
+
#observation;
|
|
5707
|
+
constructor(device, querySet, resolveBuffer, readbackBuffer, periodNanoseconds) {
|
|
5708
|
+
this.#device = device;
|
|
5709
|
+
this.#querySet = querySet;
|
|
5710
|
+
this.#resolveBuffer = resolveBuffer;
|
|
5711
|
+
this.#readbackBuffer = readbackBuffer;
|
|
5712
|
+
this.#periodNanoseconds = periodNanoseconds;
|
|
5713
|
+
}
|
|
5714
|
+
static create(device) {
|
|
5715
|
+
const period = device.caps.timestampPeriodNanoseconds;
|
|
5716
|
+
if (!device.caps.timestampQuery || period === null || !Number.isFinite(period) || period <= 0) {
|
|
5717
|
+
return ok$1(void 0);
|
|
5718
|
+
}
|
|
5719
|
+
const querySet = device.createQuerySet({
|
|
5720
|
+
label: "volume-timing",
|
|
5721
|
+
type: "timestamp",
|
|
5722
|
+
count: QUERY_COUNT
|
|
5723
|
+
});
|
|
5724
|
+
if (!querySet.ok) return querySet;
|
|
5725
|
+
const resolveBuffer = device.createBuffer({
|
|
5726
|
+
label: "volume-timing.resolve",
|
|
5727
|
+
size: READBACK_BYTES,
|
|
5728
|
+
usage: GPU_BUFFER_USAGE_QUERY_RESOLVE | GPU_BUFFER_USAGE_COPY_SRC
|
|
5729
|
+
});
|
|
5730
|
+
if (!resolveBuffer.ok) {
|
|
5731
|
+
device.destroyQuerySet(querySet.value);
|
|
5732
|
+
return resolveBuffer;
|
|
5733
|
+
}
|
|
5734
|
+
const readbackBuffer = device.createBuffer({
|
|
5735
|
+
label: "volume-timing.readback",
|
|
5736
|
+
size: READBACK_BYTES,
|
|
5737
|
+
usage: GPU_BUFFER_USAGE_MAP_READ | GPU_BUFFER_USAGE_COPY_DST
|
|
5738
|
+
});
|
|
5739
|
+
if (!readbackBuffer.ok) {
|
|
5740
|
+
device.destroyBuffer(resolveBuffer.value);
|
|
5741
|
+
device.destroyQuerySet(querySet.value);
|
|
5742
|
+
return readbackBuffer;
|
|
5743
|
+
}
|
|
5744
|
+
return ok$1(
|
|
5745
|
+
new _GpuTimingCapture(
|
|
5746
|
+
device,
|
|
5747
|
+
querySet.value,
|
|
5748
|
+
resolveBuffer.value,
|
|
5749
|
+
readbackBuffer.value,
|
|
5750
|
+
period
|
|
5751
|
+
)
|
|
5752
|
+
);
|
|
5753
|
+
}
|
|
5754
|
+
beforePass(name, encoder) {
|
|
5755
|
+
const index = PASS_NAMES.indexOf(name);
|
|
5756
|
+
if (index < 0 || this.#started.has(name)) return;
|
|
5757
|
+
this.#started.set(name, index);
|
|
5758
|
+
encoder.writeTimestamp(this.#querySet, index * 2);
|
|
5759
|
+
}
|
|
5760
|
+
afterPass(name, encoder) {
|
|
5761
|
+
const index = this.#started.get(name);
|
|
5762
|
+
if (index === void 0 || this.#finished.has(name)) return;
|
|
5763
|
+
this.#finished.set(name, index);
|
|
5764
|
+
encoder.writeTimestamp(this.#querySet, index * 2 + 1);
|
|
5765
|
+
}
|
|
5766
|
+
resolve(encoder) {
|
|
5767
|
+
if (this.#started.size === 0) {
|
|
5768
|
+
this.#resolved = true;
|
|
5769
|
+
return ok$1(void 0);
|
|
5770
|
+
}
|
|
5771
|
+
const resolved2 = encoder.resolveQuerySet(
|
|
5772
|
+
this.#querySet,
|
|
5773
|
+
0,
|
|
5774
|
+
QUERY_COUNT,
|
|
5775
|
+
this.#resolveBuffer,
|
|
5776
|
+
0
|
|
5777
|
+
);
|
|
5778
|
+
if (!resolved2.ok) return resolved2;
|
|
5779
|
+
encoder.copyBufferToBuffer(this.#resolveBuffer, this.#readbackBuffer, READBACK_BYTES);
|
|
5780
|
+
this.#resolved = true;
|
|
5781
|
+
return ok$1(void 0);
|
|
5782
|
+
}
|
|
5783
|
+
markSubmitted() {
|
|
5784
|
+
if (!this.#discarded) this.#submitted = true;
|
|
5785
|
+
}
|
|
5786
|
+
discard() {
|
|
5787
|
+
if (this.#discarded) return;
|
|
5788
|
+
this.#discarded = true;
|
|
5789
|
+
this.#resolved = true;
|
|
5790
|
+
this.#submitted = false;
|
|
5791
|
+
this.#dispose();
|
|
5792
|
+
}
|
|
5793
|
+
observation() {
|
|
5794
|
+
if (this.#observation !== void 0) return this.#observation;
|
|
5795
|
+
if (this.#discarded || !this.#resolved || !this.#submitted || this.#started.size === 0) {
|
|
5796
|
+
this.#observation = Promise.resolve(
|
|
5797
|
+
unavailable("timestamp capture did not reach an accepted volume submit")
|
|
5798
|
+
);
|
|
5799
|
+
this.#dispose();
|
|
5800
|
+
return this.#observation;
|
|
5801
|
+
}
|
|
5802
|
+
this.#observation = this.#readbackBuffer.mapAsync(GPU_BUFFER_USAGE_MAP_READ).then((mapped) => {
|
|
5803
|
+
if (!mapped.ok) return unavailable(`timestamp readback failed: ${mapped.error.code}`);
|
|
5804
|
+
const range = mapped.value.getMappedRange();
|
|
5805
|
+
if (!range.ok) {
|
|
5806
|
+
mapped.value.unmap();
|
|
5807
|
+
return unavailable(`timestamp mapping failed: ${range.error.code}`);
|
|
5808
|
+
}
|
|
5809
|
+
const values = new DataView(range.value);
|
|
5810
|
+
const decoded = [...this.#finished.entries()].sort((a, b) => a[1] - b[1]).map(([name, index]) => ({
|
|
5811
|
+
name,
|
|
5812
|
+
begin: values.getBigUint64(index * 2 * QUERY_RESULT_BYTES, true),
|
|
5813
|
+
end: values.getBigUint64((index * 2 + 1) * QUERY_RESULT_BYTES, true)
|
|
5814
|
+
}));
|
|
5815
|
+
if (decoded.some(({ end, begin }) => end < begin)) {
|
|
5816
|
+
mapped.value.unmap();
|
|
5817
|
+
this.#dispose();
|
|
5818
|
+
return unavailable("timestamp query returned an end before its begin");
|
|
5819
|
+
}
|
|
5820
|
+
const passes = decoded.map(({ name, begin, end }) => ({
|
|
5821
|
+
name,
|
|
5822
|
+
milliseconds: Number(end - begin) * this.#periodNanoseconds / 1e6
|
|
5823
|
+
}));
|
|
5824
|
+
const firstBegin = decoded.reduce(
|
|
5825
|
+
(first, sample) => sample.begin < first ? sample.begin : first,
|
|
5826
|
+
decoded[0]?.begin ?? 0n
|
|
5827
|
+
);
|
|
5828
|
+
const lastEnd = decoded.reduce(
|
|
5829
|
+
(last, sample) => sample.end > last ? sample.end : last,
|
|
5830
|
+
decoded[0]?.end ?? 0n
|
|
5831
|
+
);
|
|
5832
|
+
const totalMs = Number(lastEnd - firstBegin) * this.#periodNanoseconds / 1e6;
|
|
5833
|
+
mapped.value.unmap();
|
|
5834
|
+
this.#dispose();
|
|
5835
|
+
if (!Number.isFinite(totalMs) || passes.some((pass) => !Number.isFinite(pass.milliseconds))) {
|
|
5836
|
+
return unavailable("timestamp query returned a non-finite duration");
|
|
5837
|
+
}
|
|
5838
|
+
return Object.freeze({ status: "ready", unit: "ms", totalMs, passes });
|
|
5839
|
+
}).catch((cause) => {
|
|
5840
|
+
this.#dispose();
|
|
5841
|
+
return unavailable(`timestamp readback failed: ${String(cause)}`);
|
|
5842
|
+
});
|
|
5843
|
+
return this.#observation;
|
|
5844
|
+
}
|
|
5845
|
+
#dispose() {
|
|
5846
|
+
if (this.#disposed) return;
|
|
5847
|
+
this.#disposed = true;
|
|
5848
|
+
this.#device.destroyBuffer(this.#resolveBuffer);
|
|
5849
|
+
this.#device.destroyBuffer(this.#readbackBuffer);
|
|
5850
|
+
this.#device.destroyQuerySet(this.#querySet);
|
|
5851
|
+
}
|
|
5852
|
+
};
|
|
4897
5853
|
|
|
4898
5854
|
// src/environment/graph.ts
|
|
4899
5855
|
function sharedEnvironmentFrame(frame, _lane) {
|
|
@@ -7651,6 +8607,42 @@ function createPreparedGraphicsResolver(input) {
|
|
|
7651
8607
|
};
|
|
7652
8608
|
}
|
|
7653
8609
|
|
|
8610
|
+
// src/volume/temporal.ts
|
|
8611
|
+
function resolveVolumeTemporalReset(previous, next, options = {}) {
|
|
8612
|
+
if (options.reprojection === "out-of-screen") {
|
|
8613
|
+
return { reset: true, reason: "reprojection-out-of-screen" };
|
|
8614
|
+
}
|
|
8615
|
+
if (options.reprojection === "depth-discontinuity") {
|
|
8616
|
+
return { reset: true, reason: "reprojection-depth-discontinuity" };
|
|
8617
|
+
}
|
|
8618
|
+
if (previous.width !== next.width || previous.height !== next.height) {
|
|
8619
|
+
return { reset: true, reason: "resize" };
|
|
8620
|
+
}
|
|
8621
|
+
if (previous.cameraRevision !== next.cameraRevision) return { reset: true, reason: "camera-cut" };
|
|
8622
|
+
if (previous.fogRevision !== next.fogRevision) return { reset: true, reason: "fog-revision" };
|
|
8623
|
+
if (previous.lightRevision !== next.lightRevision) {
|
|
8624
|
+
return { reset: true, reason: "light-revision" };
|
|
8625
|
+
}
|
|
8626
|
+
if (previous.lightKind !== next.lightKind || previous.lightEntity !== next.lightEntity) {
|
|
8627
|
+
return { reset: true, reason: "light-revision" };
|
|
8628
|
+
}
|
|
8629
|
+
if (previous.pointLightEntity !== next.pointLightEntity || previous.spotLightEntity !== next.spotLightEntity) {
|
|
8630
|
+
return { reset: true, reason: "light-revision" };
|
|
8631
|
+
}
|
|
8632
|
+
const previousProjector = previous.projector;
|
|
8633
|
+
const nextProjector = next.projector;
|
|
8634
|
+
if (previousProjector?.guid !== nextProjector?.guid || previousProjector?.generation !== nextProjector?.generation || previousProjector?.view !== nextProjector?.view || previousProjector?.sampler !== nextProjector?.sampler || previousProjector?.projection !== nextProjector?.projection || previousProjector?.revision !== nextProjector?.revision) {
|
|
8635
|
+
return { reset: true, reason: "light-revision" };
|
|
8636
|
+
}
|
|
8637
|
+
if (previous.lightShadowRevision !== next.lightShadowRevision) {
|
|
8638
|
+
return { reset: true, reason: "light-revision" };
|
|
8639
|
+
}
|
|
8640
|
+
if (previous.densityGeneration !== next.densityGeneration) {
|
|
8641
|
+
return { reset: true, reason: "density-generation" };
|
|
8642
|
+
}
|
|
8643
|
+
return { reset: false };
|
|
8644
|
+
}
|
|
8645
|
+
|
|
7654
8646
|
// src/temporal/view.ts
|
|
7655
8647
|
var HALTON_23_8 = Object.freeze([
|
|
7656
8648
|
Object.freeze([0, -1 / 6]),
|
|
@@ -7832,11 +8824,14 @@ function resolveShadowMapSize(internals, lights) {
|
|
|
7832
8824
|
const requested = lights.shadowMapSize;
|
|
7833
8825
|
if (!(requested !== void 0 && requested > 0)) return requested;
|
|
7834
8826
|
const cascades = Math.max(1, Math.min(4, Math.round(lights.cascadeCount ?? 1)));
|
|
7835
|
-
const
|
|
8827
|
+
const atlasGrid = resolveDirectionalShadowAtlasGrid(cascades);
|
|
7836
8828
|
const maxDimension = internals.device.limits.maxTextureDimension2D;
|
|
7837
8829
|
if (!(maxDimension > 0)) return Math.floor(requested);
|
|
7838
8830
|
const depthTextureDimension = internals.device.caps?.backendKind === "wgpu-webgl2" ? Math.max(1, Math.floor(maxDimension / 2)) : maxDimension;
|
|
7839
|
-
const maxPerTile = Math.max(
|
|
8831
|
+
const maxPerTile = Math.max(
|
|
8832
|
+
1,
|
|
8833
|
+
Math.floor(depthTextureDimension / Math.max(atlasGrid.columns, atlasGrid.rows))
|
|
8834
|
+
);
|
|
7840
8835
|
return Math.max(1, Math.min(Math.floor(requested), maxPerTile));
|
|
7841
8836
|
}
|
|
7842
8837
|
function resolveSpotShadowMapSize(internals, lights) {
|
|
@@ -8309,7 +9304,7 @@ function commitFeatureCandidate(internals, candidate) {
|
|
|
8309
9304
|
state.plans = candidate.plans;
|
|
8310
9305
|
state.fullscreenEffects = candidate.fullscreenEffects;
|
|
8311
9306
|
}
|
|
8312
|
-
function topologyOf(internals, frameState, pipelineState, camera, clearOnly, lights, width, height, shadowMapSize, gpuDriven, featureGraphCandidate, transmissionDemand) {
|
|
9307
|
+
function topologyOf(internals, frameState, pipelineState, camera, clearOnly, lights, width, height, shadowMapSize, gpuDriven, featureGraphCandidate, transmissionDemand, volumetricFog) {
|
|
8313
9308
|
const cascadeCount = Math.max(1, Math.min(4, Math.round(lights.cascadeCount ?? 1)));
|
|
8314
9309
|
const surfaceFormats = resolveSurfaceFormatPair(
|
|
8315
9310
|
internals.device.caps.backendKind,
|
|
@@ -8335,6 +9330,11 @@ function topologyOf(internals, frameState, pipelineState, camera, clearOnly, lig
|
|
|
8335
9330
|
])
|
|
8336
9331
|
]
|
|
8337
9332
|
};
|
|
9333
|
+
const volumeCapability = hasVolumetricFogCapability(
|
|
9334
|
+
internals.device.caps,
|
|
9335
|
+
internals.volumetricFogShaders
|
|
9336
|
+
);
|
|
9337
|
+
const selectedVolumeLight = volumetricFog?.lightKind === "spot" ? lights.spot.find((light) => light.entity === volumetricFog.lightEntity) : volumetricFog?.lightKind === "point" ? lights.point.find((light) => light.entity === volumetricFog.lightEntity) : volumetricFog?.lightKind === "directional" && lights.directional?.entity === volumetricFog.lightEntity ? lights.directional : volumetricFog?.lightKind === void 0 ? lights.directional : void 0;
|
|
8338
9338
|
return ok$1({
|
|
8339
9339
|
pipelineId: STANDARD_PIPELINE_ID,
|
|
8340
9340
|
standardProfile: internals.standardProfile,
|
|
@@ -8368,6 +9368,38 @@ function topologyOf(internals, frameState, pipelineState, camera, clearOnly, lig
|
|
|
8368
9368
|
).length
|
|
8369
9369
|
)
|
|
8370
9370
|
},
|
|
9371
|
+
volumetricFog: volumetricFog?.status === "available" && volumetricFog.densityAsset?.shape.viewDimension === "3d" && camera.tonemap !== "none" && selectedVolumeLight !== void 0 && volumeCapability ? (() => {
|
|
9372
|
+
const volumeProfile = resolveVolumetricFogProfile(internals.standardProfile ?? {});
|
|
9373
|
+
return {
|
|
9374
|
+
enabled: true,
|
|
9375
|
+
format: volumetricFog.densityAsset.format,
|
|
9376
|
+
extent: {
|
|
9377
|
+
width: volumetricFog.densityAsset.shape.extent.width,
|
|
9378
|
+
height: volumetricFog.densityAsset.shape.extent.height,
|
|
9379
|
+
depth: volumetricFog.densityAsset.shape.extent.depth
|
|
9380
|
+
},
|
|
9381
|
+
froxelExtent: deriveVolumetricFogExtent(
|
|
9382
|
+
{ width, height },
|
|
9383
|
+
volumeProfile.depth,
|
|
9384
|
+
volumeProfile.tileSize
|
|
9385
|
+
),
|
|
9386
|
+
resolvedExtent: deriveVolumetricFogResolvedExtent(
|
|
9387
|
+
{ width, height },
|
|
9388
|
+
volumeProfile.tileSize
|
|
9389
|
+
),
|
|
9390
|
+
lightKind: volumetricFog.lightKind,
|
|
9391
|
+
lightEntity: volumetricFog.lightEntity,
|
|
9392
|
+
pointLightEntity: volumetricFog.pointLightEntity,
|
|
9393
|
+
spotLightEntity: volumetricFog.spotLightEntity,
|
|
9394
|
+
...volumetricFog.projector === void 0 ? {} : {
|
|
9395
|
+
projector: {
|
|
9396
|
+
guid: volumetricFog.projector.guid,
|
|
9397
|
+
generation: volumetricFog.projector.generation,
|
|
9398
|
+
revision: volumetricFog.projector.revision
|
|
9399
|
+
}
|
|
9400
|
+
}
|
|
9401
|
+
};
|
|
9402
|
+
})() : { enabled: false },
|
|
8371
9403
|
lane: {
|
|
8372
9404
|
compute: internals.device.caps.compute,
|
|
8373
9405
|
storageBuffer: internals.device.caps.storageBuffer,
|
|
@@ -8535,7 +9567,23 @@ function retire(frameState, graph) {
|
|
|
8535
9567
|
frameState.retiredCompiledFrameGraphs.add(graph);
|
|
8536
9568
|
graph.retire().finally(() => frameState.retiredCompiledFrameGraphs.delete(graph)).catch(() => void 0);
|
|
8537
9569
|
}
|
|
8538
|
-
function
|
|
9570
|
+
function settleVolumetricFogGraphCandidate(frameState, submitted) {
|
|
9571
|
+
const candidate = frameState.volumetricFogCandidateGraph ?? null;
|
|
9572
|
+
if (candidate === null) return;
|
|
9573
|
+
const previous = frameState.volumetricFogPreviousGraph ?? null;
|
|
9574
|
+
const previousKey = frameState.volumetricFogPreviousGraphKey ?? null;
|
|
9575
|
+
if (submitted) {
|
|
9576
|
+
if (previous !== null && previous !== candidate) retire(frameState, previous);
|
|
9577
|
+
} else {
|
|
9578
|
+
if (candidate !== previous) retire(frameState, candidate);
|
|
9579
|
+
frameState.compiledFrameGraph = previous;
|
|
9580
|
+
frameState.compiledFrameGraphTopologyKey = previousKey;
|
|
9581
|
+
}
|
|
9582
|
+
frameState.volumetricFogPreviousGraph = null;
|
|
9583
|
+
frameState.volumetricFogPreviousGraphKey = null;
|
|
9584
|
+
frameState.volumetricFogCandidateGraph = null;
|
|
9585
|
+
}
|
|
9586
|
+
function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera, lights, width, height, shadowMapSize, gpuDriven, featureGraphCandidate, clearOnly = false, transmissionDemand, volumetricFog, volumeTopologyCandidate = false) {
|
|
8539
9587
|
const surfaceWidth = Math.max(1, width);
|
|
8540
9588
|
const surfaceHeight = Math.max(1, height);
|
|
8541
9589
|
const featurePlans = featureGraphCandidate?.plans ?? getRenderFeatureGraphState(internals).plans;
|
|
@@ -8543,7 +9591,7 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
8543
9591
|
if (!projectedFeaturePlans.ok) {
|
|
8544
9592
|
reportRenderFeatureGraphError(internals, projectedFeaturePlans.error);
|
|
8545
9593
|
featureGraphCandidate?.onRejected?.();
|
|
8546
|
-
return frameState.compiledFrameGraph;
|
|
9594
|
+
return volumeTopologyCandidate ? null : frameState.compiledFrameGraph;
|
|
8547
9595
|
}
|
|
8548
9596
|
const topologyResult = topologyOf(
|
|
8549
9597
|
internals,
|
|
@@ -8557,12 +9605,13 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
8557
9605
|
shadowMapSize,
|
|
8558
9606
|
gpuDriven,
|
|
8559
9607
|
featureGraphCandidate,
|
|
8560
|
-
transmissionDemand
|
|
9608
|
+
transmissionDemand,
|
|
9609
|
+
volumetricFog
|
|
8561
9610
|
);
|
|
8562
9611
|
if (!topologyResult.ok) {
|
|
8563
9612
|
internals.errorRegistry.fire(topologyResult.error);
|
|
8564
9613
|
featureGraphCandidate?.onRejected?.();
|
|
8565
|
-
return frameState.compiledFrameGraph;
|
|
9614
|
+
return volumeTopologyCandidate ? null : frameState.compiledFrameGraph;
|
|
8566
9615
|
}
|
|
8567
9616
|
const topology = topologyResult.value;
|
|
8568
9617
|
const key = JSON.stringify(topology);
|
|
@@ -8611,7 +9660,7 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
8611
9660
|
if (!built.ok) {
|
|
8612
9661
|
internals.errorRegistry.fire(built.error);
|
|
8613
9662
|
featureGraphCandidate?.onRejected?.();
|
|
8614
|
-
return frameState.compiledFrameGraph;
|
|
9663
|
+
return volumeTopologyCandidate ? null : frameState.compiledFrameGraph;
|
|
8615
9664
|
}
|
|
8616
9665
|
const compiled = builder.compile({
|
|
8617
9666
|
device: internals.device,
|
|
@@ -8623,6 +9672,11 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
8623
9672
|
return frameState.compiledFrameGraph;
|
|
8624
9673
|
}
|
|
8625
9674
|
const previous = frameState.compiledFrameGraph;
|
|
9675
|
+
if (volumeTopologyCandidate) {
|
|
9676
|
+
frameState.volumetricFogPreviousGraph = previous;
|
|
9677
|
+
frameState.volumetricFogPreviousGraphKey = frameState.compiledFrameGraphTopologyKey;
|
|
9678
|
+
frameState.volumetricFogCandidateGraph = compiled.value;
|
|
9679
|
+
}
|
|
8626
9680
|
frameState.compiledFrameGraph = compiled.value;
|
|
8627
9681
|
frameState.compiledFrameGraphTopologyKey = key;
|
|
8628
9682
|
const compiledTargets = compiled.value;
|
|
@@ -8637,17 +9691,36 @@ function ensureCompiledFrameGraph(internals, frameState, pipelineState, camera,
|
|
|
8637
9691
|
if (featureGraphCandidate !== void 0) {
|
|
8638
9692
|
commitFeatureCandidate(internals, featureGraphCandidate);
|
|
8639
9693
|
}
|
|
8640
|
-
if (previous !== null) retire(frameState, previous);
|
|
9694
|
+
if (!volumeTopologyCandidate && previous !== null) retire(frameState, previous);
|
|
8641
9695
|
return compiled.value;
|
|
8642
9696
|
}
|
|
8643
|
-
function executeCompiledFrameGraph(internals, frameState, frame, encoder, runPass) {
|
|
9697
|
+
function executeCompiledFrameGraph(internals, frameState, frame, encoder, runPass, timingCapture) {
|
|
8644
9698
|
const graph = frameState.compiledFrameGraph;
|
|
8645
|
-
|
|
9699
|
+
const rejectTemporalFrame = () => {
|
|
9700
|
+
if (frameState.temporalFrameInput === void 0) return;
|
|
9701
|
+
frameState.temporalFrameTransaction.commit({ accepted: false });
|
|
9702
|
+
frameState.temporalFrameInput = void 0;
|
|
9703
|
+
};
|
|
9704
|
+
if (graph === null) {
|
|
9705
|
+
timingCapture?.discard();
|
|
9706
|
+
settleVolumetricFogGraphCandidate(frameState, false);
|
|
9707
|
+
rejectTemporalFrame();
|
|
9708
|
+
return false;
|
|
9709
|
+
}
|
|
9710
|
+
const timedRunPass = timingCapture === void 0 ? runPass : (pass, encode) => {
|
|
9711
|
+
timingCapture.beforePass(pass.name, encoder);
|
|
9712
|
+
try {
|
|
9713
|
+
if (runPass === void 0) encode();
|
|
9714
|
+
else runPass(pass, encode);
|
|
9715
|
+
} finally {
|
|
9716
|
+
timingCapture.afterPass(pass.name, encoder);
|
|
9717
|
+
}
|
|
9718
|
+
};
|
|
8646
9719
|
let preparedCommand = { command: void 0 };
|
|
8647
9720
|
const transaction = executeRendererFrameTransaction({
|
|
8648
9721
|
build: () => ({ ok: true, value: { command: void 0 } }),
|
|
8649
9722
|
execute: () => {
|
|
8650
|
-
const executed = graph.execute(frame,
|
|
9723
|
+
const executed = graph.execute(frame, timedRunPass);
|
|
8651
9724
|
if (!executed.ok) {
|
|
8652
9725
|
internals.errorRegistry.fire(executed.error);
|
|
8653
9726
|
return { ok: false, stage: "execute" };
|
|
@@ -8769,8 +9842,14 @@ function executeCompiledFrameGraph(internals, frameState, frame, encoder, runPas
|
|
|
8769
9842
|
);
|
|
8770
9843
|
return { ok: false, stage: "finish" };
|
|
8771
9844
|
}
|
|
9845
|
+
const resolved2 = timingCapture?.resolve(encoder);
|
|
9846
|
+
if (resolved2 !== void 0 && !resolved2.ok) {
|
|
9847
|
+
timingCapture?.discard();
|
|
9848
|
+
return { ok: false, stage: "finish" };
|
|
9849
|
+
}
|
|
8772
9850
|
const finished = encoder.finish();
|
|
8773
9851
|
if (!finished.ok) {
|
|
9852
|
+
timingCapture?.discard();
|
|
8774
9853
|
internals.errorRegistry.fire(finished.error);
|
|
8775
9854
|
return { ok: false, stage: "finish" };
|
|
8776
9855
|
}
|
|
@@ -8780,9 +9859,11 @@ function executeCompiledFrameGraph(internals, frameState, frame, encoder, runPas
|
|
|
8780
9859
|
submit: () => {
|
|
8781
9860
|
const submitted = internals.device.queue.submit([preparedCommand.command]);
|
|
8782
9861
|
if (!submitted.ok) {
|
|
9862
|
+
timingCapture?.discard();
|
|
8783
9863
|
internals.errorRegistry.fire(submitted.error);
|
|
8784
9864
|
return { ok: false, stage: "submit" };
|
|
8785
9865
|
}
|
|
9866
|
+
timingCapture?.markSubmitted();
|
|
8786
9867
|
return { ok: true, value: void 0 };
|
|
8787
9868
|
},
|
|
8788
9869
|
commit: () => {
|
|
@@ -8854,6 +9935,7 @@ function executeCompiledFrameGraph(internals, frameState, frame, encoder, runPas
|
|
|
8854
9935
|
}
|
|
8855
9936
|
},
|
|
8856
9937
|
abort: (failure2) => {
|
|
9938
|
+
timingCapture?.discard();
|
|
8857
9939
|
frameState.bloomFrameReceipts = void 0;
|
|
8858
9940
|
if (frameState.environmentGeneration !== void 0) {
|
|
8859
9941
|
frameState.environmentLifecycle?.recordStageFailure(
|
|
@@ -8876,11 +9958,13 @@ function executeCompiledFrameGraph(internals, frameState, frame, encoder, runPas
|
|
|
8876
9958
|
}
|
|
8877
9959
|
}
|
|
8878
9960
|
});
|
|
9961
|
+
settleVolumetricFogGraphCandidate(frameState, transaction.ok);
|
|
9962
|
+
if (!transaction.ok) rejectTemporalFrame();
|
|
8879
9963
|
return transaction.ok;
|
|
8880
9964
|
}
|
|
8881
9965
|
|
|
8882
9966
|
// src/record/frame.ts
|
|
8883
|
-
function
|
|
9967
|
+
function unavailable2(reason, hint) {
|
|
8884
9968
|
return err(new ObservationUnavailableError(reason, hint));
|
|
8885
9969
|
}
|
|
8886
9970
|
function mapLeaseFailure(code, hint) {
|
|
@@ -8888,20 +9972,20 @@ function mapLeaseFailure(code, hint) {
|
|
|
8888
9972
|
if (code === "observation-stale") reason = "stale";
|
|
8889
9973
|
else if (code === "observation-invalid-format") reason = "format";
|
|
8890
9974
|
else if (code === "observation-missing-copy-src") reason = "copy-src";
|
|
8891
|
-
return
|
|
9975
|
+
return unavailable2(reason, hint);
|
|
8892
9976
|
}
|
|
8893
9977
|
async function observeCurrentFrame(options, source, currentFrameId) {
|
|
8894
9978
|
if (options.semantic !== "linear-hdr") {
|
|
8895
|
-
return
|
|
9979
|
+
return unavailable2("identity", "request the producer-owned linear-hdr semantic");
|
|
8896
9980
|
}
|
|
8897
9981
|
if (source === void 0) {
|
|
8898
|
-
return
|
|
9982
|
+
return unavailable2(
|
|
8899
9983
|
"no-frame",
|
|
8900
9984
|
"draw a frame through the Standard producer before requesting observation"
|
|
8901
9985
|
);
|
|
8902
9986
|
}
|
|
8903
9987
|
if (source.pipelineId !== "forgeax::standard" || source.backendId.length === 0) {
|
|
8904
|
-
return
|
|
9988
|
+
return unavailable2(
|
|
8905
9989
|
"identity",
|
|
8906
9990
|
"observe a producer with explicit pipeline and backend identity"
|
|
8907
9991
|
);
|
|
@@ -8918,10 +10002,10 @@ async function observeCurrentFrame(options, source, currentFrameId) {
|
|
|
8918
10002
|
bytesResult = await options.readback(leaseResult.value);
|
|
8919
10003
|
} catch (cause) {
|
|
8920
10004
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
8921
|
-
return
|
|
10005
|
+
return unavailable2("readback-failed", `retry the current-frame readback after: ${message}`);
|
|
8922
10006
|
}
|
|
8923
10007
|
if (!bytesResult.ok) {
|
|
8924
|
-
return
|
|
10008
|
+
return unavailable2("readback-failed", bytesResult.error.message);
|
|
8925
10009
|
}
|
|
8926
10010
|
const activeResult = leaseResult.value.beginReadback();
|
|
8927
10011
|
if (!activeResult.ok) {
|
|
@@ -8980,7 +10064,432 @@ var EMPTY_ENTITY_KEYS = /* @__PURE__ */ new Set();
|
|
|
8980
10064
|
function runRecordProfilePhase(runner, phase, action) {
|
|
8981
10065
|
return runner === void 0 ? action() : runner(phase, action);
|
|
8982
10066
|
}
|
|
8983
|
-
|
|
10067
|
+
var VOLUMETRIC_FOG_PASS_COUNT = 4;
|
|
10068
|
+
function nextVolumetricFogParamsSlot(frameState) {
|
|
10069
|
+
const accepted = frameState.volumetricFogParamsAcceptedSlot;
|
|
10070
|
+
const pending = frameState.volumetricFogParamsPendingSlot;
|
|
10071
|
+
if (pending !== null && pending !== accepted) return pending;
|
|
10072
|
+
return accepted === null ? 0 : accepted === 0 ? 1 : 0;
|
|
10073
|
+
}
|
|
10074
|
+
function stageVolumetricFogParams(internals, frameState, params) {
|
|
10075
|
+
const slot = nextVolumetricFogParamsSlot(frameState);
|
|
10076
|
+
let buffer = frameState.volumetricFogParamsBuffers[slot];
|
|
10077
|
+
if (buffer === null) {
|
|
10078
|
+
const created = internals.device.createBuffer({
|
|
10079
|
+
label: `volumetric-fog-params-${slot}`,
|
|
10080
|
+
size: VOLUMETRIC_FOG_PARAMS_BYTES,
|
|
10081
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
10082
|
+
mappedAtCreation: false
|
|
10083
|
+
});
|
|
10084
|
+
if (!created.ok) {
|
|
10085
|
+
internals.errorRegistry.fire(created.error);
|
|
10086
|
+
return void 0;
|
|
10087
|
+
}
|
|
10088
|
+
buffer = created.value;
|
|
10089
|
+
frameState.volumetricFogParamsBuffers[slot] = buffer;
|
|
10090
|
+
}
|
|
10091
|
+
const written = internals.device.queue.writeBuffer(buffer, 0, params);
|
|
10092
|
+
if (!written.ok) {
|
|
10093
|
+
internals.errorRegistry.fire(written.error);
|
|
10094
|
+
return void 0;
|
|
10095
|
+
}
|
|
10096
|
+
frameState.volumetricFogParamsPendingSlot = slot;
|
|
10097
|
+
frameState.volumetricFogPendingParams = new Float32Array(params);
|
|
10098
|
+
return buffer;
|
|
10099
|
+
}
|
|
10100
|
+
function retireVolumetricFogParams(internals, frameState) {
|
|
10101
|
+
const buffers = frameState.volumetricFogParamsBuffers;
|
|
10102
|
+
frameState.volumetricFogParamsBuffers = [null, null];
|
|
10103
|
+
frameState.volumetricFogParamsPendingSlot = null;
|
|
10104
|
+
frameState.volumetricFogParamsAcceptedSlot = null;
|
|
10105
|
+
frameState.volumetricFogAcceptedParams = void 0;
|
|
10106
|
+
frameState.volumetricFogPendingParams = void 0;
|
|
10107
|
+
const live = buffers.filter((buffer) => buffer !== null);
|
|
10108
|
+
if (live.length === 0) return;
|
|
10109
|
+
const device = internals.device;
|
|
10110
|
+
const release = () => {
|
|
10111
|
+
for (const buffer of live) {
|
|
10112
|
+
const destroyed = device.destroyBuffer(buffer);
|
|
10113
|
+
if (!destroyed.ok) internals.errorRegistry.fire(destroyed.error);
|
|
10114
|
+
}
|
|
10115
|
+
};
|
|
10116
|
+
try {
|
|
10117
|
+
void device.queue.onSubmittedWorkDone().then(release, release);
|
|
10118
|
+
} catch {
|
|
10119
|
+
release();
|
|
10120
|
+
}
|
|
10121
|
+
}
|
|
10122
|
+
function volumeRevision(values) {
|
|
10123
|
+
let hash = 2166136261;
|
|
10124
|
+
for (const value of values) {
|
|
10125
|
+
const quantized = Number.isFinite(value) ? Math.round(value * 1e6) : 0;
|
|
10126
|
+
hash = Math.imul(hash ^ quantized, 16777619) >>> 0;
|
|
10127
|
+
}
|
|
10128
|
+
return hash;
|
|
10129
|
+
}
|
|
10130
|
+
function volumeTemporalSignature(fog, prepared, camera, lights, width, height) {
|
|
10131
|
+
const authored = fog.fog;
|
|
10132
|
+
const directional = lights.directional;
|
|
10133
|
+
const cameraRevision = volumeRevision([
|
|
10134
|
+
...camera.world,
|
|
10135
|
+
camera.fov,
|
|
10136
|
+
camera.aspect,
|
|
10137
|
+
camera.near,
|
|
10138
|
+
camera.far,
|
|
10139
|
+
camera.orthoLeft,
|
|
10140
|
+
camera.orthoRight,
|
|
10141
|
+
camera.orthoBottom,
|
|
10142
|
+
camera.orthoTop
|
|
10143
|
+
]);
|
|
10144
|
+
const fogRevision = authored === void 0 ? 0 : volumeRevision([
|
|
10145
|
+
...authored.bounds.min,
|
|
10146
|
+
...authored.bounds.max,
|
|
10147
|
+
...authored.extinction,
|
|
10148
|
+
...authored.albedo,
|
|
10149
|
+
...authored.emission,
|
|
10150
|
+
authored.anisotropy,
|
|
10151
|
+
authored.maxDistance
|
|
10152
|
+
]);
|
|
10153
|
+
const selectedSpot = fog.spotLightEntity === void 0 ? fog.lightKind === "spot" ? lights.spot.find((spot) => spot.entity === fog.lightEntity) : void 0 : lights.spot.find((spot) => spot.entity === fog.spotLightEntity);
|
|
10154
|
+
const selectedPoint = fog.pointLightEntity === void 0 ? fog.lightKind === "point" ? lights.point.find((point) => point.entity === fog.lightEntity) : void 0 : lights.point.find((point) => point.entity === fog.pointLightEntity);
|
|
10155
|
+
const selectedDirectional = fog.lightKind === "directional" && directional?.entity === fog.lightEntity ? directional : void 0;
|
|
10156
|
+
const lightRevision = selectedSpot === void 0 && selectedPoint === void 0 && selectedDirectional === void 0 ? 0 : volumeRevision(
|
|
10157
|
+
selectedSpot === void 0 && selectedPoint === void 0 ? selectedDirectional === void 0 ? [] : [
|
|
10158
|
+
...selectedDirectional.direction,
|
|
10159
|
+
...selectedDirectional.color,
|
|
10160
|
+
selectedDirectional.intensity,
|
|
10161
|
+
...lights.lightViewProj?.flatMap((matrix) => [...matrix]) ?? []
|
|
10162
|
+
] : [
|
|
10163
|
+
...selectedPoint === void 0 ? [] : [
|
|
10164
|
+
...selectedPoint.position,
|
|
10165
|
+
...selectedPoint.color,
|
|
10166
|
+
selectedPoint.intensity,
|
|
10167
|
+
selectedPoint.invRangeSquared,
|
|
10168
|
+
selectedPoint.shadowAtlasLayer ?? -1,
|
|
10169
|
+
selectedPoint.shadowNear ?? 0,
|
|
10170
|
+
selectedPoint.shadowFar ?? 0
|
|
10171
|
+
],
|
|
10172
|
+
...selectedSpot === void 0 ? [] : [
|
|
10173
|
+
...selectedSpot.position,
|
|
10174
|
+
...selectedSpot.direction,
|
|
10175
|
+
...selectedSpot.color,
|
|
10176
|
+
selectedSpot.intensity,
|
|
10177
|
+
selectedSpot.invRangeSquared,
|
|
10178
|
+
selectedSpot.cosInner,
|
|
10179
|
+
selectedSpot.cosOuter,
|
|
10180
|
+
selectedSpot.depthBias ?? 5e-3,
|
|
10181
|
+
selectedSpot.normalBias ?? 0.05,
|
|
10182
|
+
selectedSpot.pcfKernelSize ?? 3,
|
|
10183
|
+
selectedSpot.shadowAtlasTile,
|
|
10184
|
+
...selectedSpot.lightViewProj === void 0 ? [] : [...selectedSpot.lightViewProj]
|
|
10185
|
+
]
|
|
10186
|
+
]
|
|
10187
|
+
);
|
|
10188
|
+
return {
|
|
10189
|
+
cameraRevision,
|
|
10190
|
+
fogRevision,
|
|
10191
|
+
lightRevision,
|
|
10192
|
+
densityGeneration: prepared.densityGeneration,
|
|
10193
|
+
width,
|
|
10194
|
+
height,
|
|
10195
|
+
...fog.lightKind === void 0 ? {} : { lightKind: fog.lightKind },
|
|
10196
|
+
...fog.lightEntity === void 0 ? {} : { lightEntity: fog.lightEntity },
|
|
10197
|
+
...fog.pointLightEntity === void 0 ? {} : { pointLightEntity: fog.pointLightEntity },
|
|
10198
|
+
...fog.spotLightEntity === void 0 ? {} : { spotLightEntity: fog.spotLightEntity },
|
|
10199
|
+
...fog.projector === void 0 ? {} : { projector: fog.projector },
|
|
10200
|
+
lightShadowRevision: lightRevision
|
|
10201
|
+
};
|
|
10202
|
+
}
|
|
10203
|
+
function resolveVolumetricFogHistoryContext(frameState, graph, fog, prepared, camera, lights, width, height) {
|
|
10204
|
+
const signature = volumeTemporalSignature(fog, prepared, camera, lights, width, height);
|
|
10205
|
+
const acceptedGraph = frameState.volumetricFogHistoryGraph;
|
|
10206
|
+
const acceptedSlot = frameState.volumetricFogHistorySlot;
|
|
10207
|
+
const previousSignature = frameState.volumetricFogHistorySignature;
|
|
10208
|
+
let reset = acceptedGraph !== graph || acceptedSlot === null || previousSignature === null;
|
|
10209
|
+
if (!reset && previousSignature !== null) {
|
|
10210
|
+
const temporal = frameState.temporalFrame;
|
|
10211
|
+
const previousResetReason = temporal?.resetReason;
|
|
10212
|
+
const reprojection = previousResetReason === "out-of-screen" ? "out-of-screen" : previousResetReason === "depth-discontinuity" ? "depth-discontinuity" : void 0;
|
|
10213
|
+
reset = resolveVolumeTemporalReset(previousSignature, signature, {
|
|
10214
|
+
taaEnabled: camera.antialias === "taa",
|
|
10215
|
+
...reprojection === void 0 ? {} : { reprojection }
|
|
10216
|
+
}).reset;
|
|
10217
|
+
}
|
|
10218
|
+
const historyReadSlot = reset ? null : acceptedSlot;
|
|
10219
|
+
const historyWriteSlot = reset || acceptedSlot === null ? 0 : acceptedSlot === 0 ? 1 : 0;
|
|
10220
|
+
return {
|
|
10221
|
+
context: {
|
|
10222
|
+
...prepared,
|
|
10223
|
+
historyReadSlot,
|
|
10224
|
+
historyWriteSlot,
|
|
10225
|
+
historyValid: historyReadSlot !== null
|
|
10226
|
+
},
|
|
10227
|
+
signature
|
|
10228
|
+
};
|
|
10229
|
+
}
|
|
10230
|
+
function prepareVolumetricFogFrame(internals, frameState, world, worlds, fog, lights, volumeCapability, projector) {
|
|
10231
|
+
if (!volumeCapability || fog?.status !== "available" || fog.fog === void 0 || fog.densityHandle === void 0 || fog.densityAsset === void 0 || (fog.lightKind === "spot" ? lights.spot.find((light) => light.entity === fog.lightEntity) === void 0 : fog.lightKind === "point" ? lights.point.find((light) => light.entity === fog.lightEntity) === void 0 : lights.directional === void 0)) {
|
|
10232
|
+
return void 0;
|
|
10233
|
+
}
|
|
10234
|
+
const ownerWorld = worlds[fog.worldId ?? 0] ?? world;
|
|
10235
|
+
const resident = internals.gpuStore.ensureResident(
|
|
10236
|
+
fog.densityHandle,
|
|
10237
|
+
fog.densityAsset,
|
|
10238
|
+
ownerWorld
|
|
10239
|
+
);
|
|
10240
|
+
if (!resident.ok || !("texture" in resident.value)) {
|
|
10241
|
+
if (!resident.ok) {
|
|
10242
|
+
internals.errorRegistry.fire(
|
|
10243
|
+
resident.error instanceof RhiError ? resident.error : new RhiError({
|
|
10244
|
+
code: "webgpu-runtime-error",
|
|
10245
|
+
expected: "volumetric density residency to resolve a GPU texture",
|
|
10246
|
+
hint: "repair the authored TextureAsset payload and retry the next frame",
|
|
10247
|
+
detail: { error: resident.error }
|
|
10248
|
+
})
|
|
10249
|
+
);
|
|
10250
|
+
}
|
|
10251
|
+
return void 0;
|
|
10252
|
+
}
|
|
10253
|
+
const params = new Float32Array(32);
|
|
10254
|
+
params.set(fog.fog.bounds.min, 0);
|
|
10255
|
+
params.set(fog.fog.bounds.max, 4);
|
|
10256
|
+
params.set(fog.fog.extinction, 8);
|
|
10257
|
+
params.set(fog.fog.albedo, 12);
|
|
10258
|
+
params.set(fog.fog.emission, 16);
|
|
10259
|
+
const selectedPoint = fog.pointLightEntity === void 0 ? void 0 : lights.point.find((light) => light.entity === fog.pointLightEntity);
|
|
10260
|
+
const selectedSpot = fog.spotLightEntity === void 0 ? fog.lightKind === "spot" ? lights.spot.find((light) => light.entity === fog.lightEntity) : void 0 : lights.spot.find((light) => light.entity === fog.spotLightEntity);
|
|
10261
|
+
const lightDirection = selectedSpot?.direction ?? lights.directional?.direction ?? vec3.create(0, -1, 0);
|
|
10262
|
+
const lightColor = selectedSpot?.color ?? lights.directional?.color ?? vec3.create(0, 0, 0);
|
|
10263
|
+
if (lightDirection === void 0 || lightColor === void 0) return void 0;
|
|
10264
|
+
params.set(lightDirection, 20);
|
|
10265
|
+
params[23] = frameState.frameNumber % 1024;
|
|
10266
|
+
const selectedSpotIndex = selectedSpot === void 0 ? -1 : lights.spot.findIndex((light) => light.entity === selectedSpot.entity);
|
|
10267
|
+
params.set(lightColor, 24);
|
|
10268
|
+
params[19] = selectedPoint === void 0 ? -1 : lights.point.findIndex((light) => light.entity === selectedPoint.entity);
|
|
10269
|
+
params[27] = selectedSpotIndex;
|
|
10270
|
+
params[28] = fog.fog.maxDistance;
|
|
10271
|
+
params[29] = fog.fog.anisotropy;
|
|
10272
|
+
params[30] = selectedPoint !== void 0 && selectedSpot !== void 0 ? 3 : selectedPoint !== void 0 ? 1 : selectedSpot !== void 0 ? 2 : 0;
|
|
10273
|
+
params[31] = frameState.temporalFrame !== void 0 && frameState.temporalFrame.resetReason === "none" ? 1 : 0;
|
|
10274
|
+
const paramsBuffer = stageVolumetricFogParams(internals, frameState, params);
|
|
10275
|
+
if (paramsBuffer === void 0) return void 0;
|
|
10276
|
+
return {
|
|
10277
|
+
densityTexture: resident.value.texture.handle,
|
|
10278
|
+
densityView: resident.value.view,
|
|
10279
|
+
paramsBuffer,
|
|
10280
|
+
densityGeneration: resident.value.receipt.generation,
|
|
10281
|
+
historyReadSlot: null,
|
|
10282
|
+
historyWriteSlot: 0,
|
|
10283
|
+
historyValid: false,
|
|
10284
|
+
...projector === void 0 ? {} : {
|
|
10285
|
+
projectorTexture: projector.texture,
|
|
10286
|
+
projectorView: projector.view,
|
|
10287
|
+
projectorSampler: projector.sampler
|
|
10288
|
+
}
|
|
10289
|
+
};
|
|
10290
|
+
}
|
|
10291
|
+
function prepareSpotLightProjectorFrame(internals, world, worlds, lights, fog) {
|
|
10292
|
+
const preferred = fog?.projectorHandle === void 0 ? void 0 : lights.spot.find((light) => light.projectorHandle === fog.projectorHandle);
|
|
10293
|
+
const source = preferred ?? lights.spot.find(
|
|
10294
|
+
(light) => light.projectorHandle !== void 0 && light.projectorAsset !== void 0 && light.lightViewProj !== void 0
|
|
10295
|
+
);
|
|
10296
|
+
if (source === void 0 || source.projectorHandle === void 0 || source.projectorAsset === void 0) {
|
|
10297
|
+
return void 0;
|
|
10298
|
+
}
|
|
10299
|
+
const ownerWorld = worlds[fog?.worldId ?? 0] ?? world;
|
|
10300
|
+
const resident = internals.gpuStore.ensureResident(
|
|
10301
|
+
source.projectorHandle,
|
|
10302
|
+
source.projectorAsset,
|
|
10303
|
+
ownerWorld
|
|
10304
|
+
);
|
|
10305
|
+
if (!resident.ok || !("texture" in resident.value)) {
|
|
10306
|
+
if (!resident.ok) {
|
|
10307
|
+
internals.errorRegistry.fire(
|
|
10308
|
+
resident.error instanceof RhiError ? resident.error : new RhiError({
|
|
10309
|
+
code: "webgpu-runtime-error",
|
|
10310
|
+
expected: "SpotLight projector residency to resolve a GPU texture",
|
|
10311
|
+
hint: "repair the authored projector TextureAsset payload and retry the next frame",
|
|
10312
|
+
detail: { error: resident.error }
|
|
10313
|
+
})
|
|
10314
|
+
);
|
|
10315
|
+
}
|
|
10316
|
+
return void 0;
|
|
10317
|
+
}
|
|
10318
|
+
const sampler = internals.device.createSampler({
|
|
10319
|
+
label: "spot-projector-linear-clamp",
|
|
10320
|
+
minFilter: "linear",
|
|
10321
|
+
magFilter: "linear",
|
|
10322
|
+
mipmapFilter: "linear",
|
|
10323
|
+
addressModeU: "clamp-to-edge",
|
|
10324
|
+
addressModeV: "clamp-to-edge",
|
|
10325
|
+
addressModeW: "clamp-to-edge"
|
|
10326
|
+
});
|
|
10327
|
+
if (!sampler.ok) {
|
|
10328
|
+
internals.errorRegistry.fire(sampler.error);
|
|
10329
|
+
return void 0;
|
|
10330
|
+
}
|
|
10331
|
+
return {
|
|
10332
|
+
texture: resident.value.texture.handle,
|
|
10333
|
+
view: resident.value.view,
|
|
10334
|
+
sampler: sampler.value
|
|
10335
|
+
};
|
|
10336
|
+
}
|
|
10337
|
+
function stageAcceptedVolumetricFogFrame(internals, frameState, accepted) {
|
|
10338
|
+
if (accepted === void 0) return void 0;
|
|
10339
|
+
const params = frameState.volumetricFogAcceptedParams;
|
|
10340
|
+
if (params === void 0) return accepted;
|
|
10341
|
+
const paramsBuffer = stageVolumetricFogParams(internals, frameState, new Float32Array(params));
|
|
10342
|
+
if (paramsBuffer === void 0) return accepted;
|
|
10343
|
+
return { ...accepted, paramsBuffer };
|
|
10344
|
+
}
|
|
10345
|
+
function updateVolumetricFogInspection(frameState, fog, lights, prepared, submitted, volumeCapability, volumeSubmissionAttempted) {
|
|
10346
|
+
const resourceFacts = frameState.compiledFrameGraph === void 0 || frameState.compiledFrameGraph === null ? void 0 : inspectVolumetricFogResources(frameState.compiledFrameGraph.inspect(), {
|
|
10347
|
+
parameterBufferCount: frameState.volumetricFogParamsBuffers.filter(
|
|
10348
|
+
(buffer) => buffer !== null
|
|
10349
|
+
).length
|
|
10350
|
+
});
|
|
10351
|
+
const graphSampleCount = resourceFacts?.sampleCount ?? 0;
|
|
10352
|
+
const graphMemoryBytes = resourceFacts?.totalBytes ?? 0;
|
|
10353
|
+
const previous = frameState.volumetricFogInspection;
|
|
10354
|
+
const accepted = frameState.volumetricFogAccepted;
|
|
10355
|
+
const acceptedLkg = accepted === void 0 ? void 0 : {
|
|
10356
|
+
guid: accepted.guid ?? previous.guid,
|
|
10357
|
+
generation: accepted.generation ?? previous.generation,
|
|
10358
|
+
digest: accepted.digest ?? previous.digest,
|
|
10359
|
+
deviceEpoch: previous.deviceEpoch,
|
|
10360
|
+
format: previous.format ?? accepted.densityAsset?.format ?? fog?.densityAsset?.format,
|
|
10361
|
+
passCount: previous.passCount > 0 ? previous.passCount : VOLUMETRIC_FOG_PASS_COUNT,
|
|
10362
|
+
sampleCount: previous.sampleCount > 0 ? previous.sampleCount : graphSampleCount,
|
|
10363
|
+
memoryBytes: previous.memoryBytes > 0 ? previous.memoryBytes : graphMemoryBytes,
|
|
10364
|
+
resourceFacts: previous.resourceFacts ?? resourceFacts,
|
|
10365
|
+
lightEntity: accepted.lightEntity,
|
|
10366
|
+
lightKind: accepted.lightKind,
|
|
10367
|
+
pointLightEntity: accepted.pointLightEntity,
|
|
10368
|
+
spotLightEntity: accepted.spotLightEntity,
|
|
10369
|
+
projector: accepted.projector
|
|
10370
|
+
};
|
|
10371
|
+
const candidateIdentity = fog?.guid === void 0 || fog.generation === void 0 ? void 0 : {
|
|
10372
|
+
guid: fog.guid,
|
|
10373
|
+
generation: fog.generation,
|
|
10374
|
+
digest: fog.digest
|
|
10375
|
+
};
|
|
10376
|
+
const selectedLightAvailable = fog?.lightKind === "spot" ? lights.spot.some((light) => light.entity === fog.lightEntity) : fog?.lightKind === "point" ? lights.point.some(
|
|
10377
|
+
(light) => light.entity === (fog.pointLightEntity ?? fog.lightEntity)
|
|
10378
|
+
) && (fog.spotLightEntity === void 0 || lights.spot.some((light) => light.entity === fog.spotLightEntity)) : fog?.lightKind === "directional" ? lights.directional?.entity === fog.lightEntity : lights.directional !== void 0;
|
|
10379
|
+
const capability = volumeCapability && selectedLightAvailable ? "available" : "unavailable";
|
|
10380
|
+
const inspectAccepted = (status, candidateFailure) => {
|
|
10381
|
+
if (acceptedLkg === void 0 || acceptedLkg.guid === void 0 || acceptedLkg.generation === void 0) {
|
|
10382
|
+
return false;
|
|
10383
|
+
}
|
|
10384
|
+
const recovery = {
|
|
10385
|
+
guid: acceptedLkg.guid,
|
|
10386
|
+
generation: acceptedLkg.generation,
|
|
10387
|
+
deviceEpoch: acceptedLkg.deviceEpoch,
|
|
10388
|
+
status,
|
|
10389
|
+
...{ lkgGeneration: acceptedLkg.generation } ,
|
|
10390
|
+
...candidateIdentity === void 0 ? {} : {
|
|
10391
|
+
candidateGeneration: candidateIdentity.generation,
|
|
10392
|
+
...candidateIdentity.digest === void 0 ? {} : { candidateDigest: candidateIdentity.digest }
|
|
10393
|
+
},
|
|
10394
|
+
...acceptedLkg.lightEntity === void 0 || acceptedLkg.lightKind === void 0 ? {} : {
|
|
10395
|
+
selectedLight: {
|
|
10396
|
+
entity: acceptedLkg.lightEntity,
|
|
10397
|
+
kind: acceptedLkg.lightKind,
|
|
10398
|
+
revision: 0,
|
|
10399
|
+
shadowTile: -1,
|
|
10400
|
+
...acceptedLkg.pointLightEntity === void 0 ? {} : { pointLightEntity: acceptedLkg.pointLightEntity },
|
|
10401
|
+
...acceptedLkg.spotLightEntity === void 0 ? {} : { spotLightEntity: acceptedLkg.spotLightEntity },
|
|
10402
|
+
...acceptedLkg.projector === void 0 ? {} : { projector: acceptedLkg.projector }
|
|
10403
|
+
}
|
|
10404
|
+
},
|
|
10405
|
+
...candidateFailure === void 0 ? {} : { candidateFailure }
|
|
10406
|
+
};
|
|
10407
|
+
frameState.volumetricFogInspection = inspectVolumetricFog({
|
|
10408
|
+
authored: true,
|
|
10409
|
+
capability: "available",
|
|
10410
|
+
degraded: status === "degraded",
|
|
10411
|
+
recovery,
|
|
10412
|
+
...acceptedLkg.digest === void 0 ? {} : { acceptedDigest: acceptedLkg.digest },
|
|
10413
|
+
...acceptedLkg.format === void 0 ? {} : { format: acceptedLkg.format },
|
|
10414
|
+
passCount: acceptedLkg.passCount,
|
|
10415
|
+
sampleCount: acceptedLkg.sampleCount,
|
|
10416
|
+
memoryBytes: acceptedLkg.memoryBytes,
|
|
10417
|
+
...acceptedLkg.resourceFacts === void 0 ? {} : { resourceFacts: acceptedLkg.resourceFacts }
|
|
10418
|
+
});
|
|
10419
|
+
return true;
|
|
10420
|
+
};
|
|
10421
|
+
if (fog === void 0 || fog.status === "off") {
|
|
10422
|
+
if (!submitted && volumeSubmissionAttempted && inspectAccepted("degraded", "submit-failed")) {
|
|
10423
|
+
return;
|
|
10424
|
+
}
|
|
10425
|
+
if (!submitted && volumeSubmissionAttempted) return;
|
|
10426
|
+
if (!submitted) return;
|
|
10427
|
+
frameState.volumetricFogInspection = inspectVolumetricFog({
|
|
10428
|
+
authored: false,
|
|
10429
|
+
capability: "available"
|
|
10430
|
+
});
|
|
10431
|
+
return;
|
|
10432
|
+
}
|
|
10433
|
+
const authored = fog.status === "available" || fog.status === "degraded";
|
|
10434
|
+
if (submitted && fog.status === "available" && prepared !== void 0 && capability === "available") {
|
|
10435
|
+
const recovery = candidateIdentity === void 0 ? void 0 : {
|
|
10436
|
+
guid: candidateIdentity.guid,
|
|
10437
|
+
generation: candidateIdentity.generation,
|
|
10438
|
+
deviceEpoch: previous.deviceEpoch,
|
|
10439
|
+
status: "accepted",
|
|
10440
|
+
...fog.lightEntity === void 0 || fog.lightKind === void 0 ? {} : {
|
|
10441
|
+
selectedLight: {
|
|
10442
|
+
entity: fog.lightEntity,
|
|
10443
|
+
kind: fog.lightKind,
|
|
10444
|
+
revision: 0,
|
|
10445
|
+
shadowTile: lights.spot.find((spot) => spot.entity === fog.spotLightEntity)?.shadowAtlasTile ?? lights.spot.find((spot) => spot.entity === fog.lightEntity)?.shadowAtlasTile ?? -1,
|
|
10446
|
+
...fog.pointLightEntity === void 0 ? {} : { pointLightEntity: fog.pointLightEntity },
|
|
10447
|
+
...fog.spotLightEntity === void 0 ? {} : { spotLightEntity: fog.spotLightEntity },
|
|
10448
|
+
...fog.projector === void 0 ? {} : { projector: fog.projector }
|
|
10449
|
+
}
|
|
10450
|
+
}
|
|
10451
|
+
};
|
|
10452
|
+
frameState.volumetricFogInspection = inspectVolumetricFog({
|
|
10453
|
+
authored: true,
|
|
10454
|
+
capability,
|
|
10455
|
+
...recovery === void 0 ? {} : { recovery },
|
|
10456
|
+
...candidateIdentity?.digest === void 0 ? {} : { acceptedDigest: candidateIdentity.digest },
|
|
10457
|
+
...fog.densityAsset?.format === void 0 ? {} : { format: fog.densityAsset.format },
|
|
10458
|
+
passCount: VOLUMETRIC_FOG_PASS_COUNT,
|
|
10459
|
+
sampleCount: graphSampleCount,
|
|
10460
|
+
memoryBytes: graphMemoryBytes,
|
|
10461
|
+
...resourceFacts === void 0 ? {} : { resourceFacts }
|
|
10462
|
+
});
|
|
10463
|
+
return;
|
|
10464
|
+
}
|
|
10465
|
+
if (submitted && acceptedLkg !== void 0 && (fog.status === "degraded" || prepared === void 0)) {
|
|
10466
|
+
if (inspectAccepted("degraded")) return;
|
|
10467
|
+
}
|
|
10468
|
+
if (!submitted && volumeSubmissionAttempted && inspectAccepted("degraded", "submit-failed")) {
|
|
10469
|
+
return;
|
|
10470
|
+
}
|
|
10471
|
+
const candidateRecovery = candidateIdentity === void 0 ? void 0 : {
|
|
10472
|
+
guid: candidateIdentity.guid,
|
|
10473
|
+
generation: candidateIdentity.generation,
|
|
10474
|
+
deviceEpoch: previous.deviceEpoch,
|
|
10475
|
+
status: "candidate",
|
|
10476
|
+
candidateGeneration: candidateIdentity.generation,
|
|
10477
|
+
...candidateIdentity.digest === void 0 ? {} : { candidateDigest: candidateIdentity.digest },
|
|
10478
|
+
...!submitted && volumeSubmissionAttempted ? { candidateFailure: "submit-failed" } : {}
|
|
10479
|
+
};
|
|
10480
|
+
frameState.volumetricFogInspection = inspectVolumetricFog({
|
|
10481
|
+
authored,
|
|
10482
|
+
capability,
|
|
10483
|
+
degraded: fog.status === "degraded" || capability === "unavailable",
|
|
10484
|
+
...candidateRecovery === void 0 ? {} : { recovery: candidateRecovery },
|
|
10485
|
+
...acceptedLkg?.digest === void 0 ? {} : { acceptedDigest: acceptedLkg.digest },
|
|
10486
|
+
...fog.densityAsset?.format === void 0 ? acceptedLkg?.format === void 0 ? {} : { format: acceptedLkg.format } : { format: fog.densityAsset.format },
|
|
10487
|
+
passCount: 0,
|
|
10488
|
+
sampleCount: 0,
|
|
10489
|
+
memoryBytes: 0
|
|
10490
|
+
});
|
|
10491
|
+
}
|
|
10492
|
+
function recordFrame(internals, world, cameras, lights, renderables, transparentDispatch, frameState, dispatchCounts, bindGroupCounts, skylight, skylightCount, skybox, skyboxCount, postProcessParams, fog, worlds = [world], profilePhase, gpuDriven, renderReadLeases, featureGraphCandidate, pointsLinesOwner, environmentSignature2 = "", fogSignature = "", environmentReady = true, transmissionDemand, volumetricFog, timingCapture) {
|
|
8984
10493
|
let activeCameras = cameras;
|
|
8985
10494
|
if (activeCameras.length === 0) {
|
|
8986
10495
|
internals.errorRegistry.fire(
|
|
@@ -8992,6 +10501,11 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
8992
10501
|
);
|
|
8993
10502
|
activeCameras = [makeZeroCameraFallbackSnapshot()];
|
|
8994
10503
|
}
|
|
10504
|
+
let submitted = false;
|
|
10505
|
+
let volumeInspectionReady = false;
|
|
10506
|
+
let volumeSubmissionAttempted = false;
|
|
10507
|
+
let preparedVolumetricFogForInspection;
|
|
10508
|
+
let volumeCapabilityForInspection = false;
|
|
8995
10509
|
try {
|
|
8996
10510
|
if (activeCameras.length > 1) {
|
|
8997
10511
|
internals.errorRegistry.fire(
|
|
@@ -9047,6 +10561,39 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
9047
10561
|
const effectiveShadowMapSize = resolveShadowMapSize(internals, lights);
|
|
9048
10562
|
const effectiveSpotShadowMapSize = resolveSpotShadowMapSize(internals, lights);
|
|
9049
10563
|
const graphShadowMapSize = effectiveShadowMapSize ?? effectiveSpotShadowMapSize;
|
|
10564
|
+
const volumeCapability = hasVolumetricFogCapability(
|
|
10565
|
+
internals.device.caps,
|
|
10566
|
+
internals.volumetricFogShaders
|
|
10567
|
+
);
|
|
10568
|
+
volumeInspectionReady = true;
|
|
10569
|
+
volumeCapabilityForInspection = volumeCapability;
|
|
10570
|
+
const preparedSpotLightProjector = prepareSpotLightProjectorFrame(
|
|
10571
|
+
internals,
|
|
10572
|
+
world,
|
|
10573
|
+
worlds,
|
|
10574
|
+
lights,
|
|
10575
|
+
volumetricFog
|
|
10576
|
+
);
|
|
10577
|
+
const preparedVolumetricFog = prepareVolumetricFogFrame(
|
|
10578
|
+
internals,
|
|
10579
|
+
frameState,
|
|
10580
|
+
world,
|
|
10581
|
+
worlds,
|
|
10582
|
+
volumetricFog,
|
|
10583
|
+
lights,
|
|
10584
|
+
volumeCapability,
|
|
10585
|
+
preparedSpotLightProjector
|
|
10586
|
+
);
|
|
10587
|
+
preparedVolumetricFogForInspection = preparedVolumetricFog;
|
|
10588
|
+
const acceptedFallback = volumeCapability && volumetricFog !== void 0 && preparedVolumetricFog === void 0 ? frameState.volumetricFogAccepted : void 0;
|
|
10589
|
+
const acceptedFallbackContext = acceptedFallback === void 0 ? void 0 : stageAcceptedVolumetricFogFrame(
|
|
10590
|
+
internals,
|
|
10591
|
+
frameState,
|
|
10592
|
+
frameState.volumetricFogAcceptedContext
|
|
10593
|
+
);
|
|
10594
|
+
const graphVolumetricFog = volumeCapability ? preparedVolumetricFog === void 0 ? acceptedFallback : volumetricFog : void 0;
|
|
10595
|
+
const baseVolumetricFogContext = preparedVolumetricFog ?? acceptedFallbackContext;
|
|
10596
|
+
const volumeTopologyCandidate = graphVolumetricFog?.status === "available" && baseVolumetricFogContext !== void 0 || graphVolumetricFog === void 0 && frameState.volumetricFogAccepted !== void 0 || volumetricFog?.status === "degraded" && acceptedFallback !== void 0;
|
|
9050
10597
|
let preparedGpuDriven;
|
|
9051
10598
|
if (gpuDriven !== void 0) {
|
|
9052
10599
|
const prepared = gpuDriven.owner.prepare({
|
|
@@ -9079,7 +10626,9 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
9079
10626
|
preparedGpuDriven,
|
|
9080
10627
|
featureGraphCandidate,
|
|
9081
10628
|
cameras.length === 0,
|
|
9082
|
-
transmissionDemand
|
|
10629
|
+
transmissionDemand,
|
|
10630
|
+
graphVolumetricFog,
|
|
10631
|
+
volumeTopologyCandidate
|
|
9083
10632
|
)
|
|
9084
10633
|
);
|
|
9085
10634
|
if (preflightGraph === null) return false;
|
|
@@ -9151,6 +10700,35 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
9151
10700
|
})
|
|
9152
10701
|
} : { kind: "taa", view: temporalView };
|
|
9153
10702
|
const recordCamera = temporalView === void 0 ? camera : { ...camera, temporal: temporalView };
|
|
10703
|
+
let activeVolumetricFogContext;
|
|
10704
|
+
let activeVolumetricFogSignature;
|
|
10705
|
+
if (graphVolumetricFog?.status === "available" && baseVolumetricFogContext !== void 0) {
|
|
10706
|
+
const resolvedHistory = resolveVolumetricFogHistoryContext(
|
|
10707
|
+
frameState,
|
|
10708
|
+
preflightGraph,
|
|
10709
|
+
graphVolumetricFog,
|
|
10710
|
+
baseVolumetricFogContext,
|
|
10711
|
+
camera,
|
|
10712
|
+
lights,
|
|
10713
|
+
targetW,
|
|
10714
|
+
targetH
|
|
10715
|
+
);
|
|
10716
|
+
activeVolumetricFogContext = resolvedHistory.context;
|
|
10717
|
+
activeVolumetricFogSignature = resolvedHistory.signature;
|
|
10718
|
+
const historyFlag = new Float32Array([activeVolumetricFogContext.historyValid ? 1 : 0]);
|
|
10719
|
+
if (frameState.volumetricFogPendingParams !== void 0) {
|
|
10720
|
+
frameState.volumetricFogPendingParams[31] = historyFlag[0] ?? 0;
|
|
10721
|
+
}
|
|
10722
|
+
const historyFlagWritten = internals.device.queue.writeBuffer(
|
|
10723
|
+
activeVolumetricFogContext.paramsBuffer,
|
|
10724
|
+
31 * Float32Array.BYTES_PER_ELEMENT,
|
|
10725
|
+
historyFlag
|
|
10726
|
+
);
|
|
10727
|
+
if (!historyFlagWritten.ok) {
|
|
10728
|
+
internals.errorRegistry.fire(historyFlagWritten.error);
|
|
10729
|
+
return false;
|
|
10730
|
+
}
|
|
10731
|
+
}
|
|
9154
10732
|
const depthView = null;
|
|
9155
10733
|
const clear = [
|
|
9156
10734
|
camera.clearColor[0],
|
|
@@ -9227,7 +10805,13 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
9227
10805
|
}
|
|
9228
10806
|
ldrSpritePassView = unormViewRes.value;
|
|
9229
10807
|
}
|
|
9230
|
-
|
|
10808
|
+
const currentViewProjection = mat4.create();
|
|
10809
|
+
mat4.multiply(
|
|
10810
|
+
currentViewProjection,
|
|
10811
|
+
computeProjectionMatrix(camera),
|
|
10812
|
+
computeViewMatrix(camera)
|
|
10813
|
+
);
|
|
10814
|
+
if (validatedOrdered.length > 0 || skyboxActive || activeVolumetricFogContext !== void 0 || preparedVolumetricFog !== void 0) {
|
|
9231
10815
|
runRecordProfilePhase(profilePhase, "record/uploads", () => {
|
|
9232
10816
|
writeViewUbo(
|
|
9233
10817
|
internals.device.queue,
|
|
@@ -9341,6 +10925,9 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
9341
10925
|
geometryColorResolveView,
|
|
9342
10926
|
ldrSpriteColorView,
|
|
9343
10927
|
postProcessParams: framePostProcessParams,
|
|
10928
|
+
...internals.volumetricFogShaders === void 0 ? {} : { volumetricFogShaders: internals.volumetricFogShaders },
|
|
10929
|
+
...activeVolumetricFogContext === void 0 ? {} : { volumetricFog: activeVolumetricFogContext },
|
|
10930
|
+
...preparedSpotLightProjector === void 0 ? {} : { spotLightProjector: preparedSpotLightProjector },
|
|
9344
10931
|
dispatch: transparentDispatch,
|
|
9345
10932
|
hdrpClusterBindGroup,
|
|
9346
10933
|
hdrpClusterMembershipBindGroup,
|
|
@@ -9354,7 +10941,21 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
9354
10941
|
directionalShadowCacheReuse: directionalShadowCache.reuse,
|
|
9355
10942
|
...profilePhase !== void 0 ? { profilePhase } : {}
|
|
9356
10943
|
};
|
|
9357
|
-
const
|
|
10944
|
+
const stagedTemporalFrame = {
|
|
10945
|
+
frameId: frameState.frameNumber,
|
|
10946
|
+
currentViewProjection: new Float32Array(currentViewProjection),
|
|
10947
|
+
jitter: [0, 0],
|
|
10948
|
+
viewport: { width: targetW, height: targetH },
|
|
10949
|
+
cameraPosition: [
|
|
10950
|
+
camera.position[0] ?? 0,
|
|
10951
|
+
camera.position[1] ?? 0,
|
|
10952
|
+
camera.position[2] ?? 0
|
|
10953
|
+
]
|
|
10954
|
+
};
|
|
10955
|
+
frameState.temporalFrameInput = stagedTemporalFrame;
|
|
10956
|
+
frameState.temporalFrameTransaction.stage(stagedTemporalFrame);
|
|
10957
|
+
volumeSubmissionAttempted = volumeTopologyCandidate || activeVolumetricFogContext !== void 0;
|
|
10958
|
+
submitted = runRecordProfilePhase(
|
|
9358
10959
|
profilePhase,
|
|
9359
10960
|
"record/graph-execute",
|
|
9360
10961
|
() => executeCompiledFrameGraph(
|
|
@@ -9362,14 +10963,56 @@ function recordFrame(internals, world, cameras, lights, renderables, transparent
|
|
|
9362
10963
|
frameState,
|
|
9363
10964
|
passCtx,
|
|
9364
10965
|
encoder,
|
|
9365
|
-
profilePhase === void 0 ? void 0 : (pass, encode) => profilePhase(graphExecutionPhase(pass.name), encode)
|
|
10966
|
+
profilePhase === void 0 ? void 0 : (pass, encode) => profilePhase(graphExecutionPhase(pass.name), encode),
|
|
10967
|
+
timingCapture
|
|
9366
10968
|
)
|
|
9367
10969
|
);
|
|
9368
10970
|
if (submitted && !directionalShadowCache.reuse) {
|
|
9369
10971
|
frameState.directionalShadowCache = frameState.directionalShadowCacheRecorded ? directionalShadowCache.next : null;
|
|
9370
10972
|
}
|
|
10973
|
+
if (submitted) {
|
|
10974
|
+
if (graphVolumetricFog === void 0) {
|
|
10975
|
+
frameState.volumetricFogAccepted = void 0;
|
|
10976
|
+
frameState.volumetricFogAcceptedContext = void 0;
|
|
10977
|
+
frameState.volumetricFogHistoryGraph = null;
|
|
10978
|
+
frameState.volumetricFogHistorySlot = null;
|
|
10979
|
+
frameState.volumetricFogHistorySignature = null;
|
|
10980
|
+
retireVolumetricFogParams(internals, frameState);
|
|
10981
|
+
} else if (activeVolumetricFogContext !== void 0 && activeVolumetricFogSignature !== void 0) {
|
|
10982
|
+
frameState.volumetricFogHistoryGraph = preflightGraph;
|
|
10983
|
+
frameState.volumetricFogHistorySlot = activeVolumetricFogContext.historyWriteSlot;
|
|
10984
|
+
frameState.volumetricFogHistorySignature = activeVolumetricFogSignature;
|
|
10985
|
+
if (frameState.volumetricFogParamsPendingSlot !== null) {
|
|
10986
|
+
frameState.volumetricFogParamsAcceptedSlot = frameState.volumetricFogParamsPendingSlot;
|
|
10987
|
+
frameState.volumetricFogAcceptedParams = frameState.volumetricFogPendingParams === void 0 ? void 0 : new Float32Array(frameState.volumetricFogPendingParams);
|
|
10988
|
+
frameState.volumetricFogParamsPendingSlot = null;
|
|
10989
|
+
frameState.volumetricFogPendingParams = void 0;
|
|
10990
|
+
}
|
|
10991
|
+
if (volumetricFog?.status === "available" && preparedVolumetricFog !== void 0) {
|
|
10992
|
+
frameState.volumetricFogAccepted = volumetricFog;
|
|
10993
|
+
frameState.volumetricFogAcceptedContext = activeVolumetricFogContext;
|
|
10994
|
+
} else if (acceptedFallback !== void 0) {
|
|
10995
|
+
frameState.volumetricFogAcceptedContext = activeVolumetricFogContext;
|
|
10996
|
+
}
|
|
10997
|
+
} else if (volumetricFog?.status === "available" && preparedVolumetricFog !== void 0) {
|
|
10998
|
+
frameState.volumetricFogAccepted = volumetricFog;
|
|
10999
|
+
frameState.volumetricFogAcceptedContext = preparedVolumetricFog;
|
|
11000
|
+
}
|
|
11001
|
+
}
|
|
9371
11002
|
return submitted;
|
|
9372
11003
|
} finally {
|
|
11004
|
+
settleVolumetricFogGraphCandidate(frameState, submitted);
|
|
11005
|
+
if (volumeInspectionReady && (submitted || volumeSubmissionAttempted)) {
|
|
11006
|
+
updateVolumetricFogInspection(
|
|
11007
|
+
frameState,
|
|
11008
|
+
volumetricFog,
|
|
11009
|
+
lights,
|
|
11010
|
+
preparedVolumetricFogForInspection,
|
|
11011
|
+
submitted,
|
|
11012
|
+
volumeCapabilityForInspection,
|
|
11013
|
+
volumeSubmissionAttempted
|
|
11014
|
+
);
|
|
11015
|
+
}
|
|
9373
11016
|
frameState.frameNumber += 1;
|
|
9374
11017
|
frameState.hdrpOncePerFrameFired.clear();
|
|
9375
11018
|
}
|
|
@@ -10728,6 +12371,131 @@ function selectFogFrame(world) {
|
|
|
10728
12371
|
});
|
|
10729
12372
|
throw fogSelection.error;
|
|
10730
12373
|
}
|
|
12374
|
+
function textureDigest(texture) {
|
|
12375
|
+
let hash = 2166136261;
|
|
12376
|
+
for (const byte of texture.data) hash = Math.imul(hash ^ byte, 16777619) >>> 0;
|
|
12377
|
+
return `fnv1a:${hash.toString(16).padStart(8, "0")}`;
|
|
12378
|
+
}
|
|
12379
|
+
function extractVolumeSnapshot(world, assets, worldInternal) {
|
|
12380
|
+
const query = world.query({ read: [VolumetricFog] }).unwrap();
|
|
12381
|
+
const authorings = [];
|
|
12382
|
+
let selectedLight;
|
|
12383
|
+
let first;
|
|
12384
|
+
for (const row of query) {
|
|
12385
|
+
const source = row.get(VolumetricFog);
|
|
12386
|
+
if (source.light === null) continue;
|
|
12387
|
+
const selected = resolveSelectedVolumetricLight(world, source.light);
|
|
12388
|
+
if (selected.status === "available") {
|
|
12389
|
+
const pairSpot = source.spotLight === null ? void 0 : source.spotLight;
|
|
12390
|
+
selectedLight ??= {
|
|
12391
|
+
entity: selected.entity,
|
|
12392
|
+
kind: selected.kind,
|
|
12393
|
+
...selected.kind === "point" ? { pointLightEntity: selected.entity } : {},
|
|
12394
|
+
...pairSpot === void 0 ? {} : { spotLightEntity: pairSpot }
|
|
12395
|
+
};
|
|
12396
|
+
}
|
|
12397
|
+
const densityHandle = toShared(Math.round(Number(source.density)));
|
|
12398
|
+
const resolved2 = resolveAssetHandle(world, densityHandle);
|
|
12399
|
+
if (!resolved2.ok || resolved2.value.kind !== "texture") {
|
|
12400
|
+
worldInternal._routeError(
|
|
12401
|
+
resolved2.ok ? new RhiError({
|
|
12402
|
+
code: "asset-not-registered",
|
|
12403
|
+
expected: "VolumetricFog.density resolves to a TextureAsset",
|
|
12404
|
+
hint: "load a TextureAsset before assigning it to VolumetricFog.density",
|
|
12405
|
+
detail: { assetHandle: Number(densityHandle) }
|
|
12406
|
+
}) : resolved2.error,
|
|
12407
|
+
{ severity: Severity.Error, systemName: "RenderSystem.extract (volumetric-density)" }
|
|
12408
|
+
);
|
|
12409
|
+
return { status: "degraded" };
|
|
12410
|
+
}
|
|
12411
|
+
const densityAsset = resolved2.value;
|
|
12412
|
+
const guid = assets?.guidOf(densityAsset) ?? `handle:${Number(densityHandle)}`;
|
|
12413
|
+
const shape = densityAsset.shape;
|
|
12414
|
+
const density = shape.viewDimension === "3d" ? {
|
|
12415
|
+
guid,
|
|
12416
|
+
generation: 1,
|
|
12417
|
+
shape: {
|
|
12418
|
+
viewDimension: "3d",
|
|
12419
|
+
extent: {
|
|
12420
|
+
width: shape.extent.width,
|
|
12421
|
+
height: shape.extent.height,
|
|
12422
|
+
depth: shape.extent.depth
|
|
12423
|
+
}
|
|
12424
|
+
},
|
|
12425
|
+
format: densityAsset.format,
|
|
12426
|
+
colorSpace: densityAsset.colorSpace
|
|
12427
|
+
} : {
|
|
12428
|
+
guid,
|
|
12429
|
+
generation: 1,
|
|
12430
|
+
shape: {
|
|
12431
|
+
viewDimension: shape.viewDimension,
|
|
12432
|
+
extent: shape.viewDimension === "2d-array" ? {
|
|
12433
|
+
width: shape.extent.width,
|
|
12434
|
+
height: shape.extent.height,
|
|
12435
|
+
layers: shape.extent.layers
|
|
12436
|
+
} : { width: shape.extent.width, height: shape.extent.height }
|
|
12437
|
+
},
|
|
12438
|
+
format: densityAsset.format,
|
|
12439
|
+
colorSpace: densityAsset.colorSpace
|
|
12440
|
+
};
|
|
12441
|
+
const authoring = {
|
|
12442
|
+
light: source.light,
|
|
12443
|
+
density,
|
|
12444
|
+
bounds: {
|
|
12445
|
+
min: [source.boundsMin[0] ?? 0, source.boundsMin[1] ?? 0, source.boundsMin[2] ?? 0],
|
|
12446
|
+
max: [source.boundsMax[0] ?? 0, source.boundsMax[1] ?? 0, source.boundsMax[2] ?? 0]
|
|
12447
|
+
},
|
|
12448
|
+
extinction: [source.extinction[0] ?? 0, source.extinction[1] ?? 0, source.extinction[2] ?? 0],
|
|
12449
|
+
albedo: [source.albedo[0] ?? 0, source.albedo[1] ?? 0, source.albedo[2] ?? 0],
|
|
12450
|
+
emission: [source.emission[0] ?? 0, source.emission[1] ?? 0, source.emission[2] ?? 0],
|
|
12451
|
+
anisotropy: source.anisotropy,
|
|
12452
|
+
maxDistance: source.maxDistance
|
|
12453
|
+
};
|
|
12454
|
+
authorings.push(authoring);
|
|
12455
|
+
first ??= { densityHandle, densityAsset, guid };
|
|
12456
|
+
}
|
|
12457
|
+
if (authorings.length === 0) return void 0;
|
|
12458
|
+
const extracted = extractVolumetricFog(authorings);
|
|
12459
|
+
if (!extracted.ok) {
|
|
12460
|
+
worldInternal._routeError(extracted.error, {
|
|
12461
|
+
severity: Severity.Error,
|
|
12462
|
+
systemName: "RenderSystem.extract (volumetric-fog)"
|
|
12463
|
+
});
|
|
12464
|
+
return {
|
|
12465
|
+
status: "degraded",
|
|
12466
|
+
...first === void 0 ? {} : {
|
|
12467
|
+
densityHandle: first.densityHandle,
|
|
12468
|
+
densityAsset: first.densityAsset,
|
|
12469
|
+
guid: first.guid,
|
|
12470
|
+
generation: 1,
|
|
12471
|
+
digest: textureDigest(first.densityAsset),
|
|
12472
|
+
...selectedLight === void 0 ? {} : {
|
|
12473
|
+
lightEntity: selectedLight.entity,
|
|
12474
|
+
lightKind: selectedLight.kind,
|
|
12475
|
+
...selectedLight.pointLightEntity === void 0 ? {} : { pointLightEntity: selectedLight.pointLightEntity },
|
|
12476
|
+
...selectedLight.spotLightEntity === void 0 ? {} : { spotLightEntity: selectedLight.spotLightEntity }
|
|
12477
|
+
}
|
|
12478
|
+
}
|
|
12479
|
+
};
|
|
12480
|
+
}
|
|
12481
|
+
if (extracted.value.status === "off" || first === void 0) return { status: "off" };
|
|
12482
|
+
const selectedLightFields = selectedLight === void 0 ? {} : {
|
|
12483
|
+
lightEntity: selectedLight.entity,
|
|
12484
|
+
lightKind: selectedLight.kind,
|
|
12485
|
+
...selectedLight.pointLightEntity === void 0 ? {} : { pointLightEntity: selectedLight.pointLightEntity },
|
|
12486
|
+
...selectedLight.spotLightEntity === void 0 ? {} : { spotLightEntity: selectedLight.spotLightEntity }
|
|
12487
|
+
};
|
|
12488
|
+
return {
|
|
12489
|
+
status: "available",
|
|
12490
|
+
fog: extracted.value.fog,
|
|
12491
|
+
densityHandle: first.densityHandle,
|
|
12492
|
+
densityAsset: first.densityAsset,
|
|
12493
|
+
guid: first.guid,
|
|
12494
|
+
generation: 1,
|
|
12495
|
+
digest: textureDigest(first.densityAsset),
|
|
12496
|
+
...selectedLightFields
|
|
12497
|
+
};
|
|
12498
|
+
}
|
|
10731
12499
|
function extractFrames(worlds, owner, assets, pipelineState, materialSnapshotCachesByWorld, options = {}) {
|
|
10732
12500
|
const cameraOwner = typeof owner === "number" ? owner : owner.cameraOwner;
|
|
10733
12501
|
const resourceOwner = typeof owner === "number" ? owner : owner.resourceOwner;
|
|
@@ -10768,9 +12536,9 @@ function extractFrames(worlds, owner, assets, pipelineState, materialSnapshotCac
|
|
|
10768
12536
|
});
|
|
10769
12537
|
const frame = extractFrame(world, prepared);
|
|
10770
12538
|
framesByWorld.set(wi, frame);
|
|
10771
|
-
} catch (
|
|
12539
|
+
} catch (err32) {
|
|
10772
12540
|
try {
|
|
10773
|
-
createWorldInternalView(world)._routeError(
|
|
12541
|
+
createWorldInternalView(world)._routeError(err32, {
|
|
10774
12542
|
severity: Severity.Error,
|
|
10775
12543
|
systemName: `RenderSystem.extractFrames(world[${wi}])`
|
|
10776
12544
|
});
|
|
@@ -10899,6 +12667,7 @@ function extractFrames(worlds, owner, assets, pipelineState, materialSnapshotCac
|
|
|
10899
12667
|
directionalCsmConfig,
|
|
10900
12668
|
directionalCsmDirection
|
|
10901
12669
|
};
|
|
12670
|
+
const volumetricFog = resourceOwnerFrame?.volumetricFog;
|
|
10902
12671
|
const skylight = resourceOwnerFrame?.skylight;
|
|
10903
12672
|
const skylightCount = resourceOwnerFrame?.skylightCount ?? 0;
|
|
10904
12673
|
const skybox = resourceOwnerFrame?.skybox;
|
|
@@ -10923,6 +12692,7 @@ function extractFrames(worlds, owner, assets, pipelineState, materialSnapshotCac
|
|
|
10923
12692
|
return {
|
|
10924
12693
|
cameras,
|
|
10925
12694
|
lights,
|
|
12695
|
+
...volumetricFog === void 0 ? {} : { volumetricFog: { ...volumetricFog, worldId: resourceOwner } },
|
|
10926
12696
|
renderables,
|
|
10927
12697
|
dispatch: dispatchEntries,
|
|
10928
12698
|
skylight,
|
|
@@ -10991,6 +12761,7 @@ function extractFrame(world, context) {
|
|
|
10991
12761
|
const skinPaletteAllocator = pipelineState?.skinPaletteAllocator ?? null;
|
|
10992
12762
|
const directionalLightQuery = world.query({ read: [DirectionalLight] }).unwrap();
|
|
10993
12763
|
const worldInternal = createWorldInternalView(world);
|
|
12764
|
+
let volumetricFog = extractVolumeSnapshot(world, assets, worldInternal);
|
|
10994
12765
|
const cameras = extractCameraSnapshots(world);
|
|
10995
12766
|
let directional;
|
|
10996
12767
|
let directionalCount = 0;
|
|
@@ -11002,6 +12773,7 @@ function extractFrame(world, context) {
|
|
|
11002
12773
|
const intensity = l.intensity;
|
|
11003
12774
|
const snapshot = {
|
|
11004
12775
|
kind: "directional",
|
|
12776
|
+
entity: row.entity,
|
|
11005
12777
|
direction: vec3.create(l.direction[0] ?? 0, l.direction[1] ?? -1, l.direction[2] ?? 0),
|
|
11006
12778
|
color: vec3.create(
|
|
11007
12779
|
(l.color[0] ?? 1) * intensity,
|
|
@@ -11045,6 +12817,7 @@ function extractFrame(world, context) {
|
|
|
11045
12817
|
const position = worldMat !== void 0 ? mat4.getTranslation(vec3.create(), worldMat) : vec3.create(0, 0, 0);
|
|
11046
12818
|
pointSnapshots.push({
|
|
11047
12819
|
kind: "point",
|
|
12820
|
+
entity: entityId,
|
|
11048
12821
|
position,
|
|
11049
12822
|
color: vec3.create(
|
|
11050
12823
|
(p.color[0] ?? 1) * intensity,
|
|
@@ -11104,9 +12877,11 @@ function extractFrame(world, context) {
|
|
|
11104
12877
|
const sMapSize = s.mapSize;
|
|
11105
12878
|
const sNearPlane = s.nearPlane;
|
|
11106
12879
|
const sFarPlane = s.farPlane;
|
|
12880
|
+
const shadowIntensity = s.shadowIntensity;
|
|
12881
|
+
const hasProjector = Number(s.projector) > 0;
|
|
11107
12882
|
let lightViewProj2;
|
|
11108
12883
|
let shadowAtlasTile = -1;
|
|
11109
|
-
if (castShadow && hasValidDirection) {
|
|
12884
|
+
if ((castShadow || hasProjector) && hasValidDirection) {
|
|
11110
12885
|
const target4 = vec3.create(
|
|
11111
12886
|
(position[0] ?? 0) + (dirN[0] ?? 0),
|
|
11112
12887
|
(position[1] ?? 0) + (dirN[1] ?? 0),
|
|
@@ -11119,13 +12894,36 @@ function extractFrame(world, context) {
|
|
|
11119
12894
|
mat4.lookAt(view, position, target4, vec3.create(0, 1, 0));
|
|
11120
12895
|
lightViewProj2 = new Float32Array(16);
|
|
11121
12896
|
mat4.multiply(lightViewProj2, proj, view);
|
|
11122
|
-
if (spotTileNext < 4) {
|
|
12897
|
+
if (castShadow && spotTileNext < 4) {
|
|
11123
12898
|
shadowAtlasTile = spotTileNext;
|
|
11124
12899
|
spotTileNext += 1;
|
|
11125
12900
|
}
|
|
11126
12901
|
}
|
|
12902
|
+
let projectorHandle;
|
|
12903
|
+
let projectorAsset;
|
|
12904
|
+
let projectorGuid;
|
|
12905
|
+
if (hasProjector) {
|
|
12906
|
+
const candidate = toShared(Math.round(Number(s.projector)));
|
|
12907
|
+
const resolvedProjector = resolveAssetHandle(world, candidate);
|
|
12908
|
+
if (!resolvedProjector.ok || resolvedProjector.value.kind !== "texture") {
|
|
12909
|
+
worldInternal._routeError(
|
|
12910
|
+
resolvedProjector.ok ? new RhiError({
|
|
12911
|
+
code: "asset-not-registered",
|
|
12912
|
+
expected: "SpotLight.projector resolves to a TextureAsset",
|
|
12913
|
+
hint: "load the authored projector TextureAsset before assigning SpotLight.projector",
|
|
12914
|
+
detail: { assetHandle: Number(candidate) }
|
|
12915
|
+
}) : resolvedProjector.error,
|
|
12916
|
+
{ severity: Severity.Error, systemName: "RenderSystem.extract (spot-projector)" }
|
|
12917
|
+
);
|
|
12918
|
+
} else {
|
|
12919
|
+
projectorHandle = candidate;
|
|
12920
|
+
projectorAsset = resolvedProjector.value;
|
|
12921
|
+
projectorGuid = assets?.guidOf(projectorAsset) ?? `handle:${Number(candidate)}`;
|
|
12922
|
+
}
|
|
12923
|
+
}
|
|
11127
12924
|
spotSnapshots.push({
|
|
11128
12925
|
kind: "spot",
|
|
12926
|
+
entity: row.entity,
|
|
11129
12927
|
// D-6: position reflects world transform; direction stays sourced
|
|
11130
12928
|
// from SpotLight.direction (NOT rotated by the parent).
|
|
11131
12929
|
position,
|
|
@@ -11145,9 +12943,46 @@ function extractFrame(world, context) {
|
|
|
11145
12943
|
mapSize: sMapSize,
|
|
11146
12944
|
nearPlane: sNearPlane,
|
|
11147
12945
|
farPlane: sFarPlane,
|
|
11148
|
-
shadowAtlasTile
|
|
12946
|
+
shadowAtlasTile,
|
|
12947
|
+
shadowIntensity,
|
|
12948
|
+
depthBias: s.depthBias,
|
|
12949
|
+
normalBias: s.normalBias,
|
|
12950
|
+
pcfKernelSize: s.pcfKernelSize,
|
|
12951
|
+
...projectorHandle === void 0 || projectorAsset === void 0 || projectorGuid === void 0 ? {} : {
|
|
12952
|
+
projectorHandle,
|
|
12953
|
+
projectorAsset,
|
|
12954
|
+
projectorGuid,
|
|
12955
|
+
projectorGeneration: 1,
|
|
12956
|
+
projectorRevision: 1
|
|
12957
|
+
}
|
|
11149
12958
|
});
|
|
11150
12959
|
}
|
|
12960
|
+
let volumeSpot;
|
|
12961
|
+
const extractedFog = volumetricFog;
|
|
12962
|
+
if (extractedFog !== void 0) {
|
|
12963
|
+
if (extractedFog.spotLightEntity !== void 0) {
|
|
12964
|
+
volumeSpot = spotSnapshots.find((light) => light.entity === extractedFog.spotLightEntity);
|
|
12965
|
+
} else if (extractedFog.lightKind === "spot") {
|
|
12966
|
+
volumeSpot = spotSnapshots.find((light) => light.entity === extractedFog.lightEntity);
|
|
12967
|
+
}
|
|
12968
|
+
}
|
|
12969
|
+
if (volumetricFog !== void 0 && volumetricFog.status === "available" && volumeSpot?.projectorAsset !== void 0 && volumeSpot.projectorHandle !== void 0) {
|
|
12970
|
+
const projectorHandle = volumeSpot.projectorHandle;
|
|
12971
|
+
const projectorAsset = volumeSpot.projectorAsset;
|
|
12972
|
+
volumetricFog = {
|
|
12973
|
+
...volumetricFog,
|
|
12974
|
+
projector: {
|
|
12975
|
+
guid: volumeSpot.projectorGuid ?? `handle:${Number(volumeSpot.projectorHandle)}`,
|
|
12976
|
+
generation: volumeSpot.projectorGeneration ?? 1,
|
|
12977
|
+
view: "2d",
|
|
12978
|
+
sampler: "linear-clamp-to-edge",
|
|
12979
|
+
projection: "spot-projection",
|
|
12980
|
+
revision: volumeSpot.projectorRevision ?? 1
|
|
12981
|
+
},
|
|
12982
|
+
projectorHandle,
|
|
12983
|
+
projectorAsset
|
|
12984
|
+
};
|
|
12985
|
+
}
|
|
11151
12986
|
let lightViewProj;
|
|
11152
12987
|
let splitPlanes;
|
|
11153
12988
|
let cascadeCount;
|
|
@@ -11672,28 +13507,28 @@ function extractFrame(world, context) {
|
|
|
11672
13507
|
} else {
|
|
11673
13508
|
const resolvedResult = walkMaterialPassesOverSharedRefs(world, tagged, assets);
|
|
11674
13509
|
if (!resolvedResult.ok) {
|
|
11675
|
-
const
|
|
11676
|
-
switch (
|
|
13510
|
+
const err32 = resolvedResult.error;
|
|
13511
|
+
switch (err32.code) {
|
|
11677
13512
|
case "material-parent-not-found":
|
|
11678
13513
|
case "material-no-effective-pass":
|
|
11679
13514
|
case "material-value-unknown":
|
|
11680
13515
|
case "material-value-type-mismatch":
|
|
11681
13516
|
case "material-contract-program-mismatch":
|
|
11682
|
-
worldInternal._routeError(
|
|
13517
|
+
worldInternal._routeError(err32, {
|
|
11683
13518
|
severity: Severity.Error,
|
|
11684
|
-
systemName: `RenderSystem.extract (${
|
|
13519
|
+
systemName: `RenderSystem.extract (${err32.code})`
|
|
11685
13520
|
});
|
|
11686
13521
|
break;
|
|
11687
13522
|
case "material-circular-inheritance":
|
|
11688
|
-
worldInternal._routeError(
|
|
13523
|
+
worldInternal._routeError(err32, {
|
|
11689
13524
|
severity: Severity.Error,
|
|
11690
13525
|
systemName: "RenderSystem.extract (material-circular-inheritance)"
|
|
11691
13526
|
});
|
|
11692
13527
|
break;
|
|
11693
13528
|
default:
|
|
11694
|
-
worldInternal._routeError(
|
|
13529
|
+
worldInternal._routeError(err32, {
|
|
11695
13530
|
severity: Severity.Error,
|
|
11696
|
-
systemName: `RenderSystem.extract (_materialWalk: ${
|
|
13531
|
+
systemName: `RenderSystem.extract (_materialWalk: ${err32.code})`
|
|
11697
13532
|
});
|
|
11698
13533
|
}
|
|
11699
13534
|
continue;
|
|
@@ -12281,6 +14116,7 @@ function extractFrame(world, context) {
|
|
|
12281
14116
|
lights,
|
|
12282
14117
|
environment,
|
|
12283
14118
|
environmentReady: true,
|
|
14119
|
+
...volumetricFog === void 0 ? {} : { volumetricFog },
|
|
12284
14120
|
renderables,
|
|
12285
14121
|
dispatch,
|
|
12286
14122
|
skylight,
|
|
@@ -13693,7 +15529,8 @@ var RENDER_RELEVANT_COMPONENT_IDS = /* @__PURE__ */ new Set([
|
|
|
13693
15529
|
componentId(SpotLight),
|
|
13694
15530
|
componentId(Skylight),
|
|
13695
15531
|
componentId(SkyboxBackground),
|
|
13696
|
-
componentId(PostProcessParams)
|
|
15532
|
+
componentId(PostProcessParams),
|
|
15533
|
+
componentId(VolumetricFog)
|
|
13697
15534
|
]);
|
|
13698
15535
|
var NON_RENDERABLE_TRANSFORM_CONSUMERS = [
|
|
13699
15536
|
Camera,
|
|
@@ -14146,6 +15983,68 @@ var PersistentRenderScene = class {
|
|
|
14146
15983
|
this.options.onGpuError?.(error);
|
|
14147
15984
|
}
|
|
14148
15985
|
};
|
|
15986
|
+
function copyInput(input) {
|
|
15987
|
+
return {
|
|
15988
|
+
...input,
|
|
15989
|
+
currentViewProjection: new Float32Array(input.currentViewProjection),
|
|
15990
|
+
jitter: [input.jitter[0], input.jitter[1]],
|
|
15991
|
+
viewport: { ...input.viewport },
|
|
15992
|
+
...input.cameraPosition === void 0 ? {} : { cameraPosition: [...input.cameraPosition] }
|
|
15993
|
+
};
|
|
15994
|
+
}
|
|
15995
|
+
function copyFrame(frame) {
|
|
15996
|
+
return {
|
|
15997
|
+
...frame,
|
|
15998
|
+
currentViewProjection: new Float32Array(frame.currentViewProjection),
|
|
15999
|
+
previousViewProjection: frame.previousViewProjection === void 0 ? void 0 : new Float32Array(frame.previousViewProjection),
|
|
16000
|
+
jitter: [frame.jitter[0], frame.jitter[1]],
|
|
16001
|
+
viewport: { ...frame.viewport },
|
|
16002
|
+
currentCameraPosition: [...frame.currentCameraPosition],
|
|
16003
|
+
previousCameraPosition: frame.previousCameraPosition === void 0 ? void 0 : [...frame.previousCameraPosition]
|
|
16004
|
+
};
|
|
16005
|
+
}
|
|
16006
|
+
function createTemporalFrameTransaction(options) {
|
|
16007
|
+
let accepted;
|
|
16008
|
+
let staged;
|
|
16009
|
+
let historyEpoch = 0;
|
|
16010
|
+
return {
|
|
16011
|
+
stage(input) {
|
|
16012
|
+
staged = copyInput(input);
|
|
16013
|
+
},
|
|
16014
|
+
commit(submit) {
|
|
16015
|
+
if (!submit.accepted) {
|
|
16016
|
+
staged = void 0;
|
|
16017
|
+
return err$1(new TemporalFrameSubmitError(submit.reason ?? "queue-submit-failed"));
|
|
16018
|
+
}
|
|
16019
|
+
if (staged === void 0) {
|
|
16020
|
+
return err$1(new TemporalFrameSubmitError("queue-submit-failed"));
|
|
16021
|
+
}
|
|
16022
|
+
const resetReason = submit.resetReason ?? staged.resetReason ?? "none";
|
|
16023
|
+
const shouldReset = resetReason !== "none";
|
|
16024
|
+
if (shouldReset) historyEpoch += 1;
|
|
16025
|
+
const next = {
|
|
16026
|
+
frameId: staged.frameId,
|
|
16027
|
+
currentViewProjection: new Float32Array(staged.currentViewProjection),
|
|
16028
|
+
previousViewProjection: accepted === void 0 || shouldReset ? void 0 : new Float32Array(accepted.currentViewProjection),
|
|
16029
|
+
jitter: [staged.jitter[0], staged.jitter[1]],
|
|
16030
|
+
viewport: { ...staged.viewport },
|
|
16031
|
+
currentCameraPosition: staged.cameraPosition ?? [0, 0, 0],
|
|
16032
|
+
previousCameraPosition: accepted === void 0 || shouldReset ? void 0 : accepted.currentCameraPosition,
|
|
16033
|
+
resetReason,
|
|
16034
|
+
historyEpoch,
|
|
16035
|
+
deviceEpoch: options.deviceEpoch
|
|
16036
|
+
};
|
|
16037
|
+
accepted = next;
|
|
16038
|
+
staged = void 0;
|
|
16039
|
+
return ok$1(copyFrame(next));
|
|
16040
|
+
},
|
|
16041
|
+
snapshot() {
|
|
16042
|
+
if (accepted === void 0)
|
|
16043
|
+
throw new Error("TemporalFrame snapshot is unavailable before commit");
|
|
16044
|
+
return copyFrame(accepted);
|
|
16045
|
+
}
|
|
16046
|
+
};
|
|
16047
|
+
}
|
|
14149
16048
|
|
|
14150
16049
|
// src/temporal/inspection.ts
|
|
14151
16050
|
function gpuBytes(state) {
|
|
@@ -14795,10 +16694,26 @@ function createRenderSystem(internals) {
|
|
|
14795
16694
|
graphGeneration: 0,
|
|
14796
16695
|
successfulTemporalFrameIndex: 0,
|
|
14797
16696
|
lastSuccessfulBloom: "off",
|
|
16697
|
+
temporalFrameTransaction: createTemporalFrameTransaction({ deviceEpoch: 0 }),
|
|
16698
|
+
temporalFrameInput: void 0,
|
|
14798
16699
|
directionalShadowCache: null,
|
|
14799
16700
|
directionalShadowCacheRecorded: false,
|
|
14800
16701
|
compiledFrameGraph: null,
|
|
14801
16702
|
compiledFrameGraphTopologyKey: null,
|
|
16703
|
+
volumetricFogPreviousGraph: null,
|
|
16704
|
+
volumetricFogPreviousGraphKey: null,
|
|
16705
|
+
volumetricFogCandidateGraph: null,
|
|
16706
|
+
volumetricFogParamsBuffers: [null, null],
|
|
16707
|
+
volumetricFogParamsPendingSlot: null,
|
|
16708
|
+
volumetricFogParamsAcceptedSlot: null,
|
|
16709
|
+
volumetricFogAcceptedParams: void 0,
|
|
16710
|
+
volumetricFogPendingParams: void 0,
|
|
16711
|
+
volumetricFogInspection: inspectVolumetricFog({ authored: false, capability: "available" }),
|
|
16712
|
+
volumetricFogAccepted: void 0,
|
|
16713
|
+
volumetricFogAcceptedContext: void 0,
|
|
16714
|
+
volumetricFogHistoryGraph: null,
|
|
16715
|
+
volumetricFogHistorySlot: null,
|
|
16716
|
+
volumetricFogHistorySignature: null,
|
|
14802
16717
|
retiredCompiledFrameGraphs: /* @__PURE__ */ new Set(),
|
|
14803
16718
|
currentFrameObservationSource: void 0,
|
|
14804
16719
|
lastSuccessfulCameraAntialias: void 0,
|
|
@@ -15287,18 +17202,18 @@ function createRenderSystem(internals) {
|
|
|
15287
17202
|
const camera = latestCamera;
|
|
15288
17203
|
if (camera?.motionBlur === void 0) return void 0;
|
|
15289
17204
|
const demanded = camera.motionBlur.shutterAngle > 0;
|
|
15290
|
-
const
|
|
17205
|
+
const unavailable3 = demanded && !internals.device.caps.rgba16floatRenderable;
|
|
15291
17206
|
const admitted = frameState.activeTemporalGpuState !== void 0 || frameState.temporalGpuState !== void 0;
|
|
15292
17207
|
return {
|
|
15293
17208
|
enabled: demanded,
|
|
15294
|
-
status: !demanded ? "off" :
|
|
17209
|
+
status: !demanded ? "off" : unavailable3 || !admitted ? "reset" : "active",
|
|
15295
17210
|
shutterAngle: camera.motionBlur.shutterAngle,
|
|
15296
17211
|
maxRadiusPixels: camera.motionBlur.maxRadiusPixels,
|
|
15297
17212
|
sampleCount: camera.motionBlur.sampleCount,
|
|
15298
17213
|
temporalDemand: demanded ? "scene-data-temporal-v1" : "none",
|
|
15299
17214
|
passName: "motion-blur",
|
|
15300
17215
|
historyWrites: 0,
|
|
15301
|
-
...
|
|
17216
|
+
...unavailable3 ? { lastFailure: "scene-data-unavailable" } : {}
|
|
15302
17217
|
};
|
|
15303
17218
|
},
|
|
15304
17219
|
get temporalTargetInspection() {
|
|
@@ -15359,7 +17274,7 @@ function createRenderSystem(internals) {
|
|
|
15359
17274
|
detachScene(world) {
|
|
15360
17275
|
persistentRenderScene.detach(world);
|
|
15361
17276
|
},
|
|
15362
|
-
draw(worlds, opts, renderReadLeases) {
|
|
17277
|
+
draw(worlds, opts, renderReadLeases, timingCapture) {
|
|
15363
17278
|
preparedWorlds = worlds;
|
|
15364
17279
|
const profileSession = internals.profiler?.activeSession();
|
|
15365
17280
|
let ownsProfileFrame = false;
|
|
@@ -15402,6 +17317,7 @@ function createRenderSystem(internals) {
|
|
|
15402
17317
|
lights,
|
|
15403
17318
|
environment,
|
|
15404
17319
|
environmentReady,
|
|
17320
|
+
volumetricFog,
|
|
15405
17321
|
renderables,
|
|
15406
17322
|
dispatch,
|
|
15407
17323
|
skylight,
|
|
@@ -15573,7 +17489,9 @@ function createRenderSystem(internals) {
|
|
|
15573
17489
|
environment?.environmentSignature ?? "",
|
|
15574
17490
|
environment?.fogSignature ?? "",
|
|
15575
17491
|
environmentReady && environment !== void 0,
|
|
15576
|
-
persistentRenderScene.transmissionTopologyDemand()
|
|
17492
|
+
persistentRenderScene.transmissionTopologyDemand(),
|
|
17493
|
+
volumetricFog,
|
|
17494
|
+
timingCapture
|
|
15577
17495
|
)
|
|
15578
17496
|
);
|
|
15579
17497
|
if (submitted) {
|
|
@@ -15614,16 +17532,16 @@ function createRenderSystem(internals) {
|
|
|
15614
17532
|
if (!retired.ok) internals.errorRegistry.fire(retired.error);
|
|
15615
17533
|
}
|
|
15616
17534
|
}
|
|
15617
|
-
} catch (
|
|
17535
|
+
} catch (err32) {
|
|
15618
17536
|
if (!submitted) {
|
|
15619
17537
|
internals.getPipelineState()?.perPassResources.discardBloomResources?.();
|
|
15620
17538
|
}
|
|
15621
|
-
const innerError =
|
|
15622
|
-
name:
|
|
15623
|
-
code:
|
|
15624
|
-
message:
|
|
15625
|
-
detail:
|
|
15626
|
-
} :
|
|
17539
|
+
const innerError = err32 instanceof PipelineSpecError ? {
|
|
17540
|
+
name: err32.name,
|
|
17541
|
+
code: err32.code,
|
|
17542
|
+
message: err32.message,
|
|
17543
|
+
detail: err32.detail
|
|
17544
|
+
} : err32 instanceof RhiError ? err32 : { code: "unknown", message: String(err32), name: err32?.name };
|
|
15627
17545
|
internals.errorRegistry.fire(
|
|
15628
17546
|
new RhiError({
|
|
15629
17547
|
code: "webgpu-runtime-error",
|
|
@@ -15666,6 +17584,17 @@ function createRenderSystem(internals) {
|
|
|
15666
17584
|
frameState.graphTargetCapture = request;
|
|
15667
17585
|
},
|
|
15668
17586
|
bindGroupCounts,
|
|
17587
|
+
get temporalFrame() {
|
|
17588
|
+
const accepted = frameState.temporalFrame;
|
|
17589
|
+
if (accepted === void 0) return void 0;
|
|
17590
|
+
return {
|
|
17591
|
+
...accepted,
|
|
17592
|
+
currentViewProjection: new Float32Array(accepted.currentViewProjection),
|
|
17593
|
+
previousViewProjection: accepted.previousViewProjection === void 0 ? void 0 : new Float32Array(accepted.previousViewProjection),
|
|
17594
|
+
jitter: [accepted.jitter[0], accepted.jitter[1]],
|
|
17595
|
+
viewport: { ...accepted.viewport }
|
|
17596
|
+
};
|
|
17597
|
+
},
|
|
15669
17598
|
frustumStats: lastFrustumStats,
|
|
15670
17599
|
visibilityStats: lastVisibilityStats,
|
|
15671
17600
|
get meshMaterialBindings() {
|
|
@@ -15680,6 +17609,9 @@ function createRenderSystem(internals) {
|
|
|
15680
17609
|
get perFrameGraphInfo() {
|
|
15681
17610
|
return frameState.compiledFrameGraph?.inspect();
|
|
15682
17611
|
},
|
|
17612
|
+
get volumetricFog() {
|
|
17613
|
+
return frameState.volumetricFogInspection;
|
|
17614
|
+
},
|
|
15683
17615
|
configureStandard(config) {
|
|
15684
17616
|
const profileConfig = internals.standardProfile;
|
|
15685
17617
|
const resolvedConfig = profileConfig === void 0 ? config : {
|
|
@@ -15790,6 +17722,22 @@ function createRenderSystem(internals) {
|
|
|
15790
17722
|
retireTemporalGpuState(retiring);
|
|
15791
17723
|
}
|
|
15792
17724
|
frameState.retiringTemporalGpuStates.clear();
|
|
17725
|
+
settleVolumetricFogGraphCandidate(frameState, false);
|
|
17726
|
+
for (const buffer of frameState.volumetricFogParamsBuffers) {
|
|
17727
|
+
if (buffer === null) continue;
|
|
17728
|
+
const destroyed = internals.device.destroyBuffer(buffer);
|
|
17729
|
+
if (!destroyed.ok) internals.errorRegistry.fire(destroyed.error);
|
|
17730
|
+
}
|
|
17731
|
+
frameState.volumetricFogParamsBuffers = [null, null];
|
|
17732
|
+
frameState.volumetricFogParamsPendingSlot = null;
|
|
17733
|
+
frameState.volumetricFogParamsAcceptedSlot = null;
|
|
17734
|
+
frameState.volumetricFogAcceptedParams = void 0;
|
|
17735
|
+
frameState.volumetricFogPendingParams = void 0;
|
|
17736
|
+
frameState.volumetricFogAccepted = void 0;
|
|
17737
|
+
frameState.volumetricFogAcceptedContext = void 0;
|
|
17738
|
+
frameState.volumetricFogHistoryGraph = null;
|
|
17739
|
+
frameState.volumetricFogHistorySlot = null;
|
|
17740
|
+
frameState.volumetricFogHistorySignature = null;
|
|
15793
17741
|
const compiled = frameState.compiledFrameGraph;
|
|
15794
17742
|
frameState.compiledFrameGraph = null;
|
|
15795
17743
|
frameState.compiledFrameGraphTopologyKey = null;
|
|
@@ -15825,9 +17773,23 @@ function createRenderSystem(internals) {
|
|
|
15825
17773
|
gpuDrivenProduction = new GpuDrivenProduction(internals.device, gpuDrivenShaderFactory);
|
|
15826
17774
|
disposeFeatureGpuWork();
|
|
15827
17775
|
internals.getPipelineState()?.perPassResources.drainBloomResources?.();
|
|
17776
|
+
settleVolumetricFogGraphCandidate(frameState, false);
|
|
15828
17777
|
frameState.compiledFrameGraph?.retire().catch(() => void 0);
|
|
15829
17778
|
frameState.compiledFrameGraph = null;
|
|
15830
17779
|
frameState.compiledFrameGraphTopologyKey = null;
|
|
17780
|
+
frameState.volumetricFogPreviousGraph = null;
|
|
17781
|
+
frameState.volumetricFogPreviousGraphKey = null;
|
|
17782
|
+
frameState.volumetricFogCandidateGraph = null;
|
|
17783
|
+
frameState.volumetricFogAccepted = void 0;
|
|
17784
|
+
frameState.volumetricFogAcceptedContext = void 0;
|
|
17785
|
+
frameState.volumetricFogHistoryGraph = null;
|
|
17786
|
+
frameState.volumetricFogHistorySlot = null;
|
|
17787
|
+
frameState.volumetricFogHistorySignature = null;
|
|
17788
|
+
frameState.volumetricFogParamsBuffers = [null, null];
|
|
17789
|
+
frameState.volumetricFogParamsPendingSlot = null;
|
|
17790
|
+
frameState.volumetricFogParamsAcceptedSlot = null;
|
|
17791
|
+
frameState.volumetricFogAcceptedParams = void 0;
|
|
17792
|
+
frameState.volumetricFogPendingParams = void 0;
|
|
15831
17793
|
frameState.directionalShadowCache = null;
|
|
15832
17794
|
frameState.directionalShadowCacheRecorded = false;
|
|
15833
17795
|
frameState.currentFrameObservationSource = void 0;
|
|
@@ -16843,19 +18805,19 @@ function toGpuVertexBufferLayouts(entries) {
|
|
|
16843
18805
|
}))
|
|
16844
18806
|
}));
|
|
16845
18807
|
}
|
|
16846
|
-
function selectHdrpPbrPrewarmVariants(manifestEntry, storageBufferCapable, transmissionCapable) {
|
|
18808
|
+
function selectHdrpPbrPrewarmVariants(manifestEntry, storageBufferCapable, transmissionCapable, projectorAvailable = true) {
|
|
16847
18809
|
return manifestEntry?.variants.filter(
|
|
16848
|
-
(variant) => variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable && variant.defines.CLUSTER_FORWARD_AVAILABLE === true && (transmissionCapable || variant.defines.TRANSMISSION_AVAILABLE !== true)
|
|
18810
|
+
(variant) => variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable && variant.defines.CLUSTER_FORWARD_AVAILABLE === true && (!("PROJECTOR_AVAILABLE" in variant.defines) || variant.defines.PROJECTOR_AVAILABLE === projectorAvailable) && (transmissionCapable || variant.defines.TRANSMISSION_AVAILABLE !== true)
|
|
16849
18811
|
) ?? [];
|
|
16850
18812
|
}
|
|
16851
|
-
function selectStandardPbrTransmissionPrewarmVariants(manifestEntry, storageBufferCapable) {
|
|
18813
|
+
function selectStandardPbrTransmissionPrewarmVariants(manifestEntry, storageBufferCapable, projectorAvailable = true) {
|
|
16852
18814
|
if (manifestEntry === void 0) {
|
|
16853
18815
|
throw new Error("Standard material shader manifest row is missing");
|
|
16854
18816
|
}
|
|
16855
18817
|
const selected = [];
|
|
16856
18818
|
for (const transmissionAvailable of [false, true]) {
|
|
16857
18819
|
const declared = manifestEntry.variants.find(
|
|
16858
|
-
(variant) => variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable && variant.defines.CLUSTER_FORWARD_AVAILABLE === false && variant.defines.VERTEX_COLOR_AVAILABLE === false && variant.defines.TRANSMISSION_AVAILABLE === transmissionAvailable
|
|
18820
|
+
(variant) => variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable && variant.defines.CLUSTER_FORWARD_AVAILABLE === false && variant.defines.VERTEX_COLOR_AVAILABLE === false && (!("PROJECTOR_AVAILABLE" in variant.defines) || variant.defines.PROJECTOR_AVAILABLE === projectorAvailable) && variant.defines.TRANSMISSION_AVAILABLE === transmissionAvailable
|
|
16859
18821
|
);
|
|
16860
18822
|
if (declared === void 0) {
|
|
16861
18823
|
throw new Error(
|
|
@@ -16872,11 +18834,11 @@ function selectStandardPbrTransmissionPrewarmVariants(manifestEntry, storageBuff
|
|
|
16872
18834
|
}
|
|
16873
18835
|
return selected;
|
|
16874
18836
|
}
|
|
16875
|
-
function selectNoColorPbrVariant(manifestEntry, storageBufferCapable, variantSet) {
|
|
18837
|
+
function selectNoColorPbrVariant(manifestEntry, storageBufferCapable, variantSet, projectorAvailable = true) {
|
|
16876
18838
|
const hdrpRequested = variantSet === "" || // biome-ignore lint/complexity/useOptionalChain: G-14 requires an explicit undefined guard so no variant request becomes false.
|
|
16877
18839
|
variantSet !== void 0 && variantSet.includes("CLUSTER_FORWARD_AVAILABLE=true");
|
|
16878
18840
|
return manifestEntry?.variants.find(
|
|
16879
|
-
(variant) => variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable && variant.defines.VERTEX_COLOR_AVAILABLE === false && (!("CLUSTER_FORWARD_AVAILABLE" in variant.defines) || variant.defines.CLUSTER_FORWARD_AVAILABLE === hdrpRequested)
|
|
18841
|
+
(variant) => variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable && variant.defines.VERTEX_COLOR_AVAILABLE === false && (!("PROJECTOR_AVAILABLE" in variant.defines) || variant.defines.PROJECTOR_AVAILABLE === projectorAvailable) && (!("CLUSTER_FORWARD_AVAILABLE" in variant.defines) || variant.defines.CLUSTER_FORWARD_AVAILABLE === hdrpRequested)
|
|
16880
18842
|
);
|
|
16881
18843
|
}
|
|
16882
18844
|
function resolveMaterialShaderBindingContract(source) {
|
|
@@ -16910,6 +18872,30 @@ function resolveMaterialShaderVertexInputContract(source) {
|
|
|
16910
18872
|
}
|
|
16911
18873
|
return /@vertex\s*fn[^{]*\([^)]*@location\s*\(/u.test(withoutComments) ? "render-material" : "none";
|
|
16912
18874
|
}
|
|
18875
|
+
function isSharedMaterialUserRegionCompatible(paramSchema) {
|
|
18876
|
+
const shared = buildPbrMaterialUserRegionEntries(DEFAULT_STANDARD_PBR_PARAM_SCHEMA);
|
|
18877
|
+
const candidate = buildPbrMaterialUserRegionEntries(paramSchema);
|
|
18878
|
+
if (candidate.length > shared.length) return false;
|
|
18879
|
+
return candidate.every((entry, index) => {
|
|
18880
|
+
const canonical = shared[index];
|
|
18881
|
+
if (canonical === void 0) return false;
|
|
18882
|
+
return JSON.stringify({
|
|
18883
|
+
binding: entry.binding,
|
|
18884
|
+
visibility: entry.visibility,
|
|
18885
|
+
buffer: entry.buffer ?? null,
|
|
18886
|
+
sampler: entry.sampler ?? null,
|
|
18887
|
+
texture: entry.texture ?? null,
|
|
18888
|
+
storageTexture: entry.storageTexture ?? null
|
|
18889
|
+
}) === JSON.stringify({
|
|
18890
|
+
binding: canonical.binding,
|
|
18891
|
+
visibility: canonical.visibility,
|
|
18892
|
+
buffer: canonical.buffer ?? null,
|
|
18893
|
+
sampler: canonical.sampler ?? null,
|
|
18894
|
+
texture: canonical.texture ?? null,
|
|
18895
|
+
storageTexture: canonical.storageTexture ?? null
|
|
18896
|
+
});
|
|
18897
|
+
});
|
|
18898
|
+
}
|
|
16913
18899
|
function allowsUnlitPreparedFallback(depthFormatOverride, vertexLayout, materialShaderId) {
|
|
16914
18900
|
return depthFormatOverride === null && (vertexLayout === void 0 || !materialShaderId?.startsWith("forgeax::"));
|
|
16915
18901
|
}
|
|
@@ -16937,9 +18923,9 @@ function selectPipelineLayoutForVariant(state, variantSet, layoutKind) {
|
|
|
16937
18923
|
}
|
|
16938
18924
|
return state.pbrPipelineLayout;
|
|
16939
18925
|
}
|
|
16940
|
-
function resolveMaterialShaderVariantSet(requestedVariantSet, variants, backendKind, storageBuffer) {
|
|
18926
|
+
function resolveMaterialShaderVariantSet(requestedVariantSet, variants, backendKind, storageBuffer, projectorAvailable = true) {
|
|
16941
18927
|
const declaredAxes = new Set(variants.flatMap((variant) => Object.keys(variant.defines)));
|
|
16942
|
-
const declaresCapabilityVariants = declaredAxes.has("WEBGL2_COMPAT") || declaredAxes.has("STORAGE_BUFFER_AVAILABLE");
|
|
18928
|
+
const declaresCapabilityVariants = declaredAxes.has("WEBGL2_COMPAT") || declaredAxes.has("STORAGE_BUFFER_AVAILABLE") || declaredAxes.has("PROJECTOR_AVAILABLE");
|
|
16943
18929
|
if (!declaresCapabilityVariants) return requestedVariantSet;
|
|
16944
18930
|
const requested = {};
|
|
16945
18931
|
for (const part of requestedVariantSet?.split("+") ?? []) {
|
|
@@ -16948,7 +18934,7 @@ function resolveMaterialShaderVariantSet(requestedVariantSet, variants, backendK
|
|
|
16948
18934
|
}
|
|
16949
18935
|
const filtered = {};
|
|
16950
18936
|
for (const axis of declaredAxes) {
|
|
16951
|
-
filtered[axis] = axis === "WEBGL2_COMPAT" ? backendKind === "wgpu-webgl2" : axis === "STORAGE_BUFFER_AVAILABLE" ? backendKind !== "wgpu-webgl2" && storageBuffer : axis === "SKINNING_DISABLED" ? requested[axis] ?? true : axis === "CLUSTER_FORWARD_AVAILABLE" ? backendKind !== "wgpu-webgl2" && (requested[axis] ?? false) : axis === "PER_INSTANCE_REGION" ? requestedVariantSet === "" || (requested[axis] ?? false) : axis === "VERTEX_COLOR_AVAILABLE" ? requestedVariantSet === "" || (requested[axis] ?? false) : axis === "TRANSMISSION_AVAILABLE" ? requested[axis] ?? false : requested[axis] ?? true;
|
|
18937
|
+
filtered[axis] = axis === "WEBGL2_COMPAT" ? backendKind === "wgpu-webgl2" : axis === "STORAGE_BUFFER_AVAILABLE" ? backendKind !== "wgpu-webgl2" && storageBuffer : axis === "SKINNING_DISABLED" ? requested[axis] ?? true : axis === "CLUSTER_FORWARD_AVAILABLE" ? backendKind !== "wgpu-webgl2" && (requested[axis] ?? false) : axis === "PER_INSTANCE_REGION" ? requestedVariantSet === "" || (requested[axis] ?? false) : axis === "VERTEX_COLOR_AVAILABLE" ? requestedVariantSet === "" || (requested[axis] ?? false) : axis === "TRANSMISSION_AVAILABLE" ? requested[axis] ?? false : axis === "PROJECTOR_AVAILABLE" ? projectorAvailable : requested[axis] ?? true;
|
|
16952
18938
|
}
|
|
16953
18939
|
const sorted2 = Object.entries(filtered).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
16954
18940
|
return sorted2.every(([, value]) => value) ? requestedVariantSet === "" ? "" : void 0 : sorted2.map(([name, value]) => `${name}=${value}`).join("+");
|
|
@@ -17006,20 +18992,20 @@ function attachDeviceLostFanout(device, pack, registries) {
|
|
|
17006
18992
|
);
|
|
17007
18993
|
}
|
|
17008
18994
|
}
|
|
17009
|
-
}).catch((
|
|
18995
|
+
}).catch((err32) => {
|
|
17010
18996
|
pack.instrumentation?.onDeviceLost?.();
|
|
17011
|
-
onDeviceLost?.(`device.lost rejected: ${String(
|
|
17012
|
-
lostRegistry.fire({ reason: "unknown", message: String(
|
|
18997
|
+
onDeviceLost?.(`device.lost rejected: ${String(err32)}`);
|
|
18998
|
+
lostRegistry.fire({ reason: "unknown", message: String(err32) });
|
|
17013
18999
|
healthRegistry.fire({
|
|
17014
19000
|
reason: "device-lost",
|
|
17015
|
-
detail: { lostReason: "unknown", message: String(
|
|
19001
|
+
detail: { lostReason: "unknown", message: String(err32) },
|
|
17016
19002
|
recoverable: true
|
|
17017
19003
|
});
|
|
17018
19004
|
errorRegistry.fire(
|
|
17019
19005
|
new RhiError({
|
|
17020
19006
|
code: "device-lost",
|
|
17021
19007
|
expected: "device.lost Promise resolves with GPUDeviceLostInfo",
|
|
17022
|
-
hint: `device.lost Promise rejected unexpectedly: ${String(
|
|
19008
|
+
hint: `device.lost Promise rejected unexpectedly: ${String(err32)}`
|
|
17023
19009
|
})
|
|
17024
19010
|
);
|
|
17025
19011
|
});
|
|
@@ -17030,9 +19016,12 @@ var COMPRESSION_FEATURES = [
|
|
|
17030
19016
|
"texture-compression-astc"
|
|
17031
19017
|
];
|
|
17032
19018
|
var STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES = 17;
|
|
17033
|
-
function deviceOptionsForAdapter(adapter) {
|
|
19019
|
+
function deviceOptionsForAdapter(adapter, options) {
|
|
17034
19020
|
const requiredFeatures = COMPRESSION_FEATURES.filter((feature) => adapter.features.has(feature));
|
|
17035
19021
|
const supportsTransmission = (adapter.limits.maxSampledTexturesPerShaderStage ?? 0) >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
19022
|
+
if (options?.captureGpuTimings === true && adapter.features.has("timestamp-query")) {
|
|
19023
|
+
requiredFeatures.push("timestamp-query");
|
|
19024
|
+
}
|
|
17036
19025
|
return {
|
|
17037
19026
|
...requiredFeatures.length > 0 ? { requiredFeatures } : {},
|
|
17038
19027
|
...supportsTransmission ? {
|
|
@@ -17050,7 +19039,7 @@ async function tryCreateWebGPURenderer(canvas, options, pack, bundler) {
|
|
|
17050
19039
|
return { kind: "rhi-err", error: adapterResult.error };
|
|
17051
19040
|
}
|
|
17052
19041
|
const adapter = adapterResult.value;
|
|
17053
|
-
const deviceOpts = deviceOptionsForAdapter(adapter);
|
|
19042
|
+
const deviceOpts = deviceOptionsForAdapter(adapter, options);
|
|
17054
19043
|
const result = await adapter.requestDevice(deviceOpts);
|
|
17055
19044
|
if (!result.ok) {
|
|
17056
19045
|
return { kind: "rhi-err", error: result.error };
|
|
@@ -17328,6 +19317,14 @@ async function makeWebGPURenderer(internals) {
|
|
|
17328
19317
|
}
|
|
17329
19318
|
if (pipelineState === null) return null;
|
|
17330
19319
|
const paramSchema = lookup.value.paramSchema;
|
|
19320
|
+
if (isSharedMaterialUserRegionCompatible(paramSchema)) {
|
|
19321
|
+
perShaderMaterialLayoutCache.set(materialShaderId, {
|
|
19322
|
+
source: lookup.value.source,
|
|
19323
|
+
paramSchema,
|
|
19324
|
+
layout: null
|
|
19325
|
+
});
|
|
19326
|
+
return null;
|
|
19327
|
+
}
|
|
17331
19328
|
const spec = {
|
|
17332
19329
|
shader: { id: materialShaderId, passKind: "forward", variantSet: void 0 },
|
|
17333
19330
|
attachments: { colorFormats: [], depthFormat: void 0, sampleCount: 1 },
|
|
@@ -17370,7 +19367,8 @@ async function makeWebGPURenderer(internals) {
|
|
|
17370
19367
|
requestedVariantSet,
|
|
17371
19368
|
[],
|
|
17372
19369
|
internals.device.caps.backendKind,
|
|
17373
|
-
internals.device.caps.storageBuffer
|
|
19370
|
+
internals.device.caps.storageBuffer,
|
|
19371
|
+
(internals.device.limits.maxSampledTexturesPerShaderStage ?? 0) >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES
|
|
17374
19372
|
);
|
|
17375
19373
|
}
|
|
17376
19374
|
const variants = manifestEntry.variants;
|
|
@@ -17379,13 +19377,15 @@ async function makeWebGPURenderer(internals) {
|
|
|
17379
19377
|
byRequest = /* @__PURE__ */ new Map();
|
|
17380
19378
|
materialShaderVariantResolutionCache.set(variants, byRequest);
|
|
17381
19379
|
}
|
|
17382
|
-
const
|
|
19380
|
+
const projectorAvailable = (internals.device.limits.maxSampledTexturesPerShaderStage ?? 0) >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
19381
|
+
const cacheKey = `${requestedVariantSet ?? "\0"}|${internals.device.caps.backendKind}|${internals.device.caps.storageBuffer ? "1" : "0"}|${projectorAvailable ? "1" : "0"}`;
|
|
17383
19382
|
if (byRequest.has(cacheKey)) return byRequest.get(cacheKey);
|
|
17384
19383
|
const resolved2 = resolveMaterialShaderVariantSet(
|
|
17385
19384
|
requestedVariantSet,
|
|
17386
19385
|
variants,
|
|
17387
19386
|
internals.device.caps.backendKind,
|
|
17388
|
-
internals.device.caps.storageBuffer
|
|
19387
|
+
internals.device.caps.storageBuffer,
|
|
19388
|
+
projectorAvailable
|
|
17389
19389
|
);
|
|
17390
19390
|
byRequest.set(cacheKey, resolved2);
|
|
17391
19391
|
return resolved2;
|
|
@@ -17468,6 +19468,9 @@ async function makeWebGPURenderer(internals) {
|
|
|
17468
19468
|
});
|
|
17469
19469
|
}
|
|
17470
19470
|
temporalPostProcessesRegistered = true;
|
|
19471
|
+
},
|
|
19472
|
+
(sources) => {
|
|
19473
|
+
internals.volumetricFogShaders = sources;
|
|
17471
19474
|
}
|
|
17472
19475
|
);
|
|
17473
19476
|
const ready = buildPipeline().then(
|
|
@@ -17504,7 +19507,8 @@ async function makeWebGPURenderer(internals) {
|
|
|
17504
19507
|
const noColorVariant = selectNoColorPbrVariant(
|
|
17505
19508
|
pbrManifestEntry,
|
|
17506
19509
|
internals.device.caps.storageBuffer,
|
|
17507
|
-
variantSet
|
|
19510
|
+
variantSet,
|
|
19511
|
+
(internals.device.limits.maxSampledTexturesPerShaderStage ?? 0) >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES
|
|
17508
19512
|
);
|
|
17509
19513
|
if (noColorVariant !== void 0 && noColorVariant.composedWgsl !== standardEntry.wgsl) {
|
|
17510
19514
|
return { ...standardEntry, wgsl: noColorVariant.composedWgsl };
|
|
@@ -17975,6 +19979,9 @@ async function makeWebGPURenderer(internals) {
|
|
|
17975
19979
|
},
|
|
17976
19980
|
get meshSsboState() {
|
|
17977
19981
|
return internals.meshSsboState;
|
|
19982
|
+
},
|
|
19983
|
+
get volumetricFogShaders() {
|
|
19984
|
+
return internals.volumetricFogShaders;
|
|
17978
19985
|
}
|
|
17979
19986
|
});
|
|
17980
19987
|
if (internals.device.caps.backendKind === "null" && Array.from(getShader().entries()).length === 0) {
|
|
@@ -17993,7 +20000,8 @@ async function makeWebGPURenderer(internals) {
|
|
|
17993
20000
|
const transformReleases = /* @__PURE__ */ new Map();
|
|
17994
20001
|
const attachmentOwner = {};
|
|
17995
20002
|
let frameId = 0;
|
|
17996
|
-
|
|
20003
|
+
const issuedReceipts = /* @__PURE__ */ new WeakSet();
|
|
20004
|
+
const receiptTimings = /* @__PURE__ */ new WeakMap();
|
|
17997
20005
|
let surfaceReleased = false;
|
|
17998
20006
|
const renderer = {
|
|
17999
20007
|
attach(world) {
|
|
@@ -18046,6 +20054,7 @@ async function makeWebGPURenderer(internals) {
|
|
|
18046
20054
|
rgba16floatRenderable: internals.device.caps.rgba16floatRenderable,
|
|
18047
20055
|
...internals.context?.presentationProof === void 0 ? {} : { presentationProof: internals.context.presentationProof }
|
|
18048
20056
|
});
|
|
20057
|
+
const volumetricFog = renderSystem.volumetricFog;
|
|
18049
20058
|
return Object.freeze({
|
|
18050
20059
|
state,
|
|
18051
20060
|
surface: surfaceReleased ? "released" : "available",
|
|
@@ -18059,6 +20068,7 @@ async function makeWebGPURenderer(internals) {
|
|
|
18059
20068
|
(internals.featureHost?.features ?? []).map((feature) => feature.identity)
|
|
18060
20069
|
),
|
|
18061
20070
|
featureDiagnostics: internals.featureHost?.diagnostics() ?? Object.freeze([]),
|
|
20071
|
+
volumetricFog,
|
|
18062
20072
|
frustumStats: Object.freeze({ ...renderSystem.frustumStats }),
|
|
18063
20073
|
visibilityStats: Object.freeze({ ...renderSystem.visibilityStats }),
|
|
18064
20074
|
renderScene: renderSystem.renderScene,
|
|
@@ -18402,8 +20412,18 @@ async function makeWebGPURenderer(internals) {
|
|
|
18402
20412
|
);
|
|
18403
20413
|
if (!contextConfigured.ok) return contextConfigured;
|
|
18404
20414
|
try {
|
|
18405
|
-
const
|
|
20415
|
+
const timingRequested = isFrameRequest && (frameRequest?.profileFrame !== void 0 || internals.options?.captureGpuTimings === true);
|
|
20416
|
+
let timingCapture;
|
|
20417
|
+
let timingUnavailableReason = timingRequested ? "timestamp capture is unavailable on this device" : "timestamp capture was not requested for this frame";
|
|
20418
|
+
if (timingRequested) {
|
|
20419
|
+
const createdTiming = GpuTimingCapture.create(internals.device);
|
|
20420
|
+
if (createdTiming.ok) timingCapture = createdTiming.value;
|
|
20421
|
+
else
|
|
20422
|
+
timingUnavailableReason = `timestamp capture setup failed: ${createdTiming.error.code}`;
|
|
20423
|
+
}
|
|
20424
|
+
const submitted = renderSystem.draw(worlds, drawOptions, renderReadLeases, timingCapture);
|
|
18406
20425
|
if (!submitted) {
|
|
20426
|
+
timingCapture?.discard();
|
|
18407
20427
|
return err(
|
|
18408
20428
|
new RendererContractFailureError(
|
|
18409
20429
|
"draw",
|
|
@@ -18430,7 +20450,14 @@ async function makeWebGPURenderer(internals) {
|
|
|
18430
20450
|
deviceGeneration: receiptGeneration,
|
|
18431
20451
|
completed
|
|
18432
20452
|
});
|
|
18433
|
-
|
|
20453
|
+
receiptTimings.set(
|
|
20454
|
+
receipt,
|
|
20455
|
+
timingCapture?.observation() ?? Promise.resolve({
|
|
20456
|
+
status: "unavailable",
|
|
20457
|
+
reason: timingUnavailableReason
|
|
20458
|
+
})
|
|
20459
|
+
);
|
|
20460
|
+
issuedReceipts.add(receipt);
|
|
18434
20461
|
return ok(receipt);
|
|
18435
20462
|
}
|
|
18436
20463
|
return ok(void 0);
|
|
@@ -18447,7 +20474,7 @@ async function makeWebGPURenderer(internals) {
|
|
|
18447
20474
|
}
|
|
18448
20475
|
},
|
|
18449
20476
|
async observe(receipt, request) {
|
|
18450
|
-
if (
|
|
20477
|
+
if (!issuedReceipts.has(receipt) || receipt.deviceGeneration !== activeDeviceScope.generation) {
|
|
18451
20478
|
const currentGeneration2 = activeDeviceScope.generation;
|
|
18452
20479
|
return err(
|
|
18453
20480
|
new FrameReceiptStaleError({
|
|
@@ -18469,11 +20496,16 @@ async function makeWebGPURenderer(internals) {
|
|
|
18469
20496
|
})
|
|
18470
20497
|
);
|
|
18471
20498
|
}
|
|
20499
|
+
const timings = request.include.includes("timings") ? await (receiptTimings.get(receipt) ?? Promise.resolve({
|
|
20500
|
+
status: "unavailable",
|
|
20501
|
+
reason: "timing receipt was not retained"
|
|
20502
|
+
})) : void 0;
|
|
18472
20503
|
return ok(
|
|
18473
20504
|
Object.freeze({
|
|
18474
20505
|
frameId: receipt.frameId,
|
|
18475
20506
|
deviceGeneration: receipt.deviceGeneration,
|
|
18476
|
-
include: Object.freeze([...request.include])
|
|
20507
|
+
include: Object.freeze([...request.include]),
|
|
20508
|
+
...timings === void 0 ? {} : { timings }
|
|
18477
20509
|
})
|
|
18478
20510
|
);
|
|
18479
20511
|
},
|
|
@@ -18684,7 +20716,7 @@ async function makeWebGPURenderer(internals) {
|
|
|
18684
20716
|
let device;
|
|
18685
20717
|
try {
|
|
18686
20718
|
const deviceResult = await adapterResult.value.requestDevice(
|
|
18687
|
-
deviceOptionsForAdapter(adapterResult.value)
|
|
20719
|
+
deviceOptionsForAdapter(adapterResult.value, internals.options)
|
|
18688
20720
|
);
|
|
18689
20721
|
if (!deviceResult.ok) {
|
|
18690
20722
|
return err(new RecoverError("recover-device-unavailable"));
|
|
@@ -18979,6 +21011,11 @@ var RENDER_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
18979
21011
|
"pbr",
|
|
18980
21012
|
"ibl",
|
|
18981
21013
|
"ssao",
|
|
21014
|
+
"bloom",
|
|
21015
|
+
"tone",
|
|
21016
|
+
"antialias",
|
|
21017
|
+
"sky",
|
|
21018
|
+
"volumetricFog",
|
|
18982
21019
|
"fallback",
|
|
18983
21020
|
"postStages"
|
|
18984
21021
|
]);
|
|
@@ -19004,6 +21041,24 @@ function validateRenderProfile(profile) {
|
|
|
19004
21041
|
for (const field of ["pbr", "ibl", "ssao"]) {
|
|
19005
21042
|
if (typeof profile[field] !== "boolean") return `RenderProfile.${field} must be boolean`;
|
|
19006
21043
|
}
|
|
21044
|
+
if (profile.volumetricFog !== void 0) {
|
|
21045
|
+
if (profile.volumetricFog.quality !== "low" && profile.volumetricFog.quality !== "high")
|
|
21046
|
+
return "RenderProfile.volumetricFog.quality must be 'low' or 'high'";
|
|
21047
|
+
if (profile.volumetricFog.depth !== 48 && profile.volumetricFog.depth !== 64) {
|
|
21048
|
+
return "RenderProfile.volumetricFog.depth must be 48 or 64";
|
|
21049
|
+
}
|
|
21050
|
+
if (profile.volumetricFog.tileSize !== 4 && profile.volumetricFog.tileSize !== 16) {
|
|
21051
|
+
return "RenderProfile.volumetricFog.tileSize must be 4 or 16";
|
|
21052
|
+
}
|
|
21053
|
+
if (profile.volumetricFog.quality === "low" && profile.volumetricFog.depth !== 48)
|
|
21054
|
+
return "RenderProfile.volumetricFog.low must use depth 48";
|
|
21055
|
+
if (profile.volumetricFog.quality === "high" && profile.volumetricFog.depth !== 64)
|
|
21056
|
+
return "RenderProfile.volumetricFog.high must use depth 64";
|
|
21057
|
+
if (profile.volumetricFog.quality === "low" && profile.volumetricFog.tileSize !== 16)
|
|
21058
|
+
return "RenderProfile.volumetricFog.low must use tileSize 16";
|
|
21059
|
+
if (profile.volumetricFog.quality === "high" && profile.volumetricFog.tileSize !== 4)
|
|
21060
|
+
return "RenderProfile.volumetricFog.high must use tileSize 4";
|
|
21061
|
+
}
|
|
19007
21062
|
const stages = STANDARD_POST_STAGE_NAMES;
|
|
19008
21063
|
if (!Array.isArray(profile.postStages) || profile.postStages.length !== stages.length || stages.some((stage, index) => profile.postStages[index] !== stage)) {
|
|
19009
21064
|
return "RenderProfile.postStages must preserve the Standard post stage order";
|
|
@@ -19249,6 +21304,7 @@ async function prepareMaterialShaders(rhiDevice, getShader, assets, materialShad
|
|
|
19249
21304
|
if (!capCheck.ok) throw capCheck.error;
|
|
19250
21305
|
storageBufferCapable = !webgl2Downlevel && capCheck.value;
|
|
19251
21306
|
}
|
|
21307
|
+
const projectorAvailable = (rhiDevice.limits.maxSampledTexturesPerShaderStage ?? 0) >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
19252
21308
|
const registry = getShader();
|
|
19253
21309
|
const loaded = await registry.loadManifest();
|
|
19254
21310
|
if (!loaded.ok) {
|
|
@@ -19269,6 +21325,9 @@ async function prepareMaterialShaders(rhiDevice, getShader, assets, materialShad
|
|
|
19269
21325
|
if (msEntry.variants.some((v) => "VERTEX_COLOR_AVAILABLE" in v.defines)) {
|
|
19270
21326
|
variantDefines.VERTEX_COLOR_AVAILABLE = false;
|
|
19271
21327
|
}
|
|
21328
|
+
if (msEntry.identifier === "forgeax::default-standard-pbr" && msEntry.variants.some((v) => "PROJECTOR_AVAILABLE" in v.defines)) {
|
|
21329
|
+
variantDefines.PROJECTOR_AVAILABLE = projectorAvailable;
|
|
21330
|
+
}
|
|
19272
21331
|
if (msEntry.identifier === "forgeax::default-standard-pbr" && msEntry.variants.some((v) => "TRANSMISSION_AVAILABLE" in v.defines)) {
|
|
19273
21332
|
variantDefines.TRANSMISSION_AVAILABLE = false;
|
|
19274
21333
|
}
|
|
@@ -19340,7 +21399,7 @@ async function prepareMaterialShaders(rhiDevice, getShader, assets, materialShad
|
|
|
19340
21399
|
}
|
|
19341
21400
|
}
|
|
19342
21401
|
}
|
|
19343
|
-
async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, asyncCreateShaderModule, errorRegistry, requiredMaterialShaders, setGrowMeshSsboHook, seedShaderModule, seedMaterialShaderPipelineCache, registerBuiltinTonemap, registerBuiltinTemporalPostProcesses) {
|
|
21402
|
+
async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, asyncCreateShaderModule, errorRegistry, requiredMaterialShaders, setGrowMeshSsboHook, seedShaderModule, seedMaterialShaderPipelineCache, registerBuiltinTonemap, registerBuiltinTemporalPostProcesses, setVolumetricFogShaderSources) {
|
|
19344
21403
|
const capValue = rhiDevice.limits.maxStorageBuffersPerShaderStage;
|
|
19345
21404
|
const webgl2Downlevel = rhiDevice.caps.backendKind === "wgpu-webgl2";
|
|
19346
21405
|
let storageBufferCapable = !webgl2Downlevel;
|
|
@@ -19348,6 +21407,7 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
19348
21407
|
const capCheck = assertStorageBufferCap(capValue);
|
|
19349
21408
|
storageBufferCapable = capCheck.ok ? !webgl2Downlevel && capCheck.value : !webgl2Downlevel;
|
|
19350
21409
|
}
|
|
21410
|
+
const projectorAvailable = (rhiDevice.limits.maxSampledTexturesPerShaderStage ?? 0) >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
19351
21411
|
const surfaceViewFormats = rhiDevice.surfaceViewFormats ?? !webgl2Downlevel;
|
|
19352
21412
|
const swapChainFormats = selectSwapChainFormat(storageBufferCapable, surfaceViewFormats);
|
|
19353
21413
|
if (swapChainFormats.fallbackReason !== void 0 && rhiDevice.caps.backendKind !== "null") {
|
|
@@ -19489,7 +21549,7 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
19489
21549
|
const idToVariantWgsl = /* @__PURE__ */ new Map();
|
|
19490
21550
|
for (const ms of registry.materialShaderManifestEntries()) {
|
|
19491
21551
|
const exact = ms.variants.find(
|
|
19492
|
-
(v2) => v2.defines.STORAGE_BUFFER_AVAILABLE === false && (!("CLUSTER_FORWARD_AVAILABLE" in v2.defines) || v2.defines.CLUSTER_FORWARD_AVAILABLE === false) && (!("VERTEX_COLOR_AVAILABLE" in v2.defines) || v2.defines.VERTEX_COLOR_AVAILABLE === false) && (!("SKINNING_DISABLED" in v2.defines) || v2.defines.SKINNING_DISABLED === true)
|
|
21552
|
+
(v2) => v2.defines.STORAGE_BUFFER_AVAILABLE === false && (!("CLUSTER_FORWARD_AVAILABLE" in v2.defines) || v2.defines.CLUSTER_FORWARD_AVAILABLE === false) && (!("VERTEX_COLOR_AVAILABLE" in v2.defines) || v2.defines.VERTEX_COLOR_AVAILABLE === false) && (!("PROJECTOR_AVAILABLE" in v2.defines) || v2.defines.PROJECTOR_AVAILABLE === projectorAvailable) && (!("SKINNING_DISABLED" in v2.defines) || v2.defines.SKINNING_DISABLED === true)
|
|
19493
21553
|
);
|
|
19494
21554
|
const v = exact === void 0 ? void 0 : findVariantByKey(ms, exact.definesKey);
|
|
19495
21555
|
if (v !== void 0) {
|
|
@@ -19515,7 +21575,7 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
19515
21575
|
const idToRuntimeVariantWgsl = /* @__PURE__ */ new Map();
|
|
19516
21576
|
for (const ms of registry.materialShaderManifestEntries()) {
|
|
19517
21577
|
const exact = ms.variants.find(
|
|
19518
|
-
(variant2) => variant2.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable && (!("CLUSTER_FORWARD_AVAILABLE" in variant2.defines) || variant2.defines.CLUSTER_FORWARD_AVAILABLE === false) && (!("VERTEX_COLOR_AVAILABLE" in variant2.defines) || variant2.defines.VERTEX_COLOR_AVAILABLE === false) && (!("SKINNING_DISABLED" in variant2.defines) || variant2.defines.SKINNING_DISABLED === true) && (!("TRANSMISSION_AVAILABLE" in variant2.defines) || variant2.defines.TRANSMISSION_AVAILABLE === false)
|
|
21578
|
+
(variant2) => variant2.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable && (!("CLUSTER_FORWARD_AVAILABLE" in variant2.defines) || variant2.defines.CLUSTER_FORWARD_AVAILABLE === false) && (!("VERTEX_COLOR_AVAILABLE" in variant2.defines) || variant2.defines.VERTEX_COLOR_AVAILABLE === false) && (!("PROJECTOR_AVAILABLE" in variant2.defines) || variant2.defines.PROJECTOR_AVAILABLE === projectorAvailable) && (!("SKINNING_DISABLED" in variant2.defines) || variant2.defines.SKINNING_DISABLED === true) && (!("TRANSMISSION_AVAILABLE" in variant2.defines) || variant2.defines.TRANSMISSION_AVAILABLE === false)
|
|
19519
21579
|
);
|
|
19520
21580
|
const variant = exact === void 0 ? void 0 : findVariantByKey(ms, exact.definesKey);
|
|
19521
21581
|
if (variant !== void 0) {
|
|
@@ -19595,6 +21655,52 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
19595
21655
|
if (!shaderResult.ok) throw shaderResult.error;
|
|
19596
21656
|
seedShaderModule(label, shaderResult.value);
|
|
19597
21657
|
}
|
|
21658
|
+
if (rhiDevice.caps.compute && rhiDevice.caps.storageTexture) {
|
|
21659
|
+
const volumeSources = [
|
|
21660
|
+
["inject", "volume_inject", (source) => source.includes("fn volume_inject(")],
|
|
21661
|
+
["temporal", "volume_temporal", (source) => source.includes("fn volume_temporal(")],
|
|
21662
|
+
[
|
|
21663
|
+
"integrate",
|
|
21664
|
+
"volume_integrate",
|
|
21665
|
+
(source) => source.includes("fn volume_integrate(")
|
|
21666
|
+
],
|
|
21667
|
+
["composite", "volume_composite", (source) => source.includes("fn volume_fs(")]
|
|
21668
|
+
];
|
|
21669
|
+
const resolved2 = volumeSources.map(([stage, label, matches]) => {
|
|
21670
|
+
const entry = manifestEntries.find((candidate) => matches(candidate.wgsl));
|
|
21671
|
+
return { stage, label, entry };
|
|
21672
|
+
});
|
|
21673
|
+
const present = resolved2.filter((candidate) => candidate.entry !== void 0).length;
|
|
21674
|
+
if (present === 0) ; else {
|
|
21675
|
+
const missing2 = resolved2.find((candidate) => candidate.entry === void 0);
|
|
21676
|
+
if (missing2 !== void 0) {
|
|
21677
|
+
throw new RhiError({
|
|
21678
|
+
code: "shader-compile-failed",
|
|
21679
|
+
expected: "shader manifest contains all four volumetric fog utility entries",
|
|
21680
|
+
hint: `add the package-owned WGSL entry for '${missing2.label}' to the engine shader manifest`
|
|
21681
|
+
});
|
|
21682
|
+
}
|
|
21683
|
+
const compiled = [];
|
|
21684
|
+
for (const { stage, label, entry } of resolved2) {
|
|
21685
|
+
if (entry === void 0) throw new Error(`missing volumetric shader ${label}`);
|
|
21686
|
+
const shaderResult = await runShimStep(
|
|
21687
|
+
() => asyncCreateShaderModule ? asyncCreateShaderModule(rhiDevice, { code: entry.wgsl, label }) : invokeDeviceCreateShaderModule(rhiDevice, { code: entry.wgsl, label }),
|
|
21688
|
+
"shader-compile-failed",
|
|
21689
|
+
`volumetric fog shader module '${label}' compiled`,
|
|
21690
|
+
`inspect manifest WGSL for '${label}' and check device.features`
|
|
21691
|
+
);
|
|
21692
|
+
if (!shaderResult.ok) throw shaderResult.error;
|
|
21693
|
+
compiled.push({ stage, label, source: entry.wgsl, module: shaderResult.value });
|
|
21694
|
+
}
|
|
21695
|
+
for (const { label, module } of compiled) seedShaderModule(label, module);
|
|
21696
|
+
setVolumetricFogShaderSources({
|
|
21697
|
+
inject: compiled.find((candidate) => candidate.stage === "inject")?.source,
|
|
21698
|
+
temporal: compiled.find((candidate) => candidate.stage === "temporal")?.source,
|
|
21699
|
+
integrate: compiled.find((candidate) => candidate.stage === "integrate")?.source,
|
|
21700
|
+
composite: compiled.find((candidate) => candidate.stage === "composite")?.source
|
|
21701
|
+
});
|
|
21702
|
+
}
|
|
21703
|
+
}
|
|
19598
21704
|
const pbrShaderResult = await runShimStep(
|
|
19599
21705
|
() => asyncCreateShaderModule ? asyncCreateShaderModule(rhiDevice, { code: pbrEntry.wgsl, label: "pbr" }) : invokeDeviceCreateShaderModule(rhiDevice, { code: pbrEntry.wgsl, label: "pbr" }),
|
|
19600
21706
|
"shader-compile-failed",
|
|
@@ -19614,7 +21720,11 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
19614
21720
|
seedShaderModule(`module-forgeax::default-standard-pbr#${pbrVariant.definesKey}`, pbrModule);
|
|
19615
21721
|
}
|
|
19616
21722
|
const transmissionCapable = (rhiDevice.limits.maxSampledTexturesPerShaderStage ?? 0) >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
19617
|
-
const transmissionVariants = transmissionCapable ? selectStandardPbrTransmissionPrewarmVariants(
|
|
21723
|
+
const transmissionVariants = transmissionCapable ? selectStandardPbrTransmissionPrewarmVariants(
|
|
21724
|
+
pbrManifestEntry,
|
|
21725
|
+
storageBufferCapable,
|
|
21726
|
+
projectorAvailable
|
|
21727
|
+
) : [];
|
|
19618
21728
|
const prewarmedHdrpModules = /* @__PURE__ */ new Map([[pbrEntry.wgsl, pbrModule]]);
|
|
19619
21729
|
for (const transmissionVariant of transmissionVariants) {
|
|
19620
21730
|
const moduleLabel = `module-forgeax::default-standard-pbr#${transmissionVariant.definesKey}`;
|
|
@@ -19641,7 +21751,8 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
19641
21751
|
for (const hdrpVariant of selectHdrpPbrPrewarmVariants(
|
|
19642
21752
|
pbrManifestEntry,
|
|
19643
21753
|
storageBufferCapable,
|
|
19644
|
-
transmissionCapable
|
|
21754
|
+
transmissionCapable,
|
|
21755
|
+
projectorAvailable
|
|
19645
21756
|
)) {
|
|
19646
21757
|
const moduleLabel = `module-forgeax::default-standard-pbr#${hdrpVariant.definesKey}`;
|
|
19647
21758
|
let hdrpModule = prewarmedHdrpModules.get(hdrpVariant.composedWgsl);
|
|
@@ -19951,7 +22062,12 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
19951
22062
|
}
|
|
19952
22063
|
}
|
|
19953
22064
|
const pbrLayouts = runShimSyncStep(
|
|
19954
|
-
() => ok(
|
|
22065
|
+
() => ok(
|
|
22066
|
+
buildPbrPipelineLayouts(rhiDevice, {
|
|
22067
|
+
storageBuffer: storageBufferCapable,
|
|
22068
|
+
projectorAvailable
|
|
22069
|
+
})
|
|
22070
|
+
),
|
|
19955
22071
|
"webgpu-runtime-error",
|
|
19956
22072
|
"buildPbrPipelineLayouts succeeded",
|
|
19957
22073
|
"check device.limits.maxBindingsPerBindGroup (need >=14) and maxBindGroupsPerPipelineLayout"
|
|
@@ -20631,11 +22747,11 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
20631
22747
|
}
|
|
20632
22748
|
try {
|
|
20633
22749
|
getOrBuildPipeline(spec, pipelineDeviceProvider, pipelineCache, modules);
|
|
20634
|
-
} catch (
|
|
20635
|
-
if (
|
|
22750
|
+
} catch (err32) {
|
|
22751
|
+
if (err32 instanceof PipelineSpecError) throw err32;
|
|
20636
22752
|
throw new PipelineSpecError({
|
|
20637
22753
|
code: "pipeline-build-failed",
|
|
20638
|
-
detail: { cause:
|
|
22754
|
+
detail: { cause: err32 },
|
|
20639
22755
|
hint: `Boot-time SPEC_CONST pre-warm failed for shader '${spec.shader.id}'; inspect gpuMessage on the cause`
|
|
20640
22756
|
});
|
|
20641
22757
|
}
|
|
@@ -20799,11 +22915,11 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
20799
22915
|
pipelineCache,
|
|
20800
22916
|
modules
|
|
20801
22917
|
);
|
|
20802
|
-
} catch (
|
|
20803
|
-
if (
|
|
22918
|
+
} catch (err32) {
|
|
22919
|
+
if (err32 instanceof PipelineSpecError) throw err32;
|
|
20804
22920
|
throw new PipelineSpecError({
|
|
20805
22921
|
code: "pipeline-build-failed",
|
|
20806
|
-
detail: { cause:
|
|
22922
|
+
detail: { cause: err32 },
|
|
20807
22923
|
hint: "createRenderPipeline (fxaa fullscreen) failed; inspect gpuMessage"
|
|
20808
22924
|
});
|
|
20809
22925
|
}
|
|
@@ -21057,11 +23173,11 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
21057
23173
|
bloomBrightPipelineHandle,
|
|
21058
23174
|
() => void 0
|
|
21059
23175
|
);
|
|
21060
|
-
} catch (
|
|
21061
|
-
if (
|
|
23176
|
+
} catch (err32) {
|
|
23177
|
+
if (err32 instanceof PipelineSpecError) throw err32;
|
|
21062
23178
|
throw new PipelineSpecError({
|
|
21063
23179
|
code: "pipeline-build-failed",
|
|
21064
|
-
detail: { cause:
|
|
23180
|
+
detail: { cause: err32 },
|
|
21065
23181
|
hint: "createRenderPipeline (bloom-bright fullscreen) failed; inspect gpuMessage"
|
|
21066
23182
|
});
|
|
21067
23183
|
}
|
|
@@ -21158,11 +23274,11 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
21158
23274
|
bloomBlurHPipelineHandle,
|
|
21159
23275
|
() => void 0
|
|
21160
23276
|
);
|
|
21161
|
-
} catch (
|
|
21162
|
-
if (
|
|
23277
|
+
} catch (err32) {
|
|
23278
|
+
if (err32 instanceof PipelineSpecError) throw err32;
|
|
21163
23279
|
throw new PipelineSpecError({
|
|
21164
23280
|
code: "pipeline-build-failed",
|
|
21165
|
-
detail: { cause:
|
|
23281
|
+
detail: { cause: err32 },
|
|
21166
23282
|
hint: "createRenderPipeline (bloom-blur-h fullscreen) failed; inspect gpuMessage"
|
|
21167
23283
|
});
|
|
21168
23284
|
}
|
|
@@ -21279,11 +23395,11 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
21279
23395
|
bloomCompositePipelineHandle,
|
|
21280
23396
|
() => void 0
|
|
21281
23397
|
);
|
|
21282
|
-
} catch (
|
|
21283
|
-
if (
|
|
23398
|
+
} catch (err32) {
|
|
23399
|
+
if (err32 instanceof PipelineSpecError) throw err32;
|
|
21284
23400
|
throw new PipelineSpecError({
|
|
21285
23401
|
code: "pipeline-build-failed",
|
|
21286
|
-
detail: { cause:
|
|
23402
|
+
detail: { cause: err32 },
|
|
21287
23403
|
hint: "createRenderPipeline (bloom-composite fullscreen) failed; inspect gpuMessage"
|
|
21288
23404
|
});
|
|
21289
23405
|
}
|
|
@@ -21573,11 +23689,11 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
21573
23689
|
pipelineCache,
|
|
21574
23690
|
{ ...baseModules, fragmentEntryPoint: "fs_ssao_calc" }
|
|
21575
23691
|
);
|
|
21576
|
-
} catch (
|
|
21577
|
-
if (
|
|
23692
|
+
} catch (err32) {
|
|
23693
|
+
if (err32 instanceof PipelineSpecError) throw err32;
|
|
21578
23694
|
throw new PipelineSpecError({
|
|
21579
23695
|
code: "pipeline-build-failed",
|
|
21580
|
-
detail: { cause:
|
|
23696
|
+
detail: { cause: err32 },
|
|
21581
23697
|
hint: "createRenderPipeline (ssao-calc fullscreen) failed; inspect gpuMessage"
|
|
21582
23698
|
});
|
|
21583
23699
|
}
|
|
@@ -21608,11 +23724,11 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
21608
23724
|
pipelineCache,
|
|
21609
23725
|
{ ...baseModules, fragmentEntryPoint: "fs_ssao_blur" }
|
|
21610
23726
|
);
|
|
21611
|
-
} catch (
|
|
21612
|
-
if (
|
|
23727
|
+
} catch (err32) {
|
|
23728
|
+
if (err32 instanceof PipelineSpecError) throw err32;
|
|
21613
23729
|
throw new PipelineSpecError({
|
|
21614
23730
|
code: "pipeline-build-failed",
|
|
21615
|
-
detail: { cause:
|
|
23731
|
+
detail: { cause: err32 },
|
|
21616
23732
|
hint: "createRenderPipeline (ssao-blur fullscreen) failed; inspect gpuMessage"
|
|
21617
23733
|
});
|
|
21618
23734
|
}
|
|
@@ -21704,6 +23820,7 @@ async function buildReadyWebGPU(rhiDevice, rendererScope, getShader, gpuStore, a
|
|
|
21704
23820
|
colorAttachmentFormat: swapChainFormats.view,
|
|
21705
23821
|
surfaceProfile: surfaceViewFormats ? "dual-view" : "raw-only",
|
|
21706
23822
|
viewBindGroupLayout: viewBglResult.value,
|
|
23823
|
+
projectorAvailable,
|
|
21707
23824
|
materialBindGroupLayout: materialBglResult.value,
|
|
21708
23825
|
meshBindGroupLayout: meshArrayBglResult.value,
|
|
21709
23826
|
viewUniformBuffer: viewUboResult.value,
|