1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
use std::{cell::RefCell, sync::Arc};
use crate::{
graph::{
DataHandle, GraphResource, PassthroughDataContainer, PassthroughDataRef, PassthroughDataRefMut, ReadyData,
RenderGraph, RenderGraphDataStore, RenderGraphEncoderOrPass, RenderPassHandle, RenderPassTargets,
RenderTargetHandle, RpassTemporaryPool, ShadowArrayHandle, ShadowTargetHandle,
},
util::typedefs::SsoString,
Renderer,
};
/// Wraps a handle proving you have declared it as a dependency.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct DeclaredDependency<Handle> {
pub(super) handle: Handle,
}
#[allow(clippy::type_complexity)]
pub(super) struct RenderGraphNode<'node> {
pub inputs: Vec<GraphResource>,
pub outputs: Vec<GraphResource>,
pub label: SsoString,
pub rpass: Option<RenderPassTargets>,
pub passthrough: PassthroughDataContainer<'node>,
pub exec: Box<
dyn for<'b, 'pass> FnOnce(
&mut PassthroughDataContainer<'pass>,
&Arc<Renderer>,
RenderGraphEncoderOrPass<'b, 'pass>,
&'pass RpassTemporaryPool<'pass>,
&'pass ReadyData,
RenderGraphDataStore<'pass>,
) + 'node,
>,
}
/// Builder for a graph node.
///
/// Calling build will automatically add the node to the rendergraph.
pub struct RenderGraphNodeBuilder<'a, 'node> {
pub(super) graph: &'a mut RenderGraph<'node>,
pub(super) label: SsoString,
pub(super) inputs: Vec<GraphResource>,
pub(super) outputs: Vec<GraphResource>,
pub(super) passthrough: PassthroughDataContainer<'node>,
pub(super) rpass: Option<RenderPassTargets>,
}
impl<'a, 'node> RenderGraphNodeBuilder<'a, 'node> {
/// Declares a rendertarget to be read from but not writen to.
pub fn add_render_target_input(&mut self, handle: RenderTargetHandle) -> DeclaredDependency<RenderTargetHandle> {
self.inputs.push(handle.resource);
DeclaredDependency { handle }
}
/// Declares a rendertarget to be read or written to.
pub fn add_render_target_output(&mut self, handle: RenderTargetHandle) -> DeclaredDependency<RenderTargetHandle> {
self.inputs.push(handle.resource);
self.outputs.push(handle.resource);
DeclaredDependency { handle }
}
/// Sugar over [add_render_target_output][arto] which makes it easy to
/// declare resolve textures, which are often Option<RenderTargetHandle>
///
/// [arto]: RenderGraphNodeBuilder::add_render_target_output
pub fn add_optional_render_target_output(
&mut self,
handle: Option<RenderTargetHandle>,
) -> Option<DeclaredDependency<RenderTargetHandle>> {
Some(self.add_render_target_output(handle?))
}
/// Declares a renderpass that will be written to. Declaring a renderpass
/// will prevent access to an encoder in the node.
pub fn add_renderpass(&mut self, targets: RenderPassTargets) -> DeclaredDependency<RenderPassHandle> {
assert!(
self.rpass.is_none(),
"Cannot have more than one graph-associated renderpass per node."
);
self.rpass = Some(targets);
DeclaredDependency {
handle: RenderPassHandle,
}
}
/// Declares use of the entire shadow atlas for reading.
pub fn add_shadow_array_input(&mut self) -> DeclaredDependency<ShadowArrayHandle> {
for i in &self.graph.shadows {
let resource = GraphResource::Shadow(*i);
self.inputs.push(resource);
}
DeclaredDependency {
handle: ShadowArrayHandle,
}
}
/// Declares use of a particular shadow map for both reading and writing.
pub fn add_shadow_output(&mut self, idx: usize) -> DeclaredDependency<ShadowTargetHandle> {
let resource = GraphResource::Shadow(idx);
self.graph.shadows.insert(idx);
self.inputs.push(resource);
self.outputs.push(resource);
DeclaredDependency {
handle: ShadowTargetHandle { idx },
}
}
/// Declares use of a data handle for reading.
pub fn add_data_input<T>(&mut self, handle: DataHandle<T>) -> DeclaredDependency<DataHandle<T>>
where
T: 'static,
{
self.add_data(handle, false)
}
/// Declares use of a data handle for reading and writing.
pub fn add_data_output<T>(&mut self, handle: DataHandle<T>) -> DeclaredDependency<DataHandle<T>>
where
T: 'static,
{
self.add_data(handle, true)
}
fn add_data<T>(&mut self, handle: DataHandle<T>, output: bool) -> DeclaredDependency<DataHandle<T>>
where
T: 'static,
{
// TODO: error handling
// TODO: move this validation to all types
self.graph
.data
.get(handle.idx)
.expect("internal rendergraph error: cannot find data handle")
.downcast_ref::<RefCell<Option<T>>>()
.expect("used custom data that was previously declared with a different type");
self.inputs.push(GraphResource::Data(handle.idx));
if output {
self.outputs.push(GraphResource::Data(handle.idx));
}
DeclaredDependency { handle }
}
/// Declares that this node has an "external" output, meaning it can never
/// be culled.
pub fn add_external_output(&mut self) {
self.inputs.push(GraphResource::External);
self.outputs.push(GraphResource::External);
}
/// Passthrough a bit of immutable external data with lifetime 'node so you
/// can receieve it inside with lifetime 'rpass.
///
/// Use [PassthroughDataContainer::get][g] to get the value on the inside.
///
/// [g]: super::PassthroughDataContainer::get
pub fn passthrough_ref<T: 'node>(&mut self, data: &'node T) -> PassthroughDataRef<T> {
self.passthrough.add_ref(data)
}
/// Passthrough a bit of mutable external data with lifetime 'node so you
/// can receieve it inside with lifetime 'rpass.
///
/// Use [PassthroughDataContainer::get_mut][g] to get the value on the
/// inside.
///
/// [g]: super::PassthroughDataContainer::get_mut
pub fn passthrough_ref_mut<T: 'node>(&mut self, data: &'node mut T) -> PassthroughDataRefMut<T> {
self.passthrough.add_ref_mut(data)
}
/// Builds the rendergraph node and adds it into the rendergraph.
///
/// Takes a function that is the body of the node. Nodes will only run if a
/// following node consumes the output. See module level docs for more
/// details.
///
/// The function takes the following arguments (which I will give names):
/// - `pt`: a container which you can get all the passthrough data out of
/// - `renderer`: a reference to the renderer.
/// - `encoder_or_pass`: either the asked-for renderpass, or a command
/// encoder.
/// - `temps`: storage for temporary data that lasts the length of the
/// renderpass.
/// - `ready`: result from calling ready on various managers.
/// - `graph_data`: read-only access to various managers and access to
/// in-graph data.
pub fn build<F>(self, exec: F)
where
F: for<'b, 'pass> FnOnce(
&mut PassthroughDataContainer<'pass>,
&Arc<Renderer>,
RenderGraphEncoderOrPass<'b, 'pass>,
&'pass RpassTemporaryPool<'pass>,
&'pass ReadyData,
RenderGraphDataStore<'pass>,
) + 'node,
{
self.graph.nodes.push(RenderGraphNode {
label: self.label,
inputs: self.inputs,
outputs: self.outputs,
rpass: self.rpass,
passthrough: self.passthrough,
exec: Box::new(exec),
});
}
}