diff --git a/Cargo.toml b/Cargo.toml index dcaa195..4bb2200 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,12 +22,14 @@ tracing-subscriber = {version ="0.3.18", features = ["env-filter"]} glam = {version = "0.29.0", features = ["bytemuck"]} rand = "0.8.5" bitflags = "2.6" +thread_local = "1.1.8" ash = "0.38.0" ash-window = "0.13.0" vk-mem = "0.4.0" vk-sync = "0.1.6" +arrayvec = "0.7.6" tinyvec = "1.8" indexmap = "2" petgraph = "0.7" diff --git a/crates/renderer/Cargo.toml b/crates/renderer/Cargo.toml index bac312b..dc53ac5 100644 --- a/crates/renderer/Cargo.toml +++ b/crates/renderer/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] tinyvec = { workspace = true } +arrayvec = { workspace = true } indexmap = { workspace = true } petgraph = { workspace = true } itertools = { workspace = true } @@ -13,6 +14,7 @@ rand = { workspace = true } parking_lot = { workspace = true } glam = { workspace = true } bitflags = { workspace = true } +thread_local = {workspace = true} thiserror = { workspace = true } anyhow = { workspace = true } diff --git a/crates/renderer/src/commands.rs b/crates/renderer/src/commands.rs index 6aae7b1..9549162 100644 --- a/crates/renderer/src/commands.rs +++ b/crates/renderer/src/commands.rs @@ -70,19 +70,19 @@ impl SingleUseCommandPool { } } -pub trait CommandBuffer: DeviceOwned { +pub trait HasQueue: DeviceOwned { fn queue(&self) -> &Queue; } -impl CommandBuffer for SingleUseCommand { +impl HasQueue for SingleUseCommand { fn queue(&self) -> &Queue { &self.pool.queue } } -pub struct CommandList(pub Vec); +pub struct CommandList(pub Vec); -impl CommandList { +impl CommandList { /// all commands in list must be allocated from the same queue. pub fn submit<'a>( &'a self, @@ -215,6 +215,11 @@ impl SingleUseCommand { self.state.state() } + /// Safety: commandbuffer must not be accessed from multiple threads at the same time + pub unsafe fn buffer(&self) -> vk::CommandBuffer { + self.handle() + } + pub fn end(&self) -> VkResult<()> { assert_eq!(self.state(), CommandBufferState::Recording); unsafe { @@ -224,305 +229,6 @@ impl SingleUseCommand { Ok(()) } - /// Safety: commandbuffer must not be accessed from multiple threads at the same time - pub unsafe fn buffer(&self) -> vk::CommandBuffer { - self.handle() - } - - //pub fn copy_buffer_to_image(&self, image: &Image2D, buffer: &Buffer, ) - - pub fn image_barrier( - &self, - image: vk::Image, - aspects: vk::ImageAspectFlags, - src_stage: vk::PipelineStageFlags2, - src_access: vk::AccessFlags2, - dst_stage: vk::PipelineStageFlags2, - dst_access: vk::AccessFlags2, - old_layout: vk::ImageLayout, - new_layout: vk::ImageLayout, - queue_ownership_op: Option, - ) { - assert_eq!(self.state(), CommandBufferState::Recording); - let (src_family, dst_family) = queue_ownership_op - .map(|t| (t.src, t.dst)) - .unwrap_or((vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED)); - - let barrier = vk::ImageMemoryBarrier2::default() - .image(image) - .subresource_range( - vk::ImageSubresourceRange::default() - .aspect_mask(aspects) - .base_mip_level(0) - .base_array_layer(0) - .level_count(vk::REMAINING_MIP_LEVELS) - .layer_count(vk::REMAINING_ARRAY_LAYERS), - ) - .src_stage_mask(src_stage) - .src_access_mask(src_access) - .dst_stage_mask(dst_stage) - .dst_access_mask(dst_access) - .dst_queue_family_index(dst_family) - .src_queue_family_index(src_family) - .old_layout(old_layout) - .new_layout(new_layout); - - unsafe { - self.device().dev().cmd_pipeline_barrier2( - self.handle(), - &vk::DependencyInfo::default() - .dependency_flags(vk::DependencyFlags::BY_REGION) - .image_memory_barriers(core::slice::from_ref(&barrier)), - ); - } - } - - pub fn blit_images( - &self, - src: &Image, - src_region: util::Rect2D, - dst: &Image, - dst_region: util::Rect2D, - ) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device().dev().cmd_blit_image( - self.buffer(), - src.image(), - vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - dst.image(), - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &[vk::ImageBlit::default() - .src_subresource( - vk::ImageSubresourceLayers::default() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .layer_count(1), - ) - .dst_subresource( - vk::ImageSubresourceLayers::default() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .layer_count(1), - ) - .src_offsets(src_region.into_offsets_3d()) - .dst_offsets(dst_region.into_offsets_3d())], - vk::Filter::LINEAR, - ); - } - } - - pub fn copy_buffer_to_image( - &self, - buffer: vk::Buffer, - image: vk::Image, - layout: vk::ImageLayout, - regions: &[vk::BufferImageCopy], - ) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device().dev().cmd_copy_buffer_to_image( - self.handle(), - buffer, - image, - layout, - regions, - ); - } - } - - pub fn copy_buffers(&self, src: vk::Buffer, dst: vk::Buffer, regions: &[vk::BufferCopy]) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device() - .dev() - .cmd_copy_buffer(self.handle(), src, dst, regions); - } - } - #[allow(dead_code)] - pub fn copy_images( - &self, - src: vk::Image, - src_layout: vk::ImageLayout, - dst: vk::Image, - dst_layout: vk::ImageLayout, - regions: &[vk::ImageCopy], - ) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device().dev().cmd_copy_image( - self.handle(), - src, - src_layout, - dst, - dst_layout, - regions, - ); - } - } - - pub fn clear_color_image( - &self, - image: vk::Image, - format: vk::Format, - layout: vk::ImageLayout, - color: crate::Rgba, - subresources: &[vk::ImageSubresourceRange], - ) { - assert_eq!(self.state(), CommandBufferState::Recording); - let clear_colors = match format.get_component_kind() { - crate::util::FormatComponentKind::Float => vk::ClearColorValue { - float32: color.into_f32(), - }, - crate::util::FormatComponentKind::UInt => vk::ClearColorValue { - uint32: color.into_u32(), - }, - crate::util::FormatComponentKind::SInt => vk::ClearColorValue { - int32: color.into_i32(), - }, - }; - - unsafe { - self.device().dev().cmd_clear_color_image( - self.handle(), - image, - layout, - &clear_colors, - subresources, - ); - } - } - - pub fn begin_rendering(&self, rendering_info: vk::RenderingInfo<'_>) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device() - .dev() - .cmd_begin_rendering(self.buffer(), &rendering_info); - } - } - - pub fn set_viewport(&self, viewports: &[vk::Viewport]) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device() - .dev() - .cmd_set_viewport(self.buffer(), 0, viewports); - } - } - pub fn set_scissors(&self, scissors: &[vk::Rect2D]) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device() - .dev() - .cmd_set_scissor(self.buffer(), 0, scissors); - } - } - pub fn push_constants( - &self, - layout: &PipelineLayout, - stage: vk::ShaderStageFlags, - offset: u32, - bytes: &[u8], - ) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device().dev().cmd_push_constants( - self.handle(), - layout.handle(), - stage, - offset, - bytes, - ); - } - } - pub fn bind_pipeline(&self, pipeline: &Pipeline) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device().dev().cmd_bind_pipeline( - self.buffer(), - pipeline.bind_point(), - pipeline.handle(), - ); - } - } - pub fn bind_vertex_buffers(&self, buffers: &[vk::Buffer], offsets: &[u64]) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device() - .dev() - .cmd_bind_vertex_buffers(self.buffer(), 0, buffers, offsets); - } - } - pub fn bind_indices(&self, buffer: vk::Buffer, offset: u64, kind: vk::IndexType) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device() - .dev() - .cmd_bind_index_buffer(self.buffer(), buffer, offset, kind); - } - } - - pub fn bind_descriptor_sets( - &self, - layout: &PipelineLayout, - bind_point: vk::PipelineBindPoint, - descriptor_sets: &[vk::DescriptorSet], - ) { - assert_eq!(self.state(), CommandBufferState::Recording); - use crate::device::DeviceOwned; - unsafe { - self.device().dev().cmd_bind_descriptor_sets( - self.buffer(), - bind_point, - layout.handle(), - 0, - descriptor_sets, - &[], - ); - } - } - - #[allow(dead_code)] - pub fn draw_indexed( - &self, - indices: u32, - instances: u32, - index_offset: u32, - vertex_offset: i32, - instance_offset: u32, - ) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device().dev().cmd_draw_indexed( - self.buffer(), - indices, - instances, - index_offset, - vertex_offset, - instance_offset, - ); - } - } - - pub fn draw_indexed_indirect(&self, buffer: vk::Buffer, offset: u64, count: u32, stride: u32) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device().dev().cmd_draw_indexed_indirect( - self.buffer(), - buffer, - offset, - count, - stride, - ); - } - } - - pub fn end_rendering(&self) { - assert_eq!(self.state(), CommandBufferState::Recording); - unsafe { - self.device().dev().cmd_end_rendering(self.buffer()); - } - } - pub fn submit_fence( &self, wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, @@ -553,6 +259,9 @@ impl SingleUseCommand { .dev() .queue_submit(queue, &[submit_info], fence) })?; + + self.state.set_pending(); + tracing::trace!( "submitted queue {:?} and fence {:?}", self.pool.queue(), @@ -585,3 +294,662 @@ impl SingleUseCommand { Ok(()) } } + +impl traits::CommandBufferExt for SingleUseCommand {} + +pub mod traits { + use super::*; + + pub trait CommandBufferExt: DeviceOwned { + fn submit_to_queue( + self, + queue: &Queue, + wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, + signal: Option, + fence: Arc, + ) -> VkResult> + where + Self: Sized, + { + //assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { self.device().dev().end_command_buffer(self.handle())? }; + + let buffers = [self.handle()]; + let mut submit_info = vk::SubmitInfo::default().command_buffers(&buffers); + + if let Some(semaphore) = signal.as_ref() { + submit_info = submit_info.signal_semaphores(core::slice::from_ref(semaphore)); + } + if let Some((semaphore, stage)) = wait.as_ref() { + submit_info = submit_info + .wait_semaphores(core::slice::from_ref(semaphore)) + .wait_dst_stage_mask(core::slice::from_ref(stage)); + } + + queue.with_locked(|queue| unsafe { + self.device() + .dev() + .queue_submit(queue, &[submit_info], fence.fence()) + })?; + + tracing::trace!("submitted queue {:?} and fence {:?}", queue, fence); + + Ok(crate::sync::FenceFuture::new(fence)) + } + + fn image_barrier( + &self, + image: vk::Image, + aspects: vk::ImageAspectFlags, + src_stage: vk::PipelineStageFlags2, + src_access: vk::AccessFlags2, + dst_stage: vk::PipelineStageFlags2, + dst_access: vk::AccessFlags2, + old_layout: vk::ImageLayout, + new_layout: vk::ImageLayout, + queue_ownership_op: Option, + ) { + // assert_eq!(self.state(), CommandBufferState::Recording); + + let (src_family, dst_family) = queue_ownership_op + .map(|t| (t.src, t.dst)) + .unwrap_or((vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED)); + + let barrier = vk::ImageMemoryBarrier2::default() + .image(image) + .subresource_range( + vk::ImageSubresourceRange::default() + .aspect_mask(aspects) + .base_mip_level(0) + .base_array_layer(0) + .level_count(vk::REMAINING_MIP_LEVELS) + .layer_count(vk::REMAINING_ARRAY_LAYERS), + ) + .src_stage_mask(src_stage) + .src_access_mask(src_access) + .dst_stage_mask(dst_stage) + .dst_access_mask(dst_access) + .dst_queue_family_index(dst_family) + .src_queue_family_index(src_family) + .old_layout(old_layout) + .new_layout(new_layout); + + unsafe { + self.device().dev().cmd_pipeline_barrier2( + self.handle(), + &vk::DependencyInfo::default() + .dependency_flags(vk::DependencyFlags::BY_REGION) + .image_memory_barriers(core::slice::from_ref(&barrier)), + ); + } + } + + fn blit_images( + &self, + src: &Image, + src_region: util::Rect2D, + dst: &Image, + dst_region: util::Rect2D, + ) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device().dev().cmd_blit_image( + self.handle(), + src.image(), + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + dst.image(), + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &[vk::ImageBlit::default() + .src_subresource( + vk::ImageSubresourceLayers::default() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .layer_count(1), + ) + .dst_subresource( + vk::ImageSubresourceLayers::default() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .layer_count(1), + ) + .src_offsets(src_region.into_offsets_3d()) + .dst_offsets(dst_region.into_offsets_3d())], + vk::Filter::LINEAR, + ); + } + } + + fn copy_buffer_to_image( + &self, + buffer: vk::Buffer, + image: vk::Image, + layout: vk::ImageLayout, + regions: &[vk::BufferImageCopy], + ) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device().dev().cmd_copy_buffer_to_image( + self.handle(), + buffer, + image, + layout, + regions, + ); + } + } + + fn copy_buffers(&self, src: vk::Buffer, dst: vk::Buffer, regions: &[vk::BufferCopy]) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device() + .dev() + .cmd_copy_buffer(self.handle(), src, dst, regions); + } + } + + fn copy_images( + &self, + src: vk::Image, + src_layout: vk::ImageLayout, + dst: vk::Image, + dst_layout: vk::ImageLayout, + regions: &[vk::ImageCopy], + ) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device().dev().cmd_copy_image( + self.handle(), + src, + src_layout, + dst, + dst_layout, + regions, + ); + } + } + + fn clear_color_image( + &self, + image: vk::Image, + format: vk::Format, + layout: vk::ImageLayout, + color: crate::Rgba, + subresources: &[vk::ImageSubresourceRange], + ) { + // assert_eq!(self.state(), CommandBufferState::Recording); + let clear_colors = match format.get_component_kind() { + crate::util::FormatComponentKind::Float => vk::ClearColorValue { + float32: color.into_f32(), + }, + crate::util::FormatComponentKind::UInt => vk::ClearColorValue { + uint32: color.into_u32(), + }, + crate::util::FormatComponentKind::SInt => vk::ClearColorValue { + int32: color.into_i32(), + }, + }; + + unsafe { + self.device().dev().cmd_clear_color_image( + self.handle(), + image, + layout, + &clear_colors, + subresources, + ); + } + } + + fn begin_rendering(&self, rendering_info: vk::RenderingInfo<'_>) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device() + .dev() + .cmd_begin_rendering(self.handle(), &rendering_info); + } + } + + fn set_viewport(&self, viewports: &[vk::Viewport]) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device() + .dev() + .cmd_set_viewport(self.handle(), 0, viewports); + } + } + fn set_scissors(&self, scissors: &[vk::Rect2D]) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device() + .dev() + .cmd_set_scissor(self.handle(), 0, scissors); + } + } + fn push_constants( + &self, + layout: &PipelineLayout, + stage: vk::ShaderStageFlags, + offset: u32, + bytes: &[u8], + ) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device().dev().cmd_push_constants( + self.handle(), + layout.handle(), + stage, + offset, + bytes, + ); + } + } + fn bind_pipeline(&self, pipeline: &Pipeline) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device().dev().cmd_bind_pipeline( + self.handle(), + pipeline.bind_point(), + pipeline.handle(), + ); + } + } + fn bind_vertex_buffers(&self, buffers: &[vk::Buffer], offsets: &[u64]) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device() + .dev() + .cmd_bind_vertex_buffers(self.handle(), 0, buffers, offsets); + } + } + fn bind_indices(&self, buffer: vk::Buffer, offset: u64, kind: vk::IndexType) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device() + .dev() + .cmd_bind_index_buffer(self.handle(), buffer, offset, kind); + } + } + + fn bind_descriptor_sets( + &self, + layout: &PipelineLayout, + bind_point: vk::PipelineBindPoint, + descriptor_sets: &[vk::DescriptorSet], + ) { + // assert_eq!(self.state(), CommandBufferState::Recording); + use crate::device::DeviceOwned; + unsafe { + self.device().dev().cmd_bind_descriptor_sets( + self.handle(), + bind_point, + layout.handle(), + 0, + descriptor_sets, + &[], + ); + } + } + + fn draw_indexed( + &self, + indices: u32, + instances: u32, + index_offset: u32, + vertex_offset: i32, + instance_offset: u32, + ) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device().dev().cmd_draw_indexed( + self.handle(), + indices, + instances, + index_offset, + vertex_offset, + instance_offset, + ); + } + } + + fn draw_indexed_indirect(&self, buffer: vk::Buffer, offset: u64, count: u32, stride: u32) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device().dev().cmd_draw_indexed_indirect( + self.handle(), + buffer, + offset, + count, + stride, + ); + } + } + + fn end_rendering(&self) { + // assert_eq!(self.state(), CommandBufferState::Recording); + unsafe { + self.device().dev().cmd_end_rendering(self.handle()); + } + } + } +} + +pub use command_pools::{CommandBuffer, CommandBufferFuture, CommandPool, CommandPools}; + +mod command_pools { + use std::{borrow::Cow, cell::Cell, mem::ManuallyDrop, sync::Arc}; + + use arrayvec::ArrayVec; + use ash::{prelude::VkResult, vk}; + use parking_lot::Mutex; + use smol::future::FutureExt; + use thread_local::ThreadLocal; + + use crate::{ + define_device_owned_handle, + device::{Device, DeviceOwned}, + sync, + util::MutexExt, + Queue, + }; + + #[derive(Debug, thiserror::Error)] + pub enum Error { + #[error(transparent)] + Vulkan(#[from] vk::Result), + #[error("CommandBuffer allocation failed: wrong number of command buffers.")] + NoCommandBuffersAllocated, + #[error("Attempted to submit commandbuffer to incompatible queue.")] + InvalidQueueSubmission, + } + + define_device_owned_handle! { + #[derive()] + pub CommandPool(vk::CommandPool) { + family: u32, + free_list: Mutex, Vec)>>, + } => |this| unsafe { + this.device().dev().destroy_command_pool(this.handle(), None); + } + } + + impl !Sync for CommandPool {} + + impl CommandPool { + pub fn new( + dev: Device, + family: u32, + flags: vk::CommandPoolCreateFlags, + name: Option>, + ) -> Result { + let pool_info = vk::CommandPoolCreateInfo::default() + .queue_family_index(family) + .flags(flags); + + let pool = unsafe { dev.dev().create_command_pool(&pool_info, None)? }; + + Self::construct(dev, pool, name, family, Mutex::new(Default::default())) + } + + #[allow(dead_code)] + pub unsafe fn reset(&self) -> VkResult<()> { + unsafe { + self.device() + .dev() + .reset_command_pool(self.handle(), vk::CommandPoolResetFlags::empty()) + } + } + + pub fn free_command_buffers(&self) { + self.free_list_mut(|levels, buffers| { + unsafe { + self.device() + .dev() + .free_command_buffers(self.handle(), &buffers); + } + levels.clear(); + buffers.clear(); + }); + } + + pub fn free_command_buffer( + self: &Arc, + level: vk::CommandBufferLevel, + buffer: vk::CommandBuffer, + ) { + self.free_list_mut(|levels, buffers| { + levels.push(level); + buffers.push(buffer); + }); + } + + fn free_list_mut< + T, + F: FnOnce(&mut Vec, &mut Vec) -> T, + >( + &self, + cb: F, + ) -> T { + self.free_list.with_locked(|cell| { + let (mut levels, mut buffers) = cell.take(); + let t = cb(&mut levels, &mut buffers); + cell.set((levels, buffers)); + t + }) + } + + pub fn alloc( + self: &Arc, + name: Option>, + level: vk::CommandBufferLevel, + flags: vk::CommandBufferUsageFlags, + ) -> Result { + // TODO: is this really the right place? + self.free_command_buffers(); + + let handle = { + let info = vk::CommandBufferAllocateInfo::default() + .command_pool(self.handle()) + .command_buffer_count(1) + .level(level); + + unsafe { self.device().dev().allocate_command_buffers(&info) }? + .pop() + .ok_or(Error::NoCommandBuffersAllocated)? + }; + + let begin_info = vk::CommandBufferBeginInfo::default().flags(flags); + unsafe { + self.device() + .dev() + .begin_command_buffer(handle, &begin_info)?; + } + + Ok(CommandBuffer::construct( + self.device().clone(), + handle, + name, + self.clone(), + level, + )?) + } + + pub fn alloc_primary( + self: &Arc, + name: Option>, + flags: vk::CommandBufferUsageFlags, + ) -> Result { + self.alloc(name, vk::CommandBufferLevel::PRIMARY, flags) + } + } + + impl core::fmt::Debug for CommandPool { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CommandPool") + .field("inner", &self.inner) + .field("family", &self.family) + .finish_non_exhaustive() + } + } + + impl CommandPoolsShard { + fn new() -> Self { + Self { + per_family_pools: ArrayVec::new(), + } + } + + fn find_or_default(&mut self, dev: &Device, family: u32) -> Arc { + match self + .per_family_pools + .binary_search_by_key(&family, |entry| entry.0) + { + Ok(i) => self.per_family_pools[i].1.clone(), + Err(i) => { + let pool = Arc::new( + CommandPool::new( + dev.clone(), + family, + vk::CommandPoolCreateFlags::empty(), + Some( + format!("commandpool-{:?}-{}", std::thread::current().id(), family) + .into(), + ), + ) + .unwrap(), + ); + self.per_family_pools + .try_insert(i, (family, pool.clone())) + .expect("too many command pools in thread local"); + + pool + } + } + } + } + + // SAFETY: this isn't safe lmao. assumes pools are unused when dropped + unsafe impl Send for CommandPoolsShard {} + + #[derive(Debug)] + struct CommandPoolsShard { + per_family_pools: ArrayVec<(u32, Arc), 8>, + } + + pub struct CommandPools { + device: Device, + shards: ThreadLocal>>, + } + + impl CommandPools { + pub fn get_for_family(&self, family: u32) -> Arc { + let shard = self + .shards + .get_or(|| core::cell::Cell::new(CommandPoolsShard::new()).into()); + + let mut inner = shard.replace(CommandPoolsShard::new()); + let pool = inner.find_or_default(&self.device, family); + _ = shard.replace(inner); + + pool + } + + pub fn allocate_buffer( + &self, + family: u32, + name: Option>, + level: vk::CommandBufferLevel, + flags: vk::CommandBufferUsageFlags, + ) -> Result { + self.get_for_family(family).alloc(name, level, flags) + } + } + + define_device_owned_handle! { + #[derive(Debug)] + pub CommandBuffer(vk::CommandBuffer) { + pool: Arc, + #[allow(unused)] + level: vk::CommandBufferLevel, + } => |this| unsafe { + this.device().dev().free_command_buffers(this.pool.handle(), &[this.handle()]); + } + } + + impl !Sync for CommandBuffer {} + impl !Send for CommandBuffer {} + + impl super::traits::CommandBufferExt for CommandBuffer {} + + impl CommandBuffer { + pub fn submit( + self, + queue: &Queue, + wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, + signal: Option, + fence: Arc, + ) -> Result { + let this = ManuallyDrop::new(self); + + if queue.1 != this.pool.family { + tracing::error!("attempted to submit commandbuffer to incompatible queue."); + return Err(Error::InvalidQueueSubmission); + } + unsafe { + this.device().dev().end_command_buffer(this.handle())?; + } + + let buffers = [this.handle()]; + let mut submit_info = vk::SubmitInfo::default().command_buffers(&buffers); + + if let Some(semaphore) = signal.as_ref() { + submit_info = submit_info.signal_semaphores(core::slice::from_ref(semaphore)); + } + if let Some((semaphore, stage)) = wait.as_ref() { + submit_info = submit_info + .wait_semaphores(core::slice::from_ref(semaphore)) + .wait_dst_stage_mask(core::slice::from_ref(stage)); + } + + queue.with_locked(|queue| unsafe { + this.device() + .dev() + .queue_submit(queue, &[submit_info], fence.fence()) + })?; + + Ok(CommandBufferFuture { + inner: sync::FenceFuture::new(fence), + pool: this.pool.clone(), + buffer: this.handle(), + level: this.level, + }) + } + } + + pub struct CommandBufferFuture { + inner: sync::FenceFuture<'static>, + pool: Arc, + buffer: vk::CommandBuffer, + level: vk::CommandBufferLevel, + } + + impl CommandBufferFuture { + pub fn block(&self) -> VkResult<()> { + self.inner.block() + } + } + + impl core::future::Future for CommandBufferFuture { + type Output = as core::future::Future>::Output; + + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + self.inner.poll(cx) + } + } + + impl Drop for CommandBufferFuture { + fn drop(&mut self) { + self.pool.free_command_buffer(self.level, self.buffer); + } + } +} diff --git a/crates/renderer/src/device.rs b/crates/renderer/src/device.rs index d51e27c..a6c299b 100644 --- a/crates/renderer/src/device.rs +++ b/crates/renderer/src/device.rs @@ -26,6 +26,7 @@ pub struct DeviceQueueFamilies { pub(crate) present: (u32, u32), pub(crate) async_compute: (u32, u32), pub(crate) transfer: (u32, u32), + pub(crate) properties: Box<[vk::QueueFamilyProperties]>, } impl DeviceQueueFamilies { @@ -78,12 +79,12 @@ bitflags::bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct QueueFlags: u32 { const GRAPHICS = 1 << 0; - const PRESENT = 1 << 1; - const ASYNC_COMPUTE = 1 << 2; - const TRANSFER = 1 << 3; + const ASYNC_COMPUTE = 1 << 1; + const TRANSFER = 1 << 2; + const PRESENT = 1 << 3; const NONE = 0; - const PRESENT_GRAPHICS = 1 << 0 | 1 << 1; + const PRESENT_GRAPHICS = 1 << 0 | 1 << 2; } } @@ -256,7 +257,7 @@ impl DeviceBuilder { display_handle: Option, pdev: vk::PhysicalDevice, ) -> DeviceQueueFamilies { - let queue_families = unsafe { + let queue_familiy_properties = unsafe { instance .instance .get_physical_device_queue_family_properties(pdev) @@ -318,7 +319,7 @@ impl DeviceBuilder { } let mut queue_families = QueueFamilies( - queue_families + queue_familiy_properties .iter() .enumerate() .map(|(i, family)| { @@ -416,6 +417,7 @@ impl DeviceBuilder { async_compute, transfer, present, + properties: queue_familiy_properties.into_boxed_slice(), }; queues @@ -887,7 +889,7 @@ impl Device { pub fn physical_device(&self) -> &PhysicalDevice { &self.0.physical } - pub fn graphics_queue(&self) -> &Queue { + pub fn main_queue(&self) -> &Queue { &self.0.main_queue } pub fn transfer_queue(&self) -> &Queue { diff --git a/crates/renderer/src/egui.rs b/crates/renderer/src/egui.rs index 4d00d4c..da874d4 100644 --- a/crates/renderer/src/egui.rs +++ b/crates/renderer/src/egui.rs @@ -5,6 +5,7 @@ use indexmap::IndexMap; use crate::{ buffers::{Buffer, BufferDesc}, + commands::traits::CommandBufferExt, device::{self, DeviceOwned}, images::{Image, ImageDesc, ImageViewDesc}, render_graph::{ diff --git a/crates/renderer/src/render_graph.rs b/crates/renderer/src/render_graph.rs index 5e168e7..ff9ddd9 100644 --- a/crates/renderer/src/render_graph.rs +++ b/crates/renderer/src/render_graph.rs @@ -8,7 +8,8 @@ use std::{ use crate::{ buffers::{Buffer, BufferDesc}, - commands, def_monotonic_id, + commands::{self, traits::CommandBufferExt}, + def_monotonic_id, device::{self, DeviceOwned}, images::{self, Image, ImageDesc}, util::{self, Rgba, WithLifetime}, @@ -489,7 +490,7 @@ impl RenderGraph { })?; let pool = - commands::SingleUseCommandPool::new(device.clone(), device.graphics_queue().clone())?; + commands::SingleUseCommandPool::new(device.clone(), device.main_queue().clone())?; let resources = &self.resources; let cmds = topo diff --git a/crates/renderer/src/rendering/mod.rs b/crates/renderer/src/rendering/mod.rs index af67bed..4e5402c 100644 --- a/crates/renderer/src/rendering/mod.rs +++ b/crates/renderer/src/rendering/mod.rs @@ -7,7 +7,7 @@ pub use crate::egui_pass::{egui_pass, egui_pre_pass}; use crate::{ buffers::{Buffer, BufferDesc}, - commands, + commands::{self, traits::CommandBufferExt}, device::{Device, DeviceOwned}, images::ImageViewDesc, pipeline, @@ -97,7 +97,7 @@ impl Wireframe { }, )?; - let pool = commands::SingleUseCommandPool::new(dev.clone(), dev.graphics_queue().clone())?; + let pool = commands::SingleUseCommandPool::new(dev.clone(), dev.main_queue().clone())?; let cmd = pool.alloc()?;