diff --git a/crates/renderer/src/commands.rs b/crates/renderer/src/commands.rs index 32e261d..b9b7ca7 100644 --- a/crates/renderer/src/commands.rs +++ b/crates/renderer/src/commands.rs @@ -277,7 +277,7 @@ impl SingleUseCommand { signal: Option, fence: Arc, ) -> VkResult> { - self.submit_fence(wait, signal, Some(fence.fence()))?; + self.submit_fence(wait, signal, Some(fence.raw()))?; Ok(FenceFuture::new(fence)) } @@ -287,8 +287,8 @@ impl SingleUseCommand { self, wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, signal: Option, - ) -> VkResult<()> { - let fence = Arc::new(sync::Fence::create(self.device().clone())?); + ) -> crate::Result<()> { + let fence = Arc::new(sync::Fence::new_pooled(&self.device().pools.fences, None)?); let future = self.submit_async(wait, signal, fence)?; future.block()?; Ok(()) diff --git a/crates/renderer/src/device.rs b/crates/renderer/src/device.rs index 413b292..78dfe8e 100644 --- a/crates/renderer/src/device.rs +++ b/crates/renderer/src/device.rs @@ -1,7 +1,9 @@ use std::{ borrow::Cow, + cell::UnsafeCell, collections::{BTreeSet, HashMap, HashSet}, ffi::CStr, + mem::{ManuallyDrop, MaybeUninit}, ops::Deref, sync::Arc, }; @@ -11,6 +13,7 @@ use ash::{ prelude::VkResult, vk::{self, Handle}, }; +use parking_lot::Mutex; use raw_window_handle::RawDisplayHandle; use tinyvec::{ArrayVec, array_vec}; @@ -405,7 +408,13 @@ impl PhysicalDeviceInfo { _drop: DeviceDrop(device), }; - Ok(Device(Arc::new(inner))) + let shared = Arc::new(inner); + Ok(Device { + pools: Arc::new(DevicePools { + fences: Pool::new(shared.clone()), + }), + shared, + }) } fn required_extensions(&self, requested_extensions: &[Extension<'static>]) -> Vec<*const i8> { @@ -434,11 +443,19 @@ impl PhysicalDeviceInfo { } #[derive(Clone, Debug)] -pub struct Device(Arc); +pub(crate) struct DevicePools { + pub(crate) fences: Pool, +} + +#[derive(Clone, Debug)] +pub struct Device { + pub(crate) shared: Arc, + pub(crate) pools: Arc, +} impl PartialEq for Device { fn eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.0, &other.0) + Arc::ptr_eq(&self.shared, &other.shared) } } @@ -448,57 +465,57 @@ impl core::ops::Deref for Device { type Target = DeviceInner; fn deref(&self) -> &Self::Target { - &self.0 + &self.shared } } -impl Device { +impl DeviceInner { pub fn sync_threadpool(&self) -> &sync::SyncThreadpool { - &self.0.sync_threadpool + &self.sync_threadpool } pub fn alloc(&self) -> &vk_mem::Allocator { - &self.0.alloc + &self.alloc } pub fn dev(&self) -> &ash::Device { - &self.0.raw + &self.raw } pub fn instance(&self) -> &Instance { - &self.0.instance + &self.instance } pub fn queues(&self) -> &DeviceQueues { - &self.0.queues + &self.queues } pub fn phy(&self) -> vk::PhysicalDevice { - self.0.adapter.pdev + self.adapter.pdev } pub fn features(&self) -> &crate::PhysicalDeviceFeatures { - &self.0.adapter.features + &self.adapter.features } pub fn properties(&self) -> &crate::PhysicalDeviceProperties { - &self.0.adapter.properties + &self.adapter.properties } pub fn physical_device(&self) -> &PhysicalDeviceInfo { - &self.0.adapter + &self.adapter } pub fn main_queue(&self) -> &Queue { - self.0.queues.graphics() + self.queues.graphics() } pub fn compute_queue(&self) -> &Queue { - self.0.queues.compute() + self.queues.compute() } pub fn transfer_queue(&self) -> &Queue { - self.0.queues.transfer() + self.queues.transfer() } pub unsafe fn lock_queues(&self) { unsafe { - self.0.queues.lock(); + self.queues.lock(); } } pub unsafe fn unlock_queues(&self) { unsafe { - self.0.queues.unlock(); + self.queues.unlock(); } } @@ -515,7 +532,7 @@ impl Device { tracing::warn!("locking all queues and waiting for device to idle"); unsafe { self.lock_queues(); - self.dev().device_wait_idle()?; + self.raw.device_wait_idle()?; self.unlock_queues(); } tracing::warn!("finished waiting: unlocking all queues."); @@ -540,7 +557,9 @@ impl Device { let mut buffer = [0u8; 64]; let buffer_vec: Vec; - let name_bytes = if name.len() < buffer.len() { + let name_bytes = if name.is_empty() { + &[] + } else if name.len() < buffer.len() { buffer[..name.len()].copy_from_slice(name.as_bytes()); &buffer[..] } else { @@ -733,7 +752,89 @@ pub trait DeviceOwned { fn handle(&self) -> T; } -/// Macro for helping create and destroy Vulkan objects which are owned by a device. +pub trait Pooled: vk::Handle + Sized { + fn create_from_pool(pool: &Pool) -> Result; +} + +pub struct PoolObject { + inner: ManuallyDrop, + pool: Pool, + #[cfg(debug_assertions)] + 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)] +pub struct Pool { + pub(crate) pool: Arc>>, + pub(crate) device: Arc, +} + +impl Pool { + pub fn push(&self, item: T) { + self.pool.lock().push(item); + } + pub fn new(device: Arc) -> Self { + Self { + pool: Arc::new(Mutex::new(Vec::new())), + device, + } + } +} + +impl Pool { + pub fn get(&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, + }) + } +} + +// Macro for helping create and destroy Vulkan objects which are owned by a device. #[macro_export] macro_rules! define_device_owned_handle { ($(#[$attr:meta])* diff --git a/crates/renderer/src/images.rs b/crates/renderer/src/images.rs index 4fb18a2..4319a47 100644 --- a/crates/renderer/src/images.rs +++ b/crates/renderer/src/images.rs @@ -6,7 +6,7 @@ use std::{ use crate::{ define_device_owned_handle, - device::{DeviceOwned, QueueFlags}, + device::{DeviceObject, DeviceOwned, QueueFlags}, }; use super::Device; @@ -123,6 +123,16 @@ impl Default for ImageDesc { } } +enum ImageInner { + Swapchain(vk::Image), + Allocated(vk::Image, vk_mem::Allocation), +} + +pub struct Image2 { + image: DeviceObject, + alloc: Option, +} + define_device_owned_handle! { #[derive(Debug)] pub Image(vk::Image) { diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index ac43371..81a54e4 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -1011,7 +1011,7 @@ impl Renderer2 { let future = cmds.submit( Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)), Some(frame.release), - Arc::new(sync::Fence::create(self.device.clone())?), + Arc::new(sync::Fence::new_pooled(&self.device.pools.fences, None)?), )?; future.await; diff --git a/crates/renderer/src/swapchain.rs b/crates/renderer/src/swapchain.rs index fc7e00a..7a582f1 100644 --- a/crates/renderer/src/swapchain.rs +++ b/crates/renderer/src/swapchain.rs @@ -20,7 +20,7 @@ use crate::{ device::{Device, DeviceObject, DeviceOwned}, images, instance::InstanceInner, - sync, + sync::{self, Fence}, util::RawMutexGuard, }; @@ -338,9 +338,9 @@ impl Drop for Swapchain { fn drop(&mut self) { unsafe { self.release_resources(); - self.functor.destroy_swapchain(*self.swapchain, None); } - todo!() + // the swapchain itself will be automatically destroyed by the + // DeviceObject's Drop implementation. } } @@ -489,7 +489,7 @@ impl Swapchain { /// suboptimal and should be recreated. fn acquire_image( self: Arc, - ) -> impl std::future::Future> { + ) -> impl std::future::Future> { let frame = self .current_frame .try_update(Ordering::Release, Ordering::Relaxed, |i| { @@ -500,7 +500,7 @@ impl Swapchain { tracing::trace!(frame, "acquiring image for frame {frame}"); async move { - let fence = self.fences[frame]; + let fence = Fence::new_pooled(&self.swapchain.device().pools.fences, None)?; let acquire = self.acquire_semaphores[frame]; let release = self.release_semaphores[frame]; @@ -510,14 +510,14 @@ impl Swapchain { move || unsafe { this.with_locked(|swapchain| { this.functor - .acquire_next_image(swapchain, u64::MAX, acquire, fence) + .acquire_next_image(swapchain, u64::MAX, acquire, fence.raw()) }) } }) .await?; // wait for image to become available. - sync::FenceFuture::new(fence.clone()).await; + fence.into_future().await; let idx = idx as usize; let image = self.images[idx].clone(); diff --git a/crates/renderer/src/sync.rs b/crates/renderer/src/sync.rs index 3dd0ec6..49f9b68 100644 --- a/crates/renderer/src/sync.rs +++ b/crates/renderer/src/sync.rs @@ -5,6 +5,9 @@ use std::{ time::Duration, }; +use crate::device::{DeviceObject, DeviceOwned, Pool, PoolObject, Pooled}; +use crate::{Result, device::DeviceInner}; + use super::Device; use ash::{prelude::*, vk}; use crossbeam::channel::{Receiver, Sender}; @@ -154,75 +157,94 @@ pub struct Semaphore { inner: vk::Semaphore, } -pub struct Fence { - dev: Device, - fence: vk::Fence, +pub enum Fence { + Dedicated { fence: DeviceObject }, + Pooled { fence: PoolObject }, +} + +impl Pooled for vk::Fence { + fn create_from_pool(pool: &Pool) -> Result { + let fence = unsafe { + pool.device + .raw + .create_fence(&vk::FenceCreateInfo::default(), None)? + }; + + Ok(fence) + } } impl std::fmt::Debug for Fence { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Fence").field("fence", &self.fence).finish() - } -} - -impl Drop for Fence { - fn drop(&mut self) { - unsafe { - self.dev.dev().destroy_fence(self.fence, None); - } + f.debug_struct("Fence").field("fence", &self.raw()).finish() } } impl Fence { - unsafe fn new(dev: Device, fence: vk::Fence) -> Fence { - Self { dev, fence } + pub fn new_dedicated(device: Device, name: Option<&'static str>) -> Result { + let fence = unsafe { + device + .raw + .create_fence(&vk::FenceCreateInfo::default(), None)? + }; + Ok(Self::Dedicated { + fence: DeviceObject::new(fence, device, name.map(Into::into)), + }) } - pub fn create(dev: Device) -> VkResult { - unsafe { - Ok(Self::new( - dev.clone(), - dev.dev() - .create_fence(&vk::FenceCreateInfo::default(), None)?, - )) + pub fn new_pooled(pool: &Pool, name: Option<&'static str>) -> Result { + let mut fence = pool.get()?; + #[cfg(debug_assertions)] + if let Some(name) = name { + fence.name_object(name); + } + Ok(Self::Pooled { fence }) + } + + pub fn raw(&self) -> vk::Fence { + match self { + Fence::Dedicated { fence } => **fence, + Fence::Pooled { fence } => **fence, } } - #[allow(dead_code)] - pub fn create_signaled(dev: Device) -> VkResult { - unsafe { - Ok(Self::new( - dev.clone(), - dev.dev().create_fence( - &vk::FenceCreateInfo::default().flags(vk::FenceCreateFlags::SIGNALED), - None, - )?, - )) + + fn device(&self) -> &Arc { + match self { + Fence::Dedicated { fence } => &fence.device().shared, + Fence::Pooled { fence } => fence.device(), } } - pub fn wait_on(&self, timeout: Option) -> Result<(), vk::Result> { - use core::slice::from_ref; + + pub fn wait_on(&self, timeout: Option) -> Result<()> { unsafe { - self.dev - .dev() - .wait_for_fences(from_ref(&self.fence), true, timeout.unwrap_or(u64::MAX)) + self.device().raw.wait_for_fences( + core::slice::from_ref(&self.raw()), + true, + timeout.unwrap_or(u64::MAX), + )? } + Ok(()) } - pub fn fence(&self) -> vk::Fence { - self.fence - } + pub fn is_signaled(&self) -> bool { - unsafe { self.dev.dev().get_fence_status(self.fence).unwrap_or(false) } - } - pub fn reset(&self) -> Result<(), vk::Result> { unsafe { - self.dev - .dev() - .reset_fences(core::slice::from_ref(&self.fence)) + self.device() + .raw + .get_fence_status(self.raw()) + .unwrap_or(false) } } -} -impl AsRef for Fence { - fn as_ref(&self) -> &vk::Fence { - todo!() + + pub fn reset(&self) -> Result<()> { + unsafe { + self.device() + .raw + .reset_fences(core::slice::from_ref(&self.raw()))? + } + Ok(()) + } + + pub fn into_future<'a>(self) -> FenceFuture<'a> { + FenceFuture::new(Arc::new(self)) } } @@ -265,24 +287,16 @@ pub struct FenceFuture<'a> { } impl FenceFuture<'_> { - /// # Safety - /// `fence` must not be destroyed while this future is live. - #[allow(dead_code)] - pub unsafe fn from_fence(device: Device, fence: vk::Fence) -> Self { - Self { - fence: Arc::new(unsafe { Fence::new(device, fence) }), - _pd: PhantomData, - } - } pub fn new(fence: Arc) -> Self { Self { fence, _pd: PhantomData, } } - pub fn block(&self) -> VkResult<()> { + pub fn block(&self) -> crate::Result<()> { self.fence.wait_on(None)?; - self.fence.reset() + self.fence.reset()?; + Ok(()) } } @@ -299,7 +313,7 @@ impl Future for FenceFuture<'_> { std::task::Poll::Ready(()) } else { self.fence - .dev + .device() .sync_threadpool() .spawn_waiter(self.fence.clone(), cx.waker().clone()); std::task::Poll::Pending