Compare commits
No commits in common. "d901fe250bc603633cd2e42c087a189b7cf868f9" and "f9c18bcd3c2a94e013fdb1d7ff1e1e4d23cb2c99" have entirely different histories.
d901fe250b
...
f9c18bcd3c
|
|
@ -2,24 +2,20 @@
|
||||||
|
|
||||||
extern crate self as renderer_device;
|
extern crate self as renderer_device;
|
||||||
|
|
||||||
use crate::instance::InstanceInner;
|
|
||||||
use ash::{
|
|
||||||
ext,
|
|
||||||
vk::{self, SurfaceCapabilitiesKHR},
|
|
||||||
};
|
|
||||||
use dyn_clone::DynClone;
|
|
||||||
use std::{fmt::Debug, path::PathBuf};
|
use std::{fmt::Debug, path::PathBuf};
|
||||||
|
use ash::{ext, vk::{self, SurfaceCapabilitiesKHR}};
|
||||||
|
use dyn_clone::DynClone;
|
||||||
|
use crate::instance::InstanceInner;
|
||||||
|
|
||||||
mod debug;
|
|
||||||
pub mod device;
|
pub mod device;
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub mod pipeline_cache;
|
pub mod pipeline_cache;
|
||||||
pub mod queue;
|
pub mod queue;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
|
mod debug;
|
||||||
|
|
||||||
pub use device::{Device, DeviceOwned, DeviceRef, Extension};
|
pub use device::{Device, DeviceOwned, DeviceRef, Extension};
|
||||||
pub use instance::{DebugUtils, DebugUtilsCreateInfo, Instance};
|
pub use instance::{DebugUtils, DebugUtilsCreateInfo, Instance};
|
||||||
pub use queue::QueueFamilyIndex;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod device_ref_derive_tests {
|
mod device_ref_derive_tests {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,3 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ash.workspace = true
|
|
||||||
gpu-allocator.workspace = true
|
|
||||||
renderer_device = { path = "../renderer_device" }
|
|
||||||
renderer_resources = { path = "../renderer_resources" }
|
|
||||||
ash-formats = { git = "https://github.com/janis-bhm/ash-formats", branch = "main" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,331 +0,0 @@
|
||||||
use ash::vk;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
recorder::{CommandIndex, CommandRecorder, RecordError, RecordedCommand, SideEffectMap},
|
|
||||||
resources::{
|
|
||||||
Buffer, BufferRange, DescriptorResourceAccess, GraphDescriptorSet, IndexBinding,
|
|
||||||
IndexFormat, IndirectBinding, PushConstantData, ResourceId, SubresourceScope,
|
|
||||||
SubresourceScopeInner, Texture, VertexBinding,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A recorded operation declares its accesses before compilation and applies
|
|
||||||
/// itself to a Vulkan command buffer after resource binding.
|
|
||||||
pub trait Command: RecordedCommand {
|
|
||||||
fn side_effects(&self, command_index: CommandIndex, map: &mut SideEffectMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn access(
|
|
||||||
stage: vk::PipelineStageFlags2,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
subresource: SubresourceScopeInner,
|
|
||||||
) -> SubresourceScope {
|
|
||||||
SubresourceScope {
|
|
||||||
stage,
|
|
||||||
access,
|
|
||||||
subresource,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct CopyBuffer {
|
|
||||||
pub src: Buffer,
|
|
||||||
pub src_range: BufferRange,
|
|
||||||
pub dst: Buffer,
|
|
||||||
pub dst_range: BufferRange,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Command for CopyBuffer {
|
|
||||||
fn side_effects(&self, command_index: CommandIndex, map: &mut SideEffectMap) {
|
|
||||||
map.insert(
|
|
||||||
command_index,
|
|
||||||
self.src.id(),
|
|
||||||
access(
|
|
||||||
vk::PipelineStageFlags2::COPY,
|
|
||||||
vk::AccessFlags2::TRANSFER_READ,
|
|
||||||
SubresourceScopeInner::Buffer(self.src_range),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
map.insert(
|
|
||||||
command_index,
|
|
||||||
self.dst.id(),
|
|
||||||
access(
|
|
||||||
vk::PipelineStageFlags2::COPY,
|
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
|
||||||
SubresourceScopeInner::Buffer(self.dst_range),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecordedCommand for CopyBuffer {
|
|
||||||
fn apply(&mut self, recorder: &mut CommandRecorder<'_>) -> Result<(), RecordError> {
|
|
||||||
let region = vk::BufferCopy2::default()
|
|
||||||
.src_offset(self.src_range.offset)
|
|
||||||
.dst_offset(self.dst_range.offset)
|
|
||||||
.size(self.src_range.size.min(self.dst_range.size));
|
|
||||||
let regions = [region];
|
|
||||||
let info = vk::CopyBufferInfo2::default()
|
|
||||||
.src_buffer(recorder.buffer(self.src.id())?)
|
|
||||||
.dst_buffer(recorder.buffer(self.dst.id())?)
|
|
||||||
.regions(®ions);
|
|
||||||
unsafe {
|
|
||||||
recorder
|
|
||||||
.device
|
|
||||||
.cmd_copy_buffer2(recorder.command_buffer, &info)
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct ClearTexture {
|
|
||||||
pub texture: Texture,
|
|
||||||
pub range: vk::ImageSubresourceRange,
|
|
||||||
pub layout: vk::ImageLayout,
|
|
||||||
pub color: [f32; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Command for ClearTexture {
|
|
||||||
fn side_effects(&self, command_index: CommandIndex, map: &mut SideEffectMap) {
|
|
||||||
map.insert(
|
|
||||||
command_index,
|
|
||||||
self.texture.id(),
|
|
||||||
access(
|
|
||||||
vk::PipelineStageFlags2::CLEAR,
|
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
|
||||||
SubresourceScopeInner::Texture {
|
|
||||||
range: self.range,
|
|
||||||
layout: self.layout,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecordedCommand for ClearTexture {
|
|
||||||
fn apply(&mut self, recorder: &mut CommandRecorder<'_>) -> Result<(), RecordError> {
|
|
||||||
let value = vk::ClearColorValue {
|
|
||||||
float32: self.color,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
recorder.device.cmd_clear_color_image(
|
|
||||||
recorder.command_buffer,
|
|
||||||
recorder.image(self.texture.id())?,
|
|
||||||
self.layout,
|
|
||||||
&value,
|
|
||||||
&[self.range],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum DrawArgs {
|
|
||||||
Direct {
|
|
||||||
vertex_count: u32,
|
|
||||||
instance_count: u32,
|
|
||||||
first_vertex: u32,
|
|
||||||
first_instance: u32,
|
|
||||||
},
|
|
||||||
Indexed {
|
|
||||||
index_count: u32,
|
|
||||||
instance_count: u32,
|
|
||||||
first_index: u32,
|
|
||||||
vertex_offset: i32,
|
|
||||||
first_instance: u32,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A self-contained graphics draw packet. Every graph resource consumed by
|
|
||||||
/// vertex/index/indirect input or descriptor sets is declared by this packet.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Draw {
|
|
||||||
pub args: DrawArgs,
|
|
||||||
/// Layout compatible with the bound pipeline, descriptor sets, and push constants.
|
|
||||||
pub pipeline_layout: vk::PipelineLayout,
|
|
||||||
pub vertices: Vec<VertexBinding>,
|
|
||||||
pub index: Option<IndexBinding>,
|
|
||||||
pub indirect: Option<IndirectBinding>,
|
|
||||||
pub descriptor_sets: Vec<GraphDescriptorSet>,
|
|
||||||
pub push_constants: Vec<PushConstantData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Command for Draw {
|
|
||||||
fn side_effects(&self, command_index: CommandIndex, map: &mut SideEffectMap) {
|
|
||||||
for vertex in &self.vertices {
|
|
||||||
map.insert(
|
|
||||||
command_index,
|
|
||||||
vertex.slice.buffer.id(),
|
|
||||||
access(
|
|
||||||
vk::PipelineStageFlags2::VERTEX_INPUT,
|
|
||||||
vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
|
||||||
SubresourceScopeInner::Buffer(vertex.slice.range),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(index) = self.index {
|
|
||||||
map.insert(
|
|
||||||
command_index,
|
|
||||||
index.slice.buffer.id(),
|
|
||||||
access(
|
|
||||||
vk::PipelineStageFlags2::INDEX_INPUT,
|
|
||||||
vk::AccessFlags2::INDEX_READ,
|
|
||||||
SubresourceScopeInner::Buffer(index.slice.range),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(indirect) = self.indirect {
|
|
||||||
map.insert(
|
|
||||||
command_index,
|
|
||||||
indirect.slice.buffer.id(),
|
|
||||||
access(
|
|
||||||
vk::PipelineStageFlags2::DRAW_INDIRECT,
|
|
||||||
vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
|
||||||
SubresourceScopeInner::Buffer(indirect.slice.range),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for set in &self.descriptor_sets {
|
|
||||||
for declared in &set.accesses {
|
|
||||||
let scope = match *declared {
|
|
||||||
DescriptorResourceAccess::Buffer {
|
|
||||||
range,
|
|
||||||
stage,
|
|
||||||
access: mask,
|
|
||||||
..
|
|
||||||
} => access(stage, mask, SubresourceScopeInner::Buffer(range)),
|
|
||||||
DescriptorResourceAccess::Texture {
|
|
||||||
range,
|
|
||||||
layout,
|
|
||||||
stage,
|
|
||||||
access: mask,
|
|
||||||
..
|
|
||||||
} => access(
|
|
||||||
stage,
|
|
||||||
mask,
|
|
||||||
SubresourceScopeInner::Texture { range, layout },
|
|
||||||
),
|
|
||||||
};
|
|
||||||
let resource = match *declared {
|
|
||||||
DescriptorResourceAccess::Buffer { buffer, .. } => buffer.id(),
|
|
||||||
DescriptorResourceAccess::Texture { texture, .. } => texture.id(),
|
|
||||||
};
|
|
||||||
map.insert(command_index, resource, scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecordedCommand for Draw {
|
|
||||||
fn apply(&mut self, recorder: &mut CommandRecorder<'_>) -> Result<(), RecordError> {
|
|
||||||
for vertex in &self.vertices {
|
|
||||||
let buffers = [recorder.buffer(vertex.slice.buffer.id())?];
|
|
||||||
let offsets = [vertex.slice.range.offset];
|
|
||||||
unsafe {
|
|
||||||
recorder.device.cmd_bind_vertex_buffers(
|
|
||||||
recorder.command_buffer,
|
|
||||||
vertex.binding,
|
|
||||||
&buffers,
|
|
||||||
&offsets,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(index) = self.index {
|
|
||||||
let index_type = match index.format {
|
|
||||||
IndexFormat::Uint8 => vk::IndexType::UINT8_KHR,
|
|
||||||
IndexFormat::Uint16 => vk::IndexType::UINT16,
|
|
||||||
IndexFormat::Uint32 => vk::IndexType::UINT32,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
recorder.device.cmd_bind_index_buffer(
|
|
||||||
recorder.command_buffer,
|
|
||||||
recorder.buffer(index.slice.buffer.id())?,
|
|
||||||
index.slice.range.offset,
|
|
||||||
index_type,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for constants in &self.push_constants {
|
|
||||||
unsafe {
|
|
||||||
recorder.device.cmd_push_constants(
|
|
||||||
recorder.command_buffer,
|
|
||||||
self.pipeline_layout,
|
|
||||||
constants.stage_flags,
|
|
||||||
constants.offset,
|
|
||||||
&constants.data,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let descriptor_sets: Vec<_> = self.descriptor_sets.iter().map(|set| set.raw).collect();
|
|
||||||
if !descriptor_sets.is_empty() {
|
|
||||||
unsafe {
|
|
||||||
recorder.device.cmd_bind_descriptor_sets(
|
|
||||||
recorder.command_buffer,
|
|
||||||
vk::PipelineBindPoint::GRAPHICS,
|
|
||||||
self.pipeline_layout,
|
|
||||||
0,
|
|
||||||
&descriptor_sets,
|
|
||||||
&[],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
if let Some(indirect) = self.indirect {
|
|
||||||
let buffer = recorder.buffer(indirect.slice.buffer.id())?;
|
|
||||||
if matches!(self.args, DrawArgs::Indexed { .. }) {
|
|
||||||
recorder.device.cmd_draw_indexed_indirect(
|
|
||||||
recorder.command_buffer,
|
|
||||||
buffer,
|
|
||||||
indirect.slice.range.offset,
|
|
||||||
indirect.draw_count,
|
|
||||||
indirect.stride,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
recorder.device.cmd_draw_indirect(
|
|
||||||
recorder.command_buffer,
|
|
||||||
buffer,
|
|
||||||
indirect.slice.range.offset,
|
|
||||||
indirect.draw_count,
|
|
||||||
indirect.stride,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match self.args {
|
|
||||||
DrawArgs::Direct {
|
|
||||||
vertex_count,
|
|
||||||
instance_count,
|
|
||||||
first_vertex,
|
|
||||||
first_instance,
|
|
||||||
} => recorder.device.cmd_draw(
|
|
||||||
recorder.command_buffer,
|
|
||||||
vertex_count,
|
|
||||||
instance_count,
|
|
||||||
first_vertex,
|
|
||||||
first_instance,
|
|
||||||
),
|
|
||||||
DrawArgs::Indexed {
|
|
||||||
index_count,
|
|
||||||
instance_count,
|
|
||||||
first_index,
|
|
||||||
vertex_offset,
|
|
||||||
first_instance,
|
|
||||||
} => recorder.device.cmd_draw_indexed(
|
|
||||||
recorder.command_buffer,
|
|
||||||
index_count,
|
|
||||||
instance_count,
|
|
||||||
first_index,
|
|
||||||
vertex_offset,
|
|
||||||
first_instance,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resource_id(buffer: Buffer) -> ResourceId {
|
|
||||||
buffer.id()
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +0,0 @@
|
||||||
//! A small render-graph skeleton following the design in `rendergraph.org`.
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
pub mod commands;
|
|
||||||
pub mod graph;
|
|
||||||
pub mod recorder;
|
|
||||||
pub mod resources;
|
|
||||||
|
|
||||||
pub use graph::{CompiledGraph, GraphError, RenderGraph, ResourceInterference};
|
|
||||||
pub use recorder::{CommandIndex, CommandList, CommandRecorder, SideEffectMap};
|
|
||||||
pub use resources::{
|
|
||||||
Buffer, BufferRange, BufferSlice, DescriptorResourceAccess, GraphBufferDesc,
|
|
||||||
GraphDescriptorSet, GraphTextureDesc, IndexBinding, IndexFormat, IndirectBinding,
|
|
||||||
PushConstantData, ResourceId, Texture, VertexBinding,
|
|
||||||
};
|
|
||||||
3
crates/renderer_rendergraph/src/main.rs
Normal file
3
crates/renderer_rendergraph/src/main.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
|
|
@ -1,413 +0,0 @@
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use ash::vk;
|
|
||||||
|
|
||||||
use crate::resources::{
|
|
||||||
BufferRange, ResourceId, Subresource, SubresourceScope, SubresourceScopeInner,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The position of a command in its command list.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct CommandIndex(pub usize);
|
|
||||||
|
|
||||||
impl CommandIndex {
|
|
||||||
pub fn index(self) -> usize {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<usize> for CommandIndex {
|
|
||||||
fn from(index: usize) -> Self {
|
|
||||||
Self(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CommandIndex> for usize {
|
|
||||||
fn from(index: CommandIndex) -> Self {
|
|
||||||
index.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct ResourceAccess {
|
|
||||||
pub resource: ResourceId,
|
|
||||||
pub scope: SubresourceScope,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores every declared access together with the command that declared it.
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
pub struct SideEffectMap {
|
|
||||||
accesses: Vec<Vec<ResourceAccess>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SideEffectMap {
|
|
||||||
pub fn begin_command(&mut self) -> CommandIndex {
|
|
||||||
let index = self.accesses.len();
|
|
||||||
self.accesses.push(Vec::new());
|
|
||||||
index.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Record an access scope for a command and resource.
|
|
||||||
///
|
|
||||||
/// Each scope must describe the command's complete use for that access,
|
|
||||||
/// not merely an enclosing bound, because `SideEffectMap::insert` may merge
|
|
||||||
/// contained scopes for the same command and resource.
|
|
||||||
pub fn insert(
|
|
||||||
&mut self,
|
|
||||||
command_index: CommandIndex,
|
|
||||||
resource: ResourceId,
|
|
||||||
scope: SubresourceScope,
|
|
||||||
) {
|
|
||||||
if self.accesses.len() <= command_index.0 {
|
|
||||||
self.accesses.resize_with(command_index.0 + 1, Vec::new);
|
|
||||||
}
|
|
||||||
let accesses = &mut self.accesses[command_index.0];
|
|
||||||
for existing in accesses.iter_mut() {
|
|
||||||
if existing.resource == resource {
|
|
||||||
if existing.scope.try_merge(&scope) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if scope.has_same_access_polarity(&existing.scope)
|
|
||||||
&& scope.contains_subresource(&existing.scope)
|
|
||||||
{
|
|
||||||
let mut merged = scope;
|
|
||||||
merged.stage |= existing.scope.stage;
|
|
||||||
merged.access |= existing.scope.access;
|
|
||||||
existing.scope = merged;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
accesses.push(ResourceAccess { resource, scope });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn accesses(&self, command_index: CommandIndex) -> &[ResourceAccess] {
|
|
||||||
self.accesses
|
|
||||||
.get(command_index.0)
|
|
||||||
.map_or(&[], Vec::as_slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn command_accesses(&self) -> &[Vec<ResourceAccess>] {
|
|
||||||
&self.accesses
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A safe, type-erased temporary command buffer. Each command is owned until
|
|
||||||
/// compilation/recording, while its access declarations are stored separately.
|
|
||||||
pub struct CommandList {
|
|
||||||
pub(crate) commands: Vec<Box<dyn RecordedCommand>>,
|
|
||||||
pub side_effects: SideEffectMap,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait RecordedCommand {
|
|
||||||
fn apply(&mut self, recorder: &mut CommandRecorder<'_>) -> Result<(), RecordError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CommandList {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
commands: Vec::new(),
|
|
||||||
side_effects: SideEffectMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push<C: crate::commands::Command + 'static>(&mut self, command: C) -> CommandIndex {
|
|
||||||
let index = self.side_effects.begin_command();
|
|
||||||
command.side_effects(index, &mut self.side_effects);
|
|
||||||
self.commands.push(Box::new(command));
|
|
||||||
index
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.commands.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.commands.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CommandList {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub enum RecordError {
|
|
||||||
MissingBuffer(ResourceId),
|
|
||||||
MissingImage(ResourceId),
|
|
||||||
UnresolvedCreatedBuffer(ResourceId),
|
|
||||||
UnresolvedCreatedImage(ResourceId),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Vulkan recording context. Handle maps are populated by the compiled graph.
|
|
||||||
pub struct CommandRecorder<'a> {
|
|
||||||
pub device: &'a ash::Device,
|
|
||||||
pub command_buffer: vk::CommandBuffer,
|
|
||||||
pub buffers: BTreeMap<ResourceId, vk::Buffer>,
|
|
||||||
pub images: BTreeMap<ResourceId, vk::Image>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> CommandRecorder<'a> {
|
|
||||||
pub fn new(device: &'a ash::Device, command_buffer: vk::CommandBuffer) -> Self {
|
|
||||||
Self {
|
|
||||||
device,
|
|
||||||
command_buffer,
|
|
||||||
buffers: BTreeMap::new(),
|
|
||||||
images: BTreeMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn buffer(&self, id: ResourceId) -> Result<vk::Buffer, RecordError> {
|
|
||||||
self.buffers
|
|
||||||
.get(&id)
|
|
||||||
.copied()
|
|
||||||
.ok_or(RecordError::MissingBuffer(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn image(&self, id: ResourceId) -> Result<vk::Image, RecordError> {
|
|
||||||
self.images
|
|
||||||
.get(&id)
|
|
||||||
.copied()
|
|
||||||
.ok_or(RecordError::MissingImage(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pipeline_barrier(&self, barrier: &super::graph::PipelineBarrier) {
|
|
||||||
let buffer_barriers: Vec<_> = barrier
|
|
||||||
.resources
|
|
||||||
.iter()
|
|
||||||
.filter_map(|resource| match resource.range {
|
|
||||||
Subresource::Buffer(BufferRange { offset, size }) => {
|
|
||||||
self.buffers.get(&resource.id).map(|&buffer| {
|
|
||||||
let (src, dst) = if let Some(queue_transfer) = resource.queue_transfer {
|
|
||||||
(queue_transfer.src.index(), queue_transfer.dst.index())
|
|
||||||
} else {
|
|
||||||
(vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED)
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::BufferMemoryBarrier2::default()
|
|
||||||
.src_stage_mask(resource.src_stage)
|
|
||||||
.src_access_mask(resource.src_access)
|
|
||||||
.dst_stage_mask(resource.dst_stage)
|
|
||||||
.dst_access_mask(resource.dst_access)
|
|
||||||
.src_queue_family_index(src)
|
|
||||||
.dst_queue_family_index(dst)
|
|
||||||
.buffer(buffer)
|
|
||||||
.offset(offset)
|
|
||||||
.size(size)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Subresource::Texture(_) => None,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let image_barriers: Vec<_> = barrier
|
|
||||||
.resources
|
|
||||||
.iter()
|
|
||||||
.filter_map(|resource| match resource.range {
|
|
||||||
Subresource::Texture(range) => self.images.get(&resource.id).map(|&image| {
|
|
||||||
vk::ImageMemoryBarrier2::default()
|
|
||||||
.src_stage_mask(resource.src_stage)
|
|
||||||
.src_access_mask(resource.src_access)
|
|
||||||
.dst_stage_mask(resource.dst_stage)
|
|
||||||
.dst_access_mask(resource.dst_access)
|
|
||||||
.old_layout(resource.old_layout.unwrap_or(vk::ImageLayout::UNDEFINED))
|
|
||||||
.new_layout(resource.new_layout.unwrap_or(vk::ImageLayout::GENERAL))
|
|
||||||
.image(image)
|
|
||||||
.subresource_range(range)
|
|
||||||
}),
|
|
||||||
Subresource::Buffer(_) => None,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let dependency = vk::DependencyInfo::default()
|
|
||||||
.buffer_memory_barriers(&buffer_barriers)
|
|
||||||
.image_memory_barriers(&image_barriers);
|
|
||||||
unsafe {
|
|
||||||
self.device
|
|
||||||
.cmd_pipeline_barrier2(self.command_buffer, &dependency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn scope_range(scope: SubresourceScope) -> Subresource {
|
|
||||||
match scope.subresource {
|
|
||||||
SubresourceScopeInner::Buffer(range) => Subresource::Buffer(range),
|
|
||||||
SubresourceScopeInner::Texture { range, .. } => Subresource::Texture(range),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn read_scope(
|
|
||||||
offset: u64,
|
|
||||||
size: u64,
|
|
||||||
stage: vk::PipelineStageFlags2,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
) -> SubresourceScope {
|
|
||||||
SubresourceScope {
|
|
||||||
access,
|
|
||||||
stage,
|
|
||||||
subresource: SubresourceScopeInner::Buffer(BufferRange { offset, size }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn side_effect_map_folds_same_command_scopes_in_both_containment_directions() {
|
|
||||||
let mut map = SideEffectMap::default();
|
|
||||||
let command = map.begin_command();
|
|
||||||
map.insert(
|
|
||||||
command,
|
|
||||||
ResourceId(0),
|
|
||||||
read_scope(
|
|
||||||
0,
|
|
||||||
64,
|
|
||||||
vk::PipelineStageFlags2::VERTEX_SHADER,
|
|
||||||
vk::AccessFlags2::SHADER_READ,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
map.insert(
|
|
||||||
command,
|
|
||||||
ResourceId(0),
|
|
||||||
read_scope(
|
|
||||||
16,
|
|
||||||
16,
|
|
||||||
vk::PipelineStageFlags2::FRAGMENT_SHADER,
|
|
||||||
vk::AccessFlags2::UNIFORM_READ,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let accesses = map.accesses(command);
|
|
||||||
assert_eq!(accesses.len(), 1);
|
|
||||||
assert_eq!(
|
|
||||||
accesses[0].scope.stage,
|
|
||||||
vk::PipelineStageFlags2::VERTEX_SHADER | vk::PipelineStageFlags2::FRAGMENT_SHADER
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
accesses[0].scope.access,
|
|
||||||
vk::AccessFlags2::SHADER_READ | vk::AccessFlags2::UNIFORM_READ
|
|
||||||
);
|
|
||||||
assert!(matches!(
|
|
||||||
accesses[0].scope.subresource,
|
|
||||||
SubresourceScopeInner::Buffer(BufferRange {
|
|
||||||
offset: 0,
|
|
||||||
size: 64
|
|
||||||
})
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut reverse = SideEffectMap::default();
|
|
||||||
let command = reverse.begin_command();
|
|
||||||
reverse.insert(
|
|
||||||
command,
|
|
||||||
ResourceId(1),
|
|
||||||
read_scope(
|
|
||||||
16,
|
|
||||||
16,
|
|
||||||
vk::PipelineStageFlags2::VERTEX_SHADER,
|
|
||||||
vk::AccessFlags2::SHADER_READ,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
reverse.insert(
|
|
||||||
command,
|
|
||||||
ResourceId(1),
|
|
||||||
read_scope(
|
|
||||||
0,
|
|
||||||
64,
|
|
||||||
vk::PipelineStageFlags2::FRAGMENT_SHADER,
|
|
||||||
vk::AccessFlags2::UNIFORM_READ,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let accesses = reverse.accesses(command);
|
|
||||||
assert_eq!(accesses.len(), 1);
|
|
||||||
assert_eq!(
|
|
||||||
accesses[0].scope.stage,
|
|
||||||
vk::PipelineStageFlags2::VERTEX_SHADER | vk::PipelineStageFlags2::FRAGMENT_SHADER
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
accesses[0].scope.access,
|
|
||||||
vk::AccessFlags2::SHADER_READ | vk::AccessFlags2::UNIFORM_READ
|
|
||||||
);
|
|
||||||
assert!(matches!(
|
|
||||||
accesses[0].scope.subresource,
|
|
||||||
SubresourceScopeInner::Buffer(BufferRange {
|
|
||||||
offset: 0,
|
|
||||||
size: 64
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn side_effect_map_keeps_overlapping_non_subset_scopes_separate() {
|
|
||||||
let mut map = SideEffectMap::default();
|
|
||||||
let command = map.begin_command();
|
|
||||||
map.insert(
|
|
||||||
command,
|
|
||||||
ResourceId(0),
|
|
||||||
read_scope(
|
|
||||||
0,
|
|
||||||
16,
|
|
||||||
vk::PipelineStageFlags2::VERTEX_SHADER,
|
|
||||||
vk::AccessFlags2::SHADER_READ,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
map.insert(
|
|
||||||
command,
|
|
||||||
ResourceId(0),
|
|
||||||
read_scope(
|
|
||||||
8,
|
|
||||||
16,
|
|
||||||
vk::PipelineStageFlags2::FRAGMENT_SHADER,
|
|
||||||
vk::AccessFlags2::UNIFORM_READ,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(map.accesses(command).len(), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn side_effect_map_does_not_fold_opposite_polarity_scopes() {
|
|
||||||
let mut map = SideEffectMap::default();
|
|
||||||
let command = map.begin_command();
|
|
||||||
map.insert(
|
|
||||||
command,
|
|
||||||
ResourceId(0),
|
|
||||||
read_scope(
|
|
||||||
0,
|
|
||||||
64,
|
|
||||||
vk::PipelineStageFlags2::COMPUTE_SHADER,
|
|
||||||
vk::AccessFlags2::SHADER_READ,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
map.insert(
|
|
||||||
command,
|
|
||||||
ResourceId(0),
|
|
||||||
read_scope(
|
|
||||||
16,
|
|
||||||
16,
|
|
||||||
vk::PipelineStageFlags2::COMPUTE_SHADER,
|
|
||||||
vk::AccessFlags2::SHADER_WRITE,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let accesses = map.accesses(command);
|
|
||||||
assert_eq!(accesses.len(), 2);
|
|
||||||
assert!(matches!(
|
|
||||||
accesses[0].scope.subresource,
|
|
||||||
SubresourceScopeInner::Buffer(BufferRange {
|
|
||||||
offset: 0,
|
|
||||||
size: 64
|
|
||||||
})
|
|
||||||
));
|
|
||||||
assert!(matches!(
|
|
||||||
accesses[1].scope.subresource,
|
|
||||||
SubresourceScopeInner::Buffer(BufferRange {
|
|
||||||
offset: 16,
|
|
||||||
size: 16
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,397 +0,0 @@
|
||||||
use std::{borrow::Cow, sync::Arc};
|
|
||||||
|
|
||||||
use ash::vk;
|
|
||||||
use renderer_resources::{
|
|
||||||
buffers::Buffer as BufferResource,
|
|
||||||
images::{Image as ImageResource, ImageSubresourceExt},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Minimal graph-level buffer description. Buffer usage, queue sharing, and
|
|
||||||
/// allocation strategy are inferred from recorded command accesses at resolve.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct GraphBufferDesc {
|
|
||||||
pub size: u64,
|
|
||||||
pub name: Option<Cow<'static, str>>,
|
|
||||||
pub mem_location: gpu_allocator::MemoryLocation,
|
|
||||||
/// Additional Vulkan usages not visible in the graph's recorded commands.
|
|
||||||
pub extra_usage: vk::BufferUsageFlags,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GraphBufferDesc {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
size: 0,
|
|
||||||
name: None,
|
|
||||||
// Transient graph resources default to device-local memory.
|
|
||||||
mem_location: gpu_allocator::MemoryLocation::GpuOnly,
|
|
||||||
extra_usage: vk::BufferUsageFlags::empty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Minimal graph-level texture description. Usage, queue sharing, and
|
|
||||||
/// allocation strategy are inferred from recorded command accesses at resolve.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct GraphTextureDesc {
|
|
||||||
pub format: vk::Format,
|
|
||||||
pub kind: vk::ImageType,
|
|
||||||
pub extent: vk::Extent3D,
|
|
||||||
pub mip_levels: u32,
|
|
||||||
pub array_layers: u32,
|
|
||||||
pub samples: vk::SampleCountFlags,
|
|
||||||
pub tiling: vk::ImageTiling,
|
|
||||||
pub layout: vk::ImageLayout,
|
|
||||||
pub name: Option<Cow<'static, str>>,
|
|
||||||
pub mem_location: gpu_allocator::MemoryLocation,
|
|
||||||
/// Additional Vulkan usages not visible in the graph's recorded commands.
|
|
||||||
pub extra_usage: vk::ImageUsageFlags,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GraphTextureDesc {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
// Callers must provide a concrete format before resolving an image.
|
|
||||||
format: vk::Format::UNDEFINED,
|
|
||||||
kind: vk::ImageType::TYPE_2D,
|
|
||||||
extent: vk::Extent3D {
|
|
||||||
width: 1,
|
|
||||||
height: 1,
|
|
||||||
depth: 1,
|
|
||||||
},
|
|
||||||
mip_levels: 1,
|
|
||||||
array_layers: 1,
|
|
||||||
samples: vk::SampleCountFlags::TYPE_1,
|
|
||||||
// Optimal tiling is the usual choice for graph-managed GPU images.
|
|
||||||
tiling: vk::ImageTiling::OPTIMAL,
|
|
||||||
layout: vk::ImageLayout::UNDEFINED,
|
|
||||||
name: None,
|
|
||||||
mem_location: gpu_allocator::MemoryLocation::GpuOnly,
|
|
||||||
extra_usage: vk::ImageUsageFlags::empty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct ResourceId(pub u64);
|
|
||||||
|
|
||||||
impl ResourceId {
|
|
||||||
pub(crate) fn index(self) -> usize {
|
|
||||||
self.0 as usize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct Buffer(pub ResourceId);
|
|
||||||
|
|
||||||
impl Buffer {
|
|
||||||
pub fn id(self) -> ResourceId {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct Texture(pub ResourceId);
|
|
||||||
|
|
||||||
impl Texture {
|
|
||||||
pub fn id(self) -> ResourceId {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct BufferRange {
|
|
||||||
pub offset: u64,
|
|
||||||
pub size: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BufferRange {
|
|
||||||
pub const fn full() -> Self {
|
|
||||||
Self {
|
|
||||||
offset: 0,
|
|
||||||
size: vk::WHOLE_SIZE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end(self) -> u64 {
|
|
||||||
if self.size == vk::WHOLE_SIZE {
|
|
||||||
u64::MAX
|
|
||||||
} else {
|
|
||||||
self.offset.saturating_add(self.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn overlaps(self, other: Self) -> bool {
|
|
||||||
self.offset < other.end() && other.offset < self.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains(self, other: &Self) -> bool {
|
|
||||||
self.offset <= other.offset && self.end() >= other.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A graph buffer together with the byte range used by a command.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct BufferSlice {
|
|
||||||
pub buffer: Buffer,
|
|
||||||
pub range: BufferRange,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A vertex-input binding. `binding` is the Vulkan vertex binding number.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct VertexBinding {
|
|
||||||
pub binding: u32,
|
|
||||||
pub slice: BufferSlice,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum IndexFormat {
|
|
||||||
Uint8,
|
|
||||||
Uint16,
|
|
||||||
Uint32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct IndexBinding {
|
|
||||||
pub slice: BufferSlice,
|
|
||||||
pub format: IndexFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct IndirectBinding {
|
|
||||||
pub slice: BufferSlice,
|
|
||||||
/// Number of draw commands in the indirect buffer.
|
|
||||||
pub draw_count: u32,
|
|
||||||
/// Stride in bytes between draw commands. Must satisfy Vulkan's alignment
|
|
||||||
/// and minimum-size requirements for the corresponding draw command.
|
|
||||||
pub stride: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum DescriptorResourceAccess {
|
|
||||||
Buffer {
|
|
||||||
buffer: Buffer,
|
|
||||||
range: BufferRange,
|
|
||||||
stage: vk::PipelineStageFlags2,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
},
|
|
||||||
Texture {
|
|
||||||
texture: Texture,
|
|
||||||
range: vk::ImageSubresourceRange,
|
|
||||||
layout: vk::ImageLayout,
|
|
||||||
stage: vk::PipelineStageFlags2,
|
|
||||||
access: vk::AccessFlags2,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Vulkan descriptor set accompanied by graph-visible resource accesses.
|
|
||||||
///
|
|
||||||
/// Callers must list every resource used by the set with its accurate range,
|
|
||||||
/// pipeline stage, access mask, and (for textures) image layout. The graph can
|
|
||||||
/// track these declarations, but cannot inspect or verify raw descriptor-set
|
|
||||||
/// contents.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct GraphDescriptorSet {
|
|
||||||
pub raw: vk::DescriptorSet,
|
|
||||||
pub accesses: Vec<DescriptorResourceAccess>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PushConstantData {
|
|
||||||
pub stage_flags: vk::ShaderStageFlags,
|
|
||||||
pub offset: u32,
|
|
||||||
pub data: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum Subresource {
|
|
||||||
Buffer(BufferRange),
|
|
||||||
Texture(vk::ImageSubresourceRange),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum SubresourceScopeInner {
|
|
||||||
Buffer(BufferRange),
|
|
||||||
Texture {
|
|
||||||
range: vk::ImageSubresourceRange,
|
|
||||||
layout: vk::ImageLayout,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct SubresourceScope {
|
|
||||||
pub access: vk::AccessFlags2,
|
|
||||||
pub stage: vk::PipelineStageFlags2,
|
|
||||||
pub subresource: SubresourceScopeInner,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubresourceScope {
|
|
||||||
/// Whether this scope contains the other's subresource range.
|
|
||||||
/// Texture ranges are compatible only when their layouts match.
|
|
||||||
pub fn contains_subresource(&self, other: &Self) -> bool {
|
|
||||||
match (self.subresource, other.subresource) {
|
|
||||||
(SubresourceScopeInner::Buffer(a), SubresourceScopeInner::Buffer(b)) => a.contains(&b),
|
|
||||||
(
|
|
||||||
SubresourceScopeInner::Texture {
|
|
||||||
range: a,
|
|
||||||
layout: layout_a,
|
|
||||||
},
|
|
||||||
SubresourceScopeInner::Texture {
|
|
||||||
range: b,
|
|
||||||
layout: layout_b,
|
|
||||||
},
|
|
||||||
) => layout_a == layout_b && a.contains(&b),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether both scopes have the same read/write polarity.
|
|
||||||
pub fn has_same_access_polarity(&self, other: &Self) -> bool {
|
|
||||||
access_flag_is_read(self.access) == access_flag_is_read(other.access)
|
|
||||||
&& access_flag_is_write(self.access) == access_flag_is_write(other.access)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Merge sub-ranges with compatible access polarity into one scope.
|
|
||||||
pub fn try_merge(&mut self, other: &Self) -> bool {
|
|
||||||
if access_flag_is_read(self.access) != access_flag_is_read(other.access)
|
|
||||||
|| access_flag_is_write(self.access) != access_flag_is_write(other.access)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let can_merge = match (self.subresource, other.subresource) {
|
|
||||||
(SubresourceScopeInner::Buffer(a), SubresourceScopeInner::Buffer(b)) => a.contains(&b),
|
|
||||||
(
|
|
||||||
SubresourceScopeInner::Texture {
|
|
||||||
range: a,
|
|
||||||
layout: layout_a,
|
|
||||||
},
|
|
||||||
SubresourceScopeInner::Texture {
|
|
||||||
range: b,
|
|
||||||
layout: layout_b,
|
|
||||||
},
|
|
||||||
) => layout_a == layout_b && a.contains(&b),
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if can_merge {
|
|
||||||
self.stage |= other.stage;
|
|
||||||
self.access |= other.access;
|
|
||||||
}
|
|
||||||
|
|
||||||
can_merge
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether accesses overlap and require ordering (a write or layout change).
|
|
||||||
pub fn conflicts(&self, other: &Self) -> bool {
|
|
||||||
// Read-after-read accesses do not conflict, even if they overlap..
|
|
||||||
if access_flag_is_read(self.access)
|
|
||||||
&& !access_flag_is_write(self.access)
|
|
||||||
&& access_flag_is_read(other.access)
|
|
||||||
&& !access_flag_is_write(other.access)
|
|
||||||
{
|
|
||||||
// ..unless the layouts differ for overlapping texture subresources.
|
|
||||||
return match (self.subresource, other.subresource) {
|
|
||||||
(
|
|
||||||
SubresourceScopeInner::Texture {
|
|
||||||
layout: layout_a,
|
|
||||||
range: a,
|
|
||||||
},
|
|
||||||
SubresourceScopeInner::Texture {
|
|
||||||
layout: layout_b,
|
|
||||||
range: b,
|
|
||||||
},
|
|
||||||
) => layout_a != layout_b && a.intersects(&b),
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// for accesses involving writes:
|
|
||||||
use SubresourceScopeInner::*;
|
|
||||||
match (self.subresource, other.subresource) {
|
|
||||||
(Buffer(a), Buffer(b)) => a.overlaps(b),
|
|
||||||
(Texture { range: a, .. }, Texture { range: b, .. }) => a.intersects(&b),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// True when an access mask contains a Vulkan read access.
|
|
||||||
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::ACCELERATION_STRUCTURE_READ_KHR,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// True when an access mask contains a Vulkan write access.
|
|
||||||
pub fn access_flag_is_write(access: vk::AccessFlags2) -> bool {
|
|
||||||
access.intersects(
|
|
||||||
vk::AccessFlags2::SHADER_WRITE
|
|
||||||
| vk::AccessFlags2::COLOR_ATTACHMENT_WRITE
|
|
||||||
| vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE
|
|
||||||
| vk::AccessFlags2::TRANSFER_WRITE
|
|
||||||
| vk::AccessFlags2::HOST_WRITE
|
|
||||||
| vk::AccessFlags2::MEMORY_WRITE
|
|
||||||
| vk::AccessFlags2::SHADER_STORAGE_WRITE
|
|
||||||
| vk::AccessFlags2::ACCELERATION_STRUCTURE_WRITE_KHR,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct ResourceState {
|
|
||||||
pub stage: vk::PipelineStageFlags2,
|
|
||||||
pub access: vk::AccessFlags2,
|
|
||||||
pub layout: Option<vk::ImageLayout>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ResourceState {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
stage: vk::PipelineStageFlags2::NONE,
|
|
||||||
access: vk::AccessFlags2::NONE,
|
|
||||||
layout: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ResourceDesc {
|
|
||||||
CreatedBuffer(GraphBufferDesc),
|
|
||||||
CreatedTexture(GraphTextureDesc),
|
|
||||||
ImportedBuffer {
|
|
||||||
resource: Arc<BufferResource>,
|
|
||||||
initial: ResourceState,
|
|
||||||
},
|
|
||||||
ImportedTexture {
|
|
||||||
resource: Arc<ImageResource>,
|
|
||||||
initial: ResourceState,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResourceDesc {
|
|
||||||
pub fn initial_state(&self) -> ResourceState {
|
|
||||||
match self {
|
|
||||||
Self::ImportedBuffer { initial, .. } | Self::ImportedTexture { initial, .. } => {
|
|
||||||
*initial
|
|
||||||
}
|
|
||||||
Self::CreatedBuffer(_) | Self::CreatedTexture(_) => ResourceState::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_created(&self) -> bool {
|
|
||||||
matches!(self, Self::CreatedBuffer(_) | Self::CreatedTexture(_))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1027,110 +1027,3 @@ pub const SUBRESOURCERANGE_COLOR_ALL: vk::ImageSubresourceRange = vk::ImageSubre
|
||||||
base_array_layer: 0,
|
base_array_layer: 0,
|
||||||
layer_count: vk::REMAINING_ARRAY_LAYERS,
|
layer_count: vk::REMAINING_ARRAY_LAYERS,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait IntoImageSubresourceLayers {
|
|
||||||
fn as_layers(&self) -> vk::ImageSubresourceLayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait IntoImageSubresourceRange {
|
|
||||||
fn as_range(&self) -> vk::ImageSubresourceRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoImageSubresourceLayers for vk::ImageSubresource {
|
|
||||||
fn as_layers(&self) -> vk::ImageSubresourceLayers {
|
|
||||||
vk::ImageSubresourceLayers {
|
|
||||||
aspect_mask: self.aspect_mask,
|
|
||||||
mip_level: self.mip_level,
|
|
||||||
base_array_layer: self.array_layer,
|
|
||||||
layer_count: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IntoImageSubresourceRange for T
|
|
||||||
where
|
|
||||||
T: IntoImageSubresourceLayers,
|
|
||||||
{
|
|
||||||
fn as_range(&self) -> vk::ImageSubresourceRange {
|
|
||||||
self.as_layers().as_range()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoImageSubresourceRange for vk::ImageSubresourceLayers {
|
|
||||||
fn as_range(&self) -> vk::ImageSubresourceRange {
|
|
||||||
vk::ImageSubresourceRange {
|
|
||||||
aspect_mask: self.aspect_mask,
|
|
||||||
base_mip_level: self.mip_level,
|
|
||||||
level_count: 1,
|
|
||||||
base_array_layer: self.base_array_layer,
|
|
||||||
layer_count: self.layer_count,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoImageSubresourceRange for vk::ImageSubresourceRange {
|
|
||||||
fn as_range(&self) -> vk::ImageSubresourceRange {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ImageSubresourceExt {
|
|
||||||
fn aspects(&self) -> vk::ImageAspectFlags;
|
|
||||||
fn layers(&self) -> MipRange;
|
|
||||||
fn mip_levels(&self) -> MipRange;
|
|
||||||
fn contains(&self, other: &Self) -> bool {
|
|
||||||
self.aspects().contains(other.aspects())
|
|
||||||
&& self.layers().contains(other.layers())
|
|
||||||
&& self.mip_levels().contains(other.mip_levels())
|
|
||||||
}
|
|
||||||
fn intersects(&self, other: &Self) -> bool {
|
|
||||||
self.aspects().intersects(other.aspects())
|
|
||||||
&& self.layers().intersects(other.layers())
|
|
||||||
&& self.mip_levels().intersects(other.mip_levels())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageSubresourceExt for vk::ImageSubresourceLayers {
|
|
||||||
fn aspects(&self) -> vk::ImageAspectFlags {
|
|
||||||
self.aspect_mask
|
|
||||||
}
|
|
||||||
fn layers(&self) -> MipRange {
|
|
||||||
MipRange::new(
|
|
||||||
self.base_array_layer,
|
|
||||||
self.base_array_layer + self.layer_count,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mip_levels(&self) -> MipRange {
|
|
||||||
MipRange::single(self.mip_level)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageSubresourceExt for vk::ImageSubresourceRange {
|
|
||||||
fn aspects(&self) -> vk::ImageAspectFlags {
|
|
||||||
self.aspect_mask
|
|
||||||
}
|
|
||||||
fn layers(&self) -> MipRange {
|
|
||||||
MipRange::new(
|
|
||||||
self.base_array_layer,
|
|
||||||
self.base_array_layer + self.layer_count,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mip_levels(&self) -> MipRange {
|
|
||||||
MipRange::new(self.base_mip_level, self.base_mip_level + self.level_count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageSubresourceExt for vk::ImageSubresource {
|
|
||||||
fn aspects(&self) -> vk::ImageAspectFlags {
|
|
||||||
self.aspect_mask
|
|
||||||
}
|
|
||||||
fn layers(&self) -> MipRange {
|
|
||||||
MipRange::single(self.array_layer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mip_levels(&self) -> MipRange {
|
|
||||||
MipRange::single(self.mip_level)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue