blob: b0ac663f6d922e5995705fc50d4daee655d4ec35 [file] [log] [blame]
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
pub use super::commands::{
bind_push::UnsafeCommandBufferBuilderBindVertexBuffer,
secondary::UnsafeCommandBufferBuilderExecuteCommands,
};
use super::{
pool::CommandPoolAlloc, CommandBufferInheritanceInfo, CommandBufferLevel, CommandBufferUsage,
};
use crate::{
command_buffer::{
CommandBufferInheritanceRenderPassInfo, CommandBufferInheritanceRenderPassType,
CommandBufferInheritanceRenderingInfo,
},
device::{Device, DeviceOwned},
query::QueryControlFlags,
OomError, VulkanError, VulkanObject,
};
use smallvec::SmallVec;
use std::{ptr, sync::Arc};
/// Command buffer being built.
///
/// # Safety
///
/// - All submitted commands must be valid and follow the requirements of the Vulkan specification.
/// - Any resources used by submitted commands must outlive the returned builder and its created
/// command buffer. They must be protected against data races through manual synchronization.
///
/// > **Note**: Some checks are still made with `debug_assert!`. Do not expect to be able to
/// > submit invalid commands.
#[derive(Debug)]
pub struct UnsafeCommandBufferBuilder {
pub(super) handle: ash::vk::CommandBuffer,
pub(super) device: Arc<Device>,
usage: CommandBufferUsage,
}
impl UnsafeCommandBufferBuilder {
/// Creates a new builder, for recording commands.
///
/// # Safety
///
/// - `pool_alloc` must outlive the returned builder and its created command buffer.
/// - `kind` must match how `pool_alloc` was created.
#[inline]
pub unsafe fn new(
pool_alloc: &CommandPoolAlloc,
begin_info: CommandBufferBeginInfo,
) -> Result<UnsafeCommandBufferBuilder, OomError> {
let CommandBufferBeginInfo {
usage,
inheritance_info,
_ne: _,
} = begin_info;
// VUID-vkBeginCommandBuffer-commandBuffer-00049
// Can't validate
// VUID-vkBeginCommandBuffer-commandBuffer-00050
// Can't validate
let device = pool_alloc.device().clone();
// VUID-vkBeginCommandBuffer-commandBuffer-00051
debug_assert_eq!(
pool_alloc.level() == CommandBufferLevel::Secondary,
inheritance_info.is_some()
);
{
// VUID-vkBeginCommandBuffer-commandBuffer-02840
// Guaranteed by use of enum
let mut flags = ash::vk::CommandBufferUsageFlags::from(usage);
let mut inheritance_info_vk = None;
let mut inheritance_rendering_info_vk = None;
let mut color_attachment_formats_vk: SmallVec<[_; 4]> = SmallVec::new();
if let Some(inheritance_info) = &inheritance_info {
let &CommandBufferInheritanceInfo {
ref render_pass,
occlusion_query,
query_statistics_flags,
_ne: _,
} = inheritance_info;
let inheritance_info_vk =
inheritance_info_vk.insert(ash::vk::CommandBufferInheritanceInfo {
render_pass: ash::vk::RenderPass::null(),
subpass: 0,
framebuffer: ash::vk::Framebuffer::null(),
occlusion_query_enable: ash::vk::FALSE,
query_flags: ash::vk::QueryControlFlags::empty(),
pipeline_statistics: query_statistics_flags.into(),
..Default::default()
});
if let Some(flags) = occlusion_query {
inheritance_info_vk.occlusion_query_enable = ash::vk::TRUE;
if flags.intersects(QueryControlFlags::PRECISE) {
inheritance_info_vk.query_flags = ash::vk::QueryControlFlags::PRECISE;
}
}
if let Some(render_pass) = render_pass {
flags |= ash::vk::CommandBufferUsageFlags::RENDER_PASS_CONTINUE;
match render_pass {
CommandBufferInheritanceRenderPassType::BeginRenderPass(
render_pass_info,
) => {
let &CommandBufferInheritanceRenderPassInfo {
ref subpass,
ref framebuffer,
} = render_pass_info;
inheritance_info_vk.render_pass = subpass.render_pass().handle();
inheritance_info_vk.subpass = subpass.index();
inheritance_info_vk.framebuffer = framebuffer
.as_ref()
.map(|fb| fb.handle())
.unwrap_or_default();
}
CommandBufferInheritanceRenderPassType::BeginRendering(rendering_info) => {
let &CommandBufferInheritanceRenderingInfo {
view_mask,
ref color_attachment_formats,
depth_attachment_format,
stencil_attachment_format,
rasterization_samples,
} = rendering_info;
color_attachment_formats_vk.extend(
color_attachment_formats.iter().map(|format| {
format.map_or(ash::vk::Format::UNDEFINED, Into::into)
}),
);
let inheritance_rendering_info_vk = inheritance_rendering_info_vk
.insert(ash::vk::CommandBufferInheritanceRenderingInfo {
flags: ash::vk::RenderingFlags::empty(),
view_mask,
color_attachment_count: color_attachment_formats_vk.len()
as u32,
p_color_attachment_formats: color_attachment_formats_vk
.as_ptr(),
depth_attachment_format: depth_attachment_format
.map_or(ash::vk::Format::UNDEFINED, Into::into),
stencil_attachment_format: stencil_attachment_format
.map_or(ash::vk::Format::UNDEFINED, Into::into),
rasterization_samples: rasterization_samples.into(),
..Default::default()
});
inheritance_info_vk.p_next =
inheritance_rendering_info_vk as *const _ as *const _;
}
}
}
}
let begin_info_vk = ash::vk::CommandBufferBeginInfo {
flags,
p_inheritance_info: inheritance_info_vk
.as_ref()
.map_or(ptr::null(), |info| info),
..Default::default()
};
let fns = device.fns();
(fns.v1_0.begin_command_buffer)(pool_alloc.handle(), &begin_info_vk)
.result()
.map_err(VulkanError::from)?;
}
Ok(UnsafeCommandBufferBuilder {
handle: pool_alloc.handle(),
device,
usage,
})
}
/// Turns the builder into an actual command buffer.
#[inline]
pub fn build(self) -> Result<UnsafeCommandBuffer, OomError> {
unsafe {
let fns = self.device.fns();
(fns.v1_0.end_command_buffer)(self.handle)
.result()
.map_err(VulkanError::from)?;
Ok(UnsafeCommandBuffer {
command_buffer: self.handle,
device: self.device.clone(),
usage: self.usage,
})
}
}
}
unsafe impl VulkanObject for UnsafeCommandBufferBuilder {
type Handle = ash::vk::CommandBuffer;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for UnsafeCommandBufferBuilder {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
/// Parameters to begin recording a command buffer.
#[derive(Clone, Debug)]
pub struct CommandBufferBeginInfo {
/// How the command buffer will be used.
///
/// The default value is [`CommandBufferUsage::MultipleSubmit`].
pub usage: CommandBufferUsage,
/// For a secondary command buffer, this must be `Some`, containing the context that will be
/// inherited from the primary command buffer. For a primary command buffer, this must be
/// `None`.
///
/// The default value is `None`.
pub inheritance_info: Option<CommandBufferInheritanceInfo>,
pub _ne: crate::NonExhaustive,
}
impl Default for CommandBufferBeginInfo {
#[inline]
fn default() -> Self {
Self {
usage: CommandBufferUsage::MultipleSubmit,
inheritance_info: None,
_ne: crate::NonExhaustive(()),
}
}
}
/// Command buffer that has been built.
///
/// # Safety
///
/// The command buffer must not outlive the command pool that it was created from,
/// nor the resources used by the recorded commands.
#[derive(Debug)]
pub struct UnsafeCommandBuffer {
command_buffer: ash::vk::CommandBuffer,
device: Arc<Device>,
usage: CommandBufferUsage,
}
impl UnsafeCommandBuffer {
#[inline]
pub fn usage(&self) -> CommandBufferUsage {
self.usage
}
}
unsafe impl DeviceOwned for UnsafeCommandBuffer {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
unsafe impl VulkanObject for UnsafeCommandBuffer {
type Handle = ash::vk::CommandBuffer;
#[inline]
fn handle(&self) -> Self::Handle {
self.command_buffer
}
}