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}