Implement a Render Graph #16
|
@ -81,9 +81,8 @@ struct SlotOwnerPair {
|
||||||
slot: u64,
|
slot: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
struct Node<'a> {
|
||||||
struct Node {
|
|
||||||
id: u64,
|
id: u64,
|
||||||
desc: RenderGraphPassDesc,
|
desc: &'a RenderGraphPassDesc,
|
||||||
slot_inputs: Vec<SlotOwnerPair>,
|
slot_inputs: Vec<SlotOwnerPair>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,16 +23,13 @@ use wgpu::{util::DeviceExt, RenderPass};
|
||||||
use self::execution_path::GraphExecutionPath;
|
use self::execution_path::GraphExecutionPath;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
compute_pipeline::ComputePipeline,
|
renderer::{BasicRenderer, Renderer}, resource::{Pipeline, RenderPipeline},
|
||||||
pipeline::Pipeline,
|
|
||||||
render_pipeline::RenderPipeline,
|
|
||||||
renderer::{BasicRenderer, Renderer},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
//#[derive(Clone)]
|
||||||
struct PassEntry {
|
struct PassEntry {
|
||||||
inner: Arc<RefCell<dyn RenderGraphPass>>,
|
inner: Arc<RefCell<dyn RenderGraphPass>>,
|
||||||
desc: RenderGraphPassDesc,
|
desc: Arc<RenderGraphPassDesc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ResourcedSlot {
|
struct ResourcedSlot {
|
||||||
|
@ -115,7 +112,7 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pass(&self, id: u64) -> Option<&RenderGraphPassDesc> {
|
pub fn pass(&self, id: u64) -> Option<&RenderGraphPassDesc> {
|
||||||
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, pass: P) {
|
||||||
|
@ -150,7 +147,7 @@ impl RenderGraph {
|
||||||
desc.id,
|
desc.id,
|
||||||
PassEntry {
|
PassEntry {
|
||||||
inner: Arc::new(RefCell::new(pass)),
|
inner: Arc::new(RefCell::new(pass)),
|
||||||
desc,
|
desc: Arc::new(desc),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -162,14 +159,10 @@ impl RenderGraph {
|
||||||
|
|
||||||
// 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_info {
|
if let Some(pipei) = &pass.desc.pipeline_desc {
|
||||||
let pipeline = match pass.desc.pass_type {
|
let pipeline = match pass.desc.pass_type {
|
||||||
RenderPassType::Render => {
|
RenderPassType::Render => {
|
||||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
Pipeline::Render(RenderPipeline::create(device, pipei))
|
||||||
label: Some(pass.desc.name.as_str()),
|
|
||||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&pipei.source)),
|
|
||||||
});
|
|
||||||
Pipeline::Render(RenderPipeline::new(device, &self.surface_config, &shader, vec![], vec![]))
|
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
@ -274,7 +267,7 @@ impl RenderGraph {
|
||||||
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));
|
self.exec_path = Some(GraphExecutionPath::new(builtin, descs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,21 +288,25 @@ impl RenderGraph {
|
||||||
);
|
);
|
||||||
window_tv_slot.value = SlotValue::TextureView(view);
|
window_tv_slot.value = SlotValue::TextureView(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().clone();
|
let pass = self.passes.get(&pass_id).unwrap();
|
||||||
let label = format!("{} Encoder", pass.desc.name);
|
let pass_inn = pass.inner.clone();
|
||||||
|
let pass_desc = pass.desc.clone();
|
||||||
|
let label = format!("{} Encoder", pass_desc.name);
|
||||||
|
|
||||||
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(encoder, queue);
|
||||||
|
|
||||||
let mut inner = pass.inner.borrow_mut();
|
let mut inner = pass_inn.borrow_mut();
|
||||||
inner.execute(self, &pass.desc, &mut context);
|
inner.execute(self, &*pass_desc, &mut context);
|
||||||
|
|
||||||
queue.submit(std::iter::once(context.encoder.finish()));
|
encoders.push(context.encoder.finish());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queue.submit(encoders.into_iter());
|
||||||
output.present();
|
output.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, num::NonZeroU32, rc::Rc};
|
||||||
|
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
|
|
||||||
use super::{BufferDescriptor, BufferInitDescriptor, RenderGraph, RenderGraphContext, SamplerDescriptor, TextureDescriptor, TextureViewDescriptor};
|
use crate::render::resource::RenderPipelineDescriptor;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
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 {
|
||||||
Compute,
|
Compute,
|
||||||
#[default]
|
#[default]
|
||||||
Render
|
Render,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
@ -75,39 +80,83 @@ pub struct RenderPassSlot {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RenderGraphPipelineInfo {
|
pub struct PipelineShaderDesc {
|
||||||
pub label: String,
|
pub label: Option<String>,
|
||||||
pub source: String,
|
pub source: String,
|
||||||
|
pub entry_point: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RenderGraphPipelineInfo {
|
||||||
|
/// Debug label of the pipeline. This will show up in graphics debuggers for easy identification.
|
||||||
|
pub label: Option<String>,
|
||||||
|
/// The layout of bind groups for this pipeline.
|
||||||
|
pub bind_group_layouts: Vec<Rc<wgpu::BindGroupLayout>>,
|
||||||
|
/// The descriptor of the vertex shader.
|
||||||
|
pub vertex: PipelineShaderDesc,
|
||||||
|
/// The properties of the pipeline at the primitive assembly and rasterization level.
|
||||||
|
pub primitive: wgpu::PrimitiveState,
|
||||||
|
/// The effect of draw calls on the depth and stencil aspects of the output target, if any.
|
||||||
|
pub depth_stencil: Option<wgpu::DepthStencilState>,
|
||||||
|
/// The multi-sampling properties of the pipeline.
|
||||||
|
pub multisample: wgpu::MultisampleState,
|
||||||
|
/// The compiled fragment stage, its entry point, and the color targets.
|
||||||
|
pub fragment: Option<PipelineShaderDesc>,
|
||||||
|
/// If the pipeline will be used with a multiview render pass, this indicates how many array
|
||||||
|
/// layers the attachments will have.
|
||||||
|
pub multiview: Option<NonZeroU32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPipelineInfo {
|
impl RenderGraphPipelineInfo {
|
||||||
pub fn new(label: &str, source: &str) -> Self {
|
pub fn new(
|
||||||
|
label: &str,
|
||||||
|
bind_group_layouts: Vec<wgpu::BindGroupLayout>,
|
||||||
|
vertex: PipelineShaderDesc,
|
||||||
|
primitive: wgpu::PrimitiveState,
|
||||||
|
depth_stencil: Option<wgpu::DepthStencilState>,
|
||||||
|
multisample: wgpu::MultisampleState,
|
||||||
|
fragment: Option<PipelineShaderDesc>,
|
||||||
|
multiview: Option<NonZeroU32>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
label: label.to_string(),
|
label: Some(label.to_string()),
|
||||||
source: source.to_string(),
|
bind_group_layouts: bind_group_layouts
|
||||||
|
.into_iter()
|
||||||
|
.map(|bgl| Rc::new(bgl))
|
||||||
|
.collect(),
|
||||||
|
vertex,
|
||||||
|
primitive,
|
||||||
|
depth_stencil,
|
||||||
|
multisample,
|
||||||
|
fragment,
|
||||||
|
multiview,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RenderGraphPassDesc {
|
pub struct RenderGraphPassDesc {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub pass_type: RenderPassType,
|
pub pass_type: RenderPassType,
|
||||||
pub slots: Vec<RenderPassSlot>,
|
pub slots: Vec<RenderPassSlot>,
|
||||||
slot_names: HashMap<String, u64>,
|
slot_names: HashMap<String, u64>,
|
||||||
pub pipeline_info: Option<RenderGraphPipelineInfo>,
|
pub pipeline_desc: Option<RenderPipelineDescriptor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPassDesc {
|
impl RenderGraphPassDesc {
|
||||||
pub fn new(id: u64, name: &str, pass_type: RenderPassType, pipeline_info: Option<RenderGraphPipelineInfo>) -> Self {
|
pub fn new(
|
||||||
|
id: u64,
|
||||||
|
name: &str,
|
||||||
|
pass_type: RenderPassType,
|
||||||
|
pipeline_desc: Option<RenderPipelineDescriptor>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
pass_type,
|
pass_type,
|
||||||
slots: vec![],
|
slots: vec![],
|
||||||
slot_names: HashMap::default(),
|
slot_names: HashMap::default(),
|
||||||
pipeline_info
|
pipeline_desc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,9 +166,20 @@ impl RenderGraphPassDesc {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_buffer_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
pub fn add_buffer_slot(
|
||||||
debug_assert!(matches!(desc, None | Some(SlotDescriptor::Buffer(_)) | Some(SlotDescriptor::BufferInit(_)) ),
|
&mut self,
|
||||||
"slot descriptor does not match the type of slot");
|
id: u64,
|
||||||
|
name: &str,
|
||||||
|
attribute: SlotAttribute,
|
||||||
|
desc: Option<SlotDescriptor>,
|
||||||
|
) {
|
||||||
|
debug_assert!(
|
||||||
|
matches!(
|
||||||
|
desc,
|
||||||
|
None | Some(SlotDescriptor::Buffer(_)) | Some(SlotDescriptor::BufferInit(_))
|
||||||
|
),
|
||||||
|
"slot descriptor does not match the type of slot"
|
||||||
|
);
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = RenderPassSlot {
|
||||||
id,
|
id,
|
||||||
|
@ -132,9 +192,17 @@ impl RenderGraphPassDesc {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_texture_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
pub fn add_texture_slot(
|
||||||
debug_assert!(matches!(desc, None | Some(SlotDescriptor::Texture(_))),
|
&mut self,
|
||||||
"slot descriptor does not match the type of slot");
|
id: u64,
|
||||||
|
name: &str,
|
||||||
|
attribute: SlotAttribute,
|
||||||
|
desc: Option<SlotDescriptor>,
|
||||||
|
) {
|
||||||
|
debug_assert!(
|
||||||
|
matches!(desc, None | Some(SlotDescriptor::Texture(_))),
|
||||||
|
"slot descriptor does not match the type of slot"
|
||||||
|
);
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = RenderPassSlot {
|
||||||
id,
|
id,
|
||||||
|
@ -147,9 +215,17 @@ impl RenderGraphPassDesc {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_texture_view_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
pub fn add_texture_view_slot(
|
||||||
debug_assert!(matches!(desc, None | Some(SlotDescriptor::TextureView(_))),
|
&mut self,
|
||||||
"slot descriptor does not match the type of slot");
|
id: u64,
|
||||||
|
name: &str,
|
||||||
|
attribute: SlotAttribute,
|
||||||
|
desc: Option<SlotDescriptor>,
|
||||||
|
) {
|
||||||
|
debug_assert!(
|
||||||
|
matches!(desc, None | Some(SlotDescriptor::TextureView(_))),
|
||||||
|
"slot descriptor does not match the type of slot"
|
||||||
|
);
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = RenderPassSlot {
|
||||||
id,
|
id,
|
||||||
|
@ -162,9 +238,17 @@ impl RenderGraphPassDesc {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_sampler_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
pub fn add_sampler_slot(
|
||||||
debug_assert!(matches!(desc, None | Some(SlotDescriptor::Sampler(_))),
|
&mut self,
|
||||||
"slot descriptor does not match the type of slot");
|
id: u64,
|
||||||
|
name: &str,
|
||||||
|
attribute: SlotAttribute,
|
||||||
|
desc: Option<SlotDescriptor>,
|
||||||
|
) {
|
||||||
|
debug_assert!(
|
||||||
|
matches!(desc, None | Some(SlotDescriptor::Sampler(_))),
|
||||||
|
"slot descriptor does not match the type of slot"
|
||||||
|
);
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = RenderPassSlot {
|
||||||
id,
|
id,
|
||||||
|
@ -198,5 +282,10 @@ pub trait RenderGraphPass: 'static {
|
||||||
fn desc<'a, 'b>(&'a self, graph: &'b mut RenderGraph) -> RenderGraphPassDesc;
|
fn desc<'a, 'b>(&'a 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(&mut self, graph: &mut RenderGraph, desc: &RenderGraphPassDesc, context: &mut RenderGraphContext);
|
fn execute(
|
||||||
|
&mut self,
|
||||||
|
graph: &mut RenderGraph,
|
||||||
|
desc: &RenderGraphPassDesc,
|
||||||
|
context: &mut RenderGraphContext,
|
||||||
|
);
|
||||||
}
|
}
|
|
@ -5,9 +5,9 @@ mod base;
|
||||||
pub use base::*;
|
pub use base::*;
|
||||||
|
|
||||||
mod depth_prepass;
|
mod depth_prepass;
|
||||||
pub use depth_prepass::*;
|
pub use depth_prepass::*; */
|
||||||
|
|
||||||
mod simple_phong;
|
/* mod simple_phong;
|
||||||
pub use simple_phong::*; */
|
pub use simple_phong::*; */
|
||||||
|
|
||||||
mod triangle;
|
mod triangle;
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use glam::UVec2;
|
use glam::UVec2;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
use wgpu::include_wgsl;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render::{
|
render::{
|
||||||
camera::{CameraUniform, RenderCamera},
|
camera::{CameraUniform, RenderCamera},
|
||||||
graph::{
|
graph::{
|
||||||
BufferInitDescriptor, RenderGraphContext, RenderGraphPass, RenderGraphPassDesc,
|
BufferInitDescriptor, PipelineShaderDesc, RenderGraphContext, RenderGraphPass,
|
||||||
RenderGraphPipelineInfo, RenderPassType, SlotAttribute, SlotDescriptor,
|
RenderGraphPassDesc, RenderGraphPipelineInfo, RenderPassType, SlotAttribute,
|
||||||
|
SlotDescriptor,
|
||||||
},
|
},
|
||||||
renderer::ScreenSize,
|
renderer::ScreenSize,
|
||||||
|
resource::{FragmentState, RenderPipelineDescriptor, Shader, VertexState},
|
||||||
},
|
},
|
||||||
scene::CameraComponent,
|
scene::CameraComponent,
|
||||||
};
|
};
|
||||||
|
@ -30,17 +35,46 @@ impl RenderGraphPass for TrianglePass {
|
||||||
&self,
|
&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 {
|
||||||
|
label: Some("triangle_shader".into()),
|
||||||
|
source: include_str!("../../shaders/triangle.wgsl").to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
let mut desc = RenderGraphPassDesc::new(
|
let mut desc = RenderGraphPassDesc::new(
|
||||||
graph.next_id(),
|
graph.next_id(),
|
||||||
"TrianglePass",
|
"TrianglePass",
|
||||||
RenderPassType::Render,
|
RenderPassType::Render,
|
||||||
Some(RenderGraphPipelineInfo::new(
|
Some(RenderPipelineDescriptor {
|
||||||
"TriangleShader",
|
label: Some("triangle_pipeline".into()),
|
||||||
include_str!("../../shaders/triangle.wgsl"),
|
layouts: vec![],
|
||||||
)),
|
push_constant_ranges: vec![],
|
||||||
|
vertex: VertexState {
|
||||||
|
module: shader.clone(),
|
||||||
|
entry_point: "vs_main".into(),
|
||||||
|
buffers: vec![],
|
||||||
|
},
|
||||||
|
fragment: Some(FragmentState {
|
||||||
|
module: shader,
|
||||||
|
entry_point: "fs_main".into(),
|
||||||
|
targets: vec![Some(wgpu::ColorTargetState {
|
||||||
|
format: graph.surface_config.format,
|
||||||
|
blend: Some(wgpu::BlendState::REPLACE),
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
depth_stencil: None,
|
||||||
|
primitive: wgpu::PrimitiveState::default(),
|
||||||
|
multisample: wgpu::MultisampleState::default(),
|
||||||
|
multiview: None,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
desc.add_texture_view_slot(graph.next_id(), "window_texture_view", SlotAttribute::Input, None);
|
desc.add_texture_view_slot(
|
||||||
|
graph.next_id(),
|
||||||
|
"window_texture_view",
|
||||||
|
SlotAttribute::Input,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
/* desc.add_buffer_slot(graph.next_id(), "screen_size_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor {
|
/* desc.add_buffer_slot(graph.next_id(), "screen_size_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor {
|
||||||
label: Some("B_ScreenSize".to_string()),
|
label: Some("B_ScreenSize".to_string()),
|
||||||
|
@ -64,7 +98,10 @@ impl RenderGraphPass for TrianglePass {
|
||||||
desc: &crate::render::graph::RenderGraphPassDesc,
|
desc: &crate::render::graph::RenderGraphPassDesc,
|
||||||
context: &mut crate::render::graph::RenderGraphContext,
|
context: &mut crate::render::graph::RenderGraphContext,
|
||||||
) {
|
) {
|
||||||
let view = graph.slot_value(graph.slot_id("window_texture_view").unwrap()).unwrap().as_texture_view();
|
let view = graph
|
||||||
|
.slot_value(graph.slot_id("window_texture_view").unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.as_texture_view();
|
||||||
let encoder = &mut context.encoder;
|
let encoder = &mut context.encoder;
|
||||||
|
|
||||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
pub mod render_pipeline;
|
pub mod resource;
|
||||||
pub mod compute_pipeline;
|
|
||||||
pub mod pipeline;
|
|
||||||
pub mod vertex;
|
pub mod vertex;
|
||||||
pub mod desc_buf_lay;
|
pub mod desc_buf_lay;
|
||||||
pub mod render_buffer;
|
pub mod render_buffer;
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
use std::{collections::HashMap, ops::Deref};
|
|
||||||
|
|
||||||
use wgpu::{PipelineLayout, VertexBufferLayout, BindGroupLayout};
|
|
||||||
|
|
||||||
use super::texture::RenderTexture;
|
|
||||||
|
|
||||||
pub struct RenderPipeline {
|
|
||||||
layout: PipelineLayout,
|
|
||||||
wgpu_pipeline: wgpu::RenderPipeline,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for RenderPipeline {
|
|
||||||
type Target = wgpu::RenderPipeline;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.wgpu_pipeline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderPipeline {
|
|
||||||
/// Creates the default render pipeline
|
|
||||||
pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, shader: &wgpu::ShaderModule, buffer_layouts: Vec<VertexBufferLayout>, bind_group_layouts: Vec<&BindGroupLayout>) -> RenderPipeline {
|
|
||||||
let render_pipeline_layout =
|
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: Some("Render Pipeline Layout"),
|
|
||||||
bind_group_layouts: &bind_group_layouts,
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: Some("Render Pipeline"),
|
|
||||||
layout: Some(&render_pipeline_layout),
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: shader,
|
|
||||||
entry_point: "vs_main",
|
|
||||||
buffers: &buffer_layouts,
|
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: shader,
|
|
||||||
entry_point: "fs_main",
|
|
||||||
targets: &[Some(wgpu::ColorTargetState {
|
|
||||||
format: config.format,
|
|
||||||
blend: Some(wgpu::BlendState::REPLACE),
|
|
||||||
write_mask: wgpu::ColorWrites::ALL,
|
|
||||||
})],
|
|
||||||
}),
|
|
||||||
primitive: wgpu::PrimitiveState {
|
|
||||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
|
||||||
strip_index_format: None,
|
|
||||||
front_face: wgpu::FrontFace::Ccw,
|
|
||||||
cull_mode: Some(wgpu::Face::Back),
|
|
||||||
// Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
|
|
||||||
polygon_mode: wgpu::PolygonMode::Fill,
|
|
||||||
// Requires Features::DEPTH_CLIP_CONTROL
|
|
||||||
unclipped_depth: false,
|
|
||||||
// Requires Features::CONSERVATIVE_RASTERIZATION
|
|
||||||
conservative: false,
|
|
||||||
},
|
|
||||||
/*depth_stencil: Some(wgpu::DepthStencilState {
|
|
||||||
format: RenderTexture::DEPTH_FORMAT,
|
|
||||||
depth_write_enabled: true,
|
|
||||||
depth_compare: wgpu::CompareFunction::Less,
|
|
||||||
stencil: wgpu::StencilState::default(), // TODO: stencil buffer
|
|
||||||
bias: wgpu::DepthBiasState::default(),
|
|
||||||
}),*/
|
|
||||||
depth_stencil: None,
|
|
||||||
multisample: wgpu::MultisampleState {
|
|
||||||
count: 1,
|
|
||||||
mask: !0,
|
|
||||||
alpha_to_coverage_enabled: false,
|
|
||||||
},
|
|
||||||
multiview: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
layout: render_pipeline_layout,
|
|
||||||
wgpu_pipeline: render_pipeline,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn layout(&self) -> &PipelineLayout {
|
|
||||||
&self.layout
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn wgpu_pipeline(&self) -> &wgpu::RenderPipeline {
|
|
||||||
&self.wgpu_pipeline
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +1,29 @@
|
||||||
use std::collections::{VecDeque, HashSet};
|
use std::collections::VecDeque;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use glam::Vec3;
|
use lyra_ecs::Component;
|
||||||
use itertools::izip;
|
|
||||||
use lyra_ecs::query::filter::{Has, Not, Or};
|
|
||||||
use lyra_ecs::relation::{ChildOf, RelationOriginComponent};
|
|
||||||
use lyra_ecs::{Component, Entity};
|
|
||||||
use lyra_ecs::query::{Entities, Res, TickOf};
|
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
use lyra_scene::{SceneGraph, WorldTransform};
|
use lyra_scene::SceneGraph;
|
||||||
use tracing::{debug, instrument, warn};
|
use tracing::{debug, instrument, warn};
|
||||||
use uuid::Uuid;
|
use wgpu::Limits;
|
||||||
use wgpu::{BindGroupLayout, Limits};
|
|
||||||
use wgpu::util::DeviceExt;
|
|
||||||
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::TrianglePass;
|
||||||
use crate::render::material::MaterialUniform;
|
use crate::render::material::MaterialUniform;
|
||||||
use crate::render::render_buffer::BufferWrapperBuilder;
|
use crate::render::render_buffer::BufferWrapperBuilder;
|
||||||
use crate::scene::CameraComponent;
|
|
||||||
use crate::DeltaTime;
|
|
||||||
|
|
||||||
use super::camera::{RenderCamera, CameraUniform};
|
use super::camera::CameraUniform;
|
||||||
use super::desc_buf_lay::DescVertexBufferLayout;
|
|
||||||
use super::graph::RenderGraph;
|
use super::graph::RenderGraph;
|
||||||
use super::light::LightUniformBuffers;
|
use super::light::LightUniformBuffers;
|
||||||
use super::light_cull_compute::LightCullCompute;
|
|
||||||
use super::material::Material;
|
use super::material::Material;
|
||||||
use super::render_buffer::BufferWrapper;
|
use super::render_buffer::BufferWrapper;
|
||||||
use super::texture::RenderTexture;
|
use super::texture::RenderTexture;
|
||||||
use super::transform_buffer_storage::{TransformBuffers, TransformGroup};
|
use super::transform_buffer_storage::TransformBuffers;
|
||||||
use super::vertex::Vertex;
|
use super::{resource::RenderPipeline, render_buffer::BufferStorage, render_job::RenderJob};
|
||||||
use super::{render_pipeline::RenderPipeline, render_buffer::BufferStorage, render_job::RenderJob};
|
|
||||||
|
|
||||||
use lyra_resource::{gltf::Mesh, ResHandle};
|
use lyra_resource::{gltf::Mesh, ResHandle};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
mod pipeline;
|
||||||
|
pub use pipeline::*;
|
||||||
|
|
||||||
|
mod compute_pipeline;
|
||||||
|
pub use compute_pipeline::*;
|
||||||
|
|
||||||
|
mod render_pipeline;
|
||||||
|
pub use render_pipeline::*;
|
|
@ -0,0 +1,228 @@
|
||||||
|
use std::{num::NonZeroU32, ops::Deref, rc::Rc};
|
||||||
|
|
||||||
|
use wgpu::{BindGroupLayout, PipelineLayout};
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct VertexBufferLayout {
|
||||||
|
pub array_stride: wgpu::BufferAddress,
|
||||||
|
pub step_mode: wgpu::VertexStepMode,
|
||||||
|
pub attributes: Vec<wgpu::VertexAttribute>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes the vertex stage in a render pipeline.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct VertexState {
|
||||||
|
// TODO: make this a ResHandle<Shader>
|
||||||
|
/// The compiled shader module for the stage.
|
||||||
|
pub module: Rc<Shader>,
|
||||||
|
/// The entry point in the compiled shader.
|
||||||
|
/// There must be a function in the shader with the same name.
|
||||||
|
pub entry_point: String,
|
||||||
|
/// The format of the vertex buffers used with this pipeline.
|
||||||
|
pub buffers: Vec<VertexBufferLayout>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Shader {
|
||||||
|
pub label: Option<String>,
|
||||||
|
pub source: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes the fragment stage in the render pipeline.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FragmentState {
|
||||||
|
// TODO: make this a ResHandle<Shader>
|
||||||
|
/// The compiled shader module for the stage.
|
||||||
|
pub module: Rc<Shader>,
|
||||||
|
/// The entry point in the compiled shader.
|
||||||
|
/// There must be a function in the shader with the same name.
|
||||||
|
pub entry_point: String,
|
||||||
|
/// The color state of the render targets.
|
||||||
|
pub targets: Vec<Option<wgpu::ColorTargetState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#[derive(Debug, Clone)]
|
||||||
|
pub struct RenderPipelineDescriptor {
|
||||||
|
pub label: Option<String>,
|
||||||
|
pub layouts: Vec<wgpu::BindGroupLayout>,
|
||||||
|
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
|
||||||
|
pub vertex: VertexState,
|
||||||
|
pub fragment: Option<FragmentState>,
|
||||||
|
pub primitive: wgpu::PrimitiveState,
|
||||||
|
pub depth_stencil: Option<wgpu::DepthStencilState>,
|
||||||
|
pub multisample: wgpu::MultisampleState,
|
||||||
|
pub multiview: Option<NonZeroU32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderPipelineDescriptor {
|
||||||
|
/// Create the [`wgpu::PipelineLayout`] for this pipeline
|
||||||
|
pub(crate) fn create_layout(&self, device: &wgpu::Device) -> wgpu::PipelineLayout {
|
||||||
|
let bgs = self.layouts.iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
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> {
|
||||||
|
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 {
|
||||||
|
layout: Option<PipelineLayout>,
|
||||||
|
wgpu_pipeline: wgpu::RenderPipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for RenderPipeline {
|
||||||
|
type Target = wgpu::RenderPipeline;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.wgpu_pipeline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<wgpu::RenderPipeline> for RenderPipeline {
|
||||||
|
fn from(value: wgpu::RenderPipeline) -> Self {
|
||||||
|
Self {
|
||||||
|
layout: None,
|
||||||
|
wgpu_pipeline: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderPipeline {
|
||||||
|
/// Creates the default render pipeline
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// * `device` - The device to create the pipeline on.
|
||||||
|
/// * `config` - The surface config to use to create the pipeline.
|
||||||
|
/// * `label` - The label of the pipeline.
|
||||||
|
/// * `shader` - The compiled shader of the pipeline.
|
||||||
|
/// * `vertex_entry_point` - The entry point name of the vertex shader
|
||||||
|
pub fn create(device: &wgpu::Device, desc: &RenderPipelineDescriptor) -> RenderPipeline {
|
||||||
|
// create the layout only if bind groups layouts were specified
|
||||||
|
let layout = if !desc.layouts.is_empty() {
|
||||||
|
Some(desc.create_layout(device))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let vrtx_buffs = desc
|
||||||
|
.vertex
|
||||||
|
.buffers
|
||||||
|
.iter()
|
||||||
|
.map(|vbl| wgpu::VertexBufferLayout {
|
||||||
|
array_stride: vbl.array_stride,
|
||||||
|
step_mode: vbl.step_mode,
|
||||||
|
attributes: &vbl.attributes,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// an Rc was used here so that this shader could be reused by the fragment stage if
|
||||||
|
// they share the same shader. I tried to do it without an Rc but couldn't get past
|
||||||
|
// the borrow checker
|
||||||
|
let vrtx_shad = Rc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: desc.vertex.module.label.as_ref().map(|s| s.as_str()),
|
||||||
|
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
|
||||||
|
&desc.vertex.module.source,
|
||||||
|
)),
|
||||||
|
}));
|
||||||
|
let vrtx_state = wgpu::VertexState {
|
||||||
|
module: &*vrtx_shad,
|
||||||
|
entry_point: &desc.vertex.entry_point,
|
||||||
|
buffers: &vrtx_buffs,
|
||||||
|
};
|
||||||
|
|
||||||
|
let frag_module = desc.fragment.as_ref().map(|f| {
|
||||||
|
if f.module == desc.vertex.module {
|
||||||
|
vrtx_shad.clone()
|
||||||
|
} else {
|
||||||
|
Rc::new(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 fm = frag_module.as_ref();
|
||||||
|
let fstate = desc.fragment.as_ref().map(move |f| wgpu::FragmentState {
|
||||||
|
module: fm.unwrap(),
|
||||||
|
entry_point: &f.entry_point,
|
||||||
|
targets: &f.targets,
|
||||||
|
});
|
||||||
|
|
||||||
|
let render_desc = wgpu::RenderPipelineDescriptor {
|
||||||
|
label: desc.label.as_deref(),
|
||||||
|
layout: layout.as_ref(),
|
||||||
|
vertex: vrtx_state,
|
||||||
|
primitive: desc.primitive,
|
||||||
|
depth_stencil: desc.depth_stencil.clone(),
|
||||||
|
multisample: desc.multisample,
|
||||||
|
fragment: fstate,
|
||||||
|
multiview: desc.multiview,
|
||||||
|
};
|
||||||
|
|
||||||
|
let render_pipeline = device.create_render_pipeline(&render_desc);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
layout,
|
||||||
|
wgpu_pipeline: render_pipeline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn layout(&self) -> Option<&PipelineLayout> {
|
||||||
|
self.layout.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn wgpu_pipeline(&self) -> &wgpu::RenderPipeline {
|
||||||
|
&self.wgpu_pipeline
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue