// Copyright (c) 2022 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // 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 CommandBufferBuilder, 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 CommandBufferBuilder 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, rects: impl IntoIterator, ) -> 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, rects: impl IntoIterator, ) -> &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 }, ); } }