Skip to content

Commit 1a3a12f

Browse files
authored
Implement some float packing/unpacking instructions (#709)
* Implement some float packing/unpacking instructions * Rename f32xN to vecN in packing functions
1 parent 46c9ea0 commit 1a3a12f

File tree

2 files changed

+297
-0
lines changed

2 files changed

+297
-0
lines changed

crates/spirv-std/src/float.rs

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::vector::Vector;
2+
13
/// Abstract trait representing a SPIR-V floating point type.
24
pub unsafe trait Float: num_traits::Float + crate::scalar::Scalar + Default {
35
const WIDTH: usize;
@@ -10,3 +12,233 @@ unsafe impl Float for f32 {
1012
unsafe impl Float for f64 {
1113
const WIDTH: usize = 64;
1214
}
15+
16+
/// Converts two f32 values (floats) into two f16 values (halfs). The result is a u32, with the low
17+
/// 16 bits being the first f16, and the high 16 bits being the second f16.
18+
#[spirv_std_macros::gpu_only]
19+
pub fn vec2_to_f16x2(vec: impl Vector<f32, 2>) -> u32 {
20+
let result;
21+
unsafe {
22+
asm!(
23+
"%glsl = OpExtInstImport \"GLSL.std.450\"",
24+
"%uint = OpTypeInt 32 0",
25+
"%vec = OpLoad _ {vec}",
26+
// 58 = PackHalf2x16
27+
"{result} = OpExtInst %uint %glsl 58 %vec",
28+
vec = in(reg) &vec,
29+
result = out(reg) result,
30+
);
31+
}
32+
result
33+
}
34+
35+
/// Converts two f16 values (halfs) into two f32 values (floats). The parameter is a u32, with the
36+
/// low 16 bits being the first f16, and the high 16 bits being the second f16.
37+
#[spirv_std_macros::gpu_only]
38+
pub fn f16x2_to_vec2<V: Vector<f32, 2>>(int: u32) -> V {
39+
let mut result = Default::default();
40+
unsafe {
41+
asm!(
42+
"%glsl = OpExtInstImport \"GLSL.std.450\"",
43+
"%float = OpTypeFloat 32",
44+
"%vec2 = OpTypeVector %float 2",
45+
// 62 = UnpackHalf2x16
46+
"%result = OpExtInst %vec2 %glsl 62 {int}",
47+
"OpStore {result} %result",
48+
int = in(reg) int,
49+
result = in(reg) &mut result,
50+
);
51+
}
52+
result
53+
}
54+
55+
// We don't have access to a concrete vector type (cfg(feature = "glam") might not be enabled), so
56+
// synth up one manually.
57+
#[cfg_attr(target_arch = "spirv", repr(simd))]
58+
// sometimes dead because on cpu, the `gpu_only` macro nukes the method bodies
59+
#[allow(dead_code)]
60+
#[derive(Default)]
61+
struct F32x2 {
62+
x: f32,
63+
y: f32,
64+
}
65+
unsafe impl Vector<f32, 2> for F32x2 {}
66+
67+
/// Converts an f32 (float) into an f16 (half). The result is a u32, not a u16, due to GPU support
68+
/// for u16 not being universal - the upper 16 bits will always be zero.
69+
#[spirv_std_macros::gpu_only]
70+
pub fn f32_to_f16(float: f32) -> u32 {
71+
vec2_to_f16x2(F32x2 { x: float, y: 0.0 })
72+
}
73+
74+
/// Converts an f16 (half) into an f32 (float). The parameter is a u32, due to GPU support for u16
75+
/// not being universal - the upper 16 bits are ignored.
76+
#[cfg(feature = "glam")]
77+
#[spirv_std_macros::gpu_only]
78+
pub fn f16_to_f32(packed: u32) -> f32 {
79+
f16x2_to_vec2::<F32x2>(packed).x
80+
}
81+
82+
/// Packs a vec4 into 4 8-bit signed integers. See
83+
/// [PackSnorm4x8](https://www.khronos.org/registry/SPIR-V/specs/1.0/GLSL.std.450.html) for exact
84+
/// semantics.
85+
#[spirv_std_macros::gpu_only]
86+
pub fn vec4_to_u8x4_snorm(vec: impl Vector<f32, 4>) -> u32 {
87+
let result;
88+
unsafe {
89+
asm!(
90+
"%glsl = OpExtInstImport \"GLSL.std.450\"",
91+
"%uint = OpTypeInt 32 0",
92+
"%vec = OpLoad _ {vec}",
93+
// 54 = PackSnorm4x8
94+
"{result} = OpExtInst %uint %glsl 54 %vec",
95+
vec = in(reg) &vec,
96+
result = out(reg) result,
97+
);
98+
}
99+
result
100+
}
101+
102+
/// Packs a vec4 into 4 8-bit unsigned integers. See
103+
/// [PackUnorm4x8](https://www.khronos.org/registry/SPIR-V/specs/1.0/GLSL.std.450.html) for exact
104+
/// semantics.
105+
#[spirv_std_macros::gpu_only]
106+
pub fn vec4_to_u8x4_unorm(vec: impl Vector<f32, 4>) -> u32 {
107+
let result;
108+
unsafe {
109+
asm!(
110+
"%glsl = OpExtInstImport \"GLSL.std.450\"",
111+
"%uint = OpTypeInt 32 0",
112+
"%vec = OpLoad _ {vec}",
113+
// 55 = PackUnorm4x8
114+
"{result} = OpExtInst %uint %glsl 55 %vec",
115+
vec = in(reg) &vec,
116+
result = out(reg) result,
117+
);
118+
}
119+
result
120+
}
121+
122+
/// Packs a vec2 into 2 16-bit signed integers. See
123+
/// [PackSnorm2x16](https://www.khronos.org/registry/SPIR-V/specs/1.0/GLSL.std.450.html) for exact
124+
/// semantics.
125+
#[spirv_std_macros::gpu_only]
126+
pub fn vec2_to_u16x2_snorm(vec: impl Vector<f32, 2>) -> u32 {
127+
let result;
128+
unsafe {
129+
asm!(
130+
"%glsl = OpExtInstImport \"GLSL.std.450\"",
131+
"%uint = OpTypeInt 32 0",
132+
"%vec = OpLoad _ {vec}",
133+
// 56 = PackSnorm2x16
134+
"{result} = OpExtInst %uint %glsl 56 %vec",
135+
vec = in(reg) &vec,
136+
result = out(reg) result,
137+
);
138+
}
139+
result
140+
}
141+
142+
/// Packs a vec2 into 2 16-bit unsigned integers. See
143+
/// [PackUnorm2x16](https://www.khronos.org/registry/SPIR-V/specs/1.0/GLSL.std.450.html) for exact
144+
/// semantics.
145+
#[spirv_std_macros::gpu_only]
146+
pub fn vec2_to_u16x2_unorm(vec: impl Vector<f32, 2>) -> u32 {
147+
let result;
148+
unsafe {
149+
asm!(
150+
"%glsl = OpExtInstImport \"GLSL.std.450\"",
151+
"%uint = OpTypeInt 32 0",
152+
"%vec = OpLoad _ {vec}",
153+
// 57 = PackUnorm2x16
154+
"{result} = OpExtInst %uint %glsl 57 %vec",
155+
vec = in(reg) &vec,
156+
result = out(reg) result,
157+
);
158+
}
159+
result
160+
}
161+
162+
/// Unpacks 4 8-bit signed integers into a vec4. See
163+
/// [UnpackSnorm4x8](https://www.khronos.org/registry/SPIR-V/specs/1.0/GLSL.std.450.html) for exact
164+
/// semantics.
165+
#[spirv_std_macros::gpu_only]
166+
pub fn u8x4_to_vec4_snorm<V: Vector<f32, 4>>(int: u32) -> V {
167+
let mut result = Default::default();
168+
unsafe {
169+
asm!(
170+
"%glsl = OpExtInstImport \"GLSL.std.450\"",
171+
"%float = OpTypeFloat 32",
172+
"%vec4 = OpTypeVector %float 4",
173+
// 63 = UnpackSnorm4x8
174+
"%result = OpExtInst %vec4 %glsl 63 {int}",
175+
"OpStore {result} %result",
176+
int = in(reg) int,
177+
result = in(reg) &mut result,
178+
);
179+
}
180+
result
181+
}
182+
183+
/// Unpacks 4 8-bit unsigned integers into a vec4. See
184+
/// [UnpackSnorm4x8](https://www.khronos.org/registry/SPIR-V/specs/1.0/GLSL.std.450.html) for exact
185+
/// semantics.
186+
#[spirv_std_macros::gpu_only]
187+
pub fn u8x4_to_vec4_unorm<V: Vector<f32, 4>>(int: u32) -> V {
188+
let mut result = Default::default();
189+
unsafe {
190+
asm!(
191+
"%glsl = OpExtInstImport \"GLSL.std.450\"",
192+
"%float = OpTypeFloat 32",
193+
"%vec4 = OpTypeVector %float 4",
194+
// 64 = UnpackUnorm4x8
195+
"%result = OpExtInst %vec4 %glsl 64 {int}",
196+
"OpStore {result} %result",
197+
int = in(reg) int,
198+
result = in(reg) &mut result,
199+
);
200+
}
201+
result
202+
}
203+
204+
/// Unpacks 2 16-bit signed integers into a vec2. See
205+
/// [UnpackSnorm2x16](https://www.khronos.org/registry/SPIR-V/specs/1.0/GLSL.std.450.html) for
206+
/// exact semantics.
207+
#[spirv_std_macros::gpu_only]
208+
pub fn u16x2_to_vec2_snorm<V: Vector<f32, 2>>(int: u32) -> V {
209+
let mut result = Default::default();
210+
unsafe {
211+
asm!(
212+
"%glsl = OpExtInstImport \"GLSL.std.450\"",
213+
"%float = OpTypeFloat 32",
214+
"%vec2 = OpTypeVector %float 2",
215+
// 60 = UnpackSnorm2x16
216+
"%result = OpExtInst %vec2 %glsl 60 {int}",
217+
"OpStore {result} %result",
218+
int = in(reg) int,
219+
result = in(reg) &mut result,
220+
);
221+
}
222+
result
223+
}
224+
225+
/// Unpacks 2 16-bit unsigned integers into a vec2. See
226+
/// [UnpackUnorm2x16](https://www.khronos.org/registry/SPIR-V/specs/1.0/GLSL.std.450.html) for
227+
/// exact semantics.
228+
#[spirv_std_macros::gpu_only]
229+
pub fn u16x2_to_vec2_unorm<V: Vector<f32, 2>>(int: u32) -> V {
230+
let mut result = Default::default();
231+
unsafe {
232+
asm!(
233+
"%glsl = OpExtInstImport \"GLSL.std.450\"",
234+
"%float = OpTypeFloat 32",
235+
"%vec2 = OpTypeVector %float 2",
236+
// 61 = UnpackUnorm2x16
237+
"%result = OpExtInst %vec2 %glsl 61 {int}",
238+
"OpStore {result} %result",
239+
int = in(reg) int,
240+
result = in(reg) &mut result,
241+
);
242+
}
243+
result
244+
}

tests/ui/lang/f32/packing.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Test that various packing methods work.
2+
// build-pass
3+
4+
use spirv_std::float::*;
5+
use spirv_std::glam::{Vec2, Vec4};
6+
7+
#[spirv(fragment)]
8+
pub fn test_vec2_to_f16x2(i: Vec2, o: &mut u32) {
9+
*o = vec2_to_f16x2(i);
10+
}
11+
12+
#[spirv(fragment)]
13+
pub fn test_f16x2_to_vec2(i: u32, o: &mut Vec2) {
14+
*o = f16x2_to_vec2(i);
15+
}
16+
17+
#[spirv(fragment)]
18+
pub fn test_f32_to_f16(i: f32, o: &mut u32) {
19+
*o = f32_to_f16(i);
20+
}
21+
22+
#[spirv(fragment)]
23+
pub fn test_f16_to_f32(i: u32, o: &mut f32) {
24+
*o = f16_to_f32(i);
25+
}
26+
27+
#[spirv(fragment)]
28+
pub fn test_vec4_to_u8x4_snorm(i: Vec4, o: &mut u32) {
29+
*o = vec4_to_u8x4_snorm(i);
30+
}
31+
32+
#[spirv(fragment)]
33+
pub fn test_vec4_to_u8x4_unorm(i: Vec4, o: &mut u32) {
34+
*o = vec4_to_u8x4_unorm(i);
35+
}
36+
37+
#[spirv(fragment)]
38+
pub fn test_vec2_to_u16x2_snorm(i: Vec2, o: &mut u32) {
39+
*o = vec2_to_u16x2_snorm(i);
40+
}
41+
42+
#[spirv(fragment)]
43+
pub fn test_vec2_to_u16x2_unorm(i: Vec2, o: &mut u32) {
44+
*o = vec2_to_u16x2_unorm(i);
45+
}
46+
47+
#[spirv(fragment)]
48+
pub fn test_u8x4_to_vec4_snorm(i: u32, o: &mut Vec4) {
49+
*o = u8x4_to_vec4_snorm(i);
50+
}
51+
52+
#[spirv(fragment)]
53+
pub fn test_u8x4_to_vec4_unorm(i: u32, o: &mut Vec4) {
54+
*o = u8x4_to_vec4_unorm(i);
55+
}
56+
57+
#[spirv(fragment)]
58+
pub fn test_u16x2_to_vec2_snorm(i: u32, o: &mut Vec2) {
59+
*o = u16x2_to_vec2_snorm(i);
60+
}
61+
62+
#[spirv(fragment)]
63+
pub fn test_u16x2_to_vec2_unorm(i: u32, o: &mut Vec2) {
64+
*o = u16x2_to_vec2_unorm(i);
65+
}

0 commit comments

Comments
 (0)