Compare commits
No commits in common. "b8b9bf40a3dacbdac12cfd56aeff5558c4d0a47f" and "15afc00e514b916b0bda6e6e6bf6f7f098dd2576" have entirely different histories.
b8b9bf40a3
...
15afc00e51
|
|
@ -920,10 +920,6 @@ mod command_pools {
|
||||||
level: this.level,
|
level: this.level,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw(&self) -> vk::CommandBuffer {
|
|
||||||
self.handle()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CommandBufferFuture {
|
pub struct CommandBufferFuture {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,4 @@
|
||||||
use std::{
|
use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc};
|
||||||
borrow::Cow,
|
|
||||||
mem::ManuallyDrop,
|
|
||||||
ops::{Add, Sub},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
device::{
|
device::{
|
||||||
|
|
@ -38,22 +33,6 @@ pub struct ImageDesc {
|
||||||
pub alloc_scheme: AllocationStrategy,
|
pub alloc_scheme: AllocationStrategy,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageDesc {
|
|
||||||
pub fn mip_level_range(&self) -> MipRange {
|
|
||||||
MipRange {
|
|
||||||
start: 0,
|
|
||||||
end: self.mip_levels,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn layer_range(&self) -> MipRange {
|
|
||||||
MipRange {
|
|
||||||
start: 0,
|
|
||||||
end: self.array_layers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::hash::Hash for ImageDesc {
|
impl std::hash::Hash for ImageDesc {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
self.flags.hash(state);
|
self.flags.hash(state);
|
||||||
|
|
@ -490,9 +469,9 @@ impl Image {
|
||||||
vk::ImageSubresourceRange::default()
|
vk::ImageSubresourceRange::default()
|
||||||
.aspect_mask(desc.aspect)
|
.aspect_mask(desc.aspect)
|
||||||
.base_mip_level(desc.mip_range.start)
|
.base_mip_level(desc.mip_range.start)
|
||||||
.level_count(desc.mip_range.len())
|
.level_count(desc.mip_range.count())
|
||||||
.base_array_layer(desc.layer_range.start)
|
.base_array_layer(desc.layer_range.start)
|
||||||
.layer_count(desc.layer_range.len()),
|
.layer_count(desc.layer_range.count()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let device = self.image.device();
|
let device = self.image.device();
|
||||||
|
|
@ -606,185 +585,6 @@ impl ImageViewDesc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Extent {
|
|
||||||
pub width: u32,
|
|
||||||
pub height: u32,
|
|
||||||
pub depth: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Extent {
|
|
||||||
pub fn as_offset(self) -> Offset {
|
|
||||||
Offset {
|
|
||||||
x: self.width as i32,
|
|
||||||
y: self.height as i32,
|
|
||||||
z: self.depth as i32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_offset(offset: Offset) -> Self {
|
|
||||||
Self {
|
|
||||||
width: offset.x as u32,
|
|
||||||
height: offset.y as u32,
|
|
||||||
depth: offset.z as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn min(self, other: Self) -> Self {
|
|
||||||
Self {
|
|
||||||
width: self.width.min(other.width),
|
|
||||||
height: self.height.min(other.height),
|
|
||||||
depth: self.depth.min(other.depth),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn max(self, other: Self) -> Self {
|
|
||||||
Self {
|
|
||||||
width: self.width.max(other.width),
|
|
||||||
height: self.height.max(other.height),
|
|
||||||
depth: self.depth.max(other.depth),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<Offset> for Extent {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn sub(self, rhs: Offset) -> Self::Output {
|
|
||||||
Self {
|
|
||||||
width: self.width.saturating_sub(rhs.x as u32),
|
|
||||||
height: self.height.saturating_sub(rhs.y as u32),
|
|
||||||
depth: self.depth.saturating_sub(rhs.z as u32),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<Offset> for Extent {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn add(self, rhs: Offset) -> Self::Output {
|
|
||||||
Self {
|
|
||||||
width: self.width.saturating_add(rhs.x as u32),
|
|
||||||
height: self.height.saturating_add(rhs.y as u32),
|
|
||||||
depth: self.depth.saturating_add(rhs.z as u32),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Extent> for vk::Extent3D {
|
|
||||||
fn from(extent: Extent) -> Self {
|
|
||||||
Self {
|
|
||||||
width: extent.width,
|
|
||||||
height: extent.height,
|
|
||||||
depth: extent.depth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Extent> for (u32, u32, u32) {
|
|
||||||
fn from(extent: Extent) -> Self {
|
|
||||||
(extent.width, extent.height, extent.depth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<vk::Extent3D> for Extent {
|
|
||||||
fn from(extent: vk::Extent3D) -> Self {
|
|
||||||
Self {
|
|
||||||
width: extent.width,
|
|
||||||
height: extent.height,
|
|
||||||
depth: extent.depth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(u32, u32, u32)> for Extent {
|
|
||||||
fn from((width, height, depth): (u32, u32, u32)) -> Self {
|
|
||||||
Self {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
depth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Offset {
|
|
||||||
pub x: i32,
|
|
||||||
pub y: i32,
|
|
||||||
pub z: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Offset {
|
|
||||||
pub fn min(self, other: Self) -> Self {
|
|
||||||
Self {
|
|
||||||
x: self.x.min(other.x),
|
|
||||||
y: self.y.min(other.y),
|
|
||||||
z: self.z.min(other.z),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn max(self, other: Self) -> Self {
|
|
||||||
Self {
|
|
||||||
x: self.x.max(other.x),
|
|
||||||
y: self.y.max(other.y),
|
|
||||||
z: self.z.max(other.z),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add for Offset {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
|
||||||
Self {
|
|
||||||
x: self.x.saturating_add(rhs.x),
|
|
||||||
y: self.y.saturating_add(rhs.y),
|
|
||||||
z: self.z.saturating_add(rhs.z),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub for Offset {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
|
||||||
Self {
|
|
||||||
x: self.x.saturating_sub(rhs.x),
|
|
||||||
y: self.y.saturating_sub(rhs.y),
|
|
||||||
z: self.z.saturating_sub(rhs.z),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Offset> for vk::Offset3D {
|
|
||||||
fn from(offset: Offset) -> Self {
|
|
||||||
Self {
|
|
||||||
x: offset.x,
|
|
||||||
y: offset.y,
|
|
||||||
z: offset.z,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Offset> for (i32, i32, i32) {
|
|
||||||
fn from(offset: Offset) -> Self {
|
|
||||||
(offset.x, offset.y, offset.z)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<vk::Offset3D> for Offset {
|
|
||||||
fn from(offset: vk::Offset3D) -> Self {
|
|
||||||
Self {
|
|
||||||
x: offset.x,
|
|
||||||
y: offset.y,
|
|
||||||
z: offset.z,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(i32, i32, i32)> for Offset {
|
|
||||||
fn from((x, y, z): (i32, i32, i32)) -> Self {
|
|
||||||
Self { x, y, z }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct MipRange {
|
pub struct MipRange {
|
||||||
start: u32,
|
start: u32,
|
||||||
|
|
@ -801,57 +601,13 @@ impl Default for MipRange {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MipRange {
|
impl MipRange {
|
||||||
pub fn len(&self) -> u32 {
|
pub fn count(&self) -> u32 {
|
||||||
self.end - self.start
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(&self) -> u32 {
|
|
||||||
self.start
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end(&self) -> u32 {
|
|
||||||
self.end
|
self.end
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range(&self, other: &Self) -> Self {
|
|
||||||
Self {
|
|
||||||
start: self.start.max(other.start).min(self.end),
|
|
||||||
end: self.end.min(other.end).max(self.start),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> core::range::RangeIter<u32> {
|
|
||||||
self.into_iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fits_in(&self, total_mips: u32) -> bool {
|
pub fn fits_in(&self, total_mips: u32) -> bool {
|
||||||
self.start < total_mips && (self.end == vk::REMAINING_MIP_LEVELS || self.end <= total_mips)
|
self.start < total_mips && (self.end == vk::REMAINING_MIP_LEVELS || self.end <= total_mips)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn intersects(&self, other: &Self) -> bool {
|
|
||||||
self.start < other.end && self.end > other.start
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn union(&self, other: &Self) -> Self {
|
|
||||||
Self {
|
|
||||||
start: self.start.min(other.start),
|
|
||||||
end: self.end.max(other.end),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoIterator for MipRange {
|
|
||||||
type Item = u32;
|
|
||||||
|
|
||||||
type IntoIter = core::range::RangeIter<u32>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
core::range::Range {
|
|
||||||
start: self.start,
|
|
||||||
end: self.end,
|
|
||||||
}
|
|
||||||
.into_iter()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: core::ops::RangeBounds<u32>> From<R> for MipRange {
|
impl<R: core::ops::RangeBounds<u32>> From<R> for MipRange {
|
||||||
|
|
@ -1442,15 +1198,3 @@ impl From<vk::Format> for FormatClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_to_aspect_mask(format: vk::Format) -> vk::ImageAspectFlags {
|
|
||||||
use vk::Format as F;
|
|
||||||
match format {
|
|
||||||
F::D16_UNORM | F::X8_D24_UNORM_PACK32 | F::D32_SFLOAT => vk::ImageAspectFlags::DEPTH,
|
|
||||||
F::D16_UNORM_S8_UINT | F::D24_UNORM_S8_UINT | F::D32_SFLOAT_S8_UINT => {
|
|
||||||
vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL
|
|
||||||
}
|
|
||||||
F::S8_UINT => vk::ImageAspectFlags::STENCIL,
|
|
||||||
_ => vk::ImageAspectFlags::COLOR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
negative_impls,
|
negative_impls,
|
||||||
map_try_insert,
|
map_try_insert,
|
||||||
debug_closure_helpers,
|
debug_closure_helpers,
|
||||||
core_io_borrowed_buf,
|
|
||||||
slice_partition_dedup
|
slice_partition_dedup
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,6 @@ pub struct ShaderStageDesc<'a> {
|
||||||
// specialization: Option<vk::SpecializationInfo>
|
// specialization: Option<vk::SpecializationInfo>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShaderStageDesc<'_> {
|
|
||||||
fn as_create_info(&'_ self) -> vk::PipelineShaderStageCreateInfo<'_> {
|
|
||||||
vk::PipelineShaderStageCreateInfo::default()
|
|
||||||
.module(self.module.raw())
|
|
||||||
.flags(self.flags)
|
|
||||||
.stage(self.stage)
|
|
||||||
.name(&self.entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct DescriptorSetLayoutBindingDesc {
|
pub struct DescriptorSetLayoutBindingDesc {
|
||||||
pub binding: u32,
|
pub binding: u32,
|
||||||
|
|
@ -199,7 +189,7 @@ pub struct GraphicsPipelineDesc<'a> {
|
||||||
pub name: Option<Cow<'static, str>>,
|
pub name: Option<Cow<'static, str>>,
|
||||||
pub shader_stages: &'a [ShaderStageDesc<'a>],
|
pub shader_stages: &'a [ShaderStageDesc<'a>],
|
||||||
pub render_pass: Option<vk::RenderPass>,
|
pub render_pass: Option<vk::RenderPass>,
|
||||||
pub layout: Arc<PipelineLayout>,
|
pub layout: &'a PipelineLayout,
|
||||||
pub subpass: Option<u32>,
|
pub subpass: Option<u32>,
|
||||||
pub base_pipeline: Option<Arc<Pipeline>>,
|
pub base_pipeline: Option<Arc<Pipeline>>,
|
||||||
|
|
||||||
|
|
@ -554,7 +544,6 @@ impl ShaderModule {
|
||||||
pub struct Pipeline {
|
pub struct Pipeline {
|
||||||
pipeline: DeviceObject<vk::Pipeline>,
|
pipeline: DeviceObject<vk::Pipeline>,
|
||||||
bind_point: vk::PipelineBindPoint,
|
bind_point: vk::PipelineBindPoint,
|
||||||
layout: Arc<PipelineLayout>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Pipeline {
|
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Pipeline {
|
||||||
|
|
@ -565,6 +554,16 @@ impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Pipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ShaderStageDesc<'_> {
|
||||||
|
fn as_create_info(&'_ self) -> vk::PipelineShaderStageCreateInfo<'_> {
|
||||||
|
vk::PipelineShaderStageCreateInfo::default()
|
||||||
|
.module(self.module.raw())
|
||||||
|
.flags(self.flags)
|
||||||
|
.stage(self.stage)
|
||||||
|
.name(&self.entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
pub fn new_compute(device: Device, desc: ComputePipelineDesc) -> crate::Result<Self> {
|
pub fn new_compute(device: Device, desc: ComputePipelineDesc) -> crate::Result<Self> {
|
||||||
let info = &vk::ComputePipelineCreateInfo::default()
|
let info = &vk::ComputePipelineCreateInfo::default()
|
||||||
|
|
@ -594,7 +593,6 @@ impl Pipeline {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
pipeline: DeviceObject::new_debug_named(device, pipeline, desc.name),
|
pipeline: DeviceObject::new_debug_named(device, pipeline, desc.name),
|
||||||
bind_point: vk::PipelineBindPoint::COMPUTE,
|
bind_point: vk::PipelineBindPoint::COMPUTE,
|
||||||
layout: desc.layout,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -763,7 +761,6 @@ impl Pipeline {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
pipeline: DeviceObject::new_debug_named(device, pipeline, desc.name),
|
pipeline: DeviceObject::new_debug_named(device, pipeline, desc.name),
|
||||||
bind_point: vk::PipelineBindPoint::GRAPHICS,
|
bind_point: vk::PipelineBindPoint::GRAPHICS,
|
||||||
layout: desc.layout,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
//! This module defines the commands that can be recorded in the render
|
//! This module defines the commands that can be recorded in the render
|
||||||
//! graph.
|
//! graph.
|
||||||
//! Commands can be allocated, and then recorded into a command
|
//! Commands can be allocated, and then recorded into a command
|
||||||
|
|
@ -12,165 +13,78 @@
|
||||||
//! recorded sequentially, in the order they were inserted into a command
|
//! recorded sequentially, in the order they were inserted into a command
|
||||||
//! buffer. During command insertion
|
//! buffer. During command insertion
|
||||||
|
|
||||||
use crate::{
|
use crate::render_graph::recorder::{CommandMeta, CommandRecorder, SideEffectMap};
|
||||||
device::DeviceOwned,
|
|
||||||
render_graph::recorder::{CommandRecorder, SideEffectMap},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::resources::*;
|
use super::resources::*;
|
||||||
|
|
||||||
// pub struct CopyBuffers {
|
pub struct CopyBuffers {
|
||||||
// pub src: Read<BufferSlice>,
|
pub src: Read<BufferSlice>,
|
||||||
// pub dst: Write<BufferSlice>,
|
pub dst: Write<BufferSlice>,
|
||||||
// }
|
}
|
||||||
|
|
||||||
// pub struct CopyTextures {
|
pub struct CopyTextures {
|
||||||
// pub src: Read<TextureRegion>,
|
pub src: Read<TextureRegion>,
|
||||||
// pub dst: Write<TextureRegion>,
|
pub dst: Write<TextureRegion>,
|
||||||
// }
|
}
|
||||||
|
|
||||||
// pub struct CopyBufferToTexture {
|
pub struct CopyBufferToTexture {
|
||||||
// pub src: Read<BufferSlice>,
|
pub src: Read<BufferSlice>,
|
||||||
// pub dst: Write<TextureRegion>,
|
pub dst: Write<TextureRegion>,
|
||||||
// }
|
}
|
||||||
|
|
||||||
// pub struct CopyTextureToBuffer {
|
pub struct CopyTextureToBuffer {
|
||||||
// pub src: Read<TextureRegion>,
|
pub src: Read<TextureRegion>,
|
||||||
// pub dst: Write<BufferSlice>,
|
pub dst: Write<BufferSlice>,
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub struct Copy<T: Resource, U: Resource> {
|
pub struct Copy<T: Resource, U: Resource> {
|
||||||
pub src: T,
|
pub src: Read<T>,
|
||||||
pub dst: U,
|
pub dst: Write<U>,
|
||||||
}
|
}
|
||||||
impl<T: Resource, U: Resource> Copy<T, U> {
|
impl<T: Resource, U: Resource> Copy<T, U> {
|
||||||
fn side_effects_inner(&self, mut map: SideEffectMap) {
|
fn side_effects_inner(&self, mut map: SideEffectMap) {
|
||||||
self.src.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
self.src.id(),
|
||||||
vk::AccessFlags2::TRANSFER_READ,
|
ResourceAccess {
|
||||||
Some(vk::ImageLayout::TRANSFER_SRC_OPTIMAL),
|
range: self.src.into_access(),
|
||||||
|
access: vk::AccessFlags2::TRANSFER_READ,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
self.dst.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
self.dst.id(),
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
ResourceAccess {
|
||||||
Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
range: self.dst.into_access(),
|
||||||
|
access: vk::AccessFlags2::TRANSFER_WRITE,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command for Copy<BufferRowSlice, TextureRegion> {
|
impl Command for Copy<BufferSlice, TextureRegion> {
|
||||||
fn side_effects(&self, map: SideEffectMap) {
|
fn side_effects(&self, map: SideEffectMap) {
|
||||||
self.side_effects_inner(map)
|
self.side_effects_inner(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(self, recorder: &mut CommandRecorder) {
|
fn apply(self, _recorder: &mut CommandRecorder) {
|
||||||
let cmd = recorder.cmd_buffer();
|
// CopyBufferToTexture {src, dst}.apply(recorder)
|
||||||
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<TextureRegion, BufferSlice> {
|
||||||
|
|
||||||
impl Command for Copy<BufferRowSlices, TextureRegions> {
|
|
||||||
fn side_effects(&self, map: SideEffectMap) {
|
fn side_effects(&self, map: SideEffectMap) {
|
||||||
self.side_effects_inner(map)
|
self.side_effects_inner(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(self, recorder: &mut CommandRecorder) {
|
fn apply(self, _recorder: &mut CommandRecorder) {
|
||||||
let cmd = recorder.cmd_buffer();
|
// CopyTextureToBuffer {src, dst}.apply(recorder)
|
||||||
let dev = &cmd.device().raw;
|
|
||||||
|
|
||||||
let regions = &self
|
|
||||||
.src
|
|
||||||
.rows
|
|
||||||
.iter()
|
|
||||||
.zip(self.dst.ranges.iter())
|
|
||||||
.map(|(src_row, dst_range)| {
|
|
||||||
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())
|
|
||||||
.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()),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
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<TextureRegion, BufferRowSlice> {
|
fn get_meta(&self) -> CommandMeta {
|
||||||
fn side_effects(&self, map: SideEffectMap) {
|
CommandMeta {
|
||||||
self.side_effects_inner(map)
|
apply_command: |recorder, command_ptr, cursor| {
|
||||||
}
|
let command = unsafe { command_ptr.cast::<Self>().read() };
|
||||||
|
*cursor += std::mem::size_of::<Self>();
|
||||||
fn apply(self, recorder: &mut CommandRecorder) {
|
command.apply(recorder);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -179,39 +93,8 @@ impl Command for Copy<TextureRegion, TextureRegion> {
|
||||||
self.side_effects_inner(map)
|
self.side_effects_inner(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(self, recorder: &mut CommandRecorder) {
|
fn apply(self, _recorder: &mut CommandRecorder) {
|
||||||
let cmd = recorder.cmd_buffer();
|
// CopyTextures {src, dst}.apply(recorder)
|
||||||
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> {
|
impl Command for Copy<BufferSlice, BufferSlice> {
|
||||||
|
|
@ -219,25 +102,8 @@ impl Command for Copy<BufferSlice, BufferSlice> {
|
||||||
self.side_effects_inner(map)
|
self.side_effects_inner(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(self, recorder: &mut CommandRecorder) {
|
fn apply(self, _recorder: &mut CommandRecorder) {
|
||||||
let cmd = recorder.cmd_buffer();
|
// CopyBuffers {src, dst}.apply(recorder)
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,10 +114,12 @@ pub struct ClearTexture {
|
||||||
|
|
||||||
impl Command for ClearTexture {
|
impl Command for ClearTexture {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
self.dst.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
self.dst.id(),
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
ResourceAccess {
|
||||||
Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
range: self.dst.into_access(),
|
||||||
|
access: vk::AccessFlags2::TRANSFER_WRITE,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,42 +133,53 @@ pub struct UpdateBuffer {
|
||||||
|
|
||||||
impl Command for UpdateBuffer {
|
impl Command for UpdateBuffer {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
self.dst
|
map.insert(
|
||||||
.side_effect(map.reborrow(), vk::AccessFlags2::TRANSFER_WRITE, None);
|
self.dst.id(),
|
||||||
|
ResourceAccess {
|
||||||
|
range: self.dst.into_access(),
|
||||||
|
access: vk::AccessFlags2::TRANSFER_WRITE,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BeginRendering {
|
pub struct RenderPass {
|
||||||
pub color_attachments: Vec<TextureRegion>,
|
pub color_attachments: Vec<Write<TextureRegion>>,
|
||||||
pub depth_attachment: Option<TextureRegion>,
|
pub depth_attachment: Option<Write<TextureRegion>>,
|
||||||
pub stencil_attachment: Option<TextureRegion>,
|
pub stencil_attachment: Option<Write<TextureRegion>>,
|
||||||
pub area: (u32, u32),
|
pub area: (u32, u32),
|
||||||
pub layers: u32,
|
pub layers: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command for BeginRendering {
|
impl Command for RenderPass {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
for attachment in &self.color_attachments {
|
for attachment in &self.color_attachments {
|
||||||
attachment.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
attachment.id(),
|
||||||
vk::AccessFlags2::COLOR_ATTACHMENT_WRITE,
|
ResourceAccess {
|
||||||
Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL),
|
range: attachment.into_access(),
|
||||||
|
access: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(depth) = &self.depth_attachment {
|
if let Some(depth) = &self.depth_attachment {
|
||||||
depth.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
depth.id(),
|
||||||
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE,
|
ResourceAccess {
|
||||||
Some(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL),
|
range: depth.into_access(),
|
||||||
|
access: vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(stencil) = &self.stencil_attachment {
|
if let Some(stencil) = &self.stencil_attachment {
|
||||||
stencil.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
stencil.id(),
|
||||||
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE,
|
ResourceAccess {
|
||||||
Some(vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL),
|
range: stencil.into_access(),
|
||||||
|
access: vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -308,17 +187,7 @@ impl Command for BeginRendering {
|
||||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EndRendering;
|
pub struct BindPipeline(pub Pipeline);
|
||||||
|
|
||||||
impl Command for EndRendering {
|
|
||||||
fn side_effects(&self, _map: SideEffectMap) {
|
|
||||||
// No resource access, but ends the render pass
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BindPipeline;
|
|
||||||
|
|
||||||
impl Command for BindPipeline {
|
impl Command for BindPipeline {
|
||||||
fn side_effects(&self, _map: SideEffectMap) {
|
fn side_effects(&self, _map: SideEffectMap) {
|
||||||
|
|
@ -329,16 +198,18 @@ impl Command for BindPipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BindVertexBuffers {
|
pub struct BindVertexBuffers {
|
||||||
pub buffers: Vec<BufferSlice>,
|
pub buffers: Vec<Read<BufferSlice>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command for BindVertexBuffers {
|
impl Command for BindVertexBuffers {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
for buffer in &self.buffers {
|
for buffer in &self.buffers {
|
||||||
buffer.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
buffer.id(),
|
||||||
vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
ResourceAccess {
|
||||||
None,
|
range: buffer.into_access(),
|
||||||
|
access: vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -346,18 +217,25 @@ impl Command for BindVertexBuffers {
|
||||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BindIndexBuffer(pub BufferSlice, pub IndexFormat);
|
pub struct BindIndexBuffer(pub Read<BufferSlice>, pub IndexFormat);
|
||||||
|
|
||||||
impl Command for BindIndexBuffer {
|
impl Command for BindIndexBuffer {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
self.0
|
map.insert(
|
||||||
.side_effect(map.reborrow(), vk::AccessFlags2::INDEX_READ, None);
|
self.0.id(),
|
||||||
|
ResourceAccess {
|
||||||
|
range: self.0.into_access(),
|
||||||
|
access: vk::AccessFlags2::INDEX_READ,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BindDescriptorSets;
|
pub struct BindDescriptorSets {
|
||||||
|
pub sets: Vec<DescriptorSet>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Command for BindDescriptorSets {
|
impl Command for BindDescriptorSets {
|
||||||
fn side_effects(&self, _map: SideEffectMap) {
|
fn side_effects(&self, _map: SideEffectMap) {
|
||||||
|
|
@ -399,7 +277,9 @@ impl Command for SetScissor {
|
||||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PushConstants;
|
pub struct PushConstants {
|
||||||
|
pub data: Box<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Command for PushConstants {
|
impl Command for PushConstants {
|
||||||
fn side_effects(&self, _map: SideEffectMap) {
|
fn side_effects(&self, _map: SideEffectMap) {
|
||||||
|
|
@ -425,15 +305,11 @@ pub struct DrawIndexedData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DrawIndirectData {
|
pub struct DrawIndirectData {
|
||||||
pub indirect_buffer: BufferSlice,
|
pub indirect_buffer: Read<BufferSlice>,
|
||||||
pub count: u32,
|
|
||||||
pub stride: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DrawIndexedIndirectData {
|
pub struct DrawIndexedIndirectData {
|
||||||
pub indirect_buffer: BufferSlice,
|
pub indirect_buffer: Read<BufferSlice>,
|
||||||
pub count: u32,
|
|
||||||
pub stride: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|
@ -445,25 +321,39 @@ pub enum IndexFormat {
|
||||||
|
|
||||||
pub struct Draw<T: IsDrawData> {
|
pub struct Draw<T: IsDrawData> {
|
||||||
pub data: T,
|
pub data: T,
|
||||||
pub vertex_buffers: Vec<BufferSlice>,
|
pub vertex_buffers: Vec<Read<BufferSlice>>,
|
||||||
pub index_buffer: Option<(BufferSlice, IndexFormat)>,
|
pub index_buffer: Option<(Read<BufferSlice>, IndexFormat)>,
|
||||||
|
pub count_buffer: Option<Read<BufferSlice>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: IsDrawData> Command for Draw<T> {
|
impl<T: IsDrawData> Command for Draw<T> {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
self.data.side_effects(map.reborrow());
|
self.data.side_effects(map.reborrow());
|
||||||
for vertex_buffer in &self.vertex_buffers {
|
for vertex_buffer in &self.vertex_buffers {
|
||||||
vertex_buffer.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
vertex_buffer.id(),
|
||||||
vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
ResourceAccess {
|
||||||
None,
|
range: vertex_buffer.into_access(),
|
||||||
|
access: vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some((index_buffer, _)) = &self.index_buffer {
|
if let Some((index_buffer, _)) = &self.index_buffer {
|
||||||
index_buffer.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
index_buffer.id(),
|
||||||
vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
ResourceAccess {
|
||||||
None,
|
range: index_buffer.into_access(),
|
||||||
|
access: vk::AccessFlags2::INDEX_READ,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(count_buffer) = &self.count_buffer {
|
||||||
|
map.insert(
|
||||||
|
count_buffer.id(),
|
||||||
|
ResourceAccess {
|
||||||
|
range: count_buffer.into_access(),
|
||||||
|
access: vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -471,19 +361,18 @@ impl<T: IsDrawData> Command for Draw<T> {
|
||||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PipelineBarrier;
|
|
||||||
|
|
||||||
impl Command for PipelineBarrier {
|
|
||||||
fn side_effects(&self, _map: SideEffectMap) {}
|
|
||||||
|
|
||||||
fn apply(self, _recorder: &mut CommandRecorder) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Command: Sized {
|
pub trait Command: Sized {
|
||||||
fn side_effects(&self, map: SideEffectMap);
|
fn side_effects(&self, map: SideEffectMap);
|
||||||
fn apply(self, recorder: &mut CommandRecorder);
|
fn apply(self, recorder: &mut CommandRecorder);
|
||||||
|
fn get_meta(&self) -> CommandMeta {
|
||||||
|
CommandMeta {
|
||||||
|
apply_command: |recorder, command_ptr, cursor| {
|
||||||
|
let command = unsafe { command_ptr.cast::<Self>().read() };
|
||||||
|
*cursor += std::mem::size_of::<Self>();
|
||||||
|
command.apply(recorder);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod sealed {
|
mod sealed {
|
||||||
|
|
@ -494,31 +383,34 @@ mod sealed {
|
||||||
pub trait OutsideRenderPass {}
|
pub trait OutsideRenderPass {}
|
||||||
}
|
}
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
pub(super) use sealed::*;
|
use sealed::*;
|
||||||
|
|
||||||
impl IsDrawData for DrawData {}
|
impl IsDrawData for DrawData {}
|
||||||
impl IsDrawData for DrawIndexedData {}
|
impl IsDrawData for DrawIndexedData {}
|
||||||
impl IsDrawData for DrawIndirectData {
|
impl IsDrawData for DrawIndirectData {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
self.indirect_buffer.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
self.indirect_buffer.id(),
|
||||||
vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
ResourceAccess {
|
||||||
None,
|
range: self.indirect_buffer.into_access(),
|
||||||
|
access: vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl IsDrawData for DrawIndexedIndirectData {
|
impl IsDrawData for DrawIndexedIndirectData {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
self.indirect_buffer.side_effect(
|
map.insert(
|
||||||
map.reborrow(),
|
self.indirect_buffer.id(),
|
||||||
vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
ResourceAccess {
|
||||||
None,
|
range: self.indirect_buffer.into_access(),
|
||||||
|
access: vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutsideRenderPass for BeginRendering {}
|
impl OutsideRenderPass for RenderPass {}
|
||||||
impl InsideRenderPass for EndRendering {}
|
|
||||||
impl<T: Resource, U: Resource> OutsideRenderPass for Copy<T, U> {}
|
impl<T: Resource, U: Resource> OutsideRenderPass for Copy<T, U> {}
|
||||||
impl OutsideRenderPass for ClearTexture {}
|
impl OutsideRenderPass for ClearTexture {}
|
||||||
impl OutsideRenderPass for UpdateBuffer {}
|
impl OutsideRenderPass for UpdateBuffer {}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
mod legacy;
|
mod legacy;
|
||||||
pub use legacy::*;
|
pub use legacy::*;
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod graph_builder;
|
|
||||||
mod recorder;
|
mod recorder;
|
||||||
mod resources;
|
mod resources;
|
||||||
|
|
|
||||||
|
|
@ -1,96 +1,27 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, BTreeSet},
|
collections::BTreeMap,
|
||||||
mem::{MaybeUninit, size_of},
|
mem::{MaybeUninit, size_of},
|
||||||
ptr::NonNull,
|
ptr::NonNull,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ash::vk;
|
use crate::render_graph::{
|
||||||
|
commands::Command,
|
||||||
use crate::{
|
resources::{ResourceAccess, ResourceId},
|
||||||
commands::CommandBuffer,
|
|
||||||
images::ImageDesc,
|
|
||||||
render_graph::{
|
|
||||||
commands::{Command, InsideRenderPass, OutsideRenderPass},
|
|
||||||
resources::{ResourceAccess, ResourceId, TextureRegion, Write},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct CommandRecorder;
|
pub struct CommandRecorder;
|
||||||
|
pub struct RenderPassRecorder;
|
||||||
|
|
||||||
impl CommandRecorder {
|
pub struct CommandMeta {
|
||||||
pub fn cmd_buffer(&self) -> &CommandBuffer {
|
pub(crate) apply_command: unsafe fn(&mut CommandRecorder, NonNull<()>, cursor: &mut usize),
|
||||||
unimplemented!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_buffer_handle(&self, _id: ResourceId) -> vk::Buffer {
|
pub struct SideEffectMap<'a>(&'a mut BTreeMap<(ResourceId, u32), ResourceAccess>, u32);
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_image_desc(&self, _id: ResourceId) -> &ImageDesc {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_image_handle(&self, _id: ResourceId) -> vk::Image {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_image_layout(&self, _id: ResourceId) -> vk::ImageLayout {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_image_view_handle(&self, _id: ResourceId) -> vk::ImageView {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CommandMeta {
|
|
||||||
apply_command: unsafe fn(&mut CommandRecorder, NonNull<()>, cursor: &mut usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
enum VecOrSingle<T> {
|
|
||||||
Vec(Vec<T>),
|
|
||||||
Single(T),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> VecOrSingle<T> {
|
|
||||||
fn push(&mut self, value: T)
|
|
||||||
where
|
|
||||||
T: Default,
|
|
||||||
{
|
|
||||||
match self {
|
|
||||||
VecOrSingle::Vec(vec) => vec.push(value),
|
|
||||||
VecOrSingle::Single(existing) => {
|
|
||||||
let existing = std::mem::take(existing);
|
|
||||||
*self = VecOrSingle::Vec(vec![existing, value]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct SideEffects {
|
|
||||||
map: BTreeMap<(ResourceId, u32), VecOrSingle<ResourceAccess>>,
|
|
||||||
resources: BTreeSet<ResourceId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SideEffectMap<'a>(&'a mut SideEffects, u32);
|
|
||||||
|
|
||||||
impl<'a> SideEffectMap<'a> {
|
impl<'a> SideEffectMap<'a> {
|
||||||
pub fn insert(&mut self, id: ResourceId, access: ResourceAccess) {
|
pub fn insert(&mut self, id: ResourceId, access: ResourceAccess) {
|
||||||
self.0.resources.insert(id);
|
self.0.insert((id, self.1), access);
|
||||||
self.0
|
|
||||||
.map
|
|
||||||
.entry((id, self.1))
|
|
||||||
.and_modify(|entry| entry.push(access))
|
|
||||||
.or_insert_with(|| VecOrSingle::Single(access));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reborrow(&mut self) -> SideEffectMap<'_> {
|
pub fn reborrow(&mut self) -> SideEffectMap<'_> {
|
||||||
SideEffectMap(self.0, self.1)
|
SideEffectMap(self.0, self.1)
|
||||||
}
|
}
|
||||||
|
|
@ -101,23 +32,7 @@ pub struct CommandList {
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
// each command that accesses a resource adds an entry to this map (id, command_index) -> access
|
// each command that accesses a resource adds an entry to this map (id, command_index) -> access
|
||||||
// during command recording, we fold accesses to the same resource as commands are recorded, and when a command reads a resource, we check for any previous writes to be made available/visible
|
// during command recording, we fold accesses to the same resource as commands are recorded, and when a command reads a resource, we check for any previous writes to be made available/visible
|
||||||
side_effects: SideEffects,
|
side_effects: BTreeMap<(ResourceId, u32), ResourceAccess>,
|
||||||
num_commands: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub struct RenderPass<'a> {
|
|
||||||
cmd_list: &'a mut CommandList,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RenderPass<'a> {
|
|
||||||
pub fn finalise(self) {
|
|
||||||
self.cmd_list.push_inner(super::commands::EndRendering);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push<C: Command + InsideRenderPass>(&mut self, command: C) {
|
|
||||||
self.cmd_list.push_inner(command);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandList {
|
impl CommandList {
|
||||||
|
|
@ -125,43 +40,16 @@ impl CommandList {
|
||||||
Self {
|
Self {
|
||||||
command_bytes: Vec::new(),
|
command_bytes: Vec::new(),
|
||||||
cursor: 0,
|
cursor: 0,
|
||||||
side_effects: SideEffects::default(),
|
side_effects: BTreeMap::new(),
|
||||||
num_commands: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_rendering(
|
pub fn push<C: Command>(&mut self, command: C) {
|
||||||
&mut self,
|
|
||||||
color_attachments: Vec<TextureRegion>,
|
|
||||||
depth_attachment: Option<TextureRegion>,
|
|
||||||
stencil_attachment: Option<TextureRegion>,
|
|
||||||
area: (u32, u32),
|
|
||||||
layers: u32,
|
|
||||||
) -> RenderPass<'_> {
|
|
||||||
self.push(super::commands::BeginRendering {
|
|
||||||
color_attachments,
|
|
||||||
depth_attachment,
|
|
||||||
stencil_attachment,
|
|
||||||
area,
|
|
||||||
layers,
|
|
||||||
});
|
|
||||||
RenderPass { cmd_list: self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push<C: Command + OutsideRenderPass>(&mut self, command: C) {
|
|
||||||
self.push_inner(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_inner<C: Command>(&mut self, command: C) {
|
|
||||||
#[repr(C, packed)]
|
|
||||||
struct Packed<C> {
|
struct Packed<C> {
|
||||||
meta: CommandMeta,
|
meta: CommandMeta,
|
||||||
command: C,
|
command: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
command.side_effects(SideEffectMap(&mut self.side_effects, self.num_commands));
|
|
||||||
self.num_commands += 1;
|
|
||||||
|
|
||||||
let packed = Packed {
|
let packed = Packed {
|
||||||
meta: CommandMeta {
|
meta: CommandMeta {
|
||||||
apply_command: |recorder, command_ptr, cursor| {
|
apply_command: |recorder, command_ptr, cursor| {
|
||||||
|
|
@ -180,139 +68,4 @@ impl CommandList {
|
||||||
bytes.cast::<Packed<C>>().write_unaligned(packed);
|
bytes.cast::<Packed<C>>().write_unaligned(packed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_side_effects(&self) -> Vec<(u32, super::commands::PipelineBarrier)> {
|
|
||||||
// Buffer accesses can be granularly tracked by their offset and size;
|
|
||||||
// Image accesses are tracked by their subresource range, which means all regions of the same mip/array layer are considered the same subresource for the purposes of ImageMemoryBarriers
|
|
||||||
|
|
||||||
// But, for simplicity sake, and since buffers can already be created
|
|
||||||
// with specific sizes from device memory ranges, and aliased as
|
|
||||||
// required, we can just fold all buffer accesses together and collapse
|
|
||||||
// the size and offsets to min([offset, ..]), max([offset + size, ..])
|
|
||||||
// for all accesses to the same buffer.
|
|
||||||
|
|
||||||
// Generally, we want to be as granular as possible with barriers when
|
|
||||||
// it makes sense, but reduce the number of calls to
|
|
||||||
// vkCmdPipelineBarrier.
|
|
||||||
|
|
||||||
// Image accesses cannot be folded if they differ in: layout, access type, access stage/mask.
|
|
||||||
|
|
||||||
struct SubresourceState {
|
|
||||||
command_index: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Barrier {
|
|
||||||
resource_id: ResourceId,
|
|
||||||
command_index: u32,
|
|
||||||
from: ResourceAccess,
|
|
||||||
to: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
// map to keep track of the current state of each subresource
|
|
||||||
let mut subresource_state: BTreeMap<ResourceId, Vec<(ResourceAccess, SubresourceState)>> =
|
|
||||||
Default::default();
|
|
||||||
|
|
||||||
let mut barriers = Vec::new();
|
|
||||||
|
|
||||||
for id in &self.side_effects.resources {
|
|
||||||
// check if this subresource has been accessed before, and if we can
|
|
||||||
// fold this access with the previous one
|
|
||||||
let entry = subresource_state.entry(*id).or_default();
|
|
||||||
|
|
||||||
for (command_index, accesses) in self
|
|
||||||
.side_effects
|
|
||||||
.map
|
|
||||||
.range((*id, 0)..=(*id, u32::MAX))
|
|
||||||
.map(|((_id, command_index), accesses)| (command_index, accesses))
|
|
||||||
{
|
|
||||||
for access in accesses.iter() {
|
|
||||||
let mut access = *access;
|
|
||||||
|
|
||||||
// iterate over the previous accesses to this subresource:
|
|
||||||
// - if we find an access with the same polarity, attempt to
|
|
||||||
// fold; a barrier already exists if it is needed.
|
|
||||||
// - if we find an access with a different polarity, we need to
|
|
||||||
// insert a barrier before the current command index.
|
|
||||||
// - if we find need for a layout transition, we need to insert
|
|
||||||
// a barrier before the current command index.
|
|
||||||
|
|
||||||
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) {
|
|
||||||
access = *sub;
|
|
||||||
folded = true;
|
|
||||||
} else if sub.conflicts(&access) {
|
|
||||||
// if this access conflicts with a previous one, we need
|
|
||||||
// to insert a barrier before the current command index.
|
|
||||||
// if we have managed to fold this access with a
|
|
||||||
// previous one, a barrier already exists.
|
|
||||||
|
|
||||||
// we know that `from` is correct, as the first
|
|
||||||
// conflicting access must be of opposite polarity (or a
|
|
||||||
// layout transition) and no more accesses can be folded
|
|
||||||
// into it.
|
|
||||||
// we store the index of `to`, as more accesses may be
|
|
||||||
// folded into it, expanding mips/array layers.
|
|
||||||
barriers.push(Barrier {
|
|
||||||
resource_id: *id,
|
|
||||||
command_index: *command_index,
|
|
||||||
from: *sub,
|
|
||||||
to: len,
|
|
||||||
});
|
|
||||||
|
|
||||||
// we can stop iterating over previous accesses, as we
|
|
||||||
// can no longer fold into any accesses before this
|
|
||||||
// conflict.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !folded {
|
|
||||||
// if we couldn't fold this access with any previous one, add it as a new entry
|
|
||||||
entry.push((
|
|
||||||
access,
|
|
||||||
SubresourceState {
|
|
||||||
command_index: *command_index,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// construct barriers
|
|
||||||
for barrier in barriers {
|
|
||||||
// stuff
|
|
||||||
}
|
|
||||||
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn record_commands(&self, recorder: &mut CommandRecorder) {
|
|
||||||
let mut cursor = 0;
|
|
||||||
let mut barriers = self.fold_side_effects();
|
|
||||||
let mut command_index = 0;
|
|
||||||
|
|
||||||
while cursor < self.command_bytes.len() {
|
|
||||||
barriers
|
|
||||||
.drain(barriers.partition_point(|(idx, _)| *idx <= command_index)..)
|
|
||||||
.for_each(|(_, barrier)| {
|
|
||||||
barrier.apply(recorder);
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let meta_ptr = self
|
|
||||||
.command_bytes
|
|
||||||
.as_ptr()
|
|
||||||
.add(cursor)
|
|
||||||
.cast::<CommandMeta>();
|
|
||||||
let meta = meta_ptr.read_unaligned();
|
|
||||||
let command_ptr = meta_ptr.add(1).cast::<()>().cast_mut();
|
|
||||||
(meta.apply_command)(recorder, NonNull::new_unchecked(command_ptr), &mut cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
command_index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,13 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
|
|
||||||
use crate::{images::MipRange, render_graph::recorder::SideEffectMap};
|
use crate::images::MipRange;
|
||||||
|
|
||||||
pub trait Resource: Sized {
|
pub trait Resource: Sized + AsResourceRange {
|
||||||
fn id(&self) -> ResourceId;
|
fn id(&self) -> ResourceId;
|
||||||
fn side_effect(
|
}
|
||||||
&self,
|
|
||||||
map: SideEffectMap,
|
pub trait AsResourceRange {
|
||||||
access: vk::AccessFlags2,
|
fn into_access(&self) -> ResourceRange;
|
||||||
layout: Option<vk::ImageLayout>,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod impls {
|
mod impls {
|
||||||
|
|
@ -21,240 +17,93 @@ mod impls {
|
||||||
fn id(&self) -> ResourceId {
|
fn id(&self) -> ResourceId {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn side_effect(
|
|
||||||
&self,
|
|
||||||
mut map: SideEffectMap,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
_layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
map.insert(
|
|
||||||
self.id(),
|
|
||||||
ResourceAccess {
|
|
||||||
range: BufferRange::full().into(),
|
|
||||||
access,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Resource for BufferSlice {
|
|
||||||
fn id(&self) -> ResourceId {
|
|
||||||
self.buffer.id()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn side_effect(
|
|
||||||
&self,
|
|
||||||
mut map: SideEffectMap,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
_layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
map.insert(
|
|
||||||
self.id(),
|
|
||||||
ResourceAccess {
|
|
||||||
range: self.range.into(),
|
|
||||||
access,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Resource for BufferSlices {
|
|
||||||
fn id(&self) -> ResourceId {
|
|
||||||
self.buffer.id()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn side_effect(
|
|
||||||
&self,
|
|
||||||
mut map: SideEffectMap,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
_layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
for range in &self.ranges {
|
|
||||||
map.insert(
|
|
||||||
self.id(),
|
|
||||||
ResourceAccess {
|
|
||||||
range: (*range).into(),
|
|
||||||
access,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Resource for BufferRowSlice {
|
|
||||||
fn id(&self) -> ResourceId {
|
|
||||||
self.buffer.id()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn side_effect(
|
|
||||||
&self,
|
|
||||||
mut map: SideEffectMap,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
_layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
map.insert(
|
|
||||||
self.id(),
|
|
||||||
ResourceAccess {
|
|
||||||
range: BufferRange {
|
|
||||||
offset: self.row.offset,
|
|
||||||
size: (self.row.row_count as u64) * (self.row.row_size as u64),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
access,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Resource for BufferRowSlices {
|
|
||||||
fn id(&self) -> ResourceId {
|
|
||||||
self.buffer.id()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn side_effect(
|
|
||||||
&self,
|
|
||||||
mut map: SideEffectMap,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
_layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
for row in &self.rows {
|
|
||||||
map.insert(
|
|
||||||
self.id(),
|
|
||||||
ResourceAccess {
|
|
||||||
range: BufferRange {
|
|
||||||
offset: row.offset,
|
|
||||||
size: (row.row_count as u64) * (row.row_size as u64),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
access,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resource for Texture {
|
impl Resource for Texture {
|
||||||
fn id(&self) -> ResourceId {
|
fn id(&self) -> ResourceId {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn side_effect(
|
impl Resource for BufferSlice {
|
||||||
&self,
|
fn id(&self) -> ResourceId {
|
||||||
mut map: SideEffectMap,
|
self.buffer.id()
|
||||||
access: vk::AccessFlags2,
|
|
||||||
layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
map.insert(
|
|
||||||
self.id(),
|
|
||||||
ResourceAccess {
|
|
||||||
range: (TextureRange::full(), layout.unwrap_or_default()).into(),
|
|
||||||
access,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Resource for TextureRegion {
|
impl Resource for TextureRegion {
|
||||||
fn id(&self) -> ResourceId {
|
fn id(&self) -> ResourceId {
|
||||||
self.texture.id()
|
self.texture.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn side_effect(
|
|
||||||
&self,
|
|
||||||
mut map: SideEffectMap,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
map.insert(
|
|
||||||
self.id(),
|
|
||||||
ResourceAccess {
|
|
||||||
range: (self.range, layout.unwrap_or_default()).into(),
|
|
||||||
access,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Resource for TextureRegions {
|
|
||||||
fn id(&self) -> ResourceId {
|
|
||||||
self.texture.id()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn side_effect(
|
impl AsResourceRange for BufferSlice {
|
||||||
&self,
|
fn into_access(&self) -> ResourceRange {
|
||||||
mut map: SideEffectMap,
|
ResourceRange::Buffer {
|
||||||
access: vk::AccessFlags2,
|
offset: self.offset,
|
||||||
layout: Option<vk::ImageLayout>,
|
size: self.size,
|
||||||
) {
|
|
||||||
for range in &self.ranges {
|
|
||||||
map.insert(
|
|
||||||
self.id(),
|
|
||||||
ResourceAccess {
|
|
||||||
range: (*range, layout.unwrap_or_default()).into(),
|
|
||||||
access,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Resource for TextureView {
|
|
||||||
fn id(&self) -> ResourceId {
|
|
||||||
self.texture.id()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn side_effect(
|
|
||||||
&self,
|
|
||||||
mut map: SideEffectMap,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
map.insert(
|
|
||||||
self.id(),
|
|
||||||
ResourceAccess {
|
|
||||||
range: (self.range, layout.unwrap_or_default()).into(),
|
|
||||||
access,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
impl AsResourceRange for TextureRegion {
|
||||||
|
fn into_access(&self) -> ResourceRange {
|
||||||
|
ResourceRange::Texture {
|
||||||
|
aspect: self.aspect,
|
||||||
|
mip_level: self.mip_level,
|
||||||
|
array_layers: self.array_layers,
|
||||||
|
origin: self.origin,
|
||||||
|
extent: self.extent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsResourceRange for Texture {
|
||||||
|
fn into_access(&self) -> ResourceRange {
|
||||||
|
ResourceRange::Texture {
|
||||||
|
aspect: vk::ImageAspectFlags::NONE,
|
||||||
|
mip_level: 0,
|
||||||
|
array_layers: MipRange::default(),
|
||||||
|
origin: (0, 0, 0),
|
||||||
|
extent: (u32::MAX, u32::MAX, u32::MAX),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsResourceRange for Buffer {
|
||||||
|
fn into_access(&self) -> ResourceRange {
|
||||||
|
ResourceRange::Buffer {
|
||||||
|
offset: 0,
|
||||||
|
size: u64::MAX,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: AsResourceRange> AsResourceRange for Read<T> {
|
||||||
|
fn into_access(&self) -> ResourceRange {
|
||||||
|
self.0.into_access()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: AsResourceRange> AsResourceRange for Write<T> {
|
||||||
|
fn into_access(&self) -> ResourceRange {
|
||||||
|
self.0.into_access()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: AsResourceRange> AsResourceRange for ReadWrite<T> {
|
||||||
|
fn into_access(&self) -> ResourceRange {
|
||||||
|
self.0.into_access()
|
||||||
|
}
|
||||||
|
}
|
||||||
impl<T: Resource> Resource for Read<T> {
|
impl<T: Resource> Resource for Read<T> {
|
||||||
fn id(&self) -> ResourceId {
|
fn id(&self) -> ResourceId {
|
||||||
self.0.id()
|
self.0.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn side_effect(
|
|
||||||
&self,
|
|
||||||
map: SideEffectMap,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
self.0.side_effect(map, access, layout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl<T: Resource> Resource for Write<T> {
|
impl<T: Resource> Resource for Write<T> {
|
||||||
fn id(&self) -> ResourceId {
|
fn id(&self) -> ResourceId {
|
||||||
self.0.id()
|
self.0.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn side_effect(
|
|
||||||
&self,
|
|
||||||
map: SideEffectMap,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
self.0.side_effect(map, access, layout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl<T: Resource> Resource for ReadWrite<T> {
|
impl<T: Resource> Resource for ReadWrite<T> {
|
||||||
fn id(&self) -> ResourceId {
|
fn id(&self) -> ResourceId {
|
||||||
self.0.id()
|
self.0.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn side_effect(
|
|
||||||
&self,
|
|
||||||
map: SideEffectMap,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
layout: Option<vk::ImageLayout>,
|
|
||||||
) {
|
|
||||||
self.0.side_effect(map, access, layout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,133 +111,13 @@ pub struct Pipeline;
|
||||||
pub struct DescriptorSet;
|
pub struct DescriptorSet;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct ResourceId(pub u32);
|
pub struct ResourceId(u32);
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
|
||||||
pub struct ResourceAccess {
|
pub struct ResourceAccess {
|
||||||
pub range: ResourceRange,
|
pub range: ResourceRange,
|
||||||
pub access: vk::AccessFlags2,
|
pub access: vk::AccessFlags2,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 self.access != other.access {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
match (&mut 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,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
// only fold if other lies within self
|
|
||||||
if *offset > *offset_b || *offset + *size < *offset_b + *size_b {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
pub enum ResourceRange {
|
||||||
Buffer {
|
Buffer {
|
||||||
offset: u64,
|
offset: u64,
|
||||||
|
|
@ -396,215 +125,29 @@ pub enum ResourceRange {
|
||||||
},
|
},
|
||||||
Texture {
|
Texture {
|
||||||
aspect: vk::ImageAspectFlags,
|
aspect: vk::ImageAspectFlags,
|
||||||
mip_level: MipRange,
|
mip_level: u32,
|
||||||
array_layers: MipRange,
|
array_layers: MipRange,
|
||||||
origin: (i32, i32, i32),
|
origin: (u32, u32, u32),
|
||||||
extent: (u32, u32, u32),
|
extent: (u32, u32, u32),
|
||||||
layout: vk::ImageLayout,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ResourceRange {
|
pub struct Buffer(ResourceId);
|
||||||
fn default() -> Self {
|
pub struct Texture(ResourceId);
|
||||||
ResourceRange::Buffer {
|
pub struct BufferSlice {
|
||||||
offset: 0,
|
pub buffer: Buffer,
|
||||||
size: u64::MAX,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
|
||||||
pub struct BufferRange {
|
|
||||||
pub offset: u64,
|
pub offset: u64,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferRange {
|
|
||||||
pub fn full() -> Self {
|
|
||||||
Self {
|
|
||||||
offset: 0,
|
|
||||||
size: u64::MAX,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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<(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 struct TextureRegion {
|
||||||
pub texture: Texture,
|
pub texture: Texture,
|
||||||
pub range: TextureRange,
|
pub aspect: vk::ImageAspectFlags,
|
||||||
}
|
pub mip_level: u32,
|
||||||
|
pub array_layers: MipRange,
|
||||||
pub struct TextureRegions {
|
pub origin: (u32, u32, u32),
|
||||||
pub texture: Texture,
|
pub extent: (u32, u32, u32),
|
||||||
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 Read<T>(pub T);
|
||||||
pub struct Write<T>(pub T);
|
pub struct Write<T>(pub T);
|
||||||
pub struct ReadWrite<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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue