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}