Make it possible to create a complete pipeline descriptor for a pass
ci/woodpecker/pr/debug Pipeline failed Details
ci/woodpecker/pr/release Pipeline failed Details

This commit is contained in:
SeanOMik 2024-05-11 09:19:58 -04:00
parent bccf6287c0
commit b94a8e3cd3
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
13 changed files with 430 additions and 176 deletions

View File

@ -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>,
} }

View File

@ -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();
} }

View File

@ -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)]
@ -49,7 +54,7 @@ impl SlotValue {
pub enum SlotDescriptor { pub enum SlotDescriptor {
/// Most likely this slot is an input, so it doesn't need to specify the descriptor. /// Most likely this slot is an input, so it doesn't need to specify the descriptor.
None, None,
TextureView(TextureViewDescriptor), TextureView(TextureViewDescriptor),
Sampler(SamplerDescriptor), Sampler(SamplerDescriptor),
Texture(TextureDescriptor), Texture(TextureDescriptor),
@ -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,10 +192,18 @@ 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,
name: name.to_string(), name: name.to_string(),
@ -147,10 +215,18 @@ 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,
name: name.to_string(), name: name.to_string(),
@ -162,10 +238,18 @@ 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,
name: name.to_string(), name: name.to_string(),
@ -193,10 +277,15 @@ impl RenderGraphPassDesc {
pub trait RenderGraphPass: 'static { pub trait RenderGraphPass: 'static {
/// Create a render pass describer. /// Create a render pass describer.
/// ///
/// The `id` argument is passed as mutable so you can increment it as you use it for new slots. /// The `id` argument is passed as mutable so you can increment it as you use it for new slots.
fn desc<'a, 'b>(&'a self, graph: &'b mut RenderGraph) -> RenderGraphPassDesc; fn desc<'a, 'b>(&'a 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,
);
}

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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
}
}

View File

@ -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};

View File

@ -0,0 +1,8 @@
mod pipeline;
pub use pipeline::*;
mod compute_pipeline;
pub use compute_pipeline::*;
mod render_pipeline;
pub use render_pipeline::*;

View File

@ -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
}
}