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