diff --git a/crates/renderer/src/device.rs b/crates/renderer/src/device.rs index 066cb5f..aa39daf 100644 --- a/crates/renderer/src/device.rs +++ b/crates/renderer/src/device.rs @@ -2,7 +2,6 @@ use std::{ borrow::Cow, collections::{BTreeSet, HashMap, HashSet}, ffi::CStr, - mem::ManuallyDrop, ops::{Deref, DerefMut}, sync::Arc, }; @@ -442,9 +441,9 @@ impl PhysicalDeviceInfo { } } -#[derive(Clone, Debug)] +#[derive(Debug)] pub(crate) struct DevicePools { - pub(crate) fences: Pool, + pub(crate) fences: Arc>, pub(crate) binary_semaphores: Pool, pub(crate) timeline_semaphores: Pool, } @@ -452,7 +451,7 @@ pub(crate) struct DevicePools { impl DevicePools { pub fn new(device: Arc) -> Self { Self { - fences: Pool::new(device.clone()), + fences: Arc::new(Pool::new(device.clone())), binary_semaphores: Pool::new(device.clone()), timeline_semaphores: Pool::new(device), } @@ -716,7 +715,7 @@ impl DeviceObject { { unsafe { if let Some(name) = name.as_ref() { - device.debug_name_object(inner.clone(), &name); + device.debug_name_object(inner.clone(), name); } } @@ -807,52 +806,9 @@ pub trait Pooled: Sized { fn create_from_pool(pool: &Pool) -> Result; } -pub struct PoolObject { - pub(crate) inner: ManuallyDrop, - pub(crate) pool: Pool, - #[cfg(debug_assertions)] - pub(crate) name: Option>, -} - -impl PoolObject { - pub fn name_object(&mut self, name: impl Into>) { - #[cfg(debug_assertions)] - unsafe { - self.name = Some(name.into()); - self.pool - .device - .debug_name_object(T::clone(&self.inner), self.name.as_ref().unwrap()); - } - } - - pub fn device(&self) -> &Arc { - &self.pool.device - } -} - -impl Drop for PoolObject { - fn drop(&mut self) { - let handle = unsafe { ManuallyDrop::take(&mut self.inner) }; - #[cfg(debug_assertions)] - if self.name.is_some() { - unsafe { self.pool.device.debug_name_object(handle.clone(), "") }; - } - - self.pool.push(handle); - } -} - -impl Deref for PoolObject { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Pool { - pub(crate) pool: Arc>>, + pub(crate) pool: Mutex>, pub(crate) device: Arc, } @@ -862,7 +818,7 @@ impl Pool { } pub fn new(device: Arc) -> Self { Self { - pool: Arc::new(Mutex::new(Vec::new())), + pool: Mutex::new(Vec::new()), device, } } @@ -871,27 +827,40 @@ impl Pool { } } -impl Pool { - pub fn get(&self) -> Result> { +impl AsRef> for Pool { + fn as_ref(&self) -> &Pool { + self + } +} + +pub type PoolObject>> = asdf::ExternallyManagedObject; + +impl<'a, T: Pooled + asdf::traits::ExternallyManagedObject<&'a Pool> + 'a> Pool { + pub fn get(&'a self) -> Result>> { let item = if let Some(item) = self.pool.lock().pop() { item } else { T::create_from_pool(self)? }; - Ok(PoolObject { - inner: ManuallyDrop::new(item), - pool: self.clone(), - #[cfg(debug_assertions)] - name: None, - }) + Ok(asdf::ExternallyManagedObject::new(item, self)) } - pub fn get_named(&self, name: Option>>) -> Result> { - let mut obj = self.get()?; - if let Some(name) = name { - obj.name_object(name); + pub fn get_debug_named( + &'a self, + name: Option>>, + ) -> Result>> + where + T: asdf::traits::DebugNameable, + { + let obj = self.get()?; + + #[cfg(debug_assertions)] + { + let name = name.map(Into::into).unwrap_or_default(); + ::debug_name(&obj, &self.device, &name); } + Ok(obj) } } @@ -954,9 +923,10 @@ macro_rules! define_device_owned_handle { } // This module is an experiment in a more generic way to manage device-owned resources. -#[cfg(false)] -mod asdf { +// #[cfg(false)] +pub(crate) mod asdf { use std::{ + mem::{ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, sync::Arc, }; @@ -964,24 +934,28 @@ mod asdf { use ash::vk; use crate::{ - device::{DeviceInner, GpuAllocation}, + device::{DeviceInner, DevicePools, GpuAllocation}, util::DebugName, }; - mod traits { + pub mod traits { + /// A trait describing an object owned by some manager-type, which is + /// responsible for destroying it. pub trait ExternallyManagedObject { /// # Safety /// The caller must ensure this function is only called once for a given object. - unsafe fn destroy(&mut self, owner: &T); + unsafe fn destroy(self, owner: &T); } + /// A trait describing an object which can have a debug name assigned to it. pub trait DebugNameable { fn debug_name(&self, device: &super::DeviceInner, name: &str); } } - struct ExternallyManagedObject, O> { - inner: T, + /// Wrapper for types which are owned by another type `O`, which is responsible for destruction. + pub struct ExternallyManagedObject, O> { + inner: ManuallyDrop, owner: O, } @@ -1000,12 +974,51 @@ mod asdf { } impl, O> ExternallyManagedObject { - fn new(inner: T, owner: O) -> Self { - Self { inner, owner } + pub fn new(inner: T, owner: O) -> Self { + Self { + inner: ManuallyDrop::new(inner), + owner, + } } - fn owner(&self) -> &O { + pub fn owner(&self) -> &O { &self.owner } + + pub fn map_inner(self, f: impl FnOnce(T) -> U) -> ExternallyManagedObject + where + U: traits::ExternallyManagedObject, + { + unsafe { + let mut this = MaybeUninit::new(self); + let inner = ManuallyDrop::take(&mut this.assume_init_mut().inner); + let owner = core::ptr::read(&raw const this.assume_init_mut().owner); + + let new_inner = f(inner); + + ExternallyManagedObject { + inner: ManuallyDrop::new(new_inner), + owner, + } + } + } + + pub fn map_owner(self, f: impl FnOnce(O) -> U) -> ExternallyManagedObject + where + T: traits::ExternallyManagedObject, + { + unsafe { + let mut this = MaybeUninit::new(self); + let inner = ManuallyDrop::take(&mut this.assume_init_mut().inner); + + // get the old owner without calling `Self::drop` + let owner = core::ptr::read(&raw const this.assume_init_mut().owner); + + ExternallyManagedObject { + inner: ManuallyDrop::new(inner), + owner: f(owner), + } + } + } } impl Drop for ExternallyManagedObject @@ -1014,12 +1027,17 @@ mod asdf { { fn drop(&mut self) { unsafe { - self.inner.destroy(&self.owner); + let inner = ManuallyDrop::take(&mut self.inner); + inner.destroy(&self.owner); } } } - struct DeviceObject, O: AsRef> { + /// A wrapper for vulkan types which are owned by the device, taking care of destruction. + pub struct DeviceObject< + T: traits::ExternallyManagedObject, + O: AsRef = Arc, + > { inner: ExternallyManagedObject, name: Option, } @@ -1065,23 +1083,42 @@ mod asdf { } mod impls { - use crate::device::{DeviceInner, GpuAllocation}; + use crate::device::{DeviceInner, DevicePools, GpuAllocation}; use super::*; impl> traits::ExternallyManagedObject for ash::vk::Semaphore { - unsafe fn destroy(&mut self, device: &T) { + unsafe fn destroy(self, device: &T) { unsafe { - device.as_ref().raw.destroy_semaphore(*self, None); + device.as_ref().raw.destroy_semaphore(self, None); } } } impl> traits::ExternallyManagedObject for GpuAllocation { - unsafe fn destroy(&mut self, device: &T) { - let mut swapped = GpuAllocation::default(); - std::mem::swap(self, &mut swapped); - _ = device.as_ref().alloc2.lock().free(swapped); + unsafe fn destroy(self, device: &T) { + _ = device.as_ref().alloc2.lock().free(self); + } + } + + impl traits::ExternallyManagedObject for vk::Semaphore { + unsafe fn destroy(self, owner: &DevicePools) { + owner + .binary_semaphores + .push(crate::sync::BinarySemaphore(self)); + } + } + + impl traits::ExternallyManagedObject for super::Semaphore { + unsafe fn destroy(self, owner: &DevicePools) { + match self { + super::Semaphore::Binary(semaphore) => owner + .binary_semaphores + .push(crate::sync::BinarySemaphore(semaphore)), + super::Semaphore::Timeline(semaphore) => owner + .timeline_semaphores + .push(crate::sync::TimelineSemaphore(semaphore)), + } } } @@ -1103,6 +1140,11 @@ mod asdf { } } + enum Semaphore { + Binary(vk::Semaphore), + Timeline(vk::Semaphore), + } + fn summon() -> T { unimplemented!() } @@ -1125,5 +1167,14 @@ mod asdf { summon::>(), summon::(), ); + + let pool_owned: ExternallyManagedObject = + ExternallyManagedObject::new(summon::(), summon::()); + + let enum_semaphore_pooled: ExternallyManagedObject = + ExternallyManagedObject::new( + Semaphore::Binary(summon::()), + summon::(), + ); } } diff --git a/crates/renderer/src/sync.rs b/crates/renderer/src/sync.rs index 6509384..efe7327 100644 --- a/crates/renderer/src/sync.rs +++ b/crates/renderer/src/sync.rs @@ -155,8 +155,12 @@ impl SyncThreadpool { } pub enum Fence { - Dedicated { fence: DeviceObject }, - Pooled { fence: PoolObject }, + Dedicated { + fence: DeviceObject, + }, + Pooled { + fence: PoolObject>>, + }, } impl Pooled for vk::Fence { @@ -171,6 +175,16 @@ impl Pooled for vk::Fence { } } +impl crate::device::asdf::traits::ExternallyManagedObject for vk::Fence +where + T: AsRef>, +{ + unsafe fn destroy(self, owner: &T) { + let pool = owner.as_ref(); + pool.push(self); + } +} + impl std::fmt::Debug for Fence { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Fence").field("fence", &self.raw()).finish() @@ -188,12 +202,9 @@ impl Fence { fence: DeviceObject::new(fence, device, name.map(Into::into)), }) } - pub fn from_pool(pool: &Pool, name: Option<&'static str>) -> Result { - let mut fence = pool.get()?; - #[cfg(debug_assertions)] - if let Some(name) = name { - fence.name_object(name); - } + pub fn from_pool(pool: &Arc>, name: Option<&'static str>) -> Result { + let fence = pool.get_debug_named(name)?.map_owner(|_| pool.clone()); + Ok(Self::Pooled { fence }) } @@ -207,7 +218,7 @@ impl Fence { fn device(&self) -> &Arc { match self { Fence::Dedicated { fence } => &fence.device().shared, - Fence::Pooled { fence } => fence.device(), + Fence::Pooled { fence } => &fence.owner().device, } } @@ -266,9 +277,9 @@ pub enum Semaphore { } #[derive(Debug, Clone)] -pub(crate) struct BinarySemaphore(vk::Semaphore); +pub(crate) struct BinarySemaphore(pub(crate) vk::Semaphore); #[derive(Debug, Clone)] -pub(crate) struct TimelineSemaphore(vk::Semaphore); +pub(crate) struct TimelineSemaphore(pub(crate) vk::Semaphore); // This is just so that ash can name these semaphore newtypes impl vk::Handle for BinarySemaphore {