Skip to content

Commit 8ea787a

Browse files
committed
Add a physical pointer type to the std lib
1 parent ff9533a commit 8ea787a

File tree

2 files changed

+119
-0
lines changed

2 files changed

+119
-0
lines changed

crates/spirv-std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ pub mod indirect_command;
9898
pub mod integer;
9999
pub mod memory;
100100
pub mod number;
101+
pub mod ptr;
101102
pub mod ray_tracing;
102103
mod runtime_array;
103104
mod sampler;

crates/spirv-std/src/ptr.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//! Physical pointers
2+
3+
#[cfg(target_arch = "spirv")]
4+
use core::arch::asm;
5+
use core::marker::PhantomData;
6+
7+
/// A physical pointer in the `PhysicalStorageBuffer` storage class
8+
/// with semantics similar to `*mut T`.
9+
///
10+
/// This is similar to a raw pointer retrieved through `u64 as *mut T`, but
11+
/// provides utilities for pointer manipulation that are currently not
12+
/// supported on raw pointers due to the otherwise logical addressing model
13+
/// and 32-bit pointer size.
14+
pub struct PhysicalPtr<T> {
15+
// Use uvec2 instead of u64 to avoid demepndency on the Int64 dependency.
16+
addr: glam::UVec2,
17+
//addr: u64,
18+
_marker: PhantomData<*mut T>,
19+
}
20+
21+
impl<T> Copy for PhysicalPtr<T> {}
22+
23+
impl<T> Clone for PhysicalPtr<T> {
24+
fn clone(&self) -> Self {
25+
Self {
26+
addr: self.addr,
27+
_marker: PhantomData,
28+
}
29+
}
30+
}
31+
32+
impl<T> PhysicalPtr<T> {
33+
/// Get a mutaple pointer to the physical address.
34+
/// The same aliasing rules that apply to FFI, apply to the returned pointer.
35+
#[crate::macros::gpu_only]
36+
pub fn get(self) -> *mut T {
37+
let result: *mut T;
38+
unsafe {
39+
// FIXME(jwollen) add a way to dereference the result type further
40+
// or to pass type parameters
41+
let dummy: T = core::mem::MaybeUninit::uninit().assume_init();
42+
asm!(
43+
"%ptr_type = OpTypePointer PhysicalStorageBuffer typeof*{dummy}",
44+
"{result} = OpBitcast %ptr_type {addr}",
45+
addr = in(reg) &self.addr,
46+
dummy = in(reg) &dummy,
47+
result = out(reg) result,
48+
);
49+
result
50+
}
51+
}
52+
53+
/// Creates a null physical pointer.
54+
pub fn null() -> Self {
55+
Self {
56+
addr: glam::UVec2::ZERO,
57+
_marker: PhantomData,
58+
}
59+
}
60+
61+
/// Returns `true` if the pointer is null.
62+
pub fn is_null(self) -> bool {
63+
self.addr == glam::UVec2::ZERO
64+
}
65+
66+
/// Casts to a pointer of another type.
67+
pub fn cast<U>(self) -> PhysicalPtr<U> {
68+
PhysicalPtr { addr: self.addr, _marker: PhantomData }
69+
}
70+
71+
/// Returns `None` if the pointer is null, or else returns a shared reference to the value wrapped in `Some`.
72+
pub unsafe fn as_ref<'a>(self) -> Option<&'a T> {
73+
self.is_null().then_some(unsafe { self.as_ref_unchecked() })
74+
}
75+
76+
/// Returns `None` if the pointer is null, or else returns a mutable reference to the value wrapped in `Some`.
77+
pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T> {
78+
self.is_null().then_some(unsafe { self.as_mut_unchecked() })
79+
}
80+
81+
/// Returns a shared reference to the value behind the pointer.
82+
pub unsafe fn as_ref_unchecked<'a>(self) -> &'a T {
83+
unsafe { &*self.get() }
84+
}
85+
86+
/// Returns a mutable reference to the value behind the pointer.
87+
pub unsafe fn as_mut_unchecked<'a>(self) -> &'a mut T {
88+
unsafe { &mut *self.get() }
89+
}
90+
91+
/// Gets the address portion of the pointer. All physical pointers are considered to have global provenance.
92+
pub fn addr(self) -> u64 {
93+
unsafe { core::mem::transmute(self.addr) }
94+
}
95+
96+
/// Forms a physical pointer from an address. All physical pointers are considered to have global provenance.
97+
pub fn from_addr(addr: u64) -> Self {
98+
Self {
99+
addr: unsafe { core::mem::transmute(addr) },
100+
_marker: PhantomData,
101+
}
102+
}
103+
104+
/// Creates a new pointer by mapping `self`’s address to a new one.
105+
pub fn map_addr(self, f: impl FnOnce(u64) -> u64) -> Self {
106+
Self::from_addr(f(self.addr()))
107+
}
108+
109+
/// Adds a signed offset to a pointer.
110+
pub unsafe fn offset(self, count: i64) -> Self {
111+
unsafe { self.byte_offset(count * core::mem::size_of::<T>() as i64) }
112+
}
113+
114+
/// Adds a signed offset in bytes to a pointer.
115+
pub unsafe fn byte_offset(self, count: i64) -> Self {
116+
self.map_addr(|addr| addr.overflowing_add_signed(count).0)
117+
}
118+
}

0 commit comments

Comments
 (0)