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        event!(LogLevel::TRACE, "Creating a new render pass {}.", label);
65
66        Self {
67            label: label.to_string(),
68            render_pass,
69            pipeline_set: false,
70            vertex_buffer_set: false,
71            index_buffer_set: false
72        }
73    }
74
75    /// Set the pipeline of the render pass.
76    /// The bind groups of the pipeline are also set.
77    ///
78    /// # Arguments
79    ///
80    /// * `pipeline` - The pipeline to set.
81    ///
82    /// # Errors
83    ///
84    /// * `RenderError::PipelineNotInitialized` - The pipeline is not initialized.
85    pub fn set_pipeline(&mut self, pipeline: &'a RenderPipeline) -> Result<&mut Self, RenderError> {
86        if pipeline.get_pipeline().is_none() {
87            error!(pipeline.label, "Pipeline is not created yet.");
88            return Err(RenderError::PipelineNotInitialized);
89        }
90
91        // Set pipeline
92        self.render_pass
93            .set_pipeline(pipeline.get_pipeline().as_ref().unwrap());
94        self.pipeline_set = true;
95        Ok(self)
96    }
97
98    /// Set a vertex buffer of the render pass.
99    ///
100    /// # Arguments
101    ///
102    /// * `binding` - The binding of the vertex buffer.
103    /// * `buffer` - The buffer to set.
104    pub fn set_vertex_buffer(&mut self, binding: u32, buffer: &'a Buffer) -> &mut Self {
105        self.render_pass
106            .set_vertex_buffer(binding, buffer.buffer.slice(..));
107        self.vertex_buffer_set = true;
108        self
109    }
110
111    /// Set the index buffer of the render pass.
112    ///
113    /// # Arguments
114    ///
115    /// * `buffer` - The buffer to set.
116    pub fn set_index_buffer(&mut self, buffer: &'a Buffer) -> &mut Self {
117        self.render_pass
118            .set_index_buffer(buffer.buffer.slice(..), wgpu::IndexFormat::Uint32);
119        self.index_buffer_set = true;
120        self
121    }
122
123    /// Set the scissor rect of the render pass.
124    ///
125    /// # Arguments
126    ///
127    /// * `x` - X coordinate of the scissor rect.
128    /// * `y` - Y coordinate of the scissor rect.
129    /// * `width` - Width of the scissor rect.
130    /// * `height` - Height of the scissor rect.
131    pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) -> &mut Self {
132        self.render_pass.set_scissor_rect(x, y, width, height);
133        self
134    }
135
136    /// Set push constants of the render pass.
137    ///
138    /// # Arguments
139    ///
140    /// * `stages` - The shader stages to set the push constants for.
141    /// * `data` - The data to set.
142    pub fn set_push_constants(&mut self, stages: ShaderStages, data: &[u8]) -> &mut Self {
143        self.render_pass.set_push_constants(stages, 0, data);
144        self
145    }
146
147    /// Set a bind group of the render pass at a binding.
148    ///
149    /// # Arguments
150    ///
151    /// * `binding` - The binding of the bind group.
152    /// * `bind_group` - The bind group to set.
153    pub fn set_bind_group(&mut self, binding: u32, bind_group: &'a BindGroup) -> &mut Self {
154        debug_assert!(
155            bind_group.0.is_some(),
156            "Bind group {} is not created yet.",
157            binding
158        );
159        self.render_pass
160            .set_bind_group(binding, bind_group.0.as_ref().unwrap(), &[]);
161        self
162    }
163
164    /// Draws primitives from the active vertex buffers.
165    ///
166    /// # Arguments
167    ///
168    /// * `vertices` - Range of vertices to draw.
169    /// * `instances` - Range of instances to draw.
170    ///
171    /// # Errors
172    ///
173    /// * `RenderError::PipelineNotSet` - The pipeline is not set.
174    pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) -> Result<(), RenderError> {
175        if !self.pipeline_set {
176            error!(self.label, "Pipeline is not set.");
177            return Err(RenderError::PipelineNotSet);
178        }
179        event!(
180            LogLevel::TRACE,
181            "Drawing {} vertices and {} instances.",
182            vertices.end - vertices.start,
183            instances.end - instances.start
184        );
185        self.render_pass.draw(vertices, instances);
186        Ok(())
187    }
188
189    /// Draws primitives from the active vertex buffers as indexed triangles.
190    ///
191    /// # Arguments
192    ///
193    /// * `indices` - Range of indices to draw.
194    /// * `instance_index` - Indice of the instance to draw.
195    ///
196    /// # Errors
197    ///
198    /// * `RenderError::PipelineNotSet` - The pipeline is not set.
199    pub fn draw_indexed(
200        &mut self,
201        indices: Range<u32>,
202        instance_index: Range<u32>
203    ) -> Result<(), RenderError> {
204        if !self.pipeline_set {
205            error!(self.label, "Pipeline is not set.");
206            return Err(RenderError::PipelineNotSet);
207        }
208        event!(
209            LogLevel::TRACE,
210            "Drawing indexed {} indices and {} instances.",
211            indices.end - indices.start,
212            instance_index.end - instance_index.start
213        );
214        self.render_pass.draw_indexed(indices, 0, instance_index);
215        Ok(())
216    }
217
218    /// Draws primitives from the active vertex buffers.
219    /// The draw is indirect, meaning the draw arguments are read from a buffer.
220    ///
221    /// # Arguments
222    ///
223    /// * `buffer` - The buffer to read the draw arguments from.
224    /// * `offset` - The first draw argument to read.
225    /// * `count` - The number of draw arguments to read.
226    ///
227    /// # Errors
228    ///
229    /// * `RenderError::PipelineNotSet` - The pipeline is not set.
230    /// * `RenderError::MissingVertexBuffer` - The vertex buffer is not set.
231    pub fn multi_draw_indirect(
232        &mut self,
233        buffer: &'a Buffer,
234        offset: BufferAddress,
235        count: u32
236    ) -> Result<(), RenderError> {
237        if !self.pipeline_set {
238            error!(self.label, "Pipeline is not set.");
239            return Err(RenderError::PipelineNotSet);
240        }
241        if !self.vertex_buffer_set {
242            error!(self.label, "Vertex buffer is not set.");
243            return Err(RenderError::MissingVertexBuffer);
244        }
245        event!(
246            LogLevel::TRACE,
247            "Drawing {} instances from indirect buffer.",
248            count
249        );
250        self.render_pass
251            .multi_draw_indirect(&buffer.buffer, offset, count);
252        Ok(())
253    }
254
255    /// Draws primitives from the active vertex buffers as indexed triangles.
256    /// The draw is indirect, meaning the draw arguments are read from a buffer.
257    ///
258    /// # Arguments
259    ///
260    /// * `buffer` - The buffer to read the draw arguments from.
261    /// * `offset` - The first draw argument to read.
262    /// * `count` - The number of draw arguments to read.
263    ///
264    /// # Errors
265    ///
266    /// * `RenderError::PipelineNotSet` - The pipeline is not set.
267    /// * `RenderError::MissingVertexBuffer` - The vertex buffer is not set.
268    /// * `RenderError::MissingIndexBuffer` - The index buffer is not set.
269    pub fn multi_draw_indexed_indirect(
270        &mut self,
271        buffer: &'a Buffer,
272        offset: BufferAddress,
273        count: u32
274    ) -> Result<(), RenderError> {
275        if !self.pipeline_set {
276            error!(self.label, "Pipeline is not set.");
277            return Err(RenderError::PipelineNotSet);
278        }
279        event!(
280            LogLevel::TRACE,
281            "Drawing indexed {} instances from indirect buffer.",
282            count
283        );
284        self.render_pass
285            .multi_draw_indexed_indirect(&buffer.buffer, offset, count);
286        Ok(())
287    }
288
289    /// Forgets the lifetime of the render pass.
290    /// This is used to return the inner `wgpu::RenderPass` when needed.
291    pub fn forget_lifetime(self) -> RenderPassInstance<'static> {
292        let render_pass = self.render_pass.forget_lifetime();
293        RenderPassInstance {
294            label: self.label,
295            render_pass,
296            pipeline_set: self.pipeline_set,
297            vertex_buffer_set: self.vertex_buffer_set,
298            index_buffer_set: self.index_buffer_set
299        }
300    }
301
302    /// Consumes the render pass and returns the inner `wgpu::RenderPass`.
303    pub fn into_inner(self) -> wgpu::RenderPass<'a> {
304        self.render_pass
305    }
306}