Compare commits

..

3 commits

Author SHA1 Message Date
janis 29dbdf9f94
idk rewrite this again 2026-04-17 07:04:52 +02:00
janis d1a0fbd592
im lost.. 2026-04-15 23:33:09 +02:00
janis d1b5e427a7
access: this is quite complicated actually.. 2026-04-15 16:30:49 +02:00
5 changed files with 574 additions and 391 deletions

View file

@ -449,7 +449,11 @@ impl Image {
});
}
if !desc.mip_range.fits_in(self.desc.mip_levels) {
// update imageview desc to make sure the mip range and layer ranges don't contain `vk::REMAINING_MIP_LEVELS`.
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) {
tracing::error!(
"image view mip range {:?} exceeds image mip levels {}",
desc.mip_range,
@ -785,10 +789,12 @@ 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 {
@ -801,6 +807,24 @@ impl Default for MipRange {
}
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
}
pub fn set_max_end(&mut self, end: u32) {
if self.end == vk::REMAINING_MIP_LEVELS {
self.end = end;
}
}
pub fn len(&self) -> u32 {
self.end - self.start
}
@ -813,6 +837,8 @@ impl MipRange {
self.end
}
/// Returns the intersection of this range with another range.
/// If the ranges do not intersect, returns an empty range.
pub fn range(&self, other: &Self) -> Self {
Self {
start: self.start.max(other.start).min(self.end),
@ -828,10 +854,19 @@ impl MipRange {
self.start < total_mips && (self.end == vk::REMAINING_MIP_LEVELS || self.end <= total_mips)
}
pub fn intersects(&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 {
self.start < other.end && self.end > other.start
}
/// Returns the union of this range with another range.
/// The union of two ranges is the smallest range that contains both ranges.
/// Note that since the union of two ranges may contain values that are not
/// in either range, this function does not satisfy the properties of a
/// mathematical union operation.
pub fn union(&self, other: &Self) -> Self {
Self {
start: self.start.min(other.start),
@ -840,6 +875,59 @@ impl MipRange {
}
}
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
macro_rules! impl_op {
(impl $trait:ident {$fn:ident} for $ty:ident { fn: $method:ident }) => {
impl $trait for $ty {
type Output = $ty;
fn $fn(self, rhs: $ty) -> Self::Output {
self.$method(&rhs)
}
}
impl $trait<&$ty> for $ty {
type Output = $ty;
fn $fn(self, rhs: &$ty) -> Self::Output {
self.$method(rhs)
}
}
impl $trait<&$ty> for &$ty {
type Output = $ty;
fn $fn(self, rhs: &$ty) -> Self::Output {
self.$method(rhs)
}
}
impl $trait<$ty> for &$ty {
type Output = $ty;
fn $fn(self, rhs: $ty) -> Self::Output {
self.$method(&rhs)
}
}
};
(impl $trait:ident {$fn:ident} for $ty:ident { λ: $method:expr }) => {
impl $trait for $ty {
fn $fn(&mut self, rhs: $ty) {
$method(self, &rhs)
}
}
impl $trait<&$ty> for $ty {
fn $fn(&mut self, rhs: &$ty) {
$method(self, rhs)
}
}
};
}
impl_op!(impl BitOr { bitor } for MipRange { fn: union });
impl_op!(impl BitOrAssign { bitor_assign } for MipRange { λ: |this: &mut MipRange, other: &MipRange| {*this = this.union(other)} });
impl_op!(impl BitAnd { bitand } for MipRange { fn: range });
impl_op!(impl BitAndAssign { bitand_assign } for MipRange { λ: |this: &mut MipRange, other: &MipRange| {*this = this.range(other)} });
impl IntoIterator for MipRange {
type Item = u32;
@ -1071,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<vk::Format> for FormatClass {
fn from(format: vk::Format) -> Self {
use vk::Format as F;

View file

@ -41,120 +41,142 @@ impl<T: Resource> Command for ImportResource<T> {
}
}
// pub struct CopyBuffers {
// pub src: Read<BufferSlice>,
// pub dst: Write<BufferSlice>,
// }
// pub struct CopyTextures {
// pub src: Read<TextureRegion>,
// pub dst: Write<TextureRegion>,
// }
// pub struct CopyBufferToTexture {
// pub src: Read<BufferSlice>,
// pub dst: Write<TextureRegion>,
// }
// pub struct CopyTextureToBuffer {
// pub src: Read<TextureRegion>,
// pub dst: Write<BufferSlice>,
// }
pub struct Copy<T: Resource, U: Resource> {
pub src: T,
pub dst: U,
pub trait BufferImageBufferCopyRegion {
fn regions(&self) -> &[BufferRows];
}
impl<T: Resource, U: Resource> Copy<T, U> {
fn side_effects_inner(&self, mut map: SideEffectMap) {
self.src.side_effect(map.reborrow().into_side_effect_map2(
vk::PipelineStageFlags2::TRANSFER,
impl BufferImageBufferCopyRegion for BufferRows {
fn regions(&self) -> &[BufferRows] {
core::slice::from_ref(self)
}
}
impl<T: AsRef<[BufferRows]>> BufferImageBufferCopyRegion for T {
fn regions(&self) -> &[BufferRows] {
self.as_ref()
}
}
pub trait ImageCopyRegion {
fn regions(&self) -> &[TextureRegion];
}
impl ImageCopyRegion for TextureRegion {
fn regions(&self) -> &[TextureRegion] {
core::slice::from_ref(self)
}
}
impl<T: AsRef<[TextureRegion]>> ImageCopyRegion for T {
fn regions(&self) -> &[TextureRegion] {
self.as_ref()
}
}
pub trait BufferCopyRegion {
fn regions(&self) -> &[BufferRange];
}
impl BufferCopyRegion for BufferRange {
fn regions(&self) -> &[BufferRange] {
core::slice::from_ref(self)
}
}
impl<T: AsRef<[BufferRange]>> BufferCopyRegion for T {
fn regions(&self) -> &[BufferRange] {
self.as_ref()
}
}
pub struct CopyBuffers<T: BufferCopyRegion> {
pub src: Buffer,
pub src_region: T,
pub dst: Buffer,
pub dst_region: T,
}
pub struct CopyTextures<T: ImageCopyRegion> {
pub src: Texture,
pub src_region: T,
pub dst: Texture,
pub dst_region: T,
}
pub struct CopyBufferToTexture<T: BufferImageBufferCopyRegion, U: ImageCopyRegion> {
pub src: Buffer,
pub src_region: T,
pub dst: Texture,
pub dst_region: U,
}
pub struct CopyTextureToBuffer<T: ImageCopyRegion, U: BufferImageBufferCopyRegion> {
pub src: Texture,
pub src_region: T,
pub dst: Buffer,
pub dst_region: U,
}
fn copy_side_effects_inner<T, U>(src: &T, dst: &U, mut map: SideEffectMap)
where
T: Resource,
U: Resource,
{
src.side_effect(map.reborrow().into_side_effect_map2(
vk::PipelineStageFlags2::COPY,
vk::AccessFlags2::TRANSFER_READ,
Some(vk::ImageLayout::TRANSFER_SRC_OPTIMAL),
));
self.src.side_effect(map.reborrow().into_side_effect_map2(
vk::PipelineStageFlags2::TRANSFER,
dst.side_effect(map.reborrow().into_side_effect_map2(
vk::PipelineStageFlags2::COPY,
vk::AccessFlags2::TRANSFER_WRITE,
Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
));
}
}
impl Command for Copy<BufferRowSlice, TextureRegion> {
fn side_effects(&self, map: SideEffectMap) {
self.side_effects_inner(map)
impl<T, U> Command for CopyBufferToTexture<T, U>
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),
);
let src_id = self.src.id();
for region in self.src_region.regions() {
src_map.insert_range(src_id);
}
}
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 regions = &[vk::BufferImageCopy2::default()
.buffer_offset(self.src.row.offset)
.buffer_row_length(self.src.row.row_size)
.buffer_image_height(self.src.row.row_count)
.image_offset(self.dst.range.offset())
.image_extent(self.dst.range.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);
}
}
}
impl Command for Copy<BufferRowSlices, TextureRegions> {
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::<Vec<_>>();
.collect::<Box<[_]>>();
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(&regions);
unsafe {
dev.cmd_copy_buffer_to_image2(cmd.raw(), &info);
@ -162,107 +184,6 @@ impl Command for Copy<BufferRowSlices, TextureRegions> {
}
}
impl Command for Copy<TextureRegion, BufferRowSlice> {
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<TextureRegion, TextureRegion> {
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<BufferSlice, BufferSlice> {
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<TextureRegion>,
pub clear_value: [f32; 4],

View file

@ -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,

View file

@ -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<T> {
Vec(Vec<T>),
Single(T),
Single(ManuallyDrop<T>),
}
impl<T> From<T> for VecOrSingle<T> {
fn from(value: T) -> Self {
Self::Single(ManuallyDrop::new(value))
}
}
impl<T> From<Vec<T>> for VecOrSingle<T> {
fn from(vec: Vec<T>) -> Self {
Self::Vec(vec)
}
}
impl<T> VecOrSingle<T> {
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<T> VecOrSingle<T> {
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::<T>(item).iter(),
}
}
}
#[derive(Default)]
struct SideEffects {
map: BTreeMap<(ResourceId, u32), VecOrSingle<ResourceAccess>>,
map: BTreeMap<(ResourceId, u32), VecOrSingle<SubresourceScope>>,
resources: BTreeSet<ResourceId>,
}
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<T>(&mut self, id: ResourceId, range: T)
where
(T, vk::ImageLayout): Into<ResourceRange>,
T: Into<Subresource>,
{
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<ResourceId, Vec<(ResourceAccess, SubresourceState)>> =
let mut subresource_state: BTreeMap<ResourceId, Vec<(SubresourceScope, SubresourceState)>> =
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) {

View file

@ -9,9 +9,174 @@ pub trait Resource: Sized {
fn side_effect(&self, map: SideEffectMap2);
}
mod sealed {
pub trait Texture {
fn format(&self) -> vk::Format {
todo!()
}
}
pub trait Buffer {
fn size(&self) -> u64 {
todo!()
}
}
}
#[derive(Debug, Clone, Copy)]
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 TextureView {}
impl sealed::Buffer for Buffer {}
impl sealed::Buffer for BufferSlice {}
impl Resource for Buffer {
fn id(&self) -> ResourceId {
self.0
@ -102,7 +267,7 @@ mod impls {
fn side_effect(&self, mut map: SideEffectMap2) {
for range in &self.ranges {
map.insert_range(self.id(), *range);
map.insert_range(self.id(), range.0);
}
}
}
@ -149,150 +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 {
// if `other` has access flags that aren't in `self`, we can't fold
// them.
if !self.access.contains(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;
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,
..
},
) => {
// 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?
if !aspect.contains(*aspect_b)
|| layout != layout_b
|| !mip_level.intersects(mip_level_b)
|| !array_layers.intersects(array_layers_b)
{
return false;
}
*aspect = *aspect | *aspect_b;
*mip_level = mip_level.union(mip_level_b);
*array_layers = array_layers.union(array_layers_b);
// 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;
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 {
@ -303,8 +324,6 @@ pub enum ResourceRange {
aspect: vk::ImageAspectFlags,
mip_level: MipRange,
array_layers: MipRange,
origin: (i32, i32, i32),
extent: (u32, u32, u32),
layout: vk::ImageLayout,
},
}
@ -341,28 +360,67 @@ 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,
pub origin: (i32, i32, i32),
pub extent: (u32, u32, u32),
}
impl TextureRange {
pub fn offset(&self) -> vk::Offset3D {
vk::Offset3D {
x: self.origin.0,
y: self.origin.1,
z: self.origin.2,
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct TextureLayers {
pub aspect: vk::ImageAspectFlags,
pub mip_level: u32,
pub array_layers: MipRange,
}
impl From<TextureLayers> 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,
}
}
pub fn extent(&self) -> vk::Extent3D {
vk::Extent3D {
width: self.extent.0,
height: self.extent.1,
depth: self.extent.2,
}
impl From<TextureRange> 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<TextureLayers> 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<TextureLayers> 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<TextureRange> for Subresource {
fn from(value: TextureRange) -> Self {
Subresource::Texture(value)
}
}
@ -381,8 +439,6 @@ impl From<(TextureRange, vk::ImageLayout)> for ResourceRange {
aspect: value.aspect,
mip_level: value.mip_levels,
array_layers: value.array_layers,
origin: value.origin,
extent: value.extent,
layout,
}
}
@ -405,8 +461,6 @@ impl TextureRange {
| vk::ImageAspectFlags::STENCIL,
mip_levels: MipRange::default(),
array_layers: MipRange::default(),
origin: (0, 0, 0),
extent: (u32::MAX, u32::MAX, u32::MAX),
}
}
}
@ -448,13 +502,9 @@ pub struct BufferRowSlices {
}
pub struct TextureRegion {
pub texture: Texture,
pub range: TextureRange,
}
pub struct TextureRegions {
pub texture: Texture,
pub ranges: Vec<TextureRange>,
pub range: TextureLayers,
pub offset: vk::Offset3D,
pub extent: vk::Extent3D,
}
pub struct TextureView {