use crate::camera::Camera;
use crate::gpu::GpuContext;
use crate::render_graph::{RenderContext, RenderNode, RenderTarget};
pub struct RenderGraphBuilder {
nodes: Vec<Box<dyn RenderNode>>,
}
impl RenderGraphBuilder {
pub fn new() -> Self {
Self { nodes: Vec::new() }
}
pub fn node<N: RenderNode + 'static>(mut self, node: N) -> Self {
self.nodes.push(Box::new(node));
self
}
pub fn build(self, gpu: &GpuContext) -> RenderGraph {
let target_a = RenderTarget::new(gpu, "RenderGraph Target A");
let target_b = RenderTarget::new(gpu, "RenderGraph Target B");
RenderGraph {
nodes: self.nodes,
target_a,
target_b,
}
}
}
impl Default for RenderGraphBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct RenderGraph {
nodes: Vec<Box<dyn RenderNode>>,
target_a: RenderTarget,
target_b: RenderTarget,
}
impl RenderGraph {
pub fn builder() -> RenderGraphBuilder {
RenderGraphBuilder::new()
}
pub fn with_node<N: RenderNode + 'static>(mut self, node: N, gpu: &GpuContext) -> Self {
self.nodes.push(Box::new(node));
self.target_a.ensure_size(gpu, "RenderGraph Target A");
self.target_b.ensure_size(gpu, "RenderGraph Target B");
self
}
pub fn execute(&mut self, gpu: &GpuContext, time: f32, camera: &Camera) {
self.execute_with_ui(gpu, time, camera, |_, _| {});
}
pub fn check_hot_reload(&mut self, gpu: &GpuContext) {
for node in &mut self.nodes {
node.check_hot_reload(gpu);
}
}
pub fn execute_with_ui<F>(&mut self, gpu: &GpuContext, time: f32, camera: &Camera, ui_fn: F)
where
F: FnOnce(&GpuContext, &mut wgpu::RenderPass),
{
self.check_hot_reload(gpu);
self.target_a.ensure_size(gpu, "RenderGraph Target A");
self.target_b.ensure_size(gpu, "RenderGraph Target B");
let output = gpu.surface.get_current_texture().unwrap();
let screen_view = output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = gpu
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("RenderGraph Encoder"),
});
let node_count = self.nodes.len();
{
let mut ctx = RenderContext {
gpu,
encoder: &mut encoder,
time,
camera,
};
if node_count == 1 {
self.nodes[0].execute(&mut ctx, &screen_view, None);
} else {
let mut current_input: Option<&wgpu::TextureView> = None;
for (i, node) in self.nodes.iter().enumerate() {
let is_last = i == node_count - 1;
let target = if is_last {
&screen_view
} else if i % 2 == 0 {
&self.target_a.view
} else {
&self.target_b.view
};
node.execute(&mut ctx, target, current_input);
if !is_last {
current_input = Some(if i % 2 == 0 {
&self.target_a.view
} else {
&self.target_b.view
});
}
}
}
}
{
let mut ui_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("UI Overlay Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &screen_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
ui_fn(gpu, &mut ui_pass);
}
gpu.queue.submit(std::iter::once(encoder.finish()));
output.present();
}
pub fn execute_to_target(
&mut self,
gpu: &GpuContext,
time: f32,
camera: &Camera,
target: &wgpu::TextureView,
) {
self.check_hot_reload(gpu);
self.target_a.ensure_size(gpu, "RenderGraph Target A");
self.target_b.ensure_size(gpu, "RenderGraph Target B");
let mut encoder = gpu
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("RenderGraph To Target Encoder"),
});
let node_count = self.nodes.len();
{
let mut ctx = RenderContext {
gpu,
encoder: &mut encoder,
time,
camera,
};
if node_count == 1 {
self.nodes[0].execute(&mut ctx, target, None);
} else {
let mut current_input: Option<&wgpu::TextureView> = None;
for (i, node) in self.nodes.iter().enumerate() {
let is_last = i == node_count - 1;
let node_target = if is_last {
target
} else if i % 2 == 0 {
&self.target_a.view
} else {
&self.target_b.view
};
node.execute(&mut ctx, node_target, current_input);
if !is_last {
current_input = Some(if i % 2 == 0 {
&self.target_a.view
} else {
&self.target_b.view
});
}
}
}
}
gpu.queue.submit(std::iter::once(encoder.finish()));
}
}