wgpu/util/
mod.rs

1//! Utility structures and functions that are built on top of the main `wgpu` API.
2//!
3//! Nothing in this module is a part of the WebGPU API specification;
4//! they are unique to the `wgpu` library.
5
6// TODO: For [`belt::StagingBelt`] to be available in `no_std` its usage of [`std::sync::mpsc`]
7// must be replaced with an appropriate alternative.
8#[cfg(std)]
9mod belt;
10mod device;
11mod encoder;
12mod init;
13#[cfg(webgpu)]
14mod panicking;
15mod spirv;
16mod texture_blitter;
17
18use alloc::{format, string::String};
19
20#[cfg(std)]
21pub use belt::StagingBelt;
22pub use device::{BufferInitDescriptor, DeviceExt};
23pub use encoder::RenderEncoder;
24pub use init::*;
25pub use spirv::*;
26#[cfg(feature = "wgsl")]
27pub use texture_blitter::{TextureBlitter, TextureBlitterBuilder};
28pub use wgt::{
29    math::*, DispatchIndirectArgs, DrawIndexedIndirectArgs, DrawIndirectArgs, TextureDataOrder,
30};
31
32#[cfg(webgpu)]
33pub(crate) use panicking::is_panicking;
34pub(crate) use wgpu_sync::Mutex;
35
36use crate::BufferUsages;
37
38/// CPU-accessible buffer used to retrieve data from buffers that cannot or must not be mapped.
39///
40/// This utility is a convenience wrapper around creating and mapping a temporary
41/// [`Buffer`][crate::Buffer].
42#[derive(Debug)]
43pub struct DownloadBuffer {
44    view: crate::BufferView,
45}
46
47impl DownloadBuffer {
48    /// Asynchronously read the contents of a buffer by copying it to a staging buffer.
49    ///
50    /// `buffer_slice`’s buffer must have been created with [`BufferUsages::COPY_SRC`].
51    /// The slice’s size must be a multiple of 4.
52    ///
53    /// `callback` will be called when the data is available.
54    /// If you are not submitting further work, you must call
55    /// [`Device::poll()`][crate::Device::poll] repeatedly until the callback completes.
56    pub fn read_buffer(
57        device: &super::Device,
58        queue: &super::Queue,
59        buffer_slice: &super::BufferSlice<'_>,
60        callback: impl FnOnce(Result<Self, super::BufferAsyncError>) + Send + 'static,
61    ) {
62        let size = buffer_slice.size;
63
64        let temporary_buffer = device.create_buffer(&super::BufferDescriptor {
65            size,
66            usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,
67            mapped_at_creation: false,
68            label: None,
69        });
70
71        let mut encoder =
72            device.create_command_encoder(&super::CommandEncoderDescriptor { label: None });
73        encoder.copy_buffer_to_buffer(
74            buffer_slice.buffer,
75            buffer_slice.offset,
76            &temporary_buffer,
77            0,
78            size,
79        );
80        queue.submit([encoder.finish()]);
81
82        temporary_buffer
83            .clone()
84            .map_async(super::MapMode::Read, .., move |result| {
85                if let Err(e) = result {
86                    callback(Err(e));
87                    return;
88                }
89
90                let view = match temporary_buffer.get_mapped_range(0..size) {
91                    Ok(range) => range,
92                    Err(e) => {
93                        callback(Err(super::BufferAsyncError));
94                        log::error!("Failed to get mapped range: {e}");
95                        return;
96                    }
97                };
98                callback(Ok(Self { view }));
99            });
100    }
101}
102
103impl core::ops::Deref for DownloadBuffer {
104    type Target = [u8];
105    fn deref(&self) -> &[u8] {
106        &self.view
107    }
108}
109
110/// A recommended key for storing [`PipelineCache`]s for the adapter
111/// associated with the given [`AdapterInfo`](wgt::AdapterInfo)
112/// This key will define a class of adapters for which the same cache
113/// might be valid.
114///
115/// If this returns `None`, the adapter doesn't support [`PipelineCache`].
116/// This may be because the API doesn't support application managed caches
117/// (such as browser WebGPU), or that `wgpu` hasn't implemented it for
118/// that API yet.
119///
120/// This key could be used as a filename, as seen in the example below.
121///
122/// # Examples
123///
124/// ```no_run
125/// # use std::path::PathBuf;
126/// use wgpu::PipelineCacheDescriptor;
127/// # let adapter_info = todo!();
128/// # let device: wgpu::Device = todo!();
129/// let cache_dir: PathBuf = unimplemented!("Some reasonable platform-specific cache directory for your app.");
130/// let filename = wgpu::util::pipeline_cache_key(&adapter_info);
131/// let (pipeline_cache, cache_file) = if let Some(filename) = filename {
132///     let cache_path = cache_dir.join(&filename);
133///     // If we failed to read the cache, for whatever reason, treat the data as lost.
134///     // In a real app, we'd probably avoid caching entirely unless the error was "file not found".
135///     let cache_data = std::fs::read(&cache_path).ok();
136///     let pipeline_cache = unsafe {
137///         device.create_pipeline_cache(&PipelineCacheDescriptor {
138///             data: cache_data.as_deref(),
139///             label: None,
140///             fallback: true
141///         })
142///     };
143///     (Some(pipeline_cache), Some(cache_path))
144/// } else {
145///     (None, None)
146/// };
147///
148/// // Run pipeline initialisation, making sure to set the `cache`
149/// // fields of your `*PipelineDescriptor` to `pipeline_cache`
150///
151/// // And then save the resulting cache (probably off the main thread).
152/// if let (Some(pipeline_cache), Some(cache_file)) = (pipeline_cache, cache_file) {
153///     let data = pipeline_cache.get_data();
154///     if let Some(data) = data {
155///         let temp_file = cache_file.with_extension("temp");
156///         std::fs::write(&temp_file, &data)?;
157///         std::fs::rename(&temp_file, &cache_file)?;
158///     }
159/// }
160/// # Ok::<_, std::io::Error>(())
161/// ```
162///
163/// [`PipelineCache`]: super::PipelineCache
164pub fn pipeline_cache_key(adapter_info: &wgt::AdapterInfo) -> Option<String> {
165    match adapter_info.backend {
166        wgt::Backend::Vulkan => Some(format!(
167            // The vendor/device should uniquely define a driver
168            // We/the driver will also later validate that the vendor/device and driver
169            // version match, which may lead to clearing an outdated
170            // cache for the same device.
171            "wgpu_pipeline_cache_vulkan_{}_{}",
172            adapter_info.vendor, adapter_info.device
173        )),
174        _ => None,
175    }
176}
177
178/// Adds extra conversion functions to `TextureFormat`.
179pub trait TextureFormatExt {
180    /// Finds the [`TextureFormat`](wgt::TextureFormat) corresponding to the given
181    /// [`StorageFormat`](wgc::naga::StorageFormat).
182    ///
183    /// # Examples
184    /// ```
185    /// use wgpu::util::TextureFormatExt;
186    /// assert_eq!(wgpu::TextureFormat::from_storage_format(wgpu::naga::StorageFormat::Bgra8Unorm), wgpu::TextureFormat::Bgra8Unorm);
187    /// ```
188    #[cfg(wgpu_core)]
189    fn from_storage_format(storage_format: crate::naga::StorageFormat) -> Self;
190
191    /// Finds the [`StorageFormat`](wgc::naga::StorageFormat) corresponding to the given [`TextureFormat`](wgt::TextureFormat).
192    /// Returns `None` if there is no matching storage format,
193    /// which typically indicates this format is not supported
194    /// for storage textures.
195    ///
196    /// # Examples
197    /// ```
198    /// use wgpu::util::TextureFormatExt;
199    /// assert_eq!(wgpu::TextureFormat::Bgra8Unorm.to_storage_format(), Some(wgpu::naga::StorageFormat::Bgra8Unorm));
200    /// ```
201    #[cfg(wgpu_core)]
202    fn to_storage_format(&self) -> Option<crate::naga::StorageFormat>;
203}
204
205impl TextureFormatExt for wgt::TextureFormat {
206    #[cfg(wgpu_core)]
207    fn from_storage_format(storage_format: crate::naga::StorageFormat) -> Self {
208        wgc::map_storage_format_from_naga(storage_format)
209    }
210
211    #[cfg(wgpu_core)]
212    fn to_storage_format(&self) -> Option<crate::naga::StorageFormat> {
213        wgc::map_storage_format_to_naga(*self)
214    }
215}