diff --git a/examples/src/bin/async-update.rs b/examples/src/bin/async-update.rs index 69ff06a8e6..732198eefe 100644 --- a/examples/src/bin/async-update.rs +++ b/examples/src/bin/async-update.rs @@ -63,7 +63,9 @@ use vulkano::{ }, format::Format, image::{ - view::ImageView, ImageAccess, ImageDimensions, ImageUsage, StorageImage, SwapchainImage, + sampler::{Sampler, SamplerCreateInfo}, + view::ImageView, + ImageAccess, ImageDimensions, ImageUsage, StorageImage, SwapchainImage, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, @@ -82,7 +84,6 @@ use vulkano::{ PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, - sampler::{Sampler, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, diff --git a/examples/src/bin/gl-interop.rs b/examples/src/bin/gl-interop.rs index ebc3b581e0..8965e8824a 100644 --- a/examples/src/bin/gl-interop.rs +++ b/examples/src/bin/gl-interop.rs @@ -28,7 +28,11 @@ mod linux { QueueCreateInfo, QueueFlags, }, format::Format, - image::{view::ImageView, ImageCreateFlags, ImageUsage, StorageImage, SwapchainImage}, + image::{ + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + view::ImageView, + ImageCreateFlags, ImageUsage, StorageImage, SwapchainImage, + }, instance::{ debug::{DebugUtilsMessenger, DebugUtilsMessengerCreateInfo}, Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions, @@ -49,7 +53,6 @@ mod linux { PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, RenderPass, Subpass}, - sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo}, sync::{ now, diff --git a/examples/src/bin/image-self-copy-blit/main.rs b/examples/src/bin/image-self-copy-blit/main.rs index a6b8b21fc6..9348f3f531 100644 --- a/examples/src/bin/image-self-copy-blit/main.rs +++ b/examples/src/bin/image-self-copy-blit/main.rs @@ -25,8 +25,9 @@ use vulkano::{ }, format::Format, image::{ - view::ImageView, ImageAccess, ImageDimensions, ImageLayout, ImageUsage, StorageImage, - SwapchainImage, + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + view::ImageView, + ImageAccess, ImageDimensions, ImageLayout, ImageUsage, StorageImage, SwapchainImage, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, @@ -45,7 +46,6 @@ use vulkano::{ PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, - sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, diff --git a/examples/src/bin/image/main.rs b/examples/src/bin/image/main.rs index b6582afbe4..00980a4004 100644 --- a/examples/src/bin/image/main.rs +++ b/examples/src/bin/image/main.rs @@ -23,8 +23,9 @@ use vulkano::{ }, format::Format, image::{ - view::ImageView, ImageAccess, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, - SwapchainImage, + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + view::ImageView, + ImageAccess, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, @@ -43,7 +44,6 @@ use vulkano::{ PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, - sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, diff --git a/examples/src/bin/immutable-sampler/main.rs b/examples/src/bin/immutable-sampler/main.rs index 3814f04301..92f4352cb0 100644 --- a/examples/src/bin/immutable-sampler/main.rs +++ b/examples/src/bin/immutable-sampler/main.rs @@ -32,8 +32,9 @@ use vulkano::{ }, format::Format, image::{ - view::ImageView, ImageAccess, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, - SwapchainImage, + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + view::ImageView, + ImageAccess, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, @@ -52,7 +53,6 @@ use vulkano::{ PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, - sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, diff --git a/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs b/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs index 8a0dfb28b1..acf84402f9 100644 --- a/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs +++ b/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs @@ -18,7 +18,10 @@ use vulkano::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, }, device::Queue, - image::ImageViewAbstract, + image::{ + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode}, + ImageViewAbstract, + }, memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryUsage}, pipeline::{ graphics::{ @@ -35,7 +38,6 @@ use vulkano::{ PipelineShaderStageCreateInfo, }, render_pass::Subpass, - sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode}, }; /// Vertex for textured quads. diff --git a/examples/src/bin/multi_window_game_of_life/pixels_draw.rs b/examples/src/bin/multi_window_game_of_life/pixels_draw.rs index 77127c0f71..ccf7829f1e 100644 --- a/examples/src/bin/multi_window_game_of_life/pixels_draw.rs +++ b/examples/src/bin/multi_window_game_of_life/pixels_draw.rs @@ -19,7 +19,10 @@ use vulkano::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, }, device::Queue, - image::ImageViewAbstract, + image::{ + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode}, + ImageViewAbstract, + }, memory::allocator::{AllocationCreateInfo, MemoryUsage}, pipeline::{ graphics::{ @@ -36,7 +39,6 @@ use vulkano::{ PipelineShaderStageCreateInfo, }, render_pass::Subpass, - sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode}, }; /// Vertex for textured quads. diff --git a/examples/src/bin/push-descriptors/main.rs b/examples/src/bin/push-descriptors/main.rs index 7f41086048..24f643f2e4 100644 --- a/examples/src/bin/push-descriptors/main.rs +++ b/examples/src/bin/push-descriptors/main.rs @@ -21,8 +21,9 @@ use vulkano::{ }, format::Format, image::{ - view::ImageView, ImageAccess, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, - SwapchainImage, + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + view::ImageView, + ImageAccess, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, @@ -41,7 +42,6 @@ use vulkano::{ PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, - sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, diff --git a/examples/src/bin/runtime_array/main.rs b/examples/src/bin/runtime_array/main.rs index a4e8b572c6..76a1f2c10b 100644 --- a/examples/src/bin/runtime_array/main.rs +++ b/examples/src/bin/runtime_array/main.rs @@ -24,8 +24,9 @@ use vulkano::{ }, format::Format, image::{ - view::ImageView, ImageAccess, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, - SwapchainImage, + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + view::ImageView, + ImageAccess, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, @@ -44,7 +45,6 @@ use vulkano::{ PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, - sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, diff --git a/examples/src/bin/texture_array/main.rs b/examples/src/bin/texture_array/main.rs index 70d64638fd..c48012b4c2 100644 --- a/examples/src/bin/texture_array/main.rs +++ b/examples/src/bin/texture_array/main.rs @@ -23,8 +23,9 @@ use vulkano::{ }, format::Format, image::{ - view::ImageView, ImageAccess, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, - SwapchainImage, + sampler::{Sampler, SamplerCreateInfo}, + view::ImageView, + ImageAccess, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, @@ -43,7 +44,6 @@ use vulkano::{ PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, - sampler::{Sampler, SamplerCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, diff --git a/vulkano/src/command_buffer/auto/mod.rs b/vulkano/src/command_buffer/auto/mod.rs index 44982d10ac..b4d8e7ec54 100644 --- a/vulkano/src/command_buffer/auto/mod.rs +++ b/vulkano/src/command_buffer/auto/mod.rs @@ -322,9 +322,9 @@ mod tests { PersistentDescriptorSet, WriteDescriptorSet, }, device::{Device, DeviceCreateInfo, QueueCreateInfo}, + image::sampler::{Sampler, SamplerCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{layout::PipelineLayoutCreateInfo, PipelineBindPoint, PipelineLayout}, - sampler::{Sampler, SamplerCreateInfo}, shader::ShaderStages, sync::GpuFuture, }; diff --git a/vulkano/src/command_buffer/commands/copy.rs b/vulkano/src/command_buffer/commands/copy.rs index 10b1b0e516..39c34ea880 100644 --- a/vulkano/src/command_buffer/commands/copy.rs +++ b/vulkano/src/command_buffer/commands/copy.rs @@ -16,10 +16,9 @@ use crate::{ device::{DeviceOwned, QueueFlags}, format::{Format, FormatFeatures, NumericType}, image::{ - ImageAccess, ImageAspects, ImageDimensions, ImageLayout, ImageSubresourceLayers, ImageType, - ImageUsage, SampleCount, SampleCounts, + sampler::Filter, ImageAccess, ImageAspects, ImageDimensions, ImageLayout, + ImageSubresourceLayers, ImageType, ImageUsage, SampleCount, SampleCounts, }, - sampler::Filter, sync::PipelineStageAccessFlags, DeviceSize, RequirementNotMet, RequiresOneOf, Version, VulkanObject, }; diff --git a/vulkano/src/command_buffer/commands/pipeline.rs b/vulkano/src/command_buffer/commands/pipeline.rs index aeacbed0e9..de6c6d09bb 100644 --- a/vulkano/src/command_buffer/commands/pipeline.rs +++ b/vulkano/src/command_buffer/commands/pipeline.rs @@ -24,7 +24,8 @@ use crate::{ device::{DeviceOwned, QueueFlags}, format::{Format, FormatFeatures}, image::{ - view::ImageViewType, ImageAccess, ImageAspects, ImageLayout, ImageViewAbstract, SampleCount, + sampler::Sampler, view::ImageViewType, ImageAccess, ImageAspects, ImageLayout, + ImageViewAbstract, SampleCount, }, pipeline::{ graphics::{ @@ -34,7 +35,6 @@ use crate::{ }, DynamicState, GraphicsPipeline, PartialStateMode, Pipeline, PipelineLayout, }, - sampler::Sampler, shader::{DescriptorBindingRequirements, ShaderScalarType, ShaderStage, ShaderStages}, sync::{PipelineStageAccess, PipelineStageAccessFlags}, DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, VulkanObject, diff --git a/vulkano/src/descriptor_set/layout.rs b/vulkano/src/descriptor_set/layout.rs index 7ea36d6d80..450e93a37b 100644 --- a/vulkano/src/descriptor_set/layout.rs +++ b/vulkano/src/descriptor_set/layout.rs @@ -13,9 +13,8 @@ use crate::{ device::{Device, DeviceOwned}, - image::ImageLayout, + image::{sampler::Sampler, ImageLayout}, macros::{impl_id_counter, vulkan_bitflags, vulkan_enum}, - sampler::Sampler, shader::{DescriptorBindingRequirements, ShaderStages}, Requires, RequiresAllOf, RequiresOneOf, RuntimeError, ValidationError, Version, VulkanError, VulkanObject, diff --git a/vulkano/src/descriptor_set/mod.rs b/vulkano/src/descriptor_set/mod.rs index f2592dc6c9..f986935eb3 100644 --- a/vulkano/src/descriptor_set/mod.rs +++ b/vulkano/src/descriptor_set/mod.rs @@ -95,8 +95,7 @@ use crate::{ DescriptorBindingFlags, DescriptorSetLayoutCreateFlags, DescriptorType, }, device::DeviceOwned, - image::ImageLayout, - sampler::Sampler, + image::{sampler::Sampler, ImageLayout}, ValidationError, VulkanObject, }; use ahash::HashMap; diff --git a/vulkano/src/descriptor_set/update.rs b/vulkano/src/descriptor_set/update.rs index db7ef65953..4dbba7027e 100644 --- a/vulkano/src/descriptor_set/update.rs +++ b/vulkano/src/descriptor_set/update.rs @@ -17,9 +17,9 @@ use crate::{ descriptor_set::layout::{DescriptorBindingFlags, DescriptorSetLayoutCreateFlags}, device::DeviceOwned, image::{ - view::ImageViewType, ImageAspects, ImageLayout, ImageType, ImageUsage, ImageViewAbstract, + sampler::Sampler, view::ImageViewType, ImageAspects, ImageLayout, ImageType, ImageUsage, + ImageViewAbstract, }, - sampler::Sampler, DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, VulkanObject, }; use smallvec::SmallVec; diff --git a/vulkano/src/image/immutable.rs b/vulkano/src/image/immutable.rs index 851f0cb474..d4341ba2d5 100644 --- a/vulkano/src/image/immutable.rs +++ b/vulkano/src/image/immutable.rs @@ -21,7 +21,7 @@ use crate::{ }, device::{Device, DeviceOwned}, format::Format, - image::sys::ImageCreateInfo, + image::{sampler::Filter, sys::ImageCreateInfo}, memory::{ allocator::{ AllocationCreateInfo, AllocationType, MemoryAllocatePreference, MemoryAllocator, @@ -29,7 +29,6 @@ use crate::{ }, is_aligned, DedicatedAllocation, }, - sampler::Filter, sync::Sharing, DeviceSize, RuntimeError, }; diff --git a/vulkano/src/image/mod.rs b/vulkano/src/image/mod.rs index 9c8041c5a8..834b21383a 100644 --- a/vulkano/src/image/mod.rs +++ b/vulkano/src/image/mod.rs @@ -75,6 +75,7 @@ mod aspect; pub mod attachment; // TODO: make private pub mod immutable; // TODO: make private mod layout; +pub mod sampler; mod storage; pub mod swapchain; // TODO: make private pub mod sys; diff --git a/vulkano/src/sampler/mod.rs b/vulkano/src/image/sampler/mod.rs similarity index 74% rename from vulkano/src/sampler/mod.rs rename to vulkano/src/image/sampler/mod.rs index d8048b3acf..cee7bca46e 100644 --- a/vulkano/src/sampler/mod.rs +++ b/vulkano/src/image/sampler/mod.rs @@ -54,18 +54,10 @@ use crate::{ macros::{impl_id_counter, vulkan_enum}, pipeline::graphics::depth_stencil::CompareOp, shader::ShaderScalarType, - OomError, RequirementNotMet, Requires, RequiresAllOf, RequiresOneOf, RuntimeError, - ValidationError, VulkanObject, -}; -use std::{ - error::Error, - fmt::{Display, Error as FmtError, Formatter}, - mem::MaybeUninit, - num::NonZeroU64, - ops::RangeInclusive, - ptr, - sync::Arc, + Requires, RequiresAllOf, RequiresOneOf, RuntimeError, ValidationError, VulkanError, + VulkanObject, }; +use std::{mem::MaybeUninit, num::NonZeroU64, ops::RangeInclusive, ptr, sync::Arc}; /// Describes how to retrieve data from a sampled image within a shader. /// @@ -74,7 +66,7 @@ use std::{ /// A simple sampler for most usages: /// /// ``` -/// use vulkano::sampler::{Sampler, SamplerCreateInfo}; +/// use vulkano::image::sampler::{Sampler, SamplerCreateInfo}; /// /// # let device: std::sync::Arc = return; /// let _sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear_no_mipmap()); @@ -83,7 +75,7 @@ use std::{ /// More detailed sampler creation: /// /// ``` -/// use vulkano::sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}; +/// use vulkano::image::sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}; /// /// # let device: std::sync::Arc = return; /// let _sampler = Sampler::new(device.clone(), SamplerCreateInfo { @@ -118,16 +110,33 @@ pub struct Sampler { impl Sampler { /// Creates a new `Sampler`. - /// - /// # Panics - /// - /// - Panics if `create_info.anisotropy` is `Some` and contains a value less than 1.0. - /// - Panics if `create_info.lod` is empty. + #[inline] pub fn new( device: Arc, create_info: SamplerCreateInfo, - ) -> Result, SamplerCreationError> { - let SamplerCreateInfo { + ) -> Result, VulkanError> { + Self::validate_new(&device, &create_info)?; + + unsafe { Ok(Self::new_unchecked(device, create_info)?) } + } + + fn validate_new( + device: &Device, + create_info: &SamplerCreateInfo, + ) -> Result<(), ValidationError> { + create_info + .validate(device) + .map_err(|err| err.add_context("create_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn new_unchecked( + device: Arc, + create_info: SamplerCreateInfo, + ) -> Result, RuntimeError> { + let &SamplerCreateInfo { mag_filter, min_filter, mipmap_mode, @@ -135,264 +144,27 @@ impl Sampler { mip_lod_bias, anisotropy, compare, - lod, + ref lod, border_color, unnormalized_coordinates, reduction_mode, - sampler_ycbcr_conversion, + ref sampler_ycbcr_conversion, _ne: _, - } = create_info; - - for filter in [mag_filter, min_filter] { - // VUID-VkSamplerCreateInfo-magFilter-parameter - // VUID-VkSamplerCreateInfo-minFilter-parameter - filter.validate_device(&device)?; - } - - // VUID-VkSamplerCreateInfo-mipmapMode-parameter - mipmap_mode.validate_device(&device)?; - - for mode in address_mode { - // VUID-VkSamplerCreateInfo-addressModeU-parameter - // VUID-VkSamplerCreateInfo-addressModeV-parameter - // VUID-VkSamplerCreateInfo-addressModeW-parameter - mode.validate_device(&device)?; - - if mode == SamplerAddressMode::ClampToBorder { - // VUID-VkSamplerCreateInfo-addressModeU-01078 - border_color.validate_device(&device)?; - } - } - - if address_mode.contains(&SamplerAddressMode::MirrorClampToEdge) { - if !(device.enabled_features().sampler_mirror_clamp_to_edge - || device.enabled_extensions().khr_sampler_mirror_clamp_to_edge) - { - return Err(SamplerCreationError::RequirementNotMet { - required_for: "`create_info.address_mode` contains \ - `SamplerAddressMode::MirrorClampToEdge`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::Feature("sampler_mirror_clamp_to_edge")]), - RequiresAllOf(&[Requires::DeviceExtension( - "khr_sampler_mirror_clamp_to_edge", - )]), - ]), - }); - } - } - - { - assert!(!lod.is_empty()); - let limit = device.physical_device().properties().max_sampler_lod_bias; - if mip_lod_bias.abs() > limit { - return Err(SamplerCreationError::MaxSamplerLodBiasExceeded { - requested: mip_lod_bias, - maximum: limit, - }); - } - } - - // VUID-VkSamplerCreateInfo-samplerMipLodBias-04467 - if device.enabled_extensions().khr_portability_subset - && !device.enabled_features().sampler_mip_lod_bias - && mip_lod_bias != 0.0 - { - return Err(SamplerCreationError::RequirementNotMet { - required_for: "this device is a portability subset device, and \ - `create_info.mip_lod_bias` is not zero", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "sampler_mip_lod_bias", - )])]), - }); - } + } = &create_info; let (anisotropy_enable, max_anisotropy) = if let Some(max_anisotropy) = anisotropy { - assert!(max_anisotropy >= 1.0); - - if !device.enabled_features().sampler_anisotropy { - return Err(SamplerCreationError::RequirementNotMet { - required_for: "`create_info.anisotropy` is `Some`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "sampler_anisotropy", - )])]), - }); - } - - let limit = device.physical_device().properties().max_sampler_anisotropy; - if max_anisotropy > limit { - return Err(SamplerCreationError::MaxSamplerAnisotropyExceeded { - requested: max_anisotropy, - maximum: limit, - }); - } - - if [mag_filter, min_filter] - .into_iter() - .any(|filter| filter == Filter::Cubic) - { - return Err(SamplerCreationError::AnisotropyInvalidFilter { - mag_filter, - min_filter, - }); - } - (ash::vk::TRUE, max_anisotropy) } else { (ash::vk::FALSE, 1.0) }; let (compare_enable, compare_op) = if let Some(compare_op) = compare { - // VUID-VkSamplerCreateInfo-compareEnable-01080 - compare_op.validate_device(&device)?; - - if reduction_mode != SamplerReductionMode::WeightedAverage { - return Err(SamplerCreationError::CompareInvalidReductionMode { reduction_mode }); - } - (ash::vk::TRUE, compare_op) } else { (ash::vk::FALSE, CompareOp::Never) }; - if unnormalized_coordinates { - if min_filter != mag_filter { - return Err( - SamplerCreationError::UnnormalizedCoordinatesFiltersNotEqual { - mag_filter, - min_filter, - }, - ); - } - - if mipmap_mode != SamplerMipmapMode::Nearest { - return Err( - SamplerCreationError::UnnormalizedCoordinatesInvalidMipmapMode { mipmap_mode }, - ); - } - - if lod != (0.0..=0.0) { - return Err(SamplerCreationError::UnnormalizedCoordinatesNonzeroLod { lod }); - } - - if address_mode[0..2].iter().any(|mode| { - !matches!( - mode, - SamplerAddressMode::ClampToEdge | SamplerAddressMode::ClampToBorder - ) - }) { - return Err( - SamplerCreationError::UnnormalizedCoordinatesInvalidAddressMode { - address_mode: [address_mode[0], address_mode[1]], - }, - ); - } - - if anisotropy.is_some() { - return Err(SamplerCreationError::UnnormalizedCoordinatesAnisotropyEnabled); - } - - if compare.is_some() { - return Err(SamplerCreationError::UnnormalizedCoordinatesCompareEnabled); - } - } - - let mut sampler_reduction_mode_create_info = if reduction_mode - != SamplerReductionMode::WeightedAverage - { - if !(device.enabled_features().sampler_filter_minmax - || device.enabled_extensions().ext_sampler_filter_minmax) - { - return Err(SamplerCreationError::RequirementNotMet { - required_for: "`create_info.reduction_mode` is not \ - `SamplerReductionMode::WeightedAverage`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::Feature("sampler_filter_minmax")]), - RequiresAllOf(&[Requires::DeviceExtension("ext_sampler_filter_minmax")]), - ]), - }); - } - - // VUID-VkSamplerReductionModeCreateInfo-reductionMode-parameter - reduction_mode.validate_device(&device)?; - - Some(ash::vk::SamplerReductionModeCreateInfo { - reduction_mode: reduction_mode.into(), - ..Default::default() - }) - } else { - None - }; - - // Don't need to check features because you can't create a conversion object without the - // feature anyway. - let mut sampler_ycbcr_conversion_info = if let Some(sampler_ycbcr_conversion) = - &sampler_ycbcr_conversion - { - assert_eq!(&device, sampler_ycbcr_conversion.device()); - - // Use unchecked, because all validation has been done by the SamplerYcbcrConversion. - let potential_format_features = unsafe { - device - .physical_device() - .format_properties_unchecked(sampler_ycbcr_conversion.format().unwrap()) - .potential_format_features() - }; - - // VUID-VkSamplerCreateInfo-minFilter-01645 - if !potential_format_features.intersects( - FormatFeatures::SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER, - ) && !(mag_filter == sampler_ycbcr_conversion.chroma_filter() - && min_filter == sampler_ycbcr_conversion.chroma_filter()) - { - return Err( - SamplerCreationError::SamplerYcbcrConversionChromaFilterMismatch { - chroma_filter: sampler_ycbcr_conversion.chroma_filter(), - mag_filter, - min_filter, - }, - ); - } - - // VUID-VkSamplerCreateInfo-addressModeU-01646 - if address_mode - .into_iter() - .any(|mode| !matches!(mode, SamplerAddressMode::ClampToEdge)) - { - return Err( - SamplerCreationError::SamplerYcbcrConversionInvalidAddressMode { address_mode }, - ); - } - - // VUID-VkSamplerCreateInfo-addressModeU-01646 - if anisotropy.is_some() { - return Err(SamplerCreationError::SamplerYcbcrConversionAnisotropyEnabled); - } - - // VUID-VkSamplerCreateInfo-addressModeU-01646 - if unnormalized_coordinates { - return Err( - SamplerCreationError::SamplerYcbcrConversionUnnormalizedCoordinatesEnabled, - ); - } - - // VUID-VkSamplerCreateInfo-None-01647 - if reduction_mode != SamplerReductionMode::WeightedAverage { - return Err( - SamplerCreationError::SamplerYcbcrConversionInvalidReductionMode { - reduction_mode, - }, - ); - } - - Some(ash::vk::SamplerYcbcrConversionInfo { - conversion: sampler_ycbcr_conversion.handle(), - ..Default::default() - }) - } else { - None - }; - - let mut create_info = ash::vk::SamplerCreateInfo { + let mut create_info_vk = ash::vk::SamplerCreateInfo { flags: ash::vk::SamplerCreateFlags::empty(), mag_filter: mag_filter.into(), min_filter: min_filter.into(), @@ -411,17 +183,30 @@ impl Sampler { unnormalized_coordinates: unnormalized_coordinates as ash::vk::Bool32, ..Default::default() }; + let mut sampler_reduction_mode_create_info_vk = None; + let mut sampler_ycbcr_conversion_info_vk = None; - if let Some(sampler_reduction_mode_create_info) = - sampler_reduction_mode_create_info.as_mut() - { - sampler_reduction_mode_create_info.p_next = create_info.p_next; - create_info.p_next = sampler_reduction_mode_create_info as *const _ as *const _; + if reduction_mode != SamplerReductionMode::WeightedAverage { + let next = sampler_reduction_mode_create_info_vk.insert( + ash::vk::SamplerReductionModeCreateInfo { + reduction_mode: reduction_mode.into(), + ..Default::default() + }, + ); + + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; } - if let Some(sampler_ycbcr_conversion_info) = sampler_ycbcr_conversion_info.as_mut() { - sampler_ycbcr_conversion_info.p_next = create_info.p_next; - create_info.p_next = sampler_ycbcr_conversion_info as *const _ as *const _; + if let Some(sampler_ycbcr_conversion) = sampler_ycbcr_conversion { + let next = + sampler_ycbcr_conversion_info_vk.insert(ash::vk::SamplerYcbcrConversionInfo { + conversion: sampler_ycbcr_conversion.handle(), + ..Default::default() + }); + + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; } let handle = unsafe { @@ -429,7 +214,7 @@ impl Sampler { let mut output = MaybeUninit::uninit(); (fns.v1_0.create_sampler)( device.handle(), - &create_info, + &create_info_vk, ptr::null(), output.as_mut_ptr(), ) @@ -438,26 +223,7 @@ impl Sampler { output.assume_init() }; - Ok(Arc::new(Sampler { - handle, - device, - id: Self::next_id(), - address_mode, - anisotropy, - border_color: address_mode - .into_iter() - .any(|mode| mode == SamplerAddressMode::ClampToBorder) - .then_some(border_color), - compare, - lod, - mag_filter, - min_filter, - mip_lod_bias, - mipmap_mode, - reduction_mode, - sampler_ycbcr_conversion, - unnormalized_coordinates, - })) + Ok(Self::from_handle(device, handle, create_info)) } /// Creates a new `Sampler` from a raw object handle. @@ -840,212 +606,6 @@ unsafe impl DeviceOwned for Sampler { impl_id_counter!(Sampler); -/// Error that can happen when creating an instance. -#[derive(Clone, Debug, PartialEq)] -pub enum SamplerCreationError { - /// Not enough memory. - OomError(OomError), - - /// Too many sampler objects have been created. You must destroy some before creating new ones. - /// Note the specs guarantee that at least 4000 samplers can exist simultaneously. - TooManyObjects, - - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// Anisotropy was enabled with an invalid filter. - AnisotropyInvalidFilter { - mag_filter: Filter, - min_filter: Filter, - }, - - /// Depth comparison was enabled with an invalid reduction mode. - CompareInvalidReductionMode { - reduction_mode: SamplerReductionMode, - }, - - /// The requested anisotropy level exceeds the device's limits. - MaxSamplerAnisotropyExceeded { - /// The value that was requested. - requested: f32, - /// The maximum supported value. - maximum: f32, - }, - - /// The requested mip lod bias exceeds the device's limits. - MaxSamplerLodBiasExceeded { - /// The value that was requested. - requested: f32, - /// The maximum supported value. - maximum: f32, - }, - - /// Sampler YCbCr conversion was enabled together with anisotropy. - SamplerYcbcrConversionAnisotropyEnabled, - - /// Sampler YCbCr conversion was enabled, and its format does not support - /// `sampled_image_ycbcr_conversion_separate_reconstruction_filter`, but `mag_filter` or - /// `min_filter` did not match the conversion's `chroma_filter`. - SamplerYcbcrConversionChromaFilterMismatch { - chroma_filter: Filter, - mag_filter: Filter, - min_filter: Filter, - }, - - /// Sampler YCbCr conversion was enabled, but the address mode for `u`, `v` or `w` was - /// something other than `ClampToEdge`. - SamplerYcbcrConversionInvalidAddressMode { - address_mode: [SamplerAddressMode; 3], - }, - - /// Sampler YCbCr conversion was enabled, but the reduction mode was something other than - /// `WeightedAverage`. - SamplerYcbcrConversionInvalidReductionMode { - reduction_mode: SamplerReductionMode, - }, - - /// Sampler YCbCr conversion was enabled together with unnormalized coordinates. - SamplerYcbcrConversionUnnormalizedCoordinatesEnabled, - - /// Unnormalized coordinates were enabled together with anisotropy. - UnnormalizedCoordinatesAnisotropyEnabled, - - /// Unnormalized coordinates were enabled together with depth comparison. - UnnormalizedCoordinatesCompareEnabled, - - /// Unnormalized coordinates were enabled, but the min and mag filters were not equal. - UnnormalizedCoordinatesFiltersNotEqual { - mag_filter: Filter, - min_filter: Filter, - }, - - /// Unnormalized coordinates were enabled, but the address mode for `u` or `v` was something - /// other than `ClampToEdge` or `ClampToBorder`. - UnnormalizedCoordinatesInvalidAddressMode { - address_mode: [SamplerAddressMode; 2], - }, - - /// Unnormalized coordinates were enabled, but the mipmap mode was not `Nearest`. - UnnormalizedCoordinatesInvalidMipmapMode { mipmap_mode: SamplerMipmapMode }, - - /// Unnormalized coordinates were enabled, but the LOD range was not zero. - UnnormalizedCoordinatesNonzeroLod { lod: RangeInclusive }, -} - -impl Error for SamplerCreationError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - SamplerCreationError::OomError(err) => Some(err), - _ => None, - } - } -} - -impl Display for SamplerCreationError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::OomError(_) => write!(f, "not enough memory available"), - Self::TooManyObjects => write!(f, "too many simultaneous sampler objects"), - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::AnisotropyInvalidFilter { .. } => { - write!(f, "anisotropy was enabled with an invalid filter") - } - Self::CompareInvalidReductionMode { .. } => write!( - f, - "depth comparison was enabled with an invalid reduction mode", - ), - Self::MaxSamplerAnisotropyExceeded { .. } => { - write!(f, "max_sampler_anisotropy limit exceeded") - } - Self::MaxSamplerLodBiasExceeded { .. } => write!(f, "mip lod bias limit exceeded"), - Self::SamplerYcbcrConversionAnisotropyEnabled => write!( - f, - "sampler YCbCr conversion was enabled together with anisotropy", - ), - Self::SamplerYcbcrConversionChromaFilterMismatch { .. } => write!( - f, - "sampler YCbCr conversion was enabled, and its format does not support - `sampled_image_ycbcr_conversion_separate_reconstruction_filter`, but `mag_filter` - or `min_filter` did not match the conversion's `chroma_filter`", - ), - Self::SamplerYcbcrConversionInvalidAddressMode { .. } => write!( - f, - "sampler YCbCr conversion was enabled, but the address mode for u, v or w was - something other than `ClampToEdge`", - ), - Self::SamplerYcbcrConversionInvalidReductionMode { .. } => write!( - f, - "sampler YCbCr conversion was enabled, but the reduction mode was something other \ - than `WeightedAverage`", - ), - Self::SamplerYcbcrConversionUnnormalizedCoordinatesEnabled => write!( - f, - "sampler YCbCr conversion was enabled together with unnormalized coordinates", - ), - Self::UnnormalizedCoordinatesAnisotropyEnabled => write!( - f, - "unnormalized coordinates were enabled together with anisotropy", - ), - Self::UnnormalizedCoordinatesCompareEnabled => write!( - f, - "unnormalized coordinates were enabled together with depth comparison", - ), - Self::UnnormalizedCoordinatesFiltersNotEqual { .. } => write!( - f, - "unnormalized coordinates were enabled, but the min and mag filters were not equal", - ), - Self::UnnormalizedCoordinatesInvalidAddressMode { .. } => write!( - f, - "unnormalized coordinates were enabled, but the address mode for u or v was \ - something other than `ClampToEdge` or `ClampToBorder`", - ), - Self::UnnormalizedCoordinatesInvalidMipmapMode { .. } => write!( - f, - "unnormalized coordinates were enabled, but the mipmap mode was not `Nearest`", - ), - Self::UnnormalizedCoordinatesNonzeroLod { .. } => write!( - f, - "unnormalized coordinates were enabled, but the LOD range was not zero", - ), - } - } -} - -impl From for SamplerCreationError { - fn from(err: OomError) -> Self { - Self::OomError(err) - } -} - -impl From for SamplerCreationError { - fn from(err: RuntimeError) -> Self { - match err { - err @ RuntimeError::OutOfHostMemory => Self::OomError(OomError::from(err)), - err @ RuntimeError::OutOfDeviceMemory => Self::OomError(OomError::from(err)), - RuntimeError::TooManyObjects => Self::TooManyObjects, - _ => panic!("unexpected error: {:?}", err), - } - } -} - -impl From for SamplerCreationError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, - } - } -} - /// Parameters to create a new `Sampler`. #[derive(Clone, Debug)] pub struct SamplerCreateInfo { @@ -1235,6 +795,354 @@ impl SamplerCreateInfo { ..Default::default() } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), ValidationError> { + let &Self { + mag_filter, + min_filter, + mipmap_mode, + address_mode, + mip_lod_bias, + anisotropy, + compare, + ref lod, + border_color, + unnormalized_coordinates, + reduction_mode, + ref sampler_ycbcr_conversion, + _ne: _, + } = self; + + let properties = device.physical_device().properties(); + + mag_filter + .validate_device(device) + .map_err(|err| ValidationError { + context: "mag_filter".into(), + vuids: &["VUID-VkSamplerCreateInfo-magFilter-parameter"], + ..ValidationError::from_requirement(err) + })?; + + min_filter + .validate_device(device) + .map_err(|err| ValidationError { + context: "min_filter".into(), + vuids: &["VUID-VkSamplerCreateInfo-minFilter-parameter"], + ..ValidationError::from_requirement(err) + })?; + + mipmap_mode + .validate_device(device) + .map_err(|err| ValidationError { + context: "mipmap_mode".into(), + vuids: &["VUID-VkSamplerCreateInfo-mipmapMode-parameter"], + ..ValidationError::from_requirement(err) + })?; + + for (index, mode) in address_mode.into_iter().enumerate() { + mode.validate_device(device) + .map_err(|err| ValidationError { + context: format!("address_mode[{}]", index).into(), + vuids: &[ + "VUID-VkSamplerCreateInfo-addressModeU-parameter", + "VUID-VkSamplerCreateInfo-addressModeV-parameter", + "VUID-VkSamplerCreateInfo-addressModeW-parameter", + ], + ..ValidationError::from_requirement(err) + })?; + } + + if address_mode.contains(&SamplerAddressMode::ClampToBorder) { + border_color + .validate_device(device) + .map_err(|err| ValidationError { + context: "border_color".into(), + vuids: &["VUID-VkSamplerCreateInfo-addressModeU-01078"], + ..ValidationError::from_requirement(err) + })?; + } + + reduction_mode + .validate_device(device) + .map_err(|err| ValidationError { + context: "reduction_mode".into(), + vuids: &["VUID-VkSamplerReductionModeCreateInfo-reductionMode-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if address_mode.contains(&SamplerAddressMode::MirrorClampToEdge) { + if !(device.enabled_features().sampler_mirror_clamp_to_edge + || device.enabled_extensions().khr_sampler_mirror_clamp_to_edge) + { + return Err(ValidationError { + context: "address_mode".into(), + problem: "contains `SamplerAddressMode::MirrorClampToEdge`".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::Feature("sampler_mirror_clamp_to_edge")]), + RequiresAllOf(&[Requires::DeviceExtension( + "khr_sampler_mirror_clamp_to_edge", + )]), + ]), + ..Default::default() + }); + } + } + + if lod.is_empty() { + return Err(ValidationError { + context: "lod".into(), + problem: "is empty".into(), + vuids: &["VUID-VkSamplerCreateInfo-maxLod-01973"], + ..Default::default() + }); + } + + if mip_lod_bias.abs() > properties.max_sampler_lod_bias { + return Err(ValidationError { + context: "lod".into(), + problem: "the absolute value is greater than the `max_sampler_lod_bias` limit" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-mipLodBias-01069"], + ..Default::default() + }); + } + + if device.enabled_extensions().khr_portability_subset + && !device.enabled_features().sampler_mip_lod_bias + && mip_lod_bias != 0.0 + { + return Err(ValidationError { + problem: "this device is a portability subset device, and \ + `mip_lod_bias` is not zero" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "sampler_mip_lod_bias", + )])]), + vuids: &["VUID-VkSamplerCreateInfo-samplerMipLodBias-04467"], + ..Default::default() + }); + } + + if let Some(max_anisotropy) = anisotropy { + if !device.enabled_features().sampler_anisotropy { + return Err(ValidationError { + context: "anisotropy".into(), + problem: "is `Some`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "sampler_anisotropy", + )])]), + vuids: &["VUID-VkSamplerCreateInfo-anisotropyEnable-01070"], + }); + } + + if max_anisotropy < 1.0 { + return Err(ValidationError { + context: "anisotropy".into(), + problem: "is less than 1.0".into(), + vuids: &["VUID-VkSamplerCreateInfo-anisotropyEnable-01071"], + ..Default::default() + }); + } + + if max_anisotropy > properties.max_sampler_anisotropy { + return Err(ValidationError { + context: "anisotropy".into(), + problem: "is greater than the `max_sampler_anisotropy` limit".into(), + vuids: &["VUID-VkSamplerCreateInfo-anisotropyEnable-01071"], + ..Default::default() + }); + } + + if [mag_filter, min_filter].contains(&Filter::Cubic) { + return Err(ValidationError { + problem: "`anisotropy` is `Some`, but `mag_filter` or `min_filter` is \ + `Filter::Cubic`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-magFilter-01081"], + ..Default::default() + }); + } + } + + if let Some(compare_op) = compare { + compare_op + .validate_device(device) + .map_err(|err| ValidationError { + context: "compare".into(), + vuids: &["VUID-VkSamplerCreateInfo-compareEnable-01080"], + ..ValidationError::from_requirement(err) + })?; + + if reduction_mode != SamplerReductionMode::WeightedAverage { + return Err(ValidationError { + problem: "`compare` is `Some`, but `reduction_mode` is not \ + `SamplerReductionMode::WeightedAverage`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-compareEnable-01423"], + ..Default::default() + }); + } + } + + if unnormalized_coordinates { + if min_filter != mag_filter { + return Err(ValidationError { + problem: "`unnormalized_coordinates` is `true`, but \ + `min_filter` and `mag_filter` are not equal" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01072"], + ..Default::default() + }); + } + + if mipmap_mode != SamplerMipmapMode::Nearest { + return Err(ValidationError { + problem: "`unnormalized_coordinates` is `true`, but \ + `mipmap_mode` is not `SamplerMipmapMode::Nearest`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01073"], + ..Default::default() + }); + } + + if *lod != (0.0..=0.0) { + return Err(ValidationError { + problem: "`unnormalized_coordinates` is `true`, but \ + `lod` is not `0.0..=1.0`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01074"], + ..Default::default() + }); + } + + if address_mode[0..2].iter().any(|mode| { + !matches!( + mode, + SamplerAddressMode::ClampToEdge | SamplerAddressMode::ClampToBorder + ) + }) { + return Err(ValidationError { + problem: "`unnormalized_coordinates` is `true`, but \ + `address_mode[0]` or `address_mode[1]` are not \ + `SamplerAddressMode::ClampToEdge` or `SamplerAddressMode::ClampToBorder`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01075"], + ..Default::default() + }); + } + + if anisotropy.is_some() { + return Err(ValidationError { + problem: "`unnormalized_coordinates` is `true`, but `anisotropy` is `Some`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01076"], + ..Default::default() + }); + } + + if compare.is_some() { + return Err(ValidationError { + problem: "`unnormalized_coordinates` is `true`, but `compare` is `Some`".into(), + vuids: &["VUID-VkSamplerCreateInfo-unnormalizedCoordinates-0107"], + ..Default::default() + }); + } + } + + if reduction_mode != SamplerReductionMode::WeightedAverage { + if !(device.enabled_features().sampler_filter_minmax + || device.enabled_extensions().ext_sampler_filter_minmax) + { + return Err(ValidationError { + context: "reduction_mode".into(), + problem: "is not `SamplerReductionMode::WeightedAverage`".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::Feature("sampler_filter_minmax")]), + RequiresAllOf(&[Requires::DeviceExtension("ext_sampler_filter_minmax")]), + ]), + ..Default::default() + }); + } + } + + // Don't need to check features because you can't create a conversion object without the + // feature anyway. + if let Some(sampler_ycbcr_conversion) = sampler_ycbcr_conversion { + assert_eq!(device, sampler_ycbcr_conversion.device().as_ref()); + + // Use unchecked, because all validation has been done by the SamplerYcbcrConversion. + let potential_format_features = unsafe { + device + .physical_device() + .format_properties_unchecked(sampler_ycbcr_conversion.format().unwrap()) + .potential_format_features() + }; + + if !potential_format_features.intersects( + FormatFeatures::SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER, + ) && !(mag_filter == sampler_ycbcr_conversion.chroma_filter() + && min_filter == sampler_ycbcr_conversion.chroma_filter()) + { + return Err(ValidationError { + problem: "`sampler_ycbcr_conversion` is `Some`, and \ + the potential format features of `sampler_ycbcr_conversion.format()` \ + do not include `FormatFeatures::\ + SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER`, but \ + `mag_filter` and `min_filter` are not both equal to \ + `sampler_ycbcr_conversion.chroma_filter()`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-minFilter-01645"], + ..Default::default() + }); + } + + if address_mode + .into_iter() + .any(|mode| !matches!(mode, SamplerAddressMode::ClampToEdge)) + { + return Err(ValidationError { + problem: "`sampler_ycbcr_conversion` is `Some`, but \ + not all elements of `address_mode` are \ + `SamplerAddressMode::ClampToEdge`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-addressModeU-01646"], + ..Default::default() + }); + } + + if anisotropy.is_some() { + return Err(ValidationError { + problem: "`sampler_ycbcr_conversion` is `Some`, but \ + `anisotropy` is `Some`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-addressModeU-01646"], + ..Default::default() + }); + } + + if unnormalized_coordinates { + return Err(ValidationError { + problem: "`sampler_ycbcr_conversion` is `Some`, but \ + `unnormalized_coordinates` is `true`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-addressModeU-01646"], + ..Default::default() + }); + } + + if reduction_mode != SamplerReductionMode::WeightedAverage { + return Err(ValidationError { + problem: "`sampler_ycbcr_conversion` is `Some`, but \ + `reduction_mode` is not `SamplerReductionMode::WeightedAverage`" + .into(), + vuids: &["VUID-VkSamplerCreateInfo-None-01647"], + ..Default::default() + }); + } + } + + Ok(()) + } } /// A special value to indicate that the maximum LOD should not be clamped. @@ -1338,6 +1246,36 @@ impl ComponentMapping { }, ] } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), ValidationError> { + let &Self { r, g, b, a } = self; + + r.validate_device(device).map_err(|err| ValidationError { + context: "r".into(), + vuids: &["VUID-VkComponentMapping-r-parameter"], + ..ValidationError::from_requirement(err) + })?; + + g.validate_device(device).map_err(|err| ValidationError { + context: "g".into(), + vuids: &["VUID-VkComponentMapping-g-parameter"], + ..ValidationError::from_requirement(err) + })?; + + b.validate_device(device).map_err(|err| ValidationError { + context: "b".into(), + vuids: &["VUID-VkComponentMapping-b-parameter"], + ..ValidationError::from_requirement(err) + })?; + + a.validate_device(device).map_err(|err| ValidationError { + context: "a".into(), + vuids: &["VUID-VkComponentMapping-a-parameter"], + ..ValidationError::from_requirement(err) + })?; + + Ok(()) + } } impl From for ash::vk::ComponentMapping { @@ -1550,12 +1488,11 @@ vulkan_enum! { #[cfg(test)] mod tests { use crate::{ - pipeline::graphics::depth_stencil::CompareOp, - sampler::{ - Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerCreationError, - SamplerReductionMode, + image::sampler::{ + Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerReductionMode, }, - Requires, RequiresAllOf, RequiresOneOf, + pipeline::graphics::depth_stencil::CompareOp, + Requires, RequiresAllOf, RequiresOneOf, ValidationError, VulkanError, }; #[test] @@ -1633,39 +1570,43 @@ mod tests { fn min_lod_inferior() { let (device, _queue) = gfx_dev_and_queue!(); - assert_should_panic!({ - let _ = Sampler::new( - device, - SamplerCreateInfo { - mag_filter: Filter::Linear, - min_filter: Filter::Linear, - address_mode: [SamplerAddressMode::Repeat; 3], - mip_lod_bias: 1.0, - lod: 5.0..=2.0, - ..Default::default() - }, - ); - }); + if Sampler::new( + device, + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], + mip_lod_bias: 1.0, + lod: 5.0..=2.0, + ..Default::default() + }, + ) + .is_ok() + { + panic!() + } } #[test] fn max_anisotropy() { let (device, _queue) = gfx_dev_and_queue!(); - assert_should_panic!({ - let _ = Sampler::new( - device, - SamplerCreateInfo { - mag_filter: Filter::Linear, - min_filter: Filter::Linear, - address_mode: [SamplerAddressMode::Repeat; 3], - mip_lod_bias: 1.0, - anisotropy: Some(0.5), - lod: 0.0..=2.0, - ..Default::default() - }, - ); - }); + if Sampler::new( + device, + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], + mip_lod_bias: 1.0, + anisotropy: Some(0.5), + lod: 0.0..=2.0, + ..Default::default() + }, + ) + .is_ok() + { + panic!() + } } #[test] @@ -1686,11 +1627,7 @@ mod tests { ); match r { - Err(SamplerCreationError::RequirementNotMet { - requires_one_of: - RequiresOneOf([RequiresAllOf([Requires::Feature("sampler_anisotropy")])]), - .. - }) => (), + Err(VulkanError::ValidationError(_)) => (), _ => panic!(), } } @@ -1713,7 +1650,7 @@ mod tests { ); match r { - Err(SamplerCreationError::MaxSamplerAnisotropyExceeded { .. }) => (), + Err(VulkanError::ValidationError(_)) => (), _ => panic!(), } } @@ -1735,7 +1672,7 @@ mod tests { ); match r { - Err(SamplerCreationError::MaxSamplerLodBiasExceeded { .. }) => (), + Err(VulkanError::ValidationError(_)) => (), _ => panic!(), } } @@ -1757,7 +1694,7 @@ mod tests { ); match r { - Err(SamplerCreationError::RequirementNotMet { + Err(VulkanError::ValidationError(ValidationError { requires_one_of: RequiresOneOf( [RequiresAllOf([Requires::Feature("sampler_mirror_clamp_to_edge")]), RequiresAllOf( @@ -1765,7 +1702,7 @@ mod tests { )], ), .. - }) => {} + })) => {} _ => panic!(), } } @@ -1785,13 +1722,13 @@ mod tests { ); match r { - Err(SamplerCreationError::RequirementNotMet { + Err(VulkanError::ValidationError(ValidationError { requires_one_of: RequiresOneOf( [RequiresAllOf([Requires::Feature("sampler_filter_minmax")]), RequiresAllOf([Requires::DeviceExtension("ext_sampler_filter_minmax")])], ), .. - }) => {} + })) => {} _ => panic!(), } } diff --git a/vulkano/src/sampler/ycbcr.rs b/vulkano/src/image/sampler/ycbcr.rs similarity index 52% rename from vulkano/src/sampler/ycbcr.rs rename to vulkano/src/image/sampler/ycbcr.rs index 8c6cda5d95..4e5640e063 100644 --- a/vulkano/src/sampler/ycbcr.rs +++ b/vulkano/src/image/sampler/ycbcr.rs @@ -29,13 +29,19 @@ //! # let memory_allocator: vulkano::memory::allocator::StandardMemoryAllocator = return; //! # let descriptor_set_allocator: vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator = return; //! # let mut command_buffer_builder: vulkano::command_buffer::AutoCommandBufferBuilder = return; -//! use vulkano::descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet}; -//! use vulkano::descriptor_set::layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType}; +//! use vulkano::descriptor_set::{ +//! layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType}, +//! PersistentDescriptorSet, WriteDescriptorSet, +//! }; //! use vulkano::format::Format; -//! use vulkano::image::{ImmutableImage, ImageCreateFlags, ImageDimensions, ImageUsage, MipmapsCount}; -//! use vulkano::image::view::{ImageView, ImageViewCreateInfo}; -//! use vulkano::sampler::{Sampler, SamplerCreateInfo}; -//! use vulkano::sampler::ycbcr::{SamplerYcbcrConversion, SamplerYcbcrConversionCreateInfo, SamplerYcbcrModelConversion}; +//! use vulkano::image::{ +//! sampler::{ +//! ycbcr::{SamplerYcbcrConversion, SamplerYcbcrConversionCreateInfo, SamplerYcbcrModelConversion}, +//! Sampler, SamplerCreateInfo, +//! }, +//! view::{ImageView, ImageViewCreateInfo}, +//! ImmutableImage, ImageCreateFlags, ImageDimensions, ImageUsage, MipmapsCount, +//! }; //! use vulkano::shader::ShaderStage; //! //! let conversion = SamplerYcbcrConversion::new(device.clone(), SamplerYcbcrConversionCreateInfo { @@ -93,19 +99,12 @@ use crate::{ device::{Device, DeviceOwned}, format::{ChromaSampling, Format, FormatFeatures, NumericType}, + image::sampler::{ComponentMapping, ComponentSwizzle, Filter}, macros::{impl_id_counter, vulkan_enum}, - sampler::{ComponentMapping, ComponentSwizzle, Filter}, - OomError, RequirementNotMet, Requires, RequiresAllOf, RequiresOneOf, RuntimeError, Version, + Requires, RequiresAllOf, RequiresOneOf, RuntimeError, ValidationError, Version, VulkanError, VulkanObject, }; -use std::{ - error::Error, - fmt::{Display, Error as FmtError, Formatter}, - mem::MaybeUninit, - num::NonZeroU64, - ptr, - sync::Arc, -}; +use std::{mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc}; /// Describes how sampled image data should converted from a YCbCr representation to an RGB one. #[derive(Debug)] @@ -128,202 +127,55 @@ impl SamplerYcbcrConversion { /// /// The [`sampler_ycbcr_conversion`](crate::device::Features::sampler_ycbcr_conversion) /// feature must be enabled on the device. + #[inline] pub fn new( device: Arc, create_info: SamplerYcbcrConversionCreateInfo, - ) -> Result, SamplerYcbcrConversionCreationError> { - let SamplerYcbcrConversionCreateInfo { - format, - ycbcr_model, - ycbcr_range, - component_mapping, - chroma_offset, - chroma_filter, - force_explicit_reconstruction, - _ne: _, - } = create_info; + ) -> Result, VulkanError> { + Self::validate_new(&device, &create_info)?; + + unsafe { Ok(Self::new_unchecked(device, create_info)?) } + } + fn validate_new( + device: &Device, + create_info: &SamplerYcbcrConversionCreateInfo, + ) -> Result<(), ValidationError> { if !device.enabled_features().sampler_ycbcr_conversion { - return Err(SamplerYcbcrConversionCreationError::RequirementNotMet { - required_for: "`SamplerYcbcrConversion::new`", + return Err(ValidationError { requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( "sampler_ycbcr_conversion", )])]), + vuids: &["VUID-vkCreateSamplerYcbcrConversion-None-01648"], + ..Default::default() }); } - let format = match format { - Some(f) => f, - None => { - return Err(SamplerYcbcrConversionCreationError::FormatMissing); - } - }; - - // VUID-VkSamplerYcbcrConversionCreateInfo-format-parameter - format.validate_device(&device)?; - - // VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrModel-parameter - ycbcr_model.validate_device(&device)?; - - // VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrRange-parameter - ycbcr_range.validate_device(&device)?; - - // VUID-VkComponentMapping-r-parameter - component_mapping.r.validate_device(&device)?; - - // VUID-VkComponentMapping-g-parameter - component_mapping.g.validate_device(&device)?; - - // VUID-VkComponentMapping-b-parameter - component_mapping.b.validate_device(&device)?; - - // VUID-VkComponentMapping-a-parameter - component_mapping.a.validate_device(&device)?; - - for offset in chroma_offset { - // VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-parameter - // VUID-VkSamplerYcbcrConversionCreateInfo-yChromaOffset-parameter - offset.validate_device(&device)?; - } - - // VUID-VkSamplerYcbcrConversionCreateInfo-chromaFilter-parameter - chroma_filter.validate_device(&device)?; - - // VUID-VkSamplerYcbcrConversionCreateInfo-format-04061 - if !format - .type_color() - .map_or(false, |ty| ty == NumericType::UNORM) - { - return Err(SamplerYcbcrConversionCreationError::FormatNotUnorm); - } - - // Use unchecked, because all validation has been done above. - let potential_format_features = unsafe { - device - .physical_device() - .format_properties_unchecked(format) - .potential_format_features() - }; - - // VUID-VkSamplerYcbcrConversionCreateInfo-format-01650 - if !potential_format_features.intersects( - FormatFeatures::MIDPOINT_CHROMA_SAMPLES | FormatFeatures::COSITED_CHROMA_SAMPLES, - ) { - return Err(SamplerYcbcrConversionCreationError::FormatNotSupported); - } - - if let Some(chroma_sampling @ (ChromaSampling::Mode422 | ChromaSampling::Mode420)) = - format.ycbcr_chroma_sampling() - { - let chroma_offsets_to_check = match chroma_sampling { - ChromaSampling::Mode420 => &chroma_offset[0..2], - ChromaSampling::Mode422 => &chroma_offset[0..1], - _ => unreachable!(), - }; - - for offset in chroma_offsets_to_check { - match offset { - ChromaLocation::CositedEven => { - // VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-01651 - if !potential_format_features - .intersects(FormatFeatures::COSITED_CHROMA_SAMPLES) - { - return Err( - SamplerYcbcrConversionCreationError::FormatChromaOffsetNotSupported, - ); - } - } - ChromaLocation::Midpoint => { - // VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-01652 - if !potential_format_features - .intersects(FormatFeatures::MIDPOINT_CHROMA_SAMPLES) - { - return Err( - SamplerYcbcrConversionCreationError::FormatChromaOffsetNotSupported, - ); - } - } - } - } - - // VUID-VkSamplerYcbcrConversionCreateInfo-components-02581 - let g_ok = component_mapping.g_is_identity(); - - // VUID-VkSamplerYcbcrConversionCreateInfo-components-02582 - let a_ok = component_mapping.a_is_identity() - || matches!( - component_mapping.a, - ComponentSwizzle::One | ComponentSwizzle::Zero - ); - - // VUID-VkSamplerYcbcrConversionCreateInfo-components-02583 - // VUID-VkSamplerYcbcrConversionCreateInfo-components-02584 - // VUID-VkSamplerYcbcrConversionCreateInfo-components-02585 - let rb_ok1 = component_mapping.r_is_identity() && component_mapping.b_is_identity(); - let rb_ok2 = matches!(component_mapping.r, ComponentSwizzle::Blue) - && matches!(component_mapping.b, ComponentSwizzle::Red); - - if !(g_ok && a_ok && (rb_ok1 || rb_ok2)) { - return Err(SamplerYcbcrConversionCreationError::FormatInvalidComponentMapping); - } - } - - let components_bits = { - let bits = format.components(); - component_mapping - .component_map() - .map(move |i| i.map(|i| bits[i])) - }; - - // VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrModel-01655 - if ycbcr_model != SamplerYcbcrModelConversion::RgbIdentity - && !components_bits[0..3] - .iter() - .all(|b| b.map_or(false, |b| b != 0)) - { - return Err(SamplerYcbcrConversionCreationError::YcbcrModelInvalidComponentMapping); - } + create_info + .validate(device) + .map_err(|err| err.add_context("create_info"))?; - // VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrRange-02748 - if ycbcr_range == SamplerYcbcrRange::ItuNarrow { - // TODO: Spec doesn't say how many bits `Zero` and `One` are considered to have, so - // just skip them for now. - for &bits in components_bits[0..3].iter().flatten() { - if bits < 8 { - return Err(SamplerYcbcrConversionCreationError::YcbcrRangeFormatNotEnoughBits); - } - } - } - - // VUID-VkSamplerYcbcrConversionCreateInfo-forceExplicitReconstruction-01656 - if force_explicit_reconstruction - && !potential_format_features.intersects(FormatFeatures:: - SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE) - { - return Err( - SamplerYcbcrConversionCreationError::FormatForceExplicitReconstructionNotSupported, - ); - } + Ok(()) + } - match chroma_filter { - Filter::Nearest => (), - Filter::Linear => { - // VUID-VkSamplerYcbcrConversionCreateInfo-chromaFilter-01657 - if !potential_format_features - .intersects(FormatFeatures::SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER) - { - return Err( - SamplerYcbcrConversionCreationError::FormatLinearFilterNotSupported, - ); - } - } - Filter::Cubic => { - return Err(SamplerYcbcrConversionCreationError::CubicFilterNotSupported); - } - } + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn new_unchecked( + device: Arc, + create_info: SamplerYcbcrConversionCreateInfo, + ) -> Result, RuntimeError> { + let &SamplerYcbcrConversionCreateInfo { + format, + ycbcr_model, + ycbcr_range, + component_mapping, + chroma_offset, + chroma_filter, + force_explicit_reconstruction, + _ne: _, + } = &create_info; - let create_info = ash::vk::SamplerYcbcrConversionCreateInfo { - format: format.into(), + let create_info_vk = ash::vk::SamplerYcbcrConversionCreateInfo { + format: format.unwrap().into(), ycbcr_model: ycbcr_model.into(), ycbcr_range: ycbcr_range.into(), components: component_mapping.into(), @@ -346,7 +198,7 @@ impl SamplerYcbcrConversion { let mut output = MaybeUninit::uninit(); create_sampler_ycbcr_conversion( device.handle(), - &create_info, + &create_info_vk, ptr::null(), output.as_mut_ptr(), ) @@ -355,18 +207,7 @@ impl SamplerYcbcrConversion { output.assume_init() }; - Ok(Arc::new(SamplerYcbcrConversion { - handle, - device, - id: Self::next_id(), - format: Some(format), - ycbcr_model, - ycbcr_range, - component_mapping, - chroma_offset, - chroma_filter, - force_explicit_reconstruction, - })) + Ok(Self::from_handle(device, handle, create_info)) } /// Creates a new `SamplerYcbcrConversion` from a raw object handle. @@ -375,7 +216,6 @@ impl SamplerYcbcrConversion { /// /// - `handle` must be a valid Vulkan object handle created from `device`. /// - `create_info` must match the info used to create the object. - /// - `create_info.format` must be `Some`. #[inline] pub unsafe fn from_handle( device: Arc, @@ -512,138 +352,6 @@ unsafe impl DeviceOwned for SamplerYcbcrConversion { impl_id_counter!(SamplerYcbcrConversion); -/// Error that can happen when creating a `SamplerYcbcrConversion`. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SamplerYcbcrConversionCreationError { - /// Not enough memory. - OomError(OomError), - - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// The `Cubic` filter was specified. - CubicFilterNotSupported, - - /// No format was specified when one was required. - FormatMissing, - - /// The format has a color type other than `UNORM`. - FormatNotUnorm, - - /// The format does not support sampler YCbCr conversion. - FormatNotSupported, - - /// The format does not support the chosen chroma offsets. - FormatChromaOffsetNotSupported, - - /// The component mapping was not valid for use with the chosen format. - FormatInvalidComponentMapping, - - /// The format does not support `force_explicit_reconstruction`. - FormatForceExplicitReconstructionNotSupported, - - /// The format does not support the `Linear` filter. - FormatLinearFilterNotSupported, - - /// The component mapping was not valid for use with the chosen YCbCr model. - YcbcrModelInvalidComponentMapping, - - /// For the chosen `ycbcr_range`, the R, G or B components being read from the `format` do not - /// have the minimum number of required bits. - YcbcrRangeFormatNotEnoughBits, -} - -impl Error for SamplerYcbcrConversionCreationError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - SamplerYcbcrConversionCreationError::OomError(err) => Some(err), - _ => None, - } - } -} - -impl Display for SamplerYcbcrConversionCreationError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::OomError(_) => write!(f, "not enough memory available"), - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::CubicFilterNotSupported => { - write!(f, "the `Cubic` filter was specified") - } - Self::FormatMissing => { - write!(f, "no format was specified when one was required") - } - Self::FormatNotUnorm => { - write!(f, "the format has a color type other than `UNORM`") - } - Self::FormatNotSupported => { - write!(f, "the format does not support sampler YCbCr conversion") - } - Self::FormatChromaOffsetNotSupported => { - write!(f, "the format does not support the chosen chroma offsets") - } - Self::FormatInvalidComponentMapping => write!( - f, - "the component mapping was not valid for use with the chosen format", - ), - Self::FormatForceExplicitReconstructionNotSupported => write!( - f, - "the format does not support `force_explicit_reconstruction`", - ), - Self::FormatLinearFilterNotSupported => { - write!(f, "the format does not support the `Linear` filter") - } - Self::YcbcrModelInvalidComponentMapping => write!( - f, - "the component mapping was not valid for use with the chosen YCbCr model", - ), - Self::YcbcrRangeFormatNotEnoughBits => write!( - f, - "for the chosen `ycbcr_range`, the R, G or B components being read from the \ - `format` do not have the minimum number of required bits", - ), - } - } -} - -impl From for SamplerYcbcrConversionCreationError { - fn from(err: OomError) -> SamplerYcbcrConversionCreationError { - SamplerYcbcrConversionCreationError::OomError(err) - } -} - -impl From for SamplerYcbcrConversionCreationError { - fn from(err: RuntimeError) -> SamplerYcbcrConversionCreationError { - match err { - err @ RuntimeError::OutOfHostMemory => { - SamplerYcbcrConversionCreationError::OomError(OomError::from(err)) - } - err @ RuntimeError::OutOfDeviceMemory => { - SamplerYcbcrConversionCreationError::OomError(OomError::from(err)) - } - _ => panic!("unexpected error: {:?}", err), - } - } -} - -impl From for SamplerYcbcrConversionCreationError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, - } - } -} - /// Parameters to create a new `SamplerYcbcrConversion`. #[derive(Clone, Debug)] pub struct SamplerYcbcrConversionCreateInfo { @@ -733,6 +441,411 @@ impl Default for SamplerYcbcrConversionCreateInfo { } } +impl SamplerYcbcrConversionCreateInfo { + pub(crate) fn validate(&self, device: &Device) -> Result<(), ValidationError> { + let &Self { + format, + ycbcr_model, + ycbcr_range, + component_mapping, + chroma_offset, + chroma_filter, + force_explicit_reconstruction, + _ne: _, + } = self; + + let format = format.ok_or(ValidationError { + context: "format".into(), + problem: "is `None`".into(), + ..Default::default() + })?; + + format + .validate_device(device) + .map_err(|err| ValidationError { + context: "format".into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-format-parameter"], + ..ValidationError::from_requirement(err) + })?; + + ycbcr_model + .validate_device(device) + .map_err(|err| ValidationError { + context: "ycbcr_model".into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrModel-parameter"], + ..ValidationError::from_requirement(err) + })?; + + ycbcr_range + .validate_device(device) + .map_err(|err| ValidationError { + context: "ycbcr_range".into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrRange-parameter"], + ..ValidationError::from_requirement(err) + })?; + + component_mapping + .validate(device) + .map_err(|err| err.add_context("component_mapping"))?; + + for (index, offset) in chroma_offset.into_iter().enumerate() { + offset + .validate_device(device) + .map_err(|err| ValidationError { + context: format!("chroma_offset[{}]", index).into(), + vuids: &[ + "VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-parameter", + "VUID-VkSamplerYcbcrConversionCreateInfo-yChromaOffset-parameter", + ], + ..ValidationError::from_requirement(err) + })?; + } + + chroma_filter + .validate_device(device) + .map_err(|err| ValidationError { + context: "chroma_filter".into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-chromaFilter-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if !format + .type_color() + .map_or(false, |ty| ty == NumericType::UNORM) + { + return Err(ValidationError { + context: "format".into(), + problem: "the numeric type is not `UNORM`".into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-format-04061"], + ..Default::default() + }); + } + + // Use unchecked, because all validation has been done above. + let potential_format_features = unsafe { + device + .physical_device() + .format_properties_unchecked(format) + .potential_format_features() + }; + + if !potential_format_features.intersects( + FormatFeatures::MIDPOINT_CHROMA_SAMPLES | FormatFeatures::COSITED_CHROMA_SAMPLES, + ) { + return Err(ValidationError { + context: "format".into(), + problem: "the potential format features do not contain \ + `FormatFeatures::MIDPOINT_CHROMA_SAMPLES` or \ + `FormatFeatures::COSITED_CHROMA_SAMPLES`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-format-01650"], + ..Default::default() + }); + } + + if let Some(chroma_sampling @ (ChromaSampling::Mode422 | ChromaSampling::Mode420)) = + format.ycbcr_chroma_sampling() + { + match chroma_sampling { + ChromaSampling::Mode420 => { + if chroma_offset.contains(&ChromaLocation::CositedEven) + && !potential_format_features + .intersects(FormatFeatures::COSITED_CHROMA_SAMPLES) + { + return Err(ValidationError { + problem: "`format` has both horizontal and vertical chroma \ + subsampling, and \ + its potential format features do not \ + contain `FormatFeatures::COSITED_CHROMA_SAMPLES`, but \ + `chroma_offset[0]` or `chroma_offset[1]` are \ + `ChromaLocation::CositedEven`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-01651"], + ..Default::default() + }); + } + + if chroma_offset.contains(&ChromaLocation::Midpoint) + && !potential_format_features + .intersects(FormatFeatures::MIDPOINT_CHROMA_SAMPLES) + { + return Err(ValidationError { + problem: "`format` has both horizontal and vertical chroma \ + subsampling, and \ + its potential format features do not \ + contain `FormatFeatures::MIDPOINT_CHROMA_SAMPLES`, but \ + `chroma_offset[0]` or `chroma_offset[1]` are \ + `ChromaLocation::Midpoint`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-01652"], + ..Default::default() + }); + } + } + ChromaSampling::Mode422 => { + if chroma_offset[0] == ChromaLocation::CositedEven + && !potential_format_features + .intersects(FormatFeatures::COSITED_CHROMA_SAMPLES) + { + return Err(ValidationError { + problem: "`format` has horizontal chroma subsampling, and \ + its potential format features do not \ + contain `FormatFeatures::COSITED_CHROMA_SAMPLES`, but \ + `chroma_offset[0]` is \ + `ChromaLocation::CositedEven`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-01651"], + ..Default::default() + }); + } + + if chroma_offset[0] == ChromaLocation::Midpoint + && !potential_format_features + .intersects(FormatFeatures::MIDPOINT_CHROMA_SAMPLES) + { + return Err(ValidationError { + problem: "`format` has horizontal chroma subsampling, and \ + its potential format features do not \ + contain `FormatFeatures::MIDPOINT_CHROMA_SAMPLES`, but \ + `chroma_offset[0]` is \ + `ChromaLocation::Midpoint`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-01652"], + ..Default::default() + }); + } + } + _ => unreachable!(), + } + + if !component_mapping.g_is_identity() { + return Err(ValidationError { + problem: "`format` has chroma subsampling, but \ + `component_mapping.g` is not identity swizzled" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-components-02581"], + ..Default::default() + }); + } + + if !(component_mapping.a_is_identity() + || matches!( + component_mapping.a, + ComponentSwizzle::One | ComponentSwizzle::Zero + )) + { + return Err(ValidationError { + context: "component_mapping.a".into(), + problem: "`format` has chroma subsampling, but \ + `component_mapping.a` is not identity swizzled, or \ + `ComponentSwizzle::One` or `ComponentSwizzle::Zero`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-components-02582"], + ..Default::default() + }); + } + + if !(component_mapping.r_is_identity() + || matches!(component_mapping.r, ComponentSwizzle::Blue)) + { + return Err(ValidationError { + problem: "`format` has chroma subsampling, but \ + `component_mapping.r` is not identity swizzled, or \ + `ComponentSwizzle::Blue`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-components-02583"], + ..Default::default() + }); + } + + if !(component_mapping.b_is_identity() + || matches!(component_mapping.b, ComponentSwizzle::Red)) + { + return Err(ValidationError { + problem: "`format` has chroma subsampling, but \ + `component_mapping.b` is not identity swizzled, or \ + `ComponentSwizzle::Red`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-components-02584"], + ..Default::default() + }); + } + + match ( + component_mapping.r_is_identity(), + component_mapping.b_is_identity(), + ) { + (true, false) => { + return Err(ValidationError { + problem: "`format` has chroma subsampling, and \ + `component_mapping.r` is identity swizzled, but \ + `component_mapping.b` is not identity swizzled" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-components-02585"], + ..Default::default() + }); + } + (false, true) => { + return Err(ValidationError { + problem: "`format` has chroma subsampling, and \ + `component_mapping.b` is identity swizzled, but \ + `component_mapping.r` is not identity swizzled" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-components-02585"], + ..Default::default() + }); + } + _ => (), + } + } + + let components_bits = { + let bits = format.components(); + component_mapping + .component_map() + .map(move |i| i.map(|i| bits[i])) + }; + + // VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrModel-01655 + if ycbcr_model != SamplerYcbcrModelConversion::RgbIdentity { + if components_bits[0].map_or(true, |bits| bits == 0) { + return Err(ValidationError { + problem: "`ycbcr_model` is not `SamplerYcbcrModelConversion::RgbIdentity`, \ + and `component_mapping.r` does not map to a component that exists in \ + `format`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-components-02585"], + ..Default::default() + }); + } + + if components_bits[1].map_or(true, |bits| bits == 0) { + return Err(ValidationError { + problem: "`ycbcr_model` is not `SamplerYcbcrModelConversion::RgbIdentity`, \ + and `component_mapping.g` does not map to a component that exists in \ + `format`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-components-02585"], + ..Default::default() + }); + } + + if components_bits[2].map_or(true, |bits| bits == 0) { + return Err(ValidationError { + problem: "`ycbcr_model` is not `SamplerYcbcrModelConversion::RgbIdentity`, \ + and `component_mapping.b` does not map to a component that exists in \ + `format`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-components-02585"], + ..Default::default() + }); + } + + if components_bits[3].map_or(true, |bits| bits == 0) { + return Err(ValidationError { + problem: "`ycbcr_model` is not `SamplerYcbcrModelConversion::RgbIdentity`, \ + and `component_mapping.a` does not map to a component that exists in \ + `format`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-components-02585"], + ..Default::default() + }); + } + } + + if ycbcr_range == SamplerYcbcrRange::ItuNarrow { + // TODO: Spec doesn't say how many bits `Zero` and `One` are considered to have, so + // just skip them for now. + + if components_bits[0].map_or(false, |bits| bits < 8) { + return Err(ValidationError { + problem: "`ycbcr_range` is `SamplerYcbcrRange::ItuNarrow`, and \ + `component_mapping.r` maps to a component in `format` with less than \ + 8 bits" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrRange-02748"], + ..Default::default() + }); + } + + if components_bits[1].map_or(false, |bits| bits < 8) { + return Err(ValidationError { + problem: "`ycbcr_range` is `SamplerYcbcrRange::ItuNarrow`, and \ + `component_mapping.g` maps to a component in `format` with less than \ + 8 bits" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrRange-02748"], + ..Default::default() + }); + } + + if components_bits[2].map_or(false, |bits| bits < 8) { + return Err(ValidationError { + problem: "`ycbcr_range` is `SamplerYcbcrRange::ItuNarrow`, and \ + `component_mapping.b` maps to a component in `format` with less than \ + 8 bits" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrRange-02748"], + ..Default::default() + }); + } + + if components_bits[3].map_or(false, |bits| bits < 8) { + return Err(ValidationError { + problem: "`ycbcr_range` is `SamplerYcbcrRange::ItuNarrow`, and \ + `component_mapping.a` maps to a component in `format` with less than \ + 8 bits" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-ycbcrRange-02748"], + ..Default::default() + }); + } + } + + if force_explicit_reconstruction + && !potential_format_features.intersects(FormatFeatures:: + SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE) + { + return Err(ValidationError { + problem: "`force_explicit_reconstruction` is `true`, but the \ + potential format features of `format` do not include `FormatFeatures::\ + SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-forceExplicitReconstruction-01656"], + ..Default::default() + }); + } + + match chroma_filter { + Filter::Nearest => (), + Filter::Linear => { + if !potential_format_features + .intersects(FormatFeatures::SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER) + { + return Err(ValidationError { + problem: "`chroma_filter` is `Filter::Linear`, but the \ + potential format features of `format` do not include `FormatFeatures::\ + SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER`" + .into(), + vuids: &["VUID-VkSamplerYcbcrConversionCreateInfo-chromaFilter-01657"], + ..Default::default() + }); + } + } + Filter::Cubic => { + return Err(ValidationError { + context: "chroma_filter".into(), + problem: "is `Filter::Cubic`".into(), + // vuids? + ..Default::default() + }); + } + } + + Ok(()) + } +} + vulkan_enum! { #[non_exhaustive] @@ -790,8 +903,8 @@ vulkan_enum! { #[cfg(test)] mod tests { - use super::{SamplerYcbcrConversion, SamplerYcbcrConversionCreationError}; - use crate::{Requires, RequiresAllOf, RequiresOneOf}; + use super::SamplerYcbcrConversion; + use crate::{Requires, RequiresAllOf, RequiresOneOf, ValidationError, VulkanError}; #[test] fn feature_not_enabled() { @@ -800,11 +913,11 @@ mod tests { let r = SamplerYcbcrConversion::new(device, Default::default()); match r { - Err(SamplerYcbcrConversionCreationError::RequirementNotMet { + Err(VulkanError::ValidationError(ValidationError { requires_one_of: RequiresOneOf([RequiresAllOf([Requires::Feature("sampler_ycbcr_conversion")])]), .. - }) => (), + })) => (), _ => panic!(), } } diff --git a/vulkano/src/image/view.rs b/vulkano/src/image/view.rs index ac17568129..cb27de9a27 100644 --- a/vulkano/src/image/view.rs +++ b/vulkano/src/image/view.rs @@ -19,9 +19,11 @@ use super::{ use crate::{ device::{Device, DeviceOwned}, format::{ChromaSampling, Format, FormatFeatures}, - image::{ImageAspects, ImageCreateFlags, ImageTiling, ImageType, SampleCount}, + image::{ + sampler::{ycbcr::SamplerYcbcrConversion, ComponentMapping}, + ImageAspects, ImageCreateFlags, ImageTiling, ImageType, SampleCount, + }, macros::{impl_id_counter, vulkan_enum}, - sampler::{ycbcr::SamplerYcbcrConversion, ComponentMapping}, OomError, RequirementNotMet, Requires, RequiresAllOf, RequiresOneOf, RuntimeError, Version, VulkanObject, }; diff --git a/vulkano/src/lib.rs b/vulkano/src/lib.rs index 6a96892d57..62e96fc98d 100644 --- a/vulkano/src/lib.rs +++ b/vulkano/src/lib.rs @@ -193,7 +193,6 @@ pub mod pipeline; pub mod query; mod range_map; pub mod range_set; -pub mod sampler; pub mod shader; pub mod swapchain; pub mod sync;