@zephyr3d/scene 0.9.22 → 0.9.23
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/animation/joint_dynamics/collision.js +1 -7
- package/dist/animation/joint_dynamics/collision.js.map +1 -1
- package/dist/animation/joint_dynamics/constraints.js +2 -17
- package/dist/animation/joint_dynamics/constraints.js.map +1 -1
- package/dist/animation/joint_dynamics/controller.js +2 -20
- package/dist/animation/joint_dynamics/controller.js.map +1 -1
- package/dist/animation/joint_dynamics/joint_dynamics_system.js +1 -3
- package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
- package/dist/animation/joint_dynamics/solver.js +14 -60
- package/dist/animation/joint_dynamics/solver.js.map +1 -1
- package/dist/animation/joint_dynamics/types.js +3 -3
- package/dist/animation/joint_dynamics/types.js.map +1 -1
- package/dist/app/app.js +23 -0
- package/dist/app/app.js.map +1 -1
- package/dist/app/scriptregistry.js +16 -53
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/asset/assetmanager.js +2 -0
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/asset/model.js +22 -0
- package/dist/asset/model.js.map +1 -1
- package/dist/blitter/bilateralblur.js +3 -1
- package/dist/blitter/bilateralblur.js.map +1 -1
- package/dist/blitter/blitter.js +3 -3
- package/dist/blitter/blitter.js.map +1 -1
- package/dist/camera/camera.js +398 -45
- package/dist/camera/camera.js.map +1 -1
- package/dist/camera/perspectivecamera.js +69 -2
- package/dist/camera/perspectivecamera.js.map +1 -1
- package/dist/index.d.ts +9113 -8734
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/material/blinn.js +1 -1
- package/dist/material/blinn.js.map +1 -1
- package/dist/material/lambert.js +3 -3
- package/dist/material/lambert.js.map +1 -1
- package/dist/material/material.js +8 -5
- package/dist/material/material.js.map +1 -1
- package/dist/material/meshmaterial.js +17 -10
- package/dist/material/meshmaterial.js.map +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
- package/dist/material/mixins/lightmodel/lambert.js +1 -1
- package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +21 -6
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +1 -1
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js +1 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
- package/dist/material/mixins/lit.js +33 -11
- package/dist/material/mixins/lit.js.map +1 -1
- package/dist/material/mixins/pbr/brdf.js +44 -12
- package/dist/material/mixins/pbr/brdf.js.map +1 -1
- package/dist/material/mixins/pbr/common.js +149 -14
- package/dist/material/mixins/pbr/common.js.map +1 -1
- package/dist/material/mtoon.js +7 -4
- package/dist/material/mtoon.js.map +1 -1
- package/dist/material/pbrblueprint.js +5 -1
- package/dist/material/pbrblueprint.js.map +1 -1
- package/dist/material/pbrmr.js +1 -1
- package/dist/material/pbrmr.js.map +1 -1
- package/dist/material/pbrsg.js +1 -1
- package/dist/material/pbrsg.js.map +1 -1
- package/dist/material/shader/helper.js +186 -11
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/skin.js +2 -2
- package/dist/material/skin.js.map +1 -1
- package/dist/material/terrain-cm.js +5 -5
- package/dist/material/terrain-cm.js.map +1 -1
- package/dist/material/water.js +6 -4
- package/dist/material/water.js.map +1 -1
- package/dist/posteffect/bloom.js +54 -4
- package/dist/posteffect/bloom.js.map +1 -1
- package/dist/posteffect/compositor.js +41 -0
- package/dist/posteffect/compositor.js.map +1 -1
- package/dist/posteffect/posteffect.js +35 -0
- package/dist/posteffect/posteffect.js.map +1 -1
- package/dist/posteffect/sao.js +7 -6
- package/dist/posteffect/sao.js.map +1 -1
- package/dist/posteffect/skinsss.js +3 -3
- package/dist/posteffect/skinsss.js.map +1 -1
- package/dist/posteffect/ssgi.js +988 -436
- package/dist/posteffect/ssgi.js.map +1 -1
- package/dist/posteffect/ssr.js +83 -45
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/posteffect/sss.js +18 -15
- package/dist/posteffect/sss.js.map +1 -1
- package/dist/posteffect/taa.js +5 -4
- package/dist/posteffect/taa.js.map +1 -1
- package/dist/posteffect/tonemap.js +15 -3
- package/dist/posteffect/tonemap.js.map +1 -1
- package/dist/render/abuffer_oit.js +2 -2
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/cluster_light.js +12 -6
- package/dist/render/cluster_light.js.map +1 -1
- package/dist/render/drawable_mixin.js +8 -5
- package/dist/render/drawable_mixin.js.map +1 -1
- package/dist/render/dualdepthpeeling_oit.js +10 -5
- package/dist/render/dualdepthpeeling_oit.js.map +1 -1
- package/dist/render/envlight.js +298 -5
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/gpu_picking.js +7 -5
- package/dist/render/gpu_picking.js.map +1 -1
- package/dist/render/hzb.js +28 -19
- package/dist/render/hzb.js.map +1 -1
- package/dist/render/lightpass.js +18 -4
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/render_queue.js +37 -4
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/renderer.js +8 -3
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +20 -48
- package/dist/render/rendergraph/blackboard.js.map +1 -1
- package/dist/render/rendergraph/device_pool_allocator.js +18 -58
- package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
- package/dist/render/rendergraph/executor.js +89 -115
- package/dist/render/rendergraph/executor.js.map +1 -1
- package/dist/render/rendergraph/forward_plus_builder.js +1003 -715
- package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
- package/dist/render/rendergraph/frame_graph_context.js +1 -1
- package/dist/render/rendergraph/frame_graph_context.js.map +1 -1
- package/dist/render/rendergraph/frame_resource_requirements.js +14 -0
- package/dist/render/rendergraph/frame_resource_requirements.js.map +1 -0
- package/dist/render/rendergraph/history_resource_manager.js +74 -140
- package/dist/render/rendergraph/history_resource_manager.js.map +1 -1
- package/dist/render/rendergraph/history_resources.js +7 -5
- package/dist/render/rendergraph/history_resources.js.map +1 -1
- package/dist/render/rendergraph/render_context.js +19 -0
- package/dist/render/rendergraph/render_context.js.map +1 -0
- package/dist/render/rendergraph/render_pipeline.js +315 -0
- package/dist/render/rendergraph/render_pipeline.js.map +1 -0
- package/dist/render/rendergraph/rendergraph.js +158 -141
- package/dist/render/rendergraph/rendergraph.js.map +1 -1
- package/dist/render/rendergraph/scene_render_context.js +267 -0
- package/dist/render/rendergraph/scene_render_context.js.map +1 -0
- package/dist/render/rendergraph/texture_affinity_cache.js +19 -0
- package/dist/render/rendergraph/texture_affinity_cache.js.map +1 -0
- package/dist/render/rendergraph/types.js +19 -31
- package/dist/render/rendergraph/types.js.map +1 -1
- package/dist/render/renderpass.js +3 -3
- package/dist/render/renderpass.js.map +1 -1
- package/dist/render/shadow_mask_pass.js +7 -6
- package/dist/render/shadow_mask_pass.js.map +1 -1
- package/dist/render/sky.js +197 -35
- package/dist/render/sky.js.map +1 -1
- package/dist/render/weightedblended_oit.js +4 -3
- package/dist/render/weightedblended_oit.js.map +1 -1
- package/dist/scene/basesprite.js +2 -0
- package/dist/scene/basesprite.js.map +1 -1
- package/dist/scene/batchgroup.js +10 -0
- package/dist/scene/batchgroup.js.map +1 -1
- package/dist/scene/environment.js +61 -2
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/light.js +178 -10
- package/dist/scene/light.js.map +1 -1
- package/dist/scene/meshdrawable.js +89 -2
- package/dist/scene/meshdrawable.js.map +1 -1
- package/dist/scene/msdftext.js +2 -0
- package/dist/scene/msdftext.js.map +1 -1
- package/dist/scene/msdftextsprite.js +2 -0
- package/dist/scene/msdftextsprite.js.map +1 -1
- package/dist/scene/particlesys.js +2 -0
- package/dist/scene/particlesys.js.map +1 -1
- package/dist/scene/scene.js +34 -1
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/terrain-cm/grass.js +2 -1
- package/dist/scene/terrain-cm/grass.js.map +1 -1
- package/dist/scene/terrain-cm/terrain-cm.js +2 -0
- package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
- package/dist/scene/water.js +2 -0
- package/dist/scene/water.js.map +1 -1
- package/dist/shaders/atmosphere.js +5 -4
- package/dist/shaders/atmosphere.js.map +1 -1
- package/dist/shaders/fog.js +10 -3
- package/dist/shaders/fog.js.map +1 -1
- package/dist/shaders/shadow.js +96 -29
- package/dist/shaders/shadow.js.map +1 -1
- package/dist/shaders/ssgi_reprojection.js +118 -0
- package/dist/shaders/ssgi_reprojection.js.map +1 -0
- package/dist/shaders/ssr.js +322 -154
- package/dist/shaders/ssr.js.map +1 -1
- package/dist/shadow/esm.js +10 -7
- package/dist/shadow/esm.js.map +1 -1
- package/dist/shadow/pcf_opt.js +12 -12
- package/dist/shadow/pcf_opt.js.map +1 -1
- package/dist/shadow/pcf_pd.js +12 -12
- package/dist/shadow/pcf_pd.js.map +1 -1
- package/dist/shadow/pcss.js +15 -11
- package/dist/shadow/pcss.js.map +1 -1
- package/dist/shadow/shadow_impl.js +20 -2
- package/dist/shadow/shadow_impl.js.map +1 -1
- package/dist/shadow/shadowmapper.js +5 -2
- package/dist/shadow/shadowmapper.js.map +1 -1
- package/dist/shadow/ssm.js +13 -13
- package/dist/shadow/ssm.js.map +1 -1
- package/dist/shadow/vsm.js +7 -7
- package/dist/shadow/vsm.js.map +1 -1
- package/dist/utility/physical.js +74 -0
- package/dist/utility/physical.js.map +1 -0
- package/dist/utility/rendermipmap.js +2 -2
- package/dist/utility/rendermipmap.js.map +1 -1
- package/dist/utility/serialization/manager.js +3 -1
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +3 -1
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/batch.js +3 -1
- package/dist/utility/serialization/scene/batch.js.map +1 -1
- package/dist/utility/serialization/scene/camera.js +598 -2
- package/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/utility/serialization/scene/light.js +180 -9
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/dist/utility/serialization/scene/material.js +43 -3
- package/dist/utility/serialization/scene/material.js.map +1 -1
- package/dist/utility/serialization/scene/mesh.js +3 -1
- package/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/node.js +3 -1
- package/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/utility/serialization/scene/particle.js +3 -1
- package/dist/utility/serialization/scene/particle.js.map +1 -1
- package/dist/utility/serialization/scene/primitive.js +2 -0
- package/dist/utility/serialization/scene/primitive.js.map +1 -1
- package/dist/utility/serialization/scene/scene.js +70 -0
- package/dist/utility/serialization/scene/scene.js.map +1 -1
- package/dist/utility/serialization/scene/sprite.js +3 -1
- package/dist/utility/serialization/scene/sprite.js.map +1 -1
- package/dist/utility/serialization/scene/terrain.js +3 -1
- package/dist/utility/serialization/scene/terrain.js.map +1 -1
- package/dist/utility/serialization/scene/text.js +3 -0
- package/dist/utility/serialization/scene/text.js.map +1 -1
- package/dist/utility/serialization/scene/water.js +3 -1
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/values.js +2 -3
- package/dist/values.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,103 +1,86 @@
|
|
|
1
1
|
import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* let linearDepth: RGHandle;
|
|
12
|
-
* graph.addPass('DepthPrepass', (builder) => {
|
|
13
|
-
* linearDepth = builder.createTexture({ format: 'r32f', label: 'linearDepth' });
|
|
14
|
-
* builder.setExecute(() => { ... });
|
|
15
|
-
* });
|
|
16
|
-
*
|
|
17
|
-
* graph.addPass('LightPass', (builder) => {
|
|
18
|
-
* builder.read(linearDepth);
|
|
19
|
-
* backbuffer = builder.write(backbuffer);
|
|
20
|
-
* builder.setExecute(() => { ... });
|
|
21
|
-
* });
|
|
22
|
-
*
|
|
23
|
-
* const compiled = graph.compile([backbuffer]);
|
|
24
|
-
* graph.execute(compiled);
|
|
25
|
-
* graph.reset();
|
|
26
|
-
* ```
|
|
27
|
-
*
|
|
28
|
-
* @public
|
|
29
|
-
*/ class RenderGraph {
|
|
30
|
-
/** @internal */ _nextResourceId = 0;
|
|
31
|
-
/** @internal */ _resources = new Map();
|
|
32
|
-
/** @internal */ _passes = [];
|
|
33
|
-
/** @internal */ _compiled = null;
|
|
34
|
-
// ─── Graph Building ─────────────────────────────────────────────────
|
|
35
|
-
/**
|
|
36
|
-
* Import an external (persistent) texture into the graph.
|
|
37
|
-
*
|
|
38
|
-
* Imported resources are not allocated or released by the graph.
|
|
39
|
-
* Typically used for the backbuffer or any texture that outlives a single frame.
|
|
40
|
-
*
|
|
41
|
-
* @param name - Debug label for the imported resource.
|
|
42
|
-
* @returns A handle referencing the imported resource.
|
|
43
|
-
*/ importTexture(name) {
|
|
3
|
+
/** Declarative render pass and resource dependency graph. @public */ class RenderGraph {
|
|
4
|
+
_nextResourceId = 0;
|
|
5
|
+
_resources = new Map();
|
|
6
|
+
_passes = [];
|
|
7
|
+
_compiled = null;
|
|
8
|
+
_lastWriterByPhysicalId = new Map();
|
|
9
|
+
/** Deduplicates warnings across per-frame graph instances. */ static _warnedCulledPasses = new Set();
|
|
10
|
+
/** Import an external texture that the graph does not own. */ importTexture(name) {
|
|
44
11
|
const id = this._nextResourceId++;
|
|
45
12
|
const resource = new RGResource(id, name, 'imported', null);
|
|
46
13
|
this._resources.set(id, resource);
|
|
47
14
|
this._compiled = null;
|
|
48
|
-
return new RGHandle(id, name);
|
|
15
|
+
return new RGHandle(id, name, 'texture');
|
|
49
16
|
}
|
|
50
17
|
/**
|
|
51
|
-
* Add a
|
|
52
|
-
*
|
|
53
|
-
* The setup callback receives a {@link RGPassBuilder} to declare resource
|
|
54
|
-
* dependencies. Call `builder.setExecute(fn)` inside setup to provide the
|
|
55
|
-
* execution callback.
|
|
56
|
-
*
|
|
57
|
-
* @param name - Debug label for the pass.
|
|
58
|
-
* @param setup - Setup callback that declares resources and sets the execute function.
|
|
18
|
+
* Add a pass. The setup callback declares dependencies and execution.
|
|
59
19
|
*/ addPass(name, setup) {
|
|
60
|
-
|
|
20
|
+
let nameOccurrence = 0;
|
|
21
|
+
for (const existing of this._passes){
|
|
22
|
+
if (existing.name === name) {
|
|
23
|
+
nameOccurrence++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const pass = new RGPass(this._passes.length, name, nameOccurrence);
|
|
61
27
|
const builder = this._createBuilder(pass);
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
28
|
+
const initialResourceIds = new Set(this._resources.keys());
|
|
29
|
+
try {
|
|
30
|
+
const data = setup(builder);
|
|
31
|
+
pass.data = data;
|
|
32
|
+
this._passes.push(pass);
|
|
33
|
+
this._compiled = null;
|
|
34
|
+
return data;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
for (const id of this._resources.keys()){
|
|
37
|
+
if (!initialResourceIds.has(id)) {
|
|
38
|
+
this._resources.delete(id);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const resource of this._resources.values()){
|
|
42
|
+
for(let i = resource.consumers.length - 1; i >= 0; i--){
|
|
43
|
+
if (resource.consumers[i] === pass) {
|
|
44
|
+
resource.consumers.splice(i, 1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (resource.nextWriter === pass) {
|
|
48
|
+
resource.nextWriter = null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Remove WAR edges added to existing passes before setup failed.
|
|
52
|
+
for (const existing of this._passes){
|
|
53
|
+
for(let i = existing.warDependencies.length - 1; i >= 0; i--){
|
|
54
|
+
if (existing.warDependencies[i] === pass) {
|
|
55
|
+
existing.warDependencies.splice(i, 1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
this._lastWriterByPhysicalId.clear();
|
|
60
|
+
for (const resource of this._resources.values()){
|
|
61
|
+
if (resource.producer) {
|
|
62
|
+
this._lastWriterByPhysicalId.set(resource.physicalId, resource.producer);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
67
|
}
|
|
68
|
-
// ─── Compilation ────────────────────────────────────────────────────
|
|
69
68
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* Performs dead-pass culling, topological sorting, and resource lifetime analysis.
|
|
73
|
-
*
|
|
74
|
-
* @param outputs - Handles of resources that must be produced (graph sinks).
|
|
75
|
-
* If a resource was passed to {@link RGPassBuilder.write}, use the returned
|
|
76
|
-
* post-write handle here, not the original handle.
|
|
77
|
-
* Passes that do not contribute to these outputs (directly or transitively)
|
|
78
|
-
* are culled, unless marked as side-effect passes.
|
|
79
|
-
* @returns The compiled graph ready for execution.
|
|
69
|
+
* Cull dead passes, sort dependencies, and calculate resource lifetimes.
|
|
70
|
+
* Outputs must be their latest written versions.
|
|
80
71
|
*/ compile(outputs) {
|
|
81
72
|
this._validateOutputs(outputs);
|
|
82
|
-
// 1. Mark alive passes via backward traversal from outputs + side-effect passes
|
|
83
73
|
this._cullDeadPasses(outputs);
|
|
84
|
-
// 2. Topological sort of alive passes
|
|
85
74
|
const ordered = this._topologicalSort();
|
|
86
|
-
// 3. Resource lifetime analysis
|
|
87
75
|
const lifetimes = this._analyzeLifetimes(ordered);
|
|
76
|
+
this._validateAllocationKeys(lifetimes);
|
|
88
77
|
this._compiled = {
|
|
89
78
|
orderedPasses: ordered,
|
|
90
79
|
lifetimes
|
|
91
80
|
};
|
|
92
81
|
return this._compiled;
|
|
93
82
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Execute a compiled render graph (simple mode, no resource management).
|
|
96
|
-
*
|
|
97
|
-
* For automatic resource allocation/release, use {@link RenderGraphExecutor} instead.
|
|
98
|
-
*
|
|
99
|
-
* @param compiled - The compiled graph from {@link RenderGraph.compile}.
|
|
100
|
-
*/ execute(compiled) {
|
|
83
|
+
/** @internal Test-only callback runner; resource-managed code uses RenderGraphExecutor. */ execute(compiled) {
|
|
101
84
|
const noopCtx = {
|
|
102
85
|
getTexture () {
|
|
103
86
|
throw new Error('RenderGraph.execute(): resource resolution not available. Use RenderGraphExecutor for managed execution.');
|
|
@@ -108,9 +91,7 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
108
91
|
createFramebuffer () {
|
|
109
92
|
throw new Error('RenderGraph.execute(): framebuffer allocation not available. Use RenderGraphExecutor for managed execution.');
|
|
110
93
|
},
|
|
111
|
-
deferCleanup () {
|
|
112
|
-
// No-op in unmanaged execution mode.
|
|
113
|
-
}
|
|
94
|
+
deferCleanup () {}
|
|
114
95
|
};
|
|
115
96
|
for (const pass of compiled.orderedPasses){
|
|
116
97
|
if (pass.subpasses.length > 0) {
|
|
@@ -122,18 +103,13 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
122
103
|
}
|
|
123
104
|
}
|
|
124
105
|
}
|
|
125
|
-
/**
|
|
126
|
-
* Reset the graph for the next frame.
|
|
127
|
-
*
|
|
128
|
-
* Clears all passes, transient resources, and compiled state.
|
|
129
|
-
* Imported resources are also cleared — re-import them each frame.
|
|
130
|
-
*/ reset() {
|
|
106
|
+
/** Clear all passes, resources, and compiled state. */ reset() {
|
|
131
107
|
this._passes.length = 0;
|
|
132
108
|
this._resources.clear();
|
|
133
109
|
this._nextResourceId = 0;
|
|
134
110
|
this._compiled = null;
|
|
111
|
+
this._lastWriterByPhysicalId.clear();
|
|
135
112
|
}
|
|
136
|
-
// ─── Accessors (for testing / debugging) ────────────────────────────
|
|
137
113
|
/** @internal */ getResource(handle) {
|
|
138
114
|
return this._resources.get(handle._id);
|
|
139
115
|
}
|
|
@@ -143,27 +119,18 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
143
119
|
/** @internal */ get resources() {
|
|
144
120
|
return this._resources;
|
|
145
121
|
}
|
|
146
|
-
|
|
147
|
-
/** @internal */ _createBuilder(pass) {
|
|
122
|
+
_createBuilder(pass) {
|
|
148
123
|
const graph = this;
|
|
124
|
+
const textureLabelOccurrences = new Map();
|
|
149
125
|
return {
|
|
150
126
|
read (handle) {
|
|
151
127
|
const res = graph._resources.get(handle._id);
|
|
152
128
|
if (!res) {
|
|
153
129
|
throw new Error(`RenderGraph: unknown resource "${handle.name}" (id=${handle._id})`);
|
|
154
130
|
}
|
|
155
|
-
|
|
156
|
-
if (res.kind === 'transient' && !res.producer) {
|
|
157
|
-
throw new Error(`RenderGraph: pass "${pass.name}" attempts to read transient resource "${res.name}" ` + `which has no producer. Ensure the resource is created before being read.`);
|
|
158
|
-
}
|
|
159
|
-
if (!pass.reads.includes(res)) {
|
|
160
|
-
pass.reads.push(res);
|
|
161
|
-
}
|
|
162
|
-
if (!res.consumers.includes(pass)) {
|
|
163
|
-
res.consumers.push(pass);
|
|
164
|
-
}
|
|
131
|
+
graph._declareRead(pass, res);
|
|
165
132
|
},
|
|
166
|
-
write (handle) {
|
|
133
|
+
write (handle, options) {
|
|
167
134
|
const res = graph._resources.get(handle._id);
|
|
168
135
|
if (!res) {
|
|
169
136
|
throw new Error(`RenderGraph: unknown resource "${handle.name}" (id=${handle._id})`);
|
|
@@ -174,32 +141,62 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
174
141
|
if (res.kind === 'framebuffer') {
|
|
175
142
|
throw new Error(`RenderGraph: pass "${pass.name}" attempts to write framebuffer "${res.name}". ` + `Create a new framebuffer view instead.`);
|
|
176
143
|
}
|
|
144
|
+
const discard = options?.load === 'discard';
|
|
145
|
+
const previousWriter = graph._lastWriterByPhysicalId.get(res.physicalId);
|
|
146
|
+
if (previousWriter && previousWriter !== pass) {
|
|
147
|
+
const dependencies = discard ? pass.orderingDependencies : pass.dependencies;
|
|
148
|
+
if (!dependencies.includes(previousWriter)) {
|
|
149
|
+
dependencies.push(previousWriter);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// WAR orders live readers before the overwrite without retaining them.
|
|
177
153
|
for (const consumer of res.consumers){
|
|
178
|
-
if (consumer !== pass && !pass.
|
|
179
|
-
pass.
|
|
154
|
+
if (consumer !== pass && !pass.warDependencies.includes(consumer)) {
|
|
155
|
+
pass.warDependencies.push(consumer);
|
|
180
156
|
}
|
|
181
157
|
}
|
|
182
|
-
if (res.producer && res.producer !== pass
|
|
183
|
-
pass.dependencies
|
|
158
|
+
if (res.producer && res.producer !== pass) {
|
|
159
|
+
const dependencies = discard ? pass.orderingDependencies : pass.dependencies;
|
|
160
|
+
if (!dependencies.includes(res.producer)) {
|
|
161
|
+
dependencies.push(res.producer);
|
|
162
|
+
}
|
|
184
163
|
}
|
|
185
164
|
const id = graph._nextResourceId++;
|
|
186
165
|
const versionName = `${res.name}@${pass.name}`;
|
|
187
|
-
const version = new RGResource(id, versionName, res.kind, res.desc, res.physicalId);
|
|
166
|
+
const version = new RGResource(id, versionName, res.kind, res.desc, res.physicalId, res.allocationKey);
|
|
188
167
|
version.producer = pass;
|
|
168
|
+
// WAW edges make the first overwrite sufficient for stale readers.
|
|
169
|
+
if (!res.nextWriter) {
|
|
170
|
+
res.nextWriter = pass;
|
|
171
|
+
}
|
|
189
172
|
graph._resources.set(id, version);
|
|
173
|
+
graph._lastWriterByPhysicalId.set(res.physicalId, pass);
|
|
190
174
|
if (!pass.writes.includes(version)) {
|
|
191
175
|
pass.writes.push(version);
|
|
192
176
|
}
|
|
193
|
-
return new RGHandle(id, versionName);
|
|
177
|
+
return new RGHandle(id, versionName, 'texture');
|
|
194
178
|
},
|
|
195
179
|
createTexture (desc) {
|
|
180
|
+
if (desc.allocationKey !== undefined && desc.allocationKey.length === 0) {
|
|
181
|
+
throw new Error(`RenderGraph: pass "${pass.name}" specified an empty texture allocationKey.`);
|
|
182
|
+
}
|
|
196
183
|
const id = graph._nextResourceId++;
|
|
197
184
|
const name = desc.label ?? `_tex_${id}`;
|
|
198
|
-
const
|
|
185
|
+
const labelKey = desc.label ?? '<unnamed>';
|
|
186
|
+
const labelOccurrence = textureLabelOccurrences.get(labelKey) ?? 0;
|
|
187
|
+
textureLabelOccurrences.set(labelKey, labelOccurrence + 1);
|
|
188
|
+
const allocationKey = desc.allocationKey ?? `rg:${JSON.stringify([
|
|
189
|
+
pass.name,
|
|
190
|
+
pass.nameOccurrence,
|
|
191
|
+
desc.label ?? null,
|
|
192
|
+
labelOccurrence
|
|
193
|
+
])}`;
|
|
194
|
+
const res = new RGResource(id, name, 'transient', desc, id, allocationKey);
|
|
199
195
|
res.producer = pass;
|
|
200
196
|
graph._resources.set(id, res);
|
|
197
|
+
graph._lastWriterByPhysicalId.set(res.physicalId, pass);
|
|
201
198
|
pass.writes.push(res);
|
|
202
|
-
return new RGHandle(id, name);
|
|
199
|
+
return new RGHandle(id, name, 'texture');
|
|
203
200
|
},
|
|
204
201
|
createToken (name) {
|
|
205
202
|
const id = graph._nextResourceId++;
|
|
@@ -208,7 +205,7 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
208
205
|
res.producer = pass;
|
|
209
206
|
graph._resources.set(id, res);
|
|
210
207
|
pass.writes.push(res);
|
|
211
|
-
return new RGHandle(id, tokenName);
|
|
208
|
+
return new RGHandle(id, tokenName, 'token');
|
|
212
209
|
},
|
|
213
210
|
createFramebuffer (desc) {
|
|
214
211
|
const id = graph._nextResourceId++;
|
|
@@ -216,9 +213,10 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
216
213
|
const res = new RGResource(id, name, 'framebuffer', desc);
|
|
217
214
|
res.producer = pass;
|
|
218
215
|
graph._resources.set(id, res);
|
|
216
|
+
graph._lastWriterByPhysicalId.set(res.physicalId, pass);
|
|
219
217
|
pass.writes.push(res);
|
|
220
218
|
graph._declareFramebufferAttachmentDeps(pass, desc);
|
|
221
|
-
return new RGHandle(id, name);
|
|
219
|
+
return new RGHandle(id, name, 'framebuffer');
|
|
222
220
|
},
|
|
223
221
|
sideEffect () {
|
|
224
222
|
pass.hasSideEffect = true;
|
|
@@ -237,8 +235,21 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
237
235
|
}
|
|
238
236
|
};
|
|
239
237
|
}
|
|
240
|
-
|
|
241
|
-
|
|
238
|
+
_validateAllocationKeys(lifetimes) {
|
|
239
|
+
const physicalByKey = new Map();
|
|
240
|
+
for (const lifetime of lifetimes.values()){
|
|
241
|
+
const resource = lifetime.resource;
|
|
242
|
+
if (resource.kind !== 'transient' || !resource.allocationKey) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const existingPhysicalId = physicalByKey.get(resource.allocationKey);
|
|
246
|
+
if (existingPhysicalId !== undefined && existingPhysicalId !== resource.physicalId) {
|
|
247
|
+
throw new Error(`RenderGraph: transient allocationKey "${resource.allocationKey}" is used by multiple resources. ` + 'Use a unique allocationKey for each logical texture.');
|
|
248
|
+
}
|
|
249
|
+
physicalByKey.set(resource.allocationKey, resource.physicalId);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
_validateOutputs(outputs) {
|
|
242
253
|
const latestVersions = new Map();
|
|
243
254
|
for (const res of this._resources.values()){
|
|
244
255
|
const current = latestVersions.get(res.physicalId);
|
|
@@ -257,12 +268,10 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
257
268
|
}
|
|
258
269
|
}
|
|
259
270
|
}
|
|
260
|
-
|
|
261
|
-
// Start with all passes marked dead
|
|
271
|
+
_cullDeadPasses(outputs) {
|
|
262
272
|
for (const pass of this._passes){
|
|
263
273
|
pass.alive = false;
|
|
264
274
|
}
|
|
265
|
-
// Seed: resources that are requested outputs
|
|
266
275
|
const neededResources = new Set();
|
|
267
276
|
const stack = [];
|
|
268
277
|
const markResourceNeeded = (res)=>{
|
|
@@ -279,6 +288,7 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
279
288
|
for (const dep of pass.reads){
|
|
280
289
|
markResourceNeeded(dep);
|
|
281
290
|
}
|
|
291
|
+
// WAR dependencies order passes but do not propagate liveness.
|
|
282
292
|
for (const dependency of pass.dependencies){
|
|
283
293
|
markPassAlive(dependency);
|
|
284
294
|
}
|
|
@@ -290,14 +300,11 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
290
300
|
}
|
|
291
301
|
markResourceNeeded(res);
|
|
292
302
|
}
|
|
293
|
-
// Seed: side-effect passes (always alive) — push their read dependencies
|
|
294
303
|
for (const pass of this._passes){
|
|
295
304
|
if (pass.hasSideEffect) {
|
|
296
305
|
markPassAlive(pass);
|
|
297
306
|
}
|
|
298
307
|
}
|
|
299
|
-
// Backward traversal: for each needed resource, mark its producer alive
|
|
300
|
-
// and recursively mark the producer's read dependencies as needed
|
|
301
308
|
while(stack.length > 0){
|
|
302
309
|
const res = stack.pop();
|
|
303
310
|
const producer = res.producer;
|
|
@@ -305,8 +312,30 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
305
312
|
markPassAlive(producer);
|
|
306
313
|
}
|
|
307
314
|
}
|
|
315
|
+
// Warn when an ordering-only pass may have omitted sideEffect or an output.
|
|
316
|
+
for (const pass of this._passes){
|
|
317
|
+
if (!pass.alive && !pass.hasSideEffect && pass.warDependencies.length > 0 && !RenderGraph._warnedCulledPasses.has(pass.name)) {
|
|
318
|
+
RenderGraph._warnedCulledPasses.add(pass.name);
|
|
319
|
+
console.warn(`RenderGraph: pass "${pass.name}" was culled but is referenced by WAR ` + `ordering edges. If its work must run, call sideEffect() or route its ` + `output into a compile() sink.`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
308
322
|
}
|
|
309
|
-
|
|
323
|
+
_declareRead(pass, res) {
|
|
324
|
+
if (res.kind === 'transient' && !res.producer) {
|
|
325
|
+
throw new Error(`RenderGraph: pass "${pass.name}" attempts to read transient resource "${res.name}" ` + `which has no producer. Ensure the resource is created before being read.`);
|
|
326
|
+
}
|
|
327
|
+
if (!pass.reads.includes(res)) {
|
|
328
|
+
pass.reads.push(res);
|
|
329
|
+
}
|
|
330
|
+
if (!res.consumers.includes(pass)) {
|
|
331
|
+
res.consumers.push(pass);
|
|
332
|
+
}
|
|
333
|
+
// A stale-version read must precede its first overwrite.
|
|
334
|
+
if (res.nextWriter && res.nextWriter !== pass && !res.nextWriter.warDependencies.includes(pass)) {
|
|
335
|
+
res.nextWriter.warDependencies.push(pass);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
_declareFramebufferAttachmentDeps(pass, desc) {
|
|
310
339
|
const declare = (attachment)=>{
|
|
311
340
|
if (attachment instanceof RGHandle) {
|
|
312
341
|
const res = this._resources.get(attachment._id);
|
|
@@ -317,12 +346,7 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
317
346
|
throw new Error(`RenderGraph: framebuffer attachment "${res.name}" must be a texture resource, got ${res.kind}.`);
|
|
318
347
|
}
|
|
319
348
|
if (res.producer !== pass) {
|
|
320
|
-
|
|
321
|
-
pass.reads.push(res);
|
|
322
|
-
}
|
|
323
|
-
if (!res.consumers.includes(pass)) {
|
|
324
|
-
res.consumers.push(pass);
|
|
325
|
-
}
|
|
349
|
+
this._declareRead(pass, res);
|
|
326
350
|
}
|
|
327
351
|
}
|
|
328
352
|
};
|
|
@@ -334,13 +358,11 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
334
358
|
}
|
|
335
359
|
declare(desc.depthAttachment);
|
|
336
360
|
}
|
|
337
|
-
|
|
338
|
-
/** @internal */ _topologicalSort() {
|
|
361
|
+
_topologicalSort() {
|
|
339
362
|
const alivePasses = this._passes.filter((p)=>p.alive);
|
|
340
363
|
if (alivePasses.length === 0) {
|
|
341
364
|
return [];
|
|
342
365
|
}
|
|
343
|
-
// Build adjacency: producer -> consumers (only among alive passes)
|
|
344
366
|
const aliveSet = new Set(alivePasses);
|
|
345
367
|
const inDegree = new Map();
|
|
346
368
|
const adjacency = new Map();
|
|
@@ -362,8 +384,13 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
362
384
|
for (const dependency of pass.dependencies){
|
|
363
385
|
addEdge(dependency, pass);
|
|
364
386
|
}
|
|
387
|
+
for (const dependency of pass.orderingDependencies){
|
|
388
|
+
addEdge(dependency, pass);
|
|
389
|
+
}
|
|
390
|
+
for (const dependency of pass.warDependencies){
|
|
391
|
+
addEdge(dependency, pass);
|
|
392
|
+
}
|
|
365
393
|
}
|
|
366
|
-
// For each resource, its producer has an edge to each of its consumers
|
|
367
394
|
for (const res of this._resources.values()){
|
|
368
395
|
if (!res.producer || !aliveSet.has(res.producer)) {
|
|
369
396
|
continue;
|
|
@@ -372,7 +399,6 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
372
399
|
addEdge(res.producer, consumer);
|
|
373
400
|
}
|
|
374
401
|
}
|
|
375
|
-
// Kahn's algorithm
|
|
376
402
|
const queue = [];
|
|
377
403
|
for (const pass of alivePasses){
|
|
378
404
|
if (inDegree.get(pass) === 0) {
|
|
@@ -392,7 +418,6 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
392
418
|
}
|
|
393
419
|
}
|
|
394
420
|
if (result.length !== alivePasses.length) {
|
|
395
|
-
// Find passes that are part of the cycle (those with non-zero in-degree)
|
|
396
421
|
const cycleParticipants = [];
|
|
397
422
|
for (const [pass, degree] of inDegree){
|
|
398
423
|
if (degree > 0) {
|
|
@@ -403,10 +428,8 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
403
428
|
}
|
|
404
429
|
return result;
|
|
405
430
|
}
|
|
406
|
-
|
|
407
|
-
/** @internal */ _analyzeLifetimes(orderedPasses) {
|
|
431
|
+
_analyzeLifetimes(orderedPasses) {
|
|
408
432
|
const lifetimes = new Map();
|
|
409
|
-
// Build pass -> order index map
|
|
410
433
|
const orderMap = new Map();
|
|
411
434
|
for(let i = 0; i < orderedPasses.length; i++){
|
|
412
435
|
orderMap.set(orderedPasses[i], i);
|
|
@@ -414,13 +437,11 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
414
437
|
for (const res of this._resources.values()){
|
|
415
438
|
let first = Infinity;
|
|
416
439
|
let last = -Infinity;
|
|
417
|
-
// Producer
|
|
418
440
|
if (res.producer && orderMap.has(res.producer)) {
|
|
419
441
|
const idx = orderMap.get(res.producer);
|
|
420
442
|
first = Math.min(first, idx);
|
|
421
443
|
last = Math.max(last, idx);
|
|
422
444
|
}
|
|
423
|
-
// Consumers
|
|
424
445
|
for (const consumer of res.consumers){
|
|
425
446
|
if (orderMap.has(consumer)) {
|
|
426
447
|
const idx = orderMap.get(consumer);
|
|
@@ -436,11 +457,7 @@ import { RGHandle, RGPass, RGResource, RGSubpass } from './types.js';
|
|
|
436
457
|
});
|
|
437
458
|
}
|
|
438
459
|
}
|
|
439
|
-
//
|
|
440
|
-
// extend each RGHandle attachment's lifetime to cover the framebuffer's
|
|
441
|
-
// lifetime so the executor never releases a backing texture while a later
|
|
442
|
-
// pass can still render through the framebuffer. Framebuffers cannot be
|
|
443
|
-
// attachments of other framebuffers, so a single propagation pass suffices.
|
|
460
|
+
// Attachment textures must outlive every framebuffer use.
|
|
444
461
|
for (const lifetime of lifetimes.values()){
|
|
445
462
|
const res = lifetime.resource;
|
|
446
463
|
if (res.kind !== 'framebuffer' || !res.desc) {
|