wgpu/api/
queue.rs

1use alloc::boxed::Box;
2use core::fmt;
3use core::ops::RangeBounds;
4
5use crate::{api::DeferredCommandBufferActions, *};
6
7/// Handle to a command queue on a device.
8///
9/// A `Queue` executes recorded [`CommandBuffer`] objects and provides convenience methods
10/// for writing to [buffers](Queue::write_buffer) and [textures](Queue::write_texture).
11/// It can be created along with a [`Device`] by calling [`Adapter::request_device`].
12///
13/// Corresponds to [WebGPU `GPUQueue`](https://gpuweb.github.io/gpuweb/#gpu-queue).
14#[derive(Debug, Clone)]
15pub struct Queue {
16    pub(crate) inner: dispatch::DispatchQueue,
17}
18#[cfg(send_sync)]
19static_assertions::assert_impl_all!(Queue: Send, Sync);
20
21crate::cmp::impl_eq_ord_hash_proxy!(Queue => .inner);
22
23impl Queue {
24    #[cfg(custom)]
25    /// Returns custom implementation of Queue (if custom backend and is internally T)
26    pub fn as_custom<T: custom::QueueInterface>(&self) -> Option<&T> {
27        self.inner.as_custom()
28    }
29
30    #[cfg(custom)]
31    /// Creates Queue from custom implementation
32    pub fn from_custom<T: custom::QueueInterface>(queue: T) -> Self {
33        Self {
34            inner: dispatch::DispatchQueue::custom(queue),
35        }
36    }
37
38    /// Returns the underlying [`webgpu::GpuQueue`] handle if this `Queue`
39    /// is on the WebGPU backend, otherwise `None`.
40    ///
41    /// [`webgpu::GpuQueue`]: crate::webgpu::GpuQueue
42    #[cfg(webgpu)]
43    pub fn as_webgpu(&self) -> Option<&webgpu::GpuQueue> {
44        self.inner.as_webgpu_opt().map(|wq| &wq.inner)
45    }
46}
47
48/// Identifier for a particular call to [`Queue::submit`]. Can be used
49/// as part of an argument to [`Device::poll`] to block for a particular
50/// submission to finish.
51///
52/// This type is unique to the Rust API of `wgpu`.
53/// There is no analogue in the WebGPU specification.
54#[derive(Debug, Clone)]
55pub struct SubmissionIndex {
56    pub(crate) index: u64,
57}
58#[cfg(send_sync)]
59static_assertions::assert_impl_all!(SubmissionIndex: Send, Sync);
60
61/// Passed to [`Device::poll`] to control how and if it should block.
62pub type PollType = wgt::PollType<SubmissionIndex>;
63#[cfg(send_sync)]
64static_assertions::assert_impl_all!(PollType: Send, Sync);
65
66/// A write-only view into a staging buffer.
67///
68/// This type is what [`Queue::write_buffer_with()`] returns.
69pub struct QueueWriteBufferView {
70    queue: Queue,
71    buffer: Buffer,
72    offset: BufferAddress,
73    inner: dispatch::DispatchQueueWriteBuffer,
74}
75#[cfg(send_sync)]
76static_assertions::assert_impl_all!(QueueWriteBufferView: Send, Sync);
77
78impl QueueWriteBufferView {
79    #[cfg(custom)]
80    /// Returns custom implementation of QueueWriteBufferView (if custom backend and is internally T)
81    pub fn as_custom<T: custom::QueueWriteBufferInterface>(&self) -> Option<&T> {
82        self.inner.as_custom()
83    }
84}
85
86impl fmt::Debug for QueueWriteBufferView {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        f.debug_struct("QueueWriteBufferView")
89            .field("buffer", &self.buffer)
90            .field("offset", &self.offset)
91            .finish_non_exhaustive()
92    }
93}
94
95impl Drop for QueueWriteBufferView {
96    fn drop(&mut self) {
97        self.queue
98            .inner
99            .write_staging_buffer(&self.buffer.inner, self.offset, &self.inner);
100    }
101}
102
103/// These methods are equivalent to the methods of the same names on [`WriteOnly`].
104impl QueueWriteBufferView {
105    /// Returns the length of this view; the number of bytes to be written.
106    pub fn len(&self) -> usize {
107        self.inner.len()
108    }
109
110    /// Returns `true` if the view has a length of 0.
111    pub fn is_empty(&self) -> bool {
112        self.len() == 0
113    }
114
115    /// Returns a [`WriteOnly`] reference to a portion of this.
116    ///
117    /// `.slice(..)` can be used to access the whole data.
118    pub fn slice<'a, S: RangeBounds<usize>>(&'a mut self, bounds: S) -> WriteOnly<'a, [u8]> {
119        // SAFETY:
120        // * this is a write mapping
121        // * function signature ensures no aliasing
122        unsafe { self.inner.write_slice() }.into_slice(bounds)
123    }
124
125    /// Copies all elements from src into `self`.
126    ///
127    /// The length of `src` must be the same as `self`.
128    ///
129    /// This method is equivalent to
130    /// [`self.slice(..).copy_from_slice(src)`][WriteOnly::copy_from_slice].
131    pub fn copy_from_slice(&mut self, src: &[u8]) {
132        self.slice(..).copy_from_slice(src)
133    }
134}
135
136impl Queue {
137    /// Copies the bytes of `data` into `buffer` starting at `offset`.
138    ///
139    /// The data must be written fully in-bounds, that is, `offset + data.len() <= buffer.len()`.
140    ///
141    /// # Performance considerations
142    ///
143    /// * Calls to `write_buffer()` do *not* submit the transfer to the GPU
144    ///   immediately. They begin GPU execution only on the next call to
145    ///   [`Queue::submit()`], just before the explicitly submitted commands.
146    ///   To get a set of scheduled transfers started immediately,
147    ///   it's fine to call `submit` with no command buffers at all:
148    ///
149    ///   ```no_run
150    ///   # let queue: wgpu::Queue = todo!();
151    ///   # let buffer: wgpu::Buffer = todo!();
152    ///   # let data = [0u8];
153    ///   queue.write_buffer(&buffer, 0, &data);
154    ///   queue.submit([]);
155    ///   ```
156    ///
157    ///   However, `data` will be immediately copied into staging memory, so the
158    ///   caller may discard it any time after this call completes.
159    ///
160    /// * Consider using [`Queue::write_buffer_with()`] instead.
161    ///   That method allows you to prepare your data directly within the staging
162    ///   memory, rather than first placing it in a separate `[u8]` to be copied.
163    ///   That is, `queue.write_buffer(b, offset, data)` is approximately equivalent
164    ///   to `queue.write_buffer_with(b, offset, data.len()).copy_from_slice(data)`,
165    ///   so use `write_buffer_with()` if you can do something smarter than that
166    ///   [`copy_from_slice()`](slice::copy_from_slice). However, for small values
167    ///   (e.g. a typical uniform buffer whose contents come from a `struct`),
168    ///   there will likely be no difference, since the compiler will be able to
169    ///   optimize out unnecessary copies regardless.
170    ///
171    /// * Currently on native platforms, for both of these methods, the staging
172    ///   memory will be a new allocation. This will then be released after the
173    ///   next submission finishes. To entirely avoid short-lived allocations, you might
174    ///   be able to use [`StagingBelt`](crate::util::StagingBelt),
175    ///   or buffers you explicitly create, map, and unmap yourself.
176    pub fn write_buffer(&self, buffer: &Buffer, offset: BufferAddress, data: &[u8]) {
177        self.inner.write_buffer(&buffer.inner, offset, data);
178    }
179
180    /// Prepares to write data to a buffer via a mapped staging buffer.
181    ///
182    /// This operation allocates a temporary buffer and then returns a
183    /// [`QueueWriteBufferView`], which
184    ///
185    /// * dereferences to a `[u8]` of length `size`, and
186    /// * when dropped, schedules a copy of its contents into `buffer` at `offset`.
187    ///
188    /// Therefore, this obtains the same result as [`Queue::write_buffer()`], but may
189    /// allow you to skip one allocation and one copy of your data, if you are able to
190    /// assemble your data directly into the returned [`QueueWriteBufferView`] instead of
191    /// into a separate allocation like a [`Vec`](alloc::vec::Vec) first.
192    ///
193    /// The data must be written fully in-bounds, that is, `offset + size <= buffer.len()`.
194    ///
195    /// # Performance considerations
196    ///
197    /// * For small data not separately heap-allocated, there is no advantage of this
198    ///   over [`Queue::write_buffer()`].
199    ///
200    /// * Reading from the returned view may be slow, and will not yield the current
201    ///   contents of `buffer`. You should treat it as “write-only”.
202    ///
203    /// * Dropping the [`QueueWriteBufferView`] does *not* submit the
204    ///   transfer to the GPU immediately. The transfer begins only on the next
205    ///   call to [`Queue::submit()`] after the view is dropped, just before the
206    ///   explicitly submitted commands. To get a set of scheduled transfers started
207    ///   immediately, it's fine to call `queue.submit([])` with no command buffers at all.
208    ///
209    /// * Currently on native platforms, the staging memory will be a new allocation, which will
210    ///   then be released after the next submission finishes. To entirely avoid short-lived
211    ///   allocations, you might be able to use [`StagingBelt`](crate::util::StagingBelt),
212    ///   or buffers you explicitly create, map, and unmap yourself.
213    #[must_use]
214    pub fn write_buffer_with(
215        &self,
216        buffer: &Buffer,
217        offset: BufferAddress,
218        size: BufferSize,
219    ) -> Option<QueueWriteBufferView> {
220        profiling::scope!("Queue::write_buffer_with");
221        self.inner
222            .validate_write_buffer(&buffer.inner, offset, size)?;
223        let staging_buffer = self.inner.create_staging_buffer(size)?;
224        Some(QueueWriteBufferView {
225            queue: self.clone(),
226            buffer: buffer.clone(),
227            offset,
228            inner: staging_buffer,
229        })
230    }
231
232    /// Copies the bytes of `data` into a texture.
233    ///
234    /// * `data` contains the texels to be written, which must be in
235    ///   [the same format as the texture](TextureFormat).
236    /// * `data_layout` describes the memory layout of `data`, which does not necessarily
237    ///   have to have tightly packed rows.
238    /// * `texture` specifies the texture to write into, and the location within the
239    ///   texture (coordinate offset, mip level) that will be overwritten.
240    /// * `size` is the size, in texels, of the region to be written.
241    ///
242    /// This method fails if `size` overruns the size of `texture`, or if `data` is too short.
243    ///
244    /// # Performance considerations
245    ///
246    /// This operation has the same performance considerations as [`Queue::write_buffer()`];
247    /// see its documentation for details.
248    ///
249    /// However, since there is no “mapped texture” like a mapped buffer,
250    /// alternate techniques for writing to textures will generally consist of first copying
251    /// the data to a buffer, then using [`CommandEncoder::copy_buffer_to_texture()`], or in
252    /// some cases a compute shader, to copy texels from that buffer to the texture.
253    pub fn write_texture(
254        &self,
255        texture: TexelCopyTextureInfo<'_>,
256        data: &[u8],
257        data_layout: TexelCopyBufferLayout,
258        size: Extent3d,
259    ) {
260        self.inner.write_texture(texture, data, data_layout, size);
261    }
262
263    /// Schedule a copy of data from `image` into `texture`.
264    #[cfg(web)]
265    pub fn copy_external_image_to_texture(
266        &self,
267        source: &wgt::CopyExternalImageSourceInfo,
268        dest: wgt::CopyExternalImageDestInfo<&api::Texture>,
269        size: Extent3d,
270    ) {
271        self.inner
272            .copy_external_image_to_texture(source, dest, size);
273    }
274
275    /// Submits a series of finished command buffers for execution.
276    pub fn submit<I: IntoIterator<Item = CommandBuffer>>(
277        &self,
278        command_buffers: I,
279    ) -> SubmissionIndex {
280        // As submit drains the iterator (even on error), collect deferred actions
281        // from each CommandBuffer along the way.
282        let mut actions = DeferredCommandBufferActions::default();
283
284        let mut command_buffers = command_buffers.into_iter().map(|comb| {
285            actions.append(&mut comb.actions.lock());
286            comb.buffer
287        });
288        let index = self.inner.submit(&mut command_buffers);
289
290        // Execute all deferred actions after submit.
291        actions.execute(&self.inner);
292
293        SubmissionIndex { index }
294    }
295
296    /// Gets the amount of nanoseconds each tick of a timestamp query represents.
297    ///
298    /// Returns zero if timestamp queries are unsupported.
299    ///
300    /// Timestamp values are represented in nanosecond values on WebGPU, see <https://gpuweb.github.io/gpuweb/#timestamp>
301    /// Therefore, this is always 1.0 on the web, but on wgpu-core a manual conversion is required.
302    pub fn get_timestamp_period(&self) -> f32 {
303        self.inner.get_timestamp_period()
304    }
305
306    /// Registers a callback that is invoked when the previous [`Queue::submit`] finishes executing
307    /// on the GPU. When this callback runs, all mapped-buffer callbacks registered for the same
308    /// submission are guaranteed to have been called.
309    ///
310    /// For the callback to run, either [`queue.submit(..)`][q::s], [`instance.poll_all(..)`][i::p_a],
311    /// or [`device.poll(..)`][d::p] must be called elsewhere in the runtime, possibly integrated into
312    /// an event loop or run on a separate thread.
313    ///
314    /// The callback runs on the thread that first calls one of the above functions after the GPU work
315    /// completes. There are no restrictions on the code you can run in the callback; however, on native
316    /// the polling call will not return until the callback finishes, so keep callbacks short (set flags,
317    /// send messages, etc.).
318    ///
319    /// [q::s]: Queue::submit
320    /// [i::p_a]: Instance::poll_all
321    /// [d::p]: Device::poll
322    pub fn on_submitted_work_done(&self, callback: impl FnOnce() + Send + 'static) {
323        self.inner.on_submitted_work_done(Box::new(callback));
324    }
325
326    /// Get the [`wgpu_hal`] device from this `Queue`.
327    ///
328    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],
329    /// and pass that struct to the to the `A` type parameter.
330    ///
331    /// Returns a guard that dereferences to the type of the hal backend
332    /// which implements [`A::Queue`].
333    ///
334    /// # Types
335    ///
336    /// The returned type depends on the backend:
337    ///
338    #[doc = crate::macros::hal_type_vulkan!("Queue")]
339    #[doc = crate::macros::hal_type_metal!("Queue")]
340    #[doc = crate::macros::hal_type_dx12!("Queue")]
341    #[doc = crate::macros::hal_type_gles!("Queue")]
342    ///
343    /// # Errors
344    ///
345    /// This method will return None if:
346    /// - The queue is not from the backend specified by `A`.
347    /// - The queue is from the `webgpu` or `custom` backend.
348    ///
349    /// On the `webgpu` backend, use `as_webgpu` instead.
350    ///
351    /// # Safety
352    ///
353    /// - The returned resource must not be destroyed unless the guard
354    ///   is the last reference to it and it is not in use by the GPU.
355    ///   The guard and handle may be dropped at any time however.
356    /// - All the safety requirements of wgpu-hal must be upheld.
357    ///
358    /// [`A::Queue`]: hal::Api::Queue
359    #[cfg(wgpu_core)]
360    pub unsafe fn as_hal<A: hal::Api>(
361        &self,
362    ) -> Option<impl core::ops::Deref<Target = A::Queue> + WasmNotSendSync> {
363        let queue = self.inner.as_core_opt()?;
364        unsafe { queue.context.queue_as_hal::<A>(queue) }
365    }
366
367    /// Schedule a surface texture to be presented on the owning surface.
368    ///
369    /// Should be called after any work on the texture is submitted via [`Queue::submit`].
370    /// If no work was submitted, the texture will be cleared automatically before presenting.
371    ///
372    /// # Platform dependent behavior
373    ///
374    /// On Wayland, `present` will attach a `wl_buffer` to the underlying `wl_surface` and commit the new surface
375    /// state. If it is desired to do things such as request a frame callback, scale the surface using the viewporter
376    /// or synchronize other double buffered state, then these operations should be done before the call to `present`.
377    pub fn present(&self, mut surface_texture: SurfaceTexture) {
378        surface_texture.presented = true;
379        self.inner.present(&surface_texture.detail);
380    }
381
382    /// Compact a BLAS, it must have had [`Blas::prepare_compaction_async`] called on it and had the
383    /// callback provided called.
384    ///
385    /// The returned BLAS is more restricted than a normal BLAS because it may not be rebuilt or
386    /// compacted.
387    pub fn compact_blas(&self, blas: &Blas) -> Blas {
388        let (handle, dispatch) = self.inner.compact_blas(&blas.inner);
389        Blas {
390            handle,
391            inner: dispatch,
392        }
393    }
394}