Skip to main content

bedrock_vk/
resolver.rs

1//! Vulkan Function Resolver
2
3use core::ffi::*;
4use std::ptr::NonNull;
5
6pub trait ResolverInterface {
7    /// Loads a symbol using the resolver, without any constraints on the symbol's type.
8    ///
9    /// # Safety
10    ///
11    /// retrieved symbol must be valid value of type T.
12    unsafe fn load_symbol_unconstrainted(&self, name: &CStr) -> NonNull<core::ffi::c_void>;
13
14    /// Loads a function using the resolver, without any constraints on the function's type.
15    ///
16    /// # Safety
17    ///
18    /// retrieved function must be valid function pointer of type F.
19    unsafe fn load_function_unconstrainted(&self, name: &CStr) -> crate::PFN_vkVoidFunction;
20}
21impl ResolverInterface for Box<dyn ResolverInterface> {
22    #[inline(always)]
23    unsafe fn load_symbol_unconstrainted(&self, name: &CStr) -> NonNull<core::ffi::c_void> {
24        unsafe { self.as_ref().load_symbol_unconstrainted(name) }
25    }
26
27    #[inline(always)]
28    unsafe fn load_function_unconstrainted(&self, name: &CStr) -> crate::PFN_vkVoidFunction {
29        unsafe { self.as_ref().load_function_unconstrainted(name) }
30    }
31}
32
33/// Loads a symbol using the resolver, without any constraints on the symbol's type.
34///
35/// # Safety
36///
37/// retrieved symbol must be valid value of type T.
38pub unsafe fn load_symbol_unconstrainted<T: crate::FromPtr>(
39    interface: &(impl ResolverInterface + ?Sized),
40    name: &CStr,
41) -> T {
42    unsafe { T::from_ptr(interface.load_symbol_unconstrainted(name).as_ptr()) }
43}
44
45/// Loads a function using the resolver, without any constraints on the function's type.
46///
47/// # Safety
48///
49/// retrieved function must be valid function pointer of type F.
50#[inline(always)]
51pub unsafe fn load_function_unconstrainted<F: crate::PFN>(interface: &(impl ResolverInterface + ?Sized)) -> F {
52    unsafe { F::from_void_fn(interface.load_function_unconstrainted(F::NAME_CSTR)) }
53}
54
55#[cfg(feature = "DynamicLoaded")]
56#[inline(always)]
57pub(crate) fn current_resolver<'a>() -> &'a impl ResolverInterface {
58    #[cfg(feature = "CustomResolver")]
59    #[allow(clippy::deref_addrof)]
60    unsafe {
61        (*&raw const GLOBAL_RESOLVER).as_ref().expect("no global resolver set")
62    }
63    #[cfg(not(feature = "CustomResolver"))]
64    {
65        GLOBAL_RESOLVER_INIT.call_once(|| unsafe {
66            DefaultResolver::init_inplace(&raw mut GLOBAL_RESOLVER);
67        });
68        #[allow(clippy::deref_addrof)]
69        unsafe {
70            (*&raw const GLOBAL_RESOLVER).assume_init_ref()
71        }
72    }
73}
74
75#[cfg(feature = "DynamicLoaded")]
76static GLOBAL_RESOLVER_INIT: parking_lot::Once = parking_lot::Once::new();
77#[cfg(feature = "CustomResolver")]
78static mut GLOBAL_RESOLVER: Option<Box<dyn ResolverInterface>> = None;
79#[cfg(feature = "CustomResolver")]
80pub fn set_resolver(resolver: Box<dyn ResolverInterface>) {
81    crate::fns::FunctionPointerTable::reset();
82    GLOBAL_RESOLVER_INIT.call_once(|| {});
83    #[allow(clippy::deref_addrof)]
84    unsafe {
85        *&raw mut GLOBAL_RESOLVER = Some(resolver);
86    }
87}
88
89#[cfg(all(feature = "DynamicLoaded", not(feature = "CustomResolver"), not(windows)))]
90mod libdl;
91#[cfg(all(feature = "DynamicLoaded", not(feature = "CustomResolver"), windows))]
92mod libloaderapi;
93
94#[cfg(all(feature = "DynamicLoaded", not(feature = "CustomResolver")))]
95static mut GLOBAL_RESOLVER: core::mem::MaybeUninit<DefaultResolver> = core::mem::MaybeUninit::uninit();
96
97#[cfg(all(feature = "DynamicLoaded", not(feature = "CustomResolver")))]
98pub struct DefaultResolver(
99    #[cfg(windows)] self::libloaderapi::OwnedLibrary,
100    #[cfg(not(windows))] self::libdl::OwnedDylib,
101);
102#[cfg(all(feature = "DynamicLoaded", not(feature = "CustomResolver")))]
103impl ResolverInterface for DefaultResolver {
104    unsafe fn load_symbol_unconstrainted(&self, name: &core::ffi::CStr) -> NonNull<core::ffi::c_void> {
105        match self.0.sym(name) {
106            Ok(x) => x,
107            Err(e) => {
108                tracing::error!(?name, reason = %e, "could not resolve symbol");
109                std::process::abort();
110            }
111        }
112    }
113
114    unsafe fn load_function_unconstrainted(&self, name: &core::ffi::CStr) -> crate::PFN_vkVoidFunction {
115        match self.0.sym(name) {
116            Ok(x) => unsafe { core::mem::transmute::<*const core::ffi::c_void, crate::PFN_vkVoidFunction>(x.as_ptr()) },
117            Err(e) => {
118                tracing::error!(?name, reason = %e, "could not resolve function symbol");
119                std::process::abort();
120            }
121        }
122    }
123}
124#[cfg(all(feature = "DynamicLoaded", not(feature = "CustomResolver")))]
125impl DefaultResolver {
126    #[cfg(windows)]
127    unsafe fn init_inplace(sink: *mut core::mem::MaybeUninit<Self>) {
128        // "vulkan-1.dll\0" in utf-16
129        const LIBNAME: &[u16] = &[
130            b'v' as _, b'u' as _, b'l' as _, b'k' as _, b'a' as _, b'n' as _, b'-' as _, b'1' as _, b'.' as _,
131            b'd' as _, b'l' as _, b'l' as _, 0,
132        ];
133
134        let lib = match self::libloaderapi::OwnedLibrary::open(LIBNAME) {
135            Ok(x) => x,
136            Err(e) => {
137                tracing::error!(
138                    reason = ?e,
139                    libpath = ?LIBNAME,
140                    "Failed to open libvulkan, bedrock could not continue"
141                );
142                std::process::abort();
143            }
144        };
145
146        unsafe {
147            core::ptr::write(core::ptr::addr_of_mut!((*(*sink).as_mut_ptr()).0), lib);
148        }
149    }
150
151    #[cfg(not(windows))]
152    unsafe fn init_inplace(sink: *mut core::mem::MaybeUninit<Self>) {
153        #[cfg(target_os = "macos")]
154        fn libname() -> &'static core::ffi::CStr {
155            // TODO: packed app
156            // let mut exepath = std::env::current_exe().unwrap();
157            // exepath.pop();
158            // exepath.push("libvulkan.dylib");
159            // return exepath;
160            c"libvulkan.dylib"
161        }
162        #[cfg(not(any(target_os = "macos", windows)))]
163        fn libname() -> &'static core::ffi::CStr {
164            // assumes unix environment
165            c"libvulkan.so"
166        }
167
168        let lib = match self::libdl::Dylib::open(libname(), self::libdl::OpenFlags::RTLD_LAZY) {
169            Ok(x) => x,
170            Err(e) => {
171                tracing::error!(
172                    reason = ?e,
173                    libpath = ?libname(),
174                    "Failed to open libvulkan, bedrock could not continue"
175                );
176                std::process::abort();
177            }
178        };
179
180        unsafe {
181            core::ptr::write(core::ptr::addr_of_mut!((*(*sink).as_mut_ptr()).0), lib);
182        }
183    }
184}
185
186pub struct ResolvedFnCell<F: crate::PFN + crate::FromPtr, R>(R, std::sync::OnceLock<F>);
187impl<F: crate::PFN + crate::FromPtr, R: ResolverInterface> ResolvedFnCell<F, R> {
188    pub const fn new(resolver: R) -> Self {
189        Self(resolver, std::sync::OnceLock::new())
190    }
191
192    #[inline(always)]
193    pub fn resolve(&self) -> &F {
194        self.1.get_or_init(|| unsafe { load_function_unconstrainted(&self.0) })
195    }
196}