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
6mod belt;
7mod device;
8mod encoder;
9mod init;
10mod texture_blitter;
11
12use alloc::{borrow::Cow, format, string::String, vec};
13use core::ptr::copy_nonoverlapping;
14
15pub use belt::StagingBelt;
16pub use device::{BufferInitDescriptor, DeviceExt};
17pub use encoder::RenderEncoder;
18pub use init::*;
19#[cfg(feature = "wgsl")]
20pub use texture_blitter::{TextureBlitter, TextureBlitterBuilder};
21pub use wgt::{
22 math::*, DispatchIndirectArgs, DrawIndexedIndirectArgs, DrawIndirectArgs, TextureDataOrder,
23};
24
25use crate::dispatch;
26
27/// Treat the given byte slice as a SPIR-V module.
28///
29/// # Panic
30///
31/// This function panics if:
32///
33/// - Input length isn't multiple of 4
34/// - Input is longer than [`usize::MAX`]
35/// - Input is empty
36/// - SPIR-V magic number is missing from beginning of stream
37#[cfg(feature = "spirv")]
38pub fn make_spirv(data: &[u8]) -> super::ShaderSource<'_> {
39 super::ShaderSource::SpirV(make_spirv_raw(data))
40}
41
42/// Version of `make_spirv` intended for use with [`Device::create_shader_module_passthrough`].
43/// Returns a raw slice instead of [`ShaderSource`](super::ShaderSource).
44///
45/// [`Device::create_shader_module_passthrough`]: crate::Device::create_shader_module_passthrough
46pub fn make_spirv_raw(data: &[u8]) -> Cow<'_, [u32]> {
47 const MAGIC_NUMBER: u32 = 0x0723_0203;
48 assert_eq!(
49 data.len() % size_of::<u32>(),
50 0,
51 "data size is not a multiple of 4"
52 );
53 assert_ne!(data.len(), 0, "data size must be larger than zero");
54
55 // If the data happens to be aligned, directly use the byte array,
56 // otherwise copy the byte array in an owned vector and use that instead.
57 let mut words = if data.as_ptr().align_offset(align_of::<u32>()) == 0 {
58 let (pre, words, post) = unsafe { data.align_to::<u32>() };
59 debug_assert!(pre.is_empty());
60 debug_assert!(post.is_empty());
61 Cow::from(words)
62 } else {
63 let mut words = vec![0u32; data.len() / size_of::<u32>()];
64 unsafe {
65 copy_nonoverlapping(data.as_ptr(), words.as_mut_ptr() as *mut u8, data.len());
66 }
67 Cow::from(words)
68 };
69
70 // Before checking if the data starts with the magic, check if it starts
71 // with the magic in non-native endianness, own & swap the data if so.
72 if words[0] == MAGIC_NUMBER.swap_bytes() {
73 for word in Cow::to_mut(&mut words) {
74 *word = word.swap_bytes();
75 }
76 }
77
78 assert_eq!(
79 words[0], MAGIC_NUMBER,
80 "wrong magic word {:x}. Make sure you are using a binary SPIRV file.",
81 words[0]
82 );
83
84 words
85}
86
87/// CPU accessible buffer used to download data back from the GPU.
88pub struct DownloadBuffer {
89 _gpu_buffer: super::Buffer,
90 mapped_range: dispatch::DispatchBufferMappedRange,
91}
92
93impl DownloadBuffer {
94 /// Asynchronously read the contents of a buffer.
95 pub fn read_buffer(
96 device: &super::Device,
97 queue: &super::Queue,
98 buffer: &super::BufferSlice<'_>,
99 callback: impl FnOnce(Result<Self, super::BufferAsyncError>) + Send + 'static,
100 ) {
101 let size = match buffer.size {
102 Some(size) => size.into(),
103 None => buffer.buffer.map_context.lock().total_size - buffer.offset,
104 };
105
106 let download = device.create_buffer(&super::BufferDescriptor {
107 size,
108 usage: super::BufferUsages::COPY_DST | super::BufferUsages::MAP_READ,
109 mapped_at_creation: false,
110 label: None,
111 });
112
113 let mut encoder =
114 device.create_command_encoder(&super::CommandEncoderDescriptor { label: None });
115 encoder.copy_buffer_to_buffer(buffer.buffer, buffer.offset, &download, 0, size);
116 let command_buffer: super::CommandBuffer = encoder.finish();
117 queue.submit(Some(command_buffer));
118
119 download
120 .clone()
121 .slice(..)
122 .map_async(super::MapMode::Read, move |result| {
123 if let Err(e) = result {
124 callback(Err(e));
125 return;
126 }
127
128 let mapped_range = download.inner.get_mapped_range(0..size);
129 callback(Ok(Self {
130 _gpu_buffer: download,
131 mapped_range,
132 }));
133 });
134 }
135}
136
137impl core::ops::Deref for DownloadBuffer {
138 type Target = [u8];
139 fn deref(&self) -> &[u8] {
140 self.mapped_range.slice()
141 }
142}
143
144/// A recommended key for storing [`PipelineCache`]s for the adapter
145/// associated with the given [`AdapterInfo`](wgt::AdapterInfo)
146/// This key will define a class of adapters for which the same cache
147/// might be valid.
148///
149/// If this returns `None`, the adapter doesn't support [`PipelineCache`].
150/// This may be because the API doesn't support application managed caches
151/// (such as browser WebGPU), or that `wgpu` hasn't implemented it for
152/// that API yet.
153///
154/// This key could be used as a filename, as seen in the example below.
155///
156/// # Examples
157///
158/// ```no_run
159/// # use std::path::PathBuf;
160/// use wgpu::PipelineCacheDescriptor;
161/// # let adapter_info = todo!();
162/// # let device: wgpu::Device = todo!();
163/// let cache_dir: PathBuf = unimplemented!("Some reasonable platform-specific cache directory for your app.");
164/// let filename = wgpu::util::pipeline_cache_key(&adapter_info);
165/// let (pipeline_cache, cache_file) = if let Some(filename) = filename {
166/// let cache_path = cache_dir.join(&filename);
167/// // If we failed to read the cache, for whatever reason, treat the data as lost.
168/// // In a real app, we'd probably avoid caching entirely unless the error was "file not found".
169/// let cache_data = std::fs::read(&cache_path).ok();
170/// let pipeline_cache = unsafe {
171/// device.create_pipeline_cache(&PipelineCacheDescriptor {
172/// data: cache_data.as_deref(),
173/// label: None,
174/// fallback: true
175/// })
176/// };
177/// (Some(pipeline_cache), Some(cache_path))
178/// } else {
179/// (None, None)
180/// };
181///
182/// // Run pipeline initialisation, making sure to set the `cache`
183/// // fields of your `*PipelineDescriptor` to `pipeline_cache`
184///
185/// // And then save the resulting cache (probably off the main thread).
186/// if let (Some(pipeline_cache), Some(cache_file)) = (pipeline_cache, cache_file) {
187/// let data = pipeline_cache.get_data();
188/// if let Some(data) = data {
189/// let temp_file = cache_file.with_extension("temp");
190/// std::fs::write(&temp_file, &data)?;
191/// std::fs::rename(&temp_file, &cache_file)?;
192/// }
193/// }
194/// # Ok::<_, std::io::Error>(())
195/// ```
196///
197/// [`PipelineCache`]: super::PipelineCache
198pub fn pipeline_cache_key(adapter_info: &wgt::AdapterInfo) -> Option<String> {
199 match adapter_info.backend {
200 wgt::Backend::Vulkan => Some(format!(
201 // The vendor/device should uniquely define a driver
202 // We/the driver will also later validate that the vendor/device and driver
203 // version match, which may lead to clearing an outdated
204 // cache for the same device.
205 "wgpu_pipeline_cache_vulkan_{}_{}",
206 adapter_info.vendor, adapter_info.device
207 )),
208 _ => None,
209 }
210}
211
212/// Adds extra conversion functions to `TextureFormat`.
213pub trait TextureFormatExt {
214 /// Finds the [`TextureFormat`](wgt::TextureFormat) corresponding to the given
215 /// [`StorageFormat`](wgc::naga::StorageFormat).
216 ///
217 /// # Examples
218 /// ```
219 /// use wgpu::util::TextureFormatExt;
220 /// assert_eq!(wgpu::TextureFormat::from_storage_format(wgpu::naga::StorageFormat::Bgra8Unorm), wgpu::TextureFormat::Bgra8Unorm);
221 /// ```
222 #[cfg(wgpu_core)]
223 fn from_storage_format(storage_format: crate::naga::StorageFormat) -> Self;
224
225 /// Finds the [`StorageFormat`](wgc::naga::StorageFormat) corresponding to the given [`TextureFormat`](wgt::TextureFormat).
226 /// Returns `None` if there is no matching storage format,
227 /// which typically indicates this format is not supported
228 /// for storage textures.
229 ///
230 /// # Examples
231 /// ```
232 /// use wgpu::util::TextureFormatExt;
233 /// assert_eq!(wgpu::TextureFormat::Bgra8Unorm.to_storage_format(), Some(wgpu::naga::StorageFormat::Bgra8Unorm));
234 /// ```
235 #[cfg(wgpu_core)]
236 fn to_storage_format(&self) -> Option<crate::naga::StorageFormat>;
237}
238
239impl TextureFormatExt for wgt::TextureFormat {
240 #[cfg(wgpu_core)]
241 fn from_storage_format(storage_format: crate::naga::StorageFormat) -> Self {
242 wgc::map_storage_format_from_naga(storage_format)
243 }
244
245 #[cfg(wgpu_core)]
246 fn to_storage_format(&self) -> Option<crate::naga::StorageFormat> {
247 wgc::map_storage_format_to_naga(*self)
248 }
249}