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