aboutsummaryrefslogtreecommitdiff
path: root/src/command_buffer/standard/builder/render_pass.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/command_buffer/standard/builder/render_pass.rs')
-rw-r--r--src/command_buffer/standard/builder/render_pass.rs2248
1 files changed, 2248 insertions, 0 deletions
diff --git a/src/command_buffer/standard/builder/render_pass.rs b/src/command_buffer/standard/builder/render_pass.rs
new file mode 100644
index 0000000..d3182ef
--- /dev/null
+++ b/src/command_buffer/standard/builder/render_pass.rs
@@ -0,0 +1,2248 @@
+// Copyright (c) 2022 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.
+
+use super::{
+ BeginRenderPassState, BeginRenderingState, ClearAttachment, ClearRect, CommandBufferBuilder,
+ RenderPassBeginInfo, RenderPassError, RenderPassState, RenderPassStateAttachmentInfo,
+ RenderPassStateAttachmentResolveInfo, RenderPassStateAttachments, RenderPassStateType,
+ RenderingAttachmentInfo, RenderingAttachmentResolveInfo, RenderingInfo, ResourcesState,
+};
+use crate::{
+ command_buffer::{
+ allocator::CommandBufferAllocator, PrimaryCommandBuffer, ResourceInCommand, ResourceUseRef,
+ SubpassContents,
+ },
+ device::{DeviceOwned, QueueFlags},
+ format::{ClearColorValue, ClearValue, NumericType},
+ image::{ImageAspects, ImageLayout, ImageUsage, SampleCount},
+ pipeline::graphics::render_pass::PipelineRenderingCreateInfo,
+ render_pass::{AttachmentDescription, LoadOp, ResolveMode, SubpassDescription},
+ sync::PipelineStageAccess,
+ RequiresOneOf, Version, VulkanObject,
+};
+use smallvec::SmallVec;
+use std::cmp::min;
+
+impl<A> CommandBufferBuilder<PrimaryCommandBuffer<A::Alloc>, A>
+where
+ A: CommandBufferAllocator,
+{
+ /// Begins a render pass using a render pass object and framebuffer.
+ ///
+ /// You must call this or `begin_rendering` before you can record draw commands.
+ ///
+ /// `contents` specifies what kinds of commands will be recorded in the render pass, either
+ /// draw commands or executions of secondary command buffers.
+ ///
+ /// # Safety
+ ///
+ /// - Appropriate synchronization must be provided for all images
+ /// that are accessed by the command.
+ /// - All images that are accessed by the command must be in the expected image layout.
+ #[inline]
+ pub unsafe fn begin_render_pass(
+ &mut self,
+ render_pass_begin_info: RenderPassBeginInfo,
+ contents: SubpassContents,
+ ) -> Result<&mut Self, RenderPassError> {
+ self.validate_begin_render_pass(&render_pass_begin_info, contents)?;
+
+ unsafe { Ok(self.begin_render_pass_unchecked(render_pass_begin_info, contents)) }
+ }
+
+ fn validate_begin_render_pass(
+ &self,
+ render_pass_begin_info: &RenderPassBeginInfo,
+ contents: SubpassContents,
+ ) -> Result<(), RenderPassError> {
+ let device = self.device();
+
+ // VUID-VkSubpassBeginInfo-contents-parameter
+ contents.validate_device(device)?;
+
+ let queue_family_properties = self.queue_family_properties();
+
+ // VUID-vkCmdBeginRenderPass2-commandBuffer-cmdpool
+ if !queue_family_properties
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS)
+ {
+ return Err(RenderPassError::NotSupportedByQueueFamily);
+ }
+
+ // VUID-vkCmdBeginRenderPass2-renderpass
+ if self.builder_state.render_pass.is_some() {
+ return Err(RenderPassError::ForbiddenInsideRenderPass);
+ }
+
+ let RenderPassBeginInfo {
+ render_pass,
+ framebuffer,
+ render_area_offset,
+ render_area_extent,
+ clear_values,
+ _ne: _,
+ } = render_pass_begin_info;
+
+ // VUID-VkRenderPassBeginInfo-commonparent
+ // VUID-vkCmdBeginRenderPass2-framebuffer-02779
+ assert_eq!(device, framebuffer.device());
+
+ // VUID-VkRenderPassBeginInfo-renderPass-00904
+ if !render_pass.is_compatible_with(framebuffer.render_pass()) {
+ return Err(RenderPassError::FramebufferNotCompatible);
+ }
+
+ for i in 0..2 {
+ // VUID-VkRenderPassBeginInfo-pNext-02852
+ // VUID-VkRenderPassBeginInfo-pNext-02853
+ if render_area_offset[i] + render_area_extent[i] > framebuffer.extent()[i] {
+ return Err(RenderPassError::RenderAreaOutOfBounds);
+ }
+ }
+
+ for (attachment_index, (attachment_desc, image_view)) in render_pass
+ .attachments()
+ .iter()
+ .zip(framebuffer.attachments())
+ .enumerate()
+ {
+ let attachment_index = attachment_index as u32;
+ let &AttachmentDescription {
+ initial_layout,
+ final_layout,
+ ..
+ } = attachment_desc;
+
+ for layout in [initial_layout, final_layout] {
+ match layout {
+ ImageLayout::ColorAttachmentOptimal => {
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03094
+ if !image_view.usage().intersects(ImageUsage::COLOR_ATTACHMENT) {
+ return Err(RenderPassError::AttachmentImageMissingUsage {
+ attachment_index,
+ usage: "color_attachment",
+ });
+ }
+ }
+ ImageLayout::DepthReadOnlyStencilAttachmentOptimal
+ | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
+ | ImageLayout::DepthStencilAttachmentOptimal
+ | ImageLayout::DepthStencilReadOnlyOptimal => {
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03096
+ if !image_view
+ .usage()
+ .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
+ {
+ return Err(RenderPassError::AttachmentImageMissingUsage {
+ attachment_index,
+ usage: "depth_stencil_attachment",
+ });
+ }
+ }
+ ImageLayout::ShaderReadOnlyOptimal => {
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03097
+ if !image_view
+ .usage()
+ .intersects(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT)
+ {
+ return Err(RenderPassError::AttachmentImageMissingUsage {
+ attachment_index,
+ usage: "sampled or input_attachment",
+ });
+ }
+ }
+ ImageLayout::TransferSrcOptimal => {
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03098
+ if !image_view.usage().intersects(ImageUsage::TRANSFER_SRC) {
+ return Err(RenderPassError::AttachmentImageMissingUsage {
+ attachment_index,
+ usage: "transfer_src",
+ });
+ }
+ }
+ ImageLayout::TransferDstOptimal => {
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03099
+ if !image_view.usage().intersects(ImageUsage::TRANSFER_DST) {
+ return Err(RenderPassError::AttachmentImageMissingUsage {
+ attachment_index,
+ usage: "transfer_dst",
+ });
+ }
+ }
+ _ => (),
+ }
+ }
+ }
+
+ for subpass_desc in render_pass.subpasses() {
+ let SubpassDescription {
+ view_mask: _,
+ input_attachments,
+ color_attachments,
+ resolve_attachments,
+ depth_stencil_attachment,
+ preserve_attachments: _,
+ _ne: _,
+ } = subpass_desc;
+
+ for atch_ref in (input_attachments.iter())
+ .chain(color_attachments)
+ .chain(resolve_attachments)
+ .chain([depth_stencil_attachment])
+ .flatten()
+ {
+ let image_view = &framebuffer.attachments()[atch_ref.attachment as usize];
+
+ match atch_ref.layout {
+ ImageLayout::ColorAttachmentOptimal => {
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03094
+ if !image_view.usage().intersects(ImageUsage::COLOR_ATTACHMENT) {
+ return Err(RenderPassError::AttachmentImageMissingUsage {
+ attachment_index: atch_ref.attachment,
+ usage: "color_attachment",
+ });
+ }
+ }
+ ImageLayout::DepthReadOnlyStencilAttachmentOptimal
+ | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
+ | ImageLayout::DepthStencilAttachmentOptimal
+ | ImageLayout::DepthStencilReadOnlyOptimal => {
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03096
+ if !image_view
+ .usage()
+ .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
+ {
+ return Err(RenderPassError::AttachmentImageMissingUsage {
+ attachment_index: atch_ref.attachment,
+ usage: "depth_stencil_attachment",
+ });
+ }
+ }
+ ImageLayout::ShaderReadOnlyOptimal => {
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03097
+ if !image_view
+ .usage()
+ .intersects(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT)
+ {
+ return Err(RenderPassError::AttachmentImageMissingUsage {
+ attachment_index: atch_ref.attachment,
+ usage: "sampled or input_attachment",
+ });
+ }
+ }
+ ImageLayout::TransferSrcOptimal => {
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03098
+ if !image_view.usage().intersects(ImageUsage::TRANSFER_SRC) {
+ return Err(RenderPassError::AttachmentImageMissingUsage {
+ attachment_index: atch_ref.attachment,
+ usage: "transfer_src",
+ });
+ }
+ }
+ ImageLayout::TransferDstOptimal => {
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03099
+ if !image_view.usage().intersects(ImageUsage::TRANSFER_DST) {
+ return Err(RenderPassError::AttachmentImageMissingUsage {
+ attachment_index: atch_ref.attachment,
+ usage: "transfer_dst",
+ });
+ }
+ }
+ _ => (),
+ }
+ }
+ }
+
+ // VUID-VkRenderPassBeginInfo-clearValueCount-00902
+ if clear_values.len() < render_pass.attachments().len() {
+ return Err(RenderPassError::ClearValueMissing {
+ attachment_index: clear_values.len() as u32,
+ });
+ }
+
+ // VUID-VkRenderPassBeginInfo-clearValueCount-04962
+ for (attachment_index, (attachment_desc, &clear_value)) in render_pass
+ .attachments()
+ .iter()
+ .zip(clear_values)
+ .enumerate()
+ {
+ let attachment_index = attachment_index as u32;
+ let attachment_format = attachment_desc.format.unwrap();
+
+ if attachment_desc.load_op == LoadOp::Clear
+ || attachment_desc.stencil_load_op == LoadOp::Clear
+ {
+ let clear_value = match clear_value {
+ Some(x) => x,
+ None => return Err(RenderPassError::ClearValueMissing { attachment_index }),
+ };
+
+ if let (Some(numeric_type), LoadOp::Clear) =
+ (attachment_format.type_color(), attachment_desc.load_op)
+ {
+ match numeric_type {
+ NumericType::SFLOAT
+ | NumericType::UFLOAT
+ | NumericType::SNORM
+ | NumericType::UNORM
+ | NumericType::SSCALED
+ | NumericType::USCALED
+ | NumericType::SRGB => {
+ if !matches!(clear_value, ClearValue::Float(_)) {
+ return Err(RenderPassError::ClearValueNotCompatible {
+ clear_value,
+ attachment_index,
+ attachment_format,
+ });
+ }
+ }
+ NumericType::SINT => {
+ if !matches!(clear_value, ClearValue::Int(_)) {
+ return Err(RenderPassError::ClearValueNotCompatible {
+ clear_value,
+ attachment_index,
+ attachment_format,
+ });
+ }
+ }
+ NumericType::UINT => {
+ if !matches!(clear_value, ClearValue::Uint(_)) {
+ return Err(RenderPassError::ClearValueNotCompatible {
+ clear_value,
+ attachment_index,
+ attachment_format,
+ });
+ }
+ }
+ }
+ } else {
+ let attachment_aspects = attachment_format.aspects();
+ let need_depth = attachment_aspects.intersects(ImageAspects::DEPTH)
+ && attachment_desc.load_op == LoadOp::Clear;
+ let need_stencil = attachment_aspects.intersects(ImageAspects::STENCIL)
+ && attachment_desc.stencil_load_op == LoadOp::Clear;
+
+ if need_depth && need_stencil {
+ if !matches!(clear_value, ClearValue::DepthStencil(_)) {
+ return Err(RenderPassError::ClearValueNotCompatible {
+ clear_value,
+ attachment_index,
+ attachment_format,
+ });
+ }
+ } else if need_depth {
+ if !matches!(clear_value, ClearValue::Depth(_)) {
+ return Err(RenderPassError::ClearValueNotCompatible {
+ clear_value,
+ attachment_index,
+ attachment_format,
+ });
+ }
+ } else if need_stencil {
+ if !matches!(clear_value, ClearValue::Stencil(_)) {
+ return Err(RenderPassError::ClearValueNotCompatible {
+ clear_value,
+ attachment_index,
+ attachment_format,
+ });
+ }
+ }
+ }
+ }
+ }
+
+ // VUID-vkCmdBeginRenderPass2-initialLayout-03100
+ // TODO:
+
+ // VUID-vkCmdBeginRenderPass2-srcStageMask-06453
+ // TODO:
+
+ // VUID-vkCmdBeginRenderPass2-dstStageMask-06454
+ // TODO:
+
+ // VUID-vkCmdBeginRenderPass2-framebuffer-02533
+ // For any attachment in framebuffer that is used by renderPass and is bound to memory locations that are also bound to another attachment used by renderPass, and if at least one of those uses causes either
+ // attachment to be written to, both attachments must have had the VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT set
+
+ // TODO: sync check
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn begin_render_pass_unchecked(
+ &mut self,
+ render_pass_begin_info: RenderPassBeginInfo,
+ contents: SubpassContents,
+ ) -> &mut Self {
+ let RenderPassBeginInfo {
+ render_pass,
+ framebuffer,
+ render_area_offset,
+ render_area_extent,
+ clear_values,
+ _ne: _,
+ } = render_pass_begin_info;
+
+ let clear_values_vk: SmallVec<[_; 4]> = clear_values
+ .iter()
+ .copied()
+ .map(|clear_value| clear_value.map(Into::into).unwrap_or_default())
+ .collect();
+
+ let render_pass_begin_info = ash::vk::RenderPassBeginInfo {
+ render_pass: render_pass.handle(),
+ framebuffer: framebuffer.handle(),
+ render_area: ash::vk::Rect2D {
+ offset: ash::vk::Offset2D {
+ x: render_area_offset[0] as i32,
+ y: render_area_offset[1] as i32,
+ },
+ extent: ash::vk::Extent2D {
+ width: render_area_extent[0],
+ height: render_area_extent[1],
+ },
+ },
+ clear_value_count: clear_values_vk.len() as u32,
+ p_clear_values: clear_values_vk.as_ptr(),
+ ..Default::default()
+ };
+
+ let subpass_begin_info = ash::vk::SubpassBeginInfo {
+ contents: contents.into(),
+ ..Default::default()
+ };
+
+ let fns = self.device().fns();
+
+ if self.device().api_version() >= Version::V1_2
+ || self.device().enabled_extensions().khr_create_renderpass2
+ {
+ if self.device().api_version() >= Version::V1_2 {
+ (fns.v1_2.cmd_begin_render_pass2)(
+ self.handle(),
+ &render_pass_begin_info,
+ &subpass_begin_info,
+ );
+ } else {
+ (fns.khr_create_renderpass2.cmd_begin_render_pass2_khr)(
+ self.handle(),
+ &render_pass_begin_info,
+ &subpass_begin_info,
+ );
+ }
+ } else {
+ debug_assert!(subpass_begin_info.p_next.is_null());
+
+ (fns.v1_0.cmd_begin_render_pass)(
+ self.handle(),
+ &render_pass_begin_info,
+ subpass_begin_info.contents,
+ );
+ }
+
+ let command_index = self.next_command_index;
+ let command_name = "begin_render_pass";
+
+ // Advance to first subpass
+ {
+ let subpass = render_pass.clone().first_subpass();
+ self.builder_state.render_pass = Some(RenderPassState {
+ contents,
+ render_area_offset,
+ render_area_extent,
+
+ rendering_info: PipelineRenderingCreateInfo::from_subpass(&subpass),
+ attachments: Some(RenderPassStateAttachments::from_subpass(
+ &subpass,
+ &framebuffer,
+ )),
+
+ render_pass: BeginRenderPassState {
+ subpass,
+ framebuffer: Some(framebuffer.clone()),
+ }
+ .into(),
+ });
+ }
+
+ // Start of first subpass
+ {
+ // TODO: Apply barriers and layout transitions
+
+ let render_pass_state = self.builder_state.render_pass.as_ref().unwrap();
+ record_subpass_attachments_load(
+ &mut self.resources_usage_state,
+ command_index,
+ command_name,
+ render_pass_state,
+ );
+ }
+
+ self.resources.push(Box::new(render_pass));
+ self.resources.push(Box::new(framebuffer));
+
+ self.next_command_index += 1;
+ self
+ }
+
+ /// Advances to the next subpass of the render pass previously begun with `begin_render_pass`.
+ ///
+ /// # Safety
+ ///
+ /// - Appropriate synchronization must be provided for all images
+ /// that are accessed by the command.
+ /// - All images that are accessed by the command must be in the expected image layout.
+ #[inline]
+ pub unsafe fn next_subpass(
+ &mut self,
+ contents: SubpassContents,
+ ) -> Result<&mut Self, RenderPassError> {
+ self.validate_next_subpass(contents)?;
+
+ unsafe { Ok(self.next_subpass_unchecked(contents)) }
+ }
+
+ fn validate_next_subpass(&self, contents: SubpassContents) -> Result<(), RenderPassError> {
+ let device = self.device();
+
+ // VUID-VkSubpassBeginInfo-contents-parameter
+ contents.validate_device(device)?;
+
+ // VUID-vkCmdNextSubpass2-renderpass
+ let render_pass_state = self
+ .builder_state
+ .render_pass
+ .as_ref()
+ .ok_or(RenderPassError::ForbiddenOutsideRenderPass)?;
+
+ let begin_render_pass_state = match &render_pass_state.render_pass {
+ RenderPassStateType::BeginRenderPass(state) => state,
+ RenderPassStateType::BeginRendering(_) => {
+ return Err(RenderPassError::ForbiddenWithBeginRendering)
+ }
+ };
+
+ // VUID-vkCmdNextSubpass2-None-03102
+ if begin_render_pass_state.subpass.is_last_subpass() {
+ return Err(RenderPassError::NoSubpassesRemaining {
+ current_subpass: begin_render_pass_state.subpass.index(),
+ });
+ }
+
+ // VUID?
+ if self
+ .builder_state
+ .queries
+ .values()
+ .any(|state| state.in_subpass)
+ {
+ return Err(RenderPassError::QueryIsActive);
+ }
+
+ // VUID-vkCmdNextSubpass2-commandBuffer-cmdpool
+ debug_assert!(self
+ .queue_family_properties()
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS));
+
+ // VUID-vkCmdNextSubpass2-bufferlevel
+ // Ensured by the type of the impl block
+
+ // TODO: sync check
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn next_subpass_unchecked(&mut self, contents: SubpassContents) -> &mut Self {
+ let subpass_begin_info = ash::vk::SubpassBeginInfo {
+ contents: contents.into(),
+ ..Default::default()
+ };
+
+ let subpass_end_info = ash::vk::SubpassEndInfo::default();
+
+ let fns = self.device().fns();
+
+ if self.device().api_version() >= Version::V1_2
+ || self.device().enabled_extensions().khr_create_renderpass2
+ {
+ if self.device().api_version() >= Version::V1_2 {
+ (fns.v1_2.cmd_next_subpass2)(self.handle(), &subpass_begin_info, &subpass_end_info);
+ } else {
+ (fns.khr_create_renderpass2.cmd_next_subpass2_khr)(
+ self.handle(),
+ &subpass_begin_info,
+ &subpass_end_info,
+ );
+ }
+ } else {
+ (fns.v1_0.cmd_next_subpass)(self.handle(), subpass_begin_info.contents);
+ }
+
+ let command_index = self.next_command_index;
+ let command_name = "next_subpass";
+
+ // End of previous subpass
+ {
+ let render_pass_state = self.builder_state.render_pass.as_ref().unwrap();
+ record_subpass_attachments_resolve(
+ &mut self.resources_usage_state,
+ command_index,
+ command_name,
+ render_pass_state,
+ );
+ record_subpass_attachments_store(
+ &mut self.resources_usage_state,
+ command_index,
+ command_name,
+ render_pass_state,
+ );
+ }
+
+ // Advance to next subpass
+ {
+ let render_pass_state = self.builder_state.render_pass.as_mut().unwrap();
+ let begin_render_pass_state = match &mut render_pass_state.render_pass {
+ RenderPassStateType::BeginRenderPass(x) => x,
+ _ => unreachable!(),
+ };
+
+ begin_render_pass_state.subpass.next_subpass();
+ render_pass_state.contents = contents;
+ render_pass_state.rendering_info =
+ PipelineRenderingCreateInfo::from_subpass(&begin_render_pass_state.subpass);
+ render_pass_state.attachments = Some(RenderPassStateAttachments::from_subpass(
+ &begin_render_pass_state.subpass,
+ begin_render_pass_state.framebuffer.as_ref().unwrap(),
+ ));
+
+ if render_pass_state.rendering_info.view_mask != 0 {
+ // When multiview is enabled, at the beginning of each subpass, all
+ // non-render pass state is undefined.
+ self.builder_state = Default::default();
+ }
+ }
+
+ // Start of next subpass
+ {
+ // TODO: Apply barriers and layout transitions
+
+ let render_pass_state = self.builder_state.render_pass.as_ref().unwrap();
+ record_subpass_attachments_load(
+ &mut self.resources_usage_state,
+ command_index,
+ command_name,
+ render_pass_state,
+ );
+ }
+
+ self.next_command_index += 1;
+ self
+ }
+
+ /// Ends the render pass previously begun with `begin_render_pass`.
+ ///
+ /// This must be called after you went through all the subpasses.
+ ///
+ /// # Safety
+ ///
+ /// - Appropriate synchronization must be provided for all images
+ /// that are accessed by the command.
+ /// - All images that are accessed by the command must be in the expected image layout.
+ #[inline]
+ pub unsafe fn end_render_pass(&mut self) -> Result<&mut Self, RenderPassError> {
+ self.validate_end_render_pass()?;
+
+ unsafe { Ok(self.end_render_pass_unchecked()) }
+ }
+
+ fn validate_end_render_pass(&self) -> Result<(), RenderPassError> {
+ // VUID-vkCmdEndRenderPass2-renderpass
+ let render_pass_state = self
+ .builder_state
+ .render_pass
+ .as_ref()
+ .ok_or(RenderPassError::ForbiddenOutsideRenderPass)?;
+
+ let begin_render_pass_state = match &render_pass_state.render_pass {
+ RenderPassStateType::BeginRenderPass(state) => state,
+ RenderPassStateType::BeginRendering(_) => {
+ return Err(RenderPassError::ForbiddenWithBeginRendering)
+ }
+ };
+
+ // VUID-vkCmdEndRenderPass2-None-03103
+ if !begin_render_pass_state.subpass.is_last_subpass() {
+ return Err(RenderPassError::SubpassesRemaining {
+ current_subpass: begin_render_pass_state.subpass.index(),
+ remaining_subpasses: begin_render_pass_state
+ .subpass
+ .render_pass()
+ .subpasses()
+ .len() as u32
+ - begin_render_pass_state.subpass.index(),
+ });
+ }
+
+ // VUID?
+ if self
+ .builder_state
+ .queries
+ .values()
+ .any(|state| state.in_subpass)
+ {
+ return Err(RenderPassError::QueryIsActive);
+ }
+
+ // VUID-vkCmdEndRenderPass2-commandBuffer-cmdpool
+ debug_assert!(self
+ .queue_family_properties()
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS));
+
+ // VUID-vkCmdEndRenderPass2-bufferlevel
+ // Ensured by the type of the impl block
+
+ // TODO: sync check
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn end_render_pass_unchecked(&mut self) -> &mut Self {
+ let subpass_end_info = ash::vk::SubpassEndInfo::default();
+
+ let fns = self.device().fns();
+
+ if self.device().api_version() >= Version::V1_2
+ || self.device().enabled_extensions().khr_create_renderpass2
+ {
+ if self.device().api_version() >= Version::V1_2 {
+ (fns.v1_2.cmd_end_render_pass2)(self.handle(), &subpass_end_info);
+ } else {
+ (fns.khr_create_renderpass2.cmd_end_render_pass2_khr)(
+ self.handle(),
+ &subpass_end_info,
+ );
+ }
+ } else {
+ (fns.v1_0.cmd_end_render_pass)(self.handle());
+ }
+
+ let command_index = self.next_command_index;
+ let command_name = "end_render_pass";
+
+ // End of last subpass
+ {
+ let render_pass_state = self.builder_state.render_pass.as_ref().unwrap();
+ record_subpass_attachments_resolve(
+ &mut self.resources_usage_state,
+ command_index,
+ command_name,
+ render_pass_state,
+ );
+ record_subpass_attachments_store(
+ &mut self.resources_usage_state,
+ command_index,
+ command_name,
+ render_pass_state,
+ );
+ }
+
+ // TODO: Apply barriers and layout transitions
+
+ self.builder_state.render_pass = None;
+
+ self.next_command_index += 1;
+ self
+ }
+}
+
+impl<L, A> CommandBufferBuilder<L, A>
+where
+ A: CommandBufferAllocator,
+{
+ /// Begins a render pass without a render pass object or framebuffer.
+ ///
+ /// You must call this or `begin_render_pass` before you can record draw commands.
+ ///
+ /// # Safety
+ ///
+ /// - Appropriate synchronization must be provided for all images
+ /// that are accessed by the command.
+ /// - All images that are accessed by the command must be in the expected image layout.
+ #[inline]
+ pub unsafe fn begin_rendering(
+ &mut self,
+ mut rendering_info: RenderingInfo,
+ ) -> Result<&mut Self, RenderPassError> {
+ rendering_info.set_extent_layers()?;
+ self.validate_begin_rendering(&rendering_info)?;
+
+ unsafe { Ok(self.begin_rendering_unchecked(rendering_info)) }
+ }
+
+ fn validate_begin_rendering(
+ &self,
+ rendering_info: &RenderingInfo,
+ ) -> Result<(), RenderPassError> {
+ let device = self.device();
+ let properties = device.physical_device().properties();
+
+ // VUID-vkCmdBeginRendering-dynamicRendering-06446
+ if !device.enabled_features().dynamic_rendering {
+ return Err(RenderPassError::RequirementNotMet {
+ required_for: "`CommandBufferBuilder::begin_rendering`",
+ requires_one_of: RequiresOneOf {
+ features: &["dynamic_rendering"],
+ ..Default::default()
+ },
+ });
+ }
+
+ let queue_family_properties = self.queue_family_properties();
+
+ // VUID-vkCmdBeginRendering-commandBuffer-cmdpool
+ if !queue_family_properties
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS)
+ {
+ return Err(RenderPassError::NotSupportedByQueueFamily);
+ }
+
+ // VUID-vkCmdBeginRendering-renderpass
+ if self.builder_state.render_pass.is_some() {
+ return Err(RenderPassError::ForbiddenInsideRenderPass);
+ }
+
+ let &RenderingInfo {
+ render_area_offset,
+ render_area_extent,
+ layer_count,
+ view_mask,
+ ref color_attachments,
+ ref depth_attachment,
+ ref stencil_attachment,
+ contents,
+ _ne: _,
+ } = rendering_info;
+
+ // VUID-VkRenderingInfo-flags-parameter
+ contents.validate_device(device)?;
+
+ // VUID-vkCmdBeginRendering-commandBuffer-06068
+ if self.inheritance_info.is_some() && contents == SubpassContents::SecondaryCommandBuffers {
+ return Err(RenderPassError::ContentsForbiddenInSecondaryCommandBuffer);
+ }
+
+ // No VUID, but for sanity it makes sense to treat this the same as in framebuffers.
+ if view_mask != 0 && layer_count != 1 {
+ return Err(RenderPassError::MultiviewLayersInvalid);
+ }
+
+ // VUID-VkRenderingInfo-multiview-06127
+ if view_mask != 0 && !device.enabled_features().multiview {
+ return Err(RenderPassError::RequirementNotMet {
+ required_for: "`rendering_info.viewmask` is not `0`",
+ requires_one_of: RequiresOneOf {
+ features: &["multiview"],
+ ..Default::default()
+ },
+ });
+ }
+
+ let view_count = u32::BITS - view_mask.leading_zeros();
+
+ // VUID-VkRenderingInfo-viewMask-06128
+ if view_count > properties.max_multiview_view_count.unwrap_or(0) {
+ return Err(RenderPassError::MaxMultiviewViewCountExceeded {
+ view_count,
+ max: properties.max_multiview_view_count.unwrap_or(0),
+ });
+ }
+
+ let mut samples = None;
+
+ // VUID-VkRenderingInfo-colorAttachmentCount-06106
+ if color_attachments.len() > properties.max_color_attachments as usize {
+ return Err(RenderPassError::MaxColorAttachmentsExceeded {
+ color_attachment_count: color_attachments.len() as u32,
+ max: properties.max_color_attachments,
+ });
+ }
+
+ for (attachment_index, attachment_info) in
+ color_attachments
+ .iter()
+ .enumerate()
+ .filter_map(|(index, attachment_info)| {
+ attachment_info
+ .as_ref()
+ .map(|attachment_info| (index, attachment_info))
+ })
+ {
+ let attachment_index = attachment_index as u32;
+ let RenderingAttachmentInfo {
+ image_view,
+ image_layout,
+ resolve_info,
+ load_op,
+ store_op,
+ clear_value: _,
+ _ne: _,
+ } = attachment_info;
+
+ // VUID-VkRenderingAttachmentInfo-imageLayout-parameter
+ image_layout.validate_device(device)?;
+
+ // VUID-VkRenderingAttachmentInfo-loadOp-parameter
+ load_op.validate_device(device)?;
+
+ // VUID-VkRenderingAttachmentInfo-storeOp-parameter
+ store_op.validate_device(device)?;
+
+ // VUID-VkRenderingInfo-colorAttachmentCount-06087
+ if !image_view.usage().intersects(ImageUsage::COLOR_ATTACHMENT) {
+ return Err(RenderPassError::ColorAttachmentMissingUsage { attachment_index });
+ }
+
+ let image = image_view.image();
+ let image_extent = image.dimensions().width_height_depth();
+
+ for i in 0..2 {
+ // VUID-VkRenderingInfo-pNext-06079
+ // VUID-VkRenderingInfo-pNext-06080
+ if render_area_offset[i] + render_area_extent[i] > image_extent[i] {
+ return Err(RenderPassError::RenderAreaOutOfBounds);
+ }
+ }
+
+ // VUID-VkRenderingInfo-imageView-06070
+ match samples {
+ Some(samples) if samples == image.samples() => (),
+ Some(_) => {
+ return Err(RenderPassError::ColorAttachmentSamplesMismatch {
+ attachment_index,
+ });
+ }
+ None => samples = Some(image.samples()),
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06135
+ // VUID-VkRenderingAttachmentInfo-imageView-06145
+ // VUID-VkRenderingInfo-colorAttachmentCount-06090
+ // VUID-VkRenderingInfo-colorAttachmentCount-06096
+ if matches!(
+ image_layout,
+ ImageLayout::Undefined
+ | ImageLayout::ShaderReadOnlyOptimal
+ | ImageLayout::TransferSrcOptimal
+ | ImageLayout::TransferDstOptimal
+ | ImageLayout::Preinitialized
+ | ImageLayout::PresentSrc
+ | ImageLayout::DepthStencilAttachmentOptimal
+ | ImageLayout::DepthStencilReadOnlyOptimal
+ | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
+ | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
+ ) {
+ return Err(RenderPassError::ColorAttachmentLayoutInvalid { attachment_index });
+ }
+
+ if let Some(resolve_info) = resolve_info {
+ let &RenderingAttachmentResolveInfo {
+ mode,
+ image_view: ref resolve_image_view,
+ image_layout: resolve_image_layout,
+ } = resolve_info;
+
+ // VUID-VkRenderingAttachmentInfo-resolveImageLayout-parameter
+ resolve_image_layout.validate_device(device)?;
+
+ // VUID-VkRenderingAttachmentInfo-resolveMode-parameter
+ mode.validate_device(device)?;
+
+ let resolve_image = resolve_image_view.image();
+
+ match image_view.format().unwrap().type_color() {
+ Some(
+ NumericType::SFLOAT
+ | NumericType::UFLOAT
+ | NumericType::SNORM
+ | NumericType::UNORM
+ | NumericType::SSCALED
+ | NumericType::USCALED
+ | NumericType::SRGB,
+ ) => {
+ // VUID-VkRenderingAttachmentInfo-imageView-06129
+ if mode != ResolveMode::Average {
+ return Err(RenderPassError::ColorAttachmentResolveModeNotSupported {
+ attachment_index,
+ });
+ }
+ }
+ Some(NumericType::SINT | NumericType::UINT) => {
+ // VUID-VkRenderingAttachmentInfo-imageView-06130
+ if mode != ResolveMode::SampleZero {
+ return Err(RenderPassError::ColorAttachmentResolveModeNotSupported {
+ attachment_index,
+ });
+ }
+ }
+ None => (),
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06132
+ if image.samples() == SampleCount::Sample1 {
+ return Err(RenderPassError::ColorAttachmentWithResolveNotMultisampled {
+ attachment_index,
+ });
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06133
+ if resolve_image.samples() != SampleCount::Sample1 {
+ return Err(RenderPassError::ColorAttachmentResolveMultisampled {
+ attachment_index,
+ });
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06134
+ if image_view.format() != resolve_image_view.format() {
+ return Err(RenderPassError::ColorAttachmentResolveFormatMismatch {
+ attachment_index,
+ });
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06136
+ // VUID-VkRenderingAttachmentInfo-imageView-06146
+ // VUID-VkRenderingInfo-colorAttachmentCount-06091
+ // VUID-VkRenderingInfo-colorAttachmentCount-06097
+ if matches!(
+ resolve_image_layout,
+ ImageLayout::Undefined
+ | ImageLayout::ShaderReadOnlyOptimal
+ | ImageLayout::TransferSrcOptimal
+ | ImageLayout::TransferDstOptimal
+ | ImageLayout::Preinitialized
+ | ImageLayout::PresentSrc
+ | ImageLayout::DepthStencilAttachmentOptimal
+ | ImageLayout::DepthStencilReadOnlyOptimal
+ | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
+ | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
+ ) {
+ return Err(RenderPassError::ColorAttachmentResolveLayoutInvalid {
+ attachment_index,
+ });
+ }
+ }
+ }
+
+ if let Some(attachment_info) = depth_attachment {
+ let RenderingAttachmentInfo {
+ image_view,
+ image_layout,
+ resolve_info,
+ load_op,
+ store_op,
+ clear_value: _,
+ _ne: _,
+ } = attachment_info;
+
+ // VUID-VkRenderingAttachmentInfo-imageLayout-parameter
+ image_layout.validate_device(device)?;
+
+ // VUID-VkRenderingAttachmentInfo-loadOp-parameter
+ load_op.validate_device(device)?;
+
+ // VUID-VkRenderingAttachmentInfo-storeOp-parameter
+ store_op.validate_device(device)?;
+
+ let image_aspects = image_view.format().unwrap().aspects();
+
+ // VUID-VkRenderingInfo-pDepthAttachment-06547
+ if !image_aspects.intersects(ImageAspects::DEPTH) {
+ return Err(RenderPassError::DepthAttachmentFormatUsageNotSupported);
+ }
+
+ // VUID-VkRenderingInfo-pDepthAttachment-06088
+ if !image_view
+ .usage()
+ .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
+ {
+ return Err(RenderPassError::DepthAttachmentMissingUsage);
+ }
+
+ let image = image_view.image();
+ let image_extent = image.dimensions().width_height_depth();
+
+ for i in 0..2 {
+ // VUID-VkRenderingInfo-pNext-06079
+ // VUID-VkRenderingInfo-pNext-06080
+ if render_area_offset[i] + render_area_extent[i] > image_extent[i] {
+ return Err(RenderPassError::RenderAreaOutOfBounds);
+ }
+ }
+
+ // VUID-VkRenderingInfo-imageView-06070
+ match samples {
+ Some(samples) if samples == image.samples() => (),
+ Some(_) => {
+ return Err(RenderPassError::DepthAttachmentSamplesMismatch);
+ }
+ None => samples = Some(image.samples()),
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06135
+ // VUID-VkRenderingAttachmentInfo-imageView-06145
+ // VUID-VkRenderingInfo-pDepthAttachment-06092
+ if matches!(
+ image_layout,
+ ImageLayout::Undefined
+ | ImageLayout::ShaderReadOnlyOptimal
+ | ImageLayout::TransferSrcOptimal
+ | ImageLayout::TransferDstOptimal
+ | ImageLayout::Preinitialized
+ | ImageLayout::PresentSrc
+ | ImageLayout::ColorAttachmentOptimal
+ ) {
+ return Err(RenderPassError::DepthAttachmentLayoutInvalid);
+ }
+
+ if let Some(resolve_info) = resolve_info {
+ let &RenderingAttachmentResolveInfo {
+ mode,
+ image_view: ref resolve_image_view,
+ image_layout: resolve_image_layout,
+ } = resolve_info;
+
+ // VUID-VkRenderingAttachmentInfo-resolveImageLayout-parameter
+ resolve_image_layout.validate_device(device)?;
+
+ // VUID-VkRenderingAttachmentInfo-resolveMode-parameter
+ mode.validate_device(device)?;
+
+ // VUID-VkRenderingInfo-pDepthAttachment-06102
+ if !properties
+ .supported_depth_resolve_modes
+ .map_or(false, |modes| modes.contains_enum(mode))
+ {
+ return Err(RenderPassError::DepthAttachmentResolveModeNotSupported);
+ }
+
+ let resolve_image = resolve_image_view.image();
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06132
+ if image.samples() == SampleCount::Sample1 {
+ return Err(RenderPassError::DepthAttachmentWithResolveNotMultisampled);
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06133
+ if resolve_image.samples() != SampleCount::Sample1 {
+ return Err(RenderPassError::DepthAttachmentResolveMultisampled);
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06134
+ if image_view.format() != resolve_image_view.format() {
+ return Err(RenderPassError::DepthAttachmentResolveFormatMismatch);
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06136
+ // VUID-VkRenderingAttachmentInfo-imageView-06146
+ // VUID-VkRenderingInfo-pDepthAttachment-06093
+ // VUID-VkRenderingInfo-pDepthAttachment-06098
+ if matches!(
+ resolve_image_layout,
+ ImageLayout::Undefined
+ | ImageLayout::DepthStencilReadOnlyOptimal
+ | ImageLayout::ShaderReadOnlyOptimal
+ | ImageLayout::TransferSrcOptimal
+ | ImageLayout::TransferDstOptimal
+ | ImageLayout::Preinitialized
+ | ImageLayout::PresentSrc
+ | ImageLayout::ColorAttachmentOptimal
+ | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
+ ) {
+ return Err(RenderPassError::DepthAttachmentResolveLayoutInvalid);
+ }
+ }
+ }
+
+ if let Some(attachment_info) = stencil_attachment {
+ let RenderingAttachmentInfo {
+ image_view,
+ image_layout,
+ resolve_info,
+ load_op,
+ store_op,
+ clear_value: _,
+ _ne: _,
+ } = attachment_info;
+
+ // VUID-VkRenderingAttachmentInfo-imageLayout-parameter
+ image_layout.validate_device(device)?;
+
+ // VUID-VkRenderingAttachmentInfo-loadOp-parameter
+ load_op.validate_device(device)?;
+
+ // VUID-VkRenderingAttachmentInfo-storeOp-parameter
+ store_op.validate_device(device)?;
+
+ let image_aspects = image_view.format().unwrap().aspects();
+
+ // VUID-VkRenderingInfo-pStencilAttachment-06548
+ if !image_aspects.intersects(ImageAspects::STENCIL) {
+ return Err(RenderPassError::StencilAttachmentFormatUsageNotSupported);
+ }
+
+ // VUID-VkRenderingInfo-pStencilAttachment-06089
+ if !image_view
+ .usage()
+ .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
+ {
+ return Err(RenderPassError::StencilAttachmentMissingUsage);
+ }
+
+ let image = image_view.image();
+ let image_extent = image.dimensions().width_height_depth();
+
+ for i in 0..2 {
+ // VUID-VkRenderingInfo-pNext-06079
+ // VUID-VkRenderingInfo-pNext-06080
+ if render_area_offset[i] + render_area_extent[i] > image_extent[i] {
+ return Err(RenderPassError::RenderAreaOutOfBounds);
+ }
+ }
+
+ // VUID-VkRenderingInfo-imageView-06070
+ match samples {
+ Some(samples) if samples == image.samples() => (),
+ Some(_) => {
+ return Err(RenderPassError::StencilAttachmentSamplesMismatch);
+ }
+ None => (),
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06135
+ // VUID-VkRenderingAttachmentInfo-imageView-06145
+ // VUID-VkRenderingInfo-pStencilAttachment-06094
+ if matches!(
+ image_layout,
+ ImageLayout::Undefined
+ | ImageLayout::ShaderReadOnlyOptimal
+ | ImageLayout::TransferSrcOptimal
+ | ImageLayout::TransferDstOptimal
+ | ImageLayout::Preinitialized
+ | ImageLayout::PresentSrc
+ | ImageLayout::ColorAttachmentOptimal
+ ) {
+ return Err(RenderPassError::StencilAttachmentLayoutInvalid);
+ }
+
+ if let Some(resolve_info) = resolve_info {
+ let &RenderingAttachmentResolveInfo {
+ mode,
+ image_view: ref resolve_image_view,
+ image_layout: resolve_image_layout,
+ } = resolve_info;
+
+ // VUID-VkRenderingAttachmentInfo-resolveImageLayout-parameter
+ resolve_image_layout.validate_device(device)?;
+
+ // VUID-VkRenderingAttachmentInfo-resolveMode-parameter
+ mode.validate_device(device)?;
+
+ // VUID-VkRenderingInfo-pStencilAttachment-06103
+ if !properties
+ .supported_stencil_resolve_modes
+ .map_or(false, |modes| modes.contains_enum(mode))
+ {
+ return Err(RenderPassError::StencilAttachmentResolveModeNotSupported);
+ }
+
+ let resolve_image = resolve_image_view.image();
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06132
+ if image.samples() == SampleCount::Sample1 {
+ return Err(RenderPassError::StencilAttachmentWithResolveNotMultisampled);
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06133
+ if resolve_image.samples() != SampleCount::Sample1 {
+ return Err(RenderPassError::StencilAttachmentResolveMultisampled);
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06134
+ if image_view.format() != resolve_image_view.format() {
+ return Err(RenderPassError::StencilAttachmentResolveFormatMismatch);
+ }
+
+ // VUID-VkRenderingAttachmentInfo-imageView-06136
+ // VUID-VkRenderingAttachmentInfo-imageView-06146
+ // VUID-VkRenderingInfo-pStencilAttachment-06095
+ // VUID-VkRenderingInfo-pStencilAttachment-06099
+ if matches!(
+ resolve_image_layout,
+ ImageLayout::Undefined
+ | ImageLayout::DepthStencilReadOnlyOptimal
+ | ImageLayout::ShaderReadOnlyOptimal
+ | ImageLayout::TransferSrcOptimal
+ | ImageLayout::TransferDstOptimal
+ | ImageLayout::Preinitialized
+ | ImageLayout::PresentSrc
+ | ImageLayout::ColorAttachmentOptimal
+ | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
+ ) {
+ return Err(RenderPassError::StencilAttachmentResolveLayoutInvalid);
+ }
+ }
+ }
+
+ if let (Some(depth_attachment_info), Some(stencil_attachment_info)) =
+ (depth_attachment, stencil_attachment)
+ {
+ // VUID-VkRenderingInfo-pDepthAttachment-06085
+ if &depth_attachment_info.image_view != &stencil_attachment_info.image_view {
+ return Err(RenderPassError::DepthStencilAttachmentImageViewMismatch);
+ }
+
+ match (
+ &depth_attachment_info.resolve_info,
+ &stencil_attachment_info.resolve_info,
+ ) {
+ (None, None) => (),
+ (None, Some(_)) | (Some(_), None) => {
+ // VUID-VkRenderingInfo-pDepthAttachment-06104
+ if !properties.independent_resolve_none.unwrap_or(false) {
+ return Err(
+ RenderPassError::DepthStencilAttachmentResolveModesNotSupported,
+ );
+ }
+ }
+ (Some(depth_resolve_info), Some(stencil_resolve_info)) => {
+ // VUID-VkRenderingInfo-pDepthAttachment-06105
+ if !properties.independent_resolve.unwrap_or(false)
+ && depth_resolve_info.mode != stencil_resolve_info.mode
+ {
+ return Err(
+ RenderPassError::DepthStencilAttachmentResolveModesNotSupported,
+ );
+ }
+
+ // VUID-VkRenderingInfo-pDepthAttachment-06086
+ if &depth_resolve_info.image_view != &stencil_resolve_info.image_view {
+ return Err(
+ RenderPassError::DepthStencilAttachmentResolveImageViewMismatch,
+ );
+ }
+ }
+ }
+ }
+
+ // TODO: sync check
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn begin_rendering_unchecked(&mut self, rendering_info: RenderingInfo) -> &mut Self {
+ {
+ let &RenderingInfo {
+ render_area_offset,
+ render_area_extent,
+ layer_count,
+ view_mask,
+ ref color_attachments,
+ ref depth_attachment,
+ ref stencil_attachment,
+ contents,
+ _ne,
+ } = &rendering_info;
+
+ let map_attachment_info = |attachment_info: &Option<_>| {
+ if let Some(attachment_info) = attachment_info {
+ let &RenderingAttachmentInfo {
+ ref image_view,
+ image_layout,
+ resolve_info: ref resolve,
+ load_op,
+ store_op,
+ clear_value,
+ _ne: _,
+ } = attachment_info;
+
+ let (resolve_mode, resolve_image_view, resolve_image_layout) =
+ if let Some(resolve) = resolve {
+ let &RenderingAttachmentResolveInfo {
+ mode,
+ ref image_view,
+ image_layout,
+ } = resolve;
+
+ (mode.into(), image_view.handle(), image_layout.into())
+ } else {
+ (
+ ash::vk::ResolveModeFlags::NONE,
+ Default::default(),
+ Default::default(),
+ )
+ };
+
+ ash::vk::RenderingAttachmentInfo {
+ image_view: image_view.handle(),
+ image_layout: image_layout.into(),
+ resolve_mode,
+ resolve_image_view,
+ resolve_image_layout,
+ load_op: load_op.into(),
+ store_op: store_op.into(),
+ clear_value: clear_value.map_or_else(Default::default, Into::into),
+ ..Default::default()
+ }
+ } else {
+ ash::vk::RenderingAttachmentInfo {
+ image_view: ash::vk::ImageView::null(),
+ ..Default::default()
+ }
+ }
+ };
+
+ let color_attachments_vk: SmallVec<[_; 2]> =
+ color_attachments.iter().map(map_attachment_info).collect();
+ let depth_attachment_vk = map_attachment_info(depth_attachment);
+ let stencil_attachment_vk = map_attachment_info(stencil_attachment);
+
+ let rendering_info_vk = ash::vk::RenderingInfo {
+ flags: contents.into(),
+ render_area: ash::vk::Rect2D {
+ offset: ash::vk::Offset2D {
+ x: render_area_offset[0] as i32,
+ y: render_area_offset[1] as i32,
+ },
+ extent: ash::vk::Extent2D {
+ width: render_area_extent[0],
+ height: render_area_extent[1],
+ },
+ },
+ layer_count,
+ view_mask,
+ color_attachment_count: color_attachments_vk.len() as u32,
+ p_color_attachments: color_attachments_vk.as_ptr(),
+ p_depth_attachment: &depth_attachment_vk,
+ p_stencil_attachment: &stencil_attachment_vk,
+ ..Default::default()
+ };
+
+ let fns = self.device().fns();
+
+ if self.device().api_version() >= Version::V1_3 {
+ (fns.v1_3.cmd_begin_rendering)(self.handle(), &rendering_info_vk);
+ } else {
+ debug_assert!(self.device().enabled_extensions().khr_dynamic_rendering);
+ (fns.khr_dynamic_rendering.cmd_begin_rendering_khr)(
+ self.handle(),
+ &rendering_info_vk,
+ );
+ }
+
+ self.builder_state.render_pass = Some(RenderPassState {
+ contents,
+ render_area_offset,
+ render_area_extent,
+
+ rendering_info: PipelineRenderingCreateInfo::from_rendering_info(&rendering_info),
+ attachments: Some(RenderPassStateAttachments::from_rendering_info(
+ &rendering_info,
+ )),
+
+ render_pass: BeginRenderingState {
+ pipeline_used: false,
+ }
+ .into(),
+ });
+ }
+
+ let RenderingInfo {
+ color_attachments,
+ depth_attachment,
+ stencil_attachment,
+ ..
+ } = rendering_info;
+
+ for attachment_info in color_attachments.into_iter().flatten() {
+ let RenderingAttachmentInfo {
+ image_view,
+ image_layout: _,
+ resolve_info,
+ load_op: _,
+ store_op: _,
+ clear_value: _,
+ _ne: _,
+ } = attachment_info;
+
+ self.resources.push(Box::new(image_view));
+
+ if let Some(resolve_info) = resolve_info {
+ let RenderingAttachmentResolveInfo {
+ mode: _,
+ image_view,
+ image_layout: _,
+ } = resolve_info;
+
+ self.resources.push(Box::new(image_view));
+ }
+ }
+
+ if let Some(attachment_info) = depth_attachment {
+ let RenderingAttachmentInfo {
+ image_view,
+ image_layout: _,
+ resolve_info,
+ load_op: _,
+ store_op: _,
+ clear_value: _,
+ _ne: _,
+ } = attachment_info;
+
+ self.resources.push(Box::new(image_view));
+
+ if let Some(resolve_info) = resolve_info {
+ let RenderingAttachmentResolveInfo {
+ mode: _,
+ image_view,
+ image_layout: _,
+ } = resolve_info;
+
+ self.resources.push(Box::new(image_view));
+ }
+ }
+
+ if let Some(attachment_info) = stencil_attachment {
+ let RenderingAttachmentInfo {
+ image_view,
+ image_layout: _,
+ resolve_info,
+ load_op: _,
+ store_op: _,
+ clear_value: _,
+ _ne: _,
+ } = attachment_info;
+
+ self.resources.push(Box::new(image_view));
+
+ if let Some(resolve_info) = resolve_info {
+ let RenderingAttachmentResolveInfo {
+ mode: _,
+ image_view,
+ image_layout: _,
+ } = resolve_info;
+
+ self.resources.push(Box::new(image_view));
+ }
+ }
+
+ self.next_command_index += 1;
+ self
+ }
+
+ /// Ends the render pass previously begun with `begin_rendering`.
+ ///
+ /// # Safety
+ ///
+ /// - Appropriate synchronization must be provided for all images
+ /// that are accessed by the command.
+ /// - All images that are accessed by the command must be in the expected image layout.
+ #[inline]
+ pub unsafe fn end_rendering(&mut self) -> Result<&mut Self, RenderPassError> {
+ self.validate_end_rendering()?;
+
+ unsafe { Ok(self.end_rendering_unchecked()) }
+ }
+
+ fn validate_end_rendering(&self) -> Result<(), RenderPassError> {
+ // VUID-vkCmdEndRendering-renderpass
+ let render_pass_state = self
+ .builder_state
+ .render_pass
+ .as_ref()
+ .ok_or(RenderPassError::ForbiddenOutsideRenderPass)?;
+
+ // VUID?
+ if self.inheritance_info.is_some() {
+ return Err(RenderPassError::ForbiddenWithInheritedRenderPass);
+ }
+
+ // VUID-vkCmdEndRendering-None-06161
+ // VUID-vkCmdEndRendering-commandBuffer-06162
+ match &render_pass_state.render_pass {
+ RenderPassStateType::BeginRenderPass(_) => {
+ return Err(RenderPassError::ForbiddenWithBeginRenderPass)
+ }
+ RenderPassStateType::BeginRendering(_) => (),
+ }
+
+ // VUID-vkCmdEndRendering-commandBuffer-cmdpool
+ debug_assert!(self
+ .queue_family_properties()
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS));
+
+ // TODO: sync check
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn end_rendering_unchecked(&mut self) -> &mut Self {
+ let fns = self.device().fns();
+
+ if self.device().api_version() >= Version::V1_3 {
+ (fns.v1_3.cmd_end_rendering)(self.handle());
+ } else {
+ debug_assert!(self.device().enabled_extensions().khr_dynamic_rendering);
+ (fns.khr_dynamic_rendering.cmd_end_rendering_khr)(self.handle());
+ }
+
+ let command_index = self.next_command_index;
+ let command_name = "end_rendering";
+ let render_pass_state = self.builder_state.render_pass.as_ref().unwrap();
+
+ record_subpass_attachments_resolve(
+ &mut self.resources_usage_state,
+ command_index,
+ command_name,
+ render_pass_state,
+ );
+ record_subpass_attachments_store(
+ &mut self.resources_usage_state,
+ command_index,
+ command_name,
+ render_pass_state,
+ );
+
+ self.builder_state.render_pass = None;
+
+ self.next_command_index += 1;
+ self
+ }
+
+ /// Clears specific regions of specific attachments of the framebuffer.
+ ///
+ /// `attachments` specify the types of attachments and their clear values.
+ /// `rects` specify the regions to clear.
+ ///
+ /// A graphics pipeline must have been bound using [`bind_pipeline_graphics`].
+ /// And the command must be inside render pass.
+ ///
+ /// If the render pass instance this is recorded in uses multiview,
+ /// then `ClearRect.base_array_layer` must be zero and `ClearRect.layer_count` must be one.
+ ///
+ /// The rectangle area must be inside the render area ranges.
+ ///
+ /// # Safety
+ ///
+ /// - Appropriate synchronization must be provided for all images
+ /// that are accessed by the command.
+ /// - All images that are accessed by the command must be in the expected image layout.
+ ///
+ /// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
+ #[inline]
+ pub unsafe fn clear_attachments(
+ &mut self,
+ attachments: impl IntoIterator<Item = ClearAttachment>,
+ rects: impl IntoIterator<Item = ClearRect>,
+ ) -> Result<&mut Self, RenderPassError> {
+ let attachments: SmallVec<[ClearAttachment; 3]> = attachments.into_iter().collect();
+ let rects: SmallVec<[ClearRect; 4]> = rects.into_iter().collect();
+
+ self.validate_clear_attachments(&attachments, &rects)?;
+
+ unsafe { Ok(self.clear_attachments_unchecked(attachments, rects)) }
+ }
+
+ fn validate_clear_attachments(
+ &self,
+ attachments: &[ClearAttachment],
+ rects: &[ClearRect],
+ ) -> Result<(), RenderPassError> {
+ // VUID-vkCmdClearAttachments-renderpass
+ let render_pass_state = self
+ .builder_state
+ .render_pass
+ .as_ref()
+ .ok_or(RenderPassError::ForbiddenOutsideRenderPass)?;
+
+ if render_pass_state.contents != SubpassContents::Inline {
+ return Err(RenderPassError::ForbiddenWithSubpassContents {
+ contents: render_pass_state.contents,
+ });
+ }
+
+ //let subpass_desc = begin_render_pass_state.subpass.subpass_desc();
+ //let render_pass = begin_render_pass_state.subpass.render_pass();
+ let is_multiview = render_pass_state.rendering_info.view_mask != 0;
+ let mut layer_count = u32::MAX;
+
+ for &clear_attachment in attachments {
+ match clear_attachment {
+ ClearAttachment::Color {
+ color_attachment,
+ clear_value,
+ } => {
+ let attachment_format = *render_pass_state
+ .rendering_info
+ .color_attachment_formats
+ .get(color_attachment as usize)
+ .ok_or(RenderPassError::ColorAttachmentIndexOutOfRange {
+ color_attachment_index: color_attachment,
+ num_color_attachments: render_pass_state
+ .rendering_info
+ .color_attachment_formats
+ .len() as u32,
+ })?;
+
+ // VUID-vkCmdClearAttachments-aspectMask-02501
+ if !attachment_format.map_or(false, |format| {
+ matches!(
+ (clear_value, format.type_color().unwrap()),
+ (
+ ClearColorValue::Float(_),
+ NumericType::SFLOAT
+ | NumericType::UFLOAT
+ | NumericType::SNORM
+ | NumericType::UNORM
+ | NumericType::SSCALED
+ | NumericType::USCALED
+ | NumericType::SRGB
+ ) | (ClearColorValue::Int(_), NumericType::SINT)
+ | (ClearColorValue::Uint(_), NumericType::UINT)
+ )
+ }) {
+ return Err(RenderPassError::ClearAttachmentNotCompatible {
+ clear_attachment,
+ attachment_format,
+ });
+ }
+
+ let image_view = render_pass_state
+ .attachments
+ .as_ref()
+ .and_then(|attachments| {
+ attachments.color_attachments[color_attachment as usize].as_ref()
+ })
+ .map(|attachment_info| &attachment_info.image_view);
+
+ // We only know the layer count if we have a known attachment image.
+ if let Some(image_view) = image_view {
+ let array_layers = &image_view.subresource_range().array_layers;
+ layer_count = min(layer_count, array_layers.end - array_layers.start);
+ }
+ }
+ ClearAttachment::Depth(_)
+ | ClearAttachment::Stencil(_)
+ | ClearAttachment::DepthStencil(_) => {
+ let depth_format = render_pass_state.rendering_info.depth_attachment_format;
+ let stencil_format = render_pass_state.rendering_info.stencil_attachment_format;
+
+ // VUID-vkCmdClearAttachments-aspectMask-02502
+ if matches!(
+ clear_attachment,
+ ClearAttachment::Depth(_) | ClearAttachment::DepthStencil(_)
+ ) && !depth_format.map_or(false, |format| {
+ format.aspects().intersects(ImageAspects::DEPTH)
+ }) {
+ return Err(RenderPassError::ClearAttachmentNotCompatible {
+ clear_attachment,
+ attachment_format: None,
+ });
+ }
+
+ // VUID-vkCmdClearAttachments-aspectMask-02503
+ if matches!(
+ clear_attachment,
+ ClearAttachment::Stencil(_) | ClearAttachment::DepthStencil(_)
+ ) && !stencil_format.map_or(false, |format| {
+ format.aspects().intersects(ImageAspects::STENCIL)
+ }) {
+ return Err(RenderPassError::ClearAttachmentNotCompatible {
+ clear_attachment,
+ attachment_format: None,
+ });
+ }
+
+ let image_view = render_pass_state
+ .attachments
+ .as_ref()
+ .and_then(|attachments| attachments.depth_attachment.as_ref())
+ .map(|attachment_info| &attachment_info.image_view);
+
+ // We only know the layer count if we have a known attachment image.
+ if let Some(image_view) = image_view {
+ let array_layers = &image_view.subresource_range().array_layers;
+ layer_count = min(layer_count, array_layers.end - array_layers.start);
+ }
+ }
+ }
+ }
+
+ for (rect_index, rect) in rects.iter().enumerate() {
+ for i in 0..2 {
+ // VUID-vkCmdClearAttachments-rect-02682
+ // VUID-vkCmdClearAttachments-rect-02683
+ if rect.extent[i] == 0 {
+ return Err(RenderPassError::RectExtentZero { rect_index });
+ }
+
+ // VUID-vkCmdClearAttachments-pRects-00016
+ // TODO: This check will always pass in secondary command buffers because of how
+ // it's set in `with_level`.
+ // It needs to be checked during `execute_commands` instead.
+ if rect.offset[i] < render_pass_state.render_area_offset[i]
+ || rect.offset[i] + rect.extent[i]
+ > render_pass_state.render_area_offset[i]
+ + render_pass_state.render_area_extent[i]
+ {
+ return Err(RenderPassError::RectOutOfBounds { rect_index });
+ }
+ }
+
+ // VUID-vkCmdClearAttachments-layerCount-01934
+ if rect.array_layers.is_empty() {
+ return Err(RenderPassError::RectArrayLayersEmpty { rect_index });
+ }
+
+ // VUID-vkCmdClearAttachments-pRects-00017
+ if rect.array_layers.end > layer_count {
+ return Err(RenderPassError::RectArrayLayersOutOfBounds { rect_index });
+ }
+
+ // VUID-vkCmdClearAttachments-baseArrayLayer-00018
+ if is_multiview && rect.array_layers != (0..1) {
+ return Err(RenderPassError::MultiviewRectArrayLayersInvalid { rect_index });
+ }
+ }
+
+ // VUID-vkCmdClearAttachments-commandBuffer-cmdpool
+ debug_assert!(self
+ .queue_family_properties()
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS));
+
+ // TODO: sync check
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn clear_attachments_unchecked(
+ &mut self,
+ attachments: impl IntoIterator<Item = ClearAttachment>,
+ rects: impl IntoIterator<Item = ClearRect>,
+ ) -> &mut Self {
+ let attachments_vk: SmallVec<[_; 3]> = attachments.into_iter().map(|v| v.into()).collect();
+ let rects_vk: SmallVec<[_; 4]> = rects
+ .into_iter()
+ .map(|rect| ash::vk::ClearRect {
+ rect: ash::vk::Rect2D {
+ offset: ash::vk::Offset2D {
+ x: rect.offset[0] as i32,
+ y: rect.offset[1] as i32,
+ },
+ extent: ash::vk::Extent2D {
+ width: rect.extent[0],
+ height: rect.extent[1],
+ },
+ },
+ base_array_layer: rect.array_layers.start,
+ layer_count: rect.array_layers.end - rect.array_layers.start,
+ })
+ .collect();
+
+ if attachments_vk.is_empty() || rects_vk.is_empty() {
+ return self;
+ }
+
+ let fns = self.device().fns();
+ (fns.v1_0.cmd_clear_attachments)(
+ self.handle(),
+ attachments_vk.len() as u32,
+ attachments_vk.as_ptr(),
+ rects_vk.len() as u32,
+ rects_vk.as_ptr(),
+ );
+
+ // TODO: sync state update
+
+ self.next_command_index += 1;
+ self
+ }
+}
+
+fn record_subpass_attachments_resolve(
+ resources_usage_state: &mut ResourcesState,
+ command_index: usize,
+ command_name: &'static str,
+ render_pass_state: &RenderPassState,
+) {
+ let attachments = render_pass_state.attachments.as_ref().unwrap();
+
+ let record_attachment = |resources_usage_state: &mut ResourcesState,
+ attachment_info,
+ aspects_override,
+ resource_in_command,
+ resolve_resource_in_command| {
+ let &RenderPassStateAttachmentInfo {
+ ref image_view,
+ image_layout,
+ ref resolve_info,
+ ..
+ } = attachment_info;
+
+ let image = image_view.image();
+ let image_inner = image.inner();
+ let mut subresource_range = image_view.subresource_range().clone();
+ subresource_range.array_layers.start += image_inner.first_layer;
+ subresource_range.array_layers.end += image_inner.first_layer;
+ subresource_range.mip_levels.start += image_inner.first_mipmap_level;
+ subresource_range.mip_levels.end += image_inner.first_mipmap_level;
+
+ if let Some(aspects) = aspects_override {
+ subresource_range.aspects = aspects;
+ }
+
+ let use_ref = ResourceUseRef {
+ command_index,
+ command_name,
+ resource_in_command,
+ secondary_use_ref: None,
+ };
+
+ if let Some(resolve_info) = resolve_info {
+ let &RenderPassStateAttachmentResolveInfo {
+ image_view: ref resolve_image_view,
+ image_layout: resolve_image_layout,
+ ..
+ } = resolve_info;
+
+ let resolve_image = resolve_image_view.image();
+ let resolve_image_inner = resolve_image.inner();
+ let mut resolve_subresource_range = resolve_image_view.subresource_range().clone();
+ resolve_subresource_range.array_layers.start += resolve_image_inner.first_layer;
+ resolve_subresource_range.array_layers.end += resolve_image_inner.first_layer;
+ resolve_subresource_range.mip_levels.start += resolve_image_inner.first_mipmap_level;
+ resolve_subresource_range.mip_levels.end += resolve_image_inner.first_mipmap_level;
+
+ if let Some(aspects) = aspects_override {
+ resolve_subresource_range.aspects = aspects;
+ }
+
+ let resolve_use_ref = ResourceUseRef {
+ command_index,
+ command_name,
+ resource_in_command: resolve_resource_in_command,
+ secondary_use_ref: None,
+ };
+
+ // The resolve operation uses the stages/access for color attachments,
+ // even for depth/stencil attachments.
+ resources_usage_state.record_image_access(
+ &use_ref,
+ image_inner.image,
+ subresource_range,
+ PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentRead,
+ image_layout,
+ );
+ resources_usage_state.record_image_access(
+ &resolve_use_ref,
+ resolve_image_inner.image,
+ resolve_subresource_range,
+ PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentWrite,
+ resolve_image_layout,
+ );
+ }
+ };
+
+ if let Some(attachment_info) = attachments.depth_attachment.as_ref() {
+ record_attachment(
+ resources_usage_state,
+ attachment_info,
+ Some(ImageAspects::DEPTH),
+ ResourceInCommand::DepthStencilAttachment,
+ ResourceInCommand::DepthStencilResolveAttachment,
+ );
+ }
+
+ if let Some(attachment_info) = attachments.stencil_attachment.as_ref() {
+ record_attachment(
+ resources_usage_state,
+ attachment_info,
+ Some(ImageAspects::STENCIL),
+ ResourceInCommand::DepthStencilAttachment,
+ ResourceInCommand::DepthStencilResolveAttachment,
+ );
+ }
+
+ for (index, attachment_info) in (attachments.color_attachments.iter().enumerate())
+ .filter_map(|(i, a)| a.as_ref().map(|a| (i as u32, a)))
+ {
+ record_attachment(
+ resources_usage_state,
+ attachment_info,
+ None,
+ ResourceInCommand::ColorAttachment { index },
+ ResourceInCommand::ColorResolveAttachment { index },
+ );
+ }
+}
+
+fn record_subpass_attachments_store(
+ resources_usage_state: &mut ResourcesState,
+ command_index: usize,
+ command_name: &'static str,
+ render_pass_state: &RenderPassState,
+) {
+ let attachments = render_pass_state.attachments.as_ref().unwrap();
+
+ let record_attachment = |resources_usage_state: &mut ResourcesState,
+ attachment_info,
+ aspects_override,
+ resource_in_command,
+ resolve_resource_in_command| {
+ let &RenderPassStateAttachmentInfo {
+ ref image_view,
+ image_layout,
+ store_access,
+ ref resolve_info,
+ ..
+ } = attachment_info;
+
+ if let Some(access) = store_access {
+ let image = image_view.image();
+ let image_inner = image.inner();
+ let mut subresource_range = image_view.subresource_range().clone();
+ subresource_range.array_layers.start += image_inner.first_layer;
+ subresource_range.array_layers.end += image_inner.first_layer;
+ subresource_range.mip_levels.start += image_inner.first_mipmap_level;
+ subresource_range.mip_levels.end += image_inner.first_mipmap_level;
+
+ if let Some(aspects) = aspects_override {
+ subresource_range.aspects = aspects;
+ }
+
+ let use_ref = ResourceUseRef {
+ command_index,
+ command_name,
+ resource_in_command,
+ secondary_use_ref: None,
+ };
+
+ resources_usage_state.record_image_access(
+ &use_ref,
+ image_inner.image,
+ subresource_range,
+ access,
+ image_layout,
+ );
+ }
+
+ if let Some(resolve_info) = resolve_info {
+ let &RenderPassStateAttachmentResolveInfo {
+ ref image_view,
+ image_layout,
+ store_access,
+ ..
+ } = resolve_info;
+
+ if let Some(access) = store_access {
+ let image = image_view.image();
+ let image_inner = image.inner();
+ let mut subresource_range = image_view.subresource_range().clone();
+ subresource_range.array_layers.start += image_inner.first_layer;
+ subresource_range.array_layers.end += image_inner.first_layer;
+ subresource_range.mip_levels.start += image_inner.first_mipmap_level;
+ subresource_range.mip_levels.end += image_inner.first_mipmap_level;
+
+ if let Some(aspects) = aspects_override {
+ subresource_range.aspects = aspects;
+ }
+
+ let use_ref = ResourceUseRef {
+ command_index,
+ command_name,
+ resource_in_command: resolve_resource_in_command,
+ secondary_use_ref: None,
+ };
+
+ resources_usage_state.record_image_access(
+ &use_ref,
+ image_inner.image,
+ subresource_range,
+ access,
+ image_layout,
+ );
+ }
+ }
+ };
+
+ if let Some(attachment_info) = attachments.depth_attachment.as_ref() {
+ record_attachment(
+ resources_usage_state,
+ attachment_info,
+ Some(ImageAspects::DEPTH),
+ ResourceInCommand::DepthStencilAttachment,
+ ResourceInCommand::DepthStencilResolveAttachment,
+ );
+ }
+
+ if let Some(attachment_info) = attachments.stencil_attachment.as_ref() {
+ record_attachment(
+ resources_usage_state,
+ attachment_info,
+ Some(ImageAspects::STENCIL),
+ ResourceInCommand::DepthStencilAttachment,
+ ResourceInCommand::DepthStencilResolveAttachment,
+ );
+ }
+
+ for (index, attachment_info) in (attachments.color_attachments.iter().enumerate())
+ .filter_map(|(i, a)| a.as_ref().map(|a| (i as u32, a)))
+ {
+ record_attachment(
+ resources_usage_state,
+ attachment_info,
+ None,
+ ResourceInCommand::ColorAttachment { index },
+ ResourceInCommand::ColorResolveAttachment { index },
+ );
+ }
+}
+
+fn record_subpass_attachments_load(
+ resources_usage_state: &mut ResourcesState,
+ command_index: usize,
+ command_name: &'static str,
+ render_pass_state: &RenderPassState,
+) {
+ let attachments = render_pass_state.attachments.as_ref().unwrap();
+
+ let record_attachment = |resources_usage_state: &mut ResourcesState,
+ attachment_info,
+ aspects_override,
+ resource_in_command,
+ resolve_resource_in_command| {
+ let &RenderPassStateAttachmentInfo {
+ ref image_view,
+ image_layout,
+ load_access,
+ ref resolve_info,
+ ..
+ } = attachment_info;
+
+ if let Some(access) = load_access {
+ let image = image_view.image();
+ let image_inner = image.inner();
+ let mut subresource_range = image_view.subresource_range().clone();
+ subresource_range.array_layers.start += image_inner.first_layer;
+ subresource_range.array_layers.end += image_inner.first_layer;
+ subresource_range.mip_levels.start += image_inner.first_mipmap_level;
+ subresource_range.mip_levels.end += image_inner.first_mipmap_level;
+
+ if let Some(aspects) = aspects_override {
+ subresource_range.aspects = aspects;
+ }
+
+ let use_ref = ResourceUseRef {
+ command_index,
+ command_name,
+ resource_in_command,
+ secondary_use_ref: None,
+ };
+
+ resources_usage_state.record_image_access(
+ &use_ref,
+ image_inner.image,
+ subresource_range,
+ access,
+ image_layout,
+ );
+ }
+
+ if let Some(resolve_info) = resolve_info {
+ let &RenderPassStateAttachmentResolveInfo {
+ ref image_view,
+ image_layout,
+ load_access,
+ ..
+ } = resolve_info;
+
+ if let Some(access) = load_access {
+ let image = image_view.image();
+ let image_inner = image.inner();
+ let mut subresource_range = image_view.subresource_range().clone();
+ subresource_range.array_layers.start += image_inner.first_layer;
+ subresource_range.array_layers.end += image_inner.first_layer;
+ subresource_range.mip_levels.start += image_inner.first_mipmap_level;
+ subresource_range.mip_levels.end += image_inner.first_mipmap_level;
+
+ if let Some(aspects) = aspects_override {
+ subresource_range.aspects = aspects;
+ }
+
+ let use_ref = ResourceUseRef {
+ command_index,
+ command_name,
+ resource_in_command: resolve_resource_in_command,
+ secondary_use_ref: None,
+ };
+
+ resources_usage_state.record_image_access(
+ &use_ref,
+ image_inner.image,
+ subresource_range,
+ access,
+ image_layout,
+ );
+ }
+ }
+ };
+
+ if let Some(attachment_info) = attachments.depth_attachment.as_ref() {
+ record_attachment(
+ resources_usage_state,
+ attachment_info,
+ Some(ImageAspects::DEPTH),
+ ResourceInCommand::DepthStencilAttachment,
+ ResourceInCommand::DepthStencilResolveAttachment,
+ );
+ }
+
+ if let Some(attachment_info) = attachments.stencil_attachment.as_ref() {
+ record_attachment(
+ resources_usage_state,
+ attachment_info,
+ Some(ImageAspects::STENCIL),
+ ResourceInCommand::DepthStencilAttachment,
+ ResourceInCommand::DepthStencilResolveAttachment,
+ );
+ }
+
+ for (index, attachment_info) in (attachments.color_attachments.iter().enumerate())
+ .filter_map(|(i, a)| a.as_ref().map(|a| (i as u32, a)))
+ {
+ record_attachment(
+ resources_usage_state,
+ attachment_info,
+ None,
+ ResourceInCommand::ColorAttachment { index },
+ ResourceInCommand::ColorResolveAttachment { index },
+ );
+ }
+}