1use wgpu::PipelineCompilationOptions;
2
3use crate::{
4 bind_group::BindGroup, context::Context, draw_call::RasteriserState,
5 pipeline_layout::PipelineLayout, shader::EntryPoint,
6};
7
8#[derive(Clone, Hash, PartialEq, Eq, Debug)]
12pub struct VertexBufferLayout {
13 pub array_stride: wgpu::BufferAddress,
14 pub step_mode: wgpu::VertexStepMode,
15 pub attributes: Vec<wgpu::VertexAttribute>,
16}
17
18#[derive(Clone, Default, Hash, PartialEq, Eq, Debug)]
22pub struct ColorTargetState {
23 pub blend: Option<wgpu::BlendState>,
24 pub write_mask: wgpu::ColorWrites,
25}
26
27#[derive(Clone, Debug)]
32pub struct RenderPipeline {
33 vertex: (EntryPoint, Vec<VertexBufferLayout>),
34 fragment: Option<(EntryPoint, Vec<Option<ColorTargetState>>)>,
35 label: Option<String>,
36}
37
38#[derive(Clone, Hash, PartialEq, Eq)]
39pub(crate) struct RenderPipelineCacheKey {
40 layout: PipelineLayout,
41 vertex: (EntryPoint, Vec<VertexBufferLayout>),
42 fragment: Option<(EntryPoint, Vec<Option<ColorTargetState>>)>,
43 rasteriser_state: RasteriserState,
44}
45
46impl RenderPipeline {
47 pub(crate) fn get_or_build(
48 &self,
49 color_formats: &[wgpu::TextureFormat],
50 depth_format: Option<wgpu::TextureFormat>,
51 multisample: &Option<wgpu::MultisampleState>,
52 rasteriser_state: &RasteriserState,
53 bind_groups: &[BindGroup],
54 context: &Context,
55 ) -> wgpu::RenderPipeline {
56 let layout = PipelineLayout {
57 bind_group_layouts: bind_groups.iter().map(|b| b.build_layout()).collect(),
58 };
59
60 let mut pipeline_cache = context.caches.render_pipeline_cache.borrow_mut();
61
62 let key = RenderPipelineCacheKey {
63 layout: layout.clone(),
64 vertex: self.vertex.clone(),
65 fragment: self.fragment.clone(),
66 rasteriser_state: rasteriser_state.clone(),
67 };
68
69 pipeline_cache
70 .get_or_insert_with(key, || {
71 let layout = layout.get_or_build(context);
72
73 let buffers = self
74 .vertex
75 .1
76 .iter()
77 .map(|b| wgpu::VertexBufferLayout {
78 array_stride: b.array_stride,
79 step_mode: b.step_mode,
80 attributes: &b.attributes,
81 })
82 .collect::<Vec<_>>();
83
84 let targets = if let Some((_, targets)) = &self.fragment {
85 targets
86 .iter()
87 .zip(color_formats.iter())
88 .map(|(t, f)| {
89 t.as_ref().map(|t| wgpu::ColorTargetState {
90 format: *f,
91 blend: t.blend,
92 write_mask: t.write_mask,
93 })
94 })
95 .collect::<Vec<_>>()
96 } else {
97 vec![]
98 };
99
100 context
101 .device()
102 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
103 label: self.label.as_deref(),
104 layout: Some(&layout),
105 primitive: wgpu::PrimitiveState {
106 front_face: rasteriser_state.front_face,
107 cull_mode: rasteriser_state.cull_mode,
108 polygon_mode: rasteriser_state.polygon_mode,
109 ..Default::default()
110 },
111 vertex: wgpu::VertexState {
112 module: &self.vertex.0.shader,
113 entry_point: Some(&self.vertex.0.entry_point),
114 buffers: &buffers,
115 compilation_options: PipelineCompilationOptions::default(),
116 },
117 fragment: self.fragment.as_ref().map(|(entry_point, _)| {
118 wgpu::FragmentState {
119 module: &entry_point.shader,
120 entry_point: Some(&entry_point.entry_point),
121 targets: &targets,
122 compilation_options: PipelineCompilationOptions::default(),
123 }
124 }),
125 depth_stencil: depth_format.map(|format| wgpu::DepthStencilState {
126 format,
127 depth_compare: rasteriser_state.depth_compare,
128 depth_write_enabled: rasteriser_state.depth_write,
129 stencil: Default::default(),
130 bias: Default::default(),
131 }),
132 multisample: multisample.unwrap_or_default(),
133 multiview: None,
134 cache: None,
135 })
136 })
137 .clone()
138 }
139}
140
141#[derive(Clone)]
143pub struct RenderPipelineBuilder {
144 vertex: (EntryPoint, Vec<VertexBufferLayout>),
145 fragment: Option<(EntryPoint, Vec<Option<ColorTargetState>>)>,
146 label: Option<String>,
147}
148
149impl RenderPipelineBuilder {
150 pub fn with_vertex<I>(entry_point: &EntryPoint, vertex_buffer_layout: I) -> Self
151 where
152 I: Into<Vec<VertexBufferLayout>>,
153 {
154 Self {
155 vertex: (entry_point.clone(), vertex_buffer_layout.into()),
156 fragment: None,
157 label: None,
158 }
159 }
160
161 pub fn vertex<I>(mut self, entry_point: &EntryPoint, vertex_buffer_layout: I) -> Self
162 where
163 I: Into<Vec<VertexBufferLayout>>,
164 {
165 self.vertex = (entry_point.clone(), vertex_buffer_layout.into());
166 self
167 }
168
169 pub fn fragment<I>(mut self, entry_point: &EntryPoint, targets: I) -> Self
170 where
171 I: Into<Vec<Option<ColorTargetState>>>,
172 {
173 self.fragment = Some((entry_point.clone(), targets.into()));
174 self
175 }
176
177 pub fn no_fragment(mut self) -> Self {
178 self.fragment = None;
179 self
180 }
181
182 pub fn label(mut self, label: &str) -> Self {
184 self.label = Some(label.into());
185 self
186 }
187
188 pub fn build(self) -> RenderPipeline {
189 RenderPipeline {
190 vertex: self.vertex,
191 fragment: self.fragment,
192 label: self.label,
193 }
194 }
195}