use crate::unmanaged_interop::VulkanInstanceApi;
use crate::{IVulkanDevice, IVulkanInstance};
use ash::vk;
use ferroui_base::platform::IOptionalFeatureProvider;
use ferroui_base::reactive::{Disposable, IDisposable};
use std::any::{Any, TypeId};
use std::rc::Rc;
use std::sync::{Arc, Condvar, Mutex, Weak};
use std::thread::{self, ThreadId};
pub(crate) struct VulkanDevice {
instance_api: VulkanInstanceApi,
handle: Mutex<vk::Device>,
physical_device_handle: vk::PhysicalDevice,
main_queue: vk::Queue,
graphics_queue_index: u32,
lock: Mutex<LockState>,
unlocked: Condvar,
instance: Arc<dyn IVulkanInstance>,
enabled_extensions: Vec<String>,
this: Weak<VulkanDevice>,
}
#[derive(Default)]
struct LockState {
locked_by_thread: Option<ThreadId>,
lock_count: u32,
}
impl VulkanDevice {
pub(super) fn new(
instance_api: VulkanInstanceApi,
handle: vk::Device,
physical_device_handle: vk::PhysicalDevice,
main_queue: vk::Queue,
graphics_queue_index: u32,
enabled_extensions: Vec<String>,
) -> Arc<Self> {
let instance = instance_api.instance().clone();
Arc::new_cyclic(|this| Self {
instance_api,
handle: Mutex::new(handle),
physical_device_handle,
main_queue,
graphics_queue_index,
lock: Mutex::new(LockState::default()),
unlocked: Condvar::new(),
instance,
enabled_extensions,
this: this.clone(),
})
}
fn lock_state(&self) -> std::sync::MutexGuard<'_, LockState> {
self.lock.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
}
fn check_access<T>(&self, f: T) -> T {
if self.lock_state().locked_by_thread != Some(thread::current().id()) {
panic!("This class is only usable when locked");
}
f
}
fn unlock(&self) {
let mut state = self.lock_state();
state.lock_count -= 1;
if state.lock_count == 0 {
state.locked_by_thread = None;
self.unlocked.notify_one();
}
}
}
impl IVulkanDevice for VulkanDevice {
fn lock(&self) -> Rc<dyn IDisposable> {
let current = thread::current().id();
{
let mut state = self.lock_state();
while state.locked_by_thread.is_some_and(|thread| thread != current) {
state = self.unlocked.wait(state).unwrap_or_else(|poisoned| poisoned.into_inner());
}
state.lock_count += 1;
state.locked_by_thread = Some(current);
}
let this = self.this.upgrade().expect("a device that is called is alive");
Disposable::create(move || this.unlock())
}
fn enabled_extensions(&self) -> Vec<String> {
self.enabled_extensions.clone()
}
fn is_lost(&self) -> bool {
false
}
fn handle(&self) -> isize {
let handle = *self.handle.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
vk::Handle::as_raw(handle) as isize
}
fn physical_device_handle(&self) -> isize {
vk::Handle::as_raw(self.physical_device_handle) as isize
}
fn main_queue_handle(&self) -> isize {
vk::Handle::as_raw(self.check_access(self.main_queue)) as isize
}
fn graphics_queue_family_index(&self) -> u32 {
self.graphics_queue_index
}
fn instance(&self) -> Arc<dyn IVulkanInstance> {
self.instance.clone()
}
fn dispose(&self) {
let mut handle = self.handle.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
if *handle != vk::Device::null() {
self.instance_api.destroy_device(*handle);
*handle = vk::Device::null();
}
}
}
impl IOptionalFeatureProvider for VulkanDevice {
fn try_get_feature(&self, _feature_type: TypeId) -> Option<Rc<dyn Any>> {
None
}
}