From 55d22e3164ece5dd7cbefc5c7880cba8d91de044 Mon Sep 17 00:00:00 2001 From: janis Date: Thu, 2 Apr 2026 13:32:49 +0200 Subject: [PATCH] image? alloc? bruh.. --- Cargo.toml | 1 + crates/renderer/Cargo.toml | 1 + crates/renderer/src/device.rs | 96 +++++-- crates/renderer/src/images.rs | 479 +++++++++++++++---------------- crates/renderer/src/lib.rs | 19 +- crates/renderer/src/memory.rs | 26 -- crates/renderer/src/queue.rs | 18 ++ crates/renderer/src/swapchain.rs | 4 +- crates/renderer/src/util.rs | 71 +++++ 9 files changed, 419 insertions(+), 296 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9d38753..b49a858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ thread_local = "1.1.8" ash = "0.38.0" ash-window = "0.13.0" vk-mem = "0.5.0" +gpu-allocator = { git = "https://github.com/janis-bhm/gpu-allocator", branch = "main" } vk-sync = "0.1.6" arrayvec = "0.7.6" diff --git a/crates/renderer/Cargo.toml b/crates/renderer/Cargo.toml index 491aca3..cb95eee 100644 --- a/crates/renderer/Cargo.toml +++ b/crates/renderer/Cargo.toml @@ -23,6 +23,7 @@ tracing = { workspace = true } ash = { workspace = true } ash-window = { workspace = true } vk-mem = { workspace = true } +gpu-allocator = { workspace = true } raw-window-handle = { workspace = true } egui = { workspace = true , features = ["bytemuck"]} diff --git a/crates/renderer/src/device.rs b/crates/renderer/src/device.rs index 008ff2e..19baf26 100644 --- a/crates/renderer/src/device.rs +++ b/crates/renderer/src/device.rs @@ -1,10 +1,9 @@ use std::{ borrow::Cow, - cell::UnsafeCell, collections::{BTreeSet, HashMap, HashSet}, ffi::CStr, - mem::{ManuallyDrop, MaybeUninit}, - ops::Deref, + mem::ManuallyDrop, + ops::{Deref, DerefMut}, sync::Arc, }; @@ -19,7 +18,6 @@ use tinyvec::{ArrayVec, array_vec}; use crate::{ Instance, PhysicalDeviceFeatures, PhysicalDeviceInfo, Result, - instance::InstanceInner, queue::{DeviceQueueInfos, DeviceQueues, Queue}, sync::{self, BinarySemaphore, TimelineSemaphore}, }; @@ -110,9 +108,36 @@ struct DeviceExtensions { pub(crate) mesh_shader: Option, } -#[allow(unused)] +type GpuAllocation = gpu_allocator::vulkan::Allocation; + +impl DeviceHandle for GpuAllocation { + unsafe fn destroy(&mut self, device: &Device) { + let mut swapped = GpuAllocation::default(); + std::mem::swap(self, &mut swapped); + _ = device.alloc2.lock().free(swapped); + } +} + +#[derive(Debug)] +pub(crate) enum Allocation { + Owned(DeviceObject), + Shared(Arc>), + Unmanaged, +} + +impl Allocation { + pub(crate) fn allocation(&self) -> Option<&GpuAllocation> { + match self { + Allocation::Owned(obj) => Some(obj), + Allocation::Shared(arc) => Some(arc.as_ref()), + Allocation::Unmanaged => None, + } + } +} + pub struct DeviceInner { pub(crate) alloc: vk_mem::Allocator, + pub(crate) alloc2: Mutex, pub(crate) raw: ash::Device, pub(crate) adapter: PhysicalDeviceInfo, pub(crate) instance: Instance, @@ -389,6 +414,21 @@ impl PhysicalDeviceInfo { }, }; + let alloc2 = + gpu_allocator::vulkan::Allocator::new(&gpu_allocator::vulkan::AllocatorCreateDesc { + instance: instance.inner.raw.clone(), + device: device.clone(), + physical_device: self.pdev, + debug_settings: Default::default(), + buffer_device_address: false, + allocation_sizes: { + const MB: u64 = 1024 * 1024; + gpu_allocator::AllocationSizes::new(8 * MB, 64 * MB) + .with_max_host_memblock_size(256 * MB) + .with_max_device_memblock_size(256 * MB) + }, + })?; + let inner = DeviceInner { raw: device.clone(), alloc: unsafe { @@ -398,6 +438,7 @@ impl PhysicalDeviceInfo { self.pdev, ))? }, + alloc2: Mutex::new(alloc2), instance: instance.clone(), adapter: self, queues: device_queues, @@ -681,11 +722,20 @@ impl Deref for DeviceObject { } } +impl DerefMut for DeviceObject { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + impl DeviceObject { - pub fn new(inner: T, device: Device, name: Option>) -> Self { + pub fn new(inner: T, device: Device, name: Option>) -> Self + where + T: vk::Handle + Clone, + { unsafe { if let Some(name) = name.as_ref() { - device.debug_name_object(inner, &name); + device.debug_name_object(inner.clone(), &name); } } @@ -696,6 +746,14 @@ impl DeviceObject { name, } } + pub fn new_without_name(inner: T, device: Device) -> Self { + Self { + inner, + device, + #[cfg(debug_assertions)] + name: None, + } + } pub fn device(&self) -> &Device { &self.device } @@ -713,46 +771,48 @@ impl DeviceObject { impl Drop for DeviceObject { fn drop(&mut self) { - self.destroy(&self.device); + unsafe { + self.inner.destroy(&self.device); + } } } -pub trait DeviceHandle: vk::Handle + Copy { - fn destroy(self, device: &Device); +pub trait DeviceHandle { + unsafe fn destroy(&mut self, device: &Device); } impl DeviceHandle for vk::Semaphore { - fn destroy(self, device: &Device) { + unsafe fn destroy(&mut self, device: &Device) { unsafe { - device.dev().destroy_semaphore(self, None); + device.dev().destroy_semaphore(*self, None); } } } impl DeviceHandle for vk::Fence { - fn destroy(self, device: &Device) { + unsafe fn destroy(&mut self, device: &Device) { unsafe { - device.dev().destroy_fence(self, None); + device.dev().destroy_fence(*self, None); } } } impl DeviceHandle for vk::Buffer { - fn destroy(self, device: &Device) { + unsafe fn destroy(&mut self, device: &Device) { unsafe { - device.dev().destroy_buffer(self, None); + device.dev().destroy_buffer(*self, None); } } } impl DeviceHandle for vk::SwapchainKHR { - fn destroy(self, device: &Device) { + unsafe fn destroy(&mut self, device: &Device) { unsafe { device .device_extensions .swapchain .as_ref() - .map(|swapchain| swapchain.destroy_swapchain(self, None)); + .map(|swapchain| swapchain.destroy_swapchain(*self, None)); } } } diff --git a/crates/renderer/src/images.rs b/crates/renderer/src/images.rs index c116064..996e473 100644 --- a/crates/renderer/src/images.rs +++ b/crates/renderer/src/images.rs @@ -6,12 +6,14 @@ use std::{ use crate::{ define_device_owned_handle, - device::{DeviceHandle, DeviceObject, DeviceOwned, QueueFlags}, + device::{Allocation, DeviceHandle, DeviceObject, DeviceOwned, QueueFlags}, + swapchain::Swapchain, + util::weak_vec::WeakVec, }; use super::Device; use ash::{prelude::*, vk}; -use itertools::Itertools; +use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme}; use parking_lot::Mutex; use vk_mem::Alloc; @@ -30,8 +32,7 @@ pub struct ImageDesc { pub queue_families: QueueFlags, pub layout: vk::ImageLayout, - pub mem_usage: vk_mem::MemoryUsage, - pub alloc_flags: vk_mem::AllocationCreateFlags, + pub mem_location: gpu_allocator::MemoryLocation, } impl std::hash::Hash for ImageDesc { @@ -47,8 +48,7 @@ impl std::hash::Hash for ImageDesc { self.usage.hash(state); self.queue_families.hash(state); self.layout.hash(state); - self.mem_usage.hash(state); - self.alloc_flags.bits().hash(state); + self.mem_location.hash(state); } } @@ -67,8 +67,7 @@ impl PartialEq for ImageDesc { && self.usage == other.usage && self.queue_families == other.queue_families && self.layout == other.layout - && self.mem_usage == other.mem_usage - && self.alloc_flags.bits() == other.alloc_flags.bits() + && self.mem_location == other.mem_location } } @@ -87,17 +86,7 @@ impl<'a> std::fmt::Debug for ImageDesc { .field("usage", &self.usage) .field("queue_families", &self.queue_families) .field("layout", &self.layout) - .field("mem_usage", &self.mem_usage) - .field_with("alloc_flags", |f| { - write!( - f, - "{}", - self.alloc_flags - .iter_names() - .map(|(name, _)| name) - .format(" | ") - ) - }) + .field("mem_location", &self.mem_location) .finish() } } @@ -117,96 +106,154 @@ impl Default for ImageDesc { usage: Default::default(), queue_families: QueueFlags::empty(), layout: vk::ImageLayout::UNDEFINED, - alloc_flags: vk_mem::AllocationCreateFlags::empty(), - mem_usage: vk_mem::MemoryUsage::Auto, + mem_location: gpu_allocator::MemoryLocation::Unknown, } } } +#[derive(Debug)] enum ImageInner { - Swapchain(vk::Image), - Allocated(vk::Image, vk_mem::Allocation), + Swapchain(vk::Image, Device), + Allocated(DeviceObject, Allocation), } -#[derive(Debug, Clone, Copy)] -struct ImageAlloc { - image: vk::Image, - alloc: Option, -} - -impl vk::Handle for ImageAlloc { - const TYPE: vk::ObjectType = ::TYPE; - - fn as_raw(self) -> u64 { - self.image.as_raw() - } - - fn from_raw(_: u64) -> Self { - unimplemented!() - } -} - -impl DeviceHandle for ImageAlloc { - fn destroy(mut self, device: &Device) { +impl DeviceHandle for vk::Image { + unsafe fn destroy(&mut self, device: &Device) { unsafe { - match &mut self.alloc { - Some(alloc) => { - device.alloc.destroy_image(self.image, alloc); - } - None => { - device.raw.destroy_image(self.image, None); - } - } + device.dev().destroy_image(*self, None); } } } -pub struct Image2 { - image: DeviceObject, -} +impl ImageInner { + fn image(&self) -> vk::Image { + match self { + Self::Swapchain(image, _) => *image, + Self::Allocated(image, _) => **image, + } + } + fn device(&self) -> &Device { + match self { + Self::Swapchain(_, device) => device, + Self::Allocated(image, _) => image.device(), + } + } -define_device_owned_handle! { - #[derive(Debug)] - pub Image(vk::Image) { - alloc: Option, - size: vk::Extent3D, - format: vk::Format, - views: Mutex>, - aliases: Mutex>>, - parent: Option>, - is_swapchain_image: bool, - } => |this| if !this.is_swapchain_image { - unsafe { - for &view in this.views.lock().values() { - this.inner.dev().dev().destroy_image_view(view, None); - } - - let handle = this.handle(); - let dev = this.device().clone(); - if let Some(alloc) = this.alloc.as_mut() { - // destroy image handle and allocation - dev.alloc().destroy_image(handle, alloc); - } else { - // destroy image handle - dev.dev().destroy_image(handle, None); - } + fn allocation(&self) -> Option<&Allocation> { + match self { + Self::Swapchain(_, _) => None, + Self::Allocated(_, alloc) => Some(alloc), + } + } + fn allocation_mut(&mut self) -> Option<&mut Allocation> { + match self { + Self::Swapchain(_, _) => None, + Self::Allocated(_, alloc) => Some(alloc), } } } -impl Eq for Image {} -impl PartialEq for Image { - fn eq(&self, other: &Self) -> bool { - self.inner == other.inner - } +#[derive(Debug)] +pub struct Image { + image: ImageInner, + desc: ImageDesc, + views: Mutex>, } impl Image { - pub fn new(device: Device, desc: ImageDesc) -> VkResult { + pub fn new(device: Device, desc: ImageDesc) -> crate::Result { + let (image, requirements) = Self::new_raw(device.clone(), &desc)?; + let alloc = device.alloc2.lock().allocate(&AllocationCreateDesc { + name: desc.name.as_deref().unwrap_or(""), + requirements, + location: desc.mem_location, + linear: desc.tiling == vk::ImageTiling::LINEAR, + allocation_scheme: AllocationScheme::GpuAllocatorManaged, + })?; + + Ok(Self { + image: ImageInner::Allocated( + DeviceObject::new(image, device.clone(), desc.name.clone()), + Allocation::Owned(DeviceObject::new_without_name(alloc, device)), + ), + desc, + views: Default::default(), + }) + } + + pub fn new_with_allocation( + device: Device, + allocation: Allocation, + desc: ImageDesc, + ) -> crate::Result { + let (image, requirements) = Self::new_raw(device.clone(), &desc)?; + + // validate allocation + let alloc_size = allocation + .allocation() + .map(|alloc| alloc.size()) + .unwrap_or(0); + if alloc_size < requirements.size { + tracing::error!( + "allocation size {} is smaller than image memory requirements {}", + alloc_size, + requirements.size + ); + return Err(crate::Error::Unspecified); + } + + if allocation + .allocation() + .map(|alloc| 1 << alloc.memory_type_index()) + .unwrap_or(0) + & requirements.memory_type_bits + == 0 + { + return Err(crate::Error::Unspecified); + } + + Ok(Self { + image: ImageInner::Allocated( + DeviceObject::new(image, device.clone(), desc.name.clone()), + allocation, + ), + desc, + views: Default::default(), + }) + } + + pub fn from_swapchain_image(image: vk::Image, swapchain: &Swapchain) -> Self { + Self { + image: ImageInner::Swapchain(image, swapchain.swapchain.device().clone()), + desc: ImageDesc { + format: swapchain.config.format, + kind: vk::ImageType::TYPE_2D, + mip_levels: 1, + array_layers: 1, + samples: vk::SampleCountFlags::TYPE_1, + extent: vk::Extent3D { + width: swapchain.config.extent.width, + height: swapchain.config.extent.height, + depth: 1, + }, + tiling: vk::ImageTiling::OPTIMAL, + usage: swapchain.config.usage, + queue_families: QueueFlags::PRESENT, + layout: vk::ImageLayout::UNDEFINED, + mem_location: gpu_allocator::MemoryLocation::GpuOnly, + ..Default::default() + }, + views: Default::default(), + } + } + + fn new_raw( + device: Device, + desc: &ImageDesc, + ) -> crate::Result<(vk::Image, vk::MemoryRequirements)> { tracing::trace!("allocate new image with desc={desc:?}"); let ImageDesc { flags, - name, format, kind, mip_levels, @@ -217,11 +264,10 @@ impl Image { usage, queue_families, layout, - mem_usage, - alloc_flags, + .. } = desc; - let queue_families = device.queue_families().family_indices(queue_families); + let queue_families = device.queues.family_indices(*queue_families); let sharing_mode = if queue_families.len() > 1 { vk::SharingMode::CONCURRENT @@ -230,180 +276,87 @@ impl Image { }; let info = &vk::ImageCreateInfo::default() - .flags(flags) - .image_type(kind) - .format(format) - .extent(extent) - .samples(samples) - .initial_layout(layout) - .tiling(tiling) - .usage(usage) + .flags(*flags) + .image_type(*kind) + .format(*format) + .extent(*extent) + .samples(*samples) + .initial_layout(*layout) + .tiling(*tiling) + .usage(*usage) .sharing_mode(sharing_mode) .queue_family_indices(&queue_families) - .array_layers(array_layers) - .mip_levels(mip_levels); + .array_layers(*array_layers) + .mip_levels(*mip_levels); - let alloc_info = &vk_mem::AllocationCreateInfo { - usage: mem_usage, - flags: alloc_flags, - ..Default::default() + // validate + let limits = &device.adapter.properties.core.limits; + let max_dim = match *kind { + vk::ImageType::TYPE_1D => limits.max_image_dimension1_d, + vk::ImageType::TYPE_2D => limits.max_image_dimension2_d, + vk::ImageType::TYPE_3D => limits.max_image_dimension3_d, + _ => unreachable!(), }; - let (handle, alloc) = unsafe { device.alloc().create_image(info, alloc_info)? }; + if extent.width > max_dim || extent.height > max_dim || extent.depth > max_dim { + tracing::error!( + "image extent {extent:?} exceeds device limits (max dimension: {max_dim})" + ); - Self::construct( - device, - handle, - name, - Some(alloc), - extent, - format, - Mutex::new(HashMap::new()), - Mutex::new(HashMap::new()), - None, // aliased - false, - ) + return Err(crate::Error::ImageTooLarge { + width: extent.width, + height: extent.height, + max_size: max_dim, + }); + } + + let image = unsafe { device.raw.create_image(&info, None)? }; + + let requirements = unsafe { device.raw.get_image_memory_requirements(image) }; + + Ok((image, requirements)) } +} - pub unsafe fn from_swapchain_image( - device: Device, - image: vk::Image, - name: Option>, - extent: vk::Extent3D, - format: vk::Format, - ) -> Result { - Self::construct( - device, - image, - name, - None, - extent, - format, - Mutex::new(HashMap::new()), - Mutex::new(HashMap::new()), - None, - true, - ) +impl Eq for Image {} +impl PartialEq for Image { + fn eq(&self, other: &Self) -> bool { + self.image.image() == other.image.image() } +} +impl Image { pub fn format(&self) -> vk::Format { - self.format + self.desc.format } pub fn image(&self) -> vk::Image { - self.handle() + self.image.image() } pub fn size(&self) -> vk::Extent3D { - self.size + self.desc.extent } pub fn extent_2d(&self) -> vk::Extent2D { vk::Extent2D { - width: self.size.width, - height: self.size.height, + width: self.desc.extent.width, + height: self.desc.extent.height, } } pub fn width(&self) -> u32 { - self.size.width + self.desc.extent.width } pub fn height(&self) -> u32 { - self.size.height + self.desc.extent.height } pub fn depth(&self) -> u32 { - self.size.depth + self.desc.extent.depth } - fn get_parent_or_self(self: &Arc) -> Arc { - self.parent - .as_ref() - .map(|weak| weak.upgrade().unwrap()) - .unwrap_or_else(|| self.clone()) + pub fn allocation(&self) -> Option<&Allocation> { + self.image.allocation() } - // TODO: figure out how to make this safer - pub fn get_alias(self: &Arc, desc: ImageDesc) -> VkResult> { - unsafe { self.get_parent_or_self().get_alias_inner(desc) } - } - - /// # Safety - /// must only be called on the primogenitor of an image. - /// get the primogenitor with [`Self::get_parent_or_self()`] - unsafe fn get_alias_inner(self: Arc, desc: ImageDesc) -> VkResult> { - use std::collections::hash_map::Entry::*; - match self.aliases.lock().entry(desc.clone()) { - Occupied(occupied) => Ok(occupied.get().clone()), - Vacant(vacant) => { - let ImageDesc { - flags, - name, - format, - kind, - mip_levels, - array_layers, - samples, - extent, - tiling, - usage, - queue_families, - layout, - .. - } = desc; - - let queue_families = self - .device() - .queue_families() - .family_indices(queue_families); - - let sharing_mode = if queue_families.len() > 1 { - vk::SharingMode::CONCURRENT - } else { - vk::SharingMode::EXCLUSIVE - }; - - let info = &vk::ImageCreateInfo::default() - .flags(flags) - .image_type(kind) - .format(format) - .extent(extent) - .samples(samples) - .initial_layout(layout) - .tiling(tiling) - .usage(usage) - .sharing_mode(sharing_mode) - .queue_family_indices(&queue_families) - .array_layers(array_layers) - .mip_levels(mip_levels); - - let alloc = self - .alloc - .as_ref() - .expect("no alloc associated with image. is this the framebuffer?"); - - let image = unsafe { - let image = self.device().dev().create_image(info, None)?; - - let req = self.device().dev().get_image_memory_requirements(image); - if self.device().alloc().get_allocation_info(alloc).size < req.size { - return Err(vk::Result::ERROR_MEMORY_MAP_FAILED); - } - - self.device().alloc().bind_image_memory(alloc, image)?; - image - }; - - let alias = Self::construct( - self.device().clone(), - image, - name, - None, - extent, - format, - Mutex::new(HashMap::new()), - Mutex::new(HashMap::new()), - Some(Arc::downgrade(&self)), - self.is_swapchain_image, - )?; - Ok(vacant.insert(Arc::new(alias)).clone()) - } - } + pub fn allocation_mut(&mut self) -> Option<&mut Allocation> { + self.image.allocation_mut() } /// technically, this ImageView belongs to the image and is managed by it. @@ -434,11 +387,33 @@ impl Image { } } - pub fn create_view(&self, desc: ImageViewDesc) -> VkResult { + pub fn create_view(&self, desc: ImageViewDesc) -> crate::Result { + // validate + if !view_kind_compatible(self.desc.kind, desc.kind) { + tracing::error!( + "image view kind {:?} is not compatible with image kind {:?}", + desc.kind, + self.desc.kind + ); + return Err(crate::Error::IncompatibleImageViewKind { + image_kind: self.desc.kind, + view_kind: desc.kind, + }); + } + + if desc.mip_range.0 > self.desc.mip_levels || desc.mip_range.1 > self.desc.mip_levels { + tracing::error!( + "image view mip range {:?} exceeds image mip levels {}", + desc.mip_range, + self.desc.mip_levels + ); + return Err(crate::Error::Unspecified); + } + let create_info = vk::ImageViewCreateInfo::default() .flags(desc.flags) .image(self.image()) - .view_type(vk::ImageViewType::TYPE_2D) + .view_type(desc.kind) .format(desc.format) .components(desc.components) .subresource_range( @@ -450,16 +425,28 @@ impl Image { .layer_count(desc.layer_range.count()), ); - let view = unsafe { self.device().dev().create_image_view(&create_info, None)? }; + let device = self.image.device(); + let view = unsafe { device.raw.create_image_view(&create_info, None)? }; ImageView::construct(self.device().clone(), view, desc.name) } } +fn view_kind_compatible(image_kind: vk::ImageType, view_kind: vk::ImageViewType) -> bool { + use vk::ImageType as IT; + use vk::ImageViewType as VT; + match (image_kind, view_kind) { + (IT::TYPE_1D, VT::TYPE_1D | VT::TYPE_1D_ARRAY) => true, + (IT::TYPE_2D, VT::TYPE_2D | VT::TYPE_2D_ARRAY | VT::CUBE | VT::CUBE_ARRAY) => true, + (IT::TYPE_3D, VT::TYPE_2D | VT::TYPE_2D_ARRAY | VT::TYPE_3D) => true, + _ => false, + } +} + #[derive(Debug, Default, Clone)] pub struct ImageViewDesc { - pub flags: vk::ImageViewCreateFlags, pub name: Option>, + pub flags: vk::ImageViewCreateFlags, pub kind: vk::ImageViewType, pub format: vk::Format, pub components: vk::ComponentMapping, @@ -567,10 +554,18 @@ impl PartialEq for ImageViewDesc { } } -define_device_owned_handle! { - #[derive(Debug)] - pub ImageView(vk::ImageView) {} => |this| unsafe { - this.device().dev().destroy_image_view(this.handle(), None); +#[derive(Debug)] +pub struct ImageView { + view: DeviceObject, + desc: ImageViewDesc, + image: Arc, +} + +impl DeviceHandle for vk::ImageView { + unsafe fn destroy(&mut self, device: &Device) { + unsafe { + device.dev().destroy_image_view(*self, None); + } } } diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index c65bd91..e71ff63 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -6,17 +6,12 @@ slice_partition_dedup )] -use std::{ - cell::OnceCell, collections::HashMap, ffi::CStr, fmt::Debug, marker::PhantomData, sync::Arc, -}; +use std::{collections::HashMap, ffi::CStr, fmt::Debug, marker::PhantomData, sync::Arc}; -use bitflags::bitflags; use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; -use parking_lot::{Mutex, MutexGuard}; - use ash::{ - Entry, ext, khr, + ext, prelude::VkResult, vk::{self, SurfaceCapabilitiesKHR}, }; @@ -32,7 +27,6 @@ pub mod device; mod egui_pass; mod images; pub mod instance; -mod memory; mod pipeline; pub mod render_graph; pub mod rendering; @@ -101,6 +95,8 @@ pub enum Error { Utf8Error(#[from] core::str::Utf8Error), #[error(transparent)] NulError(#[from] std::ffi::NulError), + #[error(transparent)] + GpuAllocatorError(#[from] gpu_allocator::AllocationError), #[error("No Physical Device found.")] NoPhysicalDevice, #[error(transparent)] @@ -115,6 +111,13 @@ pub enum Error { }, #[error("Image dimensions cannot be zero.")] ImageZeroSized, + #[error("Incompatible image view kind {view_kind:?} for image kind {image_kind:?}.")] + IncompatibleImageViewKind { + view_kind: vk::ImageViewType, + image_kind: vk::ImageType, + }, + #[error("Unspecified Error")] + Unspecified, } pub type Result = core::result::Result; diff --git a/crates/renderer/src/memory.rs b/crates/renderer/src/memory.rs index b9804f1..8b13789 100644 --- a/crates/renderer/src/memory.rs +++ b/crates/renderer/src/memory.rs @@ -1,27 +1 @@ -#![allow(dead_code)] -use crate::device::Device; -//#[derive(Debug)] -pub struct DeviceMemoryDesc { - pub flags: vk_mem::AllocationCreateFlags, - pub size: u64, - pub align: u64, - pub type_bits: u32, - pub usage: vk_mem::MemoryUsage, -} - -#[derive(Debug)] -pub struct DeviceMemory { - device: Device, - alloc: vk_mem::Allocation, -} - -impl DeviceMemory {} - -impl Drop for DeviceMemory { - fn drop(&mut self) { - unsafe { - self.device.alloc().free_memory(&mut self.alloc); - } - } -} diff --git a/crates/renderer/src/queue.rs b/crates/renderer/src/queue.rs index 81b84ae..e9561d6 100644 --- a/crates/renderer/src/queue.rs +++ b/crates/renderer/src/queue.rs @@ -2,6 +2,7 @@ use bitflags::bitflags; use parking_lot::Mutex; use raw_window_handle::RawDisplayHandle; use std::{collections::HashMap, ops::Deref, sync::Arc}; +use tinyvec::{ArrayVec, array_vec}; use ash::vk; @@ -133,6 +134,23 @@ impl DeviceQueues { &self.transfer } + pub fn family_indices(&self, flags: crate::device::QueueFlags) -> ArrayVec<[u32; 4]> { + let mut indices = array_vec!([u32; 4]); + use crate::device::QueueFlags as QF; + if flags.intersects(QF::GRAPHICS | QF::PRESENT) { + indices.push(self.graphics.family.index); + } + if flags.contains(QF::ASYNC_COMPUTE) { + indices.push(self.compute.family.index); + } + if flags.contains(QF::TRANSFER) { + indices.push(self.transfer.family.index); + } + let len = indices.partition_dedup().0.len(); + _ = indices.drain(len..); + indices + } + pub fn swapchain_family_indices(&self) -> &[u32] { core::slice::from_ref(&self.graphics.family.index) } diff --git a/crates/renderer/src/swapchain.rs b/crates/renderer/src/swapchain.rs index 27d8f3c..2ca8487 100644 --- a/crates/renderer/src/swapchain.rs +++ b/crates/renderer/src/swapchain.rs @@ -280,12 +280,12 @@ impl Surface { pub struct Swapchain { // swapchain images, managed by the swapchain and must not be destroyed manually. images: Vec, - swapchain: DeviceObject, + pub(crate) swapchain: DeviceObject, // this carries the device handle, however the `swapchain` field holds a ref count on the device, so it is safe to hold the pointer in the functor as well. #[debug(skip)] functor: khr::swapchain::Device, /// current configuration of the swapchain. - config: SwapchainConfiguration, + pub(crate) config: SwapchainConfiguration, /// the minimum number of images the surface permits. This is used to calculate how many images we can have in-flight at the same time. min_image_count: u32, diff --git a/crates/renderer/src/util.rs b/crates/renderer/src/util.rs index 57ec91f..761a3c7 100644 --- a/crates/renderer/src/util.rs +++ b/crates/renderer/src/util.rs @@ -3,6 +3,77 @@ use std::ops::{Deref, DerefMut}; use ash::vk; use bytemuck::{Pod, Zeroable}; +pub(crate) mod weak_vec { + //! Module containing the [`WeakVec`] API. + + use std::{sync::Weak, vec::Vec}; + + /// An optimized container for `Weak` references of `T` that minimizes reallocations by + /// dropping older elements that no longer have strong references to them. + #[derive(Debug)] + pub(crate) struct WeakVec { + inner: Vec>, + } + + impl Default for WeakVec { + fn default() -> Self { + Self { + inner: Default::default(), + } + } + } + + impl WeakVec { + pub(crate) fn new() -> Self { + Self { inner: Vec::new() } + } + + /// Pushes a new element to this collection. + /// + /// If the inner Vec needs to be reallocated, we will first drop older elements that + /// no longer have strong references to them. + pub(crate) fn push(&mut self, value: Weak) { + if self.inner.len() == self.inner.capacity() { + // Iterating backwards has the advantage that we don't do more work than we have to. + for i in (0..self.inner.len()).rev() { + if self.inner[i].strong_count() == 0 { + self.inner.swap_remove(i); + } + } + + // Make sure our capacity is twice the number of live elements. + // Leaving some spare capacity ensures that we won't re-scan immediately. + self.inner.reserve_exact(self.inner.len()); + } + + self.inner.push(value); + } + } + + pub(crate) struct WeakVecIter { + inner: std::vec::IntoIter>, + } + + impl Iterator for WeakVecIter { + type Item = Weak; + fn next(&mut self) -> Option { + self.inner.next() + } + } + + impl IntoIterator for WeakVec { + type Item = Weak; + type IntoIter = WeakVecIter; + fn into_iter(self) -> Self::IntoIter { + WeakVecIter { + inner: self.inner.into_iter(), + } + } + } +} + +pub(crate) mod cow_arc {} + #[macro_export] macro_rules! def_monotonic_id { ($vis:vis $ty:ident) => {