// 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::{ClearColorImageInfo, ClearDepthStencilImageInfo, ClearError, CommandBufferBuilder}; use crate::{ buffer::{BufferContents, BufferUsage, Subbuffer}, command_buffer::{allocator::CommandBufferAllocator, ResourceInCommand, ResourceUseRef}, device::{DeviceOwned, QueueFlags}, format::FormatFeatures, image::{ImageAccess, ImageAspects, ImageLayout, ImageUsage}, sync::PipelineStageAccess, DeviceSize, RequiresOneOf, Version, VulkanObject, }; use smallvec::SmallVec; use std::mem::size_of_val; impl CommandBufferBuilder where A: CommandBufferAllocator, { /// Clears a color image with a specific value. /// /// # 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 clear_color_image( &mut self, clear_info: ClearColorImageInfo, ) -> Result<&mut Self, ClearError> { self.validate_clear_color_image(&clear_info)?; unsafe { Ok(self.clear_color_image_unchecked(clear_info)) } } fn validate_clear_color_image( &self, clear_info: &ClearColorImageInfo, ) -> Result<(), ClearError> { let device = self.device(); // VUID-vkCmdClearColorImage-renderpass if self.builder_state.render_pass.is_some() { return Err(ClearError::ForbiddenInsideRenderPass); } let queue_family_properties = self.queue_family_properties(); // VUID-vkCmdClearColorImage-commandBuffer-cmdpool if !queue_family_properties .queue_flags .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) { return Err(ClearError::NotSupportedByQueueFamily); } let &ClearColorImageInfo { ref image, image_layout, clear_value: _, ref regions, _ne: _, } = clear_info; // VUID-vkCmdClearColorImage-imageLayout-parameter image_layout.validate_device(device)?; // VUID-vkCmdClearColorImage-commonparent assert_eq!(device, image.device()); // VUID-vkCmdClearColorImage-image-00002 if !image.usage().intersects(ImageUsage::TRANSFER_DST) { return Err(ClearError::MissingUsage { usage: "transfer_dst", }); } if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { // VUID-vkCmdClearColorImage-image-01993 if !image .format_features() .intersects(FormatFeatures::TRANSFER_DST) { return Err(ClearError::MissingFormatFeature { format_feature: "transfer_dst", }); } } let image_aspects = image.format().aspects(); // VUID-vkCmdClearColorImage-image-00007 if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { return Err(ClearError::FormatNotSupported { format: image.format(), }); } // VUID-vkCmdClearColorImage-image-00007 if image.format().compression().is_some() { return Err(ClearError::FormatNotSupported { format: image.format(), }); } // VUID-vkCmdClearColorImage-image-01545 if image.format().ycbcr_chroma_sampling().is_some() { return Err(ClearError::FormatNotSupported { format: image.format(), }); } // VUID-vkCmdClearColorImage-imageLayout-01394 if !matches!( image_layout, ImageLayout::TransferDstOptimal | ImageLayout::General ) { return Err(ClearError::ImageLayoutInvalid { image_layout }); } for (region_index, subresource_range) in regions.iter().enumerate() { // VUID-VkImageSubresourceRange-aspectMask-parameter subresource_range.aspects.validate_device(device)?; // VUID-VkImageSubresourceRange-aspectMask-requiredbitmask assert!(!subresource_range.aspects.is_empty()); // VUID-vkCmdClearColorImage-aspectMask-02498 if !image_aspects.contains(subresource_range.aspects) { return Err(ClearError::AspectsNotAllowed { region_index, aspects: subresource_range.aspects, allowed_aspects: image_aspects, }); } // VUID-VkImageSubresourceRange-levelCount-01720 assert!(!subresource_range.mip_levels.is_empty()); // VUID-vkCmdClearColorImage-baseMipLevel-01470 // VUID-vkCmdClearColorImage-pRanges-01692 if subresource_range.mip_levels.end > image.mip_levels() { return Err(ClearError::MipLevelsOutOfRange { region_index, mip_levels_range_end: subresource_range.mip_levels.end, image_mip_levels: image.dimensions().array_layers(), }); } // VUID-VkImageSubresourceRange-layerCount-01721 assert!(!subresource_range.array_layers.is_empty()); // VUID-vkCmdClearDepthStencilImage-baseArrayLayer-01476 // VUID-vkCmdClearDepthStencilImage-pRanges-01695 if subresource_range.array_layers.end > image.dimensions().array_layers() { return Err(ClearError::ArrayLayersOutOfRange { region_index, array_layers_range_end: subresource_range.array_layers.end, image_array_layers: image.dimensions().array_layers(), }); } } // TODO: sync check Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn clear_color_image_unchecked( &mut self, clear_info: ClearColorImageInfo, ) -> &mut Self { let ClearColorImageInfo { image, image_layout, clear_value, regions, _ne: _, } = clear_info; if regions.is_empty() { return self; } let image_inner = image.inner(); let clear_value = clear_value.into(); let ranges: SmallVec<[_; 8]> = regions .iter() .cloned() .map(ash::vk::ImageSubresourceRange::from) .collect(); let fns = self.device().fns(); (fns.v1_0.cmd_clear_color_image)( self.handle(), image_inner.image.handle(), image_layout.into(), &clear_value, ranges.len() as u32, ranges.as_ptr(), ); let command_index = self.next_command_index; let command_name = "clear_color_image"; let use_ref = ResourceUseRef { command_index, command_name, resource_in_command: ResourceInCommand::Destination, secondary_use_ref: None, }; for mut subresource_range in regions { 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; self.resources_usage_state.record_image_access( &use_ref, image_inner.image, subresource_range, PipelineStageAccess::Clear_TransferWrite, image_layout, ); } self.resources.push(Box::new(image)); self.next_command_index += 1; self } /// Clears a depth/stencil image with a specific value. /// /// # 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 clear_depth_stencil_image( &mut self, clear_info: ClearDepthStencilImageInfo, ) -> Result<&mut Self, ClearError> { self.validate_clear_depth_stencil_image(&clear_info)?; unsafe { Ok(self.clear_depth_stencil_image_unchecked(clear_info)) } } fn validate_clear_depth_stencil_image( &self, clear_info: &ClearDepthStencilImageInfo, ) -> Result<(), ClearError> { let device = self.device(); // VUID-vkCmdClearDepthStencilImage-renderpass if self.builder_state.render_pass.is_some() { return Err(ClearError::ForbiddenInsideRenderPass); } let queue_family_properties = self.queue_family_properties(); // VUID-vkCmdClearDepthStencilImage-commandBuffer-cmdpool if !queue_family_properties .queue_flags .intersects(QueueFlags::GRAPHICS) { return Err(ClearError::NotSupportedByQueueFamily); } let &ClearDepthStencilImageInfo { ref image, image_layout, clear_value, ref regions, _ne: _, } = clear_info; // VUID-vkCmdClearDepthStencilImage-imageLayout-parameter image_layout.validate_device(device)?; // VUID-vkCmdClearDepthStencilImage-commonparent assert_eq!(device, image.device()); if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { // VUID-vkCmdClearDepthStencilImage-image-01994 if !image .format_features() .intersects(FormatFeatures::TRANSFER_DST) { return Err(ClearError::MissingFormatFeature { format_feature: "transfer_dst", }); } } let image_aspects = image.format().aspects(); // VUID-vkCmdClearDepthStencilImage-image-00014 if !image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { return Err(ClearError::FormatNotSupported { format: image.format(), }); } // VUID-vkCmdClearDepthStencilImage-imageLayout-00012 if !matches!( image_layout, ImageLayout::TransferDstOptimal | ImageLayout::General ) { return Err(ClearError::ImageLayoutInvalid { image_layout }); } // VUID-VkClearDepthStencilValue-depth-00022 if !device.enabled_extensions().ext_depth_range_unrestricted && !(0.0..=1.0).contains(&clear_value.depth) { return Err(ClearError::RequirementNotMet { required_for: "`clear_info.clear_value.depth` is not between `0.0` and `1.0` \ inclusive", requires_one_of: RequiresOneOf { device_extensions: &["ext_depth_range_unrestricted"], ..Default::default() }, }); } let mut image_aspects_used = ImageAspects::empty(); for (region_index, subresource_range) in regions.iter().enumerate() { // VUID-VkImageSubresourceRange-aspectMask-parameter subresource_range.aspects.validate_device(device)?; // VUID-VkImageSubresourceRange-aspectMask-requiredbitmask assert!(!subresource_range.aspects.is_empty()); // VUID-vkCmdClearDepthStencilImage-aspectMask-02824 // VUID-vkCmdClearDepthStencilImage-image-02825 // VUID-vkCmdClearDepthStencilImage-image-02826 if !image_aspects.contains(subresource_range.aspects) { return Err(ClearError::AspectsNotAllowed { region_index, aspects: subresource_range.aspects, allowed_aspects: image_aspects, }); } image_aspects_used |= subresource_range.aspects; // VUID-VkImageSubresourceRange-levelCount-01720 assert!(!subresource_range.mip_levels.is_empty()); // VUID-vkCmdClearDepthStencilImage-baseMipLevel-01474 // VUID-vkCmdClearDepthStencilImage-pRanges-01694 if subresource_range.mip_levels.end > image.mip_levels() { return Err(ClearError::MipLevelsOutOfRange { region_index, mip_levels_range_end: subresource_range.mip_levels.end, image_mip_levels: image.dimensions().array_layers(), }); } // VUID-VkImageSubresourceRange-layerCount-01721 assert!(!subresource_range.array_layers.is_empty()); // VUID-vkCmdClearDepthStencilImage-baseArrayLayer-01476 // VUID-vkCmdClearDepthStencilImage-pRanges-01695 if subresource_range.array_layers.end > image.dimensions().array_layers() { return Err(ClearError::ArrayLayersOutOfRange { region_index, array_layers_range_end: subresource_range.array_layers.end, image_array_layers: image.dimensions().array_layers(), }); } } // VUID-vkCmdClearDepthStencilImage-pRanges-02658 // VUID-vkCmdClearDepthStencilImage-pRanges-02659 if image_aspects_used.intersects(ImageAspects::STENCIL) && !image.stencil_usage().intersects(ImageUsage::TRANSFER_DST) { return Err(ClearError::MissingUsage { usage: "transfer_dst", }); } // VUID-vkCmdClearDepthStencilImage-pRanges-02660 if !(image_aspects_used - ImageAspects::STENCIL).is_empty() && !image.usage().intersects(ImageUsage::TRANSFER_DST) { return Err(ClearError::MissingUsage { usage: "transfer_dst", }); } // TODO: sync check Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn clear_depth_stencil_image_unchecked( &mut self, clear_info: ClearDepthStencilImageInfo, ) -> &mut Self { let ClearDepthStencilImageInfo { image, image_layout, clear_value, regions, _ne: _, } = clear_info; if regions.is_empty() { return self; } let image_inner = image.inner(); let clear_value = clear_value.into(); let ranges: SmallVec<[_; 8]> = regions .iter() .cloned() .map(ash::vk::ImageSubresourceRange::from) .collect(); let fns = self.device().fns(); (fns.v1_0.cmd_clear_depth_stencil_image)( self.handle(), image_inner.image.handle(), image_layout.into(), &clear_value, ranges.len() as u32, ranges.as_ptr(), ); let command_index = self.next_command_index; let command_name = "clear_depth_stencil_image"; let use_ref = ResourceUseRef { command_index, command_name, resource_in_command: ResourceInCommand::Destination, secondary_use_ref: None, }; for mut subresource_range in regions { 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; self.resources_usage_state.record_image_access( &use_ref, image_inner.image, subresource_range, PipelineStageAccess::Clear_TransferWrite, image_layout, ); } self.resources.push(Box::new(image)); self.next_command_index += 1; self } /// Fills a region of a buffer with repeated copies of a value. /// /// This function is similar to the `memset` function in C. The `data` parameter is a number /// that will be repeatedly written through the entire buffer. /// /// # Panics /// /// - Panics if `dst_buffer` was not created from the same device as `self`. /// /// # Safety /// /// - Appropriate synchronization must be provided for all buffers /// that are accessed by the command. #[inline] pub unsafe fn fill_buffer( &mut self, dst_buffer: Subbuffer<[u32]>, data: u32, ) -> Result<&mut Self, ClearError> { self.validate_fill_buffer(&dst_buffer, data)?; unsafe { Ok(self.fill_buffer_unchecked(dst_buffer, data)) } } fn validate_fill_buffer( &self, dst_buffer: &Subbuffer<[u32]>, _data: u32, ) -> Result<(), ClearError> { let device = self.device(); // VUID-vkCmdFillBuffer-renderpass if self.builder_state.render_pass.is_some() { return Err(ClearError::ForbiddenInsideRenderPass); } let queue_family_properties = self.queue_family_properties(); if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { // VUID-vkCmdFillBuffer-commandBuffer-cmdpool if !queue_family_properties .queue_flags .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) { return Err(ClearError::NotSupportedByQueueFamily); } } else { // VUID-vkCmdFillBuffer-commandBuffer-00030 if !queue_family_properties .queue_flags .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) { return Err(ClearError::NotSupportedByQueueFamily); } } // VUID-vkCmdFillBuffer-commonparent assert_eq!(device, dst_buffer.device()); // VUID-vkCmdFillBuffer-size-00026 // Guaranteed by `Subbuffer` // VUID-vkCmdFillBuffer-dstBuffer-00029 if !dst_buffer .buffer() .usage() .intersects(BufferUsage::TRANSFER_DST) { return Err(ClearError::MissingUsage { usage: "transfer_dst", }); } // VUID-vkCmdFillBuffer-dstOffset-00024 // VUID-vkCmdFillBuffer-size-00027 // Guaranteed by `Subbuffer` // VUID-vkCmdFillBuffer-dstOffset-00025 // VUID-vkCmdFillBuffer-size-00028 // Guaranteed because we take `Subbuffer<[u32]>` // TODO: sync check Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn fill_buffer_unchecked( &mut self, dst_buffer: Subbuffer<[u32]>, data: u32, ) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_fill_buffer)( self.handle(), dst_buffer.buffer().handle(), dst_buffer.offset(), dst_buffer.size(), data, ); let command_index = self.next_command_index; let command_name = "fill_buffer"; let use_ref = ResourceUseRef { command_index, command_name, resource_in_command: ResourceInCommand::Destination, secondary_use_ref: None, }; self.resources_usage_state.record_buffer_access( &use_ref, dst_buffer.buffer(), dst_buffer.range(), PipelineStageAccess::Clear_TransferWrite, ); self.resources.push(Box::new(dst_buffer)); self.next_command_index += 1; self } /// Writes data to a region of a buffer. /// /// # Panics /// /// - Panics if `dst_buffer` was not created from the same device as `self`. /// /// # Safety /// /// - Appropriate synchronization must be provided for all buffers /// that are accessed by the command. #[inline] pub unsafe fn update_buffer( &mut self, dst_buffer: Subbuffer, data: &D, ) -> Result<&mut Self, ClearError> where D: BufferContents + ?Sized, { self.validate_update_buffer(dst_buffer.as_bytes(), size_of_val(data) as DeviceSize)?; unsafe { Ok(self.update_buffer_unchecked(dst_buffer, data)) } } fn validate_update_buffer( &self, dst_buffer: &Subbuffer<[u8]>, data_size: DeviceSize, ) -> Result<(), ClearError> { let device = self.device(); // VUID-vkCmdUpdateBuffer-renderpass if self.builder_state.render_pass.is_some() { return Err(ClearError::ForbiddenInsideRenderPass); } let queue_family_properties = self.queue_family_properties(); // VUID-vkCmdUpdateBuffer-commandBuffer-cmdpool if !queue_family_properties .queue_flags .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) { return Err(ClearError::NotSupportedByQueueFamily); } // VUID-vkCmdUpdateBuffer-commonparent assert_eq!(device, dst_buffer.device()); // VUID-vkCmdUpdateBuffer-dataSize-arraylength assert!(data_size != 0); // VUID-vkCmdUpdateBuffer-dstBuffer-00034 if !dst_buffer .buffer() .usage() .intersects(BufferUsage::TRANSFER_DST) { return Err(ClearError::MissingUsage { usage: "transfer_dst", }); } // VUID-vkCmdUpdateBuffer-dstOffset-00032 // VUID-vkCmdUpdateBuffer-dataSize-00033 if data_size > dst_buffer.size() { return Err(ClearError::RegionOutOfBufferBounds { region_index: 0, offset_range_end: data_size, buffer_size: dst_buffer.size(), }); } // VUID-vkCmdUpdateBuffer-dstOffset-00036 if dst_buffer.offset() % 4 != 0 { return Err(ClearError::OffsetNotAlignedForBuffer { region_index: 0, offset: dst_buffer.offset(), required_alignment: 4, }); } // VUID-vkCmdUpdateBuffer-dataSize-00037 if data_size > 65536 { return Err(ClearError::DataTooLarge { size: data_size, max: 65536, }); } // VUID-vkCmdUpdateBuffer-dataSize-00038 if data_size % 4 != 0 { return Err(ClearError::SizeNotAlignedForBuffer { region_index: 0, size: data_size, required_alignment: 4, }); } // TODO: sync check Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn update_buffer_unchecked( &mut self, dst_buffer: Subbuffer, data: &D, ) -> &mut Self where D: BufferContents + ?Sized, { let fns = self.device().fns(); (fns.v1_0.cmd_update_buffer)( self.handle(), dst_buffer.buffer().handle(), dst_buffer.offset(), size_of_val(data) as DeviceSize, data as *const _ as *const _, ); let command_index = self.next_command_index; let command_name = "update_buffer"; let use_ref = ResourceUseRef { command_index, command_name, resource_in_command: ResourceInCommand::Destination, secondary_use_ref: None, }; let dst_range = dst_buffer.offset()..dst_buffer.offset() + size_of_val(data) as DeviceSize; self.resources_usage_state.record_buffer_access( &use_ref, dst_buffer.buffer(), dst_range, PipelineStageAccess::Clear_TransferWrite, ); self.resources.push(Box::new(dst_buffer)); self.next_command_index += 1; self } }