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;
13mod mutex;
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
32pub(crate) use mutex::Mutex;
33pub(crate) use panicking::is_panicking;
34
35use crate::dispatch;
36
37/// CPU accessible buffer used to download data back from the GPU.
38pub struct DownloadBuffer {
39    _gpu_buffer: super::Buffer,
40    mapped_range: dispatch::DispatchBufferMappedRange,
41}
42
43impl DownloadBuffer {
44    /// Asynchronously read the contents of a buffer.
45    pub fn read_buffer(
46        device: &super::Device,
47        queue: &super::Queue,
48        buffer: &super::BufferSlice<'_>,
49        callback: impl FnOnce(Result<Self, super::BufferAsyncError>) + Send + 'static,
50    ) {
51        let size = buffer.size.into();
52
53        let download = device.create_buffer(&super::BufferDescriptor {
54            size,
55            usage: super::BufferUsages::COPY_DST | super::BufferUsages::MAP_READ,
56            mapped_at_creation: false,
57            label: None,
58        });
59
60        let mut encoder =
61            device.create_command_encoder(&super::CommandEncoderDescriptor { label: None });
62        encoder.copy_buffer_to_buffer(buffer.buffer, buffer.offset, &download, 0, size);
63        let command_buffer: super::CommandBuffer = encoder.finish();
64        queue.submit(Some(command_buffer));
65
66        download
67            .clone()
68            .slice(..)
69            .map_async(super::MapMode::Read, move |result| {
70                if let Err(e) = result {
71                    callback(Err(e));
72                    return;
73                }
74
75                let mapped_range = match download.inner.get_mapped_range(0..size) {
76                    Ok(range) => range,
77                    Err(e) => {
78                        callback(Err(super::BufferAsyncError));
79                        log::error!("Failed to get mapped range: {e}");
80                        return;
81                    }
82                };
83                callback(Ok(Self {
84                    _gpu_buffer: download,
85                    mapped_range,
86                }));
87            });
88    }
89}
90
91impl core::ops::Deref for DownloadBuffer {
92    type Target = [u8];
93    fn deref(&self) -> &[u8] {
94        // SAFETY: `self.mapped_range` is always a read mapping
95        unsafe { self.mapped_range.read_slice() }
96    }
97}
98
99/// A recommended key for storing [`PipelineCache`]s for the adapter
100/// associated with the given [`AdapterInfo`](wgt::AdapterInfo)
101/// This key will define a class of adapters for which the same cache
102/// might be valid.
103///
104/// If this returns `None`, the adapter doesn't support [`PipelineCache`].
105/// This may be because the API doesn't support application managed caches
106/// (such as browser WebGPU), or that `wgpu` hasn't implemented it for
107/// that API yet.
108///
109/// This key could be used as a filename, as seen in the example below.
110///
111/// # Examples
112///
113/// ```no_run
114/// # use std::path::PathBuf;
115/// use wgpu::PipelineCacheDescriptor;
116/// # let adapter_info = todo!();
117/// # let device: wgpu::Device = todo!();
118/// let cache_dir: PathBuf = unimplemented!("Some reasonable platform-specific cache directory for your app.");
119/// let filename = wgpu::util::pipeline_cache_key(&adapter_info);
120/// let (pipeline_cache, cache_file) = if let Some(filename) = filename {
121///     let cache_path = cache_dir.join(&filename);
122///     // If we failed to read the cache, for whatever reason, treat the data as lost.
123///     // In a real app, we'd probably avoid caching entirely unless the error was "file not found".
124///     let cache_data = std::fs::read(&cache_path).ok();
125///     let pipeline_cache = unsafe {
126///         device.create_pipeline_cache(&PipelineCacheDescriptor {
127///             data: cache_data.as_deref(),
128///             label: None,
129///             fallback: true
130///         })
131///     };
132///     (Some(pipeline_cache), Some(cache_path))
133/// } else {
134///     (None, None)
135/// };
136///
137/// // Run pipeline initialisation, making sure to set the `cache`
138/// // fields of your `*PipelineDescriptor` to `pipeline_cache`
139///
140/// // And then save the resulting cache (probably off the main thread).
141/// if let (Some(pipeline_cache), Some(cache_file)) = (pipeline_cache, cache_file) {
142///     let data = pipeline_cache.get_data();
143///     if let Some(data) = data {
144///         let temp_file = cache_file.with_extension("temp");
145///         std::fs::write(&temp_file, &data)?;
146///         std::fs::rename(&temp_file, &cache_file)?;
147///     }
148/// }
149/// # Ok::<_, std::io::Error>(())
150/// ```
151///
152/// [`PipelineCache`]: super::PipelineCache
153pub fn pipeline_cache_key(adapter_info: &wgt::AdapterInfo) -> Option<String> {
154    match adapter_info.backend {
155        wgt::Backend::Vulkan => Some(format!(
156            // The vendor/device should uniquely define a driver
157            // We/the driver will also later validate that the vendor/device and driver
158            // version match, which may lead to clearing an outdated
159            // cache for the same device.
160            "wgpu_pipeline_cache_vulkan_{}_{}",
161            adapter_info.vendor, adapter_info.device
162        )),
163        _ => None,
164    }
165}
166
167/// Adds extra conversion functions to `TextureFormat`.
168pub trait TextureFormatExt {
169    /// Finds the [`TextureFormat`](wgt::TextureFormat) corresponding to the given
170    /// [`StorageFormat`](wgc::naga::StorageFormat).
171    ///
172    /// # Examples
173    /// ```
174    /// use wgpu::util::TextureFormatExt;
175    /// assert_eq!(wgpu::TextureFormat::from_storage_format(wgpu::naga::StorageFormat::Bgra8Unorm), wgpu::TextureFormat::Bgra8Unorm);
176    /// ```
177    #[cfg(wgpu_core)]
178    fn from_storage_format(storage_format: crate::naga::StorageFormat) -> Self;
179
180    /// Finds the [`StorageFormat`](wgc::naga::StorageFormat) corresponding to the given [`TextureFormat`](wgt::TextureFormat).
181    /// Returns `None` if there is no matching storage format,
182    /// which typically indicates this format is not supported
183    /// for storage textures.
184    ///
185    /// # Examples
186    /// ```
187    /// use wgpu::util::TextureFormatExt;
188    /// assert_eq!(wgpu::TextureFormat::Bgra8Unorm.to_storage_format(), Some(wgpu::naga::StorageFormat::Bgra8Unorm));
189    /// ```
190    #[cfg(wgpu_core)]
191    fn to_storage_format(&self) -> Option<crate::naga::StorageFormat>;
192}
193
194impl TextureFormatExt for wgt::TextureFormat {
195    #[cfg(wgpu_core)]
196    fn from_storage_format(storage_format: crate::naga::StorageFormat) -> Self {
197        wgc::map_storage_format_from_naga(storage_format)
198    }
199
200    #[cfg(wgpu_core)]
201    fn to_storage_format(&self) -> Option<crate::naga::StorageFormat> {
202        wgc::map_storage_format_to_naga(*self)
203    }
204}