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 /// Copies query results stored in `query_set` into `destination` so that they can be read
221 /// by compute shaders or buffer operations.
222 ///
223 /// * `query_range` is the range of query result indices to copy from `query_set`.
224 /// Occlusion and timestamp queries occupy 1 result index each;
225 /// for pipeline statistics queries, see [`PipelineStatisticsTypes`].
226 /// * `destination_offset` is the offset within `destination` to start writing at.
227 /// It must be a multiple of [`QUERY_RESOLVE_BUFFER_ALIGNMENT`].
228 ///
229 /// The length of the data written to `destination` will be 8 bytes ([`QUERY_SIZE`])
230 /// times the number of elements in `query_range`.
231 ///
232 /// For further information about using queries, see [`QuerySet`].
233 pub fn resolve_query_set(
234 &mut self,
235 query_set: &QuerySet,
236 query_range: Range<u32>,
237 destination: &Buffer,
238 destination_offset: BufferAddress,
239 ) {
240 self.inner.resolve_query_set(
241 &query_set.inner,
242 query_range.start,
243 query_range.end - query_range.start,
244 &destination.inner,
245 destination_offset,
246 );
247 }
248
249 impl_deferred_command_buffer_actions!();
250
251 /// Get the [`wgpu_hal`] command encoder from this `CommandEncoder`.
252 ///
253 /// The returned command encoder will be ready to record onto.
254 ///
255 /// # Errors
256 ///
257 /// This method will pass in [`None`] if:
258 /// - The encoder is not from the backend specified by `A`.
259 /// - The encoder is from the `webgpu` or `custom` backend.
260 ///
261 /// # Types
262 ///
263 /// The callback argument depends on the backend:
264 ///
265 #[doc = crate::hal_type_vulkan!("CommandEncoder")]
266 #[doc = crate::hal_type_metal!("CommandEncoder")]
267 #[doc = crate::hal_type_dx12!("CommandEncoder")]
268 #[doc = crate::hal_type_gles!("CommandEncoder")]
269 ///
270 /// # Safety
271 ///
272 /// - The raw handle obtained from the `A::CommandEncoder` must not be manually destroyed.
273 /// - You must not end the command buffer; wgpu will do it when you call finish.
274 /// - The wgpu command encoder must not be interacted with in any way while recording is
275 /// happening to the wgpu_hal or backend command encoder.
276 #[cfg(wgpu_core)]
277 pub unsafe fn as_hal_mut<A: hal::Api, F: FnOnce(Option<&mut A::CommandEncoder>) -> R, R>(
278 &mut self,
279 hal_command_encoder_callback: F,
280 ) -> R {
281 if let Some(encoder) = self.inner.as_core_mut_opt() {
282 unsafe {
283 encoder
284 .context
285 .command_encoder_as_hal_mut::<A, F, R>(encoder, hal_command_encoder_callback)
286 }
287 } else {
288 hal_command_encoder_callback(None)
289 }
290 }
291
292 #[cfg(custom)]
293 /// Returns custom implementation of CommandEncoder (if custom backend and is internally T)
294 pub fn as_custom<T: custom::CommandEncoderInterface>(&self) -> Option<&T> {
295 self.inner.as_custom()
296 }
297}
298
299/// [`Features::TIMESTAMP_QUERY_INSIDE_ENCODERS`] must be enabled on the device in order to call these functions.
300impl CommandEncoder {
301 /// Issue a timestamp command at this point in the queue.
302 /// The timestamp will be written to the specified query set, at the specified index.
303 ///
304 /// Must be multiplied by [`Queue::get_timestamp_period`] to get
305 /// the value in nanoseconds. Absolute values have no meaning,
306 /// but timestamps can be subtracted to get the time it takes
307 /// for a string of operations to complete.
308 ///
309 /// Attention: Since commands within a command recorder may be reordered,
310 /// there is no strict guarantee that timestamps are taken after all commands
311 /// recorded so far and all before all commands recorded after.
312 /// This may depend both on the backend and the driver.
313 pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) {
314 self.inner.write_timestamp(&query_set.inner, query_index);
315 }
316}
317
318/// [`Features::EXPERIMENTAL_RAY_QUERY`] must be enabled on the device in order to call these functions.
319impl CommandEncoder {
320 /// When encoding the acceleration structure build with the raw Hal encoder
321 /// (obtained from [`CommandEncoder::as_hal_mut`]), this function marks the
322 /// acceleration structures as having been built.
323 ///
324 /// This function must only be used with the raw encoder API. When using the
325 /// wgpu encoding API, acceleration structure build is tracked automatically.
326 ///
327 /// # Panics
328 ///
329 /// - If the encoder is being used with the wgpu encoding API.
330 ///
331 /// # Safety
332 ///
333 /// - All acceleration structures must have been build in this command encoder.
334 /// - All BLASes inputted must have been built before all TLASes that were inputted here and
335 /// which use them.
336 pub unsafe fn mark_acceleration_structures_built<'a>(
337 &self,
338 blas: impl IntoIterator<Item = &'a Blas>,
339 tlas: impl IntoIterator<Item = &'a Tlas>,
340 ) {
341 self.inner
342 .mark_acceleration_structures_built(&mut blas.into_iter(), &mut tlas.into_iter())
343 }
344 /// Build bottom and top level acceleration structures.
345 ///
346 /// Builds the BLASes then the TLASes, but does ***not*** build the BLASes into the TLASes,
347 /// that must be done by setting a TLAS instance in the TLAS package to one that contains the BLAS (and with an appropriate transform)
348 ///
349 /// # Validation
350 ///
351 /// - blas: Iterator of bottom level acceleration structure entries to build.
352 /// For each entry, the provided size descriptor must be strictly smaller or equal to the descriptor given at BLAS creation, this means:
353 /// - Less or equal number of geometries
354 /// - Same kind of geometry (with index buffer or without) (same vertex/index format)
355 /// - Same flags
356 /// - Less or equal number of vertices
357 /// - Less or equal number of indices (if applicable)
358 /// - tlas: iterator of top level acceleration structure packages to build
359 /// For each entry:
360 /// - 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`
361 /// - 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)
362 ///
363 /// If the device the command encoder is created from does not have [Features::EXPERIMENTAL_RAY_QUERY] enabled then a validation error is generated
364 ///
365 /// 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.
366 ///
367 /// # Bind group usage
368 ///
369 /// When a top level acceleration structure is used in a bind group, some validation takes place:
370 /// - The top level acceleration structure is valid and has been built.
371 /// - All the bottom level acceleration structures referenced by the top level acceleration structure are valid and have been built prior,
372 /// or at same time as the containing top level acceleration structure.
373 ///
374 /// [Features::EXPERIMENTAL_RAY_QUERY]: wgt::Features::EXPERIMENTAL_RAY_QUERY
375 pub fn build_acceleration_structures<'a>(
376 &mut self,
377 blas: impl IntoIterator<Item = &'a BlasBuildEntry<'a>>,
378 tlas: impl IntoIterator<Item = &'a Tlas>,
379 ) {
380 self.inner
381 .build_acceleration_structures(&mut blas.into_iter(), &mut tlas.into_iter());
382 }
383
384 /// Transition resources to an underlying hal resource state.
385 ///
386 /// This is an advanced, native-only API (no-op on web) that has two main use cases:
387 ///
388 /// # Batching Barriers
389 ///
390 /// 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
391 /// 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
392 /// for the start of the next user-supplied command buffer.
393 ///
394 /// Wgpu does not currently attempt to batch multiple of these generated command buffers/barriers together, which may lead to suboptimal barrier placement.
395 ///
396 /// Consider the following scenario, where the user does `queue.submit(&[a, b, c])`:
397 /// * CommandBuffer A: Use resource X as a render pass attachment
398 /// * CommandBuffer B: Use resource Y as a render pass attachment
399 /// * CommandBuffer C: Use resources X and Y in a bind group
400 ///
401 /// 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])`:
402 /// * CommandBuffer 0: Barrier to transition resource X from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
403 /// * CommandBuffer A: Use resource X as a render pass attachment
404 /// * CommandBuffer 1: Barrier to transition resource Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
405 /// * CommandBuffer B: Use resource Y as a render pass attachment
406 /// * CommandBuffer 2: Barrier to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE
407 /// * CommandBuffer C: Use resources X and Y in a bind group
408 ///
409 /// To prevent this, after profiling their app, an advanced user might choose to instead do `queue.submit(&[a, b, c])`:
410 /// * CommandBuffer A:
411 /// * Use [`CommandEncoder::transition_resources`] to transition resources X and Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
412 /// * Use resource X as a render pass attachment
413 /// * CommandBuffer B: Use resource Y as a render pass attachment
414 /// * CommandBuffer C:
415 /// * Use [`CommandEncoder::transition_resources`] to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE
416 /// * Use resources X and Y in a bind group
417 ///
418 /// 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])`:
419 /// * CommandBuffer 0: Barrier to transition resources X and Y from TextureUses::RESOURCE (from last frame) to TextureUses::COLOR_TARGET
420 /// * CommandBuffer A: Use resource X as a render pass attachment
421 /// * CommandBuffer B: Use resource Y as a render pass attachment
422 /// * CommandBuffer 1: Barrier to transition resources X and Y from TextureUses::COLOR_TARGET to TextureUses::RESOURCE
423 /// * CommandBuffer C: Use resources X and Y in a bind group
424 ///
425 /// Which eliminates the extra command buffer and barrier between command buffers A and B.
426 ///
427 /// # Native Interoperability
428 ///
429 /// 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
430 /// the native API commands, for synchronization and resource state transition purposes.
431 pub fn transition_resources<'a>(
432 &mut self,
433 buffer_transitions: impl Iterator<Item = wgt::BufferTransition<&'a Buffer>>,
434 texture_transitions: impl Iterator<Item = wgt::TextureTransition<&'a Texture>>,
435 ) {
436 self.inner.transition_resources(
437 &mut buffer_transitions.map(|t| wgt::BufferTransition {
438 buffer: &t.buffer.inner,
439 state: t.state,
440 }),
441 &mut texture_transitions.map(|t| wgt::TextureTransition {
442 texture: &t.texture.inner,
443 selector: t.selector,
444 state: t.state,
445 }),
446 );
447 }
448}