@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
|
@@ -40,14 +40,14 @@ import {
|
|
|
40
40
|
GPU_TEXTURE_USAGE_RENDER_ATTACHMENT,
|
|
41
41
|
GPU_TEXTURE_USAGE_TEXTURE_BINDING,
|
|
42
42
|
} from '../gpu-texture-usage';
|
|
43
|
-
import { STANDARD_PIPELINE_ID } from '../pipeline/standard-profile';
|
|
43
|
+
import { resolveVolumetricFogProfile, STANDARD_PIPELINE_ID } from '../pipeline/standard-profile';
|
|
44
44
|
import type { CameraSnapshot } from '../render-contract';
|
|
45
45
|
import type {
|
|
46
46
|
RenderPipelineFeatureTarget,
|
|
47
47
|
RenderPipelineFrame,
|
|
48
48
|
RenderPipelineTopology,
|
|
49
49
|
} from '../render-pipeline';
|
|
50
|
-
import type { ExtractedLights } from '../render-system-extract';
|
|
50
|
+
import type { ExtractedLights, ExtractedVolumetricFog } from '../render-system-extract';
|
|
51
51
|
import {
|
|
52
52
|
abortTemporalGpuSubmit,
|
|
53
53
|
commitTemporalGpuSubmit,
|
|
@@ -60,8 +60,11 @@ import {
|
|
|
60
60
|
} from '../temporal/gpu';
|
|
61
61
|
import { isSceneDataTarget } from '../temporal/scene-data';
|
|
62
62
|
import type { TransmissionDemand } from '../transmission/projection';
|
|
63
|
+
import { hasVolumetricFogCapability } from '../volume/capability';
|
|
64
|
+
import { deriveVolumetricFogExtent, deriveVolumetricFogResolvedExtent } from '../volume/resources';
|
|
63
65
|
import { buildPerFrameBindGroups } from './frame-lighting';
|
|
64
66
|
import { getTextureIdentity, type RenderFrameState } from './frame-snapshot';
|
|
67
|
+
import type { GpuTimingCapture } from './gpu-timing';
|
|
65
68
|
import type { PipelineState, RenderSystemInternals } from './render-context';
|
|
66
69
|
import { resolveSurfaceFormatPair, resolveSurfaceProfile } from './render-context';
|
|
67
70
|
|
|
@@ -329,6 +332,7 @@ function topologyOf(
|
|
|
329
332
|
gpuDriven: PreparedGpuDrivenFrame | undefined,
|
|
330
333
|
featureGraphCandidate: RenderFeatureGraphCandidate | undefined,
|
|
331
334
|
transmissionDemand: TransmissionDemand | undefined,
|
|
335
|
+
volumetricFog: ExtractedVolumetricFog | undefined,
|
|
332
336
|
): Result<RenderPipelineTopology, RhiError> {
|
|
333
337
|
const cascadeCount = Math.max(1, Math.min(4, Math.round(lights.cascadeCount ?? 1))) as
|
|
334
338
|
| 1
|
|
@@ -365,6 +369,21 @@ function topologyOf(
|
|
|
365
369
|
]),
|
|
366
370
|
],
|
|
367
371
|
};
|
|
372
|
+
const volumeCapability = hasVolumetricFogCapability(
|
|
373
|
+
internals.device.caps,
|
|
374
|
+
internals.volumetricFogShaders,
|
|
375
|
+
);
|
|
376
|
+
const selectedVolumeLight =
|
|
377
|
+
volumetricFog?.lightKind === 'spot'
|
|
378
|
+
? lights.spot.find((light) => light.entity === volumetricFog.lightEntity)
|
|
379
|
+
: volumetricFog?.lightKind === 'point'
|
|
380
|
+
? lights.point.find((light) => light.entity === volumetricFog.lightEntity)
|
|
381
|
+
: volumetricFog?.lightKind === 'directional' &&
|
|
382
|
+
lights.directional?.entity === volumetricFog.lightEntity
|
|
383
|
+
? lights.directional
|
|
384
|
+
: volumetricFog?.lightKind === undefined
|
|
385
|
+
? lights.directional
|
|
386
|
+
: undefined;
|
|
368
387
|
return ok({
|
|
369
388
|
pipelineId: STANDARD_PIPELINE_ID,
|
|
370
389
|
standardProfile: internals.standardProfile,
|
|
@@ -398,6 +417,47 @@ function topologyOf(
|
|
|
398
417
|
).length,
|
|
399
418
|
),
|
|
400
419
|
},
|
|
420
|
+
volumetricFog:
|
|
421
|
+
volumetricFog?.status === 'available' &&
|
|
422
|
+
volumetricFog.densityAsset?.shape.viewDimension === '3d' &&
|
|
423
|
+
camera.tonemap !== 'none' &&
|
|
424
|
+
selectedVolumeLight !== undefined &&
|
|
425
|
+
volumeCapability
|
|
426
|
+
? (() => {
|
|
427
|
+
const volumeProfile = resolveVolumetricFogProfile(internals.standardProfile ?? {});
|
|
428
|
+
return {
|
|
429
|
+
enabled: true,
|
|
430
|
+
format: volumetricFog.densityAsset.format,
|
|
431
|
+
extent: {
|
|
432
|
+
width: volumetricFog.densityAsset.shape.extent.width,
|
|
433
|
+
height: volumetricFog.densityAsset.shape.extent.height,
|
|
434
|
+
depth: volumetricFog.densityAsset.shape.extent.depth,
|
|
435
|
+
},
|
|
436
|
+
froxelExtent: deriveVolumetricFogExtent(
|
|
437
|
+
{ width, height },
|
|
438
|
+
volumeProfile.depth,
|
|
439
|
+
volumeProfile.tileSize,
|
|
440
|
+
),
|
|
441
|
+
resolvedExtent: deriveVolumetricFogResolvedExtent(
|
|
442
|
+
{ width, height },
|
|
443
|
+
volumeProfile.tileSize,
|
|
444
|
+
),
|
|
445
|
+
lightKind: volumetricFog.lightKind,
|
|
446
|
+
lightEntity: volumetricFog.lightEntity,
|
|
447
|
+
pointLightEntity: volumetricFog.pointLightEntity,
|
|
448
|
+
spotLightEntity: volumetricFog.spotLightEntity,
|
|
449
|
+
...(volumetricFog.projector === undefined
|
|
450
|
+
? {}
|
|
451
|
+
: {
|
|
452
|
+
projector: {
|
|
453
|
+
guid: volumetricFog.projector.guid,
|
|
454
|
+
generation: volumetricFog.projector.generation,
|
|
455
|
+
revision: volumetricFog.projector.revision,
|
|
456
|
+
},
|
|
457
|
+
}),
|
|
458
|
+
};
|
|
459
|
+
})()
|
|
460
|
+
: { enabled: false },
|
|
401
461
|
lane: {
|
|
402
462
|
compute: internals.device.caps.compute,
|
|
403
463
|
storageBuffer: internals.device.caps.storageBuffer,
|
|
@@ -610,6 +670,32 @@ function retire(
|
|
|
610
670
|
.catch(() => undefined);
|
|
611
671
|
}
|
|
612
672
|
|
|
673
|
+
/**
|
|
674
|
+
* Settle a volume topology candidate after the command buffer outcome is
|
|
675
|
+
* known. A graph that failed to encode, finish, or submit must never become
|
|
676
|
+
* the next frame's accepted graph: retire it and restore the prior graph (or
|
|
677
|
+
* leave the renderer without a graph when this was the first attempt).
|
|
678
|
+
*/
|
|
679
|
+
export function settleVolumetricFogGraphCandidate(
|
|
680
|
+
frameState: RenderFrameState,
|
|
681
|
+
submitted: boolean,
|
|
682
|
+
): void {
|
|
683
|
+
const candidate = frameState.volumetricFogCandidateGraph ?? null;
|
|
684
|
+
if (candidate === null) return;
|
|
685
|
+
const previous = frameState.volumetricFogPreviousGraph ?? null;
|
|
686
|
+
const previousKey = frameState.volumetricFogPreviousGraphKey ?? null;
|
|
687
|
+
if (submitted) {
|
|
688
|
+
if (previous !== null && previous !== candidate) retire(frameState, previous);
|
|
689
|
+
} else {
|
|
690
|
+
if (candidate !== previous) retire(frameState, candidate);
|
|
691
|
+
frameState.compiledFrameGraph = previous;
|
|
692
|
+
frameState.compiledFrameGraphTopologyKey = previousKey;
|
|
693
|
+
}
|
|
694
|
+
frameState.volumetricFogPreviousGraph = null;
|
|
695
|
+
frameState.volumetricFogPreviousGraphKey = null;
|
|
696
|
+
frameState.volumetricFogCandidateGraph = null;
|
|
697
|
+
}
|
|
698
|
+
|
|
613
699
|
export function ensureCompiledFrameGraph(
|
|
614
700
|
internals: RenderSystemInternals,
|
|
615
701
|
frameState: RenderFrameState,
|
|
@@ -623,6 +709,8 @@ export function ensureCompiledFrameGraph(
|
|
|
623
709
|
featureGraphCandidate?: RenderFeatureGraphCandidate,
|
|
624
710
|
clearOnly = false,
|
|
625
711
|
transmissionDemand?: TransmissionDemand,
|
|
712
|
+
volumetricFog?: ExtractedVolumetricFog,
|
|
713
|
+
volumeTopologyCandidate = false,
|
|
626
714
|
): CompiledRenderGraph<RenderPipelineFrame> | null {
|
|
627
715
|
const surfaceWidth = Math.max(1, width);
|
|
628
716
|
const surfaceHeight = Math.max(1, height);
|
|
@@ -631,7 +719,7 @@ export function ensureCompiledFrameGraph(
|
|
|
631
719
|
if (!projectedFeaturePlans.ok) {
|
|
632
720
|
reportRenderFeatureGraphError(internals, projectedFeaturePlans.error);
|
|
633
721
|
featureGraphCandidate?.onRejected?.();
|
|
634
|
-
return frameState.compiledFrameGraph;
|
|
722
|
+
return volumeTopologyCandidate ? null : frameState.compiledFrameGraph;
|
|
635
723
|
}
|
|
636
724
|
const topologyResult = topologyOf(
|
|
637
725
|
internals,
|
|
@@ -646,11 +734,12 @@ export function ensureCompiledFrameGraph(
|
|
|
646
734
|
gpuDriven,
|
|
647
735
|
featureGraphCandidate,
|
|
648
736
|
transmissionDemand,
|
|
737
|
+
volumetricFog,
|
|
649
738
|
);
|
|
650
739
|
if (!topologyResult.ok) {
|
|
651
740
|
internals.errorRegistry.fire(topologyResult.error);
|
|
652
741
|
featureGraphCandidate?.onRejected?.();
|
|
653
|
-
return frameState.compiledFrameGraph;
|
|
742
|
+
return volumeTopologyCandidate ? null : frameState.compiledFrameGraph;
|
|
654
743
|
}
|
|
655
744
|
const topology = topologyResult.value;
|
|
656
745
|
const key = JSON.stringify(topology);
|
|
@@ -658,6 +747,10 @@ export function ensureCompiledFrameGraph(
|
|
|
658
747
|
if (featureGraphCandidate !== undefined) {
|
|
659
748
|
commitFeatureCandidate(internals, featureGraphCandidate);
|
|
660
749
|
}
|
|
750
|
+
// A candidate flag only describes a possible topology replacement. When
|
|
751
|
+
// the derived key is unchanged there is no replacement to stage: reuse the
|
|
752
|
+
// accepted graph so dynamic volume history/imported views can advance and
|
|
753
|
+
// a queue-submit failure can still publish the LKG diagnostic.
|
|
661
754
|
return frameState.compiledFrameGraph;
|
|
662
755
|
}
|
|
663
756
|
|
|
@@ -701,7 +794,7 @@ export function ensureCompiledFrameGraph(
|
|
|
701
794
|
if (!built.ok) {
|
|
702
795
|
internals.errorRegistry.fire(built.error);
|
|
703
796
|
featureGraphCandidate?.onRejected?.();
|
|
704
|
-
return frameState.compiledFrameGraph;
|
|
797
|
+
return volumeTopologyCandidate ? null : frameState.compiledFrameGraph;
|
|
705
798
|
}
|
|
706
799
|
const compiled = builder.compile({
|
|
707
800
|
device: internals.device,
|
|
@@ -713,6 +806,11 @@ export function ensureCompiledFrameGraph(
|
|
|
713
806
|
return frameState.compiledFrameGraph;
|
|
714
807
|
}
|
|
715
808
|
const previous = frameState.compiledFrameGraph;
|
|
809
|
+
if (volumeTopologyCandidate) {
|
|
810
|
+
frameState.volumetricFogPreviousGraph = previous;
|
|
811
|
+
frameState.volumetricFogPreviousGraphKey = frameState.compiledFrameGraphTopologyKey;
|
|
812
|
+
frameState.volumetricFogCandidateGraph = compiled.value;
|
|
813
|
+
}
|
|
716
814
|
frameState.compiledFrameGraph = compiled.value;
|
|
717
815
|
frameState.compiledFrameGraphTopologyKey = key;
|
|
718
816
|
const compiledTargets = compiled.value as CompiledGraphTargetAccess;
|
|
@@ -727,7 +825,7 @@ export function ensureCompiledFrameGraph(
|
|
|
727
825
|
if (featureGraphCandidate !== undefined) {
|
|
728
826
|
commitFeatureCandidate(internals, featureGraphCandidate);
|
|
729
827
|
}
|
|
730
|
-
if (previous !== null) retire(frameState, previous);
|
|
828
|
+
if (!volumeTopologyCandidate && previous !== null) retire(frameState, previous);
|
|
731
829
|
return compiled.value;
|
|
732
830
|
}
|
|
733
831
|
|
|
@@ -737,15 +835,41 @@ export function executeCompiledFrameGraph(
|
|
|
737
835
|
frame: RenderPipelineFrame,
|
|
738
836
|
encoder: RhiCommandEncoder,
|
|
739
837
|
runPass?: import('@forgeax/engine-render-graph').RenderGraphPassRunner,
|
|
838
|
+
timingCapture?: GpuTimingCapture,
|
|
740
839
|
): boolean {
|
|
741
840
|
const graph = frameState.compiledFrameGraph;
|
|
742
|
-
|
|
841
|
+
const rejectTemporalFrame = (): void => {
|
|
842
|
+
if (frameState.temporalFrameInput === undefined) return;
|
|
843
|
+
frameState.temporalFrameTransaction.commit({ accepted: false });
|
|
844
|
+
frameState.temporalFrameInput = undefined;
|
|
845
|
+
};
|
|
846
|
+
if (graph === null) {
|
|
847
|
+
timingCapture?.discard();
|
|
848
|
+
settleVolumetricFogGraphCandidate(frameState, false);
|
|
849
|
+
rejectTemporalFrame();
|
|
850
|
+
return false;
|
|
851
|
+
}
|
|
852
|
+
const timedRunPass =
|
|
853
|
+
timingCapture === undefined
|
|
854
|
+
? runPass
|
|
855
|
+
: (
|
|
856
|
+
pass: import('@forgeax/engine-render-graph').RenderGraphPassExecution,
|
|
857
|
+
encode: () => void,
|
|
858
|
+
) => {
|
|
859
|
+
timingCapture.beforePass(pass.name, encoder);
|
|
860
|
+
try {
|
|
861
|
+
if (runPass === undefined) encode();
|
|
862
|
+
else runPass(pass, encode);
|
|
863
|
+
} finally {
|
|
864
|
+
timingCapture.afterPass(pass.name, encoder);
|
|
865
|
+
}
|
|
866
|
+
};
|
|
743
867
|
type PreparedCommand = { readonly command: CommandBuffer };
|
|
744
868
|
let preparedCommand: PreparedCommand = { command: undefined as never };
|
|
745
869
|
const transaction = executeRendererFrameTransaction<PreparedCommand>({
|
|
746
870
|
build: () => ({ ok: true, value: { command: undefined as never } }),
|
|
747
871
|
execute: () => {
|
|
748
|
-
const executed = graph.execute(frame,
|
|
872
|
+
const executed = graph.execute(frame, timedRunPass);
|
|
749
873
|
if (!executed.ok) {
|
|
750
874
|
internals.errorRegistry.fire(executed.error);
|
|
751
875
|
return { ok: false, stage: 'execute' };
|
|
@@ -887,8 +1011,14 @@ export function executeCompiledFrameGraph(
|
|
|
887
1011
|
);
|
|
888
1012
|
return { ok: false, stage: 'finish' };
|
|
889
1013
|
}
|
|
1014
|
+
const resolved = timingCapture?.resolve(encoder);
|
|
1015
|
+
if (resolved !== undefined && !resolved.ok) {
|
|
1016
|
+
timingCapture?.discard();
|
|
1017
|
+
return { ok: false, stage: 'finish' };
|
|
1018
|
+
}
|
|
890
1019
|
const finished = encoder.finish();
|
|
891
1020
|
if (!finished.ok) {
|
|
1021
|
+
timingCapture?.discard();
|
|
892
1022
|
internals.errorRegistry.fire(finished.error);
|
|
893
1023
|
return { ok: false, stage: 'finish' };
|
|
894
1024
|
}
|
|
@@ -898,9 +1028,11 @@ export function executeCompiledFrameGraph(
|
|
|
898
1028
|
submit: () => {
|
|
899
1029
|
const submitted = internals.device.queue.submit([preparedCommand.command]);
|
|
900
1030
|
if (!submitted.ok) {
|
|
1031
|
+
timingCapture?.discard();
|
|
901
1032
|
internals.errorRegistry.fire(submitted.error);
|
|
902
1033
|
return { ok: false, stage: 'submit' };
|
|
903
1034
|
}
|
|
1035
|
+
timingCapture?.markSubmitted();
|
|
904
1036
|
return { ok: true, value: undefined };
|
|
905
1037
|
},
|
|
906
1038
|
commit: () => {
|
|
@@ -974,6 +1106,7 @@ export function executeCompiledFrameGraph(
|
|
|
974
1106
|
}
|
|
975
1107
|
},
|
|
976
1108
|
abort: (failure) => {
|
|
1109
|
+
timingCapture?.discard();
|
|
977
1110
|
frameState.bloomFrameReceipts = undefined;
|
|
978
1111
|
if (frameState.environmentGeneration !== undefined) {
|
|
979
1112
|
frameState.environmentLifecycle?.recordStageFailure(
|
|
@@ -996,5 +1129,7 @@ export function executeCompiledFrameGraph(
|
|
|
996
1129
|
}
|
|
997
1130
|
},
|
|
998
1131
|
});
|
|
1132
|
+
settleVolumetricFogGraphCandidate(frameState, transaction.ok);
|
|
1133
|
+
if (!transaction.ok) rejectTemporalFrame();
|
|
999
1134
|
return transaction.ok;
|
|
1000
1135
|
}
|
package/src/render-contract.ts
CHANGED
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
RhiInstance,
|
|
17
17
|
RhiRenderPassEncoder,
|
|
18
18
|
RenderPipeline as RhiRenderPipeline,
|
|
19
|
+
Sampler,
|
|
19
20
|
Texture,
|
|
20
21
|
TextureFormat,
|
|
21
22
|
TextureView,
|
|
@@ -43,7 +44,9 @@ export type { PointsLinesInspection } from './points-lines/inspection';
|
|
|
43
44
|
import type { MeshMaterialBindingObservation } from './mesh-material-bindings';
|
|
44
45
|
import type { StandardProfile } from './pipeline/standard-profile';
|
|
45
46
|
import type { PostProcessError } from './post-process-errors';
|
|
47
|
+
import type { VolumeTimingObservation } from './record/gpu-timing';
|
|
46
48
|
import type { TemporalView } from './temporal/view';
|
|
49
|
+
import type { VolumetricFogInspection } from './volume/inspection';
|
|
47
50
|
|
|
48
51
|
export type RenderResult<T, E> =
|
|
49
52
|
| { readonly ok: true; readonly value: T }
|
|
@@ -118,6 +121,14 @@ export type { TemporalView } from './temporal/view';
|
|
|
118
121
|
interface RenderPipelineStateView {
|
|
119
122
|
readonly format: TextureFormat;
|
|
120
123
|
readonly colorAttachmentFormat: TextureFormat;
|
|
124
|
+
/** Renderer-owned View UBO shared by Standard lighting, shadows, and fog. */
|
|
125
|
+
readonly viewUniformBuffer: Buffer;
|
|
126
|
+
/** Renderer-owned SpotLight std430 buffer shared by Standard and volume. */
|
|
127
|
+
readonly pointLightsBuffer: Buffer;
|
|
128
|
+
readonly spotLightsBuffer: Buffer;
|
|
129
|
+
/** Fallback resources keep the shared view layout valid when no projector is authored. */
|
|
130
|
+
readonly defaultWhiteTextureView: TextureView;
|
|
131
|
+
readonly defaultSampler: Sampler;
|
|
121
132
|
}
|
|
122
133
|
|
|
123
134
|
/**
|
|
@@ -127,6 +138,16 @@ interface RenderPipelineStateView {
|
|
|
127
138
|
*/
|
|
128
139
|
interface RenderPipelineRuntime {
|
|
129
140
|
readonly device: RhiDevice;
|
|
141
|
+
/** Runtime-owned shader module factory used by built-in graph producers. */
|
|
142
|
+
readonly shaderModuleFactory?: {
|
|
143
|
+
createShaderModule(input: {
|
|
144
|
+
readonly code: string;
|
|
145
|
+
readonly label?: string | undefined;
|
|
146
|
+
}): import('@forgeax/engine-types').Result<
|
|
147
|
+
import('@forgeax/engine-rhi').ShaderModule,
|
|
148
|
+
RhiError
|
|
149
|
+
>;
|
|
150
|
+
};
|
|
130
151
|
readonly errorRegistry: { fire(error: RendererError): void };
|
|
131
152
|
readonly debugOverlay?: RenderDebugOverlay | undefined;
|
|
132
153
|
readonly lookupPostProcess?: (id: string) => PostProcessShaderEntry | undefined;
|
|
@@ -170,6 +191,52 @@ export interface RenderPipelineContext {
|
|
|
170
191
|
readonly msaaActive: boolean;
|
|
171
192
|
readonly geometryColorResolveView: TextureView | null;
|
|
172
193
|
readonly ldrSpriteColorView: TextureView | null;
|
|
194
|
+
/** Renderer-owned volume resources prepared from the authored ECS snapshot. */
|
|
195
|
+
readonly volumetricFog?: VolumetricFogFrameContext;
|
|
196
|
+
/** Renderer-owned SpotLight projector resources, independent of fog. */
|
|
197
|
+
readonly spotLightProjector?: SpotLightProjectorFrameContext;
|
|
198
|
+
/** Build-time composed utility sources for the renderer-owned volume passes. */
|
|
199
|
+
readonly volumetricFogShaders?: VolumetricFogShaderSources;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Stable utility entry-point sources emitted by the shader manifest. */
|
|
203
|
+
export interface VolumetricFogShaderSources {
|
|
204
|
+
readonly inject: string;
|
|
205
|
+
readonly temporal: string;
|
|
206
|
+
readonly integrate: string;
|
|
207
|
+
readonly composite: string;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* GPU resources for one validated volumetric-fog candidate. The record stage
|
|
212
|
+
* owns residency and parameter uploads; graph passes consume this narrow view
|
|
213
|
+
* and never reach into AssetRegistry or World.
|
|
214
|
+
*/
|
|
215
|
+
export interface VolumetricFogFrameContext {
|
|
216
|
+
readonly densityTexture: Texture;
|
|
217
|
+
readonly densityView: TextureView;
|
|
218
|
+
readonly paramsBuffer: Buffer;
|
|
219
|
+
readonly densityGeneration: number;
|
|
220
|
+
/** Accepted graph-owned history slot; null means first frame/reset. */
|
|
221
|
+
readonly historyReadSlot: 0 | 1 | null;
|
|
222
|
+
/** Pending graph-owned history slot; promoted only after queue.submit succeeds. */
|
|
223
|
+
readonly historyWriteSlot: 0 | 1;
|
|
224
|
+
readonly historyValid: boolean;
|
|
225
|
+
/** Accepted SpotLight projector resources resolved by the GPU residency owner. */
|
|
226
|
+
readonly projectorTexture?: Texture;
|
|
227
|
+
readonly projectorView?: TextureView;
|
|
228
|
+
readonly projectorSampler?: Sampler;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* GPU resources for the single surface SpotLight projector binding. The
|
|
233
|
+
* projector is a SpotLight-owned cookie and therefore remains available when
|
|
234
|
+
* volumetric fog is disabled; volume integration may reuse the same tuple.
|
|
235
|
+
*/
|
|
236
|
+
export interface SpotLightProjectorFrameContext {
|
|
237
|
+
readonly texture: Texture;
|
|
238
|
+
readonly view: TextureView;
|
|
239
|
+
readonly sampler: Sampler;
|
|
173
240
|
}
|
|
174
241
|
|
|
175
242
|
/**
|
|
@@ -274,6 +341,7 @@ export interface FrameReceiptObservation {
|
|
|
274
341
|
readonly frameId: number;
|
|
275
342
|
readonly deviceGeneration: number;
|
|
276
343
|
readonly include: readonly ('timings' | 'draws' | 'bindings')[];
|
|
344
|
+
readonly timings?: VolumeTimingObservation;
|
|
277
345
|
}
|
|
278
346
|
|
|
279
347
|
/** Stable backend facts for the Standard output transform projection. */
|
|
@@ -342,6 +410,8 @@ export interface RenderInspection {
|
|
|
342
410
|
readonly frameId: number;
|
|
343
411
|
readonly deviceGeneration: number;
|
|
344
412
|
};
|
|
413
|
+
/** Renderer-owned authored volume projection and submit/LKG state. */
|
|
414
|
+
readonly volumetricFog?: VolumetricFogInspection;
|
|
345
415
|
readonly features: readonly string[];
|
|
346
416
|
/** Truthful lifecycle facts for every registered declarative feature. */
|
|
347
417
|
readonly featureDiagnostics: readonly RenderFeatureDiagnostics[];
|
|
@@ -577,6 +647,8 @@ export interface RendererOptions {
|
|
|
577
647
|
readonly standardProfile?: RenderProfile | undefined;
|
|
578
648
|
/** Explicit profiler capability shared by App and Render. */
|
|
579
649
|
readonly profiler?: Profiler | undefined;
|
|
650
|
+
/** Explicitly request timestamp-query for receipt-bound GPU profiling. */
|
|
651
|
+
readonly captureGpuTimings?: boolean | undefined;
|
|
580
652
|
/** Optional host-owned RHI lifecycle instrumentation, such as recording. */
|
|
581
653
|
readonly rhiInstrumentation?: RhiBackendInstrumentation | undefined;
|
|
582
654
|
// feat-20260608-create-app-param-surface-trim / M1 / AC-02: `clearColor`
|
package/src/render-data.ts
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
// GPU-resource kind would surface as a `tsc -b` exhaustiveness error at the
|
|
16
16
|
// switch rather than a silent fallthrough (AC-06).
|
|
17
17
|
|
|
18
|
-
import { numMipLevels } from '@forgeax/engine-assets-runtime';
|
|
19
18
|
import {
|
|
20
19
|
bytesPerRow as blockBytesPerRow,
|
|
21
20
|
blockParamsForFormat,
|
|
@@ -29,9 +28,11 @@ import { err, ok, type Result } from '@forgeax/engine-rhi';
|
|
|
29
28
|
import {
|
|
30
29
|
ASSET_ERROR_HINTS,
|
|
31
30
|
type AssetError,
|
|
31
|
+
deriveTextureLayout,
|
|
32
32
|
type MeshAsset,
|
|
33
33
|
type Submesh,
|
|
34
34
|
type TextureAsset,
|
|
35
|
+
type TextureShape,
|
|
35
36
|
} from '@forgeax/engine-types';
|
|
36
37
|
import {
|
|
37
38
|
GPU_TEXTURE_USAGE_COPY_DST,
|
|
@@ -88,12 +89,16 @@ export interface MeshRenderData {
|
|
|
88
89
|
readonly submeshes: readonly Submesh[];
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
/** Descriptor for a
|
|
92
|
+
/** Descriptor for a sampled texture's GPU resource (projected from POD). */
|
|
92
93
|
export interface TextureRenderData {
|
|
93
94
|
/** Logical width retained for existing projection consumers. */
|
|
94
95
|
readonly width: number;
|
|
95
96
|
/** Logical height retained for existing projection consumers. */
|
|
96
97
|
readonly height: number;
|
|
98
|
+
/** Authored shape; the residency layer must preserve this view dimension. */
|
|
99
|
+
readonly shape: TextureShape;
|
|
100
|
+
/** Array layers for `2d-array`, depth slices for `3d`, otherwise one. */
|
|
101
|
+
readonly depthOrArrayLayers: number;
|
|
97
102
|
/** Authored content extent; TextureAsset remains the logical asset SSOT. */
|
|
98
103
|
readonly logicalExtent: TextureExtent;
|
|
99
104
|
/** GPU allocation extent, derived from format + logicalExtent. */
|
|
@@ -105,10 +110,10 @@ export interface TextureRenderData {
|
|
|
105
110
|
readonly usage: number;
|
|
106
111
|
/**
|
|
107
112
|
* Row pitch for the BASE mip (level 0) the store passes to `writeTexture`.
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* The per-mip layout for a multi-level compressed
|
|
111
|
-
* {@link deriveMipUploadLayout}.
|
|
113
|
+
* Derived from the canonical texture layout, so format-specific uncompressed
|
|
114
|
+
* widths (for example R8 density) and block-compressed widths stay aligned
|
|
115
|
+
* with the upload contract. The per-mip layout for a multi-level compressed
|
|
116
|
+
* upload comes from {@link deriveMipUploadLayout}.
|
|
112
117
|
*/
|
|
113
118
|
readonly bytesPerRow: number;
|
|
114
119
|
/** True iff `format` is a block-compressed format (drives the store's upload arm). */
|
|
@@ -262,6 +267,8 @@ export function deriveRenderDataMesh(mesh: MeshAsset): Result<MeshRenderData, As
|
|
|
262
267
|
* inconsistent (a `-srgb` format requires an `srgb` colorSpace and vice versa).
|
|
263
268
|
*/
|
|
264
269
|
export function deriveRenderDataTexture(tex: TextureAsset): Result<TextureRenderData, AssetError> {
|
|
270
|
+
const width = tex.shape.extent.width;
|
|
271
|
+
const height = tex.shape.extent.height;
|
|
265
272
|
const isSrgbFormat = tex.format.endsWith('-srgb');
|
|
266
273
|
const expectedColorSpace: 'srgb' | 'linear' = isSrgbFormat ? 'srgb' : 'linear';
|
|
267
274
|
if (tex.colorSpace !== expectedColorSpace) {
|
|
@@ -275,14 +282,23 @@ export function deriveRenderDataTexture(tex: TextureAsset): Result<TextureRender
|
|
|
275
282
|
}
|
|
276
283
|
|
|
277
284
|
const compressed = isCompressedFormat(tex.format);
|
|
285
|
+
const layout = deriveTextureLayout({ shape: tex.shape, format: tex.format, mips: tex.mips });
|
|
286
|
+
if (!layout.ok) {
|
|
287
|
+
return err(
|
|
288
|
+
projectionError({
|
|
289
|
+
code: 'invalid-source-format',
|
|
290
|
+
expected: layout.error.expected,
|
|
291
|
+
hint: layout.error.hint,
|
|
292
|
+
}),
|
|
293
|
+
);
|
|
294
|
+
}
|
|
278
295
|
// feat-20260707 M5 / w35 (AC-09, D-9): a block-compressed texture cannot have
|
|
279
296
|
// its mips GPU-generated (F-7). An OFFLINE chain baked into `data`
|
|
280
297
|
// (mipLevelCount > 1, e.g. a KTX2 level chain) is the normal compressed path
|
|
281
298
|
// and passes; requesting RUNTIME mip generation (`mipmap:true` with no offline
|
|
282
299
|
// chain) fails fast with a self-recovery hint (fail-fast at the projection
|
|
283
300
|
// layer -- producer self-validate).
|
|
284
|
-
|
|
285
|
-
if (compressed && tex.mipmap === true && !hasOfflineMipChain) {
|
|
301
|
+
if (compressed && tex.mips.kind === 'generate') {
|
|
286
302
|
return err(
|
|
287
303
|
projectionError({
|
|
288
304
|
code: 'mipgen-unsupported-compressed-format',
|
|
@@ -296,15 +312,19 @@ export function deriveRenderDataTexture(tex: TextureAsset): Result<TextureRender
|
|
|
296
312
|
// Uncompressed: runtime mip-gen count from the pixel size. Compressed: the
|
|
297
313
|
// level count is authored (offline chain) -- honour the POD's mipLevelCount,
|
|
298
314
|
// never re-derive it from the size (a compressed chain may stop early).
|
|
299
|
-
const mipLevelCount =
|
|
300
|
-
? Math.max(1, tex.mipLevelCount ?? 1)
|
|
301
|
-
: tex.mipmap
|
|
302
|
-
? numMipLevels(tex)
|
|
303
|
-
: 1;
|
|
315
|
+
const mipLevelCount = layout.value.levels.length;
|
|
304
316
|
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
317
|
+
const baseLevel = layout.value.levels[0];
|
|
318
|
+
if (baseLevel === undefined) {
|
|
319
|
+
return err(
|
|
320
|
+
projectionError({
|
|
321
|
+
code: 'invalid-source-format',
|
|
322
|
+
expected: 'texture layout contains a base mip level',
|
|
323
|
+
hint: ASSET_ERROR_HINTS['invalid-source-format'],
|
|
324
|
+
}),
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
if (!compressed && tex.data.byteLength < baseLevel.byteLength) {
|
|
308
328
|
return err(
|
|
309
329
|
projectionError({
|
|
310
330
|
code: 'invalid-source-format',
|
|
@@ -318,8 +338,8 @@ export function deriveRenderDataTexture(tex: TextureAsset): Result<TextureRender
|
|
|
318
338
|
// from the format, not aggregate payload length: imported images may retain
|
|
319
339
|
// trailing decoder bytes and offline mip chains contain more than the base mip.
|
|
320
340
|
const bytesPerRow = compressed
|
|
321
|
-
? (blockBytesPerRow(tex.format,
|
|
322
|
-
:
|
|
341
|
+
? (blockBytesPerRow(tex.format, width) ?? baseLevel.bytesPerRow)
|
|
342
|
+
: baseLevel.bytesPerRow;
|
|
323
343
|
|
|
324
344
|
// Compressed formats are not renderable -- RENDER_ATTACHMENT would fail
|
|
325
345
|
// createTexture validation, and the runtime mipmap blit (which needs it) never
|
|
@@ -333,9 +353,16 @@ export function deriveRenderDataTexture(tex: TextureAsset): Result<TextureRender
|
|
|
333
353
|
GPU_TEXTURE_USAGE_COPY_SRC;
|
|
334
354
|
|
|
335
355
|
return ok({
|
|
336
|
-
width
|
|
337
|
-
height
|
|
338
|
-
|
|
356
|
+
width,
|
|
357
|
+
height,
|
|
358
|
+
shape: tex.shape,
|
|
359
|
+
depthOrArrayLayers:
|
|
360
|
+
tex.shape.viewDimension === '2d'
|
|
361
|
+
? 1
|
|
362
|
+
: tex.shape.viewDimension === '2d-array'
|
|
363
|
+
? tex.shape.extent.layers
|
|
364
|
+
: tex.shape.extent.depth,
|
|
365
|
+
...deriveTextureExtent(tex.format, width, height),
|
|
339
366
|
format: tex.format,
|
|
340
367
|
mipLevelCount,
|
|
341
368
|
usage,
|
|
@@ -373,8 +400,9 @@ export function deriveRenderDataCubemap(source: TextureAsset): Result<CubeRender
|
|
|
373
400
|
}),
|
|
374
401
|
);
|
|
375
402
|
}
|
|
403
|
+
const sourceHeight = source.shape.viewDimension === '2d' ? source.shape.extent.height : 0;
|
|
376
404
|
return ok({
|
|
377
|
-
cubeFaceSize:
|
|
405
|
+
cubeFaceSize: sourceHeight,
|
|
378
406
|
outputFormat: 'rgba16float',
|
|
379
407
|
needsHalfConversion: source.format === 'rgba32float',
|
|
380
408
|
cubeUsage:
|
package/src/render-pipeline.ts
CHANGED
|
@@ -141,6 +141,48 @@ export interface RenderPipelineTopology {
|
|
|
141
141
|
readonly pointFaceSize: number;
|
|
142
142
|
readonly spotCount: number;
|
|
143
143
|
};
|
|
144
|
+
/**
|
|
145
|
+
* Authored volume topology facts. Identity/generation/digest are runtime
|
|
146
|
+
* residency facts and deliberately do not participate in graph topology.
|
|
147
|
+
*/
|
|
148
|
+
readonly volumetricFog?: {
|
|
149
|
+
readonly enabled: boolean;
|
|
150
|
+
readonly lightKind?: 'directional' | 'point' | 'spot' | undefined;
|
|
151
|
+
readonly lightEntity?: number | undefined;
|
|
152
|
+
readonly pointLightEntity?: number | undefined;
|
|
153
|
+
readonly spotLightEntity?: number | undefined;
|
|
154
|
+
readonly projector?:
|
|
155
|
+
| {
|
|
156
|
+
readonly guid: string;
|
|
157
|
+
readonly generation: number;
|
|
158
|
+
readonly revision: number;
|
|
159
|
+
}
|
|
160
|
+
| undefined;
|
|
161
|
+
readonly format?: TextureFormat | undefined;
|
|
162
|
+
readonly extent?:
|
|
163
|
+
| {
|
|
164
|
+
readonly width: number;
|
|
165
|
+
readonly height: number;
|
|
166
|
+
readonly depth: number;
|
|
167
|
+
}
|
|
168
|
+
| undefined;
|
|
169
|
+
/** Renderer-owned froxel grid derived from the compiled surface extent. */
|
|
170
|
+
readonly froxelExtent?:
|
|
171
|
+
| {
|
|
172
|
+
readonly width: number;
|
|
173
|
+
readonly height: number;
|
|
174
|
+
readonly depth: number;
|
|
175
|
+
}
|
|
176
|
+
| undefined;
|
|
177
|
+
/** Renderer-owned 2D resolve/history extent derived from the same profile. */
|
|
178
|
+
readonly resolvedExtent?:
|
|
179
|
+
| {
|
|
180
|
+
readonly width: number;
|
|
181
|
+
readonly height: number;
|
|
182
|
+
readonly depth: number;
|
|
183
|
+
}
|
|
184
|
+
| undefined;
|
|
185
|
+
};
|
|
144
186
|
readonly lane: {
|
|
145
187
|
readonly compute: boolean;
|
|
146
188
|
readonly storageBuffer: boolean;
|
|
@@ -257,6 +299,8 @@ export function importRenderPipelineSurface(
|
|
|
257
299
|
}
|
|
258
300
|
|
|
259
301
|
export interface RenderPipelineFeatureTarget {
|
|
302
|
+
/** Optional semantic alias when a pipeline exposes multiple color roles. */
|
|
303
|
+
readonly name?: string;
|
|
260
304
|
readonly kind: RenderFeatureTargetKind;
|
|
261
305
|
readonly texture: GraphTexture;
|
|
262
306
|
readonly view: GraphTextureView;
|