Implement a Render Graph #16
|
@ -2,7 +2,6 @@ mod pass;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
ptr::NonNull,
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
@ -19,15 +18,11 @@ 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, trace};
|
use tracing::{debug_span, instrument, trace};
|
||||||
use wgpu::{util::DeviceExt, RenderPass};
|
|
||||||
|
|
||||||
use self::execution_path::GraphExecutionPath;
|
use self::execution_path::GraphExecutionPath;
|
||||||
|
|
||||||
use super::{
|
use super::resource::{Pipeline, RenderPipeline};
|
||||||
renderer::{BasicRenderer, Renderer},
|
|
||||||
resource::{Pipeline, RenderPipeline},
|
|
||||||
};
|
|
||||||
|
|
||||||
//#[derive(Clone)]
|
//#[derive(Clone)]
|
||||||
struct PassEntry {
|
struct PassEntry {
|
||||||
|
@ -35,6 +30,14 @@ struct PassEntry {
|
||||||
desc: Arc<RenderGraphPassDesc>,
|
desc: Arc<RenderGraphPassDesc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct BindGroupEntry {
|
||||||
|
pub name: String,
|
||||||
|
/// BindGroup
|
||||||
|
pub bg: Rc<wgpu::BindGroup>,
|
||||||
|
/// BindGroupLayout
|
||||||
|
pub layout: Option<Rc<wgpu::BindGroupLayout>>,
|
||||||
|
}
|
||||||
|
|
||||||
struct ResourcedSlot {
|
struct ResourcedSlot {
|
||||||
name: String,
|
name: String,
|
||||||
//slot: RenderPassSlot,
|
//slot: RenderPassSlot,
|
||||||
|
@ -58,7 +61,8 @@ pub struct RenderGraph {
|
||||||
slot_names: HashMap<String, u64>,
|
slot_names: HashMap<String, u64>,
|
||||||
passes: FxHashMap<u64, PassEntry>,
|
passes: FxHashMap<u64, PassEntry>,
|
||||||
// TODO: Use a SlotMap
|
// TODO: Use a SlotMap
|
||||||
bind_groups: FxHashMap<u64, wgpu::BindGroup>,
|
bind_groups: FxHashMap<u64, BindGroupEntry>,
|
||||||
|
bind_group_names: FxHashMap<String, u64>,
|
||||||
// TODO: make pipelines a `type` parameter in RenderPasses,
|
// TODO: make pipelines a `type` parameter in RenderPasses,
|
||||||
// then the pipelines can be retrieved via TypeId to the pass.
|
// then the pipelines can be retrieved via TypeId to the pass.
|
||||||
///
|
///
|
||||||
|
@ -70,7 +74,11 @@ pub struct RenderGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraph {
|
impl RenderGraph {
|
||||||
pub fn new(device: Rc<wgpu::Device>, queue: Rc<wgpu::Queue>, 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();
|
||||||
|
|
||||||
|
@ -92,6 +100,7 @@ impl RenderGraph {
|
||||||
slot_names,
|
slot_names,
|
||||||
passes: Default::default(),
|
passes: Default::default(),
|
||||||
bind_groups: Default::default(),
|
bind_groups: 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,
|
||||||
|
@ -129,19 +138,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;
|
|
||||||
|
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
slot.ty, other.ty,
|
slot.ty, other.ty,
|
||||||
"slot {} in pass {} does not match existing slot of same name",
|
"slot {} in pass {} does not match existing slot of same name",
|
||||||
slot.name, desc.name
|
slot.name, desc.name
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// if there is a slot of the same name
|
||||||
|
slot.id = *id;
|
||||||
} 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: slot.value.clone().unwrap_or(SlotValue::None),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.slots.insert(slot.id, res_slot);
|
self.slots.insert(slot.id, res_slot);
|
||||||
|
@ -149,6 +158,19 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (name, bg, bgl) in &desc.bind_groups {
|
||||||
|
let bg_id = self.next_id();
|
||||||
|
self.bind_groups.insert(
|
||||||
|
bg_id,
|
||||||
|
BindGroupEntry {
|
||||||
|
name: name.clone(),
|
||||||
|
bg: bg.clone(),
|
||||||
|
layout: bgl.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.bind_group_names.insert(name.clone(), bg_id);
|
||||||
|
}
|
||||||
|
|
||||||
self.passes.insert(
|
self.passes.insert(
|
||||||
desc.id,
|
desc.id,
|
||||||
PassEntry {
|
PassEntry {
|
||||||
|
@ -189,6 +211,28 @@ impl RenderGraph {
|
||||||
inner.prepare(world, &mut context);
|
inner.prepare(world, &mut context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Queue all buffer writes to the gpu
|
||||||
|
{
|
||||||
|
let s = debug_span!("queue_buffer_writes");
|
||||||
|
let _e = s.enter();
|
||||||
|
|
||||||
|
while let Some(bufwr) = context.buffer_writes.pop_front() {
|
||||||
|
let slot = self
|
||||||
|
.slots
|
||||||
|
.get(&self.slot_id(&bufwr.target_slot).unwrap())
|
||||||
|
.expect(&format!(
|
||||||
|
"Failed to find slot '{}' for buffer write",
|
||||||
|
bufwr.target_slot
|
||||||
|
));
|
||||||
|
let buf = slot
|
||||||
|
.value
|
||||||
|
.as_buffer()
|
||||||
|
.expect(&format!("Slot '{}' is not a buffer", bufwr.target_slot));
|
||||||
|
|
||||||
|
self.queue.write_buffer(buf, bufwr.offset, &bufwr.bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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 builtin = {
|
||||||
let mut h = FxHashSet::default();
|
let mut h = FxHashSet::default();
|
||||||
|
@ -197,7 +241,10 @@ impl RenderGraph {
|
||||||
};
|
};
|
||||||
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(builtin, descs);
|
||||||
trace!("Found {} steps in the rendergraph to execute", path.queue.len());
|
trace!(
|
||||||
|
"Found {} steps in the rendergraph to execute",
|
||||||
|
path.queue.len()
|
||||||
|
);
|
||||||
self.exec_path = Some(path);
|
self.exec_path = Some(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +263,7 @@ impl RenderGraph {
|
||||||
"window_texture_view",
|
"window_texture_view",
|
||||||
"unexpected slot where 'window_texture_view' should be"
|
"unexpected slot where 'window_texture_view' should be"
|
||||||
);
|
);
|
||||||
window_tv_slot.value = SlotValue::TextureView(view);
|
window_tv_slot.value = SlotValue::TextureView(Rc::new(view));
|
||||||
|
|
||||||
let mut encoders = vec![];
|
let mut encoders = vec![];
|
||||||
while let Some(pass_id) = path.queue.pop_front() {
|
while let Some(pass_id) = path.queue.pop_front() {
|
||||||
|
@ -246,37 +293,45 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn try_bind_group(&self, id: u64) -> Option<&wgpu::BindGroup> {
|
pub fn try_bind_group(&self, id: u64) -> Option<&Rc<wgpu::BindGroup>> {
|
||||||
self.bind_groups.get(&id)
|
self.bind_groups.get(&id).map(|e| &e.bg)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn bind_group(&self, id: u64) -> &wgpu::BindGroup {
|
pub fn bind_group(&self, id: u64) -> &Rc<wgpu::BindGroup> {
|
||||||
self.try_bind_group(id).expect("Unknown id for bind group")
|
self.try_bind_group(id).expect("Unknown id for bind group")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn try_slot_bind_group(&self, id: u64) -> Option<&wgpu::BindGroup> {
|
pub fn try_bind_group_layout(&self, id: u64) -> Option<&Rc<wgpu::BindGroupLayout>> {
|
||||||
todo!()
|
self.bind_groups.get(&id).and_then(|e| e.layout.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn slot_bind_group(&self, id: u64) -> &wgpu::BindGroup {
|
pub fn bind_group_layout(&self, id: u64) -> &Rc<wgpu::BindGroupLayout> {
|
||||||
todo!()
|
self.try_bind_group_layout(id)
|
||||||
|
.expect("Unknown id for bind group layout")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn bind_group_id(&self, name: &str) -> Option<u64> {
|
||||||
|
self.bind_group_names.get(name).copied()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct BufferWrite {
|
pub(crate) struct BufferWrite {
|
||||||
/// The name of the slot that has the resource that will be written
|
/// The name of the slot that has the resource that will be written
|
||||||
target_slot: String,
|
target_slot: String,
|
||||||
|
offset: u64,
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
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: Option<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: VecDeque<BufferWrite>,
|
||||||
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
|
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +340,7 @@ impl<'a> RenderGraphContext<'a> {
|
||||||
Self {
|
Self {
|
||||||
encoder,
|
encoder,
|
||||||
queue,
|
queue,
|
||||||
buffer_writes: vec![],
|
buffer_writes: Default::default(),
|
||||||
renderpass_desc: vec![],
|
renderpass_desc: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,28 +351,43 @@ impl<'a> RenderGraphContext<'a> {
|
||||||
) -> wgpu::RenderPass {
|
) -> wgpu::RenderPass {
|
||||||
self.encoder
|
self.encoder
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("RenderGraphContext is missing a command encoder. This is likely \
|
.expect(
|
||||||
because you are trying to run render commands in the prepare stage.")
|
"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)
|
.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
|
self.encoder
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("RenderGraphContext is missing a command encoder. This is likely \
|
.expect(
|
||||||
because you are trying to run render commands in the prepare stage.")
|
"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)
|
.begin_compute_pass(desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_buffer(&mut self, target_slot: &str, bytes: &[u8]) {
|
/// Queue a data write to a buffer at that is contained in `target_slot`.
|
||||||
//self.queue.write_buffer(buffer, offset, data)
|
///
|
||||||
self.buffer_writes.push(BufferWrite {
|
/// This does not submit the data to the GPU immediately, or add it to the `wgpu::Queue`. The
|
||||||
|
/// data will be submitted to the GPU queue right after the prepare stage for all passes
|
||||||
|
/// is ran.
|
||||||
|
pub fn queue_buffer_write(&mut self, target_slot: &str, offset: u64, bytes: &[u8]) {
|
||||||
|
self.buffer_writes.push_back(BufferWrite {
|
||||||
target_slot: target_slot.to_string(),
|
target_slot: target_slot.to_string(),
|
||||||
|
offset,
|
||||||
bytes: bytes.to_vec(),
|
bytes: bytes.to_vec(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_buffer_muck<T: bytemuck::NoUninit>(&mut self, target_slot: &str, bytes: T) {
|
/// Write
|
||||||
self.write_buffer(target_slot, bytemuck::bytes_of(&bytes));
|
pub fn queue_buffer_write_with<T: bytemuck::NoUninit>(
|
||||||
|
&mut self,
|
||||||
|
target_slot: &str,
|
||||||
|
offset: u64,
|
||||||
|
bytes: T,
|
||||||
|
) {
|
||||||
|
self.queue_buffer_write(target_slot, offset, bytemuck::bytes_of(&bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,7 @@ use lyra_ecs::World;
|
||||||
|
|
||||||
use crate::render::resource::RenderPipelineDescriptor;
|
use crate::render::resource::RenderPipelineDescriptor;
|
||||||
|
|
||||||
use super::{
|
use super::{RenderGraph, RenderGraphContext};
|
||||||
BufferDescriptor, BufferInitDescriptor, RenderGraph, RenderGraphContext, SamplerDescriptor,
|
|
||||||
TextureDescriptor, TextureViewDescriptor,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||||
pub enum RenderPassType {
|
pub enum RenderPassType {
|
||||||
|
@ -24,12 +21,12 @@ pub enum SlotType {
|
||||||
Buffer,
|
Buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SlotValue {
|
pub enum SlotValue {
|
||||||
None,
|
None,
|
||||||
TextureView(wgpu::TextureView),
|
TextureView(Rc<wgpu::TextureView>),
|
||||||
Sampler(wgpu::Sampler),
|
Sampler(Rc<wgpu::Sampler>),
|
||||||
Texture(wgpu::Texture),
|
Texture(Rc<wgpu::Texture>),
|
||||||
Buffer(Rc<wgpu::Buffer>),
|
Buffer(Rc<wgpu::Buffer>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,17 +34,24 @@ impl SlotValue {
|
||||||
/// Gets `self` as a texture, panics if self is not a instance of [`SlotValue::Texture`].
|
/// Gets `self` as a texture, panics if self is not a instance of [`SlotValue::Texture`].
|
||||||
pub fn as_texture(&self) -> &wgpu::Texture {
|
pub fn as_texture(&self) -> &wgpu::Texture {
|
||||||
match self {
|
match self {
|
||||||
Self::Texture(t) => t,
|
Self::Texture(v) => v,
|
||||||
_ => panic!("self is not an instance of SlotValue::Texture"),
|
_ => panic!("self is not an instance of SlotValue::Texture"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_texture_view(&self) -> &wgpu::TextureView {
|
pub fn as_texture_view(&self) -> &wgpu::TextureView {
|
||||||
match self {
|
match self {
|
||||||
Self::TextureView(t) => t,
|
Self::TextureView(v) => v,
|
||||||
_ => panic!("self is not an instance of SlotValue::TextureView"),
|
_ => panic!("self is not an instance of SlotValue::TextureView"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_buffer(&self) -> Option<&wgpu::Buffer> {
|
||||||
|
match self {
|
||||||
|
Self::Buffer(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum SlotAttribute {
|
pub enum SlotAttribute {
|
||||||
|
@ -63,7 +67,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 value: Option<Rc<SlotValue>>,
|
pub value: Option<SlotValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -128,6 +132,11 @@ pub struct RenderGraphPassDesc {
|
||||||
pub slots: Vec<RenderPassSlot>,
|
pub slots: Vec<RenderPassSlot>,
|
||||||
slot_names: HashMap<String, u64>,
|
slot_names: HashMap<String, u64>,
|
||||||
pub pipeline_desc: Option<RenderPipelineDescriptor>,
|
pub pipeline_desc: Option<RenderPipelineDescriptor>,
|
||||||
|
pub bind_groups: Vec<(
|
||||||
|
String,
|
||||||
|
Rc<wgpu::BindGroup>,
|
||||||
|
Option<Rc<wgpu::BindGroupLayout>>,
|
||||||
|
)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPassDesc {
|
impl RenderGraphPassDesc {
|
||||||
|
@ -136,6 +145,7 @@ impl RenderGraphPassDesc {
|
||||||
name: &str,
|
name: &str,
|
||||||
pass_type: RenderPassType,
|
pass_type: RenderPassType,
|
||||||
pipeline_desc: Option<RenderPipelineDescriptor>,
|
pipeline_desc: Option<RenderPipelineDescriptor>,
|
||||||
|
bind_groups: Vec<(&str, Rc<wgpu::BindGroup>, Option<Rc<wgpu::BindGroupLayout>>)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
|
@ -144,6 +154,10 @@ impl RenderGraphPassDesc {
|
||||||
slots: vec![],
|
slots: vec![],
|
||||||
slot_names: HashMap::default(),
|
slot_names: HashMap::default(),
|
||||||
pipeline_desc,
|
pipeline_desc,
|
||||||
|
bind_groups: bind_groups
|
||||||
|
.into_iter()
|
||||||
|
.map(|bg| (bg.0.to_string(), bg.1, bg.2))
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,10 +175,7 @@ impl RenderGraphPassDesc {
|
||||||
value: Option<SlotValue>,
|
value: Option<SlotValue>,
|
||||||
) {
|
) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
matches!(
|
matches!(value, None | Some(SlotValue::Buffer(_))),
|
||||||
value,
|
|
||||||
None | Some(SlotValue::Buffer(_))
|
|
||||||
),
|
|
||||||
"slot value is not a buffer"
|
"slot value is not a buffer"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -173,7 +184,7 @@ impl RenderGraphPassDesc {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
ty: SlotType::Buffer,
|
ty: SlotType::Buffer,
|
||||||
attribute,
|
attribute,
|
||||||
value: value.map(|v| Rc::new(v)),
|
value,
|
||||||
};
|
};
|
||||||
self.add_slot(slot);
|
self.add_slot(slot);
|
||||||
}
|
}
|
||||||
|
@ -187,10 +198,7 @@ impl RenderGraphPassDesc {
|
||||||
value: Option<SlotValue>,
|
value: Option<SlotValue>,
|
||||||
) {
|
) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
matches!(
|
matches!(value, None | Some(SlotValue::Texture(_))),
|
||||||
value,
|
|
||||||
None | Some(SlotValue::Texture(_))
|
|
||||||
),
|
|
||||||
"slot value is not a texture"
|
"slot value is not a texture"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -199,7 +207,7 @@ impl RenderGraphPassDesc {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
ty: SlotType::Texture,
|
ty: SlotType::Texture,
|
||||||
attribute,
|
attribute,
|
||||||
value: value.map(|v| Rc::new(v)),
|
value,
|
||||||
};
|
};
|
||||||
self.add_slot(slot);
|
self.add_slot(slot);
|
||||||
}
|
}
|
||||||
|
@ -213,10 +221,7 @@ impl RenderGraphPassDesc {
|
||||||
value: Option<SlotValue>,
|
value: Option<SlotValue>,
|
||||||
) {
|
) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
matches!(
|
matches!(value, None | Some(SlotValue::TextureView(_))),
|
||||||
value,
|
|
||||||
None | Some(SlotValue::TextureView(_))
|
|
||||||
),
|
|
||||||
"slot value is not a texture view"
|
"slot value is not a texture view"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -225,7 +230,7 @@ impl RenderGraphPassDesc {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
ty: SlotType::TextureView,
|
ty: SlotType::TextureView,
|
||||||
attribute,
|
attribute,
|
||||||
value: value.map(|v| Rc::new(v)),
|
value,
|
||||||
};
|
};
|
||||||
self.add_slot(slot);
|
self.add_slot(slot);
|
||||||
}
|
}
|
||||||
|
@ -239,10 +244,7 @@ impl RenderGraphPassDesc {
|
||||||
value: Option<SlotValue>,
|
value: Option<SlotValue>,
|
||||||
) {
|
) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
matches!(
|
matches!(value, None | Some(SlotValue::Sampler(_))),
|
||||||
value,
|
|
||||||
None | Some(SlotValue::Sampler(_))
|
|
||||||
),
|
|
||||||
"slot value is not a sampler"
|
"slot value is not a sampler"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -251,7 +253,7 @@ impl RenderGraphPassDesc {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
ty: SlotType::Sampler,
|
ty: SlotType::Sampler,
|
||||||
attribute,
|
attribute,
|
||||||
value: value.map(|v| Rc::new(v)),
|
value,
|
||||||
};
|
};
|
||||||
self.add_slot(slot);
|
self.add_slot(slot);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use glam::UVec2;
|
|
||||||
use tracing::warn;
|
|
||||||
use wgpu::include_wgsl;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render::{
|
render::{
|
||||||
camera::{CameraUniform, RenderCamera},
|
|
||||||
graph::{
|
graph::{
|
||||||
BufferDescriptor, BufferInitDescriptor, PipelineShaderDesc, RenderGraphContext,
|
RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType,
|
||||||
RenderGraphPass, RenderGraphPassDesc, RenderGraphPipelineInfo, RenderPassType,
|
|
||||||
SlotAttribute, SlotValue,
|
SlotAttribute, SlotValue,
|
||||||
},
|
},
|
||||||
render_buffer::BufferWrapper,
|
render_buffer::BufferWrapper,
|
||||||
renderer::ScreenSize,
|
|
||||||
resource::{FragmentState, RenderPipelineDescriptor, Shader, VertexState},
|
resource::{FragmentState, RenderPipelineDescriptor, Shader, VertexState},
|
||||||
},
|
},
|
||||||
scene::CameraComponent, DeltaTime,
|
DeltaTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Supplies some basic things other passes needs.
|
/// Supplies some basic things other passes needs.
|
||||||
|
@ -24,8 +17,8 @@ use crate::{
|
||||||
/// 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_bg: Option<Rc<wgpu::BindGroup>>,
|
||||||
color_buf: Option<Rc<wgpu::Buffer>>,
|
//color_buf: Option<Rc<wgpu::Buffer>>,
|
||||||
acc: f32,
|
acc: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,16 +39,16 @@ impl RenderGraphPass for TrianglePass {
|
||||||
});
|
});
|
||||||
|
|
||||||
let device = graph.device();
|
let device = graph.device();
|
||||||
|
|
||||||
let (color_bgl, color_bg, color_buf, _) = BufferWrapper::builder()
|
let (color_bgl, color_bg, color_buf, _) = BufferWrapper::builder()
|
||||||
.label_prefix("color")
|
.label_prefix("color")
|
||||||
.visibility(wgpu::ShaderStages::FRAGMENT)
|
.visibility(wgpu::ShaderStages::FRAGMENT)
|
||||||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||||
.contents(&[glam::Vec4::new(0.902, 0.639, 0.451, 1.0)])
|
.contents(&[glam::Vec4::new(0.902, 0.639, 0.451, 1.0)])
|
||||||
.finish_parts(device);
|
.finish_parts(device);
|
||||||
let color_buf = Rc::new(color_buf);
|
let color_bgl = Rc::new(color_bgl);
|
||||||
self.color_bg = Some(color_bg);
|
let color_bg = Rc::new(color_bg);
|
||||||
self.color_buf = Some(color_buf.clone());
|
|
||||||
|
//let color_buf = Rc::new(color_buf);
|
||||||
|
|
||||||
let mut desc = RenderGraphPassDesc::new(
|
let mut desc = RenderGraphPassDesc::new(
|
||||||
graph.next_id(),
|
graph.next_id(),
|
||||||
|
@ -63,7 +56,7 @@ impl RenderGraphPass for TrianglePass {
|
||||||
RenderPassType::Render,
|
RenderPassType::Render,
|
||||||
Some(RenderPipelineDescriptor {
|
Some(RenderPipelineDescriptor {
|
||||||
label: Some("triangle_pipeline".into()),
|
label: Some("triangle_pipeline".into()),
|
||||||
layouts: vec![color_bgl],
|
layouts: vec![color_bgl.clone()],
|
||||||
push_constant_ranges: vec![],
|
push_constant_ranges: vec![],
|
||||||
vertex: VertexState {
|
vertex: VertexState {
|
||||||
module: shader.clone(),
|
module: shader.clone(),
|
||||||
|
@ -84,6 +77,7 @@ impl RenderGraphPass for TrianglePass {
|
||||||
multisample: wgpu::MultisampleState::default(),
|
multisample: wgpu::MultisampleState::default(),
|
||||||
multiview: None,
|
multiview: None,
|
||||||
}),
|
}),
|
||||||
|
vec![("color_bg", color_bg, Some(color_bgl))],
|
||||||
);
|
);
|
||||||
|
|
||||||
desc.add_texture_view_slot(
|
desc.add_texture_view_slot(
|
||||||
|
@ -97,7 +91,7 @@ impl RenderGraphPass for TrianglePass {
|
||||||
graph.next_id(),
|
graph.next_id(),
|
||||||
"color_buffer",
|
"color_buffer",
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::Buffer(color_buf)),
|
Some(SlotValue::Buffer(Rc::new(color_buf))),
|
||||||
);
|
);
|
||||||
|
|
||||||
desc
|
desc
|
||||||
|
@ -113,7 +107,7 @@ impl RenderGraphPass for TrianglePass {
|
||||||
let z = ((self.acc + 5.35) * SPEED).sin();
|
let z = ((self.acc + 5.35) * SPEED).sin();
|
||||||
|
|
||||||
let color = glam::Vec4::new(x, y, z, 1.0);
|
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));
|
context.queue_buffer_write_with("color_buffer", 0, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(
|
fn execute(
|
||||||
|
@ -131,7 +125,8 @@ impl RenderGraphPass for TrianglePass {
|
||||||
//context.queue.write_buffer(buffer, offset, data)
|
//context.queue.write_buffer(buffer, offset, data)
|
||||||
|
|
||||||
//let color_bg = graph.bind_group(graph.slot_id("color_buffer").unwrap());
|
//let color_bg = graph.bind_group(graph.slot_id("color_buffer").unwrap());
|
||||||
let color_bg = self.color_bg.as_ref().unwrap();
|
//let color_bg = self.color_bg.as_ref().unwrap();
|
||||||
|
let color_bg = graph.bind_group(graph.bind_group_id("color_bg").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"),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{num::NonZeroU32, ops::Deref, rc::Rc};
|
use std::{num::NonZeroU32, ops::Deref, rc::Rc};
|
||||||
|
|
||||||
use wgpu::{BindGroupLayout, PipelineLayout};
|
use wgpu::PipelineLayout;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct VertexBufferLayout {
|
pub struct VertexBufferLayout {
|
||||||
|
@ -44,7 +44,7 @@ pub struct FragmentState {
|
||||||
//#[derive(Debug, Clone)]
|
//#[derive(Debug, Clone)]
|
||||||
pub struct RenderPipelineDescriptor {
|
pub struct RenderPipelineDescriptor {
|
||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
pub layouts: Vec<wgpu::BindGroupLayout>,
|
pub layouts: Vec<Rc<wgpu::BindGroupLayout>>,
|
||||||
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
|
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
|
||||||
pub vertex: VertexState,
|
pub vertex: VertexState,
|
||||||
pub fragment: Option<FragmentState>,
|
pub fragment: Option<FragmentState>,
|
||||||
|
@ -57,7 +57,11 @@ pub struct RenderPipelineDescriptor {
|
||||||
impl RenderPipelineDescriptor {
|
impl RenderPipelineDescriptor {
|
||||||
/// Create the [`wgpu::PipelineLayout`] for this pipeline
|
/// Create the [`wgpu::PipelineLayout`] for this pipeline
|
||||||
pub(crate) fn create_layout(&self, device: &wgpu::Device) -> wgpu::PipelineLayout {
|
pub(crate) fn create_layout(&self, device: &wgpu::Device) -> wgpu::PipelineLayout {
|
||||||
let bgs = self.layouts.iter().collect::<Vec<_>>();
|
let bgs = self
|
||||||
|
.layouts
|
||||||
|
.iter()
|
||||||
|
.map(|bg| bg.as_ref())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: None, //self.label.as_ref().map(|s| format!("{}Layout", s)),
|
label: None, //self.label.as_ref().map(|s| format!("{}Layout", s)),
|
||||||
|
@ -65,51 +69,6 @@ impl RenderPipelineDescriptor {
|
||||||
push_constant_ranges: &self.push_constant_ranges,
|
push_constant_ranges: &self.push_constant_ranges,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fn as_wgpu<'a>(&'a self, device: &wgpu::Device, layout: Option<&'a wgpu::PipelineLayout>) -> wgpu::RenderPipelineDescriptor<'a> {
|
|
||||||
let vbuffers = self.vertex.buffers.iter().map(|vbl| {
|
|
||||||
wgpu::VertexBufferLayout {
|
|
||||||
array_stride: vbl.array_stride,
|
|
||||||
step_mode: vbl.step_mode,
|
|
||||||
attributes: &vbl.attributes
|
|
||||||
}
|
|
||||||
}).collect::<Vec<_>>();
|
|
||||||
let vmodule = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
||||||
label: self.vertex.module.label.as_ref().map(|s| s.as_str()),
|
|
||||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&self.vertex.module.source)),
|
|
||||||
});
|
|
||||||
let vstate = wgpu::VertexState {
|
|
||||||
module: &vmodule,
|
|
||||||
entry_point: &self.vertex.entry_point,
|
|
||||||
buffers: &vbuffers,
|
|
||||||
};
|
|
||||||
|
|
||||||
let fmodule = self.fragment.as_ref().map(|f| {
|
|
||||||
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
||||||
label: f.module.label.as_ref().map(|s| s.as_str()),
|
|
||||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&f.module.source)),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
let fstate = self.fragment.as_ref().map(move |f| {
|
|
||||||
wgpu::FragmentState {
|
|
||||||
module: fmodule.as_ref().unwrap(),
|
|
||||||
entry_point: &f.entry_point,
|
|
||||||
targets: &f.targets,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
wgpu::RenderPipelineDescriptor {
|
|
||||||
label: self.label.as_deref(),
|
|
||||||
layout,
|
|
||||||
vertex: vstate,
|
|
||||||
primitive: self.primitive,
|
|
||||||
depth_stencil: self.depth_stencil,
|
|
||||||
multisample: self.multisample,
|
|
||||||
fragment: fstate,
|
|
||||||
multiview: self.multiview,
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RenderPipeline {
|
pub struct RenderPipeline {
|
||||||
|
|
Loading…
Reference in New Issue