wde_wgpu/passes/render_pass.rs
1//! Render pass abstraction for the WGPU library.
2
3use std::ops::Range;
4
5use wde_logger::prelude::*;
6use wgpu::BufferAddress;
7use wgpu::ShaderStages;
8
9use crate::buffer::Buffer;
10use crate::instance::RenderError;
11use crate::pipelines::BindGroup;
12use crate::render_pipeline::RenderPipeline;
13
14// Alias struct for the draw indirect functions.
15pub use wgpu::util::DrawIndexedIndirectArgs;
16pub use wgpu::util::DrawIndirectArgs;
17
18/// RAII wrapper over `wgpu::RenderPass` with guard rails to prevent draws without required
19/// state (pipeline + vertex/index buffers).
20///
21/// # Example
22/// ```rust,no_run
23/// use wde_wgpu::{
24/// buffer::Buffer,
25/// render_pass::RenderPass,
26/// render_pipeline::RenderPipeline,
27/// };
28///
29/// let mut pass = RenderPassInstance::new("example", render_pass);
30/// pass
31/// .set_pipeline(pipeline).unwrap()
32/// .set_vertex_buffer(0, vbo)
33/// .set_index_buffer(ibo)
34/// .draw_indexed(0..36, 0..1)
35/// .unwrap();
36/// ```
37///
38/// Supported operations include standard draws, indexed draws, and indirect versions of
39/// both. Errors are returned if mandatory state (pipeline, vertex/index buffers) is missing.
40pub struct RenderPassInstance<'a> {
41 pub label: String,
42 render_pass: wgpu::RenderPass<'a>,
43 pipeline_set: bool,
44 vertex_buffer_set: bool,
45 index_buffer_set: bool
46}
47
48impl std::fmt::Debug for RenderPassInstance<'_> {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 f.debug_struct("RenderPassInstance")
51 .field("label", &self.label)
52 .finish()
53 }
54}
55
56impl<'a> RenderPassInstance<'a> {
57 /// Create a new render pass.
58 ///
59 /// # Arguments
60 ///
61 /// * `label` - The label of the render pass.
62 /// * `render_pass` - The render pass to create.
63 pub fn new(label: &str, render_pass: wgpu::RenderPass<'a>) -> Self {
64 Self {
65 label: label.to_string(),
66 render_pass,
67 pipeline_set: false,
68 vertex_buffer_set: false,
69 index_buffer_set: false
70 }
71 }
72
73 /// Set the pipeline of the render pass.
74 /// The bind groups of the pipeline are also set.
75 ///
76 /// # Arguments
77 ///
78 /// * `pipeline` - The pipeline to set.
79 ///
80 /// # Errors
81 ///
82 /// * `RenderError::PipelineNotInitialized` - The pipeline is not initialized.
83 pub fn set_pipeline(&mut self, pipeline: &'a RenderPipeline) -> Result<&mut Self, RenderError> {
84 if pipeline.get_pipeline().is_none() {
85 error!(pipeline.label, "Pipeline is not created yet.");
86 return Err(RenderError::PipelineNotInitialized);
87 }
88
89 // Set pipeline
90 self.render_pass
91 .set_pipeline(pipeline.get_pipeline().as_ref().unwrap());
92 self.pipeline_set = true;
93 Ok(self)
94 }
95
96 /// Set a vertex buffer of the render pass.
97 ///
98 /// # Arguments
99 ///
100 /// * `binding` - The binding of the vertex buffer.
101 /// * `buffer` - The buffer to set.
102 pub fn set_vertex_buffer(&mut self, binding: u32, buffer: &'a Buffer) -> &mut Self {
103 self.render_pass
104 .set_vertex_buffer(binding, buffer.buffer.slice(..));
105 self.vertex_buffer_set = true;
106 self
107 }
108
109 /// Set the index buffer of the render pass.
110 ///
111 /// # Arguments
112 ///
113 /// * `buffer` - The buffer to set.
114 pub fn set_index_buffer(&mut self, buffer: &'a Buffer) -> &mut Self {
115 self.render_pass
116 .set_index_buffer(buffer.buffer.slice(..), wgpu::IndexFormat::Uint32);
117 self.index_buffer_set = true;
118 self
119 }
120
121 /// Set the scissor rect of the render pass.
122 ///
123 /// # Arguments
124 ///
125 /// * `x` - X coordinate of the scissor rect.
126 /// * `y` - Y coordinate of the scissor rect.
127 /// * `width` - Width of the scissor rect.
128 /// * `height` - Height of the scissor rect.
129 pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) -> &mut Self {
130 self.render_pass.set_scissor_rect(x, y, width, height);
131 self
132 }
133
134 /// Set push constants of the render pass.
135 ///
136 /// # Arguments
137 ///
138 /// * `stages` - The shader stages to set the push constants for.
139 /// * `data` - The data to set.
140 pub fn set_push_constants(&mut self, stages: ShaderStages, data: &[u8]) -> &mut Self {
141 self.render_pass.set_push_constants(stages, 0, data);
142 self
143 }
144
145 /// Set a bind group of the render pass at a binding.
146 ///
147 /// # Arguments
148 ///
149 /// * `binding` - The binding of the bind group.
150 /// * `bind_group` - The bind group to set.
151 pub fn set_bind_group(&mut self, binding: u32, bind_group: &'a BindGroup) -> &mut Self {
152 debug_assert!(
153 bind_group.0.is_some(),
154 "Bind group {} is not created yet.",
155 binding
156 );
157 self.render_pass
158 .set_bind_group(binding, bind_group.0.as_ref().unwrap(), &[]);
159 self
160 }
161
162 /// Set the stencil reference value used by stencil compare and write operations.
163 pub fn set_stencil_reference(&mut self, reference: u32) -> &mut Self {
164 self.render_pass.set_stencil_reference(reference);
165 self
166 }
167
168 /// Draws primitives from the active vertex buffers.
169 ///
170 /// # Arguments
171 ///
172 /// * `vertices` - Range of vertices to draw.
173 /// * `instances` - Range of instances to draw.
174 ///
175 /// # Errors
176 ///
177 /// * `RenderError::PipelineNotSet` - The pipeline is not set.
178 pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) -> Result<(), RenderError> {
179 if !self.pipeline_set {
180 error!(self.label, "Pipeline is not set.");
181 return Err(RenderError::PipelineNotSet);
182 }
183 self.render_pass.draw(vertices, instances);
184 Ok(())
185 }
186
187 /// Draws primitives from the active vertex buffers as indexed triangles.
188 ///
189 /// # Arguments
190 ///
191 /// * `indices` - Range of indices to draw.
192 /// * `instance_index` - Indice of the instance to draw.
193 ///
194 /// # Errors
195 ///
196 /// * `RenderError::PipelineNotSet` - The pipeline is not set.
197 pub fn draw_indexed(
198 &mut self,
199 indices: Range<u32>,
200 instance_index: Range<u32>
201 ) -> Result<(), RenderError> {
202 if !self.pipeline_set {
203 error!(self.label, "Pipeline is not set.");
204 return Err(RenderError::PipelineNotSet);
205 }
206 self.render_pass.draw_indexed(indices, 0, instance_index);
207 Ok(())
208 }
209
210 /// Draws primitives from the active vertex buffers.
211 /// The draw is indirect, meaning the draw arguments are read from a buffer.
212 ///
213 /// # Arguments
214 ///
215 /// * `buffer` - The buffer to read the draw arguments from.
216 /// * `offset` - The first draw argument to read.
217 /// * `count` - The number of draw arguments to read.
218 ///
219 /// # Errors
220 ///
221 /// * `RenderError::PipelineNotSet` - The pipeline is not set.
222 /// * `RenderError::MissingVertexBuffer` - The vertex buffer is not set.
223 pub fn multi_draw_indirect(
224 &mut self,
225 buffer: &'a Buffer,
226 offset: BufferAddress,
227 count: u32
228 ) -> Result<(), RenderError> {
229 if !self.pipeline_set {
230 error!(self.label, "Pipeline is not set.");
231 return Err(RenderError::PipelineNotSet);
232 }
233 if !self.vertex_buffer_set {
234 error!(self.label, "Vertex buffer is not set.");
235 return Err(RenderError::MissingVertexBuffer);
236 }
237 event!(
238 LogLevel::TRACE,
239 "Drawing {} instances from indirect buffer.",
240 count
241 );
242 self.render_pass
243 .multi_draw_indirect(&buffer.buffer, offset, count);
244 Ok(())
245 }
246
247 /// Draws primitives from the active vertex buffers as indexed triangles.
248 /// The draw is indirect, meaning the draw arguments are read from a buffer.
249 ///
250 /// # Arguments
251 ///
252 /// * `buffer` - The buffer to read the draw arguments from.
253 /// * `offset` - The first draw argument to read.
254 /// * `count` - The number of draw arguments to read.
255 ///
256 /// # Errors
257 ///
258 /// * `RenderError::PipelineNotSet` - The pipeline is not set.
259 /// * `RenderError::MissingVertexBuffer` - The vertex buffer is not set.
260 /// * `RenderError::MissingIndexBuffer` - The index buffer is not set.
261 pub fn multi_draw_indexed_indirect(
262 &mut self,
263 buffer: &'a Buffer,
264 offset: BufferAddress,
265 count: u32
266 ) -> Result<(), RenderError> {
267 if !self.pipeline_set {
268 error!(self.label, "Pipeline is not set.");
269 return Err(RenderError::PipelineNotSet);
270 }
271 self.render_pass
272 .multi_draw_indexed_indirect(&buffer.buffer, offset, count);
273 Ok(())
274 }
275
276 /// Forgets the lifetime of the render pass.
277 /// This is used to return the inner `wgpu::RenderPass` when needed.
278 pub fn forget_lifetime(self) -> RenderPassInstance<'static> {
279 let render_pass = self.render_pass.forget_lifetime();
280 RenderPassInstance {
281 label: self.label,
282 render_pass,
283 pipeline_set: self.pipeline_set,
284 vertex_buffer_set: self.vertex_buffer_set,
285 index_buffer_set: self.index_buffer_set
286 }
287 }
288
289 /// Consumes the render pass and returns the inner `wgpu::RenderPass`.
290 pub fn into_inner(self) -> wgpu::RenderPass<'a> {
291 self.render_pass
292 }
293}