bedrock/
resolver.rs

1//! Vulkan Function Resolver
2
3#![allow(non_snake_case)]
4
5use crate::*;
6use derives::implements;
7
8use core::ffi::*;
9
10#[implements]
11pub trait ResolverInterface {
12    unsafe fn load_symbol_unconstrainted<T: FromPtr>(&self, name: &core::ffi::CStr) -> T;
13    unsafe fn load_function_unconstrainted<F: PFN>(&self) -> F;
14}
15
16#[implements]
17cfg_if::cfg_if! {
18    if #[cfg(feature = "CustomResolver")] {
19        static GLOBAL_RESOLVER: parking_lot::RwLock<Option<Box<dyn ResolverInterface>>> = parking_low::RwLock::new(None);
20
21        /// Sets custom resolver object for vulkan api call
22        pub fn set_custom_resolver(resolver: Box<dyn ResolverInterface>) {
23            crate::vkfn::FunctionPointerTable::reset();
24            *GLOBAL_RESOLVER.write() = Some(resolver);
25        }
26
27        /// Gets current resolver object
28        #[inline(always)]
29        pub fn get_resolver<'a>() -> &'a dyn ResolverInterface {
30            GLOBAL_RESOLVER.read().expect("no global resolver set")
31        }
32    } else if #[cfg(feature = "DynamicLoaded")] {
33        static GLOBAL_RESOLVER: std::sync::LazyLock<Box<Resolver>> = std::sync::LazyLock::new(|| Box::new(Resolver::new()));
34
35        #[cfg(windows)]
36        pub struct Resolver(crate::libloaderapi::OwnedLibrary);
37        #[cfg(not(windows))]
38        pub struct Resolver(crate::libdl::OwnedDylib);
39        impl Resolver {
40            fn new() -> Self {
41                #[cfg(target_os  ="macos")]
42                fn libname() -> &'static core::ffi::CStr {
43                    // TODO: packed app
44                    // let mut exepath = std::env::current_exe().unwrap();
45                    // exepath.pop();
46                    // exepath.push("libvulkan.dylib");
47                    // return exepath;
48                    c"libvulkan.dylib"
49                }
50                #[cfg(windows)]
51                fn libname() -> &'static [u16] {
52                    &[
53                        b'v' as _,
54                        b'u' as _,
55                        b'l' as _,
56                        b'k' as _,
57                        b'a' as _,
58                        b'n' as _,
59                        b'-' as _,
60                        b'1' as _,
61                        b'.' as _,
62                        b'd' as _,
63                        b'l' as _,
64                        b'l' as _,
65                        0
66                    ]
67                }
68                #[cfg(not(any(target_os = "macos", windows)))]
69                fn libname() -> &'static core::ffi::CStr {
70                    // assumes unix environment
71                    c"libvulkan.so"
72                }
73
74                #[cfg(windows)]
75                match crate::libloaderapi::OwnedLibrary::open(libname()) {
76                    Ok(x) => Resolver(x),
77                    Err(e) => {
78                        tracing::error!(
79                            reason = ?e,
80                            libpath = ?libname(),
81                            "Failed to open libvulkan, bedrock could not continue"
82                        );
83                        std::process::abort();
84                    }
85                }
86                #[cfg(not(windows))]
87                match crate::libdl::Dylib::open(libname(), crate::libdl::OpenFlags::RTLD_LAZY) {
88                    Ok(x) => Resolver(x),
89                    Err(e) => {
90                        tracing::error!(
91                            reason = ?e,
92                            libpath = ?libname(),
93                            "Failed to open libvulkan, bedrock could not continue"
94                        );
95                        std::process::abort();
96                    }
97                }
98            }
99        }
100        impl ResolverInterface for Resolver {
101            unsafe fn load_symbol_unconstrainted<T: FromPtr>(&self, name: &core::ffi::CStr) -> T {
102                let p = match self.0.sym(name) {
103                    Ok(x) => x.as_ptr(),
104                    Err(e) => {
105                        tracing::warn!(?name, reason = ?e, "could not resolve symbol");
106                        core::ptr::null_mut()
107                    }
108                };
109
110                unsafe { T::from_ptr(p as _) }
111            }
112
113            unsafe fn load_function_unconstrainted<F: PFN>(&self) -> F {
114                let p = match self.0.sym(F::NAME_CSTR) {
115                    Ok(x) => x.as_ptr(),
116                    Err(e) => {
117                        tracing::warn!(
118                            name = ?F::NAME_CSTR,
119                            reason = ?e,
120                            "could not resolve function symbol"
121                        );
122                        core::ptr::null_mut()
123                    }
124                };
125
126                unsafe { F::from_ptr(p as _) }
127            }
128        }
129
130        #[inline(always)]
131        pub fn get_resolver<'a>() -> &'a Resolver {
132            &*GLOBAL_RESOLVER
133        }
134    }
135}
136
137pub unsafe trait FromPtr {
138    unsafe fn from_ptr(p: *const c_void) -> Self;
139}
140pub unsafe trait PFN {
141    const NAME_CSTR: &'static core::ffi::CStr;
142
143    unsafe fn from_ptr(p: *const c_void) -> Self;
144    unsafe fn from_void_fn(p: PFN_vkVoidFunction) -> Self;
145}
146pub trait StaticCallable: PFN {
147    const STATIC: Self;
148}
149
150#[implements]
151pub struct ResolvedFnCell<F: PFN, R>(R, std::sync::OnceLock<F>);
152#[implements]
153impl<F: PFN, R: ResolverInterface> ResolvedFnCell<F, R> {
154    pub const fn new(resolver: R) -> Self {
155        Self(resolver, std::sync::OnceLock::new())
156    }
157
158    pub fn resolve(&self) -> &F {
159        self.1
160            .get_or_init(|| unsafe { self.0.load_function_unconstrainted::<F>() })
161    }
162}