516 lines
14 KiB
Rust
516 lines
14 KiB
Rust
use std::ops::Deref;
|
|
|
|
use ash::vk;
|
|
|
|
use crate::{images::MipRange, render_graph::recorder::SideEffectMap2};
|
|
|
|
pub trait Resource: Sized {
|
|
fn id(&self) -> ResourceId;
|
|
fn side_effect(&self, map: SideEffectMap2);
|
|
}
|
|
|
|
mod impls {
|
|
use super::*;
|
|
|
|
impl Resource for Buffer {
|
|
fn id(&self) -> ResourceId {
|
|
self.0
|
|
}
|
|
|
|
fn side_effect(&self, mut map: SideEffectMap2) {
|
|
map.insert_range(self.id(), BufferRange::full());
|
|
}
|
|
}
|
|
|
|
impl Resource for BufferSlice {
|
|
fn id(&self) -> ResourceId {
|
|
self.buffer.id()
|
|
}
|
|
|
|
fn side_effect(&self, mut map: SideEffectMap2) {
|
|
map.insert_range(self.id(), self.range);
|
|
}
|
|
}
|
|
impl Resource for BufferSlices {
|
|
fn id(&self) -> ResourceId {
|
|
self.buffer.id()
|
|
}
|
|
|
|
fn side_effect(&self, mut map: SideEffectMap2) {
|
|
for range in &self.ranges {
|
|
map.insert_range(self.id(), *range);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: the rows are not in bytes, so this is plain wrong. A BufferRowSlice needs to be generic over Buffer/BufferSlice, or commands taking a BufferRowSlice need to take the row parameters separately.
|
|
impl Resource for BufferRowSlice {
|
|
fn id(&self) -> ResourceId {
|
|
self.buffer.id()
|
|
}
|
|
|
|
fn side_effect(&self, mut map: SideEffectMap2) {
|
|
map.insert_range(
|
|
self.id(),
|
|
BufferRange {
|
|
offset: self.row.offset,
|
|
size: vk::WHOLE_SIZE,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
impl Resource for BufferRowSlices {
|
|
fn id(&self) -> ResourceId {
|
|
self.buffer.id()
|
|
}
|
|
|
|
fn side_effect(&self, mut map: SideEffectMap2) {
|
|
for row in &self.rows {
|
|
map.insert_range(
|
|
self.id(),
|
|
BufferRange {
|
|
offset: row.offset,
|
|
size: vk::WHOLE_SIZE,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Resource for Texture {
|
|
fn id(&self) -> ResourceId {
|
|
self.0
|
|
}
|
|
|
|
fn side_effect(&self, mut map: SideEffectMap2) {
|
|
map.insert_range(self.id(), TextureRange::full());
|
|
}
|
|
}
|
|
impl Resource for TextureRegion {
|
|
fn id(&self) -> ResourceId {
|
|
self.texture.id()
|
|
}
|
|
|
|
fn side_effect(&self, mut map: SideEffectMap2) {
|
|
map.insert_range(self.id(), self.range);
|
|
}
|
|
}
|
|
impl Resource for TextureRegions {
|
|
fn id(&self) -> ResourceId {
|
|
self.texture.id()
|
|
}
|
|
|
|
fn side_effect(&self, mut map: SideEffectMap2) {
|
|
for range in &self.ranges {
|
|
map.insert_range(self.id(), *range);
|
|
}
|
|
}
|
|
}
|
|
impl Resource for TextureView {
|
|
fn id(&self) -> ResourceId {
|
|
self.texture.id()
|
|
}
|
|
|
|
fn side_effect(&self, mut map: SideEffectMap2) {
|
|
map.insert_range(self.id(), self.range);
|
|
}
|
|
}
|
|
|
|
impl<T: Resource> Resource for Read<T> {
|
|
fn id(&self) -> ResourceId {
|
|
self.0.id()
|
|
}
|
|
|
|
fn side_effect(&self, map: SideEffectMap2) {
|
|
self.0.side_effect(map);
|
|
}
|
|
}
|
|
impl<T: Resource> Resource for Write<T> {
|
|
fn id(&self) -> ResourceId {
|
|
self.0.id()
|
|
}
|
|
|
|
fn side_effect(&self, map: SideEffectMap2) {
|
|
self.0.side_effect(map);
|
|
}
|
|
}
|
|
impl<T: Resource> Resource for ReadWrite<T> {
|
|
fn id(&self) -> ResourceId {
|
|
self.0.id()
|
|
}
|
|
|
|
fn side_effect(&self, map: SideEffectMap2) {
|
|
self.0.side_effect(map);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
#[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,
|
|
..
|
|
},
|
|
) => {
|
|
if aspect != aspect_b
|
|
|| layout != layout_b
|
|
|| !mip_level.intersects(mip_level_b)
|
|
|| !array_layers.intersects(array_layers_b)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
*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 subresources 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,
|
|
..
|
|
},
|
|
) => {
|
|
// layouts differing means we need a layout transition, which is a conflict
|
|
if layout != layout_b {
|
|
return true;
|
|
}
|
|
|
|
// 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 subresources intersect
|
|
aspect.intersects(*aspect_b)
|
|
&& (mip_level.intersects(mip_level_b)
|
|
|| array_layers.intersects(array_layers_b))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum ResourceRange {
|
|
Buffer {
|
|
offset: u64,
|
|
size: u64,
|
|
},
|
|
Texture {
|
|
aspect: vk::ImageAspectFlags,
|
|
mip_level: MipRange,
|
|
array_layers: MipRange,
|
|
origin: (i32, i32, i32),
|
|
extent: (u32, u32, u32),
|
|
layout: vk::ImageLayout,
|
|
},
|
|
}
|
|
|
|
impl Default for ResourceRange {
|
|
fn default() -> Self {
|
|
ResourceRange::Buffer {
|
|
offset: 0,
|
|
size: u64::MAX,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, Clone, Copy)]
|
|
pub struct BufferRange {
|
|
pub offset: u64,
|
|
pub size: u64,
|
|
}
|
|
|
|
impl BufferRange {
|
|
pub fn full() -> Self {
|
|
Self {
|
|
offset: 0,
|
|
size: vk::WHOLE_SIZE,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, Clone, Copy)]
|
|
pub struct BufferRows {
|
|
pub offset: u64,
|
|
pub row_count: u32,
|
|
pub row_size: u32,
|
|
}
|
|
|
|
#[derive(Debug, Default, Clone, Copy)]
|
|
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,
|
|
}
|
|
}
|
|
pub fn extent(&self) -> vk::Extent3D {
|
|
vk::Extent3D {
|
|
width: self.extent.0,
|
|
height: self.extent.1,
|
|
depth: self.extent.2,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<(BufferRange, vk::ImageLayout)> for ResourceRange {
|
|
fn from((value, _): (BufferRange, vk::ImageLayout)) -> Self {
|
|
ResourceRange::Buffer {
|
|
offset: value.offset,
|
|
size: value.size,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<(TextureRange, vk::ImageLayout)> for ResourceRange {
|
|
fn from((value, layout): (TextureRange, vk::ImageLayout)) -> Self {
|
|
ResourceRange::Texture {
|
|
aspect: value.aspect,
|
|
mip_level: value.mip_levels,
|
|
array_layers: value.array_layers,
|
|
origin: value.origin,
|
|
extent: value.extent,
|
|
layout,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<BufferRange> for ResourceRange {
|
|
fn from(value: BufferRange) -> Self {
|
|
ResourceRange::Buffer {
|
|
offset: value.offset,
|
|
size: value.size,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TextureRange {
|
|
pub fn full() -> Self {
|
|
Self {
|
|
aspect: vk::ImageAspectFlags::COLOR
|
|
| vk::ImageAspectFlags::DEPTH
|
|
| vk::ImageAspectFlags::STENCIL,
|
|
mip_levels: MipRange::default(),
|
|
array_layers: MipRange::default(),
|
|
origin: (0, 0, 0),
|
|
extent: (u32::MAX, u32::MAX, u32::MAX),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Buffer(pub ResourceId);
|
|
|
|
impl Buffer {
|
|
pub fn full_slice(self) -> BufferSlice {
|
|
BufferSlice {
|
|
buffer: self,
|
|
range: BufferRange {
|
|
offset: 0,
|
|
size: u64::MAX,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Texture(pub ResourceId);
|
|
|
|
pub struct BufferSlice {
|
|
pub buffer: Buffer,
|
|
pub range: BufferRange,
|
|
}
|
|
|
|
pub struct BufferSlices {
|
|
pub buffer: Buffer,
|
|
pub ranges: Vec<BufferRange>,
|
|
}
|
|
|
|
pub struct BufferRowSlice {
|
|
pub buffer: Buffer,
|
|
pub row: BufferRows,
|
|
}
|
|
|
|
pub struct BufferRowSlices {
|
|
pub buffer: Buffer,
|
|
pub rows: Vec<BufferRows>,
|
|
}
|
|
|
|
pub struct TextureRegion {
|
|
pub texture: Texture,
|
|
pub range: TextureRange,
|
|
}
|
|
|
|
pub struct TextureRegions {
|
|
pub texture: Texture,
|
|
pub ranges: Vec<TextureRange>,
|
|
}
|
|
|
|
pub struct TextureView {
|
|
pub texture: Texture,
|
|
pub format: vk::Format,
|
|
pub mapping: vk::ComponentMapping,
|
|
pub range: TextureRange,
|
|
}
|
|
|
|
pub struct Read<T>(pub T);
|
|
pub struct Write<T>(pub T);
|
|
pub struct ReadWrite<T>(pub T);
|
|
|
|
impl<T> Deref for Read<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl<T> Deref for Write<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl<T> Deref for ReadWrite<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
pub fn access_flag_is_read(access: vk::AccessFlags2) -> bool {
|
|
access.intersects(
|
|
vk::AccessFlags2::INDIRECT_COMMAND_READ
|
|
| vk::AccessFlags2::INDEX_READ
|
|
| vk::AccessFlags2::VERTEX_ATTRIBUTE_READ
|
|
| vk::AccessFlags2::UNIFORM_READ
|
|
| vk::AccessFlags2::INPUT_ATTACHMENT_READ
|
|
| vk::AccessFlags2::SHADER_READ
|
|
| vk::AccessFlags2::COLOR_ATTACHMENT_READ
|
|
| vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_READ
|
|
| vk::AccessFlags2::TRANSFER_READ
|
|
| vk::AccessFlags2::HOST_READ
|
|
| vk::AccessFlags2::MEMORY_READ
|
|
| vk::AccessFlags2::SHADER_SAMPLED_READ
|
|
| vk::AccessFlags2::SHADER_STORAGE_READ
|
|
| vk::AccessFlags2::VIDEO_DECODE_READ_KHR
|
|
| vk::AccessFlags2::VIDEO_ENCODE_READ_KHR
|
|
| vk::AccessFlags2::MICROMAP_READ_EXT
|
|
| vk::AccessFlags2::ACCELERATION_STRUCTURE_READ_KHR
|
|
| vk::AccessFlags2::FRAGMENT_DENSITY_MAP_READ_EXT
|
|
| vk::AccessFlags2::FRAGMENT_SHADING_RATE_ATTACHMENT_READ_KHR
|
|
| vk::AccessFlags2::CONDITIONAL_RENDERING_READ_EXT
|
|
| vk::AccessFlags2::SHADER_BINDING_TABLE_READ_KHR
|
|
| vk::AccessFlags2::DESCRIPTOR_BUFFER_READ_EXT
|
|
| vk::AccessFlags2::COLOR_ATTACHMENT_READ_NONCOHERENT_EXT
|
|
| vk::AccessFlags2::COMMAND_PREPROCESS_READ_NV
|
|
| vk::AccessFlags2::TRANSFORM_FEEDBACK_COUNTER_READ_EXT
|
|
| vk::AccessFlags2::INVOCATION_MASK_READ_HUAWEI,
|
|
)
|
|
}
|