use crate::buffer::BufferAccess;
use crate::buffer::TypedBufferAccess;
use crate::command_buffer::pool::standard::StandardCommandPoolAlloc;
use crate::command_buffer::pool::standard::StandardCommandPoolBuilder;
use crate::command_buffer::pool::CommandPool;
use crate::command_buffer::pool::CommandPoolBuilderAlloc;
use crate::command_buffer::synced::SyncCommandBuffer;
use crate::command_buffer::synced::SyncCommandBufferBuilder;
use crate::command_buffer::synced::SyncCommandBufferBuilderError;
use crate::command_buffer::sys::UnsafeCommandBuffer;
use crate::command_buffer::sys::UnsafeCommandBufferBuilderBufferImageCopy;
use crate::command_buffer::sys::UnsafeCommandBufferBuilderColorImageClear;
use crate::command_buffer::sys::UnsafeCommandBufferBuilderImageBlit;
use crate::command_buffer::sys::UnsafeCommandBufferBuilderImageCopy;
use crate::command_buffer::validity::*;
use crate::command_buffer::CommandBufferExecError;
use crate::command_buffer::CommandBufferInheritance;
use crate::command_buffer::CommandBufferInheritanceRenderPass;
use crate::command_buffer::CommandBufferLevel;
use crate::command_buffer::CommandBufferUsage;
use crate::command_buffer::DispatchIndirectCommand;
use crate::command_buffer::DrawIndexedIndirectCommand;
use crate::command_buffer::DrawIndirectCommand;
use crate::command_buffer::DynamicState;
use crate::command_buffer::PrimaryCommandBuffer;
use crate::command_buffer::SecondaryCommandBuffer;
use crate::command_buffer::StateCacher;
use crate::command_buffer::StateCacherOutcome;
use crate::command_buffer::SubpassContents;
use crate::descriptor::descriptor::{DescriptorBufferDesc, DescriptorDescTy};
use crate::descriptor::descriptor_set::{DescriptorSetDesc, DescriptorSetsCollection};
use crate::descriptor::pipeline_layout::PipelineLayoutAbstract;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::device::Queue;
use crate::format::ClearValue;
use crate::format::FormatTy;
use crate::format::Pixel;
use crate::image::ImageAccess;
use crate::image::ImageAspect;
use crate::image::ImageAspects;
use crate::image::ImageLayout;
use crate::instance::QueueFamily;
use crate::pipeline::input_assembly::Index;
use crate::pipeline::vertex::VertexSource;
use crate::pipeline::ComputePipelineAbstract;
use crate::pipeline::GraphicsPipelineAbstract;
use crate::query::QueryControlFlags;
use crate::query::QueryPipelineStatisticFlags;
use crate::query::QueryPool;
use crate::query::QueryResultElement;
use crate::query::QueryResultFlags;
use crate::query::QueryType;
use crate::render_pass::Framebuffer;
use crate::render_pass::FramebufferAbstract;
use crate::render_pass::LoadOp;
use crate::render_pass::RenderPass;
use crate::render_pass::Subpass;
use crate::sampler::Filter;
use crate::sync::AccessCheckError;
use crate::sync::AccessFlags;
use crate::sync::GpuFuture;
use crate::sync::PipelineMemoryAccess;
use crate::sync::PipelineStage;
use crate::sync::PipelineStages;
use crate::VulkanObject;
use crate::{OomError, SafeDeref};
use fnv::FnvHashMap;
use smallvec::SmallVec;
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::iter;
use std::marker::PhantomData;
use std::mem;
use std::ops::Range;
use std::slice;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::Arc;
pub struct AutoCommandBufferBuilder<L, P = StandardCommandPoolBuilder> {
inner: SyncCommandBufferBuilder,
pool_builder_alloc: P,
state_cacher: StateCacher,
queue_family_id: u32,
inheritance: Option<CommandBufferInheritance<Box<dyn FramebufferAbstract + Send + Sync>>>,
usage: CommandBufferUsage,
render_pass_state: Option<RenderPassState>,
query_state: FnvHashMap<vk::QueryType, QueryState>,
_data: PhantomData<L>,
}
struct RenderPassState {
subpass: (Arc<RenderPass>, u32),
contents: SubpassContents,
framebuffer: vk::Framebuffer,
}
struct QueryState {
query_pool: vk::QueryPool,
query: u32,
ty: QueryType,
flags: QueryControlFlags,
in_subpass: bool,
}
impl AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuilder> {
#[inline]
pub fn primary(
device: Arc<Device>,
queue_family: QueueFamily,
usage: CommandBufferUsage,
) -> Result<
AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuilder>,
OomError,
> {
AutoCommandBufferBuilder::with_level(
device,
queue_family,
usage,
CommandBufferLevel::primary(),
)
}
}
impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder> {
#[inline]
pub fn secondary_compute(
device: Arc<Device>,
queue_family: QueueFamily,
usage: CommandBufferUsage,
) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
OomError,
> {
let level = CommandBufferLevel::secondary(None, QueryPipelineStatisticFlags::none());
AutoCommandBufferBuilder::with_level(device, queue_family, usage, level)
}
#[inline]
pub fn secondary_compute_inherit_queries(
device: Arc<Device>,
queue_family: QueueFamily,
usage: CommandBufferUsage,
occlusion_query: Option<QueryControlFlags>,
query_statistics_flags: QueryPipelineStatisticFlags,
) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
BeginError,
> {
if occlusion_query.is_some() && !device.enabled_features().inherited_queries {
return Err(BeginError::InheritedQueriesFeatureNotEnabled);
}
if query_statistics_flags.count() > 0
&& !device.enabled_features().pipeline_statistics_query
{
return Err(BeginError::PipelineStatisticsQueryFeatureNotEnabled);
}
let level = CommandBufferLevel::secondary(occlusion_query, query_statistics_flags);
Ok(AutoCommandBufferBuilder::with_level(
device,
queue_family,
usage,
level,
)?)
}
#[inline]
pub fn secondary_graphics(
device: Arc<Device>,
queue_family: QueueFamily,
usage: CommandBufferUsage,
subpass: Subpass,
) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
OomError,
> {
let level = CommandBufferLevel::Secondary(CommandBufferInheritance {
render_pass: Some(CommandBufferInheritanceRenderPass {
subpass,
framebuffer: None::<Arc<Framebuffer<()>>>,
}),
occlusion_query: None,
query_statistics_flags: QueryPipelineStatisticFlags::none(),
});
AutoCommandBufferBuilder::with_level(device, queue_family, usage, level)
}
#[inline]
pub fn secondary_graphics_inherit_queries(
device: Arc<Device>,
queue_family: QueueFamily,
usage: CommandBufferUsage,
subpass: Subpass,
occlusion_query: Option<QueryControlFlags>,
query_statistics_flags: QueryPipelineStatisticFlags,
) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
BeginError,
> {
if occlusion_query.is_some() && !device.enabled_features().inherited_queries {
return Err(BeginError::InheritedQueriesFeatureNotEnabled);
}
if query_statistics_flags.count() > 0
&& !device.enabled_features().pipeline_statistics_query
{
return Err(BeginError::PipelineStatisticsQueryFeatureNotEnabled);
}
let level = CommandBufferLevel::Secondary(CommandBufferInheritance {
render_pass: Some(CommandBufferInheritanceRenderPass {
subpass,
framebuffer: None::<Arc<Framebuffer<()>>>,
}),
occlusion_query,
query_statistics_flags,
});
Ok(AutoCommandBufferBuilder::with_level(
device,
queue_family,
usage,
level,
)?)
}
}
impl<L> AutoCommandBufferBuilder<L, StandardCommandPoolBuilder> {
fn with_level<F>(
device: Arc<Device>,
queue_family: QueueFamily,
usage: CommandBufferUsage,
level: CommandBufferLevel<F>,
) -> Result<AutoCommandBufferBuilder<L, StandardCommandPoolBuilder>, OomError>
where
F: FramebufferAbstract + Clone + Send + Sync + 'static,
{
let (inheritance, render_pass_state) = match &level {
CommandBufferLevel::Primary => (None, None),
CommandBufferLevel::Secondary(inheritance) => {
let (render_pass, render_pass_state) = match inheritance.render_pass.as_ref() {
Some(CommandBufferInheritanceRenderPass {
subpass,
framebuffer,
}) => {
let render_pass = CommandBufferInheritanceRenderPass {
subpass: Subpass::from(subpass.render_pass().clone(), subpass.index())
.unwrap(),
framebuffer: framebuffer
.as_ref()
.map(|f| Box::new(f.clone()) as Box<_>),
};
let render_pass_state = RenderPassState {
subpass: (subpass.render_pass().clone(), subpass.index()),
contents: SubpassContents::Inline,
framebuffer: 0,
};
(Some(render_pass), Some(render_pass_state))
}
None => (None, None),
};
(
Some(CommandBufferInheritance {
render_pass,
occlusion_query: inheritance.occlusion_query,
query_statistics_flags: inheritance.query_statistics_flags,
}),
render_pass_state,
)
}
};
unsafe {
let pool = Device::standard_command_pool(&device, queue_family);
let pool_builder_alloc = pool
.alloc(!matches!(level, CommandBufferLevel::Primary), 1)?
.next()
.expect("Requested one command buffer from the command pool, but got zero.");
let inner = SyncCommandBufferBuilder::new(pool_builder_alloc.inner(), level, usage)?;
Ok(AutoCommandBufferBuilder {
inner,
pool_builder_alloc,
state_cacher: StateCacher::new(),
queue_family_id: queue_family.id(),
render_pass_state,
query_state: FnvHashMap::default(),
inheritance,
usage,
_data: PhantomData,
})
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum BeginError {
InheritedQueriesFeatureNotEnabled,
OomError(OomError),
PipelineStatisticsQueryFeatureNotEnabled,
}
impl error::Error for BeginError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for BeginError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
Self::InheritedQueriesFeatureNotEnabled => {
"occlusion query inheritance was requested but the corresponding feature \
wasn't enabled"
}
Self::OomError(_) => "not enough memory available",
Self::PipelineStatisticsQueryFeatureNotEnabled => {
"pipeline statistics query inheritance was requested but the corresponding \
feature wasn't enabled"
}
}
)
}
}
impl From<OomError> for BeginError {
#[inline]
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>
where
P: CommandPoolBuilderAlloc,
{
#[inline]
pub fn build(self) -> Result<PrimaryAutoCommandBuffer<P::Alloc>, BuildError> {
if self.render_pass_state.is_some() {
return Err(AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass.into());
}
if !self.query_state.is_empty() {
return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
}
let submit_state = match self.usage {
CommandBufferUsage::MultipleSubmit => SubmitState::ExclusiveUse {
in_use: AtomicBool::new(false),
},
CommandBufferUsage::SimultaneousUse => SubmitState::Concurrent,
CommandBufferUsage::OneTimeSubmit => SubmitState::OneTime {
already_submitted: AtomicBool::new(false),
},
};
Ok(PrimaryAutoCommandBuffer {
inner: self.inner.build()?,
pool_alloc: self.pool_builder_alloc.into_alloc(),
submit_state,
})
}
}
impl<P> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<P::Alloc>, P>
where
P: CommandPoolBuilderAlloc,
{
#[inline]
pub fn build(self) -> Result<SecondaryAutoCommandBuffer<P::Alloc>, BuildError> {
if !self.query_state.is_empty() {
return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
}
let submit_state = match self.usage {
CommandBufferUsage::MultipleSubmit => SubmitState::ExclusiveUse {
in_use: AtomicBool::new(false),
},
CommandBufferUsage::SimultaneousUse => SubmitState::Concurrent,
CommandBufferUsage::OneTimeSubmit => SubmitState::OneTime {
already_submitted: AtomicBool::new(false),
},
};
Ok(SecondaryAutoCommandBuffer {
inner: self.inner.build()?,
pool_alloc: self.pool_builder_alloc.into_alloc(),
inheritance: self.inheritance.unwrap(),
submit_state,
})
}
}
impl<L, P> AutoCommandBufferBuilder<L, P> {
#[inline]
fn ensure_outside_render_pass(&self) -> Result<(), AutoCommandBufferBuilderContextError> {
if self.render_pass_state.is_some() {
return Err(AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass);
}
Ok(())
}
#[inline]
fn ensure_inside_render_pass_inline<Gp>(
&self,
pipeline: &Gp,
) -> Result<(), AutoCommandBufferBuilderContextError>
where
Gp: ?Sized + GraphicsPipelineAbstract,
{
let render_pass_state = self
.render_pass_state
.as_ref()
.ok_or(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass)?;
if render_pass_state.contents != SubpassContents::Inline {
return Err(AutoCommandBufferBuilderContextError::WrongSubpassType);
}
if pipeline.subpass().index() != render_pass_state.subpass.1 {
return Err(AutoCommandBufferBuilderContextError::WrongSubpassIndex);
}
if !pipeline
.subpass()
.render_pass()
.desc()
.is_compatible_with_desc(&render_pass_state.subpass.0.desc())
{
return Err(AutoCommandBufferBuilderContextError::IncompatibleRenderPass);
}
Ok(())
}
#[inline]
fn queue_family(&self) -> QueueFamily {
self.device()
.physical_device()
.queue_family_by_id(self.queue_family_id)
.unwrap()
}
pub fn copy_image<S, D>(
&mut self,
source: S,
source_offset: [i32; 3],
source_base_array_layer: u32,
source_mip_level: u32,
destination: D,
destination_offset: [i32; 3],
destination_base_array_layer: u32,
destination_mip_level: u32,
extent: [u32; 3],
layer_count: u32,
) -> Result<&mut Self, CopyImageError>
where
S: ImageAccess + Send + Sync + 'static,
D: ImageAccess + Send + Sync + 'static,
{
unsafe {
self.ensure_outside_render_pass()?;
check_copy_image(
self.device(),
&source,
source_offset,
source_base_array_layer,
source_mip_level,
&destination,
destination_offset,
destination_base_array_layer,
destination_mip_level,
extent,
layer_count,
)?;
let copy = UnsafeCommandBufferBuilderImageCopy {
aspects: ImageAspects {
color: source.has_color(),
depth: !source.has_color() && source.has_depth() && destination.has_depth(),
stencil: !source.has_color()
&& source.has_stencil()
&& destination.has_stencil(),
..ImageAspects::none()
},
source_mip_level,
destination_mip_level,
source_base_array_layer,
destination_base_array_layer,
layer_count,
source_offset,
destination_offset,
extent,
};
self.inner.copy_image(
source,
ImageLayout::TransferSrcOptimal,
destination,
ImageLayout::TransferDstOptimal,
iter::once(copy),
)?;
Ok(self)
}
}
pub fn blit_image<S, D>(
&mut self,
source: S,
source_top_left: [i32; 3],
source_bottom_right: [i32; 3],
source_base_array_layer: u32,
source_mip_level: u32,
destination: D,
destination_top_left: [i32; 3],
destination_bottom_right: [i32; 3],
destination_base_array_layer: u32,
destination_mip_level: u32,
layer_count: u32,
filter: Filter,
) -> Result<&mut Self, BlitImageError>
where
S: ImageAccess + Send + Sync + 'static,
D: ImageAccess + Send + Sync + 'static,
{
unsafe {
if !self.queue_family().supports_graphics() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
}
self.ensure_outside_render_pass()?;
check_blit_image(
self.device(),
&source,
source_top_left,
source_bottom_right,
source_base_array_layer,
source_mip_level,
&destination,
destination_top_left,
destination_bottom_right,
destination_base_array_layer,
destination_mip_level,
layer_count,
filter,
)?;
let blit = UnsafeCommandBufferBuilderImageBlit {
aspects: if source.has_color() {
ImageAspects {
color: true,
..ImageAspects::none()
}
} else {
unimplemented!()
},
source_mip_level,
destination_mip_level,
source_base_array_layer,
destination_base_array_layer,
layer_count,
source_top_left,
source_bottom_right,
destination_top_left,
destination_bottom_right,
};
self.inner.blit_image(
source,
ImageLayout::TransferSrcOptimal,
destination,
ImageLayout::TransferDstOptimal,
iter::once(blit),
filter,
)?;
Ok(self)
}
}
pub fn clear_color_image<I>(
&mut self,
image: I,
color: ClearValue,
) -> Result<&mut Self, ClearColorImageError>
where
I: ImageAccess + Send + Sync + 'static,
{
let layers = image.dimensions().array_layers();
let levels = image.mipmap_levels();
self.clear_color_image_dimensions(image, 0, layers, 0, levels, color)
}
pub fn clear_color_image_dimensions<I>(
&mut self,
image: I,
first_layer: u32,
num_layers: u32,
first_mipmap: u32,
num_mipmaps: u32,
color: ClearValue,
) -> Result<&mut Self, ClearColorImageError>
where
I: ImageAccess + Send + Sync + 'static,
{
unsafe {
if !self.queue_family().supports_graphics() && !self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
}
self.ensure_outside_render_pass()?;
check_clear_color_image(
self.device(),
&image,
first_layer,
num_layers,
first_mipmap,
num_mipmaps,
)?;
match color {
ClearValue::Float(_) | ClearValue::Int(_) | ClearValue::Uint(_) => {}
_ => panic!("The clear color is not a color value"),
};
let region = UnsafeCommandBufferBuilderColorImageClear {
base_mip_level: first_mipmap,
level_count: num_mipmaps,
base_array_layer: first_layer,
layer_count: num_layers,
};
self.inner.clear_color_image(
image,
ImageLayout::TransferDstOptimal,
color,
iter::once(region),
)?;
Ok(self)
}
}
#[inline]
pub fn copy_buffer<S, D, T>(
&mut self,
source: S,
destination: D,
) -> Result<&mut Self, CopyBufferError>
where
S: TypedBufferAccess<Content = T> + Send + Sync + 'static,
D: TypedBufferAccess<Content = T> + Send + Sync + 'static,
T: ?Sized,
{
unsafe {
self.ensure_outside_render_pass()?;
let infos = check_copy_buffer(self.device(), &source, &destination)?;
self.inner
.copy_buffer(source, destination, iter::once((0, 0, infos.copy_size)))?;
Ok(self)
}
}
#[inline]
pub fn copy_buffer_dimensions<S, D, T>(
&mut self,
source: S,
source_offset: usize,
destination: D,
destination_offset: usize,
count: usize,
) -> Result<&mut Self, CopyBufferError>
where
S: TypedBufferAccess<Content = [T]> + Send + Sync + 'static,
D: TypedBufferAccess<Content = [T]> + Send + Sync + 'static,
{
self.ensure_outside_render_pass()?;
let _infos = check_copy_buffer(self.device(), &source, &destination)?;
debug_assert!(source_offset + count <= source.len());
debug_assert!(destination_offset + count <= destination.len());
let size = std::mem::size_of::<T>();
unsafe {
self.inner.copy_buffer(
source,
destination,
iter::once((
source_offset * size,
destination_offset * size,
count * size,
)),
)?;
}
Ok(self)
}
pub fn copy_buffer_to_image<S, D, Px>(
&mut self,
source: S,
destination: D,
) -> Result<&mut Self, CopyBufferImageError>
where
S: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static,
D: ImageAccess + Send + Sync + 'static,
Px: Pixel,
{
self.ensure_outside_render_pass()?;
let dims = destination.dimensions().width_height_depth();
self.copy_buffer_to_image_dimensions(source, destination, [0, 0, 0], dims, 0, 1, 0)
}
pub fn copy_buffer_to_image_dimensions<S, D, Px>(
&mut self,
source: S,
destination: D,
offset: [u32; 3],
size: [u32; 3],
first_layer: u32,
num_layers: u32,
mipmap: u32,
) -> Result<&mut Self, CopyBufferImageError>
where
S: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static,
D: ImageAccess + Send + Sync + 'static,
Px: Pixel,
{
unsafe {
self.ensure_outside_render_pass()?;
check_copy_buffer_image(
self.device(),
&source,
&destination,
CheckCopyBufferImageTy::BufferToImage,
offset,
size,
first_layer,
num_layers,
mipmap,
)?;
let copy = UnsafeCommandBufferBuilderBufferImageCopy {
buffer_offset: 0,
buffer_row_length: 0,
buffer_image_height: 0,
image_aspect: if destination.has_color() {
ImageAspect::Color
} else {
unimplemented!()
},
image_mip_level: mipmap,
image_base_array_layer: first_layer,
image_layer_count: num_layers,
image_offset: [offset[0] as i32, offset[1] as i32, offset[2] as i32],
image_extent: size,
};
self.inner.copy_buffer_to_image(
source,
destination,
ImageLayout::TransferDstOptimal,
iter::once(copy),
)?;
Ok(self)
}
}
pub fn copy_image_to_buffer<S, D, Px>(
&mut self,
source: S,
destination: D,
) -> Result<&mut Self, CopyBufferImageError>
where
S: ImageAccess + Send + Sync + 'static,
D: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static,
Px: Pixel,
{
self.ensure_outside_render_pass()?;
let dims = source.dimensions().width_height_depth();
self.copy_image_to_buffer_dimensions(source, destination, [0, 0, 0], dims, 0, 1, 0)
}
pub fn copy_image_to_buffer_dimensions<S, D, Px>(
&mut self,
source: S,
destination: D,
offset: [u32; 3],
size: [u32; 3],
first_layer: u32,
num_layers: u32,
mipmap: u32,
) -> Result<&mut Self, CopyBufferImageError>
where
S: ImageAccess + Send + Sync + 'static,
D: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static,
Px: Pixel,
{
unsafe {
self.ensure_outside_render_pass()?;
check_copy_buffer_image(
self.device(),
&destination,
&source,
CheckCopyBufferImageTy::ImageToBuffer,
offset,
size,
first_layer,
num_layers,
mipmap,
)?;
let copy = UnsafeCommandBufferBuilderBufferImageCopy {
buffer_offset: 0,
buffer_row_length: 0,
buffer_image_height: 0,
image_aspect: if source.has_color() {
ImageAspect::Color
} else if source.has_depth() {
ImageAspect::Depth
} else if source.has_stencil() {
ImageAspect::Stencil
} else {
unimplemented!()
},
image_mip_level: mipmap,
image_base_array_layer: first_layer,
image_layer_count: num_layers,
image_offset: [offset[0] as i32, offset[1] as i32, offset[2] as i32],
image_extent: size,
};
self.inner.copy_image_to_buffer(
source,
ImageLayout::TransferSrcOptimal,
destination,
iter::once(copy),
)?;
Ok(self)
}
}
#[inline]
pub fn debug_marker_begin(
&mut self,
name: &'static CStr,
color: [f32; 4],
) -> Result<&mut Self, DebugMarkerError> {
if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
}
check_debug_marker_color(color)?;
unsafe {
self.inner.debug_marker_begin(name.into(), color);
}
Ok(self)
}
#[inline]
pub fn debug_marker_end(&mut self) -> Result<&mut Self, DebugMarkerError> {
if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
}
unsafe {
self.inner.debug_marker_end();
}
Ok(self)
}
#[inline]
pub fn debug_marker_insert(
&mut self,
name: &'static CStr,
color: [f32; 4],
) -> Result<&mut Self, DebugMarkerError> {
if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
}
check_debug_marker_color(color)?;
unsafe {
self.inner.debug_marker_insert(name.into(), color);
}
Ok(self)
}
#[inline]
pub fn dispatch<Cp, S, Pc, Do, Doi>(
&mut self,
group_counts: [u32; 3],
pipeline: Cp,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DispatchError>
where
Cp: ComputePipelineAbstract + Send + Sync + 'static + Clone,
S: DescriptorSetsCollection,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
if !self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
}
self.ensure_outside_render_pass()?;
check_push_constants_validity(&pipeline, &constants)?;
check_descriptor_sets_validity(&pipeline, &sets)?;
check_dispatch(pipeline.device(), group_counts)?;
if let StateCacherOutcome::NeedChange =
self.state_cacher.bind_compute_pipeline(&pipeline)
{
self.inner.bind_pipeline_compute(pipeline.clone());
}
push_constants(&mut self.inner, pipeline.clone(), constants);
descriptor_sets(
&mut self.inner,
&mut self.state_cacher,
false,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
self.inner.dispatch(group_counts);
Ok(self)
}
}
#[inline]
pub fn dispatch_indirect<Inb, Cp, S, Pc, Do, Doi>(
&mut self,
indirect_buffer: Inb,
pipeline: Cp,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DispatchIndirectError>
where
Inb: BufferAccess
+ TypedBufferAccess<Content = [DispatchIndirectCommand]>
+ Send
+ Sync
+ 'static,
Cp: ComputePipelineAbstract + Send + Sync + 'static + Clone,
S: DescriptorSetsCollection,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
if !self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
}
self.ensure_outside_render_pass()?;
check_indirect_buffer(self.device(), &indirect_buffer)?;
check_push_constants_validity(&pipeline, &constants)?;
check_descriptor_sets_validity(&pipeline, &sets)?;
if let StateCacherOutcome::NeedChange =
self.state_cacher.bind_compute_pipeline(&pipeline)
{
self.inner.bind_pipeline_compute(pipeline.clone());
}
push_constants(&mut self.inner, pipeline.clone(), constants);
descriptor_sets(
&mut self.inner,
&mut self.state_cacher,
false,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
self.inner.dispatch_indirect(indirect_buffer)?;
Ok(self)
}
}
#[inline]
pub fn draw<V, Gp, S, Pc, Do, Doi>(
&mut self,
pipeline: Gp,
dynamic: &DynamicState,
vertex_buffer: V,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DrawError>
where
Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone,
S: DescriptorSetsCollection,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
self.ensure_inside_render_pass_inline(&pipeline)?;
check_dynamic_state_validity(&pipeline, dynamic)?;
check_push_constants_validity(&pipeline, &constants)?;
check_descriptor_sets_validity(&pipeline, &sets)?;
let vb_infos = check_vertex_buffers(&pipeline, vertex_buffer)?;
if let StateCacherOutcome::NeedChange =
self.state_cacher.bind_graphics_pipeline(&pipeline)
{
self.inner.bind_pipeline_graphics(pipeline.clone());
}
let dynamic = self.state_cacher.dynamic_state(dynamic);
push_constants(&mut self.inner, pipeline.clone(), constants);
set_state(&mut self.inner, &dynamic);
descriptor_sets(
&mut self.inner,
&mut self.state_cacher,
true,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
vertex_buffers(
&mut self.inner,
&mut self.state_cacher,
vb_infos.vertex_buffers,
)?;
debug_assert!(self.queue_family().supports_graphics());
self.inner.draw(
vb_infos.vertex_count as u32,
vb_infos.instance_count as u32,
0,
0,
);
Ok(self)
}
}
#[inline]
pub fn draw_indirect<V, Gp, S, Pc, Inb, Do, Doi>(
&mut self,
pipeline: Gp,
dynamic: &DynamicState,
vertex_buffer: V,
indirect_buffer: Inb,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DrawIndirectError>
where
Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone,
S: DescriptorSetsCollection,
Inb: BufferAccess
+ TypedBufferAccess<Content = [DrawIndirectCommand]>
+ Send
+ Sync
+ 'static,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
self.ensure_inside_render_pass_inline(&pipeline)?;
check_indirect_buffer(self.device(), &indirect_buffer)?;
check_dynamic_state_validity(&pipeline, dynamic)?;
check_push_constants_validity(&pipeline, &constants)?;
check_descriptor_sets_validity(&pipeline, &sets)?;
let vb_infos = check_vertex_buffers(&pipeline, vertex_buffer)?;
let draw_count = indirect_buffer.len() as u32;
if let StateCacherOutcome::NeedChange =
self.state_cacher.bind_graphics_pipeline(&pipeline)
{
self.inner.bind_pipeline_graphics(pipeline.clone());
}
let dynamic = self.state_cacher.dynamic_state(dynamic);
push_constants(&mut self.inner, pipeline.clone(), constants);
set_state(&mut self.inner, &dynamic);
descriptor_sets(
&mut self.inner,
&mut self.state_cacher,
true,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
vertex_buffers(
&mut self.inner,
&mut self.state_cacher,
vb_infos.vertex_buffers,
)?;
debug_assert!(self.queue_family().supports_graphics());
self.inner.draw_indirect(
indirect_buffer,
draw_count,
mem::size_of::<DrawIndirectCommand>() as u32,
)?;
Ok(self)
}
}
#[inline]
pub fn draw_indexed<V, Gp, S, Pc, Ib, I, Do, Doi>(
&mut self,
pipeline: Gp,
dynamic: &DynamicState,
vertex_buffer: V,
index_buffer: Ib,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DrawIndexedError>
where
Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone,
S: DescriptorSetsCollection,
Ib: BufferAccess + TypedBufferAccess<Content = [I]> + Send + Sync + 'static,
I: Index + 'static,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
self.ensure_inside_render_pass_inline(&pipeline)?;
let ib_infos = check_index_buffer(self.device(), &index_buffer)?;
check_dynamic_state_validity(&pipeline, dynamic)?;
check_push_constants_validity(&pipeline, &constants)?;
check_descriptor_sets_validity(&pipeline, &sets)?;
let vb_infos = check_vertex_buffers(&pipeline, vertex_buffer)?;
if let StateCacherOutcome::NeedChange =
self.state_cacher.bind_graphics_pipeline(&pipeline)
{
self.inner.bind_pipeline_graphics(pipeline.clone());
}
if let StateCacherOutcome::NeedChange =
self.state_cacher.bind_index_buffer(&index_buffer, I::ty())
{
self.inner.bind_index_buffer(index_buffer, I::ty())?;
}
let dynamic = self.state_cacher.dynamic_state(dynamic);
push_constants(&mut self.inner, pipeline.clone(), constants);
set_state(&mut self.inner, &dynamic);
descriptor_sets(
&mut self.inner,
&mut self.state_cacher,
true,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
vertex_buffers(
&mut self.inner,
&mut self.state_cacher,
vb_infos.vertex_buffers,
)?;
debug_assert!(self.queue_family().supports_graphics());
self.inner.draw_indexed(
ib_infos.num_indices as u32,
vb_infos.instance_count as u32,
0,
0,
0,
);
Ok(self)
}
}
#[inline]
pub fn draw_indexed_indirect<V, Gp, S, Pc, Ib, Inb, I, Do, Doi>(
&mut self,
pipeline: Gp,
dynamic: &DynamicState,
vertex_buffer: V,
index_buffer: Ib,
indirect_buffer: Inb,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DrawIndexedIndirectError>
where
Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone,
S: DescriptorSetsCollection,
Ib: BufferAccess + TypedBufferAccess<Content = [I]> + Send + Sync + 'static,
Inb: BufferAccess
+ TypedBufferAccess<Content = [DrawIndexedIndirectCommand]>
+ Send
+ Sync
+ 'static,
I: Index + 'static,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
self.ensure_inside_render_pass_inline(&pipeline)?;
let ib_infos = check_index_buffer(self.device(), &index_buffer)?;
check_indirect_buffer(self.device(), &indirect_buffer)?;
check_dynamic_state_validity(&pipeline, dynamic)?;
check_push_constants_validity(&pipeline, &constants)?;
check_descriptor_sets_validity(&pipeline, &sets)?;
let vb_infos = check_vertex_buffers(&pipeline, vertex_buffer)?;
let draw_count = indirect_buffer.len() as u32;
if let StateCacherOutcome::NeedChange =
self.state_cacher.bind_graphics_pipeline(&pipeline)
{
self.inner.bind_pipeline_graphics(pipeline.clone());
}
if let StateCacherOutcome::NeedChange =
self.state_cacher.bind_index_buffer(&index_buffer, I::ty())
{
self.inner.bind_index_buffer(index_buffer, I::ty())?;
}
let dynamic = self.state_cacher.dynamic_state(dynamic);
push_constants(&mut self.inner, pipeline.clone(), constants);
set_state(&mut self.inner, &dynamic);
descriptor_sets(
&mut self.inner,
&mut self.state_cacher,
true,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
vertex_buffers(
&mut self.inner,
&mut self.state_cacher,
vb_infos.vertex_buffers,
)?;
debug_assert!(self.queue_family().supports_graphics());
self.inner.draw_indexed_indirect(
indirect_buffer,
draw_count,
mem::size_of::<DrawIndexedIndirectCommand>() as u32,
)?;
Ok(self)
}
}
#[inline]
pub fn fill_buffer<B>(&mut self, buffer: B, data: u32) -> Result<&mut Self, FillBufferError>
where
B: BufferAccess + Send + Sync + 'static,
{
unsafe {
self.ensure_outside_render_pass()?;
check_fill_buffer(self.device(), &buffer)?;
self.inner.fill_buffer(buffer, data);
Ok(self)
}
}
#[inline]
pub fn update_buffer<B, D, Dd>(
&mut self,
buffer: B,
data: Dd,
) -> Result<&mut Self, UpdateBufferError>
where
B: TypedBufferAccess<Content = D> + Send + Sync + 'static,
D: ?Sized,
Dd: SafeDeref<Target = D> + Send + Sync + 'static,
{
unsafe {
self.ensure_outside_render_pass()?;
check_update_buffer(self.device(), &buffer, data.deref())?;
let size_of_data = mem::size_of_val(data.deref());
if buffer.size() >= size_of_data {
self.inner.update_buffer(buffer, data);
} else {
unimplemented!()
}
Ok(self)
}
}
pub unsafe fn begin_query(
&mut self,
query_pool: Arc<QueryPool>,
query: u32,
flags: QueryControlFlags,
) -> Result<&mut Self, BeginQueryError> {
check_begin_query(self.device(), &query_pool, query, flags)?;
match query_pool.ty() {
QueryType::Occlusion => {
if !self.queue_family().supports_graphics() {
return Err(
AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into(),
);
}
}
QueryType::PipelineStatistics(flags) => {
if flags.is_compute() && !self.queue_family().supports_compute()
|| flags.is_graphics() && !self.queue_family().supports_graphics()
{
return Err(
AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into(),
);
}
}
QueryType::Timestamp => unreachable!(),
}
let ty = query_pool.ty();
let raw_ty = ty.into();
let raw_query_pool = query_pool.internal_object();
if self.query_state.contains_key(&raw_ty) {
return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
}
self.inner.begin_query(query_pool, query, flags);
self.query_state.insert(
raw_ty,
QueryState {
query_pool: raw_query_pool,
query,
ty,
flags,
in_subpass: self.render_pass_state.is_some(),
},
);
Ok(self)
}
pub fn end_query(
&mut self,
query_pool: Arc<QueryPool>,
query: u32,
) -> Result<&mut Self, EndQueryError> {
unsafe {
check_end_query(self.device(), &query_pool, query)?;
let raw_ty = query_pool.ty().into();
let raw_query_pool = query_pool.internal_object();
if !self.query_state.get(&raw_ty).map_or(false, |state| {
state.query_pool == raw_query_pool && state.query == query
}) {
return Err(AutoCommandBufferBuilderContextError::QueryNotActive.into());
}
self.inner.end_query(query_pool, query);
self.query_state.remove(&raw_ty);
}
Ok(self)
}
pub unsafe fn write_timestamp(
&mut self,
query_pool: Arc<QueryPool>,
query: u32,
stage: PipelineStage,
) -> Result<&mut Self, WriteTimestampError> {
check_write_timestamp(
self.device(),
self.queue_family(),
&query_pool,
query,
stage,
)?;
if !(self.queue_family().supports_graphics()
|| self.queue_family().supports_compute()
|| self.queue_family().explicitly_supports_transfers())
{
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
}
self.inner.write_timestamp(query_pool, query, stage);
Ok(self)
}
pub fn copy_query_pool_results<D, T>(
&mut self,
query_pool: Arc<QueryPool>,
queries: Range<u32>,
destination: D,
flags: QueryResultFlags,
) -> Result<&mut Self, CopyQueryPoolResultsError>
where
D: BufferAccess + TypedBufferAccess<Content = [T]> + Send + Sync + 'static,
T: QueryResultElement,
{
unsafe {
self.ensure_outside_render_pass()?;
let stride = check_copy_query_pool_results(
self.device(),
&query_pool,
queries.clone(),
&destination,
flags,
)?;
self.inner
.copy_query_pool_results(query_pool, queries, destination, stride, flags)?;
}
Ok(self)
}
pub unsafe fn reset_query_pool(
&mut self,
query_pool: Arc<QueryPool>,
queries: Range<u32>,
) -> Result<&mut Self, ResetQueryPoolError> {
self.ensure_outside_render_pass()?;
check_reset_query_pool(self.device(), &query_pool, queries.clone())?;
let raw_query_pool = query_pool.internal_object();
if self
.query_state
.values()
.any(|state| state.query_pool == raw_query_pool && queries.contains(&state.query))
{
return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
}
self.inner.reset_query_pool(query_pool, queries);
Ok(self)
}
}
impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>
where
P: CommandPoolBuilderAlloc,
{
#[inline]
pub fn begin_render_pass<F, I>(
&mut self,
framebuffer: F,
contents: SubpassContents,
clear_values: I,
) -> Result<&mut Self, BeginRenderPassError>
where
F: FramebufferAbstract + Clone + Send + Sync + 'static,
I: IntoIterator<Item = ClearValue>,
{
unsafe {
if !self.queue_family().supports_graphics() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
}
self.ensure_outside_render_pass()?;
let clear_values = framebuffer
.render_pass()
.desc()
.convert_clear_values(clear_values);
let clear_values = clear_values.collect::<Vec<_>>().into_iter();
let mut clear_values_copy = clear_values.clone().enumerate();
for (atch_i, atch_desc) in framebuffer
.render_pass()
.desc()
.attachments()
.into_iter()
.enumerate()
{
match clear_values_copy.next() {
Some((clear_i, clear_value)) => {
if atch_desc.load == LoadOp::Clear {
match clear_value {
ClearValue::None => panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: None",
clear_i, atch_i, atch_desc.format.ty()),
ClearValue::Float(_) => if atch_desc.format.ty() != FormatTy::Float {
panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Float",
clear_i, atch_i, atch_desc.format.ty());
}
ClearValue::Int(_) => if atch_desc.format.ty() != FormatTy::Sint {
panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Int",
clear_i, atch_i, atch_desc.format.ty());
}
ClearValue::Uint(_) => if atch_desc.format.ty() != FormatTy::Uint {
panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Uint",
clear_i, atch_i, atch_desc.format.ty());
}
ClearValue::Depth(_) => if atch_desc.format.ty() != FormatTy::Depth {
panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Depth",
clear_i, atch_i, atch_desc.format.ty());
}
ClearValue::Stencil(_) => if atch_desc.format.ty() != FormatTy::Stencil {
panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Stencil",
clear_i, atch_i, atch_desc.format.ty());
}
ClearValue::DepthStencil(_) => if atch_desc.format.ty() != FormatTy::DepthStencil {
panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: DepthStencil",
clear_i, atch_i, atch_desc.format.ty());
}
}
} else {
if clear_value != ClearValue::None {
panic!("Bad ClearValue! index: {}, attachment index: {}, expected: None, got: {:?}",
clear_i, atch_i, clear_value);
}
}
}
None => panic!("Not enough clear values"),
}
}
if clear_values_copy.count() != 0 {
panic!("Too many clear values")
}
let framebuffer_object = FramebufferAbstract::inner(&framebuffer).internal_object();
self.inner
.begin_render_pass(framebuffer.clone(), contents, clear_values)?;
self.render_pass_state = Some(RenderPassState {
subpass: (framebuffer.render_pass().clone(), 0),
contents,
framebuffer: framebuffer_object,
});
Ok(self)
}
}
#[inline]
pub fn end_render_pass(&mut self) -> Result<&mut Self, AutoCommandBufferBuilderContextError> {
unsafe {
if let Some(render_pass_state) = self.render_pass_state.as_ref() {
let (ref rp, index) = render_pass_state.subpass;
if rp.desc().subpasses().len() as u32 != index + 1 {
return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch {
actual: rp.desc().subpasses().len() as u32,
current: index,
});
}
} else {
return Err(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass);
}
if self.query_state.values().any(|state| state.in_subpass) {
return Err(AutoCommandBufferBuilderContextError::QueryIsActive);
}
debug_assert!(self.queue_family().supports_graphics());
self.inner.end_render_pass();
self.render_pass_state = None;
Ok(self)
}
}
pub fn execute_commands<C>(
&mut self,
command_buffer: C,
) -> Result<&mut Self, ExecuteCommandsError>
where
C: SecondaryCommandBuffer + Send + Sync + 'static,
{
self.check_command_buffer(&command_buffer)?;
let secondary_usage = command_buffer.inner().usage();
unsafe {
let mut builder = self.inner.execute_commands();
builder.add(command_buffer);
builder.submit()?;
}
self.state_cacher.invalidate();
self.usage = std::cmp::min(self.usage, secondary_usage);
Ok(self)
}
pub fn execute_commands_from_vec<C>(
&mut self,
command_buffers: Vec<C>,
) -> Result<&mut Self, ExecuteCommandsError>
where
C: SecondaryCommandBuffer + Send + Sync + 'static,
{
for command_buffer in &command_buffers {
self.check_command_buffer(command_buffer)?;
}
let mut secondary_usage = CommandBufferUsage::SimultaneousUse;
unsafe {
let mut builder = self.inner.execute_commands();
for command_buffer in command_buffers {
secondary_usage = std::cmp::min(secondary_usage, command_buffer.inner().usage());
builder.add(command_buffer);
}
builder.submit()?;
}
self.state_cacher.invalidate();
self.usage = std::cmp::min(self.usage, secondary_usage);
Ok(self)
}
fn check_command_buffer<C>(
&self,
command_buffer: &C,
) -> Result<(), AutoCommandBufferBuilderContextError>
where
C: SecondaryCommandBuffer + Send + Sync + 'static,
{
if let Some(render_pass) = command_buffer.inheritance().render_pass {
self.ensure_inside_render_pass_secondary(&render_pass)?;
} else {
self.ensure_outside_render_pass()?;
}
for state in self.query_state.values() {
match state.ty {
QueryType::Occlusion => match command_buffer.inheritance().occlusion_query {
Some(inherited_flags) => {
let inherited_flags = vk::QueryControlFlags::from(inherited_flags);
let state_flags = vk::QueryControlFlags::from(state.flags);
if inherited_flags & state_flags != state_flags {
return Err(AutoCommandBufferBuilderContextError::QueryNotInherited);
}
}
None => return Err(AutoCommandBufferBuilderContextError::QueryNotInherited),
},
QueryType::PipelineStatistics(state_flags) => {
let inherited_flags = command_buffer.inheritance().query_statistics_flags;
let inherited_flags = vk::QueryPipelineStatisticFlags::from(inherited_flags);
let state_flags = vk::QueryPipelineStatisticFlags::from(state_flags);
if inherited_flags & state_flags != state_flags {
return Err(AutoCommandBufferBuilderContextError::QueryNotInherited);
}
}
_ => (),
}
}
Ok(())
}
#[inline]
fn ensure_inside_render_pass_secondary(
&self,
render_pass: &CommandBufferInheritanceRenderPass<&dyn FramebufferAbstract>,
) -> Result<(), AutoCommandBufferBuilderContextError> {
let render_pass_state = self
.render_pass_state
.as_ref()
.ok_or(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass)?;
if render_pass_state.contents != SubpassContents::SecondaryCommandBuffers {
return Err(AutoCommandBufferBuilderContextError::WrongSubpassType);
}
if render_pass.subpass.index() != render_pass_state.subpass.1 {
return Err(AutoCommandBufferBuilderContextError::WrongSubpassIndex);
}
if !render_pass
.subpass
.render_pass()
.desc()
.is_compatible_with_desc(render_pass_state.subpass.0.desc())
{
return Err(AutoCommandBufferBuilderContextError::IncompatibleRenderPass);
}
if let Some(framebuffer) = render_pass.framebuffer {
if FramebufferAbstract::inner(framebuffer).internal_object()
!= render_pass_state.framebuffer
{
return Err(AutoCommandBufferBuilderContextError::IncompatibleFramebuffer);
}
}
Ok(())
}
#[inline]
pub fn next_subpass(
&mut self,
contents: SubpassContents,
) -> Result<&mut Self, AutoCommandBufferBuilderContextError> {
unsafe {
if let Some(render_pass_state) = self.render_pass_state.as_mut() {
let (ref rp, ref mut index) = render_pass_state.subpass;
if *index + 1 >= rp.desc().subpasses().len() as u32 {
return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch {
actual: rp.desc().subpasses().len() as u32,
current: *index,
});
} else {
*index += 1;
render_pass_state.contents = contents;
}
} else {
return Err(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass);
}
if self.query_state.values().any(|state| state.in_subpass) {
return Err(AutoCommandBufferBuilderContextError::QueryIsActive);
}
debug_assert!(self.queue_family().supports_graphics());
self.inner.next_subpass(contents);
Ok(self)
}
}
}
impl<P> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<P::Alloc>, P> where
P: CommandPoolBuilderAlloc
{
}
unsafe impl<L, P> DeviceOwned for AutoCommandBufferBuilder<L, P> {
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
unsafe fn push_constants<Pl, Pc>(
destination: &mut SyncCommandBufferBuilder,
pipeline: Pl,
push_constants: Pc,
) where
Pl: PipelineLayoutAbstract + Send + Sync + Clone + 'static,
{
for num_range in 0..pipeline.num_push_constants_ranges() {
let range = match pipeline.push_constants_range(num_range) {
Some(r) => r,
None => continue,
};
debug_assert_eq!(range.offset % 4, 0);
debug_assert_eq!(range.size % 4, 0);
let data = slice::from_raw_parts(
(&push_constants as *const Pc as *const u8).offset(range.offset as isize),
range.size as usize,
);
destination.push_constants::<_, [u8]>(
pipeline.clone(),
range.stages,
range.offset as u32,
range.size as u32,
data,
);
}
}
unsafe fn set_state(destination: &mut SyncCommandBufferBuilder, dynamic: &DynamicState) {
if let Some(line_width) = dynamic.line_width {
destination.set_line_width(line_width);
}
if let Some(ref viewports) = dynamic.viewports {
destination.set_viewport(0, viewports.iter().cloned().collect::<Vec<_>>().into_iter());
}
if let Some(ref scissors) = dynamic.scissors {
destination.set_scissor(0, scissors.iter().cloned().collect::<Vec<_>>().into_iter());
}
if let Some(compare_mask) = dynamic.compare_mask {
destination.set_stencil_compare_mask(compare_mask);
}
if let Some(write_mask) = dynamic.write_mask {
destination.set_stencil_write_mask(write_mask);
}
if let Some(reference) = dynamic.reference {
destination.set_stencil_reference(reference);
}
}
unsafe fn vertex_buffers(
destination: &mut SyncCommandBufferBuilder,
state_cacher: &mut StateCacher,
vertex_buffers: Vec<Box<dyn BufferAccess + Send + Sync>>,
) -> Result<(), SyncCommandBufferBuilderError> {
let binding_range = {
let mut compare = state_cacher.bind_vertex_buffers();
for vb in vertex_buffers.iter() {
compare.add(vb);
}
match compare.compare() {
Some(r) => r,
None => return Ok(()),
}
};
let first_binding = binding_range.start;
let num_bindings = binding_range.end - binding_range.start;
let mut binder = destination.bind_vertex_buffers();
for vb in vertex_buffers
.into_iter()
.skip(first_binding as usize)
.take(num_bindings as usize)
{
binder.add(vb);
}
binder.submit(first_binding)?;
Ok(())
}
unsafe fn descriptor_sets<Pl, S, Do, Doi>(
destination: &mut SyncCommandBufferBuilder,
state_cacher: &mut StateCacher,
gfx: bool,
pipeline: Pl,
sets: S,
dynamic_offsets: Do,
) -> Result<(), SyncCommandBufferBuilderError>
where
Pl: PipelineLayoutAbstract + Send + Sync + Clone + 'static,
S: DescriptorSetsCollection,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
let sets = sets.into_vec();
let dynamic_offsets: SmallVec<[u32; 32]> = dynamic_offsets.into_iter().collect();
let limits = pipeline.device().physical_device().limits();
let min_uniform_off_align = limits.min_uniform_buffer_offset_alignment() as u32;
let min_storage_off_align = limits.min_storage_buffer_offset_alignment() as u32;
let mut dynamic_offset_index = 0;
for set in &sets {
for desc_index in 0..set.num_bindings() {
let desc = DescriptorSetDesc::descriptor(&set, desc_index).unwrap();
if let DescriptorDescTy::Buffer(DescriptorBufferDesc {
dynamic: Some(true),
storage,
}) = desc.ty
{
if dynamic_offsets.len() > dynamic_offset_index {
if storage {
assert!(
dynamic_offsets[dynamic_offset_index] % min_storage_off_align == 0,
"Dynamic storage buffer offset must be a multiple of min_storage_buffer_offset_alignment: got {}, expected a multiple of {}",
dynamic_offsets[dynamic_offset_index],
min_storage_off_align
);
} else {
assert!(
dynamic_offsets[dynamic_offset_index] % min_uniform_off_align == 0,
"Dynamic uniform buffer offset must be a multiple of min_uniform_buffer_offset_alignment: got {}, expected a multiple of {}",
dynamic_offsets[dynamic_offset_index],
min_uniform_off_align
);
}
}
dynamic_offset_index += 1;
}
}
}
assert!(
!(dynamic_offsets.len() < dynamic_offset_index),
"Too few dynamic offsets: got {}, expected {}",
dynamic_offsets.len(),
dynamic_offset_index
);
assert!(
!(dynamic_offsets.len() > dynamic_offset_index),
"Too many dynamic offsets: got {}, expected {}",
dynamic_offsets.len(),
dynamic_offset_index
);
let first_binding = {
let mut compare = state_cacher.bind_descriptor_sets(gfx);
for set in sets.iter() {
compare.add(set, &dynamic_offsets);
}
compare.compare()
};
let first_binding = match first_binding {
None => return Ok(()),
Some(fb) => fb,
};
let mut sets_binder = destination.bind_descriptor_sets();
for set in sets.into_iter().skip(first_binding as usize) {
sets_binder.add(set);
}
sets_binder.submit(
gfx,
pipeline.clone(),
first_binding,
dynamic_offsets.into_iter(),
)?;
Ok(())
}
pub struct PrimaryAutoCommandBuffer<P = StandardCommandPoolAlloc> {
inner: SyncCommandBuffer,
pool_alloc: P,
submit_state: SubmitState,
}
unsafe impl<P> DeviceOwned for PrimaryAutoCommandBuffer<P> {
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
unsafe impl<P> PrimaryCommandBuffer for PrimaryAutoCommandBuffer<P> {
#[inline]
fn inner(&self) -> &UnsafeCommandBuffer {
self.inner.as_ref()
}
#[inline]
fn lock_submit(
&self,
future: &dyn GpuFuture,
queue: &Queue,
) -> Result<(), CommandBufferExecError> {
match self.submit_state {
SubmitState::OneTime {
ref already_submitted,
} => {
let was_already_submitted = already_submitted.swap(true, Ordering::SeqCst);
if was_already_submitted {
return Err(CommandBufferExecError::OneTimeSubmitAlreadySubmitted);
}
}
SubmitState::ExclusiveUse { ref in_use } => {
let already_in_use = in_use.swap(true, Ordering::SeqCst);
if already_in_use {
return Err(CommandBufferExecError::ExclusiveAlreadyInUse);
}
}
SubmitState::Concurrent => (),
};
let err = match self.inner.lock_submit(future, queue) {
Ok(()) => return Ok(()),
Err(err) => err,
};
match self.submit_state {
SubmitState::OneTime {
ref already_submitted,
} => {
already_submitted.store(false, Ordering::SeqCst);
}
SubmitState::ExclusiveUse { ref in_use } => {
in_use.store(false, Ordering::SeqCst);
}
SubmitState::Concurrent => (),
};
Err(err)
}
#[inline]
unsafe fn unlock(&self) {
self.inner.unlock();
match self.submit_state {
SubmitState::OneTime {
ref already_submitted,
} => {
debug_assert!(already_submitted.load(Ordering::SeqCst));
}
SubmitState::ExclusiveUse { ref in_use } => {
let old_val = in_use.swap(false, Ordering::SeqCst);
debug_assert!(old_val);
}
SubmitState::Concurrent => (),
};
}
#[inline]
fn check_buffer_access(
&self,
buffer: &dyn BufferAccess,
exclusive: bool,
queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
self.inner.check_buffer_access(buffer, exclusive, queue)
}
#[inline]
fn check_image_access(
&self,
image: &dyn ImageAccess,
layout: ImageLayout,
exclusive: bool,
queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
self.inner
.check_image_access(image, layout, exclusive, queue)
}
}
pub struct SecondaryAutoCommandBuffer<P = StandardCommandPoolAlloc> {
inner: SyncCommandBuffer,
pool_alloc: P,
inheritance: CommandBufferInheritance<Box<dyn FramebufferAbstract + Send + Sync>>,
submit_state: SubmitState,
}
unsafe impl<P> DeviceOwned for SecondaryAutoCommandBuffer<P> {
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
unsafe impl<P> SecondaryCommandBuffer for SecondaryAutoCommandBuffer<P> {
#[inline]
fn inner(&self) -> &UnsafeCommandBuffer {
self.inner.as_ref()
}
#[inline]
fn lock_record(&self) -> Result<(), CommandBufferExecError> {
match self.submit_state {
SubmitState::OneTime {
ref already_submitted,
} => {
let was_already_submitted = already_submitted.swap(true, Ordering::SeqCst);
if was_already_submitted {
return Err(CommandBufferExecError::OneTimeSubmitAlreadySubmitted);
}
}
SubmitState::ExclusiveUse { ref in_use } => {
let already_in_use = in_use.swap(true, Ordering::SeqCst);
if already_in_use {
return Err(CommandBufferExecError::ExclusiveAlreadyInUse);
}
}
SubmitState::Concurrent => (),
};
Ok(())
}
#[inline]
unsafe fn unlock(&self) {
match self.submit_state {
SubmitState::OneTime {
ref already_submitted,
} => {
debug_assert!(already_submitted.load(Ordering::SeqCst));
}
SubmitState::ExclusiveUse { ref in_use } => {
let old_val = in_use.swap(false, Ordering::SeqCst);
debug_assert!(old_val);
}
SubmitState::Concurrent => (),
};
}
fn inheritance(&self) -> CommandBufferInheritance<&dyn FramebufferAbstract> {
CommandBufferInheritance {
render_pass: self.inheritance.render_pass.as_ref().map(
|CommandBufferInheritanceRenderPass {
subpass,
framebuffer,
}| {
CommandBufferInheritanceRenderPass {
subpass: subpass.clone(),
framebuffer: framebuffer.as_ref().map(|f| f.as_ref() as &_),
}
},
),
occlusion_query: self.inheritance.occlusion_query,
query_statistics_flags: self.inheritance.query_statistics_flags,
}
}
#[inline]
fn num_buffers(&self) -> usize {
self.inner.num_buffers()
}
#[inline]
fn buffer(&self, index: usize) -> Option<(&dyn BufferAccess, PipelineMemoryAccess)> {
self.inner.buffer(index)
}
#[inline]
fn num_images(&self) -> usize {
self.inner.num_images()
}
#[inline]
fn image(
&self,
index: usize,
) -> Option<(
&dyn ImageAccess,
PipelineMemoryAccess,
ImageLayout,
ImageLayout,
)> {
self.inner.image(index)
}
}
#[derive(Debug)]
enum SubmitState {
Concurrent,
ExclusiveUse {
in_use: AtomicBool,
},
OneTime {
already_submitted: AtomicBool,
},
}
macro_rules! err_gen {
($name:ident { $($err:ident,)+ }) => (
#[derive(Debug, Clone)]
pub enum $name {
$(
$err($err),
)+
}
impl error::Error for $name {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
$(
$name::$err(ref err) => Some(err),
)+
}
}
}
impl fmt::Display for $name {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", match *self {
$(
$name::$err(_) => {
concat!("a ", stringify!($err))
}
)+
})
}
}
$(
impl From<$err> for $name {
#[inline]
fn from(err: $err) -> $name {
$name::$err(err)
}
}
)+
);
}
err_gen!(BuildError {
AutoCommandBufferBuilderContextError,
OomError,
});
err_gen!(BeginRenderPassError {
AutoCommandBufferBuilderContextError,
SyncCommandBufferBuilderError,
});
err_gen!(CopyImageError {
AutoCommandBufferBuilderContextError,
CheckCopyImageError,
SyncCommandBufferBuilderError,
});
err_gen!(BlitImageError {
AutoCommandBufferBuilderContextError,
CheckBlitImageError,
SyncCommandBufferBuilderError,
});
err_gen!(ClearColorImageError {
AutoCommandBufferBuilderContextError,
CheckClearColorImageError,
SyncCommandBufferBuilderError,
});
err_gen!(CopyBufferError {
AutoCommandBufferBuilderContextError,
CheckCopyBufferError,
SyncCommandBufferBuilderError,
});
err_gen!(CopyBufferImageError {
AutoCommandBufferBuilderContextError,
CheckCopyBufferImageError,
SyncCommandBufferBuilderError,
});
err_gen!(CopyQueryPoolResultsError {
AutoCommandBufferBuilderContextError,
CheckCopyQueryPoolResultsError,
SyncCommandBufferBuilderError,
});
err_gen!(FillBufferError {
AutoCommandBufferBuilderContextError,
CheckFillBufferError,
});
err_gen!(DebugMarkerError {
AutoCommandBufferBuilderContextError,
CheckColorError,
});
err_gen!(DispatchError {
AutoCommandBufferBuilderContextError,
CheckPushConstantsValidityError,
CheckDescriptorSetsValidityError,
CheckDispatchError,
SyncCommandBufferBuilderError,
});
err_gen!(DispatchIndirectError {
AutoCommandBufferBuilderContextError,
CheckPushConstantsValidityError,
CheckDescriptorSetsValidityError,
CheckIndirectBufferError,
SyncCommandBufferBuilderError,
});
err_gen!(DrawError {
AutoCommandBufferBuilderContextError,
CheckDynamicStateValidityError,
CheckPushConstantsValidityError,
CheckDescriptorSetsValidityError,
CheckVertexBufferError,
SyncCommandBufferBuilderError,
});
err_gen!(DrawIndexedError {
AutoCommandBufferBuilderContextError,
CheckDynamicStateValidityError,
CheckPushConstantsValidityError,
CheckDescriptorSetsValidityError,
CheckVertexBufferError,
CheckIndexBufferError,
SyncCommandBufferBuilderError,
});
err_gen!(DrawIndirectError {
AutoCommandBufferBuilderContextError,
CheckDynamicStateValidityError,
CheckPushConstantsValidityError,
CheckDescriptorSetsValidityError,
CheckVertexBufferError,
CheckIndirectBufferError,
SyncCommandBufferBuilderError,
});
err_gen!(DrawIndexedIndirectError {
AutoCommandBufferBuilderContextError,
CheckDynamicStateValidityError,
CheckPushConstantsValidityError,
CheckDescriptorSetsValidityError,
CheckVertexBufferError,
CheckIndexBufferError,
CheckIndirectBufferError,
SyncCommandBufferBuilderError,
});
err_gen!(ExecuteCommandsError {
AutoCommandBufferBuilderContextError,
SyncCommandBufferBuilderError,
});
err_gen!(BeginQueryError {
AutoCommandBufferBuilderContextError,
CheckBeginQueryError,
});
err_gen!(EndQueryError {
AutoCommandBufferBuilderContextError,
CheckEndQueryError,
});
err_gen!(WriteTimestampError {
AutoCommandBufferBuilderContextError,
CheckWriteTimestampError,
});
err_gen!(ResetQueryPoolError {
AutoCommandBufferBuilderContextError,
CheckResetQueryPoolError,
});
err_gen!(UpdateBufferError {
AutoCommandBufferBuilderContextError,
CheckUpdateBufferError,
});
#[derive(Debug, Copy, Clone)]
pub enum AutoCommandBufferBuilderContextError {
ForbiddenInsideRenderPass,
ForbiddenOutsideRenderPass,
IncompatibleFramebuffer,
IncompatibleRenderPass,
NotSupportedByQueueFamily,
NumSubpassesMismatch {
actual: u32,
current: u32,
},
QueryIsActive,
QueryNotActive,
QueryNotInherited,
WrongSubpassIndex,
WrongSubpassType,
}
impl error::Error for AutoCommandBufferBuilderContextError {}
impl fmt::Display for AutoCommandBufferBuilderContextError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass => {
"operation forbidden inside of a render pass"
}
AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass => {
"operation forbidden outside of a render pass"
}
AutoCommandBufferBuilderContextError::IncompatibleFramebuffer => {
"tried to use a secondary command buffer with a specified framebuffer that is \
incompatible with the current framebuffer"
}
AutoCommandBufferBuilderContextError::IncompatibleRenderPass => {
"tried to use a graphics pipeline or secondary command buffer whose render pass \
is incompatible with the current render pass"
}
AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily => {
"the queue family doesn't allow this operation"
}
AutoCommandBufferBuilderContextError::NumSubpassesMismatch { .. } => {
"tried to end a render pass with subpasses remaining, or tried to go to next \
subpass with no subpass remaining"
}
AutoCommandBufferBuilderContextError::QueryIsActive => {
"a query is active that conflicts with the current operation"
}
AutoCommandBufferBuilderContextError::QueryNotActive => {
"this query was not active"
}
AutoCommandBufferBuilderContextError::QueryNotInherited => {
"a query is active that is not included in the inheritance of the secondary command buffer"
}
AutoCommandBufferBuilderContextError::WrongSubpassIndex => {
"tried to use a graphics pipeline whose subpass index didn't match the current \
subpass index"
}
AutoCommandBufferBuilderContextError::WrongSubpassType => {
"tried to execute a secondary command buffer inside a subpass that only allows \
inline commands, or a draw command in a subpass that only allows secondary \
command buffers"
}
}
)
}
}
#[cfg(test)]
mod tests {
use crate::buffer::BufferUsage;
use crate::buffer::CpuAccessibleBuffer;
use crate::command_buffer::synced::SyncCommandBufferBuilderError;
use crate::command_buffer::AutoCommandBufferBuilder;
use crate::command_buffer::CommandBufferExecError;
use crate::command_buffer::CommandBufferUsage;
use crate::command_buffer::ExecuteCommandsError;
use crate::command_buffer::PrimaryCommandBuffer;
use crate::device::Device;
use crate::device::DeviceExtensions;
use crate::device::Features;
use crate::instance;
use crate::sync::GpuFuture;
use std::sync::Arc;
#[test]
fn copy_buffer_dimensions() {
let instance = instance!();
let phys = match instance::PhysicalDevice::enumerate(&instance).next() {
Some(p) => p,
None => return,
};
let queue_family = match phys.queue_families().next() {
Some(q) => q,
None => return,
};
let (device, mut queues) = Device::new(
phys,
&Features::none(),
&DeviceExtensions::none(),
std::iter::once((queue_family, 0.5)),
)
.unwrap();
let queue = queues.next().unwrap();
let source = CpuAccessibleBuffer::from_iter(
device.clone(),
BufferUsage::all(),
true,
[1_u32, 2].iter().copied(),
)
.unwrap();
let destination = CpuAccessibleBuffer::from_iter(
device.clone(),
BufferUsage::all(),
true,
[0_u32, 10, 20, 3, 4].iter().copied(),
)
.unwrap();
let mut cbb = AutoCommandBufferBuilder::primary(
device.clone(),
queue.family(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
cbb.copy_buffer_dimensions(source.clone(), 0, destination.clone(), 1, 2)
.unwrap();
let cb = cbb.build().unwrap();
let future = cb
.execute(queue.clone())
.unwrap()
.then_signal_fence_and_flush()
.unwrap();
future.wait(None).unwrap();
let result = destination.read().unwrap();
assert_eq!(*result, [0_u32, 1, 2, 3, 4]);
}
#[test]
fn secondary_nonconcurrent_conflict() {
let (device, queue) = gfx_dev_and_queue!();
let builder = AutoCommandBufferBuilder::secondary_compute(
device.clone(),
queue.family(),
CommandBufferUsage::MultipleSubmit,
)
.unwrap();
let secondary = Arc::new(builder.build().unwrap());
{
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
queue.family(),
CommandBufferUsage::SimultaneousUse,
)
.unwrap();
builder.execute_commands(secondary.clone()).unwrap();
assert!(matches!(
builder.execute_commands(secondary.clone()),
Err(ExecuteCommandsError::SyncCommandBufferBuilderError(
SyncCommandBufferBuilderError::ExecError(
CommandBufferExecError::ExclusiveAlreadyInUse
)
))
));
}
{
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
queue.family(),
CommandBufferUsage::SimultaneousUse,
)
.unwrap();
builder.execute_commands(secondary.clone()).unwrap();
let cb1 = builder.build().unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
queue.family(),
CommandBufferUsage::SimultaneousUse,
)
.unwrap();
assert!(matches!(
builder.execute_commands(secondary.clone()),
Err(ExecuteCommandsError::SyncCommandBufferBuilderError(
SyncCommandBufferBuilderError::ExecError(
CommandBufferExecError::ExclusiveAlreadyInUse
)
))
));
std::mem::drop(cb1);
builder.execute_commands(secondary.clone()).unwrap();
}
}
}