Implement a Render Graph #16
|
@ -3,9 +3,11 @@ use std::{
|
|||
cell::RefCell,
|
||||
collections::{HashMap, VecDeque},
|
||||
ptr::NonNull,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use lyra_ecs::World;
|
||||
pub use pass::*;
|
||||
|
||||
mod passes;
|
||||
|
@ -17,13 +19,14 @@ pub use slot_desc::*;
|
|||
mod execution_path;
|
||||
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use tracing::{debug, debug_span, instrument};
|
||||
use tracing::{debug, debug_span, instrument, trace};
|
||||
use wgpu::{util::DeviceExt, RenderPass};
|
||||
|
||||
use self::execution_path::GraphExecutionPath;
|
||||
|
||||
use super::{
|
||||
renderer::{BasicRenderer, Renderer}, resource::{Pipeline, RenderPipeline},
|
||||
renderer::{BasicRenderer, Renderer},
|
||||
resource::{Pipeline, RenderPipeline},
|
||||
};
|
||||
|
||||
//#[derive(Clone)]
|
||||
|
@ -37,11 +40,6 @@ struct ResourcedSlot {
|
|||
//slot: RenderPassSlot,
|
||||
ty: SlotType,
|
||||
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.
|
||||
|
@ -54,6 +52,8 @@ pub struct PipelineResource {
|
|||
}
|
||||
|
||||
pub struct RenderGraph {
|
||||
device: Rc<wgpu::Device>,
|
||||
queue: Rc<wgpu::Queue>,
|
||||
slots: FxHashMap<u64, ResourcedSlot>,
|
||||
slot_names: HashMap<String, u64>,
|
||||
passes: FxHashMap<u64, PassEntry>,
|
||||
|
@ -70,7 +70,7 @@ pub struct 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 slot_names = HashMap::default();
|
||||
|
||||
|
@ -79,14 +79,15 @@ impl RenderGraph {
|
|||
ResourcedSlot {
|
||||
name: "window_texture_view".to_string(),
|
||||
ty: SlotType::TextureView,
|
||||
// this will get set in prepare stage.
|
||||
value: SlotValue::None,
|
||||
bind_group_id: None,
|
||||
create_desc: None,
|
||||
},
|
||||
);
|
||||
slot_names.insert("window_texture_view".to_string(), 0u64);
|
||||
|
||||
Self {
|
||||
device,
|
||||
queue,
|
||||
slots,
|
||||
slot_names,
|
||||
passes: Default::default(),
|
||||
|
@ -98,6 +99,10 @@ impl RenderGraph {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn device(&self) -> &wgpu::Device {
|
||||
&*self.device
|
||||
}
|
||||
|
||||
pub fn next_id(&mut self) -> u64 {
|
||||
self.current_id += 1;
|
||||
self.current_id
|
||||
|
@ -115,7 +120,7 @@ impl RenderGraph {
|
|||
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);
|
||||
|
||||
for slot in &mut desc.slots {
|
||||
|
@ -124,18 +129,19 @@ impl RenderGraph {
|
|||
.get(&slot.name)
|
||||
.and_then(|id| self.slots.get_mut(id).map(|s| (id, s)))
|
||||
{
|
||||
// if there is a slot of the same name
|
||||
slot.id = *id;
|
||||
|
||||
if slot.desc.is_some() && other.create_desc.is_none() {
|
||||
other.create_desc = slot.desc.clone();
|
||||
}
|
||||
debug_assert_eq!(
|
||||
slot.ty, other.ty,
|
||||
"slot {} in pass {} does not match existing slot of same name",
|
||||
slot.name, desc.name
|
||||
);
|
||||
} else {
|
||||
let res_slot = ResourcedSlot {
|
||||
name: slot.name.clone(),
|
||||
ty: slot.ty,
|
||||
value: SlotValue::None,
|
||||
bind_group_id: None,
|
||||
create_desc: slot.desc.clone(),
|
||||
};
|
||||
|
||||
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.
|
||||
#[instrument(skip(self, device))]
|
||||
pub fn setup(&mut self, device: &wgpu::Device) {
|
||||
let mut later_slots = VecDeque::new();
|
||||
|
||||
// For all passes, create their pipelines
|
||||
for pass in self.passes.values() {
|
||||
if let Some(pipei) = &pass.desc.pipeline_desc {
|
||||
|
@ -174,101 +178,27 @@ impl RenderGraph {
|
|||
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 mut h = FxHashSet::default();
|
||||
h.insert(0u64);
|
||||
h
|
||||
};
|
||||
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) {
|
||||
|
@ -298,12 +228,12 @@ impl RenderGraph {
|
|||
let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
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();
|
||||
inner.execute(self, &*pass_desc, &mut context);
|
||||
|
||||
encoders.push(context.encoder.finish());
|
||||
encoders.push(context.encoder.unwrap().finish());
|
||||
}
|
||||
|
||||
queue.submit(encoders.into_iter());
|
||||
|
@ -327,23 +257,12 @@ impl RenderGraph {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn try_slot_bind_group(&self, id: u64) -> Option<&wgpu::BindGroup> {
|
||||
self.slots.get(&id).and_then(|s| {
|
||||
self.bind_groups.get(
|
||||
&s.bind_group_id
|
||||
.expect("Slot bind group has not been created yet"),
|
||||
)
|
||||
})
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn slot_bind_group(&self, id: u64) -> &wgpu::BindGroup {
|
||||
let bg_id = self
|
||||
.slots
|
||||
.get(&id)
|
||||
.expect("unknown slot id")
|
||||
.bind_group_id
|
||||
.expect("Slot bind group has not been created yet");
|
||||
self.bind_group(id)
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -355,14 +274,14 @@ pub(crate) struct BufferWrite {
|
|||
|
||||
pub struct RenderGraphContext<'a> {
|
||||
/// 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) buffer_writes: Vec<BufferWrite>,
|
||||
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, '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 {
|
||||
encoder,
|
||||
queue,
|
||||
|
@ -375,11 +294,19 @@ impl<'a> RenderGraphContext<'a> {
|
|||
&'a mut self,
|
||||
desc: wgpu::RenderPassDescriptor<'a, 'a>,
|
||||
) -> 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 {
|
||||
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]) {
|
||||
|
|
|
@ -30,7 +30,7 @@ pub enum SlotValue {
|
|||
TextureView(wgpu::TextureView),
|
||||
Sampler(wgpu::Sampler),
|
||||
Texture(wgpu::Texture),
|
||||
Buffer(wgpu::Buffer),
|
||||
Buffer(Rc<wgpu::Buffer>),
|
||||
}
|
||||
|
||||
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)]
|
||||
pub enum SlotAttribute {
|
||||
Input,
|
||||
|
@ -76,7 +63,7 @@ pub struct RenderPassSlot {
|
|||
pub name: String,
|
||||
/// The descriptor of the slot value.
|
||||
/// This will be `None` if this slot is an input.
|
||||
pub desc: Option<SlotDescriptor>,
|
||||
pub value: Option<Rc<SlotValue>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -171,14 +158,14 @@ impl RenderGraphPassDesc {
|
|||
id: u64,
|
||||
name: &str,
|
||||
attribute: SlotAttribute,
|
||||
desc: Option<SlotDescriptor>,
|
||||
value: Option<SlotValue>,
|
||||
) {
|
||||
debug_assert!(
|
||||
matches!(
|
||||
desc,
|
||||
None | Some(SlotDescriptor::Buffer(_)) | Some(SlotDescriptor::BufferInit(_))
|
||||
value,
|
||||
None | Some(SlotValue::Buffer(_))
|
||||
),
|
||||
"slot descriptor does not match the type of slot"
|
||||
"slot value is not a buffer"
|
||||
);
|
||||
|
||||
let slot = RenderPassSlot {
|
||||
|
@ -186,7 +173,7 @@ impl RenderGraphPassDesc {
|
|||
name: name.to_string(),
|
||||
ty: SlotType::Buffer,
|
||||
attribute,
|
||||
desc,
|
||||
value: value.map(|v| Rc::new(v)),
|
||||
};
|
||||
self.add_slot(slot);
|
||||
}
|
||||
|
@ -197,11 +184,14 @@ impl RenderGraphPassDesc {
|
|||
id: u64,
|
||||
name: &str,
|
||||
attribute: SlotAttribute,
|
||||
desc: Option<SlotDescriptor>,
|
||||
value: Option<SlotValue>,
|
||||
) {
|
||||
debug_assert!(
|
||||
matches!(desc, None | Some(SlotDescriptor::Texture(_))),
|
||||
"slot descriptor does not match the type of slot"
|
||||
matches!(
|
||||
value,
|
||||
None | Some(SlotValue::Texture(_))
|
||||
),
|
||||
"slot value is not a texture"
|
||||
);
|
||||
|
||||
let slot = RenderPassSlot {
|
||||
|
@ -209,7 +199,7 @@ impl RenderGraphPassDesc {
|
|||
name: name.to_string(),
|
||||
ty: SlotType::Texture,
|
||||
attribute,
|
||||
desc,
|
||||
value: value.map(|v| Rc::new(v)),
|
||||
};
|
||||
self.add_slot(slot);
|
||||
}
|
||||
|
@ -220,11 +210,14 @@ impl RenderGraphPassDesc {
|
|||
id: u64,
|
||||
name: &str,
|
||||
attribute: SlotAttribute,
|
||||
desc: Option<SlotDescriptor>,
|
||||
value: Option<SlotValue>,
|
||||
) {
|
||||
debug_assert!(
|
||||
matches!(desc, None | Some(SlotDescriptor::TextureView(_))),
|
||||
"slot descriptor does not match the type of slot"
|
||||
matches!(
|
||||
value,
|
||||
None | Some(SlotValue::TextureView(_))
|
||||
),
|
||||
"slot value is not a texture view"
|
||||
);
|
||||
|
||||
let slot = RenderPassSlot {
|
||||
|
@ -232,7 +225,7 @@ impl RenderGraphPassDesc {
|
|||
name: name.to_string(),
|
||||
ty: SlotType::TextureView,
|
||||
attribute,
|
||||
desc,
|
||||
value: value.map(|v| Rc::new(v)),
|
||||
};
|
||||
self.add_slot(slot);
|
||||
}
|
||||
|
@ -243,11 +236,14 @@ impl RenderGraphPassDesc {
|
|||
id: u64,
|
||||
name: &str,
|
||||
attribute: SlotAttribute,
|
||||
desc: Option<SlotDescriptor>,
|
||||
value: Option<SlotValue>,
|
||||
) {
|
||||
debug_assert!(
|
||||
matches!(desc, None | Some(SlotDescriptor::Sampler(_))),
|
||||
"slot descriptor does not match the type of slot"
|
||||
matches!(
|
||||
value,
|
||||
None | Some(SlotValue::Sampler(_))
|
||||
),
|
||||
"slot value is not a sampler"
|
||||
);
|
||||
|
||||
let slot = RenderPassSlot {
|
||||
|
@ -255,7 +251,7 @@ impl RenderGraphPassDesc {
|
|||
name: name.to_string(),
|
||||
ty: SlotType::Sampler,
|
||||
attribute,
|
||||
desc,
|
||||
value: value.map(|v| Rc::new(v)),
|
||||
};
|
||||
self.add_slot(slot);
|
||||
}
|
||||
|
@ -279,7 +275,7 @@ pub trait RenderGraphPass: 'static {
|
|||
/// Create a render pass describer.
|
||||
///
|
||||
/// 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 execute(
|
||||
|
|
|
@ -8,21 +8,26 @@ use crate::{
|
|||
render::{
|
||||
camera::{CameraUniform, RenderCamera},
|
||||
graph::{
|
||||
BufferInitDescriptor, PipelineShaderDesc, RenderGraphContext, RenderGraphPass,
|
||||
RenderGraphPassDesc, RenderGraphPipelineInfo, RenderPassType, SlotAttribute,
|
||||
SlotDescriptor,
|
||||
BufferDescriptor, BufferInitDescriptor, PipelineShaderDesc, RenderGraphContext,
|
||||
RenderGraphPass, RenderGraphPassDesc, RenderGraphPipelineInfo, RenderPassType,
|
||||
SlotAttribute, SlotValue,
|
||||
},
|
||||
render_buffer::BufferWrapper,
|
||||
renderer::ScreenSize,
|
||||
resource::{FragmentState, RenderPipelineDescriptor, Shader, VertexState},
|
||||
},
|
||||
scene::CameraComponent,
|
||||
scene::CameraComponent, DeltaTime,
|
||||
};
|
||||
|
||||
/// Supplies some basic things other passes needs.
|
||||
///
|
||||
/// screen size buffer, camera buffer,
|
||||
#[derive(Default)]
|
||||
pub struct TrianglePass;
|
||||
pub struct TrianglePass {
|
||||
color_bg: Option<wgpu::BindGroup>,
|
||||
color_buf: Option<Rc<wgpu::Buffer>>,
|
||||
acc: f32,
|
||||
}
|
||||
|
||||
impl TrianglePass {
|
||||
pub fn new() -> Self {
|
||||
|
@ -32,7 +37,7 @@ impl TrianglePass {
|
|||
|
||||
impl RenderGraphPass for TrianglePass {
|
||||
fn desc(
|
||||
&self,
|
||||
&mut self,
|
||||
graph: &mut crate::render::graph::RenderGraph,
|
||||
) -> crate::render::graph::RenderGraphPassDesc {
|
||||
let shader = Rc::new(Shader {
|
||||
|
@ -40,13 +45,25 @@ impl RenderGraphPass for TrianglePass {
|
|||
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(
|
||||
graph.next_id(),
|
||||
"TrianglePass",
|
||||
RenderPassType::Render,
|
||||
Some(RenderPipelineDescriptor {
|
||||
label: Some("triangle_pipeline".into()),
|
||||
layouts: vec![],
|
||||
layouts: vec![color_bgl],
|
||||
push_constant_ranges: vec![],
|
||||
vertex: VertexState {
|
||||
module: shader.clone(),
|
||||
|
@ -76,21 +93,28 @@ impl RenderGraphPass for TrianglePass {
|
|||
None,
|
||||
);
|
||||
|
||||
/* desc.add_buffer_slot(graph.next_id(), "screen_size_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor {
|
||||
label: Some("B_ScreenSize".to_string()),
|
||||
contents: bytemuck::bytes_of(&UVec2::new(800, 600)).to_vec(),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
})));
|
||||
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.add_buffer_slot(
|
||||
graph.next_id(),
|
||||
"color_buffer",
|
||||
SlotAttribute::Output,
|
||||
Some(SlotValue::Buffer(color_buf)),
|
||||
);
|
||||
|
||||
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(
|
||||
&mut self,
|
||||
|
@ -102,7 +126,12 @@ impl RenderGraphPass for TrianglePass {
|
|||
.slot_value(graph.slot_id("window_texture_view").unwrap())
|
||||
.unwrap()
|
||||
.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 {
|
||||
label: Some("TrianglePass"),
|
||||
|
@ -127,6 +156,7 @@ impl RenderGraphPass for TrianglePass {
|
|||
|
||||
let pipeline = graph.pipeline(desc.id);
|
||||
pass.set_pipeline(&pipeline.as_render());
|
||||
pass.set_bind_group(0, color_bg, &[]);
|
||||
pass.draw(0..3, 0..1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::num::{NonZeroU32, NonZeroU8};
|
||||
use std::{mem, num::{NonZeroU32, NonZeroU8}};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct TextureViewDescriptor {
|
||||
|
@ -162,6 +162,14 @@ pub struct 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> {
|
||||
wgpu::BufferDescriptor {
|
||||
label,
|
||||
|
@ -184,6 +192,14 @@ pub struct 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> {
|
||||
wgpu::util::BufferInitDescriptor {
|
||||
label,
|
||||
|
|
|
@ -134,6 +134,15 @@ impl BufferWrapper {
|
|||
"BufferWrapper is missing bindgroup pair! Cannot set bind group on RenderPass!",
|
||||
).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
|
||||
|
@ -221,7 +230,7 @@ impl BufferWrapperBuilder {
|
|||
/// * `contents` - The contents to initialize the buffer with.
|
||||
///
|
||||
/// 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 buffer = if let Some(contents) = self.contents.as_ref() {
|
||||
device.create_buffer_init(
|
||||
|
@ -293,10 +302,25 @@ impl BufferWrapperBuilder {
|
|||
}
|
||||
};
|
||||
|
||||
BufferWrapper {
|
||||
/* BufferWrapper {
|
||||
bindgroup_pair: Some(bg_pair),
|
||||
inner_buf: buffer,
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,7 +214,7 @@ impl BasicRenderer {
|
|||
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 mut g = RenderGraph::new(config.clone());
|
||||
let mut g = RenderGraph::new(device.clone(), queue.clone(), config.clone());
|
||||
/* debug!("Adding base pass");
|
||||
g.add_pass(TrianglePass::new());
|
||||
debug!("Adding depth pre-pass");
|
||||
|
@ -265,7 +265,7 @@ impl BasicRenderer {
|
|||
impl Renderer for BasicRenderer {
|
||||
#[instrument(skip(self, main_world))]
|
||||
fn prepare(&mut self, main_world: &mut World) {
|
||||
self.graph.prepare();
|
||||
self.graph.prepare(main_world);
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
|
|
|
@ -63,9 +63,7 @@ impl RenderPipelineDescriptor {
|
|||
label: None, //self.label.as_ref().map(|s| format!("{}Layout", s)),
|
||||
bind_group_layouts: &bgs,
|
||||
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> {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -2,6 +2,9 @@ struct VertexOutput {
|
|||
@builtin(position) clip_position: vec4<f32>,
|
||||
};
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> u_triangle_color: vec4<f32>;
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@builtin(vertex_index) in_vertex_index: u32,
|
||||
|
@ -15,5 +18,5 @@ fn vs_main(
|
|||
|
||||
@fragment
|
||||
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);
|
||||
}
|
Loading…
Reference in New Issue