Implement a Render Graph #16

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

View File

@ -81,9 +81,8 @@ struct SlotOwnerPair {
slot: u64,
}
#[derive(Clone)]
struct Node {
struct Node<'a> {
id: u64,
desc: RenderGraphPassDesc,
desc: &'a RenderGraphPassDesc,
slot_inputs: Vec<SlotOwnerPair>,
}

View File

@ -23,16 +23,13 @@ use wgpu::{util::DeviceExt, RenderPass};
use self::execution_path::GraphExecutionPath;
use super::{
compute_pipeline::ComputePipeline,
pipeline::Pipeline,
render_pipeline::RenderPipeline,
renderer::{BasicRenderer, Renderer},
renderer::{BasicRenderer, Renderer}, resource::{Pipeline, RenderPipeline},
};
#[derive(Clone)]
//#[derive(Clone)]
struct PassEntry {
inner: Arc<RefCell<dyn RenderGraphPass>>,
desc: RenderGraphPassDesc,
desc: Arc<RenderGraphPassDesc>,
}
struct ResourcedSlot {
@ -115,7 +112,7 @@ impl RenderGraph {
}
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) {
@ -150,7 +147,7 @@ impl RenderGraph {
desc.id,
PassEntry {
inner: Arc::new(RefCell::new(pass)),
desc,
desc: Arc::new(desc),
},
);
}
@ -162,14 +159,10 @@ impl RenderGraph {
// For all passes, create their pipelines
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 {
RenderPassType::Render => {
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
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![]))
Pipeline::Render(RenderPipeline::create(device, pipei))
}
_ => todo!(),
};
@ -274,7 +267,7 @@ impl RenderGraph {
h.insert(0u64);
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));
}
@ -295,21 +288,25 @@ impl RenderGraph {
);
window_tv_slot.value = SlotValue::TextureView(view);
let mut encoders = vec![];
while let Some(pass_id) = path.queue.pop_front() {
let pass = self.passes.get(&pass_id).unwrap().clone();
let label = format!("{} Encoder", pass.desc.name);
let pass = self.passes.get(&pass_id).unwrap();
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 {
label: Some(&label),
});
let mut context = RenderGraphContext::new(encoder, queue);
let mut inner = pass.inner.borrow_mut();
inner.execute(self, &pass.desc, &mut context);
let mut inner = pass_inn.borrow_mut();
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();
}

View File

@ -1,14 +1,19 @@
use std::collections::HashMap;
use std::{collections::HashMap, num::NonZeroU32, rc::Rc};
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)]
pub enum RenderPassType {
Compute,
#[default]
Render
Render,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@ -75,39 +80,83 @@ pub struct RenderPassSlot {
}
#[derive(Clone)]
pub struct RenderGraphPipelineInfo {
pub label: String,
pub struct PipelineShaderDesc {
pub label: Option<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 {
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 {
label: label.to_string(),
source: source.to_string(),
label: Some(label.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 id: u64,
pub name: String,
pub pass_type: RenderPassType,
pub slots: Vec<RenderPassSlot>,
slot_names: HashMap<String, u64>,
pub pipeline_info: Option<RenderGraphPipelineInfo>,
pub pipeline_desc: Option<RenderPipelineDescriptor>,
}
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 {
id,
name: name.to_string(),
pass_type,
slots: vec![],
slot_names: HashMap::default(),
pipeline_info
pipeline_desc,
}
}
@ -117,9 +166,20 @@ impl RenderGraphPassDesc {
}
#[inline(always)]
pub fn add_buffer_slot(&mut self, 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");
pub fn add_buffer_slot(
&mut self,
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 {
id,
@ -132,9 +192,17 @@ impl RenderGraphPassDesc {
}
#[inline(always)]
pub fn add_texture_slot(&mut self, 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");
pub fn add_texture_slot(
&mut self,
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 {
id,
@ -147,9 +215,17 @@ impl RenderGraphPassDesc {
}
#[inline(always)]
pub fn add_texture_view_slot(&mut self, 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");
pub fn add_texture_view_slot(
&mut self,
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 {
id,
@ -162,9 +238,17 @@ impl RenderGraphPassDesc {
}
#[inline(always)]
pub fn add_sampler_slot(&mut self, 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");
pub fn add_sampler_slot(
&mut self,
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 {
id,
@ -198,5 +282,10 @@ pub trait RenderGraphPass: 'static {
fn desc<'a, 'b>(&'a self, graph: &'b mut RenderGraph) -> RenderGraphPassDesc;
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::*;
mod depth_prepass;
pub use depth_prepass::*;
pub use depth_prepass::*; */
mod simple_phong;
/* mod simple_phong;
pub use simple_phong::*; */
mod triangle;

View File

@ -1,14 +1,19 @@
use std::rc::Rc;
use glam::UVec2;
use tracing::warn;
use wgpu::include_wgsl;
use crate::{
render::{
camera::{CameraUniform, RenderCamera},
graph::{
BufferInitDescriptor, RenderGraphContext, RenderGraphPass, RenderGraphPassDesc,
RenderGraphPipelineInfo, RenderPassType, SlotAttribute, SlotDescriptor,
BufferInitDescriptor, PipelineShaderDesc, RenderGraphContext, RenderGraphPass,
RenderGraphPassDesc, RenderGraphPipelineInfo, RenderPassType, SlotAttribute,
SlotDescriptor,
},
renderer::ScreenSize,
resource::{FragmentState, RenderPipelineDescriptor, Shader, VertexState},
},
scene::CameraComponent,
};
@ -30,17 +35,46 @@ impl RenderGraphPass for TrianglePass {
&self,
graph: &mut crate::render::graph::RenderGraph,
) -> 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(
graph.next_id(),
"TrianglePass",
RenderPassType::Render,
Some(RenderGraphPipelineInfo::new(
"TriangleShader",
include_str!("../../shaders/triangle.wgsl"),
)),
Some(RenderPipelineDescriptor {
label: Some("triangle_pipeline".into()),
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 {
label: Some("B_ScreenSize".to_string()),
@ -64,7 +98,10 @@ impl RenderGraphPass for TrianglePass {
desc: &crate::render::graph::RenderGraphPassDesc,
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 mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {

View File

@ -1,7 +1,5 @@
pub mod renderer;
pub mod render_pipeline;
pub mod compute_pipeline;
pub mod pipeline;
pub mod resource;
pub mod vertex;
pub mod desc_buf_lay;
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::rc::Rc;
use std::sync::Arc;
use std::borrow::Cow;
use glam::Vec3;
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::Component;
use lyra_ecs::World;
use lyra_scene::{SceneGraph, WorldTransform};
use lyra_scene::SceneGraph;
use tracing::{debug, instrument, warn};
use uuid::Uuid;
use wgpu::{BindGroupLayout, Limits};
use wgpu::util::DeviceExt;
use wgpu::Limits;
use winit::window::Window;
use crate::math::Transform;
use crate::render::graph::TrianglePass;
use crate::render::material::MaterialUniform;
use crate::render::render_buffer::BufferWrapperBuilder;
use crate::scene::CameraComponent;
use crate::DeltaTime;
use super::camera::{RenderCamera, CameraUniform};
use super::desc_buf_lay::DescVertexBufferLayout;
use super::camera::CameraUniform;
use super::graph::RenderGraph;
use super::light::LightUniformBuffers;
use super::light_cull_compute::LightCullCompute;
use super::material::Material;
use super::render_buffer::BufferWrapper;
use super::texture::RenderTexture;
use super::transform_buffer_storage::{TransformBuffers, TransformGroup};
use super::vertex::Vertex;
use super::{render_pipeline::RenderPipeline, render_buffer::BufferStorage, render_job::RenderJob};
use super::transform_buffer_storage::TransformBuffers;
use super::{resource::RenderPipeline, render_buffer::BufferStorage, render_job::RenderJob};
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
}
}