simplify device ownership logic

This commit is contained in:
janis 2026-10-24 18:11:14 +02:00
parent ce46ca1801
commit dd02ea23cf
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
9 changed files with 225 additions and 348 deletions

View file

@ -223,7 +223,7 @@ impl SingleUseCommand {
pub fn end(&self) -> VkResult<()> { pub fn end(&self) -> VkResult<()> {
assert_eq!(self.state(), CommandBufferState::Recording); assert_eq!(self.state(), CommandBufferState::Recording);
unsafe { unsafe {
self.inner.dev().dev().end_command_buffer(self.handle())?; self.device().dev().end_command_buffer(self.handle())?;
} }
self.state.set_executable(); self.state.set_executable();
Ok(()) Ok(())
@ -288,7 +288,7 @@ impl SingleUseCommand {
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
signal: Option<vk::Semaphore>, signal: Option<vk::Semaphore>,
) -> crate::Result<()> { ) -> crate::Result<()> {
let fence = Arc::new(sync::Fence::from_pool(&self.device().pools().fences, None)?); let fence = Arc::new(sync::Fence::from_pool(self.device(), None)?);
let future = self.submit_async(wait, signal, fence)?; let future = self.submit_async(wait, signal, fence)?;
future.block()?; future.block()?;
Ok(()) Ok(())

View file

@ -460,7 +460,7 @@ impl Renderer2 {
let future = cmds.submit( let future = cmds.submit(
Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)), Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)),
Some(frame.release), Some(frame.release),
Arc::new(sync::Fence::from_pool(&self.device.pools().fences, None)?), Arc::new(sync::Fence::from_pool(&self.device, None)?),
)?; )?;
future.await; future.await;

View file

@ -521,7 +521,7 @@ impl Pipeline {
device device
.dev() .dev()
.create_compute_pipelines( .create_compute_pipelines(
device.pools().pipeline_cache.raw, device.pools.pipeline_cache.raw,
core::slice::from_ref(info), core::slice::from_ref(info),
None, None,
) )
@ -690,7 +690,7 @@ impl Pipeline {
device device
.dev() .dev()
.create_graphics_pipelines( .create_graphics_pipelines(
device.pools().pipeline_cache.raw, device.pools.pipeline_cache.raw,
core::slice::from_ref(&info), core::slice::from_ref(&info),
None, None,
) )

View file

@ -168,7 +168,7 @@ impl Wireframe {
let future = cmd.submit_async( let future = cmd.submit_async(
None, None,
None, None,
Arc::new(sync::Fence::from_pool(&dev.pools().fences, None)?), Arc::new(sync::Fence::from_pool(&dev, None)?),
)?; )?;
let pipeline = Self::create_pipeline(dev.clone())?; let pipeline = Self::create_pipeline(dev.clone())?;

View file

@ -542,7 +542,7 @@ impl Swapchain {
tracing::trace!(frame, "acquiring image for frame {frame}"); tracing::trace!(frame, "acquiring image for frame {frame}");
async move { async move {
let fence = Fence::from_pool(&self.swapchain.device().pools().fences, None)?; let fence = Fence::from_pool(self.swapchain.device(), None)?;
let acquire = self.acquire_semaphores[frame]; let acquire = self.acquire_semaphores[frame];
// spawn on threadpool because it might block. // spawn on threadpool because it might block.

View file

@ -3,13 +3,13 @@ use std::{
collections::{BTreeSet, HashMap, HashSet}, collections::{BTreeSet, HashMap, HashSet},
ffi::CStr, ffi::CStr,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
sync::Arc, sync::{Arc, Weak},
}; };
use ash::{ use ash::{
ext, khr, ext, khr,
prelude::VkResult, prelude::VkResult,
vk::{self, Handle}, vk,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use raw_window_handle::RawDisplayHandle; use raw_window_handle::RawDisplayHandle;
@ -93,12 +93,16 @@ bitflags::bitflags! {
} }
} }
struct DeviceDrop(ash::Device); struct DeviceDrop {
device: ash::Device,
pipeline_cache: PipelineCache,
}
impl Drop for DeviceDrop { impl Drop for DeviceDrop {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
_ = self.0.device_wait_idle(); _ = self.device.device_wait_idle();
self.0.destroy_device(None); self.pipeline_cache.destroy(&self.device);
self.device.destroy_device(None);
} }
} }
} }
@ -157,6 +161,7 @@ pub struct DeviceInner {
pub queues: DeviceQueues, pub queues: DeviceQueues,
pub sync_threadpool: sync::SyncThreadpool, pub sync_threadpool: sync::SyncThreadpool,
pub device_extensions: DeviceExtensions, pub device_extensions: DeviceExtensions,
pub pools: DevicePools,
#[allow(dead_code)] #[allow(dead_code)]
pub enabled_extensions: Vec<&'static CStr>, pub enabled_extensions: Vec<&'static CStr>,
@ -450,7 +455,8 @@ impl PhysicalDeviceInfo {
}, },
})?; })?;
let inner = DeviceInner { let pipeline_cache = PipelineCache::new(&device, &self).unwrap();
let shared = Arc::new_cyclic(|inner: &Weak<DeviceInner>| DeviceInner {
raw: device.clone(), raw: device.clone(),
alloc2: Mutex::new(alloc2), alloc2: Mutex::new(alloc2),
instance: instance.clone(), instance: instance.clone(),
@ -459,14 +465,13 @@ impl PhysicalDeviceInfo {
device_extensions, device_extensions,
enabled_extensions, enabled_extensions,
sync_threadpool: sync::SyncThreadpool::new(), sync_threadpool: sync::SyncThreadpool::new(),
_drop: DeviceDrop(device), pools: DevicePools::new(inner.clone(), pipeline_cache),
}; _drop: DeviceDrop {
device,
let shared = Arc::new(inner); pipeline_cache,
Ok(Device { },
pools: Arc::new(DevicePools::new(shared.clone())), });
shared, Ok(Device(shared))
})
} }
fn required_extensions(&self, requested_extensions: &[Extension<'static>]) -> Vec<*const i8> { fn required_extensions(&self, requested_extensions: &[Extension<'static>]) -> Vec<*const i8> {
@ -496,28 +501,19 @@ impl PhysicalDeviceInfo {
#[derive(Debug)] #[derive(Debug)]
pub struct DevicePools { pub struct DevicePools {
pub pipeline_cache: asdf::DeviceObject<PipelineCache, Arc<DeviceInner>>, pub pipeline_cache: PipelineCache,
pub fences: Arc<Pool<vk::Fence>>, pub fences: Pool<vk::Fence>,
pub binary_semaphores: Pool<BinarySemaphore>, pub binary_semaphores: Pool<BinarySemaphore>,
pub timeline_semaphores: Pool<TimelineSemaphore>, pub timeline_semaphores: Pool<TimelineSemaphore>,
} }
impl AsRef<DevicePools> for DevicePools {
fn as_ref(&self) -> &DevicePools {
self
}
}
impl DevicePools { impl DevicePools {
pub fn new(device: Arc<DeviceInner>) -> Self { fn new(device: Weak<DeviceInner>, pipeline_cache: PipelineCache) -> Self {
Self { Self {
fences: Arc::new(Pool::new(device.clone())), fences: Pool::new(device.clone()),
binary_semaphores: Pool::new(device.clone()), binary_semaphores: Pool::new(device.clone()),
timeline_semaphores: Pool::new(device.clone()), timeline_semaphores: Pool::new(device.clone()),
pipeline_cache: asdf::DeviceObject::new( pipeline_cache,
device.clone(),
PipelineCache::new(&device.raw, &device.adapter).unwrap(),
),
} }
} }
} }
@ -551,20 +547,11 @@ impl DeviceRef for &Arc<Device> {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Device { pub struct Device(Arc<DeviceInner>);
pub(crate) shared: Arc<DeviceInner>,
pools: Arc<DevicePools>,
}
impl Device {
pub fn pools(&self) -> &Arc<DevicePools> {
&self.pools
}
}
impl PartialEq for Device { impl PartialEq for Device {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.shared, &other.shared) Arc::ptr_eq(&self.0, &other.0)
} }
} }
@ -574,7 +561,7 @@ impl core::ops::Deref for Device {
type Target = DeviceInner; type Target = DeviceInner;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.shared &self.0
} }
} }
@ -716,75 +703,7 @@ impl DeviceInner {
} }
} }
#[derive(Clone)] pub use asdf::DeviceObject;
pub struct DeviceOwnedDebugObject<T> {
pub device: Device,
pub object: T,
#[cfg(debug_assertions)]
name: Option<Cow<'static, str>>,
}
impl<T: Eq> Eq for DeviceOwnedDebugObject<T> {}
impl<T: PartialEq> PartialEq for DeviceOwnedDebugObject<T> {
fn eq(&self, other: &Self) -> bool {
std::sync::Arc::ptr_eq(&self.device.shared, &other.device.shared)
&& self.object == other.object
}
}
impl<T: std::fmt::Debug + vk::Handle + Copy> std::fmt::Debug for DeviceOwnedDebugObject<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut fmt = f.debug_struct(core::any::type_name::<T>());
fmt.field_with("device", |f| {
write!(f, "0x{:x}", self.device.raw.handle().as_raw())
})
.field_with("handle", |f| write!(f, "0x{:x}", &self.object.as_raw()));
#[cfg(debug_assertions)]
{
fmt.field("name", &self.name);
}
fmt.finish()
}
}
impl<T> DeviceOwnedDebugObject<T> {
pub fn new(
device: crate::Device,
object: T,
name: Option<Cow<'static, str>>,
) -> ash::prelude::VkResult<Self>
where
T: vk::Handle + Copy,
{
if let Some(name) = name.as_ref() {
unsafe {
device.debug_name_object(object, name);
}
}
Ok(Self {
device,
object,
#[cfg(debug_assertions)]
name,
})
}
pub fn dev(&self) -> &crate::Device {
&self.device
}
pub fn handle(&self) -> T
where
T: Copy,
{
self.object
}
}
pub use asdf::{DeviceObject, InnerDeviceObject};
pub trait DeviceHandle { pub trait DeviceHandle {
/// # Safety /// # Safety
@ -799,23 +718,19 @@ impl<O, T: ExternallyManagedObject<O>> ExternallyManagedObject<O> for Mutex<T> {
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Buffer { impl ExternallyManagedObject<Device> for vk::Buffer {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &Device) {
unsafe { unsafe { device.raw.destroy_buffer(self, None) };
device.as_ref().raw.destroy_buffer(self, None);
}
} }
} }
impl ExternallyManagedObject<Device> for vk::Image {
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Image { unsafe fn destroy(self, device: &Device) {
unsafe fn destroy(self, device: &T) { unsafe { device.raw.destroy_image(self, None) };
unsafe { device.as_ref().raw.destroy_image(self, None) };
} }
} }
impl ExternallyManagedObject<Device> for vk::ImageView {
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::ImageView { unsafe fn destroy(self, device: &Device) {
unsafe fn destroy(self, device: &T) { unsafe { device.raw.destroy_image_view(self, None) };
unsafe { device.as_ref().raw.destroy_image_view(self, None) };
} }
} }
@ -825,46 +740,46 @@ impl DeviceHandle for vk::DescriptorPool {
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::DescriptorPool { impl ExternallyManagedObject<Device> for vk::DescriptorPool {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &Device) {
unsafe { device.as_ref().raw.destroy_descriptor_pool(self, None) }; unsafe { device.raw.destroy_descriptor_pool(self, None) };
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::DescriptorSetLayout { impl ExternallyManagedObject<Device> for vk::DescriptorSetLayout {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &Device) {
unsafe { device.as_ref().raw.destroy_descriptor_set_layout(self, None) }; unsafe { device.raw.destroy_descriptor_set_layout(self, None) };
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::PipelineLayout { impl ExternallyManagedObject<Device> for vk::PipelineLayout {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &Device) {
unsafe { device.as_ref().raw.destroy_pipeline_layout(self, None) }; unsafe { device.raw.destroy_pipeline_layout(self, None) };
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Sampler { impl ExternallyManagedObject<Device> for vk::Sampler {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &Device) {
unsafe { device.as_ref().raw.destroy_sampler(self, None) }; unsafe { device.raw.destroy_sampler(self, None) };
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::ShaderModule { impl ExternallyManagedObject<Device> for vk::ShaderModule {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &Device) {
unsafe { device.as_ref().raw.destroy_shader_module(self, None) }; unsafe { device.raw.destroy_shader_module(self, None) };
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Pipeline { impl ExternallyManagedObject<Device> for vk::Pipeline {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &Device) {
unsafe { device.as_ref().raw.destroy_pipeline(self, None) }; unsafe { device.raw.destroy_pipeline(self, None) };
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::SwapchainKHR { impl ExternallyManagedObject<Device> for vk::SwapchainKHR {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &Device) {
unsafe { unsafe {
if let Some(swapchain) = device.as_ref().device_extensions.swapchain.as_ref() { if let Some(swapchain) = device.device_extensions.swapchain.as_ref() {
swapchain.destroy_swapchain(self, None) swapchain.destroy_swapchain(self, None)
} }
} }
@ -880,17 +795,22 @@ pub trait Pooled: Sized {
fn create_from_pool(pool: &Pool<Self>) -> Result<Self>; fn create_from_pool(pool: &Pool<Self>) -> Result<Self>;
} }
/// A pooled handle which knows how to return itself to its pool.
pub trait Recycle: Sized {
fn recycle(self, device: &Device);
}
#[derive(Debug)] #[derive(Debug)]
pub struct Pool<T> { pub struct Pool<T> {
pub pool: Mutex<Vec<T>>, pub pool: Mutex<Vec<T>>,
pub device: Arc<DeviceInner>, device: Weak<DeviceInner>,
} }
impl<T> Pool<T> { impl<T> Pool<T> {
pub fn push(&self, item: T) { pub fn push(&self, item: T) {
self.pool.lock().push(item); self.pool.lock().push(item);
} }
pub fn new(device: Arc<DeviceInner>) -> Self { pub fn new(device: Weak<DeviceInner>) -> Self {
Self { Self {
pool: Mutex::new(Vec::new()), pool: Mutex::new(Vec::new()),
device, device,
@ -899,6 +819,10 @@ impl<T> Pool<T> {
pub fn pop(&self) -> Option<T> { pub fn pop(&self) -> Option<T> {
self.pool.lock().pop() self.pool.lock().pop()
} }
pub fn device(&self) -> Arc<DeviceInner> {
self.device.upgrade().expect("pool outlived its device")
}
} }
impl<T> AsRef<Pool<T>> for Pool<T> { impl<T> AsRef<Pool<T>> for Pool<T> {
@ -907,7 +831,43 @@ impl<T> AsRef<Pool<T>> for Pool<T> {
} }
} }
pub type PoolObject<T, U = Arc<Pool<T>>> = asdf::ExternallyManagedObject<T, U>; pub struct PoolObject<T: Recycle> {
inner: std::mem::ManuallyDrop<T>,
device: Device,
}
impl<T: Recycle> PoolObject<T> {
pub fn new(inner: T, device: Device) -> Self {
Self {
inner: std::mem::ManuallyDrop::new(inner),
device,
}
}
pub fn device(&self) -> &Device {
&self.device
}
}
impl<T: Recycle> Deref for PoolObject<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: Recycle> DerefMut for PoolObject<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: Recycle> Drop for PoolObject<T> {
fn drop(&mut self) {
let inner = unsafe { std::mem::ManuallyDrop::take(&mut self.inner) };
inner.recycle(&self.device);
}
}
impl<T: Pooled> Pool<T> { impl<T: Pooled> Pool<T> {
pub fn get(&self) -> Result<T> { pub fn get(&self) -> Result<T> {
@ -929,7 +889,8 @@ impl<T: Pooled> Pool<T> {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ {
let name = name.map(Into::into).unwrap_or_default(); let name = name.map(Into::into).unwrap_or_default();
<T as asdf::traits::DebugNameable>::debug_name(&obj, &self.device, &name); let device = self.device();
<T as asdf::traits::DebugNameable>::debug_name(&obj, &device, &name);
} }
Ok(obj) Ok(obj)
@ -946,7 +907,7 @@ macro_rules! define_device_owned_handle {
} $(=> |$this:ident| $dtor:stmt)?) => { } $(=> |$this:ident| $dtor:stmt)?) => {
$(#[$attr])* $(#[$attr])*
$ty_vis struct $ty { $ty_vis struct $ty {
inner: $crate::device::DeviceOwnedDebugObject<$handle>, inner: $crate::device::DeviceObject<$handle>,
$( $(
$(#[$field_attr])* $(#[$field_attr])*
$field_vis $field_name: $field_ty, $field_vis $field_name: $field_ty,
@ -955,10 +916,10 @@ macro_rules! define_device_owned_handle {
impl $crate::device::DeviceOwned<$handle> for $ty { impl $crate::device::DeviceOwned<$handle> for $ty {
fn device(&self) -> &$crate::device::Device { fn device(&self) -> &$crate::device::Device {
self.inner.dev() self.inner.device()
} }
fn handle(&self) -> $handle { fn handle(&self) -> $handle {
self.inner.handle() *self.inner
} }
} }
@ -971,11 +932,11 @@ macro_rules! define_device_owned_handle {
$($field_name: $field_ty,)* $($field_name: $field_ty,)*
) -> ::ash::prelude::VkResult<Self> { ) -> ::ash::prelude::VkResult<Self> {
Ok(Self { Ok(Self {
inner: $crate::device::DeviceOwnedDebugObject::new( inner: $crate::device::DeviceObject::new_debug_named(
device, device,
handle, handle,
name, name,
)?, ),
$($field_name,)* $($field_name,)*
}) })
} }
@ -993,19 +954,17 @@ macro_rules! define_device_owned_handle {
}; };
} }
// This module is an experiment in a more generic way to manage device-owned resources.
// #[cfg(false)]
pub mod asdf { pub mod asdf {
use std::{ use std::{
mem::{ManuallyDrop, MaybeUninit}, mem::ManuallyDrop,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
sync::Arc,
}; };
use ash::vk; use ash::vk;
use ash::vk::Handle; use ash::vk::Handle;
use crate::device::{DebugName, DeviceInner}; use crate::device::{DebugName, DeviceInner};
use crate::Device;
pub mod traits { pub mod traits {
/// A trait describing an object owned by some manager-type, which is /// A trait describing an object owned by some manager-type, which is
@ -1050,45 +1009,6 @@ pub mod asdf {
owner, owner,
} }
} }
pub fn owner(&self) -> &O {
&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>
@ -1103,95 +1023,56 @@ pub mod asdf {
} }
} }
pub type InnerDeviceObject<T> = DeviceObject<T, Arc<DeviceInner>>;
/// A wrapper for vulkan types which are owned by the device, taking care of destruction. /// A wrapper for vulkan types which are owned by the device, taking care of destruction.
pub struct DeviceObject< pub struct DeviceObject<T: traits::ExternallyManagedObject<Device>> {
T: traits::ExternallyManagedObject<O>, inner: ExternallyManagedObject<T, Device>,
O: AsRef<super::DeviceInner> = super::Device,
> {
inner: ExternallyManagedObject<T, O>,
#[allow(dead_code)] #[allow(dead_code)]
name: Option<DebugName>, name: Option<DebugName>,
} }
impl< impl<T: traits::ExternallyManagedObject<Device> + traits::DebugNameable> DeviceObject<T> {
T: traits::ExternallyManagedObject<O> + traits::DebugNameable, pub fn new_debug_named(
O: AsRef<super::DeviceInner>, device: Device,
> DeviceObject<T, O> inner: T,
{ name: Option<impl Into<DebugName>>,
pub fn new_debug_named(owner: O, inner: T, name: Option<impl Into<DebugName>>) -> Self { ) -> Self {
let name = name.map(Into::into); let name = name.map(Into::into);
if let Some(ref name) = name { if let Some(ref name) = name {
traits::DebugNameable::debug_name(&inner, owner.as_ref(), name); traits::DebugNameable::debug_name(&inner, &device, name);
} }
let obj = ExternallyManagedObject::new(inner, owner); let obj = ExternallyManagedObject::new(inner, device);
Self { inner: obj, name } Self { inner: obj, name }
} }
} }
impl<T: traits::ExternallyManagedObject<O>, O: AsRef<super::DeviceInner>> DeviceObject<T, O> { impl<T: traits::ExternallyManagedObject<Device>> DeviceObject<T> {
pub fn new(owner: O, inner: T) -> Self { pub fn new(device: Device, inner: T) -> Self {
let inner = ExternallyManagedObject::new(inner, owner); let inner = ExternallyManagedObject::new(inner, device);
Self { inner, name: None } Self { inner, name: None }
} }
pub fn new_debug_named_with<D>( pub fn device(&self) -> &Device {
owner: O, &self.inner.owner
inner: T, }
name: Option<impl Into<DebugName>>,
debug_namable: impl FnOnce(&T) -> D, pub fn handle(&self) -> T
) -> Self
where where
D: traits::DebugNameable, T: Copy,
{ {
let name = name.map(Into::into); *Deref::deref(self)
if let Some(ref name) = name {
traits::DebugNameable::debug_name(&debug_namable(&inner), owner.as_ref(), name);
}
let obj = ExternallyManagedObject::new(inner, owner);
Self { inner: obj, name }
}
pub fn device(&self) -> &O {
self.inner.owner()
} }
pub fn name(&self) -> Option<&str> { pub fn name(&self) -> Option<&str> {
self.name.as_ref().map(|n| &**n) self.name.as_deref()
}
pub fn map_inner<U>(self, f: impl FnOnce(T) -> U) -> DeviceObject<U, O>
where
U: traits::ExternallyManagedObject<O>,
{
DeviceObject {
inner: self.inner.map_inner(f),
name: self.name,
} }
} }
pub fn map_owner<U>(self, f: impl FnOnce(O) -> U) -> DeviceObject<T, U> impl<T> std::fmt::Debug for DeviceObject<T>
where where
T: traits::ExternallyManagedObject<U>, T: std::fmt::Debug + traits::ExternallyManagedObject<Device>,
U: AsRef<super::DeviceInner>,
{
DeviceObject {
inner: self.inner.map_owner(f),
name: self.name,
}
}
}
impl<T, O> std::fmt::Debug for DeviceObject<T, O>
where
T: std::fmt::Debug + traits::ExternallyManagedObject<O>,
O: AsRef<super::DeviceInner>,
{ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut fmt = f.debug_struct(core::any::type_name::<T>()); let mut fmt = f.debug_struct(core::any::type_name::<T>());
@ -1200,8 +1081,7 @@ pub mod asdf {
fmt.field_with("device", |f| { fmt.field_with("device", |f| {
write!( write!(
f, f,
"0x{:x}", "0x{:x}", self.inner.owner.raw.handle().as_raw()
self.inner.owner().as_ref().raw.handle().as_raw()
) )
}) })
.field_with("handle", |f| write!(f, "{inner:?}")) .field_with("handle", |f| write!(f, "{inner:?}"))
@ -1211,10 +1091,9 @@ pub mod asdf {
} }
} }
impl<T, O> Deref for DeviceObject<T, O> impl<T> Deref for DeviceObject<T>
where where
T: traits::ExternallyManagedObject<O>, T: traits::ExternallyManagedObject<Device>,
O: AsRef<super::DeviceInner>,
{ {
type Target = T; type Target = T;
@ -1223,10 +1102,9 @@ pub mod asdf {
} }
} }
impl<T, O> DerefMut for DeviceObject<T, O> impl<T> DerefMut for DeviceObject<T>
where where
T: traits::ExternallyManagedObject<O>, T: traits::ExternallyManagedObject<Device>,
O: AsRef<super::DeviceInner>,
{ {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner &mut self.inner
@ -1234,29 +1112,39 @@ pub mod asdf {
} }
mod impls { mod impls {
use crate::device::{DeviceInner, DevicePools, GpuAllocation}; use crate::device::GpuAllocation;
use super::*; use super::*;
impl<T: AsRef<DeviceInner>> traits::ExternallyManagedObject<T> for vk::Semaphore { impl traits::ExternallyManagedObject<Device> for vk::Semaphore {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &Device) {
unsafe { unsafe { device.raw.destroy_semaphore(self, None) };
device.as_ref().raw.destroy_semaphore(self, None);
}
}
}
impl<T: AsRef<DeviceInner>> traits::ExternallyManagedObject<T> for GpuAllocation {
unsafe fn destroy(self, device: &T) {
_ = device.as_ref().alloc2.lock().free(self);
} }
} }
impl traits::ExternallyManagedObject<DevicePools> for vk::Semaphore { impl traits::ExternallyManagedObject<Device> for GpuAllocation {
unsafe fn destroy(self, owner: &DevicePools) { unsafe fn destroy(self, device: &Device) {
owner _ = device.alloc2.lock().free(self);
.binary_semaphores }
.push(crate::sync::BinarySemaphore(self)); }
impl traits::ExternallyManagedObject<Device> for vk::CommandBuffer {
unsafe fn destroy(self, _: &Device) {}
}
impl traits::ExternallyManagedObject<Device> for vk::CommandPool {
unsafe fn destroy(self, _: &Device) {}
}
impl<T> crate::device::DeviceOwned<T> for DeviceObject<T>
where
T: vk::Handle + Copy + traits::ExternallyManagedObject<Device>,
{
fn device(&self) -> &Device {
self.device()
}
fn handle(&self) -> T {
self.handle()
} }
} }
@ -1275,30 +1163,27 @@ pub mod asdf {
#[allow(dead_code)] #[allow(dead_code)]
#[cfg(test)] #[cfg(test)]
fn asdf() { fn asdf() {
use crate::device::{DevicePools, GpuAllocation}; use crate::device::GpuAllocation;
fn summon<T>() -> T { fn summon<T>() -> T {
unimplemented!() unimplemented!()
} }
let _inner_ref: DeviceObject<vk::Semaphore, &DeviceInner> = DeviceObject::new_debug_named( let _inner_ref: DeviceObject<vk::Semaphore> = DeviceObject::new_debug_named(
summon::<&DeviceInner>(), summon::<Device>(),
summon::<vk::Semaphore>(), summon::<vk::Semaphore>(),
Some("my semaphore"), Some("my semaphore"),
); );
let _device_owned: DeviceObject<vk::Semaphore, super::Device> = let _device_owned: DeviceObject<vk::Semaphore> = DeviceObject::new_debug_named(
DeviceObject::new_debug_named(
summon::<super::Device>(), summon::<super::Device>(),
summon::<vk::Semaphore>(), summon::<vk::Semaphore>(),
Some("my other semaphore"), Some("my other semaphore"),
); );
let _allocation: DeviceObject<GpuAllocation, Arc<super::DeviceInner>> = DeviceObject::new( let _allocation: DeviceObject<GpuAllocation> =
summon::<Arc<super::DeviceInner>>(), DeviceObject::new(summon::<Device>(), summon::<GpuAllocation>());
summon::<GpuAllocation>(),
);
let _pool_owned: ExternallyManagedObject<vk::Semaphore, DevicePools> = let _pool_owned: DeviceObject<vk::Semaphore> =
ExternallyManagedObject::new(summon::<vk::Semaphore>(), summon::<DevicePools>()); DeviceObject::new(summon::<Device>(), summon::<vk::Semaphore>());
} }
} }

View file

@ -38,7 +38,7 @@ mod device_ref_derive_tests {
} }
#[derive(renderer_macros::DeviceRef)] #[derive(renderer_macros::DeviceRef)]
struct GenericWrapper<D> { struct GenericWrapper<D: DeviceRef> {
#[device] #[device]
device: D, device: D,
tag: &'static str, tag: &'static str,

View file

@ -1,23 +1,19 @@
use std::sync::Arc;
use ash::vk; use ash::vk;
use ash::Device; use ash::Device;
use crate::PhysicalDeviceInfo; use crate::PhysicalDeviceInfo;
use crate::device::DeviceInner; #[derive(Debug, Clone, Copy)]
#[derive(Debug)]
pub struct PipelineCache { pub struct PipelineCache {
#[allow(dead_code)] #[allow(dead_code)]
key: u128, key: u128,
pub raw: vk::PipelineCache, pub raw: vk::PipelineCache,
} }
impl crate::device::asdf::traits::ExternallyManagedObject<Arc<DeviceInner>> for PipelineCache { impl PipelineCache {
unsafe fn destroy(self, owner: &Arc<DeviceInner>) { pub(crate) unsafe fn destroy(self, device: &ash::Device) {
tracing::info!("destroying pipeline cache with key {:x}", self.key); tracing::info!("destroying pipeline cache with key {:x}", self.key);
if let Ok(data) = self.export(&owner.raw) { if let Ok(data) = self.export(device) {
tracing::info!( tracing::info!(
"exported pipeline cache with key {:x} and size {} bytes", "exported pipeline cache with key {:x} and size {} bytes",
self.key, self.key,
@ -27,11 +23,9 @@ impl crate::device::asdf::traits::ExternallyManagedObject<Arc<DeviceInner>> for
tracing::error!("failed to write pipeline cache to disk: {err}"); tracing::error!("failed to write pipeline cache to disk: {err}");
}); });
} }
unsafe { owner.raw.destroy_pipeline_cache(self.raw, None) }; unsafe { device.destroy_pipeline_cache(self.raw, None) };
} }
}
impl PipelineCache {
const MAGIC: [u8; 4] = *b"VYPC"; const MAGIC: [u8; 4] = *b"VYPC";
const KEY_VERSION: u32 = 1; const KEY_VERSION: u32 = 1;
const PATH: &'static str = "pipeline_cache.bin"; const PATH: &'static str = "pipeline_cache.bin";

View file

@ -6,9 +6,9 @@ use std::{
}; };
use crate::device::{ use crate::device::{
DevicePools, Pool, PoolObject, Pooled, Pool, PoolObject, Pooled, Recycle,
asdf::{ asdf::{
DeviceObject, InnerDeviceObject, DeviceObject,
traits::ExternallyManagedObject as ExternallyManagedObjectTrait, traits::ExternallyManagedObject as ExternallyManagedObjectTrait,
}, },
}; };
@ -160,17 +160,17 @@ impl SyncThreadpool {
pub enum Fence { pub enum Fence {
Dedicated { Dedicated {
fence: InnerDeviceObject<vk::Fence>, fence: DeviceObject<vk::Fence>,
}, },
Pooled { Pooled {
fence: PoolObject<vk::Fence, Arc<Pool<vk::Fence>>>, fence: PoolObject<vk::Fence>,
}, },
} }
impl Pooled for vk::Fence { impl Pooled for vk::Fence {
fn create_from_pool(pool: &Pool<Self>) -> Result<Self> { fn create_from_pool(pool: &Pool<Self>) -> Result<Self> {
let fence = unsafe { let fence = unsafe {
pool.device pool.device()
.raw .raw
.create_fence(&vk::FenceCreateInfo::default(), None)? .create_fence(&vk::FenceCreateInfo::default(), None)?
}; };
@ -179,17 +179,15 @@ impl Pooled for vk::Fence {
} }
} }
impl ExternallyManagedObjectTrait<Arc<Pool<vk::Fence>>> for vk::Fence { impl Recycle for vk::Fence {
unsafe fn destroy(self, pool: &Arc<Pool<vk::Fence>>) { fn recycle(self, device: &Device) {
pool.push(self); device.pools.fences.push(self);
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObjectTrait<T> for vk::Fence { impl ExternallyManagedObjectTrait<Device> for vk::Fence {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &Device) {
unsafe { unsafe { device.raw.destroy_fence(self, None) };
device.as_ref().raw.destroy_fence(self, None);
}
} }
} }
@ -207,14 +205,14 @@ impl Fence {
.create_fence(&vk::FenceCreateInfo::default(), None)? .create_fence(&vk::FenceCreateInfo::default(), None)?
}; };
Ok(Self::Dedicated { Ok(Self::Dedicated {
fence: DeviceObject::new_debug_named(device.shared.clone(), fence, name), fence: DeviceObject::new_debug_named(device, fence, name),
}) })
} }
pub fn from_pool(pool: &Arc<Pool<vk::Fence>>, name: Option<&'static str>) -> Result<Fence> { pub fn from_pool(device: &Device, name: Option<&'static str>) -> Result<Fence> {
let fence = pool.get_debug_named(name)?; let fence = device.pools.fences.get_debug_named(name)?;
Ok(Self::Pooled { Ok(Self::Pooled {
fence: PoolObject::new(fence, pool.clone()), fence: PoolObject::new(fence, device.clone()),
}) })
} }
@ -225,10 +223,10 @@ impl Fence {
} }
} }
fn device(&self) -> &Arc<DeviceInner> { fn device(&self) -> &Device {
match self { match self {
Fence::Dedicated { fence } => &fence.device(), Fence::Dedicated { fence } => fence.device(),
Fence::Pooled { fence } => &fence.owner().device, Fence::Pooled { fence } => fence.device(),
} }
} }
@ -278,21 +276,21 @@ pub enum SemaphoreInner {
Timeline(vk::Semaphore), Timeline(vk::Semaphore),
} }
impl ExternallyManagedObjectTrait<Arc<DevicePools>> for SemaphoreInner { impl Recycle for SemaphoreInner {
unsafe fn destroy(self, owner: &Arc<DevicePools>) { fn recycle(self, owner: &Device) {
match self { match self {
SemaphoreInner::Binary(semaphore) => { SemaphoreInner::Binary(semaphore) => {
owner.binary_semaphores.push(BinarySemaphore(semaphore)) owner.pools.binary_semaphores.push(BinarySemaphore(semaphore))
} }
SemaphoreInner::Timeline(semaphore) => { SemaphoreInner::Timeline(semaphore) => {
owner.timeline_semaphores.push(TimelineSemaphore(semaphore)) owner.pools.timeline_semaphores.push(TimelineSemaphore(semaphore))
} }
} }
} }
} }
impl ExternallyManagedObjectTrait<Arc<DeviceInner>> for SemaphoreInner { impl ExternallyManagedObjectTrait<Device> for SemaphoreInner {
unsafe fn destroy(self, owner: &Arc<DeviceInner>) { unsafe fn destroy(self, owner: &Device) {
match self { match self {
SemaphoreInner::Binary(semaphore) | SemaphoreInner::Timeline(semaphore) => { SemaphoreInner::Binary(semaphore) | SemaphoreInner::Timeline(semaphore) => {
unsafe { owner.raw.destroy_semaphore(semaphore, None) }; unsafe { owner.raw.destroy_semaphore(semaphore, None) };
@ -336,11 +334,11 @@ impl From<TimelineSemaphore> for SemaphoreInner {
pub enum Semaphore { pub enum Semaphore {
Dedicated { Dedicated {
semaphore: InnerDeviceObject<SemaphoreInner>, semaphore: DeviceObject<SemaphoreInner>,
}, },
Pooled { Pooled {
#[allow(private_interfaces)] #[allow(private_interfaces)]
semaphore: PoolObject<SemaphoreInner, Arc<DevicePools>>, semaphore: PoolObject<SemaphoreInner>,
}, },
} }
@ -379,7 +377,7 @@ impl Pooled for BinarySemaphore {
let mut type_info = let mut type_info =
vk::SemaphoreTypeCreateInfo::default().semaphore_type(vk::SemaphoreType::BINARY); vk::SemaphoreTypeCreateInfo::default().semaphore_type(vk::SemaphoreType::BINARY);
let create_info = vk::SemaphoreCreateInfo::default().push_next(&mut type_info); let create_info = vk::SemaphoreCreateInfo::default().push_next(&mut type_info);
let inner = unsafe { pool.device.raw.create_semaphore(&create_info, None)? }; let inner = unsafe { pool.device().raw.create_semaphore(&create_info, None)? };
Ok(Self(inner)) Ok(Self(inner))
} }
} }
@ -390,7 +388,7 @@ impl Pooled for TimelineSemaphore {
.semaphore_type(vk::SemaphoreType::TIMELINE) .semaphore_type(vk::SemaphoreType::TIMELINE)
.initial_value(0); .initial_value(0);
let create_info = vk::SemaphoreCreateInfo::default().push_next(&mut type_info); let create_info = vk::SemaphoreCreateInfo::default().push_next(&mut type_info);
let inner = unsafe { pool.device.raw.create_semaphore(&create_info, None)? }; let inner = unsafe { pool.device().raw.create_semaphore(&create_info, None)? };
Ok(Self(inner)) Ok(Self(inner))
} }
} }
@ -421,7 +419,7 @@ impl Semaphore {
}; };
Ok(Self::Dedicated { Ok(Self::Dedicated {
semaphore: DeviceObject::new_debug_named(device.shared.clone(), inner, name), semaphore: DeviceObject::new_debug_named(device, inner, name),
}) })
} }
@ -433,12 +431,12 @@ impl Semaphore {
let semaphore = match semaphore_type { let semaphore = match semaphore_type {
SemaphoreType::Binary => { SemaphoreType::Binary => {
let semaphore: SemaphoreInner = let semaphore: SemaphoreInner =
device.pools().binary_semaphores.get_debug_named(name)?.into(); device.pools.binary_semaphores.get_debug_named(name)?.into();
PoolObject::new(semaphore, device.pools().clone()) PoolObject::new(semaphore, device)
} }
SemaphoreType::Timeline(value) => { SemaphoreType::Timeline(value) => {
let semaphore: SemaphoreInner = device let semaphore: SemaphoreInner = device
.pools() .pools
.timeline_semaphores .timeline_semaphores
.get_debug_named(name)? .get_debug_named(name)?
.into(); .into();
@ -450,7 +448,7 @@ impl Semaphore {
device.raw.signal_semaphore(&info)?; device.raw.signal_semaphore(&info)?;
} }
PoolObject::new(semaphore, device.pools().clone()) PoolObject::new(semaphore, device)
} }
}; };