wgpu/api/command_encoder.rs
1use core::ops::Range;
2
3use crate::{
4 api::{blas::BlasBuildEntry, tlas::Tlas},
5 *,
6};
7
8/// Encodes a series of GPU operations.
9///
10/// A command encoder can record [`RenderPass`]es, [`ComputePass`]es,
11/// and transfer operations between driver-managed resources like [`Buffer`]s and [`Texture`]s.
12///
13/// When finished recording, call [`CommandEncoder::finish`] to obtain a [`CommandBuffer`] which may
14/// be submitted for execution.
15///
16/// Corresponds to [WebGPU `GPUCommandEncoder`](https://gpuweb.github.io/gpuweb/#command-encoder).
17#[derive(Debug)]
18pub struct CommandEncoder {
19 pub(crate) inner: dispatch::DispatchCommandEncoder,
20}
21#[cfg(send_sync)]
22static_assertions::assert_impl_all!(CommandEncoder: Send, Sync);
23
24crate::cmp::impl_eq_ord_hash_proxy!(CommandEncoder => .inner);
25
26/// Describes a [`CommandEncoder`].
27///
28/// For use with [`Device::create_command_encoder`].
29///
30/// Corresponds to [WebGPU `GPUCommandEncoderDescriptor`](
31/// https://gpuweb.github.io/gpuweb/#dictdef-gpucommandencoderdescriptor).
32pub type CommandEncoderDescriptor<'a> = wgt::CommandEncoderDescriptor<Label<'a>>;
33static_assertions::assert_impl_all!(CommandEncoderDescriptor<'_>: Send, Sync);
34
35pub use wgt::TexelCopyBufferInfo as TexelCopyBufferInfoBase;
36/// View of a buffer which can be used to copy to/from a texture.
37///
38/// Corresponds to [WebGPU `GPUTexelCopyBufferInfo`](
39/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopybuffer).
40pub type TexelCopyBufferInfo<'a> = TexelCopyBufferInfoBase<&'a Buffer>;
41#[cfg(send_sync)]
42static_assertions::assert_impl_all!(TexelCopyBufferInfo<'_>: Send, Sync);
43
44pub use wgt::TexelCopyTextureInfo as TexelCopyTextureInfoBase;
45/// View of a texture which can be used to copy to/from a buffer/texture.
46///
47/// Corresponds to [WebGPU `GPUTexelCopyTextureInfo`](
48/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopytexture).
49pub type TexelCopyTextureInfo<'a> = TexelCopyTextureInfoBase<&'a Texture>;
50#[cfg(send_sync)]
51static_assertions::assert_impl_all!(TexelCopyTextureInfo<'_>: Send, Sync);
52
53impl CommandEncoder {
54 /// Finishes recording and returns a [`CommandBuffer`] that can be submitted for execution.
55 pub fn finish(mut self) -> CommandBuffer {
56 let buffer = self.inner.finish();
57
58 CommandBuffer { buffer }
59 }
60
61 /// Begins recording of a render pass.
62 ///
63 /// This function returns a [`RenderPass`] object which records a single render pass.
64 ///
65 /// As long as the returned [`RenderPass`] has not ended,
66 /// any mutating operation on this command encoder causes an error and invalidates it.
67 /// Note that the `'encoder` lifetime relationship protects against this,
68 /// but it is possible to opt out of it by calling [`RenderPass::forget_lifetime`].
69 /// This can be useful for runtime handling of the encoder->pass
70 /// dependency e.g. when pass and encoder are stored in the same data structure.
71 pub fn begin_render_pass<'encoder>(
72 &'encoder mut self,
73 desc: &RenderPassDescriptor<'_>,
74 ) -> RenderPass<'encoder> {
75 let rpass = self.inner.begin_render_pass(desc);
76 RenderPass {
77 inner: rpass,
78 _encoder_guard: api::PhantomDrop::default(),
79 }
80 }
81
82 /// Begins recording of a compute pass.
83 ///
84 /// This function returns a [`ComputePass`] object which records a single compute pass.
85 ///
86 /// As long as the returned [`ComputePass`] has not ended,
87 /// any mutating operation on this command encoder causes an error and invalidates it.
88 /// Note that the `'encoder` lifetime relationship protects against this,
89 /// but it is possible to opt out of it by calling [`ComputePass::forget_lifetime`].
90 /// This can be useful for runtime handling of the encoder->pass
91 /// dependency e.g. when pass and encoder are stored in the same data structure.
92 pub fn begin_compute_pass<'encoder>(
93 &'encoder mut self,
94 desc: &ComputePassDescriptor<'_>,
95 ) -> ComputePass<'encoder> {
96 let cpass = self.inner.begin_compute_pass(desc);
97 ComputePass {
98 inner: cpass,
99 _encoder_guard: api::PhantomDrop::default(),
100 }
101 }
102
103 /// Copy data from one buffer to another.
104 ///
105 /// # Panics
106 ///
107 /// - Buffer offsets or copy size not a multiple of [`COPY_BUFFER_ALIGNMENT`].
108 /// - Copy would overrun buffer.
109 /// - Copy within the same buffer.
110 pub fn copy_buffer_to_buffer(
111 &mut self,
112 source: &Buffer,
113 source_offset: BufferAddress,
114 destination: &Buffer,
115 destination_offset: BufferAddress,
116 copy_size: impl Into<Option<BufferAddress>>,
117 ) {
118 self.inner.copy_buffer_to_buffer(
119 &source.inner,
120 source_offset,
121 &destination.inner,
122 destination_offset,
123 copy_size.into(),
124 );
125 }
126
127 /// Copy data from a buffer to a texture.
128 pub fn copy_buffer_to_texture(
129 &mut self,
130 source: TexelCopyBufferInfo<'_>,
131 destination: TexelCopyTextureInfo<'_>,
132 copy_size: Extent3d,
133 ) {
134 self.inner
135 .copy_buffer_to_texture(source, destination, copy_size);
136 }
137
138 /// Copy data from a texture to a buffer.
139 pub fn copy_texture_to_buffer(
140 &mut self,
141 source: TexelCopyTextureInfo<'_>,
142 destination: TexelCopyBufferInfo<'_>,
143 copy_size: Extent3d,
144 ) {
145 self.inner
146 .copy_texture_to_buffer(source, destination, copy_size);
147 }
148
149 /// Copy data from one texture to another.
150 ///
151 /// # Panics
152 ///
153 /// - Textures are not the same type
154 /// - If a depth texture, or a multisampled texture, the entire texture must be copied
155 /// - Copy would overrun either texture
156 pub fn copy_texture_to_texture(
157 &mut self,
158 source: TexelCopyTextureInfo<'_>,
159 destination: TexelCopyTextureInfo<'_>,
160 copy_size: Extent3d,
161 ) {
162 self.inner
163 .copy_texture_to_texture(source, destination, copy_size);
164 }
165
166 /// Clears texture to zero.
167 ///
168 /// Note that unlike with clear_buffer, `COPY_DST` usage is not required.
169 ///
170 /// # Implementation notes
171 ///
172 /// - implemented either via buffer copies and render/depth target clear, path depends on texture usages
173 /// - behaves like texture zero init, but is performed immediately (clearing is *not* delayed via marking it as uninitialized)
174 ///
175 /// # Panics
176 ///
177 /// - `CLEAR_TEXTURE` extension not enabled
178 /// - Range is out of bounds
179 pub fn clear_texture(&mut self, texture: &Texture, subresource_range: &ImageSubresourceRange) {
180 self.inner.clear_texture(&texture.inner, subresource_range);
181 }
182
183 /// Clears buffer to zero.
184 ///
185 /// # Panics
186 ///
187 /// - Buffer does not have `COPY_DST` usage.
188 /// - Range is out of bounds
189 pub fn clear_buffer(
190 &mut self,
191 buffer: &Buffer,
192 offset: BufferAddress,
193 size: Option<BufferAddress>,
194 ) {
195 self.inner.clear_buffer(&buffer.inner, offset, size);
196 }
197
198 /// Inserts debug marker.
199 pub fn insert_debug_marker(&mut self, label: &str) {
200 self.inner.insert_debug_marker(label);
201 }
202
203 /// Start record commands and group it into debug marker group.
204 pub fn push_debug_group(&mut self, label: &str) {
205 self.inner.push_debug_group(label);
206 }
207
208 /// Stops command recording and creates debug group.
209 pub fn pop_debug_group(&mut self) {
210 self.inner.pop_debug_group();
211 }
212
213 /// Resolves a query set, writing the results into the supplied destination buffer.
214 ///
215 /// Occlusion and timestamp queries are 8 bytes each (see [`crate::QUERY_SIZE`]). For pipeline statistics queries,
216 /// see [`PipelineStatisticsTypes`] for more information.
217 ///
218 /// `destination_offset` must be aligned to [`QUERY_RESOLVE_BUFFER_ALIGNMENT`].
219 pub fn resolve_query_set(
220 &mut self,
221 query_set: &QuerySet,
222 query_range: Range<u32>,
223 destination: &Buffer,
224 destination_offset: BufferAddress,
225 ) {
226 self.inner.resolve_query_set(
227 &query_set.inner,
228 query_range.start,
229 query_range.end - query_range.start,
230 &destination.inner,
231 destination_offset,
232 );
233 }
234
235 /// Returns the inner hal CommandEncoder using a callback. The hal command encoder will be `None` if the
236 /// backend type argument does not match with this wgpu CommandEncoder
237 ///
238 /// This method will start the wgpu_core level command recording.
239 ///
240 /// # Safety
241 ///
242 /// - The raw handle obtained from the hal CommandEncoder must not be manually destroyed
243 #[cfg(wgpu_core)]
244 pub unsafe fn as_hal_mut<
245 A: wgc::hal_api::HalApi,
246 F: FnOnce(Option<&mut A::CommandEncoder>) -> R,
247 R,
248 >(
249 &mut self,
250 hal_command_encoder_callback: F,
251 ) -> R {
252 if let Some(encoder) = self.inner.as_core_mut_opt() {
253 unsafe {
254 encoder
255 .context
256 .command_encoder_as_hal_mut::<A, F, R>(encoder, hal_command_encoder_callback)
257 }
258 } else {
259 hal_command_encoder_callback(None)
260 }
261 }
262
263 #[cfg(custom)]
264 /// Returns custom implementation of CommandEncoder (if custom backend and is internally T)
265 pub fn as_custom<T: custom::CommandEncoderInterface>(&self) -> Option<&T> {
266 self.inner.as_custom()
267 }
268}
269
270/// [`Features::TIMESTAMP_QUERY_INSIDE_ENCODERS`] must be enabled on the device in order to call these functions.
271impl CommandEncoder {
272 /// Issue a timestamp command at this point in the queue.
273 /// The timestamp will be written to the specified query set, at the specified index.
274 ///
275 /// Must be multiplied by [`Queue::get_timestamp_period`] to get
276 /// the value in nanoseconds. Absolute values have no meaning,
277 /// but timestamps can be subtracted to get the time it takes
278 /// for a string of operations to complete.
279 ///
280 /// Attention: Since commands within a command recorder may be reordered,
281 /// there is no strict guarantee that timestamps are taken after all commands
282 /// recorded so far and all before all commands recorded after.
283 /// This may depend both on the backend and the driver.
284 pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) {
285 self.inner.write_timestamp(&query_set.inner, query_index);
286 }
287}
288
289/// [`Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE`] must be enabled on the device in order to call these functions.
290impl CommandEncoder {
291 /// Mark acceleration structures as being built. ***Should only*** be used with wgpu-hal
292 /// functions, all wgpu functions already mark acceleration structures as built.
293 ///
294 /// # Safety
295 ///
296 /// - All acceleration structures must have been build in this command encoder.
297 /// - All BLASes inputted must have been built before all TLASes that were inputted here and
298 /// which use them.
299 pub unsafe fn mark_acceleration_structures_built<'a>(
300 &self,
301 blas: impl IntoIterator<Item = &'a Blas>,
302 tlas: impl IntoIterator<Item = &'a Tlas>,
303 ) {
304 self.inner
305 .mark_acceleration_structures_built(&mut blas.into_iter(), &mut tlas.into_iter())
306 }
307 /// Build bottom and top level acceleration structures.
308 ///
309 /// Builds the BLASes then the TLASes, but does ***not*** build the BLASes into the TLASes,
310 /// that must be done by setting a TLAS instance in the TLAS package to one that contains the BLAS (and with an appropriate transform)
311 ///
312 /// # Validation
313 ///
314 /// - blas: Iterator of bottom level acceleration structure entries to build.
315 /// For each entry, the provided size descriptor must be strictly smaller or equal to the descriptor given at BLAS creation, this means:
316 /// - Less or equal number of geometries
317 /// - Same kind of geometry (with index buffer or without) (same vertex/index format)
318 /// - Same flags
319 /// - Less or equal number of vertices
320 /// - Less or equal number of indices (if applicable)
321 /// - tlas: iterator of top level acceleration structure packages to build
322 /// For each entry:
323 /// - Each BLAS in each TLAS instance must have been being built in the current call or in a previous call to `build_acceleration_structures` or `build_acceleration_structures_unsafe_tlas`
324 /// - The number of TLAS instances must be less than or equal to the max number of tlas instances when creating (if creating a package with `TlasPackage::new()` this is already satisfied)
325 ///
326 /// If the device the command encoder is created from does not have [Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE] enabled then a validation error is generated
327 ///
328 /// A bottom level acceleration structure may be build and used as a reference in a top level acceleration structure in the same invocation of this function.
329 ///
330 /// # Bind group usage
331 ///
332 /// When a top level acceleration structure is used in a bind group, some validation takes place:
333 /// - The top level acceleration structure is valid and has been built.
334 /// - All the bottom level acceleration structures referenced by the top level acceleration structure are valid and have been built prior,
335 /// or at same time as the containing top level acceleration structure.
336 ///
337 /// [Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE]: wgt::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE
338 pub fn build_acceleration_structures<'a>(
339 &mut self,
340 blas: impl IntoIterator<Item = &'a BlasBuildEntry<'a>>,
341 tlas: impl IntoIterator<Item = &'a Tlas>,
342 ) {
343 self.inner
344 .build_acceleration_structures(&mut blas.into_iter(), &mut tlas.into_iter());
345 }
346
347 /// Transition resources to an underlying hal resource state.
348 ///
349 /// This is an advanced, native-only API (no-op on web) that has two main use cases:
350 ///
351 /// # Batching Barriers
352 ///
353 /// Wgpu does not have a global view of the frame when recording command buffers. When you submit multiple command buffers in a single queue submission, wgpu may need to record and
354 /// insert new command buffers (holding 1 or more barrier commands) in between the user-supplied command buffers in order to ensure that resources are transitioned to the correct state
355 /// for the start of the next user-supplied command buffer.
356 ///
357 /// Wgpu does not currently attempt to batch multiple of these generated command buffers/barriers together, which may lead to suboptimal barrier placement.
358 ///
359 /// Consider the following scenario, where the user does `queue.submit(&[a, b, c])`:
360 /// * CommandBuffer A: Use resource X as a render pass attachment
361 /// * CommandBuffer B: Use resource Y as a render pass attachment
362 /// * CommandBuffer C: Use resources X and Y in a bind group
363 ///
364 /// At submission time, wgpu will record and insert some new command buffers, resulting in a submission that looks like `queue.submit(&[0, a, 1, b, 2, c])`:
365 /// * CommandBuffer 0: Barrier to transition resource X from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
366 /// * CommandBuffer A: Use resource X as a render pass attachment
367 /// * CommandBuffer 1: Barrier to transition resource Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
368 /// * CommandBuffer B: Use resource Y as a render pass attachment
369 /// * CommandBuffer 2: Barrier to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE
370 /// * CommandBuffer C: Use resources X and Y in a bind group
371 ///
372 /// To prevent this, after profiling their app, an advanced user might choose to instead do `queue.submit(&[a, b, c])`:
373 /// * CommandBuffer A:
374 /// * Use [`CommandEncoder::transition_resources`] to transition resources X and Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
375 /// * Use resource X as a render pass attachment
376 /// * CommandBuffer B: Use resource Y as a render pass attachment
377 /// * CommandBuffer C:
378 /// * Use [`CommandEncoder::transition_resources`] to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE
379 /// * Use resources X and Y in a bind group
380 ///
381 /// At submission time, wgpu will record and insert some new command buffers, resulting in a submission that looks like `queue.submit(&[0, a, b, 1, c])`:
382 /// * CommandBuffer 0: Barrier to transition resources X and Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
383 /// * CommandBuffer A: Use resource X as a render pass attachment
384 /// * CommandBuffer B: Use resource Y as a render pass attachment
385 /// * CommandBuffer 1: Barrier to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE
386 /// * CommandBuffer C: Use resources X and Y in a bind group
387 ///
388 /// Which eliminates the extra command buffer and barrier between command buffers A and B.
389 ///
390 /// # Native Interoperability
391 ///
392 /// A user wanting to interoperate with the underlying native graphics APIs (Vulkan, DirectX12, Metal, etc) can use this API to generate barriers between wgpu commands and
393 /// the native API commands, for synchronization and resource state transition purposes.
394 pub fn transition_resources<'a>(
395 &mut self,
396 buffer_transitions: impl Iterator<Item = wgt::BufferTransition<&'a Buffer>>,
397 texture_transitions: impl Iterator<Item = wgt::TextureTransition<&'a Texture>>,
398 ) {
399 self.inner.transition_resources(
400 &mut buffer_transitions.map(|t| wgt::BufferTransition {
401 buffer: &t.buffer.inner,
402 state: t.state,
403 }),
404 &mut texture_transitions.map(|t| wgt::TextureTransition {
405 texture: &t.texture.inner,
406 selector: t.selector,
407 state: t.state,
408 }),
409 );
410 }
411}