wgpu/api/
command_encoder.rs

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