wgpu/api/command_encoder.rs
1use std::{marker::PhantomData, ops::Range, sync::Arc, thread};
2
3use crate::context::DynContext;
4use crate::*;
5
6/// Encodes a series of GPU operations.
7///
8/// A command encoder can record [`RenderPass`]es, [`ComputePass`]es,
9/// and transfer operations between driver-managed resources like [`Buffer`]s and [`Texture`]s.
10///
11/// When finished recording, call [`CommandEncoder::finish`] to obtain a [`CommandBuffer`] which may
12/// be submitted for execution.
13///
14/// Corresponds to [WebGPU `GPUCommandEncoder`](https://gpuweb.github.io/gpuweb/#command-encoder).
15#[derive(Debug)]
16pub struct CommandEncoder {
17 pub(crate) context: Arc<C>,
18 pub(crate) data: Box<Data>,
19}
20#[cfg(send_sync)]
21static_assertions::assert_impl_all!(CommandEncoder: Send, Sync);
22
23impl Drop for CommandEncoder {
24 fn drop(&mut self) {
25 if !thread::panicking() {
26 self.context.command_encoder_drop(self.data.as_ref());
27 }
28 }
29}
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::ImageCopyBuffer as ImageCopyBufferBase;
41/// View of a buffer which can be used to copy to/from a texture.
42///
43/// Corresponds to [WebGPU `GPUImageCopyBuffer`](
44/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopybuffer).
45pub type ImageCopyBuffer<'a> = ImageCopyBufferBase<&'a Buffer>;
46#[cfg(send_sync)]
47static_assertions::assert_impl_all!(ImageCopyBuffer<'_>: Send, Sync);
48
49pub use wgt::ImageCopyTexture as ImageCopyTextureBase;
50/// View of a texture which can be used to copy to/from a buffer/texture.
51///
52/// Corresponds to [WebGPU `GPUImageCopyTexture`](
53/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopytexture).
54pub type ImageCopyTexture<'a> = ImageCopyTextureBase<&'a Texture>;
55#[cfg(send_sync)]
56static_assertions::assert_impl_all!(ImageCopyTexture<'_>: Send, Sync);
57
58pub use wgt::ImageCopyTextureTagged as ImageCopyTextureTaggedBase;
59/// View of a texture which can be used to copy to a texture, including
60/// color space and alpha premultiplication information.
61///
62/// Corresponds to [WebGPU `GPUImageCopyTextureTagged`](
63/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopytexturetagged).
64pub type ImageCopyTextureTagged<'a> = ImageCopyTextureTaggedBase<&'a Texture>;
65#[cfg(send_sync)]
66static_assertions::assert_impl_all!(ImageCopyTexture<'_>: Send, Sync);
67
68impl CommandEncoder {
69 /// Finishes recording and returns a [`CommandBuffer`] that can be submitted for execution.
70 pub fn finish(mut self) -> CommandBuffer {
71 let data = DynContext::command_encoder_finish(&*self.context, self.data.as_mut());
72 CommandBuffer {
73 context: Arc::clone(&self.context),
74 data: Some(data),
75 }
76 }
77
78 /// Begins recording of a render pass.
79 ///
80 /// This function returns a [`RenderPass`] object which records a single render pass.
81 ///
82 /// As long as the returned [`RenderPass`] has not ended,
83 /// any mutating operation on this command encoder causes an error and invalidates it.
84 /// Note that the `'encoder` lifetime relationship protects against this,
85 /// but it is possible to opt out of it by calling [`RenderPass::forget_lifetime`].
86 /// This can be useful for runtime handling of the encoder->pass
87 /// dependency e.g. when pass and encoder are stored in the same data structure.
88 pub fn begin_render_pass<'encoder>(
89 &'encoder mut self,
90 desc: &RenderPassDescriptor<'_>,
91 ) -> RenderPass<'encoder> {
92 let data =
93 DynContext::command_encoder_begin_render_pass(&*self.context, self.data.as_ref(), desc);
94 RenderPass {
95 inner: RenderPassInner {
96 data,
97 context: self.context.clone(),
98 },
99 encoder_guard: PhantomData,
100 }
101 }
102
103 /// Begins recording of a compute pass.
104 ///
105 /// This function returns a [`ComputePass`] object which records a single compute pass.
106 ///
107 /// As long as the returned [`ComputePass`] has not ended,
108 /// any mutating operation on this command encoder causes an error and invalidates it.
109 /// Note that the `'encoder` lifetime relationship protects against this,
110 /// but it is possible to opt out of it by calling [`ComputePass::forget_lifetime`].
111 /// This can be useful for runtime handling of the encoder->pass
112 /// dependency e.g. when pass and encoder are stored in the same data structure.
113 pub fn begin_compute_pass<'encoder>(
114 &'encoder mut self,
115 desc: &ComputePassDescriptor<'_>,
116 ) -> ComputePass<'encoder> {
117 let data = DynContext::command_encoder_begin_compute_pass(
118 &*self.context,
119 self.data.as_ref(),
120 desc,
121 );
122 ComputePass {
123 inner: ComputePassInner {
124 data,
125 context: self.context.clone(),
126 },
127 encoder_guard: PhantomData,
128 }
129 }
130
131 /// Copy data from one buffer to another.
132 ///
133 /// # Panics
134 ///
135 /// - Buffer offsets or copy size not a multiple of [`COPY_BUFFER_ALIGNMENT`].
136 /// - Copy would overrun buffer.
137 /// - Copy within the same buffer.
138 pub fn copy_buffer_to_buffer(
139 &mut self,
140 source: &Buffer,
141 source_offset: BufferAddress,
142 destination: &Buffer,
143 destination_offset: BufferAddress,
144 copy_size: BufferAddress,
145 ) {
146 DynContext::command_encoder_copy_buffer_to_buffer(
147 &*self.context,
148 self.data.as_ref(),
149 source.data.as_ref(),
150 source_offset,
151 destination.data.as_ref(),
152 destination_offset,
153 copy_size,
154 );
155 }
156
157 /// Copy data from a buffer to a texture.
158 pub fn copy_buffer_to_texture(
159 &mut self,
160 source: ImageCopyBuffer<'_>,
161 destination: ImageCopyTexture<'_>,
162 copy_size: Extent3d,
163 ) {
164 DynContext::command_encoder_copy_buffer_to_texture(
165 &*self.context,
166 self.data.as_ref(),
167 source,
168 destination,
169 copy_size,
170 );
171 }
172
173 /// Copy data from a texture to a buffer.
174 pub fn copy_texture_to_buffer(
175 &mut self,
176 source: ImageCopyTexture<'_>,
177 destination: ImageCopyBuffer<'_>,
178 copy_size: Extent3d,
179 ) {
180 DynContext::command_encoder_copy_texture_to_buffer(
181 &*self.context,
182 self.data.as_ref(),
183 source,
184 destination,
185 copy_size,
186 );
187 }
188
189 /// Copy data from one texture to another.
190 ///
191 /// # Panics
192 ///
193 /// - Textures are not the same type
194 /// - If a depth texture, or a multisampled texture, the entire texture must be copied
195 /// - Copy would overrun either texture
196 pub fn copy_texture_to_texture(
197 &mut self,
198 source: ImageCopyTexture<'_>,
199 destination: ImageCopyTexture<'_>,
200 copy_size: Extent3d,
201 ) {
202 DynContext::command_encoder_copy_texture_to_texture(
203 &*self.context,
204 self.data.as_ref(),
205 source,
206 destination,
207 copy_size,
208 );
209 }
210
211 /// Clears texture to zero.
212 ///
213 /// Note that unlike with clear_buffer, `COPY_DST` usage is not required.
214 ///
215 /// # Implementation notes
216 ///
217 /// - implemented either via buffer copies and render/depth target clear, path depends on texture usages
218 /// - behaves like texture zero init, but is performed immediately (clearing is *not* delayed via marking it as uninitialized)
219 ///
220 /// # Panics
221 ///
222 /// - `CLEAR_TEXTURE` extension not enabled
223 /// - Range is out of bounds
224 pub fn clear_texture(&mut self, texture: &Texture, subresource_range: &ImageSubresourceRange) {
225 DynContext::command_encoder_clear_texture(
226 &*self.context,
227 self.data.as_ref(),
228 texture.data.as_ref(),
229 subresource_range,
230 );
231 }
232
233 /// Clears buffer to zero.
234 ///
235 /// # Panics
236 ///
237 /// - Buffer does not have `COPY_DST` usage.
238 /// - Range is out of bounds
239 pub fn clear_buffer(
240 &mut self,
241 buffer: &Buffer,
242 offset: BufferAddress,
243 size: Option<BufferAddress>,
244 ) {
245 DynContext::command_encoder_clear_buffer(
246 &*self.context,
247 self.data.as_ref(),
248 buffer.data.as_ref(),
249 offset,
250 size,
251 );
252 }
253
254 /// Inserts debug marker.
255 pub fn insert_debug_marker(&mut self, label: &str) {
256 DynContext::command_encoder_insert_debug_marker(&*self.context, self.data.as_ref(), label);
257 }
258
259 /// Start record commands and group it into debug marker group.
260 pub fn push_debug_group(&mut self, label: &str) {
261 DynContext::command_encoder_push_debug_group(&*self.context, self.data.as_ref(), label);
262 }
263
264 /// Stops command recording and creates debug group.
265 pub fn pop_debug_group(&mut self) {
266 DynContext::command_encoder_pop_debug_group(&*self.context, self.data.as_ref());
267 }
268
269 /// Resolves a query set, writing the results into the supplied destination buffer.
270 ///
271 /// Occlusion and timestamp queries are 8 bytes each (see [`crate::QUERY_SIZE`]). For pipeline statistics queries,
272 /// see [`PipelineStatisticsTypes`] for more information.
273 pub fn resolve_query_set(
274 &mut self,
275 query_set: &QuerySet,
276 query_range: Range<u32>,
277 destination: &Buffer,
278 destination_offset: BufferAddress,
279 ) {
280 DynContext::command_encoder_resolve_query_set(
281 &*self.context,
282 self.data.as_ref(),
283 query_set.data.as_ref(),
284 query_range.start,
285 query_range.end - query_range.start,
286 destination.data.as_ref(),
287 destination_offset,
288 )
289 }
290
291 /// Returns the inner hal CommandEncoder using a callback. The hal command encoder will be `None` if the
292 /// backend type argument does not match with this wgpu CommandEncoder
293 ///
294 /// This method will start the wgpu_core level command recording.
295 ///
296 /// # Safety
297 ///
298 /// - The raw handle obtained from the hal CommandEncoder must not be manually destroyed
299 #[cfg(wgpu_core)]
300 pub unsafe fn as_hal_mut<
301 A: wgc::hal_api::HalApi,
302 F: FnOnce(Option<&mut A::CommandEncoder>) -> R,
303 R,
304 >(
305 &mut self,
306 hal_command_encoder_callback: F,
307 ) -> Option<R> {
308 self.context
309 .as_any()
310 .downcast_ref::<crate::backend::ContextWgpuCore>()
311 .map(|ctx| unsafe {
312 ctx.command_encoder_as_hal_mut::<A, F, R>(
313 crate::context::downcast_ref(self.data.as_ref()),
314 hal_command_encoder_callback,
315 )
316 })
317 }
318}
319
320/// [`Features::TIMESTAMP_QUERY_INSIDE_ENCODERS`] must be enabled on the device in order to call these functions.
321impl CommandEncoder {
322 /// Issue a timestamp command at this point in the queue.
323 /// The timestamp will be written to the specified query set, at the specified index.
324 ///
325 /// Must be multiplied by [`Queue::get_timestamp_period`] to get
326 /// the value in nanoseconds. Absolute values have no meaning,
327 /// but timestamps can be subtracted to get the time it takes
328 /// for a string of operations to complete.
329 ///
330 /// Attention: Since commands within a command recorder may be reordered,
331 /// there is no strict guarantee that timestamps are taken after all commands
332 /// recorded so far and all before all commands recorded after.
333 /// This may depend both on the backend and the driver.
334 pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) {
335 DynContext::command_encoder_write_timestamp(
336 &*self.context,
337 self.data.as_mut(),
338 query_set.data.as_ref(),
339 query_index,
340 )
341 }
342}