Skip to main content

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}