use super::{
CommandInfo, PrimaryAutoCommandBuffer, RenderPassCommand, Resource, ResourceUseRef2,
SubmitState,
};
use crate::{
buffer::{Buffer, IndexBuffer, Subbuffer},
command_buffer::{
allocator::{CommandBufferAllocator, StandardCommandBufferAllocator},
sys::{CommandBufferBeginInfo, UnsafeCommandBuffer, UnsafeCommandBufferBuilder},
CommandBufferBufferRangeUsage, CommandBufferBufferUsage, CommandBufferImageRangeUsage,
CommandBufferImageUsage, CommandBufferInheritanceInfo,
CommandBufferInheritanceRenderPassType, CommandBufferLevel, CommandBufferResourcesUsage,
CommandBufferUsage, RenderingInfo, ResourceUseRef, SecondaryAutoCommandBuffer,
SecondaryCommandBufferBufferUsage, SecondaryCommandBufferImageUsage,
SecondaryCommandBufferResourcesUsage, SubpassContents,
},
descriptor_set::{DescriptorSetResources, DescriptorSetWithOffsets},
device::{Device, DeviceOwned},
image::{view::ImageView, Image, ImageAspects, ImageLayout, ImageSubresourceRange},
pipeline::{
graphics::{
color_blend::LogicOp,
depth_stencil::{CompareOp, StencilOps},
input_assembly::PrimitiveTopology,
rasterization::{CullMode, DepthBiasState, FrontFace, LineStipple},
subpass::PipelineRenderingCreateInfo,
viewport::{Scissor, Viewport},
},
ComputePipeline, DynamicState, GraphicsPipeline, PipelineBindPoint, PipelineLayout,
},
query::{QueryControlFlags, QueryPool},
range_map::RangeMap,
range_set::RangeSet,
render_pass::{Framebuffer, Subpass},
sync::{
AccessFlags, BufferMemoryBarrier, DependencyFlags, DependencyInfo, ImageMemoryBarrier,
PipelineStageAccessFlags, PipelineStages,
},
DeviceSize, Validated, ValidationError, VulkanError,
};
use ahash::HashMap;
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{
collections::hash_map::Entry,
fmt::Debug,
marker::PhantomData,
mem::take,
ops::{Range, RangeInclusive},
sync::{atomic::AtomicBool, Arc},
};
pub struct AutoCommandBufferBuilder<L, A = StandardCommandBufferAllocator>
where
A: CommandBufferAllocator,
{
pub(in crate::command_buffer) inner: UnsafeCommandBufferBuilder<A>,
commands: Vec<(
CommandInfo,
Box<dyn Fn(&mut UnsafeCommandBufferBuilder<A>) + Send + Sync + 'static>,
)>,
pub(in crate::command_buffer) builder_state: CommandBufferBuilderState,
_data: PhantomData<L>,
}
impl<A> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, A>
where
A: CommandBufferAllocator,
{
#[inline]
pub fn primary(
allocator: &A,
queue_family_index: u32,
usage: CommandBufferUsage,
) -> Result<AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<A>, A>, Validated<VulkanError>>
{
unsafe {
AutoCommandBufferBuilder::begin(
allocator,
queue_family_index,
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
usage,
inheritance_info: None,
_ne: crate::NonExhaustive(()),
},
)
}
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn primary_unchecked(
allocator: &A,
queue_family_index: u32,
usage: CommandBufferUsage,
) -> Result<AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<A>, A>, VulkanError> {
AutoCommandBufferBuilder::begin_unchecked(
allocator,
queue_family_index,
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
usage,
inheritance_info: None,
_ne: crate::NonExhaustive(()),
},
)
}
}
impl<A> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, A>
where
A: CommandBufferAllocator,
{
#[inline]
pub fn secondary(
allocator: &A,
queue_family_index: u32,
usage: CommandBufferUsage,
inheritance_info: CommandBufferInheritanceInfo,
) -> Result<AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<A>, A>, Validated<VulkanError>>
{
unsafe {
AutoCommandBufferBuilder::begin(
allocator,
queue_family_index,
CommandBufferLevel::Secondary,
CommandBufferBeginInfo {
usage,
inheritance_info: Some(inheritance_info),
_ne: crate::NonExhaustive(()),
},
)
}
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn secondary_unchecked(
allocator: &A,
queue_family_index: u32,
usage: CommandBufferUsage,
inheritance_info: CommandBufferInheritanceInfo,
) -> Result<AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<A>, A>, VulkanError> {
AutoCommandBufferBuilder::begin_unchecked(
allocator,
queue_family_index,
CommandBufferLevel::Secondary,
CommandBufferBeginInfo {
usage,
inheritance_info: Some(inheritance_info),
_ne: crate::NonExhaustive(()),
},
)
}
}
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
unsafe fn begin(
allocator: &A,
queue_family_index: u32,
level: CommandBufferLevel,
begin_info: CommandBufferBeginInfo,
) -> Result<AutoCommandBufferBuilder<L, A>, Validated<VulkanError>> {
Self::validate_begin(allocator.device(), queue_family_index, level, &begin_info)?;
unsafe {
Ok(Self::begin_unchecked(
allocator,
queue_family_index,
level,
begin_info,
)?)
}
}
fn validate_begin(
device: &Device,
_queue_family_index: u32,
_level: CommandBufferLevel,
begin_info: &CommandBufferBeginInfo,
) -> Result<(), Box<ValidationError>> {
begin_info
.validate(device)
.map_err(|err| err.add_context("begin_info"))?;
Ok(())
}
#[inline]
unsafe fn begin_unchecked(
allocator: &A,
queue_family_index: u32,
level: CommandBufferLevel,
begin_info: CommandBufferBeginInfo,
) -> Result<Self, VulkanError> {
let &CommandBufferBeginInfo {
usage: _,
ref inheritance_info,
_ne: _,
} = &begin_info;
let mut builder_state: CommandBufferBuilderState = Default::default();
if let Some(inheritance_info) = &inheritance_info {
let &CommandBufferInheritanceInfo {
ref render_pass,
occlusion_query: _,
query_statistics_flags: _,
_ne: _,
} = inheritance_info;
if let Some(render_pass) = render_pass {
builder_state.render_pass = Some(RenderPassState::from_inheritance(render_pass));
}
}
let inner =
UnsafeCommandBufferBuilder::new(allocator, queue_family_index, level, begin_info)?;
Ok(AutoCommandBufferBuilder {
inner,
commands: Vec::new(),
builder_state,
_data: PhantomData,
})
}
unsafe fn end_unchecked(
mut self,
) -> Result<
(
UnsafeCommandBuffer<A>,
Vec<Box<dyn Fn(&mut UnsafeCommandBufferBuilder<A>) + Send + Sync + 'static>>,
CommandBufferResourcesUsage,
SecondaryCommandBufferResourcesUsage,
),
Validated<VulkanError>,
> {
let mut auto_sync_state = AutoSyncState::new(
self.device().clone(),
self.inner.level(),
self.inner
.inheritance_info()
.as_ref()
.map_or(false, |info| info.render_pass.is_some()),
);
for (command_info, _) in self.commands.iter() {
auto_sync_state.add_command(command_info).map_err(|err| {
Box::new(ValidationError {
problem: format!(
"unsolvable resource conflict between:\n\
command resource use: {:?}\n\
previous conflicting command resource use: {:?}",
err.current_use_ref, err.previous_use_ref,
)
.into(),
..Default::default()
})
})?;
}
let (mut barriers, resources_usage, secondary_resources_usage) = auto_sync_state.build();
let final_barrier_index = self.commands.len();
for (command_index, (_, record_func)) in self.commands.iter().enumerate() {
if let Some(barriers) = barriers.remove(&command_index) {
for dependency_info in barriers {
unsafe {
#[cfg(debug_assertions)]
self.inner
.pipeline_barrier(&dependency_info)
.expect("bug in Vulkano");
#[cfg(not(debug_assertions))]
self.inner.pipeline_barrier_unchecked(&dependency_info);
}
}
}
record_func(&mut self.inner);
}
if let Some(final_barriers) = barriers.remove(&final_barrier_index) {
for dependency_info in final_barriers {
unsafe {
#[cfg(debug_assertions)]
self.inner
.pipeline_barrier(&dependency_info)
.expect("bug in Vulkano");
#[cfg(not(debug_assertions))]
self.inner.pipeline_barrier_unchecked(&dependency_info);
}
}
}
debug_assert!(barriers.is_empty());
Ok((
self.inner.build()?,
self.commands
.into_iter()
.map(|(_, record_func)| record_func)
.collect(),
resources_usage,
secondary_resources_usage,
))
}
}
impl<A> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<A>, A>
where
A: CommandBufferAllocator,
{
pub fn build(self) -> Result<Arc<PrimaryAutoCommandBuffer<A>>, Validated<VulkanError>> {
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is still active".into(),
vuids: &["VUID-vkEndCommandBuffer-commandBuffer-00060"],
..Default::default()
})
.into());
}
if !self.builder_state.queries.is_empty() {
return Err(Box::new(ValidationError {
problem: "a query is still active".into(),
vuids: &["VUID-vkEndCommandBuffer-commandBuffer-00061"],
..Default::default()
})
.into());
}
let (inner, keep_alive_objects, resources_usage, _) = unsafe { self.end_unchecked()? };
Ok(Arc::new(PrimaryAutoCommandBuffer {
inner,
_keep_alive_objects: keep_alive_objects,
resources_usage,
state: Mutex::new(Default::default()),
}))
}
}
impl<A> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<A>, A>
where
A: CommandBufferAllocator,
{
pub fn build(self) -> Result<Arc<SecondaryAutoCommandBuffer<A>>, Validated<VulkanError>> {
if !self.builder_state.queries.is_empty() {
return Err(Box::new(ValidationError {
problem: "a query is still active".into(),
vuids: &["VUID-vkEndCommandBuffer-commandBuffer-00061"],
..Default::default()
})
.into());
}
let submit_state = match self.inner.usage() {
CommandBufferUsage::MultipleSubmit => SubmitState::ExclusiveUse {
in_use: AtomicBool::new(false),
},
CommandBufferUsage::SimultaneousUse => SubmitState::Concurrent,
CommandBufferUsage::OneTimeSubmit => SubmitState::OneTime {
already_submitted: AtomicBool::new(false),
},
};
let (inner, keep_alive_objects, _, resources_usage) = unsafe { self.end_unchecked()? };
Ok(Arc::new(SecondaryAutoCommandBuffer {
inner,
_keep_alive_objects: keep_alive_objects,
resources_usage,
submit_state,
}))
}
}
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
pub(in crate::command_buffer) fn add_command(
&mut self,
name: &'static str,
used_resources: Vec<(ResourceUseRef2, Resource)>,
record_func: impl Fn(&mut UnsafeCommandBufferBuilder<A>) + Send + Sync + 'static,
) {
self.commands.push((
CommandInfo {
name,
used_resources,
render_pass: RenderPassCommand::None,
},
Box::new(record_func),
));
}
pub(in crate::command_buffer) fn add_render_pass_begin(
&mut self,
name: &'static str,
used_resources: Vec<(ResourceUseRef2, Resource)>,
record_func: impl Fn(&mut UnsafeCommandBufferBuilder<A>) + Send + Sync + 'static,
) {
self.commands.push((
CommandInfo {
name,
used_resources,
render_pass: RenderPassCommand::Begin,
},
Box::new(record_func),
));
}
pub(in crate::command_buffer) fn add_render_pass_end(
&mut self,
name: &'static str,
used_resources: Vec<(ResourceUseRef2, Resource)>,
record_func: impl Fn(&mut UnsafeCommandBufferBuilder<A>) + Send + Sync + 'static,
) {
self.commands.push((
CommandInfo {
name,
used_resources,
render_pass: RenderPassCommand::End,
},
Box::new(record_func),
));
}
}
unsafe impl<L, A> DeviceOwned for AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
struct AutoSyncState {
device: Arc<Device>,
level: CommandBufferLevel,
command_index: usize,
barriers: HashMap<usize, Vec<DependencyInfo>>,
pending_barrier: DependencyInfo,
first_unflushed: usize,
latest_render_pass_enter: Option<usize>,
buffers: HashMap<Arc<Buffer>, RangeMap<DeviceSize, BufferState>>,
images: HashMap<Arc<Image>, RangeMap<DeviceSize, ImageState>>,
secondary_resources_usage: SecondaryCommandBufferResourcesUsage,
}
impl AutoSyncState {
fn new(
device: Arc<Device>,
level: CommandBufferLevel,
has_inherited_render_pass: bool,
) -> Self {
Self {
device,
level,
command_index: 0,
pending_barrier: DependencyInfo {
dependency_flags: DependencyFlags::BY_REGION,
..DependencyInfo::default()
},
barriers: Default::default(),
first_unflushed: 0,
latest_render_pass_enter: has_inherited_render_pass.then_some(0),
buffers: HashMap::default(),
images: HashMap::default(),
secondary_resources_usage: Default::default(),
}
}
fn build(
mut self,
) -> (
HashMap<usize, Vec<DependencyInfo>>,
CommandBufferResourcesUsage,
SecondaryCommandBufferResourcesUsage,
) {
debug_assert!(self.latest_render_pass_enter.is_none() || self.pending_barrier.is_empty());
self.barriers
.entry(self.first_unflushed)
.or_default()
.push(take(&mut self.pending_barrier));
if self.level == CommandBufferLevel::Primary {
let mut final_barrier = DependencyInfo {
dependency_flags: DependencyFlags::BY_REGION,
..DependencyInfo::default()
};
for (image, range_map) in self.images.iter_mut() {
for (range, state) in range_map
.iter_mut()
.filter(|(_range, state)| state.final_layout != state.current_layout)
{
final_barrier
.image_memory_barriers
.push(ImageMemoryBarrier {
src_stages: PipelineStages::from(state.memory_access)
.into_supported(&self.device),
src_access: AccessFlags::from(state.memory_access)
.into_supported(&self.device),
dst_stages: PipelineStages::TOP_OF_PIPE,
dst_access: AccessFlags::empty(),
old_layout: state.current_layout,
new_layout: state.final_layout,
subresource_range: image.range_to_subresources(range.clone()),
..ImageMemoryBarrier::image(image.clone())
});
state.is_written = true;
}
}
self.barriers
.entry(self.command_index)
.or_default()
.push(final_barrier);
}
let mut resources_usage = CommandBufferResourcesUsage {
buffers: self
.buffers
.into_iter()
.map(|(buffer, ranges)| CommandBufferBufferUsage {
buffer,
ranges: ranges
.into_iter()
.filter(|(_range, state)| !state.resource_uses.is_empty())
.map(|(range, state)| {
let first_use = state.resource_uses.into_iter().next();
(
range,
CommandBufferBufferRangeUsage {
first_use,
mutable: state.is_written,
},
)
})
.collect(),
})
.collect(),
images: self
.images
.into_iter()
.map(|(image, ranges)| CommandBufferImageUsage {
image,
ranges: ranges
.into_iter()
.filter(|(_range, state)| {
!state.resource_uses.is_empty()
|| (self.level == CommandBufferLevel::Primary
&& state.current_layout != state.final_layout)
})
.map(|(range, mut state)| {
if self.level == CommandBufferLevel::Primary {
state.current_layout = state.final_layout;
}
let first_use = state.resource_uses.into_iter().next();
(
range,
CommandBufferImageRangeUsage {
first_use,
mutable: state.is_written,
expected_layout: state.initial_layout,
final_layout: state.current_layout,
},
)
})
.collect(),
})
.collect(),
buffer_indices: Default::default(),
image_indices: Default::default(),
};
resources_usage.buffer_indices = resources_usage
.buffers
.iter()
.enumerate()
.map(|(index, usage)| (usage.buffer.clone(), index))
.collect();
resources_usage.image_indices = resources_usage
.images
.iter()
.enumerate()
.map(|(index, usage)| (usage.image.clone(), index))
.collect();
(
self.barriers,
resources_usage,
self.secondary_resources_usage,
)
}
fn add_command(
&mut self,
command_info: &CommandInfo,
) -> Result<(), UnsolvableResourceConflict> {
self.check_resource_conflicts(command_info)?;
self.add_resources(command_info);
match command_info.render_pass {
RenderPassCommand::None => (),
RenderPassCommand::Begin => {
debug_assert!(self.latest_render_pass_enter.is_none());
self.latest_render_pass_enter = Some(self.command_index);
}
RenderPassCommand::End => {
debug_assert!(self.latest_render_pass_enter.is_some());
self.latest_render_pass_enter = None;
}
}
self.command_index += 1;
Ok(())
}
fn check_resource_conflicts(
&self,
command_info: &CommandInfo,
) -> Result<(), UnsolvableResourceConflict> {
let &CommandInfo {
name: command_name,
ref used_resources,
..
} = command_info;
for (use_ref, resource) in used_resources {
match *resource {
Resource::Buffer {
ref buffer,
ref range,
memory_access,
} => {
debug_assert!(range.start <= range.end);
debug_assert!(range.end <= buffer.size());
if let Some(previous_use_ref) = self.find_buffer_conflict(
self.command_index,
buffer,
range.clone(),
memory_access,
) {
return Err(UnsolvableResourceConflict {
current_use_ref: ResourceUseRef {
command_index: self.command_index,
command_name,
resource_in_command: use_ref.resource_in_command,
secondary_use_ref: use_ref.secondary_use_ref,
},
previous_use_ref,
});
}
}
Resource::Image {
ref image,
ref subresource_range,
memory_access,
start_layout,
end_layout,
} => {
debug_assert!(image.format().aspects().contains(subresource_range.aspects));
debug_assert!(
subresource_range.mip_levels.start <= subresource_range.mip_levels.end
);
debug_assert!(subresource_range.mip_levels.end <= image.mip_levels());
debug_assert!(
subresource_range.array_layers.start <= subresource_range.array_layers.end
);
debug_assert!(subresource_range.array_layers.end <= image.array_layers());
debug_assert!(memory_access.contains_write() || start_layout == end_layout);
debug_assert!(end_layout != ImageLayout::Undefined);
debug_assert!(end_layout != ImageLayout::Preinitialized);
if let Some(previous_use) = self.find_image_conflict(
image,
subresource_range.clone(),
memory_access,
start_layout,
end_layout,
) {
return Err(UnsolvableResourceConflict {
current_use_ref: ResourceUseRef {
command_index: self.command_index,
command_name,
resource_in_command: use_ref.resource_in_command,
secondary_use_ref: use_ref.secondary_use_ref,
},
previous_use_ref: previous_use,
});
}
}
}
}
Ok(())
}
fn find_buffer_conflict(
&self,
command_index: usize,
buffer: &Subbuffer<[u8]>,
mut range: Range<DeviceSize>,
memory_access: PipelineStageAccessFlags,
) -> Option<ResourceUseRef> {
let last_allowed_barrier_index = self.latest_render_pass_enter.unwrap_or(command_index);
range.start += buffer.offset();
range.end += buffer.offset();
let range_map = self.buffers.get(buffer.buffer())?;
for (_range, state) in range_map
.range(&range)
.filter(|(_range, state)| !state.resource_uses.is_empty())
{
debug_assert!(state
.resource_uses
.iter()
.all(|resource_use| resource_use.command_index <= command_index));
if memory_access.contains_write() || state.memory_access.contains_write() {
if let Some(&use_ref) = state
.resource_uses
.iter()
.find(|resource_use| resource_use.command_index >= last_allowed_barrier_index)
{
return Some(use_ref);
}
}
}
None
}
fn find_image_conflict(
&self,
image: &Image,
subresource_range: ImageSubresourceRange,
memory_access: PipelineStageAccessFlags,
start_layout: ImageLayout,
_end_layout: ImageLayout,
) -> Option<ResourceUseRef> {
let last_allowed_barrier_index =
self.latest_render_pass_enter.unwrap_or(self.command_index);
let range_map = self.images.get(image)?;
for range in image.iter_ranges(subresource_range) {
for (_range, state) in range_map
.range(&range)
.filter(|(_range, state)| !state.resource_uses.is_empty())
{
debug_assert!(state
.resource_uses
.iter()
.all(|resource_use| resource_use.command_index <= self.command_index));
let start_layout = if start_layout == ImageLayout::Undefined {
state.current_layout
} else {
start_layout
};
if memory_access.contains_write()
|| state.memory_access.contains_write()
|| state.current_layout != start_layout
{
if let Some(&use_ref) = state.resource_uses.iter().find(|resource_use| {
resource_use.command_index >= last_allowed_barrier_index
}) {
return Some(use_ref);
}
}
}
}
None
}
fn add_resources(&mut self, command_info: &CommandInfo) {
let &CommandInfo {
name: command_name,
ref used_resources,
..
} = command_info;
for (use_ref, resource) in used_resources {
match *resource {
Resource::Buffer {
ref buffer,
ref range,
memory_access,
} => {
self.add_buffer(
ResourceUseRef {
command_index: self.command_index,
command_name,
resource_in_command: use_ref.resource_in_command,
secondary_use_ref: use_ref.secondary_use_ref,
},
buffer.clone(),
range.clone(),
memory_access,
);
}
Resource::Image {
ref image,
ref subresource_range,
memory_access,
start_layout,
end_layout,
} => {
self.add_image(
ResourceUseRef {
command_index: self.command_index,
command_name,
resource_in_command: use_ref.resource_in_command,
secondary_use_ref: use_ref.secondary_use_ref,
},
image.clone(),
subresource_range.clone(),
memory_access,
start_layout,
end_layout,
);
}
}
}
}
fn add_buffer(
&mut self,
use_ref: ResourceUseRef,
buffer: Subbuffer<[u8]>,
mut range: Range<DeviceSize>,
memory_access: PipelineStageAccessFlags,
) {
self.secondary_resources_usage
.buffers
.push(SecondaryCommandBufferBufferUsage {
use_ref,
buffer: buffer.clone(),
range: range.clone(),
memory_access,
});
let last_allowed_barrier_index =
self.latest_render_pass_enter.unwrap_or(self.command_index);
range.start += buffer.offset();
range.end += buffer.offset();
let range_map = self
.buffers
.entry(buffer.buffer().clone())
.or_insert_with(|| {
[(
0..buffer.buffer().size(),
BufferState {
resource_uses: Vec::new(),
memory_access: PipelineStageAccessFlags::empty(),
is_written: false,
},
)]
.into_iter()
.collect()
});
range_map.split_at(&range.start);
range_map.split_at(&range.end);
for (range, state) in range_map.range_mut(&range) {
if state.resource_uses.is_empty() {
state.resource_uses.push(use_ref);
state.memory_access = memory_access;
state.is_written = memory_access.contains_write();
match self.level {
CommandBufferLevel::Primary => {
let barrier = BufferMemoryBarrier {
src_stages: PipelineStages::ALL_COMMANDS,
src_access: AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE,
dst_stages: PipelineStages::ALL_COMMANDS,
dst_access: AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE,
range: range.clone(),
..BufferMemoryBarrier::buffer(buffer.buffer().clone())
};
self.pending_barrier.buffer_memory_barriers.push(barrier);
}
CommandBufferLevel::Secondary => (),
}
} else {
if memory_access.contains_write() || state.memory_access.contains_write() {
if state
.resource_uses
.iter()
.any(|resource_use| resource_use.command_index >= self.first_unflushed)
{
self.barriers
.entry(self.first_unflushed)
.or_default()
.push(take(&mut self.pending_barrier));
self.first_unflushed = last_allowed_barrier_index;
}
self.pending_barrier
.buffer_memory_barriers
.push(BufferMemoryBarrier {
src_stages: PipelineStages::from(state.memory_access)
.into_supported(&self.device),
src_access: AccessFlags::from(state.memory_access)
.into_supported(&self.device),
dst_stages: PipelineStages::from(memory_access)
.into_supported(&self.device),
dst_access: AccessFlags::from(memory_access)
.into_supported(&self.device),
range: range.clone(),
..BufferMemoryBarrier::buffer(buffer.buffer().clone())
});
state.memory_access = memory_access;
state.is_written = true;
} else {
state.memory_access |= memory_access;
}
state.resource_uses.push(use_ref);
}
}
}
fn add_image(
&mut self,
use_ref: ResourceUseRef,
image: Arc<Image>,
mut subresource_range: ImageSubresourceRange,
memory_access: PipelineStageAccessFlags,
start_layout: ImageLayout,
end_layout: ImageLayout,
) {
self.secondary_resources_usage
.images
.push(SecondaryCommandBufferImageUsage {
use_ref,
image: image.clone(),
subresource_range: subresource_range.clone(),
memory_access,
start_layout,
end_layout,
});
let last_allowed_barrier_index =
self.latest_render_pass_enter.unwrap_or(self.command_index);
if !self
.device
.enabled_features()
.separate_depth_stencil_layouts
&& image
.format()
.aspects()
.contains(ImageAspects::DEPTH | ImageAspects::STENCIL)
{
subresource_range.aspects = ImageAspects::DEPTH | ImageAspects::STENCIL;
}
let range_map = self.images.entry(image.clone()).or_insert_with(|| {
[(
0..image.range_size(),
match self.level {
CommandBufferLevel::Primary => {
let initial_layout = if !image.is_layout_initialized() {
unsafe {
image.layout_initialized();
}
image.initial_layout()
} else {
image.initial_layout_requirement()
};
ImageState {
resource_uses: Vec::new(),
memory_access: PipelineStageAccessFlags::empty(),
is_written: false,
initial_layout,
current_layout: initial_layout,
final_layout: image.final_layout_requirement(),
}
}
CommandBufferLevel::Secondary => {
ImageState {
resource_uses: Vec::new(),
memory_access: PipelineStageAccessFlags::empty(),
is_written: false,
initial_layout: ImageLayout::Undefined,
current_layout: ImageLayout::Undefined,
final_layout: ImageLayout::Undefined,
}
}
},
)]
.into_iter()
.collect()
});
for range in image.iter_ranges(subresource_range) {
range_map.split_at(&range.start);
range_map.split_at(&range.end);
for (range, state) in range_map.range_mut(&range) {
if state.resource_uses.is_empty() {
debug_assert_eq!(state.initial_layout, state.current_layout);
state.resource_uses.push(use_ref);
state.memory_access = memory_access;
state.is_written = memory_access.contains_write();
state.current_layout = end_layout;
match self.level {
CommandBufferLevel::Primary => {
let mut barrier = ImageMemoryBarrier {
src_stages: PipelineStages::ALL_COMMANDS,
src_access: AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE,
dst_stages: PipelineStages::ALL_COMMANDS,
dst_access: AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE,
old_layout: state.initial_layout,
new_layout: start_layout,
subresource_range: image.range_to_subresources(range.clone()),
..ImageMemoryBarrier::image(image.clone())
};
match barrier.new_layout {
ImageLayout::Undefined => {
barrier.new_layout = if !self
.device
.enabled_features()
.synchronization2
&& matches!(
barrier.old_layout,
ImageLayout::Undefined | ImageLayout::Preinitialized
) {
image.final_layout_requirement()
} else {
barrier.old_layout
};
}
ImageLayout::Preinitialized => {
if state.initial_layout != ImageLayout::Preinitialized {
panic!(
"The command requires the `Preinitialized layout`, but
the initial layout of the image is not `Preinitialized`"
);
}
if !self.device.enabled_features().synchronization2 {
panic!(
"The command requires the `Preinitialized` layout, \
but this is not allowed in pipeline barriers without \
the `synchronization2` feature enabled"
);
}
}
_ => (),
}
if barrier.old_layout != barrier.new_layout {
state.is_written = true;
}
self.pending_barrier.image_memory_barriers.push(barrier);
}
CommandBufferLevel::Secondary => {
state.initial_layout = start_layout;
}
}
} else {
let start_layout = if start_layout == ImageLayout::Undefined {
state.current_layout
} else {
start_layout
};
if memory_access.contains_write()
|| state.memory_access.contains_write()
|| state.current_layout != start_layout
{
if state
.resource_uses
.iter()
.any(|resource_use| resource_use.command_index >= self.first_unflushed)
|| state.current_layout != start_layout
{
self.barriers
.entry(self.first_unflushed)
.or_default()
.push(take(&mut self.pending_barrier));
self.first_unflushed = last_allowed_barrier_index;
}
self.pending_barrier
.image_memory_barriers
.push(ImageMemoryBarrier {
src_stages: PipelineStages::from(state.memory_access)
.into_supported(&self.device),
src_access: AccessFlags::from(state.memory_access)
.into_supported(&self.device),
dst_stages: PipelineStages::from(memory_access)
.into_supported(&self.device),
dst_access: AccessFlags::from(memory_access)
.into_supported(&self.device),
old_layout: state.current_layout,
new_layout: start_layout,
subresource_range: image.range_to_subresources(range.clone()),
..ImageMemoryBarrier::image(image.clone())
});
state.memory_access = memory_access;
state.is_written = true;
if memory_access.contains_write() || end_layout != ImageLayout::Undefined {
state.current_layout = end_layout;
}
} else {
state.memory_access |= memory_access;
}
state.resource_uses.push(use_ref);
}
}
}
}
}
#[derive(Clone, Debug)]
struct UnsolvableResourceConflict {
current_use_ref: ResourceUseRef,
previous_use_ref: ResourceUseRef,
}
#[derive(Clone, Debug, PartialEq, Eq)]
struct BufferState {
resource_uses: Vec<ResourceUseRef>,
memory_access: PipelineStageAccessFlags,
is_written: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
struct ImageState {
resource_uses: Vec<ResourceUseRef>,
memory_access: PipelineStageAccessFlags,
is_written: bool,
initial_layout: ImageLayout,
current_layout: ImageLayout,
final_layout: ImageLayout,
}
#[derive(Default)]
pub(in crate::command_buffer) struct CommandBufferBuilderState {
pub(in crate::command_buffer) render_pass: Option<RenderPassState>,
pub(in crate::command_buffer) descriptor_sets: HashMap<PipelineBindPoint, DescriptorSetState>,
pub(in crate::command_buffer) index_buffer: Option<IndexBuffer>,
pub(in crate::command_buffer) pipeline_compute: Option<Arc<ComputePipeline>>,
pub(in crate::command_buffer) pipeline_graphics: Option<Arc<GraphicsPipeline>>,
pub(in crate::command_buffer) vertex_buffers: HashMap<u32, Subbuffer<[u8]>>,
pub(in crate::command_buffer) push_constants: RangeSet<u32>,
pub(in crate::command_buffer) push_constants_pipeline_layout: Option<Arc<PipelineLayout>>,
pub(in crate::command_buffer) blend_constants: Option<[f32; 4]>,
pub(in crate::command_buffer) color_write_enable: Option<SmallVec<[bool; 4]>>,
pub(in crate::command_buffer) cull_mode: Option<CullMode>,
pub(in crate::command_buffer) depth_bias: Option<DepthBiasState>,
pub(in crate::command_buffer) depth_bias_enable: Option<bool>,
pub(in crate::command_buffer) depth_bounds: Option<RangeInclusive<f32>>,
pub(in crate::command_buffer) depth_bounds_test_enable: Option<bool>,
pub(in crate::command_buffer) depth_compare_op: Option<CompareOp>,
pub(in crate::command_buffer) depth_test_enable: Option<bool>,
pub(in crate::command_buffer) depth_write_enable: Option<bool>,
pub(in crate::command_buffer) discard_rectangle: HashMap<u32, Scissor>,
pub(in crate::command_buffer) front_face: Option<FrontFace>,
pub(in crate::command_buffer) line_stipple: Option<LineStipple>,
pub(in crate::command_buffer) line_width: Option<f32>,
pub(in crate::command_buffer) logic_op: Option<LogicOp>,
pub(in crate::command_buffer) patch_control_points: Option<u32>,
pub(in crate::command_buffer) primitive_restart_enable: Option<bool>,
pub(in crate::command_buffer) primitive_topology: Option<PrimitiveTopology>,
pub(in crate::command_buffer) rasterizer_discard_enable: Option<bool>,
pub(in crate::command_buffer) scissor: HashMap<u32, Scissor>,
pub(in crate::command_buffer) scissor_with_count: Option<SmallVec<[Scissor; 2]>>,
pub(in crate::command_buffer) stencil_compare_mask: StencilStateDynamic,
pub(in crate::command_buffer) stencil_op: StencilOpStateDynamic,
pub(in crate::command_buffer) stencil_reference: StencilStateDynamic,
pub(in crate::command_buffer) stencil_test_enable: Option<bool>,
pub(in crate::command_buffer) stencil_write_mask: StencilStateDynamic,
pub(in crate::command_buffer) viewport: HashMap<u32, Viewport>,
pub(in crate::command_buffer) viewport_with_count: Option<SmallVec<[Viewport; 2]>>,
pub(in crate::command_buffer) queries: HashMap<ash::vk::QueryType, QueryState>,
}
impl CommandBufferBuilderState {
pub(in crate::command_buffer) fn reset_non_render_pass_states(&mut self) {
*self = Self {
render_pass: take(&mut self.render_pass),
..Default::default()
}
}
pub(in crate::command_buffer) fn reset_dynamic_states(
&mut self,
states: impl IntoIterator<Item = DynamicState>,
) {
for state in states {
match state {
DynamicState::BlendConstants => self.blend_constants = None,
DynamicState::ColorWriteEnable => self.color_write_enable = None,
DynamicState::CullMode => self.cull_mode = None,
DynamicState::DepthBias => self.depth_bias = None,
DynamicState::DepthBiasEnable => self.depth_bias_enable = None,
DynamicState::DepthBounds => self.depth_bounds = None,
DynamicState::DepthBoundsTestEnable => self.depth_bounds_test_enable = None,
DynamicState::DepthCompareOp => self.depth_compare_op = None,
DynamicState::DepthTestEnable => self.depth_test_enable = None,
DynamicState::DepthWriteEnable => self.depth_write_enable = None,
DynamicState::DiscardRectangle => self.discard_rectangle.clear(),
DynamicState::FrontFace => self.front_face = None,
DynamicState::LineStipple => self.line_stipple = None,
DynamicState::LineWidth => self.line_width = None,
DynamicState::LogicOp => self.logic_op = None,
DynamicState::PatchControlPoints => self.patch_control_points = None,
DynamicState::PrimitiveRestartEnable => self.primitive_restart_enable = None,
DynamicState::PrimitiveTopology => self.primitive_topology = None,
DynamicState::RasterizerDiscardEnable => self.rasterizer_discard_enable = None,
DynamicState::Scissor => self.scissor.clear(),
DynamicState::ScissorWithCount => self.scissor_with_count = None,
DynamicState::StencilCompareMask => self.stencil_compare_mask = Default::default(),
DynamicState::StencilOp => self.stencil_op = Default::default(),
DynamicState::StencilReference => self.stencil_reference = Default::default(),
DynamicState::StencilTestEnable => self.stencil_test_enable = None,
DynamicState::StencilWriteMask => self.stencil_write_mask = Default::default(),
DynamicState::Viewport => self.viewport.clear(),
DynamicState::ViewportWithCount => self.viewport_with_count = None,
}
}
}
pub(in crate::command_buffer) fn invalidate_descriptor_sets(
&mut self,
pipeline_bind_point: PipelineBindPoint,
pipeline_layout: Arc<PipelineLayout>,
first_set: u32,
num_descriptor_sets: u32,
) -> &mut DescriptorSetState {
match self.descriptor_sets.entry(pipeline_bind_point) {
Entry::Vacant(entry) => entry.insert(DescriptorSetState {
descriptor_sets: Default::default(),
pipeline_layout,
}),
Entry::Occupied(entry) => {
let state = entry.into_mut();
let invalidate_from = if state.pipeline_layout == pipeline_layout {
None
} else if state.pipeline_layout.push_constant_ranges()
!= pipeline_layout.push_constant_ranges()
{
Some(0)
} else {
let current_layouts = state.pipeline_layout.set_layouts();
let new_layouts = pipeline_layout.set_layouts();
let max = (current_layouts.len() as u32).min(first_set + num_descriptor_sets);
(0..max).find(|&num| {
let num = num as usize;
!current_layouts[num].is_compatible_with(&new_layouts[num])
})
};
if let Some(invalidate_from) = invalidate_from {
state
.descriptor_sets
.retain(|&num, _| num < invalidate_from);
state.pipeline_layout = pipeline_layout;
} else if (first_set + num_descriptor_sets) as usize
>= state.pipeline_layout.set_layouts().len()
{
state.pipeline_layout = pipeline_layout;
}
state
}
}
}
}
pub(in crate::command_buffer) struct RenderPassState {
pub(in crate::command_buffer) contents: SubpassContents,
pub(in crate::command_buffer) render_area_offset: [u32; 2],
pub(in crate::command_buffer) render_area_extent: [u32; 2],
pub(in crate::command_buffer) rendering_info: PipelineRenderingCreateInfo,
pub(in crate::command_buffer) attachments: Option<RenderPassStateAttachments>,
pub(in crate::command_buffer) render_pass: RenderPassStateType,
}
impl RenderPassState {
pub(in crate::command_buffer) fn from_inheritance(
render_pass: &CommandBufferInheritanceRenderPassType,
) -> Self {
match render_pass {
CommandBufferInheritanceRenderPassType::BeginRenderPass(info) => {
RenderPassState {
contents: SubpassContents::Inline,
render_area_offset: [0, 0],
render_area_extent: (info.framebuffer.as_ref())
.map_or([u32::MAX, u32::MAX], |framebuffer| framebuffer.extent()),
rendering_info: PipelineRenderingCreateInfo::from_subpass(&info.subpass),
attachments: info.framebuffer.as_ref().map(|framebuffer| {
RenderPassStateAttachments::from_subpass(&info.subpass, framebuffer)
}),
render_pass: BeginRenderPassState {
subpass: info.subpass.clone(),
framebuffer: info.framebuffer.clone(),
}
.into(),
}
}
CommandBufferInheritanceRenderPassType::BeginRendering(info) => RenderPassState {
contents: SubpassContents::Inline,
render_area_offset: [0, 0],
render_area_extent: [u32::MAX, u32::MAX],
rendering_info: PipelineRenderingCreateInfo::from_inheritance_rendering_info(info),
attachments: None,
render_pass: BeginRenderingState {
pipeline_used: false,
}
.into(),
},
}
}
}
pub(in crate::command_buffer) enum RenderPassStateType {
BeginRenderPass(BeginRenderPassState),
BeginRendering(BeginRenderingState),
}
impl From<BeginRenderPassState> for RenderPassStateType {
#[inline]
fn from(val: BeginRenderPassState) -> Self {
Self::BeginRenderPass(val)
}
}
impl From<BeginRenderingState> for RenderPassStateType {
#[inline]
fn from(val: BeginRenderingState) -> Self {
Self::BeginRendering(val)
}
}
pub(in crate::command_buffer) struct BeginRenderPassState {
pub(in crate::command_buffer) subpass: Subpass,
pub(in crate::command_buffer) framebuffer: Option<Arc<Framebuffer>>,
}
pub(in crate::command_buffer) struct BeginRenderingState {
pub(in crate::command_buffer) pipeline_used: bool,
}
pub(in crate::command_buffer) struct RenderPassStateAttachments {
pub(in crate::command_buffer) color_attachments: Vec<Option<RenderPassStateAttachmentInfo>>,
pub(in crate::command_buffer) depth_attachment: Option<RenderPassStateAttachmentInfo>,
pub(in crate::command_buffer) stencil_attachment: Option<RenderPassStateAttachmentInfo>,
}
impl RenderPassStateAttachments {
pub(in crate::command_buffer) fn from_subpass(
subpass: &Subpass,
framebuffer: &Framebuffer,
) -> Self {
let subpass_desc = subpass.subpass_desc();
let rp_attachments = subpass.render_pass().attachments();
let fb_attachments = framebuffer.attachments();
Self {
color_attachments: (subpass_desc.color_attachments.iter())
.zip(subpass_desc.color_resolve_attachments.iter())
.map(|(color_attachment, color_resolve_attachment)| {
(color_attachment.as_ref()).map(|color_attachment| {
RenderPassStateAttachmentInfo {
image_view: fb_attachments[color_attachment.attachment as usize]
.clone(),
_image_layout: color_attachment.layout,
_resolve_info: color_resolve_attachment.as_ref().map(
|color_resolve_attachment| RenderPassStateAttachmentResolveInfo {
_image_view: fb_attachments
[color_resolve_attachment.attachment as usize]
.clone(),
_image_layout: color_resolve_attachment.layout,
},
),
}
})
})
.collect(),
depth_attachment: (subpass_desc.depth_stencil_attachment.as_ref())
.filter(|depth_stencil_attachment| {
(rp_attachments[depth_stencil_attachment.attachment as usize].format)
.aspects()
.intersects(ImageAspects::DEPTH)
})
.map(|depth_stencil_attachment| RenderPassStateAttachmentInfo {
image_view: fb_attachments[depth_stencil_attachment.attachment as usize]
.clone(),
_image_layout: depth_stencil_attachment.layout,
_resolve_info: subpass_desc.depth_stencil_resolve_attachment.as_ref().map(
|depth_stencil_resolve_attachment| RenderPassStateAttachmentResolveInfo {
_image_view: fb_attachments
[depth_stencil_resolve_attachment.attachment as usize]
.clone(),
_image_layout: depth_stencil_resolve_attachment.layout,
},
),
}),
stencil_attachment: (subpass_desc.depth_stencil_attachment.as_ref())
.filter(|depth_stencil_attachment| {
(rp_attachments[depth_stencil_attachment.attachment as usize].format)
.aspects()
.intersects(ImageAspects::STENCIL)
})
.map(|depth_stencil_attachment| RenderPassStateAttachmentInfo {
image_view: fb_attachments[depth_stencil_attachment.attachment as usize]
.clone(),
_image_layout: depth_stencil_attachment
.stencil_layout
.unwrap_or(depth_stencil_attachment.layout),
_resolve_info: subpass_desc.depth_stencil_resolve_attachment.as_ref().map(
|depth_stencil_resolve_attachment| RenderPassStateAttachmentResolveInfo {
_image_view: fb_attachments
[depth_stencil_resolve_attachment.attachment as usize]
.clone(),
_image_layout: depth_stencil_resolve_attachment
.stencil_layout
.unwrap_or(depth_stencil_resolve_attachment.layout),
},
),
}),
}
}
pub(in crate::command_buffer) fn from_rendering_info(info: &RenderingInfo) -> Self {
Self {
color_attachments: (info.color_attachments.iter())
.map(|atch_info| {
(atch_info.as_ref()).map(|atch_info| RenderPassStateAttachmentInfo {
image_view: atch_info.image_view.clone(),
_image_layout: atch_info.image_layout,
_resolve_info: atch_info.resolve_info.as_ref().map(|resolve_atch_info| {
RenderPassStateAttachmentResolveInfo {
_image_view: resolve_atch_info.image_view.clone(),
_image_layout: resolve_atch_info.image_layout,
}
}),
})
})
.collect(),
depth_attachment: (info.depth_attachment.as_ref()).map(|atch_info| {
RenderPassStateAttachmentInfo {
image_view: atch_info.image_view.clone(),
_image_layout: atch_info.image_layout,
_resolve_info: atch_info.resolve_info.as_ref().map(|resolve_atch_info| {
RenderPassStateAttachmentResolveInfo {
_image_view: resolve_atch_info.image_view.clone(),
_image_layout: resolve_atch_info.image_layout,
}
}),
}
}),
stencil_attachment: (info.stencil_attachment.as_ref()).map(|atch_info| {
RenderPassStateAttachmentInfo {
image_view: atch_info.image_view.clone(),
_image_layout: atch_info.image_layout,
_resolve_info: atch_info.resolve_info.as_ref().map(|resolve_atch_info| {
RenderPassStateAttachmentResolveInfo {
_image_view: resolve_atch_info.image_view.clone(),
_image_layout: resolve_atch_info.image_layout,
}
}),
}
}),
}
}
}
pub(in crate::command_buffer) struct RenderPassStateAttachmentInfo {
pub(in crate::command_buffer) image_view: Arc<ImageView>,
pub(in crate::command_buffer) _image_layout: ImageLayout,
pub(in crate::command_buffer) _resolve_info: Option<RenderPassStateAttachmentResolveInfo>,
}
pub(in crate::command_buffer) struct RenderPassStateAttachmentResolveInfo {
pub(in crate::command_buffer) _image_view: Arc<ImageView>,
pub(in crate::command_buffer) _image_layout: ImageLayout,
}
pub(in crate::command_buffer) struct DescriptorSetState {
pub(in crate::command_buffer) descriptor_sets: HashMap<u32, SetOrPush>,
pub(in crate::command_buffer) pipeline_layout: Arc<PipelineLayout>,
}
#[derive(Clone)]
pub(in crate::command_buffer) enum SetOrPush {
Set(DescriptorSetWithOffsets),
Push(DescriptorSetResources),
}
impl SetOrPush {
pub(in crate::command_buffer) fn resources(&self) -> &DescriptorSetResources {
match self {
Self::Set(set) => set.as_ref().0.resources(),
Self::Push(resources) => resources,
}
}
#[inline]
pub(in crate::command_buffer) fn dynamic_offsets(&self) -> &[u32] {
match self {
Self::Set(set) => set.as_ref().1,
Self::Push(_) => &[],
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub(in crate::command_buffer) struct StencilStateDynamic {
pub(in crate::command_buffer) front: Option<u32>,
pub(in crate::command_buffer) back: Option<u32>,
}
#[derive(Clone, Copy, Debug, Default)]
pub(in crate::command_buffer) struct StencilOpStateDynamic {
pub(in crate::command_buffer) front: Option<StencilOps>,
pub(in crate::command_buffer) back: Option<StencilOps>,
}
pub(in crate::command_buffer) struct QueryState {
pub(in crate::command_buffer) query_pool: Arc<QueryPool>,
pub(in crate::command_buffer) query: u32,
pub(in crate::command_buffer) flags: QueryControlFlags,
pub(in crate::command_buffer) in_subpass: bool,
}