Implement a Render Graph #16

Merged
SeanOMik merged 20 commits from feature/render-graph into main 2024-06-15 22:54:47 +00:00
9 changed files with 266 additions and 185 deletions
Showing only changes of commit cee6e44d61 - Show all commits

View File

@ -3,9 +3,11 @@ use std::{
cell::RefCell, cell::RefCell,
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
ptr::NonNull, ptr::NonNull,
rc::Rc,
sync::Arc, sync::Arc,
}; };
use lyra_ecs::World;
pub use pass::*; pub use pass::*;
mod passes; mod passes;
@ -17,13 +19,14 @@ pub use slot_desc::*;
mod execution_path; mod execution_path;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use tracing::{debug, debug_span, instrument}; use tracing::{debug, debug_span, instrument, trace};
use wgpu::{util::DeviceExt, RenderPass}; use wgpu::{util::DeviceExt, RenderPass};
use self::execution_path::GraphExecutionPath; use self::execution_path::GraphExecutionPath;
use super::{ use super::{
renderer::{BasicRenderer, Renderer}, resource::{Pipeline, RenderPipeline}, renderer::{BasicRenderer, Renderer},
resource::{Pipeline, RenderPipeline},
}; };
//#[derive(Clone)] //#[derive(Clone)]
@ -37,11 +40,6 @@ struct ResourcedSlot {
//slot: RenderPassSlot, //slot: RenderPassSlot,
ty: SlotType, ty: SlotType,
value: SlotValue, value: SlotValue,
// will when the bind group is created
/// The id of the bind group for this slot. Becomes `Some` when the render graph creates the
/// bind group
bind_group_id: Option<u64>,
create_desc: Option<SlotDescriptor>,
} }
/// Stores the pipeline and other resources it uses. /// Stores the pipeline and other resources it uses.
@ -54,6 +52,8 @@ pub struct PipelineResource {
} }
pub struct RenderGraph { pub struct RenderGraph {
device: Rc<wgpu::Device>,
queue: Rc<wgpu::Queue>,
slots: FxHashMap<u64, ResourcedSlot>, slots: FxHashMap<u64, ResourcedSlot>,
slot_names: HashMap<String, u64>, slot_names: HashMap<String, u64>,
passes: FxHashMap<u64, PassEntry>, passes: FxHashMap<u64, PassEntry>,
@ -70,7 +70,7 @@ pub struct RenderGraph {
} }
impl RenderGraph { impl RenderGraph {
pub fn new(surface_config: wgpu::SurfaceConfiguration) -> Self { pub fn new(device: Rc<wgpu::Device>, queue: Rc<wgpu::Queue>, surface_config: wgpu::SurfaceConfiguration) -> Self {
let mut slots = FxHashMap::default(); let mut slots = FxHashMap::default();
let mut slot_names = HashMap::default(); let mut slot_names = HashMap::default();
@ -79,14 +79,15 @@ impl RenderGraph {
ResourcedSlot { ResourcedSlot {
name: "window_texture_view".to_string(), name: "window_texture_view".to_string(),
ty: SlotType::TextureView, ty: SlotType::TextureView,
// this will get set in prepare stage.
value: SlotValue::None, value: SlotValue::None,
bind_group_id: None,
create_desc: None,
}, },
); );
slot_names.insert("window_texture_view".to_string(), 0u64); slot_names.insert("window_texture_view".to_string(), 0u64);
Self { Self {
device,
queue,
slots, slots,
slot_names, slot_names,
passes: Default::default(), passes: Default::default(),
@ -98,6 +99,10 @@ impl RenderGraph {
} }
} }
pub fn device(&self) -> &wgpu::Device {
&*self.device
}
pub fn next_id(&mut self) -> u64 { pub fn next_id(&mut self) -> u64 {
self.current_id += 1; self.current_id += 1;
self.current_id self.current_id
@ -115,7 +120,7 @@ impl RenderGraph {
self.passes.get(&id).map(|s| &*s.desc) self.passes.get(&id).map(|s| &*s.desc)
} }
pub fn add_pass<P: RenderGraphPass>(&mut self, pass: P) { pub fn add_pass<P: RenderGraphPass>(&mut self, mut pass: P) {
let mut desc = pass.desc(self); let mut desc = pass.desc(self);
for slot in &mut desc.slots { for slot in &mut desc.slots {
@ -124,18 +129,19 @@ impl RenderGraph {
.get(&slot.name) .get(&slot.name)
.and_then(|id| self.slots.get_mut(id).map(|s| (id, s))) .and_then(|id| self.slots.get_mut(id).map(|s| (id, s)))
{ {
// if there is a slot of the same name
slot.id = *id; slot.id = *id;
if slot.desc.is_some() && other.create_desc.is_none() { debug_assert_eq!(
other.create_desc = slot.desc.clone(); slot.ty, other.ty,
} "slot {} in pass {} does not match existing slot of same name",
slot.name, desc.name
);
} else { } else {
let res_slot = ResourcedSlot { let res_slot = ResourcedSlot {
name: slot.name.clone(), name: slot.name.clone(),
ty: slot.ty, ty: slot.ty,
value: SlotValue::None, value: SlotValue::None,
bind_group_id: None,
create_desc: slot.desc.clone(),
}; };
self.slots.insert(slot.id, res_slot); self.slots.insert(slot.id, res_slot);
@ -155,8 +161,6 @@ impl RenderGraph {
/// Creates all buffers required for the passes, also creates an internal execution path. /// Creates all buffers required for the passes, also creates an internal execution path.
#[instrument(skip(self, device))] #[instrument(skip(self, device))]
pub fn setup(&mut self, device: &wgpu::Device) { pub fn setup(&mut self, device: &wgpu::Device) {
let mut later_slots = VecDeque::new();
// For all passes, create their pipelines // For all passes, create their pipelines
for pass in self.passes.values() { for pass in self.passes.values() {
if let Some(pipei) = &pass.desc.pipeline_desc { if let Some(pipei) = &pass.desc.pipeline_desc {
@ -174,101 +178,27 @@ impl RenderGraph {
self.pipelines.insert(pass.desc.id, res); self.pipelines.insert(pass.desc.id, res);
} }
} }
for (slot_id, slot) in &mut self.slots {
if slot.bind_group_id.is_none() && slot.create_desc.is_some() {
let s = debug_span!("res_creation", slot = slot.name);
let _e = s.enter();
debug!("Creating bind group for slot");
match &slot.create_desc {
Some(SlotDescriptor::BufferInit(bi)) => {
let label = format!("B_{}", slot.name);
let wb = bi.as_wgpu(Some(&label));
let buf = device.create_buffer_init(&wb);
slot.value = SlotValue::Buffer(buf);
debug!("Created and initialized buffer for slot");
}
Some(SlotDescriptor::Buffer(b)) => {
let label = format!("B_{}", slot.name);
let wb = b.as_wgpu(Some(&label));
let buf = device.create_buffer(&wb);
slot.value = SlotValue::Buffer(buf);
debug!("Created buffer");
}
Some(SlotDescriptor::Texture(t)) => {
let label = format!("Tex_{}", slot.name);
let wt = t.as_wgpu(Some(&label));
let tex = device.create_texture(&wt);
slot.value = SlotValue::Texture(tex);
debug!("Created texture");
}
Some(SlotDescriptor::TextureView(tv)) => {
// texture views cannot be done in this step. Not only would we run into a
// borrow checker error when trying to get the texture for the view, we
// can also not guarantee that the texture was actually created.
let tex_slot = self
.slot_names
.get(&tv.texture_label)
.expect("Failed to find texture for texture view slot");
later_slots.push_back((*slot_id, *tex_slot));
debug!("Queuing slot resources for later creation");
}
//Some(SlotDescriptor::Sampler(b)) => {},
//Some(SlotDescriptor::Texture(b)) => {},
Some(SlotDescriptor::None) => {}
None => {}
_ => todo!(),
}
}
}
// create buffers for the queued slots
while let Some((lower_id, upper_id)) = later_slots.pop_front() {
let many = self.slots.get_many_mut([&lower_id, &upper_id]).unwrap();
let (lower_slot, upper_slot) = match many {
[a, b] => (a, b),
};
let s = debug_span!("deferred_res_creation", slot = lower_slot.name);
let _e = s.enter();
match &lower_slot.create_desc {
Some(SlotDescriptor::TextureView(tv)) => {
let label = format!("TexView_{}", lower_slot.name);
let wt = tv.as_wgpu(Some(&label));
let tex = upper_slot.value.as_texture();
let texview = tex.create_view(&wt);
lower_slot.value = SlotValue::TextureView(texview);
debug!(slot = lower_slot.name, "Created texture view");
}
Some(SlotDescriptor::None) => {}
None => {}
_ => unreachable!("this slot should not have been put into the do later list"),
}
}
} }
pub fn prepare(&mut self) { #[instrument(skip(self, world))]
pub fn prepare(&mut self, world: &mut World) {
// prepare all passes
let mut context = RenderGraphContext::new(&self.queue, None);
for (_, pass) in &mut self.passes {
let mut inner = pass.inner.borrow_mut();
inner.prepare(world, &mut context);
}
// create the execution path for the graph. This will be executed in `RenderGraph::render`
let builtin = { let builtin = {
let mut h = FxHashSet::default(); let mut h = FxHashSet::default();
h.insert(0u64); h.insert(0u64);
h h
}; };
let descs = self.passes.values().map(|p| &*p.desc).collect(); let descs = self.passes.values().map(|p| &*p.desc).collect();
self.exec_path = Some(GraphExecutionPath::new(builtin, descs)); let path = GraphExecutionPath::new(builtin, descs);
trace!("Found {} steps in the rendergraph to execute", path.queue.len());
self.exec_path = Some(path);
} }
pub fn render(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, surface: &wgpu::Surface) { pub fn render(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, surface: &wgpu::Surface) {
@ -298,12 +228,12 @@ impl RenderGraph {
let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some(&label), label: Some(&label),
}); });
let mut context = RenderGraphContext::new(encoder, queue); let mut context = RenderGraphContext::new(queue, Some(encoder));
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.finish()); encoders.push(context.encoder.unwrap().finish());
} }
queue.submit(encoders.into_iter()); queue.submit(encoders.into_iter());
@ -327,23 +257,12 @@ impl RenderGraph {
#[inline(always)] #[inline(always)]
pub fn try_slot_bind_group(&self, id: u64) -> Option<&wgpu::BindGroup> { pub fn try_slot_bind_group(&self, id: u64) -> Option<&wgpu::BindGroup> {
self.slots.get(&id).and_then(|s| { todo!()
self.bind_groups.get(
&s.bind_group_id
.expect("Slot bind group has not been created yet"),
)
})
} }
#[inline(always)] #[inline(always)]
pub fn slot_bind_group(&self, id: u64) -> &wgpu::BindGroup { pub fn slot_bind_group(&self, id: u64) -> &wgpu::BindGroup {
let bg_id = self todo!()
.slots
.get(&id)
.expect("unknown slot id")
.bind_group_id
.expect("Slot bind group has not been created yet");
self.bind_group(id)
} }
} }
@ -355,14 +274,14 @@ pub(crate) struct BufferWrite {
pub struct RenderGraphContext<'a> { pub struct RenderGraphContext<'a> {
/// Becomes None when the encoder is submitted /// Becomes None when the encoder is submitted
pub(crate) encoder: wgpu::CommandEncoder, pub(crate) encoder: Option<wgpu::CommandEncoder>,
pub(crate) queue: &'a wgpu::Queue, pub(crate) queue: &'a wgpu::Queue,
pub(crate) buffer_writes: Vec<BufferWrite>, pub(crate) buffer_writes: Vec<BufferWrite>,
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>, renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
} }
impl<'a> RenderGraphContext<'a> { impl<'a> RenderGraphContext<'a> {
pub fn new(encoder: wgpu::CommandEncoder, queue: &'a wgpu::Queue) -> Self { pub fn new(queue: &'a wgpu::Queue, encoder: Option<wgpu::CommandEncoder>) -> Self {
Self { Self {
encoder, encoder,
queue, queue,
@ -375,11 +294,19 @@ impl<'a> RenderGraphContext<'a> {
&'a mut self, &'a mut self,
desc: wgpu::RenderPassDescriptor<'a, 'a>, desc: wgpu::RenderPassDescriptor<'a, 'a>,
) -> wgpu::RenderPass { ) -> wgpu::RenderPass {
self.encoder.begin_render_pass(&desc) self.encoder
.as_mut()
.expect("RenderGraphContext is missing a command encoder. This is likely \
because you are trying to run render commands in the prepare stage.")
.begin_render_pass(&desc)
} }
pub fn begin_compute_pass(&mut self, desc: &wgpu::ComputePassDescriptor) -> wgpu::ComputePass { pub fn begin_compute_pass(&mut self, desc: &wgpu::ComputePassDescriptor) -> wgpu::ComputePass {
self.encoder.begin_compute_pass(desc) self.encoder
.as_mut()
.expect("RenderGraphContext is missing a command encoder. This is likely \
because you are trying to run render commands in the prepare stage.")
.begin_compute_pass(desc)
} }
pub fn write_buffer(&mut self, target_slot: &str, bytes: &[u8]) { pub fn write_buffer(&mut self, target_slot: &str, bytes: &[u8]) {

View File

@ -30,7 +30,7 @@ pub enum SlotValue {
TextureView(wgpu::TextureView), TextureView(wgpu::TextureView),
Sampler(wgpu::Sampler), Sampler(wgpu::Sampler),
Texture(wgpu::Texture), Texture(wgpu::Texture),
Buffer(wgpu::Buffer), Buffer(Rc<wgpu::Buffer>),
} }
impl SlotValue { impl SlotValue {
@ -49,19 +49,6 @@ impl SlotValue {
} }
} }
} }
#[derive(Clone, Debug)]
pub enum SlotDescriptor {
/// Most likely this slot is an input, so it doesn't need to specify the descriptor.
None,
TextureView(TextureViewDescriptor),
Sampler(SamplerDescriptor),
Texture(TextureDescriptor),
Buffer(BufferDescriptor),
BufferInit(BufferInitDescriptor),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SlotAttribute { pub enum SlotAttribute {
Input, Input,
@ -76,7 +63,7 @@ pub struct RenderPassSlot {
pub name: String, pub name: String,
/// The descriptor of the slot value. /// The descriptor of the slot value.
/// This will be `None` if this slot is an input. /// This will be `None` if this slot is an input.
pub desc: Option<SlotDescriptor>, pub value: Option<Rc<SlotValue>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -171,14 +158,14 @@ impl RenderGraphPassDesc {
id: u64, id: u64,
name: &str, name: &str,
attribute: SlotAttribute, attribute: SlotAttribute,
desc: Option<SlotDescriptor>, value: Option<SlotValue>,
) { ) {
debug_assert!( debug_assert!(
matches!( matches!(
desc, value,
None | Some(SlotDescriptor::Buffer(_)) | Some(SlotDescriptor::BufferInit(_)) None | Some(SlotValue::Buffer(_))
), ),
"slot descriptor does not match the type of slot" "slot value is not a buffer"
); );
let slot = RenderPassSlot { let slot = RenderPassSlot {
@ -186,7 +173,7 @@ impl RenderGraphPassDesc {
name: name.to_string(), name: name.to_string(),
ty: SlotType::Buffer, ty: SlotType::Buffer,
attribute, attribute,
desc, value: value.map(|v| Rc::new(v)),
}; };
self.add_slot(slot); self.add_slot(slot);
} }
@ -197,11 +184,14 @@ impl RenderGraphPassDesc {
id: u64, id: u64,
name: &str, name: &str,
attribute: SlotAttribute, attribute: SlotAttribute,
desc: Option<SlotDescriptor>, value: Option<SlotValue>,
) { ) {
debug_assert!( debug_assert!(
matches!(desc, None | Some(SlotDescriptor::Texture(_))), matches!(
"slot descriptor does not match the type of slot" value,
None | Some(SlotValue::Texture(_))
),
"slot value is not a texture"
); );
let slot = RenderPassSlot { let slot = RenderPassSlot {
@ -209,7 +199,7 @@ impl RenderGraphPassDesc {
name: name.to_string(), name: name.to_string(),
ty: SlotType::Texture, ty: SlotType::Texture,
attribute, attribute,
desc, value: value.map(|v| Rc::new(v)),
}; };
self.add_slot(slot); self.add_slot(slot);
} }
@ -220,11 +210,14 @@ impl RenderGraphPassDesc {
id: u64, id: u64,
name: &str, name: &str,
attribute: SlotAttribute, attribute: SlotAttribute,
desc: Option<SlotDescriptor>, value: Option<SlotValue>,
) { ) {
debug_assert!( debug_assert!(
matches!(desc, None | Some(SlotDescriptor::TextureView(_))), matches!(
"slot descriptor does not match the type of slot" value,
None | Some(SlotValue::TextureView(_))
),
"slot value is not a texture view"
); );
let slot = RenderPassSlot { let slot = RenderPassSlot {
@ -232,7 +225,7 @@ impl RenderGraphPassDesc {
name: name.to_string(), name: name.to_string(),
ty: SlotType::TextureView, ty: SlotType::TextureView,
attribute, attribute,
desc, value: value.map(|v| Rc::new(v)),
}; };
self.add_slot(slot); self.add_slot(slot);
} }
@ -243,11 +236,14 @@ impl RenderGraphPassDesc {
id: u64, id: u64,
name: &str, name: &str,
attribute: SlotAttribute, attribute: SlotAttribute,
desc: Option<SlotDescriptor>, value: Option<SlotValue>,
) { ) {
debug_assert!( debug_assert!(
matches!(desc, None | Some(SlotDescriptor::Sampler(_))), matches!(
"slot descriptor does not match the type of slot" value,
None | Some(SlotValue::Sampler(_))
),
"slot value is not a sampler"
); );
let slot = RenderPassSlot { let slot = RenderPassSlot {
@ -255,7 +251,7 @@ impl RenderGraphPassDesc {
name: name.to_string(), name: name.to_string(),
ty: SlotType::Sampler, ty: SlotType::Sampler,
attribute, attribute,
desc, value: value.map(|v| Rc::new(v)),
}; };
self.add_slot(slot); self.add_slot(slot);
} }
@ -279,7 +275,7 @@ pub trait RenderGraphPass: 'static {
/// Create a render pass describer. /// Create a render pass describer.
/// ///
/// The `id` argument is passed as mutable so you can increment it as you use it for new slots. /// The `id` argument is passed as mutable so you can increment it as you use it for new slots.
fn desc<'a, 'b>(&'a self, graph: &'b mut RenderGraph) -> RenderGraphPassDesc; fn desc<'a, 'b>(&'a mut self, graph: &'b mut RenderGraph) -> RenderGraphPassDesc;
fn prepare(&mut self, world: &mut World, context: &mut RenderGraphContext); fn prepare(&mut self, world: &mut World, context: &mut RenderGraphContext);
fn execute( fn execute(

View File

@ -8,21 +8,26 @@ use crate::{
render::{ render::{
camera::{CameraUniform, RenderCamera}, camera::{CameraUniform, RenderCamera},
graph::{ graph::{
BufferInitDescriptor, PipelineShaderDesc, RenderGraphContext, RenderGraphPass, BufferDescriptor, BufferInitDescriptor, PipelineShaderDesc, RenderGraphContext,
RenderGraphPassDesc, RenderGraphPipelineInfo, RenderPassType, SlotAttribute, RenderGraphPass, RenderGraphPassDesc, RenderGraphPipelineInfo, RenderPassType,
SlotDescriptor, SlotAttribute, SlotValue,
}, },
render_buffer::BufferWrapper,
renderer::ScreenSize, renderer::ScreenSize,
resource::{FragmentState, RenderPipelineDescriptor, Shader, VertexState}, resource::{FragmentState, RenderPipelineDescriptor, Shader, VertexState},
}, },
scene::CameraComponent, scene::CameraComponent, DeltaTime,
}; };
/// 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 TrianglePass; pub struct TrianglePass {
color_bg: Option<wgpu::BindGroup>,
color_buf: Option<Rc<wgpu::Buffer>>,
acc: f32,
}
impl TrianglePass { impl TrianglePass {
pub fn new() -> Self { pub fn new() -> Self {
@ -32,7 +37,7 @@ impl TrianglePass {
impl RenderGraphPass for TrianglePass { impl RenderGraphPass for TrianglePass {
fn desc( fn desc(
&self, &mut self,
graph: &mut crate::render::graph::RenderGraph, graph: &mut crate::render::graph::RenderGraph,
) -> crate::render::graph::RenderGraphPassDesc { ) -> crate::render::graph::RenderGraphPassDesc {
let shader = Rc::new(Shader { let shader = Rc::new(Shader {
@ -40,13 +45,25 @@ impl RenderGraphPass for TrianglePass {
source: include_str!("../../shaders/triangle.wgsl").to_string(), source: include_str!("../../shaders/triangle.wgsl").to_string(),
}); });
let device = graph.device();
let (color_bgl, color_bg, color_buf, _) = BufferWrapper::builder()
.label_prefix("color")
.visibility(wgpu::ShaderStages::FRAGMENT)
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
.contents(&[glam::Vec4::new(0.902, 0.639, 0.451, 1.0)])
.finish_parts(device);
let color_buf = Rc::new(color_buf);
self.color_bg = Some(color_bg);
self.color_buf = Some(color_buf.clone());
let mut desc = RenderGraphPassDesc::new( let mut desc = RenderGraphPassDesc::new(
graph.next_id(), graph.next_id(),
"TrianglePass", "TrianglePass",
RenderPassType::Render, RenderPassType::Render,
Some(RenderPipelineDescriptor { Some(RenderPipelineDescriptor {
label: Some("triangle_pipeline".into()), label: Some("triangle_pipeline".into()),
layouts: vec![], layouts: vec![color_bgl],
push_constant_ranges: vec![], push_constant_ranges: vec![],
vertex: VertexState { vertex: VertexState {
module: shader.clone(), module: shader.clone(),
@ -76,21 +93,28 @@ impl RenderGraphPass for TrianglePass {
None, None,
); );
/* desc.add_buffer_slot(graph.next_id(), "screen_size_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor { desc.add_buffer_slot(
label: Some("B_ScreenSize".to_string()), graph.next_id(),
contents: bytemuck::bytes_of(&UVec2::new(800, 600)).to_vec(), "color_buffer",
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, SlotAttribute::Output,
}))); Some(SlotValue::Buffer(color_buf)),
desc.add_buffer_slot(graph.next_id(), "camera_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor { );
label: Some("B_Camera".to_string()),
contents: bytemuck::bytes_of(&CameraUniform::default()).to_vec(),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}))); */
desc desc
} }
fn prepare(&mut self, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {} fn prepare(&mut self, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
const SPEED: f32 = 1.5;
let dt = **world.get_resource::<DeltaTime>();
self.acc += dt;
let x = (self.acc * SPEED).sin();
let y = ((self.acc + 2.15) * SPEED).sin();
let z = ((self.acc + 5.35) * SPEED).sin();
let color = glam::Vec4::new(x, y, z, 1.0);
context.queue.write_buffer(self.color_buf.as_ref().unwrap(), 0, bytemuck::bytes_of(&color));
}
fn execute( fn execute(
&mut self, &mut self,
@ -102,7 +126,12 @@ impl RenderGraphPass for TrianglePass {
.slot_value(graph.slot_id("window_texture_view").unwrap()) .slot_value(graph.slot_id("window_texture_view").unwrap())
.unwrap() .unwrap()
.as_texture_view(); .as_texture_view();
let encoder = &mut context.encoder; let encoder = context.encoder.as_mut().unwrap();
//context.queue.write_buffer(buffer, offset, data)
//let color_bg = graph.bind_group(graph.slot_id("color_buffer").unwrap());
let color_bg = self.color_bg.as_ref().unwrap();
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("TrianglePass"), label: Some("TrianglePass"),
@ -127,6 +156,7 @@ impl RenderGraphPass for TrianglePass {
let pipeline = graph.pipeline(desc.id); let pipeline = graph.pipeline(desc.id);
pass.set_pipeline(&pipeline.as_render()); pass.set_pipeline(&pipeline.as_render());
pass.set_bind_group(0, color_bg, &[]);
pass.draw(0..3, 0..1); pass.draw(0..3, 0..1);
} }
} }

View File

@ -1,4 +1,4 @@
use std::num::{NonZeroU32, NonZeroU8}; use std::{mem, num::{NonZeroU32, NonZeroU8}};
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct TextureViewDescriptor { pub struct TextureViewDescriptor {
@ -162,6 +162,14 @@ pub struct BufferDescriptor {
} }
impl BufferDescriptor { impl BufferDescriptor {
pub fn new<T: Sized>(usage: wgpu::BufferUsages, mapped_at_creation: bool) -> Self {
Self {
size: mem::size_of::<T>() as _,
usage,
mapped_at_creation,
}
}
pub fn as_wgpu<'a>(&self, label: Option<&'a str>) -> wgpu::BufferDescriptor<'a> { pub fn as_wgpu<'a>(&self, label: Option<&'a str>) -> wgpu::BufferDescriptor<'a> {
wgpu::BufferDescriptor { wgpu::BufferDescriptor {
label, label,
@ -184,6 +192,14 @@ pub struct BufferInitDescriptor {
} }
impl BufferInitDescriptor { impl BufferInitDescriptor {
pub fn new<T: bytemuck::Pod>(label: Option<&str>, data: &T, usage: wgpu::BufferUsages) -> Self {
Self {
label: label.map(|s| s.to_string()),
contents: bytemuck::bytes_of(data).to_vec(),
usage,
}
}
pub fn as_wgpu<'a>(&'a self, label: Option<&'a str>) -> wgpu::util::BufferInitDescriptor<'a> { pub fn as_wgpu<'a>(&'a self, label: Option<&'a str>) -> wgpu::util::BufferInitDescriptor<'a> {
wgpu::util::BufferInitDescriptor { wgpu::util::BufferInitDescriptor {
label, label,

View File

@ -134,6 +134,15 @@ impl BufferWrapper {
"BufferWrapper is missing bindgroup pair! Cannot set bind group on RenderPass!", "BufferWrapper is missing bindgroup pair! Cannot set bind group on RenderPass!",
).bindgroup ).bindgroup
} }
/// Take the bind group layout, the bind group, and the buffer out of the wrapper.
pub fn parts(self) -> (Option<Rc<wgpu::BindGroupLayout>>, Option<wgpu::BindGroup>, wgpu::Buffer) {
if let Some(pair) = self.bindgroup_pair {
(Some(pair.layout), Some(pair.bindgroup), self.inner_buf)
} else {
(None, None, self.inner_buf)
}
}
} }
/// Struct used for building a BufferWrapper /// Struct used for building a BufferWrapper
@ -221,7 +230,7 @@ impl BufferWrapperBuilder {
/// * `contents` - The contents to initialize the buffer with. /// * `contents` - The contents to initialize the buffer with.
/// ///
/// If a field is missing, a panic will occur. /// If a field is missing, a panic will occur.
pub fn finish(self, device: &wgpu::Device) -> BufferWrapper { pub fn finish_parts(self, device: &wgpu::Device) -> (wgpu::BindGroupLayout, wgpu::BindGroup, wgpu::Buffer, usize) {
let buf_usage = self.buffer_usage.expect("Buffer usage was not set"); let buf_usage = self.buffer_usage.expect("Buffer usage was not set");
let buffer = if let Some(contents) = self.contents.as_ref() { let buffer = if let Some(contents) = self.contents.as_ref() {
device.create_buffer_init( device.create_buffer_init(
@ -293,10 +302,25 @@ impl BufferWrapperBuilder {
} }
}; };
BufferWrapper { /* BufferWrapper {
bindgroup_pair: Some(bg_pair), bindgroup_pair: Some(bg_pair),
inner_buf: buffer, inner_buf: buffer,
len: Some(self.count.unwrap_or_default() as usize), len: Some(self.count.unwrap_or_default() as usize),
} */
(Rc::try_unwrap(bg_pair.layout).unwrap(), bg_pair.bindgroup, buffer, self.count.unwrap_or_default() as usize)
}
pub fn finish(self, device: &wgpu::Device) -> BufferWrapper {
let (bgl, bg, buff, len) = self.finish_parts(device);
BufferWrapper {
bindgroup_pair: Some(BindGroupPair {
layout: Rc::new(bgl),
bindgroup: bg
}),
inner_buf: buff,
len: Some(len),
} }
} }
} }

View File

@ -214,7 +214,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(config.clone()); let mut g = RenderGraph::new(device.clone(), queue.clone(), config.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");
@ -265,7 +265,7 @@ impl BasicRenderer {
impl Renderer for BasicRenderer { impl Renderer for BasicRenderer {
#[instrument(skip(self, main_world))] #[instrument(skip(self, main_world))]
fn prepare(&mut self, main_world: &mut World) { fn prepare(&mut self, main_world: &mut World) {
self.graph.prepare(); self.graph.prepare(main_world);
} }
#[instrument(skip(self))] #[instrument(skip(self))]

View File

@ -63,9 +63,7 @@ impl RenderPipelineDescriptor {
label: None, //self.label.as_ref().map(|s| format!("{}Layout", s)), label: None, //self.label.as_ref().map(|s| format!("{}Layout", s)),
bind_group_layouts: &bgs, bind_group_layouts: &bgs,
push_constant_ranges: &self.push_constant_ranges, push_constant_ranges: &self.push_constant_ranges,
}); })
todo!()
} }
/* fn as_wgpu<'a>(&'a self, device: &wgpu::Device, layout: Option<&'a wgpu::PipelineLayout>) -> wgpu::RenderPipelineDescriptor<'a> { /* fn as_wgpu<'a>(&'a self, device: &wgpu::Device, layout: Option<&'a wgpu::PipelineLayout>) -> wgpu::RenderPipelineDescriptor<'a> {

View File

@ -0,0 +1,87 @@
// Vertex shader
const max_light_count: u32 = 16u;
const LIGHT_TY_DIRECTIONAL = 0u;
const LIGHT_TY_POINT = 1u;
const LIGHT_TY_SPOT = 2u;
const ALPHA_CUTOFF = 0.1;
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) tex_coords: vec2<f32>,
@location(2) normal: vec3<f32>,
}
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) tex_coords: vec2<f32>,
@location(1) world_position: vec3<f32>,
@location(2) world_normal: vec3<f32>,
}
struct TransformData {
transform: mat4x4<f32>,
normal_matrix: mat4x4<f32>,
}
struct CameraUniform {
view: mat4x4<f32>,
inverse_projection: mat4x4<f32>,
view_projection: mat4x4<f32>,
projection: mat4x4<f32>,
position: vec3<f32>,
tile_debug: u32,
};
@group(1) @binding(0)
var<uniform> u_model_transform_data: TransformData;
@group(2) @binding(0)
var<uniform> u_camera: CameraUniform;
@vertex
fn vs_main(
model: VertexInput,
) -> VertexOutput {
var out: VertexOutput;
out.tex_coords = model.tex_coords;
out.clip_position = u_camera.view_projection * u_model_transform_data.transform * vec4<f32>(model.position, 1.0);
// the normal mat is actually only a mat3x3, but there's a bug in wgpu: https://github.com/gfx-rs/wgpu-rs/issues/36
let normal_mat4 = u_model_transform_data.normal_matrix;
let normal_mat = mat3x3(normal_mat4[0].xyz, normal_mat4[1].xyz, normal_mat4[2].xyz);
out.world_normal = normalize(normal_mat * model.normal, );
var world_position: vec4<f32> = u_model_transform_data.transform * vec4<f32>(model.position, 1.0);
out.world_position = world_position.xyz;
return out;
}
// Fragment shader
struct Material {
ambient: vec4<f32>,
diffuse: vec4<f32>,
specular: vec4<f32>,
shininess: f32,
}
@group(0) @binding(0)
var t_diffuse: texture_2d<f32>;
@group(0) @binding(1)
var s_diffuse: sampler;
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
if (object_color.a < ALPHA_CUTOFF) {
discard;
}
return object_color;
}

View File

@ -2,6 +2,9 @@ struct VertexOutput {
@builtin(position) clip_position: vec4<f32>, @builtin(position) clip_position: vec4<f32>,
}; };
@group(0) @binding(0)
var<uniform> u_triangle_color: vec4<f32>;
@vertex @vertex
fn vs_main( fn vs_main(
@builtin(vertex_index) in_vertex_index: u32, @builtin(vertex_index) in_vertex_index: u32,
@ -15,5 +18,5 @@ fn vs_main(
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(0.3, 0.2, 0.1, 1.0); return vec4<f32>(u_triangle_color.xyz, 1.0);
} }