Implement a Render Graph #16
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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::*;
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
})],
|
})],
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue