1use std::cell::RefCell;
2use std::ffi::{self, CString};
3use std::fmt;
4use std::mem;
5use std::os::raw::{self, c_char};
6use std::ptr;
7
8use skia_bindings as sb;
9
10use super::{Device, GetProc, GetProcOf, Instance, PhysicalDevice, Queue, Version};
11use crate::gpu;
12
13pub use super::vulkan_backend_context_builder::BackendContextBuilder as Builder;
14
15pub struct BackendContext<'a> {
18 pub(crate) native: ptr::NonNull<ffi::c_void>,
19 get_proc: &'a dyn GetProc,
20}
21
22impl Drop for BackendContext<'_> {
23 fn drop(&mut self) {
24 unsafe { sb::C_VulkanBackendContext_delete(self.native.as_ptr()) }
25 }
26}
27
28impl fmt::Debug for BackendContext<'_> {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 f.debug_struct("BackendContext")
31 .field("native", &self.native)
32 .finish()
33 }
34}
35
36impl BackendContext<'_> {
41 pub fn new_builder<'a>(
42 instance: Instance,
43 physical_device: PhysicalDevice,
44 device: Device,
45 (queue, queue_index): (Queue, usize),
46 get_proc: &'a impl GetProc,
47 max_api_version: Option<Version>,
48 ) -> Builder<'a> {
49 Builder::new(
50 instance,
51 physical_device,
52 device,
53 (queue, queue_index),
54 get_proc,
55 max_api_version,
56 )
57 }
58
59 #[deprecated(
60 since = "0.98.0",
61 note = "use BackendContext::new_builder(...).build()"
62 )]
63 #[allow(deprecated)]
64 pub unsafe fn new(
68 instance: Instance,
69 physical_device: PhysicalDevice,
70 device: Device,
71 (queue, queue_index): (Queue, usize),
72 get_proc: &dyn GetProc,
73 ) -> BackendContext {
74 unsafe {
75 Self::new_internal(
76 instance,
77 physical_device,
78 device,
79 (queue, queue_index),
80 get_proc,
81 None,
82 gpu::Protected::No,
83 &[],
84 &[],
85 )
86 }
87 }
88
89 #[deprecated(
90 since = "0.98.0",
91 note = "use BackendContext::new_builder(...).with_extensions(...).build()"
92 )]
93 pub unsafe fn new_with_extensions<'a>(
97 instance: Instance,
98 physical_device: PhysicalDevice,
99 device: Device,
100 (queue, queue_index): (Queue, usize),
101 get_proc: &'a dyn GetProc,
102 instance_extensions: &[&str],
103 device_extensions: &[&str],
104 ) -> BackendContext<'a> {
105 unsafe {
106 Self::new_internal(
107 instance,
108 physical_device,
109 device,
110 (queue, queue_index),
111 get_proc,
112 None,
113 gpu::Protected::No,
114 instance_extensions,
115 device_extensions,
116 )
117 }
118 }
119
120 #[allow(clippy::too_many_arguments)]
121 pub(crate) unsafe fn new_internal<'a>(
122 instance: Instance,
123 physical_device: PhysicalDevice,
124 device: Device,
125 (queue, queue_index): (Queue, usize),
126 get_proc: &'a dyn GetProc,
127 max_api_version: Option<Version>,
128 protected_context: gpu::Protected,
129 instance_extensions: &[&str],
130 device_extensions: &[&str],
131 ) -> BackendContext<'a> {
132 let instance_extensions: Vec<CString> = instance_extensions
135 .iter()
136 .map(|str| CString::new(*str).unwrap())
137 .collect();
138 let instance_extensions: Vec<*const c_char> =
139 instance_extensions.iter().map(|cs| cs.as_ptr()).collect();
140 let device_extensions: Vec<CString> = device_extensions
141 .iter()
142 .map(|str| CString::new(*str).unwrap())
143 .collect();
144 let device_extensions: Vec<*const c_char> =
145 device_extensions.iter().map(|cs| cs.as_ptr()).collect();
146
147 let resolver = unsafe { Self::begin_resolving_proc(get_proc) };
148 let native = unsafe {
149 sb::C_VulkanBackendContext_new(
150 instance as _,
151 physical_device as _,
152 device as _,
153 queue as _,
154 queue_index.try_into().unwrap(),
155 protected_context,
156 max_api_version.map(|version| *version).unwrap_or(0),
157 Some(global_get_proc),
158 instance_extensions.as_ptr(),
159 instance_extensions.len(),
160 device_extensions.as_ptr(),
161 device_extensions.len(),
162 )
163 };
164 drop(resolver);
165 BackendContext {
166 native: ptr::NonNull::new(native).unwrap(),
167 get_proc,
168 }
169 }
170
171 pub fn set_protected_context(&mut self, protected_context: gpu::Protected) -> &mut Self {
172 unsafe {
173 sb::C_VulkanBackendContext_setProtectedContext(
174 self.native.as_ptr() as _,
175 protected_context,
176 )
177 }
178 self
179 }
180
181 pub fn set_max_api_version(&mut self, version: impl Into<Option<Version>>) -> &mut Self {
187 unsafe {
188 sb::C_VulkanBackendContext_setMaxAPIVersion(
189 self.native.as_ptr() as _,
190 version.into().map(|version| *version).unwrap_or(0),
191 )
192 }
193 self
194 }
195
196 pub(crate) unsafe fn begin_resolving(&self) -> impl Drop {
197 unsafe { Self::begin_resolving_proc(self.get_proc) }
198 }
199
200 unsafe fn begin_resolving_proc(get_proc_trait_object: &dyn GetProc) -> impl Drop {
208 THREAD_LOCAL_GET_PROC.with(|get_proc| {
209 *get_proc.borrow_mut() =
210 Some(unsafe { mem::transmute::<&dyn GetProc, TraitObject>(get_proc_trait_object) })
211 });
212
213 EndResolving {}
214 }
215}
216
217struct EndResolving {}
218
219impl Drop for EndResolving {
220 fn drop(&mut self) {
221 THREAD_LOCAL_GET_PROC.with(|get_proc| *get_proc.borrow_mut() = None)
222 }
223}
224
225thread_local! {
226 static THREAD_LOCAL_GET_PROC: RefCell<Option<TraitObject>> = const { RefCell::new(None) };
227}
228
229#[repr(C)]
231#[derive(Copy, Clone)]
234struct TraitObject {
235 pub data: *mut (),
236 pub vtable: *mut (),
237}
238
239unsafe extern "C" fn global_get_proc(
241 name: *const raw::c_char,
242 instance: Instance,
243 device: Device,
244) -> *const raw::c_void {
245 THREAD_LOCAL_GET_PROC.with(|get_proc| {
246 match *get_proc.borrow() {
247 Some(get_proc) => {
248 let get_proc_trait_object: &dyn GetProc = unsafe { mem::transmute(get_proc) };
249 if !device.is_null() {
250 get_proc_trait_object(GetProcOf::Device(device, name))
251 } else {
252 get_proc_trait_object(GetProcOf::Instance(instance, name))
254 }
255 }
256 None => panic!("Vulkan GetProc called outside of a thread local resolvement context."),
257 }
258 })
259}