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