use std::cell::RefCell;
use std::ffi::{self, CString};
use std::fmt;
use std::mem;
use std::os::raw::{self, c_char};
use std::ptr;
use skia_bindings as sb;
use super::{Device, GetProc, GetProcOf, Instance, PhysicalDevice, Queue, Version};
use crate::gpu;
pub use super::vulkan_backend_context_builder::BackendContextBuilder as Builder;
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 fn new_builder<'a>(
instance: Instance,
physical_device: PhysicalDevice,
device: Device,
(queue, queue_index): (Queue, usize),
get_proc: &'a impl GetProc,
max_api_version: Option<Version>,
) -> Builder<'a> {
Builder::new(
instance,
physical_device,
device,
(queue, queue_index),
get_proc,
max_api_version,
)
}
#[deprecated(
since = "0.98.0",
note = "use BackendContext::new_builder(...).build()"
)]
#[allow(deprecated)]
pub unsafe fn new(
instance: Instance,
physical_device: PhysicalDevice,
device: Device,
(queue, queue_index): (Queue, usize),
get_proc: &dyn GetProc,
) -> BackendContext {
unsafe {
Self::new_internal(
instance,
physical_device,
device,
(queue, queue_index),
get_proc,
None,
gpu::Protected::No,
&[],
&[],
)
}
}
#[deprecated(
since = "0.98.0",
note = "use BackendContext::new_builder(...).with_extensions(...).build()"
)]
pub unsafe fn new_with_extensions<'a>(
instance: Instance,
physical_device: PhysicalDevice,
device: Device,
(queue, queue_index): (Queue, usize),
get_proc: &'a dyn GetProc,
instance_extensions: &[&str],
device_extensions: &[&str],
) -> BackendContext<'a> {
unsafe {
Self::new_internal(
instance,
physical_device,
device,
(queue, queue_index),
get_proc,
None,
gpu::Protected::No,
instance_extensions,
device_extensions,
)
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) unsafe fn new_internal<'a>(
instance: Instance,
physical_device: PhysicalDevice,
device: Device,
(queue, queue_index): (Queue, usize),
get_proc: &'a dyn GetProc,
max_api_version: Option<Version>,
protected_context: gpu::Protected,
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 = unsafe { Self::begin_resolving_proc(get_proc) };
let native = unsafe {
sb::C_VulkanBackendContext_new(
instance as _,
physical_device as _,
device as _,
queue as _,
queue_index.try_into().unwrap(),
protected_context,
max_api_version.map(|version| *version).unwrap_or(0),
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<Option<Version>>) -> &mut Self {
unsafe {
sb::C_VulkanBackendContext_setMaxAPIVersion(
self.native.as_ptr() as _,
version.into().map(|version| *version).unwrap_or(0),
)
}
self
}
pub(crate) unsafe fn begin_resolving(&self) -> impl Drop {
unsafe { 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(unsafe { 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 = unsafe { 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."),
}
})
}