From b0f6c7a1b30e1ac02f2824edf3fd0e0e3cfd3cde Mon Sep 17 00:00:00 2001 From: janis Date: Thu, 2 Apr 2026 17:10:37 +0200 Subject: [PATCH] yeah........ --- crates/renderer/src/buffers.rs | 24 ++-- crates/renderer/src/commands.rs | 14 +-- crates/renderer/src/device.rs | 23 ++-- crates/renderer/src/egui.rs | 104 +++++++++--------- crates/renderer/src/images.rs | 115 ++++++++++++------- crates/renderer/src/instance.rs | 1 - crates/renderer/src/lib.rs | 7 +- crates/renderer/src/queue.rs | 9 ++ crates/renderer/src/render_graph.rs | 26 ++--- crates/renderer/src/rendering/mod.rs | 40 +++---- crates/renderer/src/swapchain.rs | 159 ++++++++++++++++++--------- crates/renderer/src/sync.rs | 7 +- 12 files changed, 316 insertions(+), 213 deletions(-) diff --git a/crates/renderer/src/buffers.rs b/crates/renderer/src/buffers.rs index ca2e099..5169195 100644 --- a/crates/renderer/src/buffers.rs +++ b/crates/renderer/src/buffers.rs @@ -1,14 +1,11 @@ -use std::{ - borrow::Cow, - ops::{Deref, DerefMut}, - sync::Arc, -}; +use std::borrow::Cow; -use ash::{prelude::VkResult, vk}; +use ash::vk; +use gpu_allocator::vulkan::AllocationScheme; use crate::{ Device, - device::{Allocation, DeviceObject, QueueFlags}, + device::{Allocation, AllocationStrategy, DeviceObject, QueueFlags}, }; #[derive(Clone)] @@ -18,7 +15,9 @@ pub struct BufferDesc { pub size: u64, pub usage: vk::BufferUsageFlags, pub queue_families: QueueFlags, + pub mem_location: gpu_allocator::MemoryLocation, + pub alloc_scheme: AllocationStrategy, } impl std::hash::Hash for BufferDesc { @@ -40,6 +39,7 @@ impl std::fmt::Debug for BufferDesc { .field("usage", &self.usage) .field("queue_families", &self.queue_families) .field("mem_location", &self.mem_location) + .field("alloc_scheme", &self.alloc_scheme) .finish() } } @@ -66,6 +66,7 @@ impl Default for BufferDesc { usage: Default::default(), queue_families: QueueFlags::empty(), mem_location: gpu_allocator::MemoryLocation::Unknown, + alloc_scheme: AllocationStrategy::default(), } } } @@ -97,7 +98,12 @@ impl Buffer { requirements, location: desc.mem_location, linear: true, - allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, + allocation_scheme: match desc.alloc_scheme { + AllocationStrategy::AllocatorManaged => { + AllocationScheme::GpuAllocatorManaged + } + AllocationStrategy::Dedicated => AllocationScheme::DedicatedBuffer(buffer), + }, })?; Ok(Self { @@ -146,7 +152,7 @@ impl Buffer { } } - pub fn buffer(&self) -> vk::Buffer { + pub fn raw(&self) -> vk::Buffer { *self.buffer } pub fn len(&self) -> u64 { diff --git a/crates/renderer/src/commands.rs b/crates/renderer/src/commands.rs index bdc7b40..8f3e7d5 100644 --- a/crates/renderer/src/commands.rs +++ b/crates/renderer/src/commands.rs @@ -33,7 +33,7 @@ impl Drop for SingleUseCommandPool { impl SingleUseCommandPool { pub fn new(device: Device, queue: Queue) -> VkResult> { let pool_info = vk::CommandPoolCreateInfo::default() - .queue_family_index(queue.family()) + .queue_family_index(queue.family_index()) .flags(vk::CommandPoolCreateFlags::TRANSIENT); let pool = unsafe { device.dev().create_command_pool(&pool_info, None)? }; @@ -110,7 +110,7 @@ impl CommandList { self.0[0] .device() .dev() - .queue_submit(queue, &[info], fence.fence()) + .queue_submit(queue.raw(), &[info], fence.raw()) })?; Ok(FenceFuture::<'a>::new(fence)) @@ -257,7 +257,7 @@ impl SingleUseCommand { self.pool.queue().with_locked(|queue| unsafe { self.device() .dev() - .queue_submit(queue, &[submit_info], fence) + .queue_submit(queue.raw(), &[submit_info], fence) })?; self.state.set_pending(); @@ -329,7 +329,7 @@ pub mod traits { queue.with_locked(|queue| unsafe { self.device() .dev() - .queue_submit(queue, &[submit_info], fence.fence()) + .queue_submit(queue.raw(), &[submit_info], fence.raw()) })?; tracing::trace!("submitted queue {:?} and fence {:?}", queue, fence); @@ -888,7 +888,7 @@ mod command_pools { ) -> Result { let this = ManuallyDrop::new(self); - if queue.1 != this.pool.family { + if queue.family_index() != this.pool.family { tracing::error!("attempted to submit commandbuffer to incompatible queue."); return Err(Error::InvalidQueueSubmission); } @@ -911,7 +911,7 @@ mod command_pools { queue.with_locked(|queue| unsafe { this.device() .dev() - .queue_submit(queue, &[submit_info], fence.fence()) + .queue_submit(queue.raw(), &[submit_info], fence.raw()) })?; Ok(CommandBufferFuture { @@ -931,7 +931,7 @@ mod command_pools { } impl CommandBufferFuture { - pub fn block(&self) -> VkResult<()> { + pub fn block(&self) -> crate::Result<()> { self.inner.block() } } diff --git a/crates/renderer/src/device.rs b/crates/renderer/src/device.rs index 4babdc6..e7420e1 100644 --- a/crates/renderer/src/device.rs +++ b/crates/renderer/src/device.rs @@ -118,6 +118,18 @@ impl DeviceHandle for GpuAllocation { } } +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub enum AllocationStrategy { + #[default] + /// Let gpu_allocator manage the memory for this allocation, sub-allocating + /// from larger blocks as needed. + AllocatorManaged, + /// Allocate a dedicated block of memory for this allocation. This is + /// recommended for long-lived resources or resources with specific memory + /// requirements. + Dedicated, +} + #[derive(Debug)] pub(crate) enum Allocation { Owned(DeviceObject), @@ -144,7 +156,6 @@ impl Allocation { } pub struct DeviceInner { - pub(crate) alloc: vk_mem::Allocator, pub(crate) alloc2: Mutex, pub(crate) raw: ash::Device, pub(crate) adapter: PhysicalDeviceInfo, @@ -439,13 +450,6 @@ impl PhysicalDeviceInfo { let inner = DeviceInner { raw: device.clone(), - alloc: unsafe { - vk_mem::Allocator::new(vk_mem::AllocatorCreateInfo::new( - &instance.inner.raw, - &device, - self.pdev, - ))? - }, alloc2: Mutex::new(alloc2), instance: instance.clone(), adapter: self, @@ -531,9 +535,6 @@ impl DeviceInner { pub fn sync_threadpool(&self) -> &sync::SyncThreadpool { &self.sync_threadpool } - pub fn alloc(&self) -> &vk_mem::Allocator { - &self.alloc - } pub fn dev(&self) -> &ash::Device { &self.raw } diff --git a/crates/renderer/src/egui.rs b/crates/renderer/src/egui.rs index 7291b01..187b72d 100644 --- a/crates/renderer/src/egui.rs +++ b/crates/renderer/src/egui.rs @@ -1,13 +1,14 @@ use std::{collections::BTreeMap, sync::Arc}; -use ash::{prelude::VkResult, vk}; +use ash::vk; +use gpu_allocator::MemoryLocation; use indexmap::IndexMap; use crate::{ EguiState, buffers::{Buffer, BufferDesc}, commands::traits::CommandBufferExt, - device::{self, DeviceOwned}, + device::{self, AllocationStrategy}, images::{Image, ImageDesc, ImageViewDesc}, render_graph::{ Access, Barrier, GraphResourceDesc, GraphResourceId, PassDesc, RecordFn, RenderContext, @@ -23,7 +24,7 @@ pub fn egui_pre_pass( textures: &mut crate::texture::TextureManager, egui_state: &mut EguiState, output: &egui::FullOutput, -) -> VkResult<()> { +) -> crate::Result<()> { // allocate resource ids for textures in tessellated list (imported from texture manager) // define accesses for resource ids @@ -50,7 +51,8 @@ pub fn egui_pre_pass( depth: 1, }, usage: vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, - mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, + mem_location: MemoryLocation::GpuOnly, + alloc_scheme: AllocationStrategy::Dedicated, ..Default::default() }, )?; @@ -94,10 +96,7 @@ pub fn egui_pre_pass( size: staging_size as u64, usage: vk::BufferUsageFlags::TRANSFER_SRC, queue_families: device::QueueFlags::empty(), - mem_usage: vk_mem::MemoryUsage::AutoPreferHost, - alloc_flags: vk_mem::AllocationCreateFlags::MAPPED - | vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE - | vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT, + mem_location: MemoryLocation::CpuToGpu, ..Default::default() }, )?; @@ -115,14 +114,14 @@ pub fn egui_pre_pass( }, usage: vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::TRANSFER_DST, queue_families: device::QueueFlags::empty(), - mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, + mem_location: MemoryLocation::GpuOnly, ..Default::default() }, )?); let aliased_images = { tracing::trace!("mmap-ing staging buffer"); - let mut staging_map = staging_buffer.map()?; + let mut staging_map = staging_buffer.map().unwrap(); let mut offset = 0; output @@ -214,7 +213,7 @@ pub fn egui_pre_pass( let texture = textures.get(&id).and_then(|id| ctx.get_image(*id)).unwrap(); let image: Barrier = image_barrier( - alias.handle(), + alias.raw(), alias.format(), Access { stage: vk::PipelineStageFlags2::NONE, @@ -237,8 +236,8 @@ pub fn egui_pre_pass( } ctx.cmd.copy_buffer_to_image( - staging_buffer.handle(), - alias.handle(), + staging_buffer.raw(), + alias.raw(), vk::ImageLayout::TRANSFER_DST_OPTIMAL, &[vk::BufferImageCopy { buffer_offset: offset as u64, @@ -255,7 +254,7 @@ pub fn egui_pre_pass( ); let from_barrier = image_barrier( - alias.handle(), + alias.raw(), alias.format(), Access { stage: vk::PipelineStageFlags2::TRANSFER, @@ -271,7 +270,7 @@ pub fn egui_pre_pass( ); let to_barrier = image_barrier( - texture.handle(), + texture.raw(), texture.format(), Access { stage: vk::PipelineStageFlags2::NONE, @@ -304,9 +303,9 @@ pub fn egui_pre_pass( ); } ctx.cmd.copy_images( - alias.handle(), + alias.raw(), vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - texture.handle(), + texture.raw(), vk::ImageLayout::TRANSFER_DST_OPTIMAL, &[vk::ImageCopy { src_subresource: vk::ImageSubresourceLayers::default() @@ -330,7 +329,7 @@ pub fn egui_pre_pass( ); let image: Barrier = image_barrier( - texture.handle(), + texture.raw(), texture.format(), Access { stage: vk::PipelineStageFlags2::TRANSFER, @@ -397,7 +396,7 @@ pub fn egui_pass( egui: &egui::Context, output: egui::FullOutput, target: GraphResourceId, -) -> VkResult> { +) -> crate::Result> { let draw_data = egui.tessellate(output.shapes, output.pixels_per_point); #[repr(C)] @@ -461,16 +460,13 @@ pub fn egui_pass( name: Some("egui-draw-staging".into()), size: staging_size as u64, usage: vk::BufferUsageFlags::TRANSFER_SRC, - mem_usage: vk_mem::MemoryUsage::AutoPreferHost, - alloc_flags: vk_mem::AllocationCreateFlags::MAPPED - | vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE - | vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT, + mem_location: MemoryLocation::CpuToGpu, ..Default::default() }, )?; { - let mut map = staging.map()?; + let map = staging.map_mut().unwrap(); let (st_vertices, rest) = map.split_at_mut(vertices_size); let (st_indices, st_drawcalls) = rest.split_at_mut(indices_size); @@ -484,21 +480,21 @@ pub fn egui_pass( name: Some("egui-draw-vertices".into()), size: vertices_size as u64, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, - mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, + mem_location: MemoryLocation::GpuOnly, ..Default::default() })); let indices = rg.add_resource(GraphResourceDesc::Buffer(BufferDesc { name: Some("egui-draw-indices".into()), size: indices_size as u64, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER, - mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, + mem_location: MemoryLocation::GpuOnly, ..Default::default() })); let draw_calls = rg.add_resource(GraphResourceDesc::Buffer(BufferDesc { name: Some("egui-draw-draw_calls".into()), size: draw_calls_size as u64, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDIRECT_BUFFER, - mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, + mem_location: MemoryLocation::GpuOnly, ..Default::default() })); @@ -508,13 +504,14 @@ pub fn egui_pass( name: Some("egui-draw-texture_ids".into()), size: (textures_indices.len() * size_of::()) as u64, usage: vk::BufferUsageFlags::STORAGE_BUFFER, - mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, - alloc_flags: vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE, + mem_location: MemoryLocation::CpuToGpu, ..Default::default() }, )?; { - let mut map = texture_ids.map()?; + let map = texture_ids + .map_mut() + .expect("texture id buffer should be device visible"); map.copy_from_slice(bytemuck::cast_slice(&textures_indices)); } @@ -528,7 +525,7 @@ pub fn egui_pass( vk::DescriptorImageInfo { sampler: samplers.get_sampler(entry.as_sampler_desc()).unwrap(), image_view: texture - .get_view(ImageViewDesc { + .create_view(ImageViewDesc { kind: vk::ImageViewType::TYPE_2D, format: texture.format(), aspect: vk::ImageAspectFlags::COLOR, @@ -536,14 +533,15 @@ pub fn egui_pass( layer_range: (0..1).into(), ..Default::default() }) - .unwrap(), + .unwrap() + .raw(), image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, } }) .collect::>(); let uniform_info = vk::DescriptorBufferInfo { - buffer: texture_ids.buffer(), + buffer: texture_ids.raw(), offset: 0, range: texture_ids.len(), }; @@ -609,8 +607,8 @@ pub fn egui_pass( let target = ctx.get_image(target).unwrap(); cmd.copy_buffers( - staging.buffer(), - vertices.buffer(), + staging.raw(), + vertices.raw(), &[vk::BufferCopy { src_offset: 0, dst_offset: 0, @@ -618,8 +616,8 @@ pub fn egui_pass( }], ); cmd.copy_buffers( - staging.buffer(), - indices.buffer(), + staging.raw(), + indices.raw(), &[vk::BufferCopy { src_offset: vertices_size as u64, dst_offset: 0, @@ -627,8 +625,8 @@ pub fn egui_pass( }], ); cmd.copy_buffers( - staging.buffer(), - draw_calls.buffer(), + staging.raw(), + draw_calls.raw(), &[vk::BufferCopy { src_offset: (vertices_size + indices_size) as u64, dst_offset: 0, @@ -638,7 +636,7 @@ pub fn egui_pass( let barriers = [ buffer_barrier( - vertices.handle(), + vertices.raw(), 0, vertices.len(), Access::transfer_write(), @@ -646,7 +644,7 @@ pub fn egui_pass( None, ), buffer_barrier( - indices.handle(), + indices.raw(), 0, indices.len(), Access::transfer_write(), @@ -654,7 +652,7 @@ pub fn egui_pass( None, ), buffer_barrier( - draw_calls.handle(), + draw_calls.raw(), 0, draw_calls.len(), Access::transfer_write(), @@ -671,12 +669,16 @@ pub fn egui_pass( let color_attachment = &vk::RenderingAttachmentInfo::default() .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) - .image_view(target.get_view(ImageViewDesc { - kind: vk::ImageViewType::TYPE_2D, - format: target.format(), - aspect: vk::ImageAspectFlags::COLOR, - ..Default::default() - })?) + .image_view( + target + .create_view(ImageViewDesc { + kind: vk::ImageViewType::TYPE_2D, + format: target.format(), + aspect: vk::ImageAspectFlags::COLOR, + ..Default::default() + })? + .raw(), + ) .load_op(vk::AttachmentLoadOp::LOAD) .store_op(vk::AttachmentStoreOp::STORE); @@ -700,8 +702,8 @@ pub fn egui_pass( .height(target.height() as f32)]); cmd.bind_pipeline(&pipeline); - cmd.bind_indices(indices.buffer(), 0, vk::IndexType::UINT32); - cmd.bind_vertex_buffers(&[vertices.buffer()], &[0]); + cmd.bind_indices(indices.raw(), 0, vk::IndexType::UINT32); + cmd.bind_vertex_buffers(&[vertices.raw()], &[0]); cmd.push_constants( &pipeline_layout, vk::ShaderStageFlags::VERTEX, @@ -716,7 +718,7 @@ pub fn egui_pass( &[descriptor_set], ); cmd.draw_indexed_indirect( - draw_calls.buffer(), + draw_calls.raw(), 0, num_draw_calls as u32, size_of::() as u32, diff --git a/crates/renderer/src/images.rs b/crates/renderer/src/images.rs index 996e473..c8d655e 100644 --- a/crates/renderer/src/images.rs +++ b/crates/renderer/src/images.rs @@ -1,21 +1,15 @@ -use std::{ - borrow::Cow, - collections::HashMap, - sync::{Arc, Weak}, -}; +use std::{borrow::Cow, sync::Arc}; use crate::{ - define_device_owned_handle, - device::{Allocation, DeviceHandle, DeviceObject, DeviceOwned, QueueFlags}, + device::{Allocation, AllocationStrategy, DeviceHandle, DeviceObject, QueueFlags}, swapchain::Swapchain, util::weak_vec::WeakVec, }; use super::Device; -use ash::{prelude::*, vk}; +use ash::vk; use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme}; use parking_lot::Mutex; -use vk_mem::Alloc; #[derive(Clone)] pub struct ImageDesc { @@ -33,6 +27,7 @@ pub struct ImageDesc { pub layout: vk::ImageLayout, pub mem_location: gpu_allocator::MemoryLocation, + pub alloc_scheme: AllocationStrategy, } impl std::hash::Hash for ImageDesc { @@ -87,6 +82,7 @@ impl<'a> std::fmt::Debug for ImageDesc { .field("queue_families", &self.queue_families) .field("layout", &self.layout) .field("mem_location", &self.mem_location) + .field("alloc_scheme", &self.alloc_scheme) .finish() } } @@ -107,6 +103,7 @@ impl Default for ImageDesc { queue_families: QueueFlags::empty(), layout: vk::ImageLayout::UNDEFINED, mem_location: gpu_allocator::MemoryLocation::Unknown, + alloc_scheme: AllocationStrategy::AllocatorManaged, } } } @@ -168,7 +165,10 @@ impl Image { requirements, location: desc.mem_location, linear: desc.tiling == vk::ImageTiling::LINEAR, - allocation_scheme: AllocationScheme::GpuAllocatorManaged, + allocation_scheme: match desc.alloc_scheme { + AllocationStrategy::AllocatorManaged => AllocationScheme::GpuAllocatorManaged, + AllocationStrategy::Dedicated => AllocationScheme::DedicatedImage(image), + }, })?; Ok(Self { @@ -316,6 +316,10 @@ impl Image { Ok((image, requirements)) } + + pub fn raw(&self) -> vk::Image { + self.image.image() + } } impl Eq for Image {} @@ -359,35 +363,35 @@ impl Image { self.image.allocation_mut() } - /// technically, this ImageView belongs to the image and is managed by it. - pub fn get_view(&self, desc: ImageViewDesc) -> VkResult { - use std::collections::hash_map::Entry::*; - match self.views.lock().entry(desc.hash_eq_copy()) { - Occupied(occupied) => Ok(*occupied.get()), - Vacant(vacant) => { - let view = unsafe { - let create_info = vk::ImageViewCreateInfo::default() - .flags(desc.flags) - .image(self.image()) - .view_type(vk::ImageViewType::TYPE_2D) - .format(desc.format) - .components(desc.components) - .subresource_range( - vk::ImageSubresourceRange::default() - .aspect_mask(desc.aspect) - .base_mip_level(desc.mip_range.0) - .level_count(desc.mip_range.count()) - .base_array_layer(desc.layer_range.0) - .layer_count(desc.layer_range.count()), - ); - self.device().dev().create_image_view(&create_info, None)? - }; - Ok(*vacant.insert(view)) - } - } - } + // /// technically, this ImageView belongs to the image and is managed by it. + // pub fn get_view(&self, desc: ImageViewDesc) -> VkResult { + // use std::collections::hash_map::Entry::*; + // match self.views.lock().entry(desc.hash_eq_copy()) { + // Occupied(occupied) => Ok(*occupied.get()), + // Vacant(vacant) => { + // let view = unsafe { + // let create_info = vk::ImageViewCreateInfo::default() + // .flags(desc.flags) + // .image(self.image()) + // .view_type(vk::ImageViewType::TYPE_2D) + // .format(desc.format) + // .components(desc.components) + // .subresource_range( + // vk::ImageSubresourceRange::default() + // .aspect_mask(desc.aspect) + // .base_mip_level(desc.mip_range.0) + // .level_count(desc.mip_range.count()) + // .base_array_layer(desc.layer_range.0) + // .layer_count(desc.layer_range.count()), + // ); + // self.device().dev().create_image_view(&create_info, None)? + // }; + // Ok(*vacant.insert(view)) + // } + // } + // } - pub fn create_view(&self, desc: ImageViewDesc) -> crate::Result { + pub fn create_view(self: &Arc, mut desc: ImageViewDesc) -> crate::Result { // validate if !view_kind_compatible(self.desc.kind, desc.kind) { tracing::error!( @@ -410,6 +414,10 @@ impl Image { return Err(crate::Error::Unspecified); } + if desc.format == vk::Format::UNDEFINED { + desc.format = self.desc.format; + } + let create_info = vk::ImageViewCreateInfo::default() .flags(desc.flags) .image(self.image()) @@ -428,10 +436,16 @@ impl Image { let device = self.image.device(); let view = unsafe { device.raw.create_image_view(&create_info, None)? }; - ImageView::construct(self.device().clone(), view, desc.name) + Ok(ImageView { + view: DeviceObject::new(view, device.clone(), desc.name.clone()), + desc, + image: self.clone(), + }) } } +fn validate_image_view_format(image: &ImageDesc, view_format: vk::Format) -> bool {} + fn view_kind_compatible(image_kind: vk::ImageType, view_kind: vk::ImageViewType) -> bool { use vk::ImageType as IT; use vk::ImageViewType as VT; @@ -456,7 +470,19 @@ pub struct ImageViewDesc { } impl ImageViewDesc { - pub fn hash_eq_copy(&self) -> Self { + pub fn color_2d() -> Self { + Self { + kind: vk::ImageViewType::TYPE_2D, + aspect: vk::ImageAspectFlags::COLOR, + ..Default::default() + } + } + + pub fn with_format(self, format: vk::Format) -> Self { + Self { format, ..self } + } + + pub(crate) fn hash_eq_copy(&self) -> Self { let &Self { flags, kind, @@ -561,6 +587,15 @@ pub struct ImageView { image: Arc, } +impl ImageView { + pub fn raw(&self) -> vk::ImageView { + *self.view + } + pub fn image(&self) -> &Arc { + &self.image + } +} + impl DeviceHandle for vk::ImageView { unsafe fn destroy(&mut self, device: &Device) { unsafe { diff --git a/crates/renderer/src/instance.rs b/crates/renderer/src/instance.rs index 6bcc07d..62edc7b 100644 --- a/crates/renderer/src/instance.rs +++ b/crates/renderer/src/instance.rs @@ -1,5 +1,4 @@ use std::{ - cell::OnceCell, cmp::Ordering, ffi::{CStr, CString}, ops::Deref, diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index e71ff63..53018d1 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -1021,10 +1021,13 @@ impl Renderer2 { surface: &swapchain::Surface, cb: F, ) -> Result { - let frame = surface.acquire_image().await?; + let Some(frame) = surface.acquire_image() else { + return Err(Error::SuboptimalSwapchain); + }; + let (frame, _suboptimal) = frame.await?; let mut rg = render_graph::RenderGraph::new(); - let _framebuffer = rg.import_framebuffer(frame.image.clone()); + let _framebuffer = rg.import_framebuffer(frame.image().clone()); let out = cb(self, &mut rg); diff --git a/crates/renderer/src/queue.rs b/crates/renderer/src/queue.rs index e9561d6..291f3c5 100644 --- a/crates/renderer/src/queue.rs +++ b/crates/renderer/src/queue.rs @@ -47,6 +47,15 @@ pub struct QueueInner { pub(crate) lock: Mutex<()>, } +impl QueueInner { + pub fn raw(&self) -> vk::Queue { + self.raw + } + pub fn family_index(&self) -> u32 { + self.family.index + } +} + #[derive(Debug, Clone, Copy)] pub struct QueueFamily { pub index: u32, diff --git a/crates/renderer/src/render_graph.rs b/crates/renderer/src/render_graph.rs index 1d2611c..fa6f82f 100644 --- a/crates/renderer/src/render_graph.rs +++ b/crates/renderer/src/render_graph.rs @@ -62,13 +62,11 @@ impl GraphResource { discr.hash(&mut state); match self { - GraphResource::Framebuffer(swapchain_frame) => { - (swapchain_frame.handle()).hash(&mut state) - } - GraphResource::ImportedImage(image) => image.handle().hash(&mut state), - GraphResource::ImportedBuffer(buffer) => buffer.handle().hash(&mut state), - GraphResource::Image(image) => image.handle().hash(&mut state), - GraphResource::Buffer(buffer) => buffer.handle().hash(&mut state), + GraphResource::Framebuffer(swapchain_frame) => (swapchain_frame.raw()).hash(&mut state), + GraphResource::ImportedImage(image) => image.raw().hash(&mut state), + GraphResource::ImportedBuffer(buffer) => buffer.raw().hash(&mut state), + GraphResource::Image(image) => image.raw().hash(&mut state), + GraphResource::Buffer(buffer) => buffer.raw().hash(&mut state), GraphResource::ImageDesc(image_desc) => image_desc.hash(&mut state), GraphResource::BufferDesc(buffer_desc) => buffer_desc.hash(&mut state), GraphResource::Default => {} @@ -486,7 +484,7 @@ impl RenderGraph { _ => {} } } - ash::prelude::VkResult::Ok(()) + crate::Result::Ok(()) })?; let pool = @@ -561,19 +559,19 @@ impl RenderGraph { ) { let barrier: Barrier = match res { GraphResource::Framebuffer(arc) => { - image_barrier(arc.handle(), arc.format(), from, to, None).into() + image_barrier(arc.raw(), arc.format(), from, to, None).into() } GraphResource::ImportedImage(arc) => { - image_barrier(arc.handle(), arc.format(), from, to, None).into() + image_barrier(arc.raw(), arc.format(), from, to, None).into() } GraphResource::ImportedBuffer(arc) => { - buffer_barrier(arc.handle(), 0, arc.len(), from, to, None).into() + buffer_barrier(arc.raw(), 0, arc.len(), from, to, None).into() } GraphResource::Image(image) => { - image_barrier(image.handle(), image.format(), from, to, None).into() + image_barrier(image.raw(), image.format(), from, to, None).into() } GraphResource::Buffer(buffer) => { - buffer_barrier(buffer.handle(), 0, buffer.len(), from, to, None).into() + buffer_barrier(buffer.raw(), 0, buffer.len(), from, to, None).into() } _ => { unreachable!() @@ -1297,7 +1295,7 @@ pub fn clear_pass(rg: &mut RenderGraph, color: Rgba, target: GraphResourceId) { let cmd = &ctx.cmd; cmd.clear_color_image( - target.handle(), + target.raw(), target.format(), vk::ImageLayout::TRANSFER_DST_OPTIMAL, color, diff --git a/crates/renderer/src/rendering/mod.rs b/crates/renderer/src/rendering/mod.rs index 2c741d3..2c32b81 100644 --- a/crates/renderer/src/rendering/mod.rs +++ b/crates/renderer/src/rendering/mod.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use ash::vk; use glam::{f32::Mat4, vec3}; +use gpu_allocator::MemoryLocation; pub use crate::egui_pass::{egui_pass, egui_pre_pass}; @@ -9,7 +10,7 @@ use crate::{ Result, buffers::{Buffer, BufferDesc}, commands::{self, traits::CommandBufferExt}, - device::{Device, DeviceOwned}, + device::Device, images::ImageViewDesc, pipeline, render_graph::{ @@ -50,16 +51,15 @@ impl Wireframe { name: Some("wireframe-staging".into()), size: staging_size as u64, usage: vk::BufferUsageFlags::TRANSFER_SRC, - mem_usage: vk_mem::MemoryUsage::AutoPreferHost, - alloc_flags: vk_mem::AllocationCreateFlags::MAPPED - | vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE - | vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT, + mem_location: MemoryLocation::CpuToGpu, ..Default::default() }, )?; { - let mut map = staging.map()?; + let map = staging + .map_mut() + .expect("staging buffer should be host visible"); map[..positions_size].copy_from_slice(bytemuck::cast_slice(&positions)); map[indices_offset..][..indices_size].copy_from_slice(bytemuck::cast_slice(&indices)); @@ -72,7 +72,7 @@ impl Wireframe { name: Some("wireframe-positions".into()), size: positions_size as u64, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, - mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, + mem_location: MemoryLocation::GpuOnly, ..Default::default() }, )?; @@ -82,7 +82,7 @@ impl Wireframe { name: Some("wireframe-indices".into()), size: indices_size as u64, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER, - mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, + mem_location: MemoryLocation::GpuOnly, ..Default::default() }, )?; @@ -92,7 +92,7 @@ impl Wireframe { name: Some("wireframe-colors".into()), size: colors_size as u64, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, - mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, + mem_location: MemoryLocation::GpuOnly, ..Default::default() }, )?; @@ -102,8 +102,8 @@ impl Wireframe { let cmd = pool.alloc()?; cmd.copy_buffers( - staging.handle(), - positions.handle(), + staging.raw(), + positions.raw(), &[vk::BufferCopy { src_offset: 0, dst_offset: 0, @@ -111,8 +111,8 @@ impl Wireframe { }], ); cmd.copy_buffers( - staging.handle(), - indices.handle(), + staging.raw(), + indices.raw(), &[vk::BufferCopy { src_offset: indices_offset as u64, dst_offset: 0, @@ -120,8 +120,8 @@ impl Wireframe { }], ); cmd.copy_buffers( - staging.handle(), - colors.handle(), + staging.raw(), + colors.raw(), &[vk::BufferCopy { src_offset: colors_offset as u64, dst_offset: 0, @@ -131,7 +131,7 @@ impl Wireframe { let barriers = [ buffer_barrier( - positions.handle(), + positions.raw(), 0, positions.len(), Access::transfer_write(), @@ -139,7 +139,7 @@ impl Wireframe { None, ), buffer_barrier( - indices.handle(), + indices.raw(), 0, indices.len(), Access::transfer_write(), @@ -147,7 +147,7 @@ impl Wireframe { None, ), buffer_barrier( - colors.handle(), + colors.raw(), 0, colors.len(), Access::transfer_write(), @@ -363,8 +363,8 @@ impl Wireframe { .height(target.height() as f32)]); cmd.bind_pipeline(&pipeline); - cmd.bind_indices(indices.buffer(), 0, vk::IndexType::UINT32); - cmd.bind_vertex_buffers(&[positions.handle(), colors.handle()], &[0, 0]); + cmd.bind_indices(indices.raw(), 0, vk::IndexType::UINT32); + cmd.bind_vertex_buffers(&[positions.raw(), colors.raw()], &[0, 0]); cmd.push_constants( &layout, vk::ShaderStageFlags::VERTEX, diff --git a/crates/renderer/src/swapchain.rs b/crates/renderer/src/swapchain.rs index 2ca8487..f1030c9 100644 --- a/crates/renderer/src/swapchain.rs +++ b/crates/renderer/src/swapchain.rs @@ -1,6 +1,6 @@ use std::{ collections::HashMap, - marker::PhantomData, + ops::Deref, sync::{ Arc, atomic::{AtomicU32, AtomicU64, Ordering}, @@ -12,16 +12,14 @@ use ash::{ prelude::VkResult, vk::{self, Handle}, }; -use parking_lot::{Mutex, RawMutex, RwLock}; +use parking_lot::{Mutex, RwLock}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use crate::{ - Instance, PhysicalDeviceInfo, Result, SurfaceCapabilities, define_device_owned_handle, - device::{Device, DeviceObject, DeviceOwned}, - images, - instance::InstanceInner, - sync::{self, Fence}, - util::RawMutexGuard, + Instance, PhysicalDeviceInfo, Result, SurfaceCapabilities, + device::{Device, DeviceObject}, + images::{self, ImageViewDesc}, + sync::Fence, }; use derive_more::Debug; @@ -274,6 +272,18 @@ impl Surface { composite_alpha_mode, }) } + + pub fn acquire_image( + &self, + ) -> Option>> { + // ensure lock does not block for the entire duration of the async image acquisition. + let swapchain = self.swapchain.read().as_ref().cloned(); + if let Some(swapchain) = swapchain { + Some(swapchain.acquire_image()) + } else { + None + } + } } #[derive(Debug)] @@ -302,7 +312,7 @@ pub struct Swapchain { // Some of the swapchain operations require external synchronisation; this mutex allows `Swapchain` to be `Sync`. #[debug(skip)] - guard: parking_lot::RawMutex, + pub(crate) guard: parking_lot::Mutex<()>, // for khr_present_id/khr_present_wait #[allow(unused)] @@ -365,7 +375,7 @@ impl Swapchain { .expect("swapchain extension not loaded"); let (swapchain, images) = { - let _lock = old_swapchain.as_ref().map(|old| old.lock()); + let _lock = old_swapchain.as_ref().map(|old| old.guard.lock()); let old_swapchain = old_swapchain .map(|swp| *swp.swapchain) .unwrap_or(vk::SwapchainKHR::null()); @@ -467,7 +477,7 @@ impl Swapchain { ), images, config, - guard: ::INIT, + guard: Mutex::new(()), min_image_count: surface_caps.capabilities.min_image_count, acquire_semaphores, release_semaphores, @@ -485,11 +495,15 @@ impl Swapchain { self.images.len() as u32 } + pub fn raw(&self) -> vk::SwapchainKHR { + *self.swapchain + } + /// returns a future yielding the frame, and true if the swapchain is /// 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| { @@ -507,28 +521,34 @@ impl Swapchain { // spawn on threadpool because it might block. let (idx, suboptimal) = smol::unblock({ let this = self.clone(); + let fence = fence.raw(); move || unsafe { this.with_locked(|swapchain| { this.functor - .acquire_next_image(swapchain, u64::MAX, acquire, fence.raw()) + .acquire_next_image(swapchain.raw(), u64::MAX, acquire, fence) }) } }) .await?; + let idx = idx as usize; + let image = self.images[idx].clone(); + let image = Arc::new(images::Image::from_swapchain_image(image, &self)); + let view = image.create_view(ImageViewDesc { + name: Some(format!("swapchain-{:x}-image-view-{idx}", self.raw().as_raw()).into()), + kind: vk::ImageViewType::TYPE_2D, + format: self.config.format, + aspect: vk::ImageAspectFlags::COLOR, + ..Default::default() + })?; + // wait for image to become available. fence.into_future().await; - let idx = idx as usize; - let image = self.images[idx].clone(); - let view = self.image_views[idx]; - Ok(( - SwapchainFrame { + SwapchainImage { index: idx as u32, - swapchain: self.clone(), - format: self.config.format, - image, + swapchain: self, view, acquire, release, @@ -538,31 +558,36 @@ impl Swapchain { } } - fn present(&self, frame: SwapchainFrame, wait: Option) -> Result<()> { - let swpchain = self.lock(); - let queue = self.device().present_queue().lock(); + /// # Safety + /// The caller must ensure that the provided index corresponds to an image + /// that is currently acquired and not yet presented. + unsafe fn present(&self, index: u32, wait: Option) -> Result<()> { + let _lock = self.guard.lock(); let wait_semaphores = wait.as_slice(); - - // TODO: make this optional for devices with no support for present_wait/present_id - // let present_id = self - // .present_id - // .fetch_add(1, std::sync::atomic::Ordering::Relaxed); - // let mut present_id = - // vk::PresentIdKHR::default().present_ids(core::slice::from_ref(&present_id)); - let present_info = vk::PresentInfoKHR::default() - .image_indices(core::slice::from_ref(&frame.index)) - .swapchains(core::slice::from_ref(&swpchain)) + .image_indices(core::slice::from_ref(&index)) + .swapchains(core::slice::from_ref(&*self.swapchain)) .wait_semaphores(wait_semaphores); - //.push_next(&mut present_id) - // call winits pre_present_notify here + let queue = self.swapchain.device().queues.graphics(); + queue.with_locked(|queue| -> crate::Result<()> { + // TODO: make this optional for devices with no support for present_wait/present_id + // let present_id = self + // .present_id + // .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + // let mut present_id = + // vk::PresentIdKHR::default().present_ids(core::slice::from_ref(&present_id)); - unsafe { - self.functor.queue_present(*queue, &present_info)?; - } - Ok(()) + //.push_next(&mut present_id) + + // call winits pre_present_notify here + + unsafe { + self.functor.queue_present(queue.raw(), &present_info)?; + } + Ok(()) + }) } #[allow(clippy::too_many_arguments)] @@ -615,6 +640,41 @@ impl Swapchain { } static SWAPCHAIN_COUNT: AtomicU64 = AtomicU64::new(0); +#[derive(Debug)] +#[must_use = "This struct represents an acquired image from the Swapchain and + must be presented in order to free resources on the device."] +pub struct SwapchainImage { + view: images::ImageView, + // The swapchain must be kept alive while the image is in use, because the + // image is owned by the swapchain and will be freed when the swapchain is + // dropped. Additionally, we need access to the swapchain in order to + // present the image. + swapchain: Arc, + index: u32, + pub acquire: vk::Semaphore, + pub release: vk::Semaphore, +} + +impl Deref for SwapchainImage { + type Target = images::ImageView; + + fn deref(&self) -> &Self::Target { + &self.view + } +} + +impl SwapchainImage { + pub fn index(&self) -> u32 { + self.index + } + + pub fn present(self, wait: Option) -> crate::Result<()> { + // SAFETY: we know the index is valid because we've aquired the image, + // and we know it isn't presented yet because we still own the image. + unsafe { self.swapchain.present(self.index, wait) } + } +} + #[derive(Debug)] #[must_use = "This struct represents an acquired image from the swapchain and must be presented in order to free resources on the device."] @@ -637,7 +697,8 @@ impl PartialEq for SwapchainFrame { impl SwapchainFrame { pub fn present(self, wait: Option) -> crate::Result<()> { - self.swapchain.clone().present(self, wait) + // SAFETY: we know the index is valid because we've aquired the image, and we know it isn't presented yet because we still own the image. + unsafe { self.swapchain.present(self.index, wait) } } } @@ -678,19 +739,9 @@ pub struct SwapchainConfiguration { } impl Swapchain { - pub fn lock(&self) -> RawMutexGuard<'_, vk::SwapchainKHR> { - use parking_lot::lock_api::RawMutex; - self.guard.lock(); - RawMutexGuard { - mutex: &self.guard, - value: &*self.swapchain, - _pd: PhantomData, - } - } - - pub fn with_locked T>(&self, f: F) -> T { - let lock = self.lock(); - f(*lock) + pub fn with_locked T>(&self, f: F) -> T { + let _lock = self.guard.lock(); + f(self) } } diff --git a/crates/renderer/src/sync.rs b/crates/renderer/src/sync.rs index f996c02..47fb841 100644 --- a/crates/renderer/src/sync.rs +++ b/crates/renderer/src/sync.rs @@ -3,16 +3,15 @@ use std::borrow::Cow; use std::{ future::Future, marker::PhantomData, - mem::ManuallyDrop, sync::{Arc, atomic::AtomicU32}, time::Duration, }; -use crate::device::{DeviceObject, DeviceOwned, Pool, PoolObject, Pooled}; +use crate::device::{DeviceObject, Pool, PoolObject, Pooled}; use crate::{Result, device::DeviceInner}; use super::Device; -use ash::{prelude::*, vk}; +use ash::{prelude::VkResult, vk}; use crossbeam::channel::{Receiver, Sender}; type Message = (SyncPrimitive, std::task::Waker); @@ -212,7 +211,7 @@ impl Fence { } } - pub fn wait_on(&self, timeout: Option) -> Result<()> { + pub fn wait_on(&self, timeout: Option) -> VkResult<()> { unsafe { self.device().raw.wait_for_fences( core::slice::from_ref(&self.raw()),