1 // Copyright (c) 2016 The vulkano developers 2 // Licensed under the Apache License, Version 2.0 3 // <LICENSE-APACHE or 4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT 5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, 6 // at your option. All files in the project carrying such 7 // notice may not be copied, modified, or distributed except 8 // according to those terms. 9 10 //! How to retrieve data from a sampled image within a shader. 11 //! 12 //! When you retrieve data from a sampled image, you have to pass the coordinates of the pixel you 13 //! want to retrieve. The implementation then performs various calculations, and these operations 14 //! are what the `Sampler` object describes. 15 //! 16 //! # Level of detail 17 //! 18 //! The level-of-detail (LOD) is a floating-point value that expresses a sense of how much texture 19 //! detail is visible to the viewer. It is used in texture filtering and mipmapping calculations. 20 //! 21 //! LOD is calculated through one or more steps to produce a final value. The base LOD is 22 //! determined by one of two ways: 23 //! - Implicitly, by letting Vulkan calculate it automatically, based on factors such as number of 24 //! pixels, distance and viewing angle. This is done using an `ImplicitLod` SPIR-V sampling 25 //! operation, which corresponds to the `texture*` functions not suffixed with `Lod` in GLSL. 26 //! - Explicitly, specified in the shader. This is done using an `ExplicitLod` SPIR-V sampling 27 //! operation, which corresponds to the `texture*Lod` functions in GLSL. 28 //! 29 //! It is possible to provide a *bias* to the base LOD value, which is simply added to it. 30 //! An LOD bias can be provided both in the sampler object and as part of the sampling operation in 31 //! the shader, and are combined by addition to produce the final bias value, which is then added to 32 //! the base LOD. 33 //! 34 //! Once LOD bias has been applied, the resulting value may be *clamped* to a minimum and maximum 35 //! value to provide the final LOD. A maximum may be specified by the sampler, while a minimum 36 //! can be specified by the sampler or the shader sampling operation. 37 //! 38 //! # Texel filtering 39 //! 40 //! Texel filtering operations determine how the color value to be sampled from each mipmap is 41 //! calculated. The filtering mode can be set independently for different signs of the LOD value: 42 //! - Negative or zero: **magnification**. The rendered object is closer to the viewer, and each 43 //! pixel in the texture corresponds to exactly one or more than one framebuffer pixel. 44 //! - Positive: **minification**. The rendered object is further from the viewer, and each pixel in 45 //! the texture corresponds to less than one framebuffer pixel. 46 47 pub mod ycbcr; 48 49 use self::ycbcr::SamplerYcbcrConversion; 50 use crate::{ 51 device::{Device, DeviceOwned}, 52 format::FormatFeatures, 53 image::{view::ImageViewType, ImageAspects, ImageViewAbstract}, 54 macros::{impl_id_counter, vulkan_enum}, 55 pipeline::graphics::depth_stencil::CompareOp, 56 shader::ShaderScalarType, 57 OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject, 58 }; 59 use std::{ 60 error::Error, 61 fmt::{Display, Error as FmtError, Formatter}, 62 mem::MaybeUninit, 63 num::NonZeroU64, 64 ops::RangeInclusive, 65 ptr, 66 sync::Arc, 67 }; 68 69 /// Describes how to retrieve data from a sampled image within a shader. 70 /// 71 /// # Examples 72 /// 73 /// A simple sampler for most usages: 74 /// 75 /// ``` 76 /// use vulkano::sampler::{Sampler, SamplerCreateInfo}; 77 /// 78 /// # let device: std::sync::Arc<vulkano::device::Device> = return; 79 /// let _sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear_no_mipmap()); 80 /// ``` 81 /// 82 /// More detailed sampler creation: 83 /// 84 /// ``` 85 /// use vulkano::sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}; 86 /// 87 /// # let device: std::sync::Arc<vulkano::device::Device> = return; 88 /// let _sampler = Sampler::new(device.clone(), SamplerCreateInfo { 89 /// mag_filter: Filter::Linear, 90 /// min_filter: Filter::Linear, 91 /// address_mode: [SamplerAddressMode::Repeat; 3], 92 /// mip_lod_bias: 1.0, 93 /// lod: 0.0..=100.0, 94 /// ..Default::default() 95 /// }) 96 /// .unwrap(); 97 /// ``` 98 #[derive(Debug)] 99 pub struct Sampler { 100 handle: ash::vk::Sampler, 101 device: Arc<Device>, 102 id: NonZeroU64, 103 104 address_mode: [SamplerAddressMode; 3], 105 anisotropy: Option<f32>, 106 border_color: Option<BorderColor>, 107 compare: Option<CompareOp>, 108 lod: RangeInclusive<f32>, 109 mag_filter: Filter, 110 min_filter: Filter, 111 mip_lod_bias: f32, 112 mipmap_mode: SamplerMipmapMode, 113 reduction_mode: SamplerReductionMode, 114 sampler_ycbcr_conversion: Option<Arc<SamplerYcbcrConversion>>, 115 unnormalized_coordinates: bool, 116 } 117 118 impl Sampler { 119 /// Creates a new `Sampler`. 120 /// 121 /// # Panics 122 /// 123 /// - Panics if `create_info.anisotropy` is `Some` and contains a value less than 1.0. 124 /// - Panics if `create_info.lod` is empty. new( device: Arc<Device>, create_info: SamplerCreateInfo, ) -> Result<Arc<Sampler>, SamplerCreationError>125 pub fn new( 126 device: Arc<Device>, 127 create_info: SamplerCreateInfo, 128 ) -> Result<Arc<Sampler>, SamplerCreationError> { 129 let SamplerCreateInfo { 130 mag_filter, 131 min_filter, 132 mipmap_mode, 133 address_mode, 134 mip_lod_bias, 135 anisotropy, 136 compare, 137 lod, 138 border_color, 139 unnormalized_coordinates, 140 reduction_mode, 141 sampler_ycbcr_conversion, 142 _ne: _, 143 } = create_info; 144 145 for filter in [mag_filter, min_filter] { 146 // VUID-VkSamplerCreateInfo-magFilter-parameter 147 // VUID-VkSamplerCreateInfo-minFilter-parameter 148 filter.validate_device(&device)?; 149 } 150 151 // VUID-VkSamplerCreateInfo-mipmapMode-parameter 152 mipmap_mode.validate_device(&device)?; 153 154 for mode in address_mode { 155 // VUID-VkSamplerCreateInfo-addressModeU-parameter 156 // VUID-VkSamplerCreateInfo-addressModeV-parameter 157 // VUID-VkSamplerCreateInfo-addressModeW-parameter 158 mode.validate_device(&device)?; 159 160 if mode == SamplerAddressMode::ClampToBorder { 161 // VUID-VkSamplerCreateInfo-addressModeU-01078 162 border_color.validate_device(&device)?; 163 } 164 } 165 166 if address_mode.contains(&SamplerAddressMode::MirrorClampToEdge) { 167 if !device.enabled_features().sampler_mirror_clamp_to_edge 168 && !device.enabled_extensions().khr_sampler_mirror_clamp_to_edge 169 { 170 return Err(SamplerCreationError::RequirementNotMet { 171 required_for: "`create_info.address_mode` contains \ 172 `SamplerAddressMode::MirrorClampToEdge`", 173 requires_one_of: RequiresOneOf { 174 features: &["sampler_mirror_clamp_to_edge"], 175 device_extensions: &["khr_sampler_mirror_clamp_to_edge"], 176 ..Default::default() 177 }, 178 }); 179 } 180 } 181 182 { 183 assert!(!lod.is_empty()); 184 let limit = device.physical_device().properties().max_sampler_lod_bias; 185 if mip_lod_bias.abs() > limit { 186 return Err(SamplerCreationError::MaxSamplerLodBiasExceeded { 187 requested: mip_lod_bias, 188 maximum: limit, 189 }); 190 } 191 } 192 193 // VUID-VkSamplerCreateInfo-samplerMipLodBias-04467 194 if device.enabled_extensions().khr_portability_subset 195 && !device.enabled_features().sampler_mip_lod_bias 196 && mip_lod_bias != 0.0 197 { 198 return Err(SamplerCreationError::RequirementNotMet { 199 required_for: "this device is a portability subset device, and \ 200 `create_info.mip_lod_bias` is not zero", 201 requires_one_of: RequiresOneOf { 202 features: &["sampler_mip_lod_bias"], 203 ..Default::default() 204 }, 205 }); 206 } 207 208 let (anisotropy_enable, max_anisotropy) = if let Some(max_anisotropy) = anisotropy { 209 assert!(max_anisotropy >= 1.0); 210 211 if !device.enabled_features().sampler_anisotropy { 212 return Err(SamplerCreationError::RequirementNotMet { 213 required_for: "`create_info.anisotropy` is `Some`", 214 requires_one_of: RequiresOneOf { 215 features: &["sampler_anisotropy"], 216 ..Default::default() 217 }, 218 }); 219 } 220 221 let limit = device.physical_device().properties().max_sampler_anisotropy; 222 if max_anisotropy > limit { 223 return Err(SamplerCreationError::MaxSamplerAnisotropyExceeded { 224 requested: max_anisotropy, 225 maximum: limit, 226 }); 227 } 228 229 if [mag_filter, min_filter] 230 .into_iter() 231 .any(|filter| filter == Filter::Cubic) 232 { 233 return Err(SamplerCreationError::AnisotropyInvalidFilter { 234 mag_filter, 235 min_filter, 236 }); 237 } 238 239 (ash::vk::TRUE, max_anisotropy) 240 } else { 241 (ash::vk::FALSE, 1.0) 242 }; 243 244 let (compare_enable, compare_op) = if let Some(compare_op) = compare { 245 // VUID-VkSamplerCreateInfo-compareEnable-01080 246 compare_op.validate_device(&device)?; 247 248 if reduction_mode != SamplerReductionMode::WeightedAverage { 249 return Err(SamplerCreationError::CompareInvalidReductionMode { reduction_mode }); 250 } 251 252 (ash::vk::TRUE, compare_op) 253 } else { 254 (ash::vk::FALSE, CompareOp::Never) 255 }; 256 257 if unnormalized_coordinates { 258 if min_filter != mag_filter { 259 return Err( 260 SamplerCreationError::UnnormalizedCoordinatesFiltersNotEqual { 261 mag_filter, 262 min_filter, 263 }, 264 ); 265 } 266 267 if mipmap_mode != SamplerMipmapMode::Nearest { 268 return Err( 269 SamplerCreationError::UnnormalizedCoordinatesInvalidMipmapMode { mipmap_mode }, 270 ); 271 } 272 273 if lod != (0.0..=0.0) { 274 return Err(SamplerCreationError::UnnormalizedCoordinatesNonzeroLod { lod }); 275 } 276 277 if address_mode[0..2].iter().any(|mode| { 278 !matches!( 279 mode, 280 SamplerAddressMode::ClampToEdge | SamplerAddressMode::ClampToBorder 281 ) 282 }) { 283 return Err( 284 SamplerCreationError::UnnormalizedCoordinatesInvalidAddressMode { 285 address_mode: [address_mode[0], address_mode[1]], 286 }, 287 ); 288 } 289 290 if anisotropy.is_some() { 291 return Err(SamplerCreationError::UnnormalizedCoordinatesAnisotropyEnabled); 292 } 293 294 if compare.is_some() { 295 return Err(SamplerCreationError::UnnormalizedCoordinatesCompareEnabled); 296 } 297 } 298 299 let mut sampler_reduction_mode_create_info = 300 if reduction_mode != SamplerReductionMode::WeightedAverage { 301 if !(device.enabled_features().sampler_filter_minmax 302 || device.enabled_extensions().ext_sampler_filter_minmax) 303 { 304 return Err(SamplerCreationError::RequirementNotMet { 305 required_for: "`create_info.reduction_mode` is not \ 306 `SamplerReductionMode::WeightedAverage`", 307 requires_one_of: RequiresOneOf { 308 features: &["sampler_filter_minmax"], 309 device_extensions: &["ext_sampler_filter_minmax"], 310 ..Default::default() 311 }, 312 }); 313 } 314 315 // VUID-VkSamplerReductionModeCreateInfo-reductionMode-parameter 316 reduction_mode.validate_device(&device)?; 317 318 Some(ash::vk::SamplerReductionModeCreateInfo { 319 reduction_mode: reduction_mode.into(), 320 ..Default::default() 321 }) 322 } else { 323 None 324 }; 325 326 // Don't need to check features because you can't create a conversion object without the 327 // feature anyway. 328 let mut sampler_ycbcr_conversion_info = if let Some(sampler_ycbcr_conversion) = 329 &sampler_ycbcr_conversion 330 { 331 assert_eq!(&device, sampler_ycbcr_conversion.device()); 332 333 // Use unchecked, because all validation has been done by the SamplerYcbcrConversion. 334 let potential_format_features = unsafe { 335 device 336 .physical_device() 337 .format_properties_unchecked(sampler_ycbcr_conversion.format().unwrap()) 338 .potential_format_features() 339 }; 340 341 // VUID-VkSamplerCreateInfo-minFilter-01645 342 if !potential_format_features.intersects( 343 FormatFeatures::SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER, 344 ) && !(mag_filter == sampler_ycbcr_conversion.chroma_filter() 345 && min_filter == sampler_ycbcr_conversion.chroma_filter()) 346 { 347 return Err( 348 SamplerCreationError::SamplerYcbcrConversionChromaFilterMismatch { 349 chroma_filter: sampler_ycbcr_conversion.chroma_filter(), 350 mag_filter, 351 min_filter, 352 }, 353 ); 354 } 355 356 // VUID-VkSamplerCreateInfo-addressModeU-01646 357 if address_mode 358 .into_iter() 359 .any(|mode| !matches!(mode, SamplerAddressMode::ClampToEdge)) 360 { 361 return Err( 362 SamplerCreationError::SamplerYcbcrConversionInvalidAddressMode { address_mode }, 363 ); 364 } 365 366 // VUID-VkSamplerCreateInfo-addressModeU-01646 367 if anisotropy.is_some() { 368 return Err(SamplerCreationError::SamplerYcbcrConversionAnisotropyEnabled); 369 } 370 371 // VUID-VkSamplerCreateInfo-addressModeU-01646 372 if unnormalized_coordinates { 373 return Err( 374 SamplerCreationError::SamplerYcbcrConversionUnnormalizedCoordinatesEnabled, 375 ); 376 } 377 378 // VUID-VkSamplerCreateInfo-None-01647 379 if reduction_mode != SamplerReductionMode::WeightedAverage { 380 return Err( 381 SamplerCreationError::SamplerYcbcrConversionInvalidReductionMode { 382 reduction_mode, 383 }, 384 ); 385 } 386 387 Some(ash::vk::SamplerYcbcrConversionInfo { 388 conversion: sampler_ycbcr_conversion.handle(), 389 ..Default::default() 390 }) 391 } else { 392 None 393 }; 394 395 let mut create_info = ash::vk::SamplerCreateInfo { 396 flags: ash::vk::SamplerCreateFlags::empty(), 397 mag_filter: mag_filter.into(), 398 min_filter: min_filter.into(), 399 mipmap_mode: mipmap_mode.into(), 400 address_mode_u: address_mode[0].into(), 401 address_mode_v: address_mode[1].into(), 402 address_mode_w: address_mode[2].into(), 403 mip_lod_bias, 404 anisotropy_enable, 405 max_anisotropy, 406 compare_enable, 407 compare_op: compare_op.into(), 408 min_lod: *lod.start(), 409 max_lod: *lod.end(), 410 border_color: border_color.into(), 411 unnormalized_coordinates: unnormalized_coordinates as ash::vk::Bool32, 412 ..Default::default() 413 }; 414 415 if let Some(sampler_reduction_mode_create_info) = 416 sampler_reduction_mode_create_info.as_mut() 417 { 418 sampler_reduction_mode_create_info.p_next = create_info.p_next; 419 create_info.p_next = sampler_reduction_mode_create_info as *const _ as *const _; 420 } 421 422 if let Some(sampler_ycbcr_conversion_info) = sampler_ycbcr_conversion_info.as_mut() { 423 sampler_ycbcr_conversion_info.p_next = create_info.p_next; 424 create_info.p_next = sampler_ycbcr_conversion_info as *const _ as *const _; 425 } 426 427 let handle = unsafe { 428 let fns = device.fns(); 429 let mut output = MaybeUninit::uninit(); 430 (fns.v1_0.create_sampler)( 431 device.handle(), 432 &create_info, 433 ptr::null(), 434 output.as_mut_ptr(), 435 ) 436 .result() 437 .map_err(VulkanError::from)?; 438 output.assume_init() 439 }; 440 441 Ok(Arc::new(Sampler { 442 handle, 443 device, 444 id: Self::next_id(), 445 address_mode, 446 anisotropy, 447 border_color: address_mode 448 .into_iter() 449 .any(|mode| mode == SamplerAddressMode::ClampToBorder) 450 .then_some(border_color), 451 compare, 452 lod, 453 mag_filter, 454 min_filter, 455 mip_lod_bias, 456 mipmap_mode, 457 reduction_mode, 458 sampler_ycbcr_conversion, 459 unnormalized_coordinates, 460 })) 461 } 462 463 /// Creates a new `Sampler` from a raw object handle. 464 /// 465 /// # Safety 466 /// 467 /// - `handle` must be a valid Vulkan object handle created from `device`. 468 /// - `create_info` must match the info used to create the object. 469 #[inline] from_handle( device: Arc<Device>, handle: ash::vk::Sampler, create_info: SamplerCreateInfo, ) -> Arc<Sampler>470 pub unsafe fn from_handle( 471 device: Arc<Device>, 472 handle: ash::vk::Sampler, 473 create_info: SamplerCreateInfo, 474 ) -> Arc<Sampler> { 475 let SamplerCreateInfo { 476 mag_filter, 477 min_filter, 478 mipmap_mode, 479 address_mode, 480 mip_lod_bias, 481 anisotropy, 482 compare, 483 lod, 484 border_color, 485 unnormalized_coordinates, 486 reduction_mode, 487 sampler_ycbcr_conversion, 488 _ne: _, 489 } = create_info; 490 491 Arc::new(Sampler { 492 handle, 493 device, 494 id: Self::next_id(), 495 address_mode, 496 anisotropy, 497 border_color: address_mode 498 .into_iter() 499 .any(|mode| mode == SamplerAddressMode::ClampToBorder) 500 .then_some(border_color), 501 compare, 502 lod, 503 mag_filter, 504 min_filter, 505 mip_lod_bias, 506 mipmap_mode, 507 reduction_mode, 508 sampler_ycbcr_conversion, 509 unnormalized_coordinates, 510 }) 511 } 512 513 /// Checks whether this sampler is compatible with `image_view`. check_can_sample( &self, image_view: &(impl ImageViewAbstract + ?Sized), ) -> Result<(), SamplerImageViewIncompatibleError>514 pub fn check_can_sample( 515 &self, 516 image_view: &(impl ImageViewAbstract + ?Sized), 517 ) -> Result<(), SamplerImageViewIncompatibleError> { 518 /* 519 Note: Most of these checks come from the Instruction/Sampler/Image View Validation 520 section, and are not strictly VUIDs. 521 https://registry.khronos.org/vulkan/specs/1.2-extensions/html/chap16.html#textures-input-validation 522 */ 523 524 if self.compare.is_some() { 525 // VUID-vkCmdDispatch-None-06479 526 if !image_view 527 .format_features() 528 .intersects(FormatFeatures::SAMPLED_IMAGE_DEPTH_COMPARISON) 529 { 530 return Err(SamplerImageViewIncompatibleError::DepthComparisonNotSupported); 531 } 532 533 // The SPIR-V instruction is one of the OpImage*Dref* instructions, the image 534 // view format is one of the depth/stencil formats, and the image view aspect 535 // is not VK_IMAGE_ASPECT_DEPTH_BIT. 536 if !image_view 537 .subresource_range() 538 .aspects 539 .intersects(ImageAspects::DEPTH) 540 { 541 return Err(SamplerImageViewIncompatibleError::DepthComparisonWrongAspect); 542 } 543 } else { 544 if !image_view 545 .format_features() 546 .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR) 547 { 548 // VUID-vkCmdDispatch-magFilter-04553 549 if self.mag_filter == Filter::Linear || self.min_filter == Filter::Linear { 550 return Err(SamplerImageViewIncompatibleError::FilterLinearNotSupported); 551 } 552 553 // VUID-vkCmdDispatch-mipmapMode-04770 554 if self.mipmap_mode == SamplerMipmapMode::Linear { 555 return Err(SamplerImageViewIncompatibleError::MipmapModeLinearNotSupported); 556 } 557 } 558 } 559 560 if self.mag_filter == Filter::Cubic || self.min_filter == Filter::Cubic { 561 // VUID-vkCmdDispatch-None-02692 562 if !image_view 563 .format_features() 564 .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC) 565 { 566 return Err(SamplerImageViewIncompatibleError::FilterCubicNotSupported); 567 } 568 569 // VUID-vkCmdDispatch-filterCubic-02694 570 if !image_view.filter_cubic() { 571 return Err(SamplerImageViewIncompatibleError::FilterCubicNotSupported); 572 } 573 574 // VUID-vkCmdDispatch-filterCubicMinmax-02695 575 if matches!( 576 self.reduction_mode, 577 SamplerReductionMode::Min | SamplerReductionMode::Max 578 ) && !image_view.filter_cubic_minmax() 579 { 580 return Err(SamplerImageViewIncompatibleError::FilterCubicMinmaxNotSupported); 581 } 582 } 583 584 if let Some(border_color) = self.border_color { 585 let aspects = image_view.subresource_range().aspects; 586 let view_scalar_type = ShaderScalarType::from( 587 if aspects.intersects( 588 ImageAspects::COLOR 589 | ImageAspects::PLANE_0 590 | ImageAspects::PLANE_1 591 | ImageAspects::PLANE_2, 592 ) { 593 image_view.format().unwrap().type_color().unwrap() 594 } else if aspects.intersects(ImageAspects::DEPTH) { 595 image_view.format().unwrap().type_depth().unwrap() 596 } else if aspects.intersects(ImageAspects::STENCIL) { 597 image_view.format().unwrap().type_stencil().unwrap() 598 } else { 599 // Per `ImageViewBuilder::aspects` and 600 // VUID-VkDescriptorImageInfo-imageView-01976 601 unreachable!() 602 }, 603 ); 604 605 match border_color { 606 BorderColor::IntTransparentBlack 607 | BorderColor::IntOpaqueBlack 608 | BorderColor::IntOpaqueWhite => { 609 // The sampler borderColor is an integer type and the image view 610 // format is not one of the VkFormat integer types or a stencil 611 // component of a depth/stencil format. 612 if !matches!( 613 view_scalar_type, 614 ShaderScalarType::Sint | ShaderScalarType::Uint 615 ) { 616 return Err( 617 SamplerImageViewIncompatibleError::BorderColorFormatNotCompatible, 618 ); 619 } 620 } 621 BorderColor::FloatTransparentBlack 622 | BorderColor::FloatOpaqueBlack 623 | BorderColor::FloatOpaqueWhite => { 624 // The sampler borderColor is a float type and the image view 625 // format is not one of the VkFormat float types or a depth 626 // component of a depth/stencil format. 627 if !matches!(view_scalar_type, ShaderScalarType::Float) { 628 return Err( 629 SamplerImageViewIncompatibleError::BorderColorFormatNotCompatible, 630 ); 631 } 632 } 633 } 634 635 // The sampler borderColor is one of the opaque black colors 636 // (VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK or VK_BORDER_COLOR_INT_OPAQUE_BLACK) 637 // and the image view VkComponentSwizzle for any of the VkComponentMapping 638 // components is not the identity swizzle, and 639 // VkPhysicalDeviceBorderColorSwizzleFeaturesEXT::borderColorSwizzleFromImage 640 // feature is not enabled, and 641 // VkSamplerBorderColorComponentMappingCreateInfoEXT is not specified. 642 if matches!( 643 border_color, 644 BorderColor::FloatOpaqueBlack | BorderColor::IntOpaqueBlack 645 ) && !image_view.component_mapping().is_identity() 646 { 647 return Err( 648 SamplerImageViewIncompatibleError::BorderColorOpaqueBlackNotIdentitySwizzled, 649 ); 650 } 651 } 652 653 // The sampler unnormalizedCoordinates is VK_TRUE and any of the limitations of 654 // unnormalized coordinates are violated. 655 // https://registry.khronos.org/vulkan/specs/1.2-extensions/html/chap13.html#samplers-unnormalizedCoordinates 656 if self.unnormalized_coordinates { 657 // The viewType must be either VK_IMAGE_VIEW_TYPE_1D or 658 // VK_IMAGE_VIEW_TYPE_2D. 659 // VUID-vkCmdDispatch-None-02702 660 if !matches!( 661 image_view.view_type(), 662 ImageViewType::Dim1d | ImageViewType::Dim2d 663 ) { 664 return Err( 665 SamplerImageViewIncompatibleError::UnnormalizedCoordinatesViewTypeNotCompatible, 666 ); 667 } 668 669 // The image view must have a single layer and a single mip level. 670 if image_view.subresource_range().mip_levels.end 671 - image_view.subresource_range().mip_levels.start 672 != 1 673 { 674 return Err( 675 SamplerImageViewIncompatibleError::UnnormalizedCoordinatesMultipleMipLevels, 676 ); 677 } 678 } 679 680 Ok(()) 681 } 682 683 /// Returns the address modes for the u, v and w coordinates. 684 #[inline] address_mode(&self) -> [SamplerAddressMode; 3]685 pub fn address_mode(&self) -> [SamplerAddressMode; 3] { 686 self.address_mode 687 } 688 689 /// Returns the anisotropy mode. 690 #[inline] anisotropy(&self) -> Option<f32>691 pub fn anisotropy(&self) -> Option<f32> { 692 self.anisotropy 693 } 694 695 /// Returns the border color if one is used by this sampler. 696 #[inline] border_color(&self) -> Option<BorderColor>697 pub fn border_color(&self) -> Option<BorderColor> { 698 self.border_color 699 } 700 701 /// Returns the compare operation if the sampler is a compare-mode sampler. 702 #[inline] compare(&self) -> Option<CompareOp>703 pub fn compare(&self) -> Option<CompareOp> { 704 self.compare 705 } 706 707 /// Returns the LOD range. 708 #[inline] lod(&self) -> RangeInclusive<f32>709 pub fn lod(&self) -> RangeInclusive<f32> { 710 self.lod.clone() 711 } 712 713 /// Returns the magnification filter. 714 #[inline] mag_filter(&self) -> Filter715 pub fn mag_filter(&self) -> Filter { 716 self.mag_filter 717 } 718 719 /// Returns the minification filter. 720 #[inline] min_filter(&self) -> Filter721 pub fn min_filter(&self) -> Filter { 722 self.min_filter 723 } 724 725 /// Returns the mip LOD bias. 726 #[inline] mip_lod_bias(&self) -> f32727 pub fn mip_lod_bias(&self) -> f32 { 728 self.mip_lod_bias 729 } 730 731 /// Returns the mipmap mode. 732 #[inline] mipmap_mode(&self) -> SamplerMipmapMode733 pub fn mipmap_mode(&self) -> SamplerMipmapMode { 734 self.mipmap_mode 735 } 736 737 /// Returns the reduction mode. 738 #[inline] reduction_mode(&self) -> SamplerReductionMode739 pub fn reduction_mode(&self) -> SamplerReductionMode { 740 self.reduction_mode 741 } 742 743 /// Returns a reference to the sampler YCbCr conversion of this sampler, if any. 744 #[inline] sampler_ycbcr_conversion(&self) -> Option<&Arc<SamplerYcbcrConversion>>745 pub fn sampler_ycbcr_conversion(&self) -> Option<&Arc<SamplerYcbcrConversion>> { 746 self.sampler_ycbcr_conversion.as_ref() 747 } 748 749 /// Returns true if the sampler uses unnormalized coordinates. 750 #[inline] unnormalized_coordinates(&self) -> bool751 pub fn unnormalized_coordinates(&self) -> bool { 752 self.unnormalized_coordinates 753 } 754 } 755 756 impl Drop for Sampler { 757 #[inline] drop(&mut self)758 fn drop(&mut self) { 759 unsafe { 760 let fns = self.device.fns(); 761 (fns.v1_0.destroy_sampler)(self.device.handle(), self.handle, ptr::null()); 762 } 763 } 764 } 765 766 unsafe impl VulkanObject for Sampler { 767 type Handle = ash::vk::Sampler; 768 769 #[inline] handle(&self) -> Self::Handle770 fn handle(&self) -> Self::Handle { 771 self.handle 772 } 773 } 774 775 unsafe impl DeviceOwned for Sampler { 776 #[inline] device(&self) -> &Arc<Device>777 fn device(&self) -> &Arc<Device> { 778 &self.device 779 } 780 } 781 782 impl_id_counter!(Sampler); 783 784 /// Error that can happen when creating an instance. 785 #[derive(Clone, Debug, PartialEq)] 786 pub enum SamplerCreationError { 787 /// Not enough memory. 788 OomError(OomError), 789 790 /// Too many sampler objects have been created. You must destroy some before creating new ones. 791 /// Note the specs guarantee that at least 4000 samplers can exist simultaneously. 792 TooManyObjects, 793 794 RequirementNotMet { 795 required_for: &'static str, 796 requires_one_of: RequiresOneOf, 797 }, 798 799 /// Anisotropy was enabled with an invalid filter. 800 AnisotropyInvalidFilter { 801 mag_filter: Filter, 802 min_filter: Filter, 803 }, 804 805 /// Depth comparison was enabled with an invalid reduction mode. 806 CompareInvalidReductionMode { 807 reduction_mode: SamplerReductionMode, 808 }, 809 810 /// The requested anisotropy level exceeds the device's limits. 811 MaxSamplerAnisotropyExceeded { 812 /// The value that was requested. 813 requested: f32, 814 /// The maximum supported value. 815 maximum: f32, 816 }, 817 818 /// The requested mip lod bias exceeds the device's limits. 819 MaxSamplerLodBiasExceeded { 820 /// The value that was requested. 821 requested: f32, 822 /// The maximum supported value. 823 maximum: f32, 824 }, 825 826 /// Sampler YCbCr conversion was enabled together with anisotropy. 827 SamplerYcbcrConversionAnisotropyEnabled, 828 829 /// Sampler YCbCr conversion was enabled, and its format does not support 830 /// `sampled_image_ycbcr_conversion_separate_reconstruction_filter`, but `mag_filter` or 831 /// `min_filter` did not match the conversion's `chroma_filter`. 832 SamplerYcbcrConversionChromaFilterMismatch { 833 chroma_filter: Filter, 834 mag_filter: Filter, 835 min_filter: Filter, 836 }, 837 838 /// Sampler YCbCr conversion was enabled, but the address mode for `u`, `v` or `w` was 839 /// something other than `ClampToEdge`. 840 SamplerYcbcrConversionInvalidAddressMode { 841 address_mode: [SamplerAddressMode; 3], 842 }, 843 844 /// Sampler YCbCr conversion was enabled, but the reduction mode was something other than 845 /// `WeightedAverage`. 846 SamplerYcbcrConversionInvalidReductionMode { 847 reduction_mode: SamplerReductionMode, 848 }, 849 850 /// Sampler YCbCr conversion was enabled together with unnormalized coordinates. 851 SamplerYcbcrConversionUnnormalizedCoordinatesEnabled, 852 853 /// Unnormalized coordinates were enabled together with anisotropy. 854 UnnormalizedCoordinatesAnisotropyEnabled, 855 856 /// Unnormalized coordinates were enabled together with depth comparison. 857 UnnormalizedCoordinatesCompareEnabled, 858 859 /// Unnormalized coordinates were enabled, but the min and mag filters were not equal. 860 UnnormalizedCoordinatesFiltersNotEqual { 861 mag_filter: Filter, 862 min_filter: Filter, 863 }, 864 865 /// Unnormalized coordinates were enabled, but the address mode for `u` or `v` was something 866 /// other than `ClampToEdge` or `ClampToBorder`. 867 UnnormalizedCoordinatesInvalidAddressMode { 868 address_mode: [SamplerAddressMode; 2], 869 }, 870 871 /// Unnormalized coordinates were enabled, but the mipmap mode was not `Nearest`. 872 UnnormalizedCoordinatesInvalidMipmapMode { mipmap_mode: SamplerMipmapMode }, 873 874 /// Unnormalized coordinates were enabled, but the LOD range was not zero. 875 UnnormalizedCoordinatesNonzeroLod { lod: RangeInclusive<f32> }, 876 } 877 878 impl Error for SamplerCreationError { source(&self) -> Option<&(dyn Error + 'static)>879 fn source(&self) -> Option<&(dyn Error + 'static)> { 880 match self { 881 SamplerCreationError::OomError(err) => Some(err), 882 _ => None, 883 } 884 } 885 } 886 887 impl Display for SamplerCreationError { fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>888 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 889 match self { 890 Self::OomError(_) => write!(f, "not enough memory available"), 891 Self::TooManyObjects => write!(f, "too many simultaneous sampler objects"), 892 Self::RequirementNotMet { 893 required_for, 894 requires_one_of, 895 } => write!( 896 f, 897 "a requirement was not met for: {}; requires one of: {}", 898 required_for, requires_one_of, 899 ), 900 Self::AnisotropyInvalidFilter { .. } => { 901 write!(f, "anisotropy was enabled with an invalid filter") 902 } 903 Self::CompareInvalidReductionMode { .. } => write!( 904 f, 905 "depth comparison was enabled with an invalid reduction mode", 906 ), 907 Self::MaxSamplerAnisotropyExceeded { .. } => { 908 write!(f, "max_sampler_anisotropy limit exceeded") 909 } 910 Self::MaxSamplerLodBiasExceeded { .. } => write!(f, "mip lod bias limit exceeded"), 911 Self::SamplerYcbcrConversionAnisotropyEnabled => write!( 912 f, 913 "sampler YCbCr conversion was enabled together with anisotropy", 914 ), 915 Self::SamplerYcbcrConversionChromaFilterMismatch { .. } => write!( 916 f, 917 "sampler YCbCr conversion was enabled, and its format does not support 918 `sampled_image_ycbcr_conversion_separate_reconstruction_filter`, but `mag_filter` 919 or `min_filter` did not match the conversion's `chroma_filter`", 920 ), 921 Self::SamplerYcbcrConversionInvalidAddressMode { .. } => write!( 922 f, 923 "sampler YCbCr conversion was enabled, but the address mode for u, v or w was 924 something other than `ClampToEdge`", 925 ), 926 Self::SamplerYcbcrConversionInvalidReductionMode { .. } => write!( 927 f, 928 "sampler YCbCr conversion was enabled, but the reduction mode was something other \ 929 than `WeightedAverage`", 930 ), 931 Self::SamplerYcbcrConversionUnnormalizedCoordinatesEnabled => write!( 932 f, 933 "sampler YCbCr conversion was enabled together with unnormalized coordinates", 934 ), 935 Self::UnnormalizedCoordinatesAnisotropyEnabled => write!( 936 f, 937 "unnormalized coordinates were enabled together with anisotropy", 938 ), 939 Self::UnnormalizedCoordinatesCompareEnabled => write!( 940 f, 941 "unnormalized coordinates were enabled together with depth comparison", 942 ), 943 Self::UnnormalizedCoordinatesFiltersNotEqual { .. } => write!( 944 f, 945 "unnormalized coordinates were enabled, but the min and mag filters were not equal", 946 ), 947 Self::UnnormalizedCoordinatesInvalidAddressMode { .. } => write!( 948 f, 949 "unnormalized coordinates were enabled, but the address mode for u or v was \ 950 something other than `ClampToEdge` or `ClampToBorder`", 951 ), 952 Self::UnnormalizedCoordinatesInvalidMipmapMode { .. } => write!( 953 f, 954 "unnormalized coordinates were enabled, but the mipmap mode was not `Nearest`", 955 ), 956 Self::UnnormalizedCoordinatesNonzeroLod { .. } => write!( 957 f, 958 "unnormalized coordinates were enabled, but the LOD range was not zero", 959 ), 960 } 961 } 962 } 963 964 impl From<OomError> for SamplerCreationError { from(err: OomError) -> Self965 fn from(err: OomError) -> Self { 966 Self::OomError(err) 967 } 968 } 969 970 impl From<VulkanError> for SamplerCreationError { from(err: VulkanError) -> Self971 fn from(err: VulkanError) -> Self { 972 match err { 973 err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)), 974 err @ VulkanError::OutOfDeviceMemory => Self::OomError(OomError::from(err)), 975 VulkanError::TooManyObjects => Self::TooManyObjects, 976 _ => panic!("unexpected error: {:?}", err), 977 } 978 } 979 } 980 981 impl From<RequirementNotMet> for SamplerCreationError { from(err: RequirementNotMet) -> Self982 fn from(err: RequirementNotMet) -> Self { 983 Self::RequirementNotMet { 984 required_for: err.required_for, 985 requires_one_of: err.requires_one_of, 986 } 987 } 988 } 989 990 /// Parameters to create a new `Sampler`. 991 #[derive(Clone, Debug)] 992 pub struct SamplerCreateInfo { 993 /// How the sampled value of a single mipmap should be calculated, 994 /// when magnification is applied (LOD <= 0.0). 995 /// 996 /// The default value is [`Nearest`](Filter::Nearest). 997 pub mag_filter: Filter, 998 999 /// How the sampled value of a single mipmap should be calculated, 1000 /// when minification is applied (LOD > 0.0). 1001 /// 1002 /// The default value is [`Nearest`](Filter::Nearest). 1003 pub min_filter: Filter, 1004 1005 /// How the final sampled value should be calculated from the samples of individual 1006 /// mipmaps. 1007 /// 1008 /// The default value is [`Nearest`](SamplerMipmapMode::Nearest). 1009 pub mipmap_mode: SamplerMipmapMode, 1010 1011 /// How out-of-range texture coordinates should be treated, for the `u`, `v` and `w` texture 1012 /// coordinate indices respectively. 1013 /// 1014 /// The default value is [`ClampToEdge`](SamplerAddressMode::ClampToEdge). 1015 pub address_mode: [SamplerAddressMode; 3], 1016 1017 /// The bias value to be added to the base LOD before clamping. 1018 /// 1019 /// The absolute value of the provided value must not exceed the 1020 /// [`max_sampler_lod_bias`](crate::device::Properties::max_sampler_lod_bias) limit of the 1021 /// device. 1022 /// 1023 /// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag) 1024 /// devices, if `mip_lod_bias` is not `0.0`, the 1025 /// [`sampler_mip_lod_bias`](crate::device::Features::sampler_mip_lod_bias) 1026 /// feature must be enabled on the device. 1027 /// 1028 /// The default value is `0.0`. 1029 pub mip_lod_bias: f32, 1030 1031 /// Whether anisotropic texel filtering is enabled (`Some`), and the maximum anisotropy value 1032 /// to use if it is enabled. 1033 /// 1034 /// Anisotropic filtering is a special filtering mode that takes into account the differences in 1035 /// scaling between the horizontal and vertical framebuffer axes. 1036 /// 1037 /// If set to `Some`, the [`sampler_anisotropy`](crate::device::Features::sampler_anisotropy) 1038 /// feature must be enabled on the device, the provided maximum value must not exceed the 1039 /// [`max_sampler_anisotropy`](crate::device::Properties::max_sampler_anisotropy) limit, and 1040 /// the [`Cubic`](Filter::Cubic) filter must not be used. 1041 /// 1042 /// The default value is `None`. 1043 pub anisotropy: Option<f32>, 1044 1045 /// Whether depth comparison is enabled (`Some`), and the comparison operator to use if it is 1046 /// enabled. 1047 /// 1048 /// Depth comparison is an alternative mode for samplers that can be used in combination with 1049 /// image views specifying the depth aspect. Instead of returning a value that is sampled from 1050 /// the image directly, a comparison operation is applied between the sampled value and a 1051 /// reference value that is specified as part of the operation. The result is binary: 1.0 if the 1052 /// operation returns `true`, 0.0 if it returns `false`. 1053 /// 1054 /// If set to `Some`, the `reduction_mode` must be set to 1055 /// [`WeightedAverage`](SamplerReductionMode::WeightedAverage). 1056 /// 1057 /// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag) 1058 /// devices, if the sampler is going to be used as a mutable sampler (written to descriptor sets 1059 /// rather than being an immutable part of a descriptor set layout), the 1060 /// [`mutable_comparison_samplers`](crate::device::Features::mutable_comparison_samplers) 1061 /// feature must be enabled on the device. 1062 /// 1063 /// The default value is `None`. 1064 pub compare: Option<CompareOp>, 1065 1066 /// The range that LOD values must be clamped to. 1067 /// 1068 /// If the end of the range is set to [`LOD_CLAMP_NONE`], it is unbounded. 1069 /// 1070 /// The default value is `0.0..=0.0`. 1071 pub lod: RangeInclusive<f32>, 1072 1073 /// The border color to use if `address_mode` is set to 1074 /// [`ClampToBorder`](SamplerAddressMode::ClampToBorder). 1075 /// 1076 /// The default value is [`FloatTransparentBlack`](BorderColor::FloatTransparentBlack). 1077 pub border_color: BorderColor, 1078 1079 /// Whether unnormalized texture coordinates are enabled. 1080 /// 1081 /// When a sampler is set to use unnormalized coordinates as input, the texture coordinates are 1082 /// not scaled by the size of the image, and therefore range up to the size of the image rather 1083 /// than 1.0. Enabling this comes with several restrictions: 1084 /// - `min_filter` and `mag_filter` must be equal. 1085 /// - `mipmap_mode` must be [`Nearest`](SamplerMipmapMode::Nearest). 1086 /// - The `lod` range must be `0.0..=0.0`. 1087 /// - `address_mode` for u and v must be either 1088 /// [`ClampToEdge`](`SamplerAddressMode::ClampToEdge`) or 1089 /// [`ClampToBorder`](`SamplerAddressMode::ClampToBorder`). 1090 /// - Anisotropy and depth comparison must be disabled. 1091 /// 1092 /// Some restrictions also apply to the image view being sampled: 1093 /// - The view type must be [`Dim1d`](crate::image::view::ImageViewType::Dim1d) or 1094 /// [`Dim2d`](crate::image::view::ImageViewType::Dim2d). Arrayed types are not allowed. 1095 /// - It must have a single mipmap level. 1096 /// 1097 /// Finally, restrictions apply to the sampling operations that can be used in a shader: 1098 /// - Only explicit LOD operations are allowed, implicit LOD operations are not. 1099 /// - Sampling with projection is not allowed. 1100 /// - Sampling with an LOD bias is not allowed. 1101 /// - Sampling with an offset is not allowed. 1102 /// 1103 /// The default value is `false`. 1104 pub unnormalized_coordinates: bool, 1105 1106 /// How the value sampled from a mipmap should be calculated from the selected 1107 /// pixels, for the `Linear` and `Cubic` filters. 1108 /// 1109 /// The default value is [`WeightedAverage`](SamplerReductionMode::WeightedAverage). 1110 pub reduction_mode: SamplerReductionMode, 1111 1112 /// Adds a sampler YCbCr conversion to the sampler. 1113 /// 1114 /// If set to `Some`, several restrictions apply: 1115 /// - If the `format` of `conversion` does not support 1116 /// `sampled_image_ycbcr_conversion_separate_reconstruction_filter`, then `mag_filter` and 1117 /// `min_filter` must be equal to the `chroma_filter` of `conversion`. 1118 /// - `address_mode` for u, v and w must be [`ClampToEdge`](`SamplerAddressMode::ClampToEdge`). 1119 /// - Anisotropy and unnormalized coordinates must be disabled. 1120 /// - The `reduction_mode` must be [`WeightedAverage`](SamplerReductionMode::WeightedAverage). 1121 /// 1122 /// In addition, the sampler must only be used as an immutable sampler within a descriptor set 1123 /// layout, and only in a combined image sampler descriptor. 1124 /// 1125 /// The default value is `None`. 1126 pub sampler_ycbcr_conversion: Option<Arc<SamplerYcbcrConversion>>, 1127 1128 pub _ne: crate::NonExhaustive, 1129 } 1130 1131 impl Default for SamplerCreateInfo { 1132 #[inline] default() -> Self1133 fn default() -> Self { 1134 Self { 1135 mag_filter: Filter::Nearest, 1136 min_filter: Filter::Nearest, 1137 mipmap_mode: SamplerMipmapMode::Nearest, 1138 address_mode: [SamplerAddressMode::ClampToEdge; 3], 1139 mip_lod_bias: 0.0, 1140 anisotropy: None, 1141 compare: None, 1142 lod: 0.0..=0.0, 1143 border_color: BorderColor::FloatTransparentBlack, 1144 unnormalized_coordinates: false, 1145 reduction_mode: SamplerReductionMode::WeightedAverage, 1146 sampler_ycbcr_conversion: None, 1147 _ne: crate::NonExhaustive(()), 1148 } 1149 } 1150 } 1151 1152 impl SamplerCreateInfo { 1153 /// Shortcut for creating a sampler with linear sampling, linear mipmaps, and with the repeat 1154 /// mode for borders. 1155 #[inline] simple_repeat_linear() -> Self1156 pub fn simple_repeat_linear() -> Self { 1157 Self { 1158 mag_filter: Filter::Linear, 1159 min_filter: Filter::Linear, 1160 mipmap_mode: SamplerMipmapMode::Linear, 1161 address_mode: [SamplerAddressMode::Repeat; 3], 1162 lod: 0.0..=LOD_CLAMP_NONE, 1163 ..Default::default() 1164 } 1165 } 1166 1167 /// Shortcut for creating a sampler with linear sampling, that only uses the main level of 1168 /// images, and with the repeat mode for borders. 1169 #[inline] simple_repeat_linear_no_mipmap() -> Self1170 pub fn simple_repeat_linear_no_mipmap() -> Self { 1171 Self { 1172 mag_filter: Filter::Linear, 1173 min_filter: Filter::Linear, 1174 address_mode: [SamplerAddressMode::Repeat; 3], 1175 lod: 0.0..=1.0, 1176 ..Default::default() 1177 } 1178 } 1179 } 1180 1181 /// A special value to indicate that the maximum LOD should not be clamped. 1182 pub const LOD_CLAMP_NONE: f32 = ash::vk::LOD_CLAMP_NONE; 1183 1184 /// A mapping between components of a source format and components read by a shader. 1185 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] 1186 pub struct ComponentMapping { 1187 /// First component. 1188 pub r: ComponentSwizzle, 1189 /// Second component. 1190 pub g: ComponentSwizzle, 1191 /// Third component. 1192 pub b: ComponentSwizzle, 1193 /// Fourth component. 1194 pub a: ComponentSwizzle, 1195 } 1196 1197 impl ComponentMapping { 1198 /// Creates a `ComponentMapping` with all components identity swizzled. 1199 #[inline] identity() -> Self1200 pub fn identity() -> Self { 1201 Self::default() 1202 } 1203 1204 /// Returns `true` if all components are identity swizzled, 1205 /// meaning that all the members are `Identity` or the name of that member. 1206 /// 1207 /// Certain operations require views that are identity swizzled, and will return an error 1208 /// otherwise. For example, attaching a view to a framebuffer is only possible if the view is 1209 /// identity swizzled. 1210 #[inline] is_identity(&self) -> bool1211 pub fn is_identity(&self) -> bool { 1212 self.r_is_identity() && self.g_is_identity() && self.b_is_identity() && self.a_is_identity() 1213 } 1214 1215 /// Returns `true` if the red component mapping is identity swizzled. 1216 #[inline] r_is_identity(&self) -> bool1217 pub fn r_is_identity(&self) -> bool { 1218 matches!(self.r, ComponentSwizzle::Identity | ComponentSwizzle::Red) 1219 } 1220 1221 /// Returns `true` if the green component mapping is identity swizzled. 1222 #[inline] g_is_identity(&self) -> bool1223 pub fn g_is_identity(&self) -> bool { 1224 matches!(self.g, ComponentSwizzle::Identity | ComponentSwizzle::Green) 1225 } 1226 1227 /// Returns `true` if the blue component mapping is identity swizzled. 1228 #[inline] b_is_identity(&self) -> bool1229 pub fn b_is_identity(&self) -> bool { 1230 matches!(self.b, ComponentSwizzle::Identity | ComponentSwizzle::Blue) 1231 } 1232 1233 /// Returns `true` if the alpha component mapping is identity swizzled. 1234 #[inline] a_is_identity(&self) -> bool1235 pub fn a_is_identity(&self) -> bool { 1236 matches!(self.a, ComponentSwizzle::Identity | ComponentSwizzle::Alpha) 1237 } 1238 1239 /// Returns the component indices that each component reads from. The index is `None` if the 1240 /// component has a fixed value and is not read from anywhere (`Zero` or `One`). 1241 #[inline] component_map(&self) -> [Option<usize>; 4]1242 pub fn component_map(&self) -> [Option<usize>; 4] { 1243 [ 1244 match self.r { 1245 ComponentSwizzle::Identity => Some(0), 1246 ComponentSwizzle::Zero => None, 1247 ComponentSwizzle::One => None, 1248 ComponentSwizzle::Red => Some(0), 1249 ComponentSwizzle::Green => Some(1), 1250 ComponentSwizzle::Blue => Some(2), 1251 ComponentSwizzle::Alpha => Some(3), 1252 }, 1253 match self.g { 1254 ComponentSwizzle::Identity => Some(1), 1255 ComponentSwizzle::Zero => None, 1256 ComponentSwizzle::One => None, 1257 ComponentSwizzle::Red => Some(0), 1258 ComponentSwizzle::Green => Some(1), 1259 ComponentSwizzle::Blue => Some(2), 1260 ComponentSwizzle::Alpha => Some(3), 1261 }, 1262 match self.b { 1263 ComponentSwizzle::Identity => Some(2), 1264 ComponentSwizzle::Zero => None, 1265 ComponentSwizzle::One => None, 1266 ComponentSwizzle::Red => Some(0), 1267 ComponentSwizzle::Green => Some(1), 1268 ComponentSwizzle::Blue => Some(2), 1269 ComponentSwizzle::Alpha => Some(3), 1270 }, 1271 match self.a { 1272 ComponentSwizzle::Identity => Some(3), 1273 ComponentSwizzle::Zero => None, 1274 ComponentSwizzle::One => None, 1275 ComponentSwizzle::Red => Some(0), 1276 ComponentSwizzle::Green => Some(1), 1277 ComponentSwizzle::Blue => Some(2), 1278 ComponentSwizzle::Alpha => Some(3), 1279 }, 1280 ] 1281 } 1282 } 1283 1284 impl From<ComponentMapping> for ash::vk::ComponentMapping { 1285 #[inline] from(value: ComponentMapping) -> Self1286 fn from(value: ComponentMapping) -> Self { 1287 Self { 1288 r: value.r.into(), 1289 g: value.g.into(), 1290 b: value.b.into(), 1291 a: value.a.into(), 1292 } 1293 } 1294 } 1295 1296 vulkan_enum! { 1297 #[non_exhaustive] 1298 1299 /// Describes the value that an individual component must return when being accessed. 1300 ComponentSwizzle = ComponentSwizzle(i32); 1301 1302 /// Returns the value that this component should normally have. 1303 /// 1304 /// This is the `Default` value. 1305 Identity = IDENTITY, 1306 1307 /// Always return zero. 1308 Zero = ZERO, 1309 1310 /// Always return one. 1311 One = ONE, 1312 1313 /// Returns the value of the first component. 1314 Red = R, 1315 1316 /// Returns the value of the second component. 1317 Green = G, 1318 1319 /// Returns the value of the third component. 1320 Blue = B, 1321 1322 /// Returns the value of the fourth component. 1323 Alpha = A, 1324 } 1325 1326 impl Default for ComponentSwizzle { 1327 #[inline] default() -> ComponentSwizzle1328 fn default() -> ComponentSwizzle { 1329 ComponentSwizzle::Identity 1330 } 1331 } 1332 1333 vulkan_enum! { 1334 #[non_exhaustive] 1335 1336 /// Describes how the color of each pixel should be determined. 1337 Filter = Filter(i32); 1338 1339 /// The pixel whose center is nearest to the requested coordinates is taken from the source 1340 /// and its value is returned as-is. 1341 Nearest = NEAREST, 1342 1343 /// The 8/4/2 pixels (depending on view dimensionality) whose center surround the requested 1344 /// coordinates are taken, then their values are combined according to the chosen 1345 /// `reduction_mode`. 1346 Linear = LINEAR, 1347 1348 /// The 64/16/4 pixels (depending on the view dimensionality) whose center surround the 1349 /// requested coordinates are taken, then their values are combined according to the chosen 1350 /// `reduction_mode`. 1351 /// 1352 /// The [`ext_filter_cubic`](crate::device::DeviceExtensions::ext_filter_cubic) extension must 1353 /// be enabled on the device, and anisotropy must be disabled. Sampled image views must have 1354 /// a type of [`Dim2d`](crate::image::view::ImageViewType::Dim2d). 1355 Cubic = CUBIC_EXT { 1356 device_extensions: [ext_filter_cubic, img_filter_cubic], 1357 }, 1358 } 1359 1360 vulkan_enum! { 1361 #[non_exhaustive] 1362 1363 /// Describes which mipmap from the source to use. 1364 SamplerMipmapMode = SamplerMipmapMode(i32); 1365 1366 /// Use the mipmap whose dimensions are the nearest to the dimensions of the destination. 1367 Nearest = NEAREST, 1368 1369 /// Take the mipmap whose dimensions are no greater than that of the destination together 1370 /// with the next higher level mipmap, calculate the value for both, and interpolate them. 1371 Linear = LINEAR, 1372 } 1373 1374 vulkan_enum! { 1375 #[non_exhaustive] 1376 1377 /// How the sampler should behave when it needs to access a pixel that is out of range of the 1378 /// texture. 1379 SamplerAddressMode = SamplerAddressMode(i32); 1380 1381 /// Repeat the texture. In other words, the pixel at coordinate `x + 1.0` is the same as the 1382 /// one at coordinate `x`. 1383 Repeat = REPEAT, 1384 1385 /// Repeat the texture but mirror it at every repetition. In other words, the pixel at 1386 /// coordinate `x + 1.0` is the same as the one at coordinate `1.0 - x`. 1387 MirroredRepeat = MIRRORED_REPEAT, 1388 1389 /// The coordinates are clamped to the valid range. Coordinates below 0.0 have the same value 1390 /// as coordinate 0.0. Coordinates over 1.0 have the same value as coordinate 1.0. 1391 ClampToEdge = CLAMP_TO_EDGE, 1392 1393 /// Any pixel out of range is colored using the colour selected with the `border_color` on the 1394 /// `SamplerBuilder`. 1395 /// 1396 /// When this mode is chosen, the numeric type of the image view's format must match the border 1397 /// color. When using a floating-point border color, the sampler can only be used with 1398 /// floating-point or depth image views. When using an integer border color, the sampler can 1399 /// only be used with integer or stencil image views. In addition to this, you can't use an 1400 /// opaque black border color with an image view that uses component swizzling. 1401 ClampToBorder = CLAMP_TO_BORDER, 1402 1403 /// Similar to `MirroredRepeat`, except that coordinates are clamped to the range 1404 /// `[-1.0, 1.0]`. 1405 /// 1406 /// The [`sampler_mirror_clamp_to_edge`](crate::device::Features::sampler_mirror_clamp_to_edge) 1407 /// feature or the 1408 /// [`khr_sampler_mirror_clamp_to_edge`](crate::device::DeviceExtensions::khr_sampler_mirror_clamp_to_edge) 1409 /// extension must be enabled on the device. 1410 MirrorClampToEdge = MIRROR_CLAMP_TO_EDGE { 1411 api_version: V1_2, 1412 device_extensions: [khr_sampler_mirror_clamp_to_edge], 1413 }, 1414 } 1415 1416 vulkan_enum! { 1417 #[non_exhaustive] 1418 1419 /// The color to use for the border of an image. 1420 /// 1421 /// Only relevant if you use `ClampToBorder`. 1422 /// 1423 /// Using a border color restricts the sampler to either floating-point images or integer images. 1424 BorderColor = BorderColor(i32); 1425 1426 /// The value `(0.0, 0.0, 0.0, 0.0)`. Can only be used with floating-point images. 1427 FloatTransparentBlack = FLOAT_TRANSPARENT_BLACK, 1428 1429 /// The value `(0, 0, 0, 0)`. Can only be used with integer images. 1430 IntTransparentBlack = INT_TRANSPARENT_BLACK, 1431 1432 /// The value `(0.0, 0.0, 0.0, 1.0)`. Can only be used with floating-point identity-swizzled 1433 /// images. 1434 FloatOpaqueBlack = FLOAT_OPAQUE_BLACK, 1435 1436 /// The value `(0, 0, 0, 1)`. Can only be used with integer identity-swizzled images. 1437 IntOpaqueBlack = INT_OPAQUE_BLACK, 1438 1439 /// The value `(1.0, 1.0, 1.0, 1.0)`. Can only be used with floating-point images. 1440 FloatOpaqueWhite = FLOAT_OPAQUE_WHITE, 1441 1442 /// The value `(1, 1, 1, 1)`. Can only be used with integer images. 1443 IntOpaqueWhite = INT_OPAQUE_WHITE, 1444 1445 /* TODO: enable 1446 // TODO: document 1447 FloatCustom = VK_BORDER_COLOR_FLOAT_CUSTOM_EXT { 1448 device_extensions: [ext_custom_border_color], 1449 },*/ 1450 1451 /* TODO: enable 1452 // TODO: document 1453 IntCustom = INT_CUSTOM_EXT { 1454 device_extensions: [ext_custom_border_color], 1455 },*/ 1456 } 1457 1458 vulkan_enum! { 1459 #[non_exhaustive] 1460 1461 /// Describes how the value sampled from a mipmap should be calculated from the selected 1462 /// pixels, for the `Linear` and `Cubic` filters. 1463 SamplerReductionMode = SamplerReductionMode(i32); 1464 1465 /// Calculates a weighted average of the selected pixels. For `Linear` filtering the pixels 1466 /// are evenly weighted, for `Cubic` filtering they use Catmull-Rom weights. 1467 WeightedAverage = WEIGHTED_AVERAGE, 1468 1469 /// Calculates the minimum of the selected pixels. 1470 /// 1471 /// The [`sampler_filter_minmax`](crate::device::Features::sampler_filter_minmax) 1472 /// feature or the 1473 /// [`ext_sampler_filter_minmax`](crate::device::DeviceExtensions::ext_sampler_filter_minmax) 1474 /// extension must be enabled on the device. 1475 Min = MIN, 1476 1477 /// Calculates the maximum of the selected pixels. 1478 /// 1479 /// The [`sampler_filter_minmax`](crate::device::Features::sampler_filter_minmax) 1480 /// feature or the 1481 /// [`ext_sampler_filter_minmax`](crate::device::DeviceExtensions::ext_sampler_filter_minmax) 1482 /// extension must be enabled on the device. 1483 Max = MAX, 1484 } 1485 1486 #[derive(Clone, Copy, Debug)] 1487 pub enum SamplerImageViewIncompatibleError { 1488 /// The sampler has a border color with a numeric type different from the image view. 1489 BorderColorFormatNotCompatible, 1490 1491 /// The sampler has an opaque black border color, but the image view is not identity swizzled. 1492 BorderColorOpaqueBlackNotIdentitySwizzled, 1493 1494 /// The sampler has depth comparison enabled, but this is not supported by the image view. 1495 DepthComparisonNotSupported, 1496 1497 /// The sampler has depth comparison enabled, but the image view does not select the `depth` 1498 /// aspect. 1499 DepthComparisonWrongAspect, 1500 1501 /// The sampler uses a linear filter, but this is not supported by the image view's format 1502 /// features. 1503 FilterLinearNotSupported, 1504 1505 /// The sampler uses a cubic filter, but this is not supported by the image view's format 1506 /// features. 1507 FilterCubicNotSupported, 1508 1509 /// The sampler uses a cubic filter with a `Min` or `Max` reduction mode, but this is not 1510 /// supported by the image view's format features. 1511 FilterCubicMinmaxNotSupported, 1512 1513 /// The sampler uses a linear mipmap mode, but this is not supported by the image view's format 1514 /// features. 1515 MipmapModeLinearNotSupported, 1516 1517 /// The sampler uses unnormalized coordinates, but the image view has multiple mip levels. 1518 UnnormalizedCoordinatesMultipleMipLevels, 1519 1520 /// The sampler uses unnormalized coordinates, but the image view has a type other than `Dim1d` 1521 /// or `Dim2d`. 1522 UnnormalizedCoordinatesViewTypeNotCompatible, 1523 } 1524 1525 impl Error for SamplerImageViewIncompatibleError {} 1526 1527 impl Display for SamplerImageViewIncompatibleError { fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>1528 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 1529 match self { 1530 Self::BorderColorFormatNotCompatible => write!( 1531 f, 1532 "the sampler has a border color with a numeric type different from the image view", 1533 ), 1534 Self::BorderColorOpaqueBlackNotIdentitySwizzled => write!( 1535 f, 1536 "the sampler has an opaque black border color, but the image view is not identity \ 1537 swizzled", 1538 ), 1539 Self::DepthComparisonNotSupported => write!( 1540 f, 1541 "the sampler has depth comparison enabled, but this is not supported by the image \ 1542 view", 1543 ), 1544 Self::DepthComparisonWrongAspect => write!( 1545 f, 1546 "the sampler has depth comparison enabled, but the image view does not select the \ 1547 `depth` aspect", 1548 ), 1549 Self::FilterLinearNotSupported => write!( 1550 f, 1551 "the sampler uses a linear filter, but this is not supported by the image view's \ 1552 format features", 1553 ), 1554 Self::FilterCubicNotSupported => write!( 1555 f, 1556 "the sampler uses a cubic filter, but this is not supported by the image view's \ 1557 format features", 1558 ), 1559 Self::FilterCubicMinmaxNotSupported => write!( 1560 f, 1561 "the sampler uses a cubic filter with a `Min` or `Max` reduction mode, but this is \ 1562 not supported by the image view's format features", 1563 ), 1564 Self::MipmapModeLinearNotSupported => write!( 1565 f, 1566 "the sampler uses a linear mipmap mode, but this is not supported by the image \ 1567 view's format features", 1568 ), 1569 Self::UnnormalizedCoordinatesMultipleMipLevels => write!( 1570 f, 1571 "the sampler uses unnormalized coordinates, but the image view has multiple mip \ 1572 levels", 1573 ), 1574 Self::UnnormalizedCoordinatesViewTypeNotCompatible => write!( 1575 f, 1576 "the sampler uses unnormalized coordinates, but the image view has a type other \ 1577 than `Dim1d` or `Dim2d`", 1578 ), 1579 } 1580 } 1581 } 1582 1583 #[cfg(test)] 1584 mod tests { 1585 use crate::{ 1586 pipeline::graphics::depth_stencil::CompareOp, 1587 sampler::{ 1588 Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerCreationError, 1589 SamplerReductionMode, 1590 }, 1591 RequiresOneOf, 1592 }; 1593 1594 #[test] create_regular()1595 fn create_regular() { 1596 let (device, _queue) = gfx_dev_and_queue!(); 1597 1598 let s = Sampler::new( 1599 device, 1600 SamplerCreateInfo { 1601 mag_filter: Filter::Linear, 1602 min_filter: Filter::Linear, 1603 address_mode: [SamplerAddressMode::Repeat; 3], 1604 mip_lod_bias: 1.0, 1605 lod: 0.0..=2.0, 1606 ..Default::default() 1607 }, 1608 ) 1609 .unwrap(); 1610 assert!(s.compare().is_none()); 1611 assert!(!s.unnormalized_coordinates()); 1612 } 1613 1614 #[test] create_compare()1615 fn create_compare() { 1616 let (device, _queue) = gfx_dev_and_queue!(); 1617 1618 let s = Sampler::new( 1619 device, 1620 SamplerCreateInfo { 1621 mag_filter: Filter::Linear, 1622 min_filter: Filter::Linear, 1623 address_mode: [SamplerAddressMode::Repeat; 3], 1624 mip_lod_bias: 1.0, 1625 compare: Some(CompareOp::Less), 1626 lod: 0.0..=2.0, 1627 ..Default::default() 1628 }, 1629 ) 1630 .unwrap(); 1631 assert!(s.compare().is_some()); 1632 assert!(!s.unnormalized_coordinates()); 1633 } 1634 1635 #[test] create_unnormalized()1636 fn create_unnormalized() { 1637 let (device, _queue) = gfx_dev_and_queue!(); 1638 1639 let s = Sampler::new( 1640 device, 1641 SamplerCreateInfo { 1642 mag_filter: Filter::Linear, 1643 min_filter: Filter::Linear, 1644 unnormalized_coordinates: true, 1645 ..Default::default() 1646 }, 1647 ) 1648 .unwrap(); 1649 assert!(s.compare().is_none()); 1650 assert!(s.unnormalized_coordinates()); 1651 } 1652 1653 #[test] simple_repeat_linear()1654 fn simple_repeat_linear() { 1655 let (device, _queue) = gfx_dev_and_queue!(); 1656 let _ = Sampler::new(device, SamplerCreateInfo::simple_repeat_linear()); 1657 } 1658 1659 #[test] simple_repeat_linear_no_mipmap()1660 fn simple_repeat_linear_no_mipmap() { 1661 let (device, _queue) = gfx_dev_and_queue!(); 1662 let _ = Sampler::new(device, SamplerCreateInfo::simple_repeat_linear_no_mipmap()); 1663 } 1664 1665 #[test] min_lod_inferior()1666 fn min_lod_inferior() { 1667 let (device, _queue) = gfx_dev_and_queue!(); 1668 1669 assert_should_panic!({ 1670 let _ = Sampler::new( 1671 device, 1672 SamplerCreateInfo { 1673 mag_filter: Filter::Linear, 1674 min_filter: Filter::Linear, 1675 address_mode: [SamplerAddressMode::Repeat; 3], 1676 mip_lod_bias: 1.0, 1677 lod: 5.0..=2.0, 1678 ..Default::default() 1679 }, 1680 ); 1681 }); 1682 } 1683 1684 #[test] max_anisotropy()1685 fn max_anisotropy() { 1686 let (device, _queue) = gfx_dev_and_queue!(); 1687 1688 assert_should_panic!({ 1689 let _ = Sampler::new( 1690 device, 1691 SamplerCreateInfo { 1692 mag_filter: Filter::Linear, 1693 min_filter: Filter::Linear, 1694 address_mode: [SamplerAddressMode::Repeat; 3], 1695 mip_lod_bias: 1.0, 1696 anisotropy: Some(0.5), 1697 lod: 0.0..=2.0, 1698 ..Default::default() 1699 }, 1700 ); 1701 }); 1702 } 1703 1704 #[test] anisotropy_feature()1705 fn anisotropy_feature() { 1706 let (device, _queue) = gfx_dev_and_queue!(); 1707 1708 let r = Sampler::new( 1709 device, 1710 SamplerCreateInfo { 1711 mag_filter: Filter::Linear, 1712 min_filter: Filter::Linear, 1713 address_mode: [SamplerAddressMode::Repeat; 3], 1714 mip_lod_bias: 1.0, 1715 anisotropy: Some(2.0), 1716 lod: 0.0..=2.0, 1717 ..Default::default() 1718 }, 1719 ); 1720 1721 match r { 1722 Err(SamplerCreationError::RequirementNotMet { 1723 requires_one_of: RequiresOneOf { features, .. }, 1724 .. 1725 }) if features.contains(&"sampler_anisotropy") => (), 1726 _ => panic!(), 1727 } 1728 } 1729 1730 #[test] anisotropy_limit()1731 fn anisotropy_limit() { 1732 let (device, _queue) = gfx_dev_and_queue!(sampler_anisotropy); 1733 1734 let r = Sampler::new( 1735 device, 1736 SamplerCreateInfo { 1737 mag_filter: Filter::Linear, 1738 min_filter: Filter::Linear, 1739 address_mode: [SamplerAddressMode::Repeat; 3], 1740 mip_lod_bias: 1.0, 1741 anisotropy: Some(100000000.0), 1742 lod: 0.0..=2.0, 1743 ..Default::default() 1744 }, 1745 ); 1746 1747 match r { 1748 Err(SamplerCreationError::MaxSamplerAnisotropyExceeded { .. }) => (), 1749 _ => panic!(), 1750 } 1751 } 1752 1753 #[test] mip_lod_bias_limit()1754 fn mip_lod_bias_limit() { 1755 let (device, _queue) = gfx_dev_and_queue!(); 1756 1757 let r = Sampler::new( 1758 device, 1759 SamplerCreateInfo { 1760 mag_filter: Filter::Linear, 1761 min_filter: Filter::Linear, 1762 address_mode: [SamplerAddressMode::Repeat; 3], 1763 mip_lod_bias: 100000000.0, 1764 lod: 0.0..=2.0, 1765 ..Default::default() 1766 }, 1767 ); 1768 1769 match r { 1770 Err(SamplerCreationError::MaxSamplerLodBiasExceeded { .. }) => (), 1771 _ => panic!(), 1772 } 1773 } 1774 1775 #[test] sampler_mirror_clamp_to_edge_extension()1776 fn sampler_mirror_clamp_to_edge_extension() { 1777 let (device, _queue) = gfx_dev_and_queue!(); 1778 1779 let r = Sampler::new( 1780 device, 1781 SamplerCreateInfo { 1782 mag_filter: Filter::Linear, 1783 min_filter: Filter::Linear, 1784 address_mode: [SamplerAddressMode::MirrorClampToEdge; 3], 1785 mip_lod_bias: 1.0, 1786 lod: 0.0..=2.0, 1787 ..Default::default() 1788 }, 1789 ); 1790 1791 match r { 1792 Err(SamplerCreationError::RequirementNotMet { 1793 requires_one_of: 1794 RequiresOneOf { 1795 features, 1796 device_extensions, 1797 .. 1798 }, 1799 .. 1800 }) if features.contains(&"sampler_mirror_clamp_to_edge") 1801 && device_extensions.contains(&"khr_sampler_mirror_clamp_to_edge") => {} 1802 _ => panic!(), 1803 } 1804 } 1805 1806 #[test] sampler_filter_minmax_extension()1807 fn sampler_filter_minmax_extension() { 1808 let (device, _queue) = gfx_dev_and_queue!(); 1809 1810 let r = Sampler::new( 1811 device, 1812 SamplerCreateInfo { 1813 mag_filter: Filter::Linear, 1814 min_filter: Filter::Linear, 1815 reduction_mode: SamplerReductionMode::Min, 1816 ..Default::default() 1817 }, 1818 ); 1819 1820 match r { 1821 Err(SamplerCreationError::RequirementNotMet { 1822 requires_one_of: 1823 RequiresOneOf { 1824 features, 1825 device_extensions, 1826 .. 1827 }, 1828 .. 1829 }) if features.contains(&"sampler_filter_minmax") 1830 && device_extensions.contains(&"ext_sampler_filter_minmax") => {} 1831 _ => panic!(), 1832 } 1833 } 1834 } 1835