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