1use core::ffi::*;
4use std::ptr::NonNull;
5
6pub trait ResolverInterface {
7 unsafe fn load_symbol_unconstrainted(&self, name: &CStr) -> NonNull<core::ffi::c_void>;
13
14 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
33pub 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#[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 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 c"libvulkan.dylib"
161 }
162 #[cfg(not(any(target_os = "macos", windows)))]
163 fn libname() -> &'static core::ffi::CStr {
164 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}