render: move render targets to be graph slots, create present passes and base passes
ci/woodpecker/pr/debug Pipeline failed Details
ci/woodpecker/pr/release Pipeline failed Details

Since the render graph no longer has default slots, base passes must be created that supply things like render targets. This also makes it easier to render offscreen to some other surface that is not the window, or just some other texture
This commit is contained in:
SeanOMik 2024-05-19 12:56:03 -04:00
parent 8c3446389c
commit fc57777a45
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
9 changed files with 256 additions and 117 deletions

18
.vscode/launch.json vendored
View File

@ -22,6 +22,24 @@
"args": [], "args": [],
"cwd": "${workspaceFolder}/examples/testbed" "cwd": "${workspaceFolder}/examples/testbed"
}, },
{
"type": "lldb",
"request": "launch",
"name": "Debug example simple_scene",
"cargo": {
"args": [
"build",
"--manifest-path", "${workspaceFolder}/examples/simple_scene/Cargo.toml"
//"--bin=testbed",
],
"filter": {
"name": "simple_scene",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}/examples/simple_scene"
},
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",

View File

@ -11,9 +11,11 @@ pub struct GraphExecutionPath {
} }
impl GraphExecutionPath { impl GraphExecutionPath {
pub fn new(built_in_slots: FxHashSet<u64>, pass_descriptions: Vec<&RenderGraphPassDesc>) -> Self { pub fn new(pass_descriptions: Vec<&RenderGraphPassDesc>) -> Self {
// collect all the output slots // collect all the output slots
let mut total_outputs = HashMap::new(); let mut total_outputs = HashMap::new();
total_outputs.reserve(pass_descriptions.len());
for desc in pass_descriptions.iter() { for desc in pass_descriptions.iter() {
for slot in desc.output_slots() { for slot in desc.output_slots() {
total_outputs.insert(slot.name.clone(), SlotOwnerPair { total_outputs.insert(slot.name.clone(), SlotOwnerPair {
@ -28,9 +30,6 @@ impl GraphExecutionPath {
// find the node inputs // find the node inputs
let mut inputs = vec![]; let mut inputs = vec![];
for slot in desc.input_slots() { for slot in desc.input_slots() {
// If the slot is built in to the graph, no need to care about the sorting.
if built_in_slots.contains(&slot.id) { continue; }
let inp = total_outputs.get(&slot.name) let inp = total_outputs.get(&slot.name)
.expect(&format!("failed to find slot: '{}', ensure that there is a pass outputting it", slot.name)); .expect(&format!("failed to find slot: '{}', ensure that there is a pass outputting it", slot.name));
inputs.push(*inp); inputs.push(*inp);

View File

@ -17,8 +17,8 @@ pub use slot_desc::*;
mod execution_path; mod execution_path;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::FxHashMap;
use tracing::{debug_span, instrument, trace}; use tracing::{debug_span, instrument, trace, warn};
use self::execution_path::GraphExecutionPath; use self::execution_path::GraphExecutionPath;
@ -38,6 +38,7 @@ pub struct BindGroupEntry {
pub layout: Option<Rc<wgpu::BindGroupLayout>>, pub layout: Option<Rc<wgpu::BindGroupLayout>>,
} }
#[allow(dead_code)]
struct ResourcedSlot { struct ResourcedSlot {
name: String, name: String,
//slot: RenderPassSlot, //slot: RenderPassSlot,
@ -54,6 +55,13 @@ pub struct PipelineResource {
pub bg_layout_name_lookup: HashMap<String, u32>, pub bg_layout_name_lookup: HashMap<String, u32>,
} }
#[derive(Debug)]
pub struct RenderTarget {
pub surface: wgpu::Surface,
pub surface_config: wgpu::SurfaceConfiguration,
pub current_texture: Option<wgpu::SurfaceTexture>,
}
pub struct RenderGraph { pub struct RenderGraph {
device: Rc<wgpu::Device>, device: Rc<wgpu::Device>,
queue: Rc<wgpu::Queue>, queue: Rc<wgpu::Queue>,
@ -69,42 +77,21 @@ pub struct RenderGraph {
pipelines: FxHashMap<u64, PipelineResource>, pipelines: FxHashMap<u64, PipelineResource>,
current_id: u64, current_id: u64,
exec_path: Option<GraphExecutionPath>, exec_path: Option<GraphExecutionPath>,
pub(crate) surface_config: wgpu::SurfaceConfiguration,
} }
impl RenderGraph { impl RenderGraph {
pub fn new( pub fn new(device: Rc<wgpu::Device>, queue: Rc<wgpu::Queue>) -> Self {
device: Rc<wgpu::Device>,
queue: Rc<wgpu::Queue>,
surface_config: wgpu::SurfaceConfiguration,
) -> Self {
let mut slots = FxHashMap::default();
let mut slot_names = HashMap::default();
slots.insert(
0,
ResourcedSlot {
name: "window_texture_view".to_string(),
ty: SlotType::TextureView,
// this will get set in prepare stage.
value: SlotValue::None,
},
);
slot_names.insert("window_texture_view".to_string(), 0u64);
Self { Self {
device, device,
queue, queue,
slots, slots: Default::default(),
slot_names, slot_names: Default::default(),
passes: Default::default(), passes: Default::default(),
bind_groups: Default::default(), bind_groups: Default::default(),
bind_group_names: Default::default(), bind_group_names: Default::default(),
pipelines: Default::default(), pipelines: Default::default(),
current_id: 1, current_id: 1,
exec_path: None, exec_path: None,
surface_config,
} }
} }
@ -138,6 +125,11 @@ impl RenderGraph {
slot.name, desc.name slot.name, desc.name
); );
trace!(
"Found existing slot for {}, changing id to {}",
slot.name, id
);
// if there is a slot of the same name // if there is a slot of the same name
slot.id = *id; slot.id = *id;
} else { } else {
@ -229,61 +221,61 @@ impl RenderGraph {
} }
// create the execution path for the graph. This will be executed in `RenderGraph::render` // create the execution path for the graph. This will be executed in `RenderGraph::render`
let builtin = {
let mut h = FxHashSet::default();
h.insert(0u64); // include the base pass
h
};
let descs = self.passes.values().map(|p| &*p.desc).collect(); let descs = self.passes.values().map(|p| &*p.desc).collect();
let path = GraphExecutionPath::new(builtin, descs); let path = GraphExecutionPath::new(descs);
trace!( trace!(
"Found {} steps in the rendergraph to execute", "Found {} steps in the rendergraph to execute",
path.queue.len() path.queue.len()
); );
self.exec_path = Some(path); self.exec_path = Some(path);
} }
#[instrument(skip(self, surface))] #[instrument(skip(self))]
pub fn render(&mut self, surface: &wgpu::Surface) { pub fn render(&mut self) {
let mut path = self.exec_path.take().unwrap(); let mut path = self.exec_path.take().unwrap();
let output = surface.get_current_texture().unwrap(); let mut encoders = Vec::with_capacity(self.passes.len() / 2);
let view = output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
// update the window texture view slot.
let window_tv_slot = self.slots.get_mut(&0).unwrap(); // 0 is window_texture_view
debug_assert_eq!(
window_tv_slot.name.as_str(),
"window_texture_view",
"unexpected slot where 'window_texture_view' should be"
);
window_tv_slot.value = SlotValue::TextureView(Rc::new(view));
let mut encoders = vec![];
while let Some(pass_id) = path.queue.pop_front() { while let Some(pass_id) = path.queue.pop_front() {
let pass = self.passes.get(&pass_id).unwrap(); let pass = self.passes.get(&pass_id).unwrap();
let pass_inn = pass.inner.clone(); let pass_inn = pass.inner.clone();
let pass_desc = pass.desc.clone(); let pass_desc = pass.desc.clone();
let label = format!("{} Encoder", pass_desc.name); let label = format!("{} Encoder", pass_desc.name);
let encoder = self // encoders are not needed for presenter nodes.
.device let encoder = if pass_desc.pass_type == RenderPassType::Presenter {
None
} else {
Some(
self.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { .create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some(&label), label: Some(&label),
}); }),
)
};
let queue = self.queue.clone(); // clone is required to appease the borrow checker let queue = self.queue.clone(); // clone is required to appease the borrow checker
let mut context = RenderGraphContext::new(&queue, Some(encoder)); let mut context = RenderGraphContext::new(&queue, encoder);
// all encoders need to be submitted before a presenter node is executed.
if pass_desc.pass_type == RenderPassType::Presenter {
self.queue.submit(encoders.drain(..));
}
let mut inner = pass_inn.borrow_mut(); let mut inner = pass_inn.borrow_mut();
inner.execute(self, &*pass_desc, &mut context); inner.execute(self, &*pass_desc, &mut context);
encoders.push(context.encoder.unwrap().finish()); if let Some(encoder) = context.encoder {
encoders.push(encoder.finish());
}
} }
if !encoders.is_empty() {
warn!("{} encoders were not submitted in the same render cycle they were created. \
Make sure there is a presenting pass at the end. You may still see something, \
however it will be delayed a render cycle.", encoders.len());
self.queue.submit(encoders.into_iter()); self.queue.submit(encoders.into_iter());
output.present(); }
} }
pub fn slot_value(&self, id: u64) -> Option<&SlotValue> { pub fn slot_value(&self, id: u64) -> Option<&SlotValue> {

View File

@ -1,16 +1,17 @@
use std::{collections::HashMap, num::NonZeroU32, rc::Rc}; use std::{cell::{Ref, RefCell, RefMut}, collections::HashMap, num::NonZeroU32, rc::Rc};
use lyra_ecs::World; use lyra_ecs::World;
use crate::render::resource::RenderPipelineDescriptor; use crate::render::resource::RenderPipelineDescriptor;
use super::{RenderGraph, RenderGraphContext}; use super::{RenderGraph, RenderGraphContext, RenderTarget};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum RenderPassType { pub enum RenderPassType {
Compute, Compute,
#[default] #[default]
Render, Render,
Presenter,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@ -19,15 +20,19 @@ pub enum SlotType {
Sampler, Sampler,
Texture, Texture,
Buffer, Buffer,
RenderTarget,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum SlotValue { pub enum SlotValue {
None, None,
/// The value will be set during a later phase of the render graph.
Lazy,
TextureView(Rc<wgpu::TextureView>), TextureView(Rc<wgpu::TextureView>),
Sampler(Rc<wgpu::Sampler>), Sampler(Rc<wgpu::Sampler>),
Texture(Rc<wgpu::Texture>), Texture(Rc<wgpu::Texture>),
Buffer(Rc<wgpu::Buffer>), Buffer(Rc<wgpu::Buffer>),
RenderTarget(Rc<RefCell<RenderTarget>>),
} }
impl SlotValue { impl SlotValue {
@ -52,6 +57,20 @@ impl SlotValue {
_ => None, _ => None,
} }
} }
pub fn as_render_target(&self) -> Option<Ref<RenderTarget>> {
match self {
Self::RenderTarget(v) => Some(v.borrow()),
_ => None,
}
}
pub fn as_render_target_mut(&mut self) -> Option<RefMut<RenderTarget>> {
match self {
Self::RenderTarget(v) => Some(v.borrow_mut()),
_ => None,
}
}
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SlotAttribute { pub enum SlotAttribute {
@ -162,6 +181,11 @@ impl RenderGraphPassDesc {
} }
pub fn add_slot(&mut self, slot: RenderPassSlot) { pub fn add_slot(&mut self, slot: RenderPassSlot) {
debug_assert!(
!(slot.attribute == SlotAttribute::Input && slot.value.is_some()),
"input slots should not have values"
);
self.slot_names.insert(slot.name.clone(), slot.id); self.slot_names.insert(slot.name.clone(), slot.id);
self.slots.push(slot); self.slots.push(slot);
} }
@ -175,7 +199,7 @@ impl RenderGraphPassDesc {
value: Option<SlotValue>, value: Option<SlotValue>,
) { ) {
debug_assert!( debug_assert!(
matches!(value, None | Some(SlotValue::Buffer(_))), matches!(value, None | Some(SlotValue::Lazy) | Some(SlotValue::Buffer(_))),
"slot value is not a buffer" "slot value is not a buffer"
); );
@ -198,7 +222,7 @@ impl RenderGraphPassDesc {
value: Option<SlotValue>, value: Option<SlotValue>,
) { ) {
debug_assert!( debug_assert!(
matches!(value, None | Some(SlotValue::Texture(_))), matches!(value, None | Some(SlotValue::Lazy) | Some(SlotValue::Texture(_))),
"slot value is not a texture" "slot value is not a texture"
); );
@ -221,7 +245,7 @@ impl RenderGraphPassDesc {
value: Option<SlotValue>, value: Option<SlotValue>,
) { ) {
debug_assert!( debug_assert!(
matches!(value, None | Some(SlotValue::TextureView(_))), matches!(value, None | Some(SlotValue::Lazy) | Some(SlotValue::TextureView(_))),
"slot value is not a texture view" "slot value is not a texture view"
); );
@ -244,7 +268,7 @@ impl RenderGraphPassDesc {
value: Option<SlotValue>, value: Option<SlotValue>,
) { ) {
debug_assert!( debug_assert!(
matches!(value, None | Some(SlotValue::Sampler(_))), matches!(value, None | Some(SlotValue::Lazy) | Some(SlotValue::Sampler(_))),
"slot value is not a sampler" "slot value is not a sampler"
); );

View File

@ -1,47 +1,100 @@
use glam::UVec2; use std::{cell::RefCell, rc::Rc};
use crate::render::{camera::CameraUniform, graph::{BufferInitDescriptor, RenderGraphPass, RenderGraphPassDesc, RenderPassType, SlotAttribute, SlotDescriptor}}; use crate::render::graph::{RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassSlot, RenderPassType, RenderTarget, SlotAttribute, SlotType, SlotValue};
/// Supplies some basic things other passes needs. /// Supplies some basic things other passes needs.
/// ///
/// screen size buffer, camera buffer, /// screen size buffer, camera buffer,
#[derive(Default)] #[derive(Default)]
pub struct BasePass; pub struct BasePass {
/// Temporary storage for the main render target
///
/// This should be Some when the pass is first created then after its added to
/// the render graph it will be None and stay None.
temp_render_target: Option<RenderTarget>,
main_rt_id: u64,
window_tv_id: u64,
}
impl BasePass { impl BasePass {
pub fn new() -> Self { pub fn new(surface: wgpu::Surface, surface_config: wgpu::SurfaceConfiguration) -> Self {
Self::default() Self {
temp_render_target: Some(RenderTarget {
surface,
surface_config,
current_texture: None,
}),
main_rt_id: 0,
window_tv_id: 0,
}
} }
} }
impl RenderGraphPass for BasePass { impl RenderGraphPass for BasePass {
fn desc(&self, graph: &mut crate::render::graph::RenderGraph, id: &mut u64) -> crate::render::graph::RenderGraphPassDesc { fn desc(&mut self, graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::RenderGraphPassDesc {
let mut desc = RenderGraphPassDesc::new(*id, "BasePass", RenderPassType::Compute); let mut desc = RenderGraphPassDesc::new(
*id += 1; graph.next_id(),
"base",
RenderPassType::Render,
None,
vec![],
);
desc.add_buffer_slot(*id, "screen_size_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor { /* desc.add_buffer_slot(*id, "screen_size_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor {
label: Some("B_ScreenSize".to_string()), label: Some("B_ScreenSize".to_string()),
contents: bytemuck::bytes_of(&UVec2::new(800, 600)).to_vec(), contents: bytemuck::bytes_of(&UVec2::new(800, 600)).to_vec(),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}))); })));
*id += 1;
desc.add_buffer_slot(*id, "camera_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor { desc.add_buffer_slot(*id, "camera_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor {
label: Some("B_Camera".to_string()), label: Some("B_Camera".to_string()),
contents: bytemuck::bytes_of(&CameraUniform::default()).to_vec(), contents: bytemuck::bytes_of(&CameraUniform::default()).to_vec(),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}))); })));
*id += 1; *id += 1; */
self.main_rt_id = graph.next_id();
let render_target = self.temp_render_target.take().unwrap();
desc.add_slot(
RenderPassSlot {
ty: SlotType::RenderTarget,
attribute: SlotAttribute::Output,
id: self.main_rt_id,
name: "main_render_target".into(),
value: Some(SlotValue::RenderTarget(Rc::new(RefCell::new(render_target)))),
}
);
self.window_tv_id = graph.next_id();
desc.add_texture_view_slot(
self.window_tv_id,
"window_texture_view",
SlotAttribute::Output,
Some(SlotValue::Lazy),
);
desc desc
} }
fn prepare(&mut self, world: &mut lyra_ecs::World) { fn prepare(&mut self, _world: &mut lyra_ecs::World, _context: &mut RenderGraphContext) {
let _ = world;
todo!()
} }
fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, desc: &crate::render::graph::RenderGraphPassDesc, context: &mut crate::render::graph::RenderGraphContext) { fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, _desc: &crate::render::graph::RenderGraphPassDesc, _context: &mut crate::render::graph::RenderGraphContext) {
let _ = (graph, desc, context); let tv_slot = graph.slot_value_mut(self.main_rt_id)
todo!() .expect("somehow the main render target slot is missing");
let mut rt = tv_slot.as_render_target_mut().unwrap();
debug_assert!(!rt.current_texture.is_some(), "main render target surface was not presented!");
let surface_tex = rt.surface.get_current_texture().unwrap();
let view = surface_tex.texture.create_view(&wgpu::TextureViewDescriptor::default());
rt.current_texture = Some(surface_tex);
drop(rt); // must be manually dropped for borrow checker when getting texture view slot
// store the surface texture to the slot
let tv_slot = graph.slot_value_mut(self.window_tv_id)
.expect("somehow the window texture view slot is missing");
*tv_slot = SlotValue::TextureView(Rc::new(view));
} }
} }

View File

@ -1,8 +1,7 @@
/* mod light_cull_compute; /* mod light_cull_compute;
pub use light_cull_compute::*; pub use light_cull_compute::*;
mod base;
pub use base::*;
mod depth_prepass; mod depth_prepass;
pub use depth_prepass::*; */ pub use depth_prepass::*; */
@ -10,5 +9,11 @@ pub use depth_prepass::*; */
/* mod simple_phong; /* mod simple_phong;
pub use simple_phong::*; */ pub use simple_phong::*; */
mod base;
pub use base::*;
mod present_pass;
pub use present_pass::*;
mod triangle; mod triangle;
pub use triangle::*; pub use triangle::*;

View File

@ -0,0 +1,52 @@
use crate::render::graph::{RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassSlot, RenderPassType, SlotAttribute, SlotType};
/// Supplies some basic things other passes needs.
///
/// screen size buffer, camera buffer,
pub struct PresentPass {
render_target_slot: String,
}
impl PresentPass {
pub fn new(render_target_slot: &str) -> Self {
Self {
render_target_slot: render_target_slot.into(),
}
}
}
impl RenderGraphPass for PresentPass {
fn desc(&mut self, graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::RenderGraphPassDesc {
let mut desc = RenderGraphPassDesc::new(
graph.next_id(),
&format!("present_{}", self.render_target_slot),
RenderPassType::Presenter,
None,
vec![],
);
desc.add_slot(
RenderPassSlot {
ty: SlotType::RenderTarget,
attribute: SlotAttribute::Input,
id: graph.next_id(),
name: self.render_target_slot.clone(),
value: None,
}
);
desc
}
fn prepare(&mut self, _world: &mut lyra_ecs::World, _context: &mut RenderGraphContext) {
}
fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, _desc: &crate::render::graph::RenderGraphPassDesc, _context: &mut crate::render::graph::RenderGraphContext) {
let id = graph.slot_id(&self.render_target_slot)
.expect(&format!("render target slot '{}' for PresentPass is missing", self.render_target_slot));
let mut slot = graph.slot_value_mut(id).unwrap().as_render_target_mut().unwrap();
let surf_tex = slot.current_texture.take().unwrap();
surf_tex.present();
}
}

View File

@ -44,6 +44,13 @@ impl RenderGraphPass for TrianglePass {
let color_bgl = Rc::new(color_bgl); let color_bgl = Rc::new(color_bgl);
let color_bg = Rc::new(color_bg); let color_bg = Rc::new(color_bg);
let main_rt = graph.slot_id("main_render_target")
.and_then(|s| graph.slot_value(s))
.and_then(|s| s.as_render_target())
.expect("missing main render target");
let surface_config_format = main_rt.surface_config.format;
drop(main_rt);
let mut desc = RenderGraphPassDesc::new( let mut desc = RenderGraphPassDesc::new(
graph.next_id(), graph.next_id(),
"TrianglePass", "TrianglePass",
@ -61,7 +68,7 @@ impl RenderGraphPass for TrianglePass {
module: shader, module: shader,
entry_point: "fs_main".into(), entry_point: "fs_main".into(),
targets: vec![Some(wgpu::ColorTargetState { targets: vec![Some(wgpu::ColorTargetState {
format: graph.surface_config.format, format: surface_config_format,
blend: Some(wgpu::BlendState::REPLACE), blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL, write_mask: wgpu::ColorWrites::ALL,
})], })],

View File

@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::rc::Rc; use std::rc::Rc;
@ -12,7 +13,7 @@ use wgpu::Limits;
use winit::window::Window; use winit::window::Window;
use crate::math::Transform; use crate::math::Transform;
use crate::render::graph::TrianglePass; use crate::render::graph::{BasePass, PresentPass, TrianglePass};
use crate::render::material::MaterialUniform; use crate::render::material::MaterialUniform;
use crate::render::render_buffer::BufferWrapperBuilder; use crate::render::render_buffer::BufferWrapperBuilder;
@ -82,10 +83,8 @@ pub struct InterpTransform {
} }
pub struct BasicRenderer { pub struct BasicRenderer {
pub surface: wgpu::Surface,
pub device: Rc<wgpu::Device>, // device does not need to be mutable, no need for refcell pub device: Rc<wgpu::Device>, // device does not need to be mutable, no need for refcell
pub queue: Rc<wgpu::Queue>, pub queue: Rc<wgpu::Queue>,
pub config: wgpu::SurfaceConfiguration,
pub size: winit::dpi::PhysicalSize<u32>, pub size: winit::dpi::PhysicalSize<u32>,
pub window: Arc<Window>, pub window: Arc<Window>,
@ -214,7 +213,7 @@ impl BasicRenderer {
let queue = Rc::new(queue); let queue = Rc::new(queue);
//let light_cull_compute = LightCullCompute::new(device.clone(), queue.clone(), size, &light_uniform_buffers, &camera_buffer, &mut depth_texture); //let light_cull_compute = LightCullCompute::new(device.clone(), queue.clone(), size, &light_uniform_buffers, &camera_buffer, &mut depth_texture);
let mut g = RenderGraph::new(device.clone(), queue.clone(), config.clone()); let mut g = RenderGraph::new(device.clone(), queue.clone());
/* debug!("Adding base pass"); /* debug!("Adding base pass");
g.add_pass(TrianglePass::new()); g.add_pass(TrianglePass::new());
debug!("Adding depth pre-pass"); debug!("Adding depth pre-pass");
@ -222,16 +221,18 @@ impl BasicRenderer {
debug!("Adding light cull compute pass"); debug!("Adding light cull compute pass");
g.add_pass(LightCullComputePass::new(size)); */ g.add_pass(LightCullComputePass::new(size)); */
debug!("Adding base pass");
g.add_pass(BasePass::new(surface, config));
debug!("Adding triangle pass"); debug!("Adding triangle pass");
g.add_pass(TrianglePass::new()); g.add_pass(TrianglePass::new());
debug!("Adding present pass");
g.add_pass(PresentPass::new("main_render_target"));
g.setup(&device); g.setup(&device);
let mut s = Self { Self {
window, window,
surface,
device, device,
queue, queue,
config,
size, size,
clear_color: wgpu::Color { clear_color: wgpu::Color {
r: 0.1, r: 0.1,
@ -244,21 +245,7 @@ impl BasicRenderer {
render_limits, render_limits,
graph: g, graph: g,
}; }
// create the default pipelines
/* let mut pipelines = rustc_hash::FxHashMap::default();
pipelines.insert(0, Arc::new(RenderPipeline::new(&s.device, &s.config, &shader,
vec![super::vertex::Vertex::desc(),],
vec![&s.bgl_texture, &s.transform_buffers.bindgroup_layout,
s.camera_buffer.bindgroup_layout().unwrap(),
&s.light_buffers.bind_group_pair.layout, &s.material_buffer.bindgroup_pair.as_ref().unwrap().layout,
&s.bgl_texture,
&s.light_cull_compute.light_indices_grid.bg_pair.layout,
])));
s.render_pipelines = pipelines; */
s
} }
} }
@ -270,7 +257,7 @@ impl Renderer for BasicRenderer {
#[instrument(skip(self))] #[instrument(skip(self))]
fn render(&mut self) -> Result<(), wgpu::SurfaceError> { fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
self.graph.render(&self.surface); self.graph.render();
Ok(()) Ok(())
} }
@ -279,15 +266,17 @@ impl Renderer for BasicRenderer {
fn on_resize(&mut self, world: &mut World, new_size: winit::dpi::PhysicalSize<u32>) { fn on_resize(&mut self, world: &mut World, new_size: winit::dpi::PhysicalSize<u32>) {
if new_size.width > 0 && new_size.height > 0 { if new_size.width > 0 && new_size.height > 0 {
self.size = new_size; self.size = new_size;
self.config.width = new_size.width;
self.config.height = new_size.height;
// tell other things of updated resize // update surface config and the surface
self.surface.configure(&self.device, &self.config); let mut rt = self.graph.slot_value_mut(self.graph.slot_id("main_render_target").unwrap())
.unwrap().as_render_target_mut().unwrap();
rt.surface_config.width = new_size.width;
rt.surface_config.height = new_size.height;
rt.surface.configure(&self.device, &rt.surface_config);
// update screen size resource in ecs
let mut world_ss = world.get_resource_mut::<ScreenSize>(); let mut world_ss = world.get_resource_mut::<ScreenSize>();
world_ss.0 = glam::UVec2::new(new_size.width, new_size.height); world_ss.0 = glam::UVec2::new(new_size.width, new_size.height);
self.graph.surface_config = self.config.clone();
} }
} }