skia_safe/gpu/vk/
vulkan_backend_context.rsuse std::{
cell::RefCell,
ffi::{self, CString},
fmt, mem,
ops::Deref,
os::raw::{self, c_char},
ptr,
};
use skia_bindings as sb;
use super::{Device, GetProc, GetProcOf, Instance, PhysicalDevice, Queue, Version};
use crate::gpu;
pub struct BackendContext<'a> {
pub(crate) native: ptr::NonNull<ffi::c_void>,
get_proc: &'a dyn GetProc,
}
impl Drop for BackendContext<'_> {
fn drop(&mut self) {
unsafe { sb::C_VulkanBackendContext_delete(self.native.as_ptr()) }
}
}
impl fmt::Debug for BackendContext<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BackendContext")
.field("native", &self.native)
.finish()
}
}
impl BackendContext<'_> {
pub unsafe fn new(
instance: Instance,
physical_device: PhysicalDevice,
device: Device,
(queue, queue_index): (Queue, usize),
get_proc: &impl GetProc,
) -> BackendContext {
Self::new_with_extensions(
instance,
physical_device,
device,
(queue, queue_index),
get_proc,
&[],
&[],
)
}
pub unsafe fn new_with_extensions<'a>(
instance: Instance,
physical_device: PhysicalDevice,
device: Device,
(queue, queue_index): (Queue, usize),
get_proc: &'a impl GetProc,
instance_extensions: &[&str],
device_extensions: &[&str],
) -> BackendContext<'a> {
let instance_extensions: Vec<CString> = instance_extensions
.iter()
.map(|str| CString::new(*str).unwrap())
.collect();
let instance_extensions: Vec<*const c_char> =
instance_extensions.iter().map(|cs| cs.as_ptr()).collect();
let device_extensions: Vec<CString> = device_extensions
.iter()
.map(|str| CString::new(*str).unwrap())
.collect();
let device_extensions: Vec<*const c_char> =
device_extensions.iter().map(|cs| cs.as_ptr()).collect();
let resolver = Self::begin_resolving_proc(get_proc);
let native = sb::C_VulkanBackendContext_new(
instance as _,
physical_device as _,
device as _,
queue as _,
queue_index.try_into().unwrap(),
Some(global_get_proc),
instance_extensions.as_ptr(),
instance_extensions.len(),
device_extensions.as_ptr(),
device_extensions.len(),
);
drop(resolver);
BackendContext {
native: ptr::NonNull::new(native).unwrap(),
get_proc,
}
}
pub fn set_protected_context(&mut self, protected_context: gpu::Protected) -> &mut Self {
unsafe {
sb::C_VulkanBackendContext_setProtectedContext(
self.native.as_ptr() as _,
protected_context,
)
}
self
}
pub fn set_max_api_version(&mut self, version: impl Into<Version>) -> &mut Self {
unsafe {
sb::C_VulkanBackendContext_setMaxAPIVersion(
self.native.as_ptr() as _,
*version.into().deref(),
)
}
self
}
pub(crate) unsafe fn begin_resolving(&self) -> impl Drop {
Self::begin_resolving_proc(self.get_proc)
}
unsafe fn begin_resolving_proc(get_proc_trait_object: &dyn GetProc) -> impl Drop {
THREAD_LOCAL_GET_PROC.with(|get_proc| {
*get_proc.borrow_mut() = Some(mem::transmute::<&dyn GetProc, TraitObject>(
get_proc_trait_object,
))
});
EndResolving {}
}
}
struct EndResolving {}
impl Drop for EndResolving {
fn drop(&mut self) {
THREAD_LOCAL_GET_PROC.with(|get_proc| *get_proc.borrow_mut() = None)
}
}
thread_local! {
static THREAD_LOCAL_GET_PROC: RefCell<Option<TraitObject>> = const { RefCell::new(None) };
}
#[repr(C)]
#[derive(Copy, Clone)]
struct TraitObject {
pub data: *mut (),
pub vtable: *mut (),
}
unsafe extern "C" fn global_get_proc(
name: *const raw::c_char,
instance: Instance,
device: Device,
) -> *const raw::c_void {
THREAD_LOCAL_GET_PROC.with(|get_proc| {
match *get_proc.borrow() {
Some(get_proc) => {
let get_proc_trait_object: &dyn GetProc = mem::transmute(get_proc);
if !device.is_null() {
get_proc_trait_object(GetProcOf::Device(device, name))
} else {
get_proc_trait_object(GetProcOf::Instance(instance, name))
}
}
None => panic!("Vulkan GetProc called outside of a thread local resolvement context."),
}
})
}