From 29dbdf9f94bc959740a04b7b334ef245ad055844 Mon Sep 17 00:00:00 2001 From: janis Date: Fri, 17 Apr 2026 07:04:52 +0200 Subject: [PATCH] idk rewrite this again --- crates/renderer/src/images.rs | 100 ++++- crates/renderer/src/render_graph/commands.rs | 266 ++++-------- .../src/render_graph/graph_builder.rs | 18 + crates/renderer/src/render_graph/recorder.rs | 69 ++- crates/renderer/src/render_graph/resources.rs | 392 ++++++++++-------- 5 files changed, 455 insertions(+), 390 deletions(-) diff --git a/crates/renderer/src/images.rs b/crates/renderer/src/images.rs index 5ae2f71..5228435 100644 --- a/crates/renderer/src/images.rs +++ b/crates/renderer/src/images.rs @@ -453,7 +453,7 @@ impl Image { desc.mip_range.set_max_end(self.desc.mip_levels); desc.layer_range.set_max_end(self.desc.array_layers); - if !MipRange::from(..self.desc.mip_levels).contains(&desc.mip_range) { + if !MipRange::from(..self.desc.mip_levels).contains(desc.mip_range) { tracing::error!( "image view mip range {:?} exceeds image mip levels {}", desc.mip_range, @@ -791,9 +791,10 @@ impl From<(i32, i32, i32)> for Offset { /// `start..end` range of mip levels or array layers. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(C)] pub struct MipRange { - start: u32, - end: u32, + pub start: u32, + pub end: u32, } impl Default for MipRange { @@ -809,6 +810,12 @@ impl MipRange { pub fn new(start: u32, end: u32) -> Self { Self { start, end } } + pub fn single(level: u32) -> Self { + Self { + start: level, + end: level + 1, + } + } pub fn with_max_end(mut self, end: u32) -> Self { self.set_max_end(end); self @@ -847,11 +854,11 @@ impl MipRange { self.start < total_mips && (self.end == vk::REMAINING_MIP_LEVELS || self.end <= total_mips) } - pub fn contains(&self, other: &Self) -> bool { + pub fn contains(&self, other: Self) -> bool { self.start <= other.start && self.end >= other.end } - pub fn intersects(&self, other: &Self) -> bool { + pub fn intersects(&self, other: Self) -> bool { self.start < other.end && self.end > other.start } @@ -1152,6 +1159,89 @@ pub enum FormatClass { YuvG14X2B14X2R14X2Biplane422, } +impl FormatClass { + pub fn block_size(self) -> u32 { + use FormatClass::*; + + match self { + S8 | Bits8 | Alpha8 => 1, + D16 | Bits16 => 2, + Bits24 | D24 | D16S8 => 3, + Bits32 | D24S8 => 4, + D32S8 => 5, + Bits48 => 6, + Bits64 | D32 => 8, + Bits96 => 12, + Bits128 => 16, + Bits192 => 24, + Bits256 => 32, + + Bc1Rgb | Bc1Rgba => 8, + Bc2 | Bc3 | Bc6h | Bc7 => 16, + Bc4 => 8, + Bc5 => 16, + + Etc2Rgb | Etc2Rgba | EacR => 8, + Etc2EacRgba | EacRg => 16, + + Astc4x4 | Astc5x4 | Astc5x5 | Astc6x5 | Astc6x6 | Astc8x5 | Astc8x6 | Astc8x8 + | Astc10x5 | Astc10x6 | Astc10x8 | Astc10x10 | Astc12x10 | Astc12x12 => 16, + + YuvG8B8G8R8_422 | YuvB8G8R8G8_422 => 2, + + YuvG8B8R8Triplane420 + | YuvG8B8R8Biplane420 + | YuvG10X6B10X6R10X6Triplane420 + | YuvG10X6B10X6R10X6Biplane420 + | YuvG12X4B12X4R12X4Triplane420 + | YuvG12X4B12X4R12X4Biplane420 + | YuvG16B16R16Triplane420 + | YuvG16B16R16Biplane420 => 1, + YuvG8B8R8Triplane422 => todo!(), + YuvG8B8R8Biplane422 => todo!(), + YuvG8B8R8Triplane444 => todo!(), + YuvG10X6B10X6R10X6Triplane422 => todo!(), + YuvG10X6B10X6R10X6Biplane422 => todo!(), + YuvG10X6B10X6R10X6Triplane444 => todo!(), + YuvG12X4B12X4R12X4Triplane422 => todo!(), + YuvG12X4B12X4R12X4Biplane422 => todo!(), + YuvG12X4B12X4R12X4Triplane444 => todo!(), + YuvG16B16R16Triplane422 => todo!(), + YuvG16B16R16Biplane422 => todo!(), + YuvG16B16R16Triplane444 => todo!(), + YuvG8B8R8Biplane444 => todo!(), + YuvG10X6B10X6R10X6Biplane444 => todo!(), + YuvG12X4B12X4R12X4Biplane444 => todo!(), + YuvG16B16R16Biplane444 => todo!(), + Bits64R10G10B10A10 => todo!(), + Bits64G10B10G10R10_422 => todo!(), + Bits64B10G10R10G10_422 => todo!(), + Bits64R12G12B12A12 => todo!(), + Bits64G12B12G12R12_422 => todo!(), + Bits64B12G12R12G12_422 => todo!(), + Bits64G16B16G16R16_422 => todo!(), + Bits64B16G16R16G16_422 => todo!(), + Bits64R14G14B14A14 => todo!(), + Pvrtc1_2bpp => todo!(), + Pvrtc1_4bpp => todo!(), + Pvrtc2_2bpp => todo!(), + Pvrtc2_4bpp => todo!(), + Astc3x3x3 => todo!(), + Astc4x3x3 => todo!(), + Astc4x4x3 => todo!(), + Astc4x4x4 => todo!(), + Astc5x4x4 => todo!(), + Astc5x5x4 => todo!(), + Astc5x5x5 => todo!(), + Astc6x5x5 => todo!(), + Astc6x6x5 => todo!(), + Astc6x6x6 => todo!(), + YuvG14X2B14X2R14X2Biplane420 => todo!(), + YuvG14X2B14X2R14X2Biplane422 => todo!(), + } + } +} + impl From for FormatClass { fn from(format: vk::Format) -> Self { use vk::Format as F; diff --git a/crates/renderer/src/render_graph/commands.rs b/crates/renderer/src/render_graph/commands.rs index 341a464..eb88362 100644 --- a/crates/renderer/src/render_graph/commands.rs +++ b/crates/renderer/src/render_graph/commands.rs @@ -41,38 +41,74 @@ impl Command for ImportResource { } } -pub struct CopyBuffers { - pub src: BufferSlice, - pub dst: BufferSlice, +pub trait BufferImageBufferCopyRegion { + fn regions(&self) -> &[BufferRows]; +} +impl BufferImageBufferCopyRegion for BufferRows { + fn regions(&self) -> &[BufferRows] { + core::slice::from_ref(self) + } +} +impl> BufferImageBufferCopyRegion for T { + fn regions(&self) -> &[BufferRows] { + self.as_ref() + } } -pub struct CopyTextures { - pub src: TextureRegion, - pub dst: TextureRegion, +pub trait ImageCopyRegion { + fn regions(&self) -> &[TextureRegion]; +} +impl ImageCopyRegion for TextureRegion { + fn regions(&self) -> &[TextureRegion] { + core::slice::from_ref(self) + } +} +impl> ImageCopyRegion for T { + fn regions(&self) -> &[TextureRegion] { + self.as_ref() + } } -pub struct CopyBufferToTexture { - pub src: BufferSlice, - pub rows: Option, - pub dst: TextureRegion, +pub trait BufferCopyRegion { + fn regions(&self) -> &[BufferRange]; +} +impl BufferCopyRegion for BufferRange { + fn regions(&self) -> &[BufferRange] { + core::slice::from_ref(self) + } +} +impl> BufferCopyRegion for T { + fn regions(&self) -> &[BufferRange] { + self.as_ref() + } } -pub struct CopyTextureToBuffer { - pub src: TextureRegion, - pub rows: Option, - pub dst: BufferSlice, +pub struct CopyBuffers { + pub src: Buffer, + pub src_region: T, + pub dst: Buffer, + pub dst_region: T, } -pub struct CopyBuffersToTextures { - pub src: BufferSlice, - pub src_rows: Vec, +pub struct CopyTextures { + pub src: Texture, + pub src_region: T, pub dst: Texture, - pub src_ranges: Vec<(TextureRegion, vk::Offset3D, vk::Extent3D)>, + pub dst_region: T, } -pub struct Copy { - pub src: T, - pub dst: U, +pub struct CopyBufferToTexture { + pub src: Buffer, + pub src_region: T, + pub dst: Texture, + pub dst_region: U, +} + +pub struct CopyTextureToBuffer { + pub src: Texture, + pub src_region: T, + pub dst: Buffer, + pub dst_region: U, } fn copy_side_effects_inner(src: &T, dst: &U, mut map: SideEffectMap) @@ -92,82 +128,55 @@ where )); } -impl Command for CopyBufferToTexture { - fn side_effects(&self, map: SideEffectMap) { - copy_side_effects_inner(&self.src, &self.dst, map) - } +impl Command for CopyBufferToTexture +where + T: BufferImageBufferCopyRegion, + U: ImageCopyRegion, +{ + fn side_effects(&self, mut map: SideEffectMap) { + let src_map = map.reborrow().into_side_effect_map2( + vk::PipelineStageFlags2::COPY, + vk::AccessFlags2::TRANSFER_READ, + Some(vk::ImageLayout::TRANSFER_SRC_OPTIMAL), + ); - fn apply(self, recorder: &mut CommandRecorder) { - let cmd = recorder.cmd_buffer(); - let dev = &cmd.device().raw; - - // it doesn't really make sense to copy the same data into multiple mip - // levels, considering the mip extents are different for each level. - debug_assert_eq!(self.dst.range.mip_levels.len(), 1); - - let rows = self.rows.unwrap_or_default(); - let regions = &[vk::BufferImageCopy2::default() - .buffer_offset(self.src.range.offset + rows.offset) - .buffer_row_length(rows.row_size) - .buffer_image_height(rows.row_count) - .image_offset(self.dst.offset) - .image_extent(self.dst.extent) - .image_subresource( - vk::ImageSubresourceLayers::default() - .aspect_mask(self.dst.range.aspect) - .mip_level(self.dst.range.mip_levels.start()) - .base_array_layer(self.dst.range.array_layers.start()) - .layer_count(self.dst.range.array_layers.len()), - )]; - - let info = vk::CopyBufferToImageInfo2::default() - .src_buffer(recorder.get_buffer_handle(self.src.id())) - .dst_image(recorder.get_image_handle(self.dst.id())) - .dst_image_layout(recorder.get_image_layout(self.dst.id())) - .regions(regions); - - unsafe { - dev.cmd_copy_buffer_to_image2(cmd.raw(), &info); + let src_id = self.src.id(); + for region in self.src_region.regions() { + src_map.insert_range(src_id); } } -} - -impl Command for Copy { - fn side_effects(&self, map: SideEffectMap) { - self.side_effects_inner(map) - } fn apply(self, recorder: &mut CommandRecorder) { let cmd = recorder.cmd_buffer(); let dev = &cmd.device().raw; - let regions = &self - .src - .rows + let regions = self + .src_region + .regions() .iter() - .zip(self.dst.ranges.iter()) - .map(|(src_row, dst_range)| { + .zip(self.dst_region.regions()) + .map(|(src_region, dst_region)| { vk::BufferImageCopy2::default() - .buffer_offset(src_row.offset) - .buffer_row_length(src_row.row_size) - .buffer_image_height(src_row.row_count) - .image_offset(dst_range.offset()) - .image_extent(dst_range.extent()) + .buffer_offset(src_region.offset) + .buffer_row_length(src_region.row_size) + .buffer_image_height(src_region.row_count) + .image_offset(dst_region.offset) + .image_extent(dst_region.extent) .image_subresource( vk::ImageSubresourceLayers::default() - .aspect_mask(dst_range.aspect) - .mip_level(dst_range.mip_levels.start()) - .base_array_layer(dst_range.array_layers.start()) - .layer_count(dst_range.array_layers.len()), + .aspect_mask(dst_region.range.aspect) + .mip_level(dst_region.range.mip_level) + .base_array_layer(dst_region.range.array_layers.start()) + .layer_count(dst_region.range.array_layers.len()), ) }) - .collect::>(); + .collect::>(); let info = vk::CopyBufferToImageInfo2::default() .src_buffer(recorder.get_buffer_handle(self.src.id())) .dst_image(recorder.get_image_handle(self.dst.id())) .dst_image_layout(recorder.get_image_layout(self.dst.id())) - .regions(regions); + .regions(®ions); unsafe { dev.cmd_copy_buffer_to_image2(cmd.raw(), &info); @@ -175,107 +184,6 @@ impl Command for Copy { } } -impl Command for Copy { - fn side_effects(&self, map: SideEffectMap) { - self.side_effects_inner(map) - } - - fn apply(self, recorder: &mut CommandRecorder) { - let cmd = recorder.cmd_buffer(); - let dev = &cmd.device().raw; - - let regions = &[vk::BufferImageCopy2::default() - .buffer_offset(self.dst.row.offset) - .buffer_row_length(self.dst.row.row_size) - .buffer_image_height(self.dst.row.row_count) - .image_offset(self.src.range.offset()) - .image_extent(self.src.range.extent()) - .image_subresource( - vk::ImageSubresourceLayers::default() - .aspect_mask(self.src.range.aspect) - .mip_level(self.src.range.mip_levels.start()) - .base_array_layer(self.src.range.array_layers.start()) - .layer_count(self.src.range.array_layers.len()), - )]; - - let info = vk::CopyImageToBufferInfo2::default() - .dst_buffer(recorder.get_buffer_handle(self.dst.id())) - .src_image(recorder.get_image_handle(self.src.id())) - .src_image_layout(recorder.get_image_layout(self.src.id())) - .regions(regions); - - unsafe { - dev.cmd_copy_image_to_buffer2(cmd.raw(), &info); - } - } -} -impl Command for Copy { - fn side_effects(&self, map: SideEffectMap) { - self.side_effects_inner(map) - } - - fn apply(self, recorder: &mut CommandRecorder) { - let cmd = recorder.cmd_buffer(); - let dev = &cmd.device().raw; - - let regions = &[vk::ImageCopy2::default() - .extent(self.src.range.extent()) - .dst_offset(self.dst.range.offset()) - .src_offset(self.src.range.offset()) - .dst_subresource( - vk::ImageSubresourceLayers::default() - .aspect_mask(self.dst.range.aspect) - .mip_level(self.dst.range.mip_levels.start()) - .base_array_layer(self.dst.range.array_layers.start()) - .layer_count(self.dst.range.array_layers.len()), - ) - .src_subresource( - vk::ImageSubresourceLayers::default() - .aspect_mask(self.src.range.aspect) - .mip_level(self.src.range.mip_levels.start()) - .base_array_layer(self.src.range.array_layers.start()) - .layer_count(self.src.range.array_layers.len()), - )]; - - let info = vk::CopyImageInfo2::default() - .dst_image(recorder.get_image_handle(self.dst.id())) - .dst_image_layout(recorder.get_image_layout(self.dst.id())) - .src_image(recorder.get_image_handle(self.src.id())) - .src_image_layout(recorder.get_image_layout(self.src.id())) - .regions(regions); - - unsafe { - dev.cmd_copy_image2(cmd.raw(), &info); - } - } -} -impl Command for Copy { - fn side_effects(&self, map: SideEffectMap) { - self.side_effects_inner(map) - } - - fn apply(self, recorder: &mut CommandRecorder) { - let cmd = recorder.cmd_buffer(); - let dev = &cmd.device().raw; - - debug_assert_eq!(self.src.range.size, self.dst.range.size); - - let regions = &[vk::BufferCopy2::default() - .dst_offset(self.dst.range.offset) - .src_offset(self.src.range.offset) - .size(self.src.range.size)]; - - let info = vk::CopyBufferInfo2::default() - .dst_buffer(recorder.get_buffer_handle(self.dst.id())) - .src_buffer(recorder.get_buffer_handle(self.src.id())) - .regions(regions); - - unsafe { - dev.cmd_copy_buffer2(cmd.raw(), &info); - } - } -} - pub struct ClearTexture { pub dst: Write, pub clear_value: [f32; 4], diff --git a/crates/renderer/src/render_graph/graph_builder.rs b/crates/renderer/src/render_graph/graph_builder.rs index 246512b..6d558c7 100644 --- a/crates/renderer/src/render_graph/graph_builder.rs +++ b/crates/renderer/src/render_graph/graph_builder.rs @@ -44,6 +44,24 @@ impl<'r> GraphBuilder<'r> { todo!() } + fn make_persistent_buffer( + &self, + _size: u64, + _usage: vk::BufferUsageFlags, + _location: gpu_allocator::MemoryLocation, + ) -> Buffer<'r> { + todo!() + } + + fn make_transient_buffer( + &self, + _size: u64, + _usage: vk::BufferUsageFlags, + _location: gpu_allocator::MemoryLocation, + ) -> Buffer<'r> { + todo!() + } + fn make_buffer( &self, _size: u64, diff --git a/crates/renderer/src/render_graph/recorder.rs b/crates/renderer/src/render_graph/recorder.rs index 6e83079..ca14dbb 100644 --- a/crates/renderer/src/render_graph/recorder.rs +++ b/crates/renderer/src/render_graph/recorder.rs @@ -1,7 +1,7 @@ use std::{ collections::{BTreeMap, BTreeSet}, marker::PhantomData, - mem::{MaybeUninit, size_of}, + mem::{ManuallyDrop, MaybeUninit, size_of}, ptr::NonNull, }; @@ -12,7 +12,9 @@ use crate::{ images::ImageDesc, render_graph::{ commands::{Command, InsideRenderPass, OutsideRenderPass}, - resources::{ResourceAccess, ResourceId, ResourceRange, TextureRegion}, + resources::{ + ResourceId, Subresource, SubresourceScope, SubresourceScopeInner, TextureRegion, + }, }, }; @@ -50,18 +52,27 @@ struct CommandMeta { enum VecOrSingle { Vec(Vec), - Single(T), + Single(ManuallyDrop), +} + +impl From for VecOrSingle { + fn from(value: T) -> Self { + Self::Single(ManuallyDrop::new(value)) + } +} + +impl From> for VecOrSingle { + fn from(vec: Vec) -> Self { + Self::Vec(vec) + } } impl VecOrSingle { - fn push(&mut self, value: T) - where - T: Default, - { + fn push(&mut self, value: T) { match self { VecOrSingle::Vec(vec) => vec.push(value), VecOrSingle::Single(existing) => { - let existing = std::mem::take(existing); + let existing = unsafe { ManuallyDrop::take(existing) }; *self = VecOrSingle::Vec(vec![existing, value]); } } @@ -69,27 +80,27 @@ impl VecOrSingle { fn iter(&self) -> core::slice::Iter<'_, T> { match self { VecOrSingle::Vec(items) => items.as_slice().iter(), - VecOrSingle::Single(item) => core::slice::from_ref(item).iter(), + VecOrSingle::Single(item) => core::slice::from_ref::(item).iter(), } } } #[derive(Default)] struct SideEffects { - map: BTreeMap<(ResourceId, u32), VecOrSingle>, + map: BTreeMap<(ResourceId, u32), VecOrSingle>, resources: BTreeSet, } pub struct SideEffectMap<'a>(&'a mut SideEffects, u32); impl<'a> SideEffectMap<'a> { - pub fn insert(&mut self, id: ResourceId, access: ResourceAccess) { + pub fn insert(&mut self, id: ResourceId, scope: SubresourceScope) { self.0.resources.insert(id); self.0 .map .entry((id, self.1)) - .and_modify(|entry| entry.push(access)) - .or_insert_with(|| VecOrSingle::Single(access)); + .and_modify(|entry| entry.push(scope)) + .or_insert_with(|| scope.into()); } pub fn reborrow(&mut self) -> SideEffectMap<'_> { @@ -132,18 +143,30 @@ impl<'a> SideEffectMap2<'a> { } pub fn insert_range(&mut self, id: ResourceId, range: T) where - (T, vk::ImageLayout): Into, + T: Into, { self.inner.resources.insert(id); - SideEffectMap(self.inner, self.command_index).insert( - id, - ResourceAccess { - range: (range, self.required_layout.unwrap_or_default()).into(), - stages: self.stage_flags, + let subresource: Subresource = range.into(); + let scope = match subresource { + Subresource::Buffer(buffer) => SubresourceScope { access: self.access_flags, + stage: self.stage_flags, + subresource: SubresourceScopeInner::Buffer { + subresource: buffer, + }, }, - ); + Subresource::Texture(texture) => SubresourceScope { + access: self.access_flags, + stage: self.stage_flags, + subresource: SubresourceScopeInner::Texture { + subresource: texture, + layout: self.required_layout.unwrap_or_default(), + }, + }, + }; + + SideEffectMap(self.inner, self.command_index).insert(id, scope); } } @@ -258,12 +281,12 @@ impl<'cmd> CommandList<'cmd> { struct Barrier { resource_id: ResourceId, command_index: u32, - from: ResourceAccess, + from: SubresourceScope, to: usize, } // map to keep track of the current state of each subresource - let mut subresource_state: BTreeMap> = + let mut subresource_state: BTreeMap> = Default::default(); let mut barriers = Vec::new(); @@ -293,7 +316,7 @@ impl<'cmd> CommandList<'cmd> { let mut folded = false; let len = entry.len(); for (_i, (sub, _state)) in &mut entry.iter_mut().enumerate().rev() { - if !folded && sub.try_fold(&access) { + if !folded && sub.try_merge(&access) { access = *sub; folded = true; } else if sub.conflicts(&access) { diff --git a/crates/renderer/src/render_graph/resources.rs b/crates/renderer/src/render_graph/resources.rs index 3e83f23..ef5bf13 100644 --- a/crates/renderer/src/render_graph/resources.rs +++ b/crates/renderer/src/render_graph/resources.rs @@ -10,27 +10,172 @@ pub trait Resource: Sized { } mod sealed { - pub trait Texture {} - pub trait Buffer {} + pub trait Texture { + fn format(&self) -> vk::Format { + todo!() + } + } + pub trait Buffer { + fn size(&self) -> u64 { + todo!() + } + } } #[derive(Debug, Clone, Copy)] -pub enum SubresourceRange { +pub enum Subresource { Buffer(BufferRange), Texture(TextureRange), } +#[derive(Debug, Clone, Copy)] +pub struct SubresourceScope { + pub access: vk::AccessFlags2, + pub stage: vk::PipelineStageFlags2, + pub subresource: SubresourceScopeInner, +} + +#[derive(Debug, Clone, Copy)] +pub enum SubresourceScopeInner { + Buffer { + subresource: BufferRange, + }, + Texture { + subresource: TextureRange, + layout: vk::ImageLayout, + }, +} + +impl SubresourceScope { + pub fn try_merge(&mut self, other: &Self) -> bool { + // cannot merge a read and a write. + if access_flag_is_read(self.access) != access_flag_is_read(other.access) { + return false; + } + + use SubresourceScopeInner::*; + match (self.subresource, other.subresource) { + (Buffer { subresource: lhs }, Buffer { subresource: rhs }) => { + if lhs.offset > rhs.offset || lhs.offset + lhs.size < rhs.offset + rhs.size { + // other is not a subresource of self, so we can't merge them. + return false; + } + + self.stage |= other.stage; + self.access |= other.access; + + true + } + ( + Texture { + subresource: lhs, + layout: lhs_layout, + }, + Texture { + subresource: rhs, + layout: rhs_layout, + }, + ) => { + // layout transition required even on reads, so can't fold if layouts differ. + if lhs_layout != rhs_layout { + // TODO: consider if we can still merge `other` into `self` + // if they overlap, and reduce the second barrier to only a + // layout transition. + return false; + } + + // two accesses conflict if they overlap; + // they overlap if they share mip levels or array layers with + // the same aspects. + // If one access contains the other, we can merge them (i + // think), however: if a previous access requires a less general + // barrier, then is it better to split? + + let overlaps = lhs.aspect.intersects(rhs.aspect) + && lhs.mip_levels.intersects(rhs.mip_levels) + && lhs.array_layers.intersects(rhs.array_layers); + + // non-overlapping sub-resources shouldn't be merged. + if !overlaps { + return false; + } + + let lhs_contains_rhs = lhs.aspect.contains(rhs.aspect) + && lhs.mip_levels.contains(rhs.mip_levels) + && lhs.array_layers.contains(rhs.array_layers); + + // other is not a subresource of self, so we can't merge them. + if !lhs_contains_rhs { + return false; + } + + self.stage |= other.stage; + self.access |= other.access; + + true + } + // different resource types can't merge + _ => false, + } + } + + pub fn conflicts(&self, other: &Self) -> bool { + use SubresourceScopeInner::*; + match (self.subresource, other.subresource) { + (Buffer { subresource: lhs }, Buffer { subresource: rhs }) => { + // two reads don't conflict + if access_flag_is_read(self.access) && access_flag_is_read(other.access) { + return false; + } + + // if they overlap, they conflict + !(lhs.offset > rhs.offset + rhs.size || rhs.offset > lhs.offset + lhs.size) + } + ( + Texture { + subresource: lhs, + layout: lhs_layout, + }, + Texture { + subresource: rhs, + layout: rhs_layout, + }, + ) => { + // image subresource ranges overlap if they share mip levels and + // array layers with the same aspects. + let overlaps = lhs.aspect.intersects(rhs.aspect) + && lhs.mip_levels.intersects(rhs.mip_levels) + && lhs.array_layers.intersects(rhs.array_layers); + + if !overlaps { + return false; + } + + // if they require different layouts, they conflict even on + // reads, since a layout transition is required. + if lhs_layout != rhs_layout { + return true; + } + + // finally, two reads don't conflict + access_flag_is_read(self.access) && access_flag_is_read(other.access) + } + + // different resource types can't conflict + _ => false, + } + } +} + mod impls { use super::*; impl sealed::Texture for Texture {} impl sealed::Texture for TextureRegion {} - impl sealed::Texture for TextureRegions {} impl sealed::Texture for TextureView {} impl sealed::Buffer for Buffer {} impl sealed::Buffer for BufferSlice {} - impl sealed::Buffer for BufferSlices {} impl Resource for Buffer { fn id(&self) -> ResourceId { @@ -169,177 +314,6 @@ mod impls { #[repr(transparent)] pub struct ResourceId(pub u64); -#[derive(Debug, Default, Clone, Copy)] -pub struct ResourceAccess { - pub range: ResourceRange, - pub access: vk::AccessFlags2, - pub stages: vk::PipelineStageFlags2, -} - -impl ResourceAccess { - /// Attempts to fold another ResourceAccess into this one. Returns true if - /// successful, false if they are incompatible. - pub fn try_fold(&mut self, other: &Self) -> bool { - // can only fold if both accesses are reads or both are writes - if access_flag_is_read(self.access) != access_flag_is_read(other.access) { - return false; - } - - match (&mut self.range, &other.range) { - ( - ResourceRange::Buffer { offset, size }, - ResourceRange::Buffer { - offset: offset_b, - size: size_b, - }, - ) => { - // only fold if other lies within self - if *offset > *offset_b || *offset + *size < *offset_b + *size_b { - return false; - } - - // merge stages: we need the barrier to scope all stages that - // touch the resource. - self.stages |= other.stages; - self.access |= other.access; - - true - } - ( - ResourceRange::Texture { - aspect, - mip_level, - array_layers, - layout, - .. - }, - ResourceRange::Texture { - aspect: aspect_b, - mip_level: mip_level_b, - array_layers: array_layers_b, - layout: layout_b, - .. - }, - ) => { - if layout != layout_b { - // layout transition required even on reads, so can't fold if layouts differ. - // TODO: consider if we can still merge `other` into `self` - // if they overlap, and reduce the second barrier to only a - // layout transition. - return false; - } - - // two accesses conflict if they overlap; - // they overlap if they share mip levels or array layers with - // the same aspects. - // If one access contains the other, we can merge them (i - // think), however: if a previous access requires a less general - // barrier, then is it better to split? - - let overlaps = aspect.intersects(*aspect_b) - && mip_level.intersects(mip_level_b) - && array_layers.intersects(array_layers_b); - - // non-overlapping sub-resources shouldn't be folded. - if !overlaps { - return false; - } - - // we know they overlap, but does `other` lie within `self`? - let b_is_subresource = aspect.contains(*aspect_b) - && mip_level.contains(mip_level_b) - && array_layers.contains(array_layers_b); - - // only fold if `other` is a subresource of `self`, otherwise we - // might need a more general barrier than `self` requires, and - // it's better to split. - if !b_is_subresource { - return false; - } - - // this should be a no-op? - - // *aspect |= *aspect_b; - // *mip_level |= mip_level_b; - // *array_layers |= array_layers_b; - - // origin and extent don't matter for barriers. - // let lower_left = Offset::from(*origin).min(Offset::from(*origin_b)); - // let upper_right = (Offset::from(*origin) + Extent::from(*extent).as_offset()) - // .max(Offset::from(*origin_b) + Extent::from(*extent_b).as_offset()); - // *origin = lower_left.into(); - // *extent = Extent::from_offset(upper_right - lower_left).into(); - - // merge stages: we need the barrier to scope all stages that - // touch the resource. - self.stages |= other.stages; - self.access |= other.access; - - true - } - // different resource types can't fold - _ => false, - } - } - - pub fn conflicts(&self, other: &Self) -> bool { - match (&self.range, &other.range) { - (ResourceRange::Buffer { .. }, ResourceRange::Texture { .. }) => false, - (ResourceRange::Texture { .. }, ResourceRange::Buffer { .. }) => false, - ( - ResourceRange::Buffer { offset, size }, - ResourceRange::Buffer { - offset: offset_b, - size: size_b, - }, - ) => { - // two reads don't conflict - // TODO: two writes may conflict if the user cares about the order of writes. - if access_flag_is_read(self.access) == access_flag_is_read(other.access) { - return false; - } - - // must be a read/write conflict, check if sub-resources intersect - !(*offset > *offset_b + *size_b || *offset_b > *offset + *size) - } - ( - ResourceRange::Texture { - aspect, - mip_level, - array_layers, - layout, - .. - }, - ResourceRange::Texture { - aspect: aspect_b, - mip_level: mip_level_b, - array_layers: array_layers_b, - layout: layout_b, - .. - }, - ) => { - // check if sub-resources intersect - let overlaps = aspect.intersects(*aspect_b) - && (mip_level.intersects(mip_level_b) - || array_layers.intersects(array_layers_b)); - - // non-overlapping sub-resources never conflict - if !overlaps { - return false; - } - - // layout transition required even on reads - if layout != layout_b { - return true; - } - - // two reads don't conflict - access_flag_is_read(self.access) != access_flag_is_read(other.access) - } - } - } -} - #[derive(Debug, Clone, Copy)] pub enum ResourceRange { Buffer { @@ -386,12 +360,70 @@ pub struct BufferRows { } #[derive(Debug, Default, Clone, Copy)] +#[repr(C)] pub struct TextureRange { pub aspect: vk::ImageAspectFlags, pub mip_levels: MipRange, pub array_layers: MipRange, } +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub struct TextureLayers { + pub aspect: vk::ImageAspectFlags, + pub mip_level: u32, + pub array_layers: MipRange, +} + +impl From for vk::ImageSubresourceLayers { + fn from(value: TextureLayers) -> Self { + vk::ImageSubresourceLayers { + aspect_mask: value.aspect, + mip_level: value.mip_level, + base_array_layer: value.array_layers.start, + layer_count: value.array_layers.end - value.array_layers.start, + } + } +} + +impl From for vk::ImageSubresourceRange { + fn from(value: TextureRange) -> Self { + vk::ImageSubresourceRange { + aspect_mask: value.aspect, + base_mip_level: value.mip_levels.start, + level_count: value.mip_levels.end - value.mip_levels.start, + base_array_layer: value.array_layers.start, + layer_count: value.array_layers.end - value.array_layers.start, + } + } +} + +impl From for TextureRange { + fn from(value: TextureLayers) -> Self { + Self { + aspect: value.aspect, + mip_levels: MipRange::single(value.mip_level), + array_layers: value.array_layers, + } + } +} + +impl From for Subresource { + fn from(value: TextureLayers) -> Self { + Subresource::Texture(TextureRange { + aspect: value.aspect, + mip_levels: MipRange::single(value.mip_level), + array_layers: value.array_layers, + }) + } +} + +impl From for Subresource { + fn from(value: TextureRange) -> Self { + Subresource::Texture(value) + } +} + impl From<(BufferRange, vk::ImageLayout)> for ResourceRange { fn from((value, _): (BufferRange, vk::ImageLayout)) -> Self { ResourceRange::Buffer { @@ -470,17 +502,11 @@ pub struct BufferRowSlices { } pub struct TextureRegion { - pub texture: Texture, - pub range: TextureRange, + pub range: TextureLayers, pub offset: vk::Offset3D, pub extent: vk::Extent3D, } -pub struct TextureRegions { - pub texture: Texture, - pub ranges: Vec<(TextureRange, vk::Offset3D, vk::Extent3D)>, -} - pub struct TextureView { pub texture: Texture, pub format: vk::Format,