This commit is contained in:
janis 2026-04-05 00:06:23 +02:00
parent 7503a73a57
commit e4c0479757
2 changed files with 155 additions and 93 deletions

View file

@ -2,7 +2,6 @@ use std::{
borrow::Cow, borrow::Cow,
collections::{BTreeSet, HashMap, HashSet}, collections::{BTreeSet, HashMap, HashSet},
ffi::CStr, ffi::CStr,
mem::ManuallyDrop,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
sync::Arc, sync::Arc,
}; };
@ -442,9 +441,9 @@ impl PhysicalDeviceInfo {
} }
} }
#[derive(Clone, Debug)] #[derive(Debug)]
pub(crate) struct DevicePools { pub(crate) struct DevicePools {
pub(crate) fences: Pool<vk::Fence>, pub(crate) fences: Arc<Pool<vk::Fence>>,
pub(crate) binary_semaphores: Pool<BinarySemaphore>, pub(crate) binary_semaphores: Pool<BinarySemaphore>,
pub(crate) timeline_semaphores: Pool<TimelineSemaphore>, pub(crate) timeline_semaphores: Pool<TimelineSemaphore>,
} }
@ -452,7 +451,7 @@ pub(crate) struct DevicePools {
impl DevicePools { impl DevicePools {
pub fn new(device: Arc<DeviceInner>) -> Self { pub fn new(device: Arc<DeviceInner>) -> Self {
Self { Self {
fences: Pool::new(device.clone()), fences: Arc::new(Pool::new(device.clone())),
binary_semaphores: Pool::new(device.clone()), binary_semaphores: Pool::new(device.clone()),
timeline_semaphores: Pool::new(device), timeline_semaphores: Pool::new(device),
} }
@ -716,7 +715,7 @@ impl<T: DeviceHandle> DeviceObject<T> {
{ {
unsafe { unsafe {
if let Some(name) = name.as_ref() { 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<Self>) -> Result<Self>; fn create_from_pool(pool: &Pool<Self>) -> Result<Self>;
} }
pub struct PoolObject<T: Pooled + vk::Handle + Clone> { #[derive(Debug)]
pub(crate) inner: ManuallyDrop<T>,
pub(crate) pool: Pool<T>,
#[cfg(debug_assertions)]
pub(crate) name: Option<Cow<'static, str>>,
}
impl<T: Pooled + vk::Handle + Clone> PoolObject<T> {
pub fn name_object(&mut self, name: impl Into<Cow<'static, str>>) {
#[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<DeviceInner> {
&self.pool.device
}
}
impl<T: Pooled + vk::Handle + Clone> Drop for PoolObject<T> {
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<T: Pooled + vk::Handle + Clone> Deref for PoolObject<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[derive(Debug, Clone)]
pub struct Pool<T> { pub struct Pool<T> {
pub(crate) pool: Arc<Mutex<Vec<T>>>, pub(crate) pool: Mutex<Vec<T>>,
pub(crate) device: Arc<DeviceInner>, pub(crate) device: Arc<DeviceInner>,
} }
@ -862,7 +818,7 @@ impl<T> Pool<T> {
} }
pub fn new(device: Arc<DeviceInner>) -> Self { pub fn new(device: Arc<DeviceInner>) -> Self {
Self { Self {
pool: Arc::new(Mutex::new(Vec::new())), pool: Mutex::new(Vec::new()),
device, device,
} }
} }
@ -871,27 +827,40 @@ impl<T> Pool<T> {
} }
} }
impl<T: Pooled + vk::Handle + Clone> Pool<T> { impl<T> AsRef<Pool<T>> for Pool<T> {
pub fn get(&self) -> Result<PoolObject<T>> { fn as_ref(&self) -> &Pool<T> {
self
}
}
pub type PoolObject<T, U: AsRef<Pool<T>>> = asdf::ExternallyManagedObject<T, U>;
impl<'a, T: Pooled + asdf::traits::ExternallyManagedObject<&'a Pool<T>> + 'a> Pool<T> {
pub fn get(&'a self) -> Result<PoolObject<T, &'a Pool<T>>> {
let item = if let Some(item) = self.pool.lock().pop() { let item = if let Some(item) = self.pool.lock().pop() {
item item
} else { } else {
T::create_from_pool(self)? T::create_from_pool(self)?
}; };
Ok(PoolObject { Ok(asdf::ExternallyManagedObject::new(item, self))
inner: ManuallyDrop::new(item),
pool: self.clone(),
#[cfg(debug_assertions)]
name: None,
})
} }
pub fn get_named(&self, name: Option<impl Into<Cow<'static, str>>>) -> Result<PoolObject<T>> { pub fn get_debug_named(
let mut obj = self.get()?; &'a self,
if let Some(name) = name { name: Option<impl Into<Cow<'static, str>>>,
obj.name_object(name); ) -> Result<PoolObject<T, &'a Pool<T>>>
where
T: asdf::traits::DebugNameable,
{
let obj = self.get()?;
#[cfg(debug_assertions)]
{
let name = name.map(Into::into).unwrap_or_default();
<T as asdf::traits::DebugNameable>::debug_name(&obj, &self.device, &name);
} }
Ok(obj) 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. // This module is an experiment in a more generic way to manage device-owned resources.
#[cfg(false)] // #[cfg(false)]
mod asdf { pub(crate) mod asdf {
use std::{ use std::{
mem::{ManuallyDrop, MaybeUninit},
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
sync::Arc, sync::Arc,
}; };
@ -964,24 +934,28 @@ mod asdf {
use ash::vk; use ash::vk;
use crate::{ use crate::{
device::{DeviceInner, GpuAllocation}, device::{DeviceInner, DevicePools, GpuAllocation},
util::DebugName, 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<T> { pub trait ExternallyManagedObject<T> {
/// # Safety /// # Safety
/// The caller must ensure this function is only called once for a given object. /// 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 { pub trait DebugNameable {
fn debug_name(&self, device: &super::DeviceInner, name: &str); fn debug_name(&self, device: &super::DeviceInner, name: &str);
} }
} }
struct ExternallyManagedObject<T: traits::ExternallyManagedObject<O>, O> { /// Wrapper for types which are owned by another type `O`, which is responsible for destruction.
inner: T, pub struct ExternallyManagedObject<T: traits::ExternallyManagedObject<O>, O> {
inner: ManuallyDrop<T>,
owner: O, owner: O,
} }
@ -1000,12 +974,51 @@ mod asdf {
} }
impl<T: traits::ExternallyManagedObject<O>, O> ExternallyManagedObject<T, O> { impl<T: traits::ExternallyManagedObject<O>, O> ExternallyManagedObject<T, O> {
fn new(inner: T, owner: O) -> Self { pub fn new(inner: T, owner: O) -> Self {
Self { inner, owner } Self {
inner: ManuallyDrop::new(inner),
owner,
}
} }
fn owner(&self) -> &O { pub fn owner(&self) -> &O {
&self.owner &self.owner
} }
pub fn map_inner<U>(self, f: impl FnOnce(T) -> U) -> ExternallyManagedObject<U, O>
where
U: traits::ExternallyManagedObject<O>,
{
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<U>(self, f: impl FnOnce(O) -> U) -> ExternallyManagedObject<T, U>
where
T: traits::ExternallyManagedObject<U>,
{
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<T, O> Drop for ExternallyManagedObject<T, O> impl<T, O> Drop for ExternallyManagedObject<T, O>
@ -1014,12 +1027,17 @@ mod asdf {
{ {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
self.inner.destroy(&self.owner); let inner = ManuallyDrop::take(&mut self.inner);
inner.destroy(&self.owner);
} }
} }
} }
struct DeviceObject<T: traits::ExternallyManagedObject<O>, O: AsRef<super::DeviceInner>> { /// A wrapper for vulkan types which are owned by the device, taking care of destruction.
pub struct DeviceObject<
T: traits::ExternallyManagedObject<O>,
O: AsRef<super::DeviceInner> = Arc<super::DeviceInner>,
> {
inner: ExternallyManagedObject<T, O>, inner: ExternallyManagedObject<T, O>,
name: Option<DebugName>, name: Option<DebugName>,
} }
@ -1065,23 +1083,42 @@ mod asdf {
} }
mod impls { mod impls {
use crate::device::{DeviceInner, GpuAllocation}; use crate::device::{DeviceInner, DevicePools, GpuAllocation};
use super::*; use super::*;
impl<T: AsRef<DeviceInner>> traits::ExternallyManagedObject<T> for ash::vk::Semaphore { impl<T: AsRef<DeviceInner>> traits::ExternallyManagedObject<T> for ash::vk::Semaphore {
unsafe fn destroy(&mut self, device: &T) { unsafe fn destroy(self, device: &T) {
unsafe { unsafe {
device.as_ref().raw.destroy_semaphore(*self, None); device.as_ref().raw.destroy_semaphore(self, None);
} }
} }
} }
impl<T: AsRef<DeviceInner>> traits::ExternallyManagedObject<T> for GpuAllocation { impl<T: AsRef<DeviceInner>> traits::ExternallyManagedObject<T> for GpuAllocation {
unsafe fn destroy(&mut self, device: &T) { unsafe fn destroy(self, device: &T) {
let mut swapped = GpuAllocation::default(); _ = device.as_ref().alloc2.lock().free(self);
std::mem::swap(self, &mut swapped); }
_ = device.as_ref().alloc2.lock().free(swapped); }
impl traits::ExternallyManagedObject<DevicePools> for vk::Semaphore {
unsafe fn destroy(self, owner: &DevicePools) {
owner
.binary_semaphores
.push(crate::sync::BinarySemaphore(self));
}
}
impl traits::ExternallyManagedObject<DevicePools> 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>() -> T { fn summon<T>() -> T {
unimplemented!() unimplemented!()
} }
@ -1125,5 +1167,14 @@ mod asdf {
summon::<Arc<super::DeviceInner>>(), summon::<Arc<super::DeviceInner>>(),
summon::<GpuAllocation>(), summon::<GpuAllocation>(),
); );
let pool_owned: ExternallyManagedObject<vk::Semaphore, DevicePools> =
ExternallyManagedObject::new(summon::<vk::Semaphore>(), summon::<DevicePools>());
let enum_semaphore_pooled: ExternallyManagedObject<Semaphore, DevicePools> =
ExternallyManagedObject::new(
Semaphore::Binary(summon::<vk::Semaphore>()),
summon::<DevicePools>(),
);
} }
} }

View file

@ -155,8 +155,12 @@ impl SyncThreadpool {
} }
pub enum Fence { pub enum Fence {
Dedicated { fence: DeviceObject<vk::Fence> }, Dedicated {
Pooled { fence: PoolObject<vk::Fence> }, fence: DeviceObject<vk::Fence>,
},
Pooled {
fence: PoolObject<vk::Fence, Arc<Pool<vk::Fence>>>,
},
} }
impl Pooled for vk::Fence { impl Pooled for vk::Fence {
@ -171,6 +175,16 @@ impl Pooled for vk::Fence {
} }
} }
impl<T> crate::device::asdf::traits::ExternallyManagedObject<T> for vk::Fence
where
T: AsRef<Pool<vk::Fence>>,
{
unsafe fn destroy(self, owner: &T) {
let pool = owner.as_ref();
pool.push(self);
}
}
impl std::fmt::Debug for Fence { impl std::fmt::Debug for Fence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Fence").field("fence", &self.raw()).finish() f.debug_struct("Fence").field("fence", &self.raw()).finish()
@ -188,12 +202,9 @@ impl Fence {
fence: DeviceObject::new(fence, device, name.map(Into::into)), fence: DeviceObject::new(fence, device, name.map(Into::into)),
}) })
} }
pub fn from_pool(pool: &Pool<vk::Fence>, name: Option<&'static str>) -> Result<Fence> { pub fn from_pool(pool: &Arc<Pool<vk::Fence>>, name: Option<&'static str>) -> Result<Fence> {
let mut fence = pool.get()?; let fence = pool.get_debug_named(name)?.map_owner(|_| pool.clone());
#[cfg(debug_assertions)]
if let Some(name) = name {
fence.name_object(name);
}
Ok(Self::Pooled { fence }) Ok(Self::Pooled { fence })
} }
@ -207,7 +218,7 @@ impl Fence {
fn device(&self) -> &Arc<DeviceInner> { fn device(&self) -> &Arc<DeviceInner> {
match self { match self {
Fence::Dedicated { fence } => &fence.device().shared, 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)] #[derive(Debug, Clone)]
pub(crate) struct BinarySemaphore(vk::Semaphore); pub(crate) struct BinarySemaphore(pub(crate) vk::Semaphore);
#[derive(Debug, Clone)] #[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 // This is just so that ash can name these semaphore newtypes
impl vk::Handle for BinarySemaphore { impl vk::Handle for BinarySemaphore {