Skip to content

Commit 168e573

Browse files
kpreidcwfitzgerald
authored andcommitted
Move SPIR-V util functions to a separate module.
1 parent 9611ecb commit 168e573

File tree

2 files changed

+126
-120
lines changed

2 files changed

+126
-120
lines changed

wgpu/src/util/mod.rs

Lines changed: 3 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,17 @@ mod encoder;
1212
mod init;
1313
mod mutex;
1414
mod panicking;
15+
mod spirv;
1516
mod texture_blitter;
1617

17-
use alloc::{borrow::Cow, format, string::String, vec};
18-
use core::{mem, ptr::copy_nonoverlapping};
18+
use alloc::{format, string::String};
1919

2020
#[cfg(std)]
2121
pub use belt::StagingBelt;
2222
pub use device::{BufferInitDescriptor, DeviceExt};
2323
pub use encoder::RenderEncoder;
2424
pub use init::*;
25+
pub use spirv::*;
2526
#[cfg(feature = "wgsl")]
2627
pub use texture_blitter::{TextureBlitter, TextureBlitterBuilder};
2728
pub use wgt::{
@@ -33,124 +34,6 @@ pub(crate) use panicking::is_panicking;
3334

3435
use crate::dispatch;
3536

36-
/// Treat the given byte slice as a SPIR-V module.
37-
///
38-
/// # Panic
39-
///
40-
/// This function panics if:
41-
///
42-
/// - Input length isn't multiple of 4
43-
/// - Input is longer than [`usize::MAX`]
44-
/// - Input is empty
45-
/// - SPIR-V magic number is missing from beginning of stream
46-
#[cfg(feature = "spirv")]
47-
pub fn make_spirv(data: &[u8]) -> super::ShaderSource<'_> {
48-
super::ShaderSource::SpirV(make_spirv_raw(data))
49-
}
50-
51-
const SPIRV_MAGIC_NUMBER: u32 = 0x0723_0203;
52-
53-
const fn check_spirv_len(data: &[u8]) {
54-
assert!(
55-
data.len() % size_of::<u32>() == 0,
56-
"SPIRV data size must be a multiple of 4."
57-
);
58-
assert!(!data.is_empty(), "SPIRV data must not be empty.");
59-
}
60-
61-
const fn verify_spirv_magic(words: &[u32]) {
62-
assert!(
63-
words[0] == SPIRV_MAGIC_NUMBER,
64-
"Wrong magic word in data. Make sure you are using a binary SPIRV file.",
65-
);
66-
}
67-
68-
/// Version of `make_spirv` intended for use with [`Device::create_shader_module_passthrough`].
69-
/// Returns a raw slice instead of [`ShaderSource`](super::ShaderSource).
70-
///
71-
/// [`Device::create_shader_module_passthrough`]: crate::Device::create_shader_module_passthrough
72-
pub fn make_spirv_raw(data: &[u8]) -> Cow<'_, [u32]> {
73-
check_spirv_len(data);
74-
75-
// If the data happens to be aligned, directly use the byte array,
76-
// otherwise copy the byte array in an owned vector and use that instead.
77-
let mut words = if data.as_ptr().align_offset(align_of::<u32>()) == 0 {
78-
let (pre, words, post) = unsafe { data.align_to::<u32>() };
79-
debug_assert!(pre.is_empty());
80-
debug_assert!(post.is_empty());
81-
Cow::from(words)
82-
} else {
83-
let mut words = vec![0u32; data.len() / size_of::<u32>()];
84-
unsafe {
85-
copy_nonoverlapping(data.as_ptr(), words.as_mut_ptr() as *mut u8, data.len());
86-
}
87-
Cow::from(words)
88-
};
89-
90-
// Before checking if the data starts with the magic, check if it starts
91-
// with the magic in non-native endianness, own & swap the data if so.
92-
if words[0] == SPIRV_MAGIC_NUMBER.swap_bytes() {
93-
for word in Cow::to_mut(&mut words) {
94-
*word = word.swap_bytes();
95-
}
96-
}
97-
98-
verify_spirv_magic(&words);
99-
100-
words
101-
}
102-
103-
/// Version of `make_spirv_raw` used for implementing [`include_spirv!`] and [`include_spirv_raw!`] macros.
104-
///
105-
/// Not public API. Also, don't even try calling at runtime; you'll get a stack overflow.
106-
///
107-
/// [`include_spirv!`]: crate::include_spirv
108-
#[doc(hidden)]
109-
pub const fn make_spirv_const<const IN: usize, const OUT: usize>(data: [u8; IN]) -> [u32; OUT] {
110-
#[repr(align(4))]
111-
struct Aligned<T: ?Sized>(T);
112-
113-
check_spirv_len(&data);
114-
115-
// NOTE: to get around lack of generic const expressions
116-
assert!(IN / 4 == OUT);
117-
118-
let aligned = Aligned(data);
119-
let mut words: [u32; OUT] = unsafe { mem::transmute_copy(&aligned) };
120-
121-
// Before checking if the data starts with the magic, check if it starts
122-
// with the magic in non-native endianness, own & swap the data if so.
123-
if words[0] == SPIRV_MAGIC_NUMBER.swap_bytes() {
124-
let mut idx = 0;
125-
while idx < words.len() {
126-
words[idx] = words[idx].swap_bytes();
127-
idx += 1;
128-
}
129-
}
130-
131-
verify_spirv_magic(&words);
132-
133-
words
134-
}
135-
136-
#[should_panic = "multiple of 4"]
137-
#[test]
138-
fn make_spirv_le_fail() {
139-
let _: [u32; 1] = make_spirv_const([0x03, 0x02, 0x23, 0x07, 0x44, 0x33]);
140-
}
141-
142-
#[should_panic = "multiple of 4"]
143-
#[test]
144-
fn make_spirv_be_fail() {
145-
let _: [u32; 1] = make_spirv_const([0x07, 0x23, 0x02, 0x03, 0x11, 0x22]);
146-
}
147-
148-
#[should_panic = "empty"]
149-
#[test]
150-
fn make_spirv_empty() {
151-
let _: [u32; 0] = make_spirv_const([]);
152-
}
153-
15437
/// CPU accessible buffer used to download data back from the GPU.
15538
pub struct DownloadBuffer {
15639
_gpu_buffer: super::Buffer,

wgpu/src/util/spirv.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use alloc::{borrow::Cow, vec};
2+
use core::{mem, ptr};
3+
4+
#[cfg(doc)]
5+
use crate::Device;
6+
7+
const SPIRV_MAGIC_NUMBER: u32 = 0x0723_0203;
8+
9+
/// Treat the given byte slice as a SPIR-V module.
10+
///
11+
/// # Panic
12+
///
13+
/// This function panics if:
14+
///
15+
/// - Input length isn't multiple of 4
16+
/// - Input is longer than [`usize::MAX`]
17+
/// - Input is empty
18+
/// - SPIR-V magic number is missing from beginning of stream
19+
#[cfg(feature = "spirv")]
20+
pub fn make_spirv(data: &[u8]) -> crate::ShaderSource<'_> {
21+
crate::ShaderSource::SpirV(make_spirv_raw(data))
22+
}
23+
24+
const fn check_spirv_len(data: &[u8]) {
25+
assert!(
26+
data.len() % size_of::<u32>() == 0,
27+
"SPIRV data size must be a multiple of 4."
28+
);
29+
assert!(!data.is_empty(), "SPIRV data must not be empty.");
30+
}
31+
32+
const fn verify_spirv_magic(words: &[u32]) {
33+
assert!(
34+
words[0] == SPIRV_MAGIC_NUMBER,
35+
"Wrong magic word in data. Make sure you are using a binary SPIRV file.",
36+
);
37+
}
38+
39+
/// Version of `make_spirv` intended for use with [`Device::create_shader_module_passthrough`].
40+
/// Returns a raw slice instead of [`ShaderSource`](super::ShaderSource).
41+
///
42+
/// [`Device::create_shader_module_passthrough`]: crate::Device::create_shader_module_passthrough
43+
pub fn make_spirv_raw(data: &[u8]) -> Cow<'_, [u32]> {
44+
check_spirv_len(data);
45+
46+
// If the data happens to be aligned, directly use the byte array,
47+
// otherwise copy the byte array in an owned vector and use that instead.
48+
let mut words = if data.as_ptr().align_offset(align_of::<u32>()) == 0 {
49+
let (pre, words, post) = unsafe { data.align_to::<u32>() };
50+
debug_assert!(pre.is_empty());
51+
debug_assert!(post.is_empty());
52+
Cow::from(words)
53+
} else {
54+
let mut words = vec![0u32; data.len() / size_of::<u32>()];
55+
unsafe {
56+
ptr::copy_nonoverlapping(data.as_ptr(), words.as_mut_ptr() as *mut u8, data.len());
57+
}
58+
Cow::from(words)
59+
};
60+
61+
// Before checking if the data starts with the magic, check if it starts
62+
// with the magic in non-native endianness, own & swap the data if so.
63+
if words[0] == SPIRV_MAGIC_NUMBER.swap_bytes() {
64+
for word in Cow::to_mut(&mut words) {
65+
*word = word.swap_bytes();
66+
}
67+
}
68+
69+
verify_spirv_magic(&words);
70+
71+
words
72+
}
73+
74+
/// Version of `make_spirv_raw` used for implementing [`include_spirv!`] and [`include_spirv_raw!`] macros.
75+
///
76+
/// Not public API. Also, don't even try calling at runtime; you'll get a stack overflow.
77+
///
78+
/// [`include_spirv!`]: crate::include_spirv
79+
#[doc(hidden)]
80+
pub const fn make_spirv_const<const IN: usize, const OUT: usize>(data: [u8; IN]) -> [u32; OUT] {
81+
#[repr(align(4))]
82+
struct Aligned<T: ?Sized>(T);
83+
84+
check_spirv_len(&data);
85+
86+
// NOTE: to get around lack of generic const expressions
87+
assert!(IN / 4 == OUT);
88+
89+
let aligned = Aligned(data);
90+
let mut words: [u32; OUT] = unsafe { mem::transmute_copy(&aligned) };
91+
92+
// Before checking if the data starts with the magic, check if it starts
93+
// with the magic in non-native endianness, own & swap the data if so.
94+
if words[0] == SPIRV_MAGIC_NUMBER.swap_bytes() {
95+
let mut idx = 0;
96+
while idx < words.len() {
97+
words[idx] = words[idx].swap_bytes();
98+
idx += 1;
99+
}
100+
}
101+
102+
verify_spirv_magic(&words);
103+
104+
words
105+
}
106+
107+
#[should_panic = "multiple of 4"]
108+
#[test]
109+
fn make_spirv_le_fail() {
110+
let _: [u32; 1] = make_spirv_const([0x03, 0x02, 0x23, 0x07, 0x44, 0x33]);
111+
}
112+
113+
#[should_panic = "multiple of 4"]
114+
#[test]
115+
fn make_spirv_be_fail() {
116+
let _: [u32; 1] = make_spirv_const([0x07, 0x23, 0x02, 0x03, 0x11, 0x22]);
117+
}
118+
119+
#[should_panic = "empty"]
120+
#[test]
121+
fn make_spirv_empty() {
122+
let _: [u32; 0] = make_spirv_const([]);
123+
}

0 commit comments

Comments
 (0)