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 an image within a shader. 11 //! 12 //! When you retrieve data from an image, you have to pass the coordinates of the pixel you want 13 //! to retrieve. The implementation then performs various calculations, and these operations are 14 //! what the `Sampler` struct describes. 15 //! 16 //! Sampling is a very complex topic but that hasn't changed much since the beginnings of 3D 17 //! rendering. Documentation here is missing, but any tutorial about OpenGL or DirectX can teach 18 //! you how it works. 19 //! 20 //! # Examples 21 //! 22 //! A simple sampler for most usages: 23 //! 24 //! ``` 25 //! use vulkano::sampler::Sampler; 26 //! 27 //! # let device: std::sync::Arc<vulkano::device::Device> = return; 28 //! let _sampler = Sampler::simple_repeat_linear_no_mipmap(device.clone()); 29 //! ``` 30 //! 31 //! More detailed sampler creation: 32 //! 33 //! ``` 34 //! use vulkano::sampler; 35 //! 36 //! # let device: std::sync::Arc<vulkano::device::Device> = return; 37 //! let _sampler = sampler::Sampler::new(device.clone(), sampler::Filter::Linear, 38 //! sampler::Filter::Linear, 39 //! sampler::MipmapMode::Nearest, 40 //! sampler::SamplerAddressMode::Repeat, 41 //! sampler::SamplerAddressMode::Repeat, 42 //! sampler::SamplerAddressMode::Repeat, 1.0, 1.0, 43 //! 0.0, 100.0).unwrap();; 44 //! ``` 45 //! 46 //! # About border colors 47 //! 48 //! One of the possible values of `SamplerAddressMode` and `UnnormalizedSamplerAddressMode` is 49 //! `ClampToBorder`. This value indicates that accessing an image outside of its range must return 50 //! the specified color. 51 //! 52 //! However this comes with restrictions. When using a floating-point border color, the sampler can 53 //! only be used with floating-point or depth image views. When using an integer border color, the 54 //! sampler can only be used with integer or stencil image views. In addition to this, you can't 55 //! use an opaque black border color with an image view that uses components swizzling. 56 //! 57 //! > **Note**: The reason for this restriction about opaque black borders is that the value of the 58 //! > alpha is 1.0 while the value of the color components is 0.0. In the other border colors, the 59 //! > value of all the components is the same. 60 //! 61 //! Samplers that don't use `ClampToBorder` are not concerned by these restrictions. 62 //! 63 // FIXME: restrictions aren't checked yet 64 65 use crate::check_errors; 66 use crate::device::Device; 67 use crate::device::DeviceOwned; 68 pub use crate::pipeline::depth_stencil::Compare; 69 use crate::Error; 70 use crate::OomError; 71 use crate::VulkanObject; 72 use std::error; 73 use std::fmt; 74 use std::mem::MaybeUninit; 75 use std::ptr; 76 use std::sync::Arc; 77 78 /// Describes how to retrieve data from an image within a shader. 79 pub struct Sampler { 80 sampler: ash::vk::Sampler, 81 device: Arc<Device>, 82 compare_mode: bool, 83 unnormalized: bool, 84 usable_with_float_formats: bool, 85 usable_with_int_formats: bool, 86 usable_with_swizzling: bool, 87 } 88 89 impl Sampler { 90 /// Shortcut for creating a sampler with linear sampling, linear mipmaps, and with the repeat 91 /// mode for borders. 92 /// 93 /// Useful for prototyping, but can also be used in real projects. 94 /// 95 /// # Panic 96 /// 97 /// - Panics if out of memory or the maximum number of samplers has exceeded. 98 /// 99 #[inline] simple_repeat_linear(device: Arc<Device>) -> Arc<Sampler>100 pub fn simple_repeat_linear(device: Arc<Device>) -> Arc<Sampler> { 101 Sampler::new( 102 device, 103 Filter::Linear, 104 Filter::Linear, 105 MipmapMode::Linear, 106 SamplerAddressMode::Repeat, 107 SamplerAddressMode::Repeat, 108 SamplerAddressMode::Repeat, 109 0.0, 110 1.0, 111 0.0, 112 1_000.0, 113 ) 114 .unwrap() 115 } 116 117 /// Shortcut for creating a sampler with linear sampling, that only uses the main level of 118 /// images, and with the repeat mode for borders. 119 /// 120 /// Useful for prototyping, but can also be used in real projects. 121 /// 122 /// # Panic 123 /// 124 /// - Panics if out of memory or the maximum number of samplers has exceeded. 125 /// 126 #[inline] simple_repeat_linear_no_mipmap(device: Arc<Device>) -> Arc<Sampler>127 pub fn simple_repeat_linear_no_mipmap(device: Arc<Device>) -> Arc<Sampler> { 128 Sampler::new( 129 device, 130 Filter::Linear, 131 Filter::Linear, 132 MipmapMode::Nearest, 133 SamplerAddressMode::Repeat, 134 SamplerAddressMode::Repeat, 135 SamplerAddressMode::Repeat, 136 0.0, 137 1.0, 138 0.0, 139 1.0, 140 ) 141 .unwrap() 142 } 143 144 /// Creates a new `Sampler` with the given behavior. 145 /// 146 /// `mag_filter` and `min_filter` define how the implementation should sample from the image 147 /// when it is respectively larger and smaller than the original. 148 /// 149 /// `mipmap_mode` defines how the implementation should choose which mipmap to use. 150 /// 151 /// `address_u`, `address_v` and `address_w` define how the implementation should behave when 152 /// sampling outside of the texture coordinates range `[0.0, 1.0]`. 153 /// 154 /// `mip_lod_bias` is a value to add to . 155 /// 156 /// `max_anisotropy` must be greater than or equal to 1.0. If greater than 1.0, the 157 /// implementation will use anisotropic filtering. Using a value greater than 1.0 requires 158 /// the `sampler_anisotropy` feature to be enabled when creating the device. 159 /// 160 /// `min_lod` and `max_lod` are respectively the minimum and maximum mipmap level to use. 161 /// `max_lod` must always be greater than or equal to `min_lod`. 162 /// 163 /// # Panic 164 /// 165 /// - Panics if multiple `ClampToBorder` values are passed and the border color is different. 166 /// - Panics if `max_anisotropy < 1.0`. 167 /// - Panics if `min_lod > max_lod`. 168 /// 169 #[inline(always)] new( device: Arc<Device>, mag_filter: Filter, min_filter: Filter, mipmap_mode: MipmapMode, address_u: SamplerAddressMode, address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32, max_anisotropy: f32, min_lod: f32, max_lod: f32, ) -> Result<Arc<Sampler>, SamplerCreationError>170 pub fn new( 171 device: Arc<Device>, 172 mag_filter: Filter, 173 min_filter: Filter, 174 mipmap_mode: MipmapMode, 175 address_u: SamplerAddressMode, 176 address_v: SamplerAddressMode, 177 address_w: SamplerAddressMode, 178 mip_lod_bias: f32, 179 max_anisotropy: f32, 180 min_lod: f32, 181 max_lod: f32, 182 ) -> Result<Arc<Sampler>, SamplerCreationError> { 183 Sampler::new_impl( 184 device, 185 mag_filter, 186 min_filter, 187 mipmap_mode, 188 address_u, 189 address_v, 190 address_w, 191 mip_lod_bias, 192 max_anisotropy, 193 min_lod, 194 max_lod, 195 None, 196 ) 197 } 198 199 /// Creates a new `Sampler` with the given behavior. 200 /// 201 /// Contrary to `new`, this creates a sampler that is used to compare depth values. 202 /// 203 /// A sampler like this can only operate on depth or depth-stencil textures. Instead of 204 /// returning the value of the texture, this sampler will return a value between 0.0 and 1.0 205 /// indicating how much the reference value (passed by the shader) compares to the value in the 206 /// texture. 207 /// 208 /// Note that it doesn't make sense to create a compare-mode sampler with an integer border 209 /// color, as such a sampler would be unusable. 210 /// 211 /// # Panic 212 /// 213 /// Same panic reasons as `new`. 214 /// 215 #[inline(always)] compare( device: Arc<Device>, mag_filter: Filter, min_filter: Filter, mipmap_mode: MipmapMode, address_u: SamplerAddressMode, address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32, max_anisotropy: f32, min_lod: f32, max_lod: f32, compare: Compare, ) -> Result<Arc<Sampler>, SamplerCreationError>216 pub fn compare( 217 device: Arc<Device>, 218 mag_filter: Filter, 219 min_filter: Filter, 220 mipmap_mode: MipmapMode, 221 address_u: SamplerAddressMode, 222 address_v: SamplerAddressMode, 223 address_w: SamplerAddressMode, 224 mip_lod_bias: f32, 225 max_anisotropy: f32, 226 min_lod: f32, 227 max_lod: f32, 228 compare: Compare, 229 ) -> Result<Arc<Sampler>, SamplerCreationError> { 230 Sampler::new_impl( 231 device, 232 mag_filter, 233 min_filter, 234 mipmap_mode, 235 address_u, 236 address_v, 237 address_w, 238 mip_lod_bias, 239 max_anisotropy, 240 min_lod, 241 max_lod, 242 Some(compare), 243 ) 244 } 245 new_impl( device: Arc<Device>, mag_filter: Filter, min_filter: Filter, mipmap_mode: MipmapMode, address_u: SamplerAddressMode, address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32, max_anisotropy: f32, min_lod: f32, max_lod: f32, compare: Option<Compare>, ) -> Result<Arc<Sampler>, SamplerCreationError>246 fn new_impl( 247 device: Arc<Device>, 248 mag_filter: Filter, 249 min_filter: Filter, 250 mipmap_mode: MipmapMode, 251 address_u: SamplerAddressMode, 252 address_v: SamplerAddressMode, 253 address_w: SamplerAddressMode, 254 mip_lod_bias: f32, 255 max_anisotropy: f32, 256 min_lod: f32, 257 max_lod: f32, 258 compare: Option<Compare>, 259 ) -> Result<Arc<Sampler>, SamplerCreationError> { 260 assert!(max_anisotropy >= 1.0); 261 assert!(min_lod <= max_lod); 262 263 // Check max anisotropy. 264 if max_anisotropy > 1.0 { 265 if !device.enabled_features().sampler_anisotropy { 266 return Err(SamplerCreationError::SamplerAnisotropyFeatureNotEnabled); 267 } 268 269 let limit = device 270 .physical_device() 271 .properties() 272 .max_sampler_anisotropy; 273 if max_anisotropy > limit { 274 return Err(SamplerCreationError::AnisotropyLimitExceeded { 275 requested: max_anisotropy, 276 maximum: limit, 277 }); 278 } 279 } 280 281 // Check mip_lod_bias value. 282 { 283 let limit = device 284 .physical_device() 285 .properties() 286 .max_sampler_lod_bias; 287 if mip_lod_bias > limit { 288 return Err(SamplerCreationError::MipLodBiasLimitExceeded { 289 requested: mip_lod_bias, 290 maximum: limit, 291 }); 292 } 293 } 294 295 // Check MirrorClampToEdge extension support 296 if [address_u, address_v, address_w] 297 .iter() 298 .any(|&mode| mode == SamplerAddressMode::MirrorClampToEdge) 299 { 300 if !device.enabled_extensions().khr_sampler_mirror_clamp_to_edge { 301 return Err(SamplerCreationError::SamplerMirrorClampToEdgeExtensionNotEnabled); 302 } 303 } 304 305 // Handling border color. 306 let border_color = address_u.border_color(); 307 let border_color = match (border_color, address_v.border_color()) { 308 (Some(b1), Some(b2)) => { 309 assert_eq!(b1, b2); 310 Some(b1) 311 } 312 (None, b) => b, 313 (b, None) => b, 314 }; 315 let border_color = match (border_color, address_w.border_color()) { 316 (Some(b1), Some(b2)) => { 317 assert_eq!(b1, b2); 318 Some(b1) 319 } 320 (None, b) => b, 321 (b, None) => b, 322 }; 323 324 let fns = device.fns(); 325 let sampler = unsafe { 326 let infos = ash::vk::SamplerCreateInfo { 327 flags: ash::vk::SamplerCreateFlags::empty(), 328 mag_filter: mag_filter.into(), 329 min_filter: min_filter.into(), 330 mipmap_mode: mipmap_mode.into(), 331 address_mode_u: address_u.into(), 332 address_mode_v: address_v.into(), 333 address_mode_w: address_w.into(), 334 mip_lod_bias: mip_lod_bias, 335 anisotropy_enable: if max_anisotropy > 1.0 { 336 ash::vk::TRUE 337 } else { 338 ash::vk::FALSE 339 }, 340 max_anisotropy: max_anisotropy, 341 compare_enable: if compare.is_some() { 342 ash::vk::TRUE 343 } else { 344 ash::vk::FALSE 345 }, 346 compare_op: compare 347 .map(|c| c.into()) 348 .unwrap_or(ash::vk::CompareOp::NEVER), 349 min_lod: min_lod, 350 max_lod: max_lod, 351 border_color: border_color 352 .map(|b| b.into()) 353 .unwrap_or(ash::vk::BorderColor::FLOAT_TRANSPARENT_BLACK), 354 unnormalized_coordinates: ash::vk::FALSE, 355 ..Default::default() 356 }; 357 358 let mut output = MaybeUninit::uninit(); 359 check_errors(fns.v1_0.create_sampler( 360 device.internal_object(), 361 &infos, 362 ptr::null(), 363 output.as_mut_ptr(), 364 ))?; 365 output.assume_init() 366 }; 367 368 Ok(Arc::new(Sampler { 369 sampler: sampler, 370 device: device.clone(), 371 compare_mode: compare.is_some(), 372 unnormalized: false, 373 usable_with_float_formats: match border_color { 374 Some(BorderColor::FloatTransparentBlack) => true, 375 Some(BorderColor::FloatOpaqueBlack) => true, 376 Some(BorderColor::FloatOpaqueWhite) => true, 377 Some(_) => false, 378 None => true, 379 }, 380 usable_with_int_formats: compare.is_none() 381 && match border_color { 382 Some(BorderColor::IntTransparentBlack) => true, 383 Some(BorderColor::IntOpaqueBlack) => true, 384 Some(BorderColor::IntOpaqueWhite) => true, 385 Some(_) => false, 386 None => true, 387 }, 388 usable_with_swizzling: match border_color { 389 Some(BorderColor::FloatOpaqueBlack) => false, 390 Some(BorderColor::IntOpaqueBlack) => false, 391 _ => true, 392 }, 393 })) 394 } 395 396 /// Creates a sampler with unnormalized coordinates. This means that texture coordinates won't 397 /// range between `0.0` and `1.0` but use plain pixel offsets. 398 /// 399 /// Using an unnormalized sampler adds a few restrictions: 400 /// 401 /// - It can only be used with non-array 1D or 2D images. 402 /// - It can only be used with images with a single mipmap. 403 /// - Projection and offsets can't be used by shaders. Only the first mipmap can be accessed. 404 /// 405 /// # Panic 406 /// 407 /// - Panics if multiple `ClampToBorder` values are passed and the border color is different. 408 /// unnormalized( device: Arc<Device>, filter: Filter, address_u: UnnormalizedSamplerAddressMode, address_v: UnnormalizedSamplerAddressMode, ) -> Result<Arc<Sampler>, SamplerCreationError>409 pub fn unnormalized( 410 device: Arc<Device>, 411 filter: Filter, 412 address_u: UnnormalizedSamplerAddressMode, 413 address_v: UnnormalizedSamplerAddressMode, 414 ) -> Result<Arc<Sampler>, SamplerCreationError> { 415 let fns = device.fns(); 416 417 let border_color = address_u.border_color(); 418 let border_color = match (border_color, address_v.border_color()) { 419 (Some(b1), Some(b2)) => { 420 assert_eq!(b1, b2); 421 Some(b1) 422 } 423 (None, b) => b, 424 (b, None) => b, 425 }; 426 427 let sampler = unsafe { 428 let infos = ash::vk::SamplerCreateInfo { 429 flags: ash::vk::SamplerCreateFlags::empty(), 430 mag_filter: filter.into(), 431 min_filter: filter.into(), 432 mipmap_mode: ash::vk::SamplerMipmapMode::NEAREST, 433 address_mode_u: address_u.into(), 434 address_mode_v: address_v.into(), 435 address_mode_w: ash::vk::SamplerAddressMode::CLAMP_TO_EDGE, // unused by the impl 436 mip_lod_bias: 0.0, 437 anisotropy_enable: ash::vk::FALSE, 438 max_anisotropy: 1.0, 439 compare_enable: ash::vk::FALSE, 440 compare_op: ash::vk::CompareOp::NEVER, 441 min_lod: 0.0, 442 max_lod: 0.0, 443 border_color: border_color 444 .map(|b| b.into()) 445 .unwrap_or(ash::vk::BorderColor::FLOAT_TRANSPARENT_BLACK), 446 unnormalized_coordinates: ash::vk::TRUE, 447 ..Default::default() 448 }; 449 450 let mut output = MaybeUninit::uninit(); 451 check_errors(fns.v1_0.create_sampler( 452 device.internal_object(), 453 &infos, 454 ptr::null(), 455 output.as_mut_ptr(), 456 ))?; 457 output.assume_init() 458 }; 459 460 Ok(Arc::new(Sampler { 461 sampler: sampler, 462 device: device.clone(), 463 compare_mode: false, 464 unnormalized: true, 465 usable_with_float_formats: match border_color { 466 Some(BorderColor::FloatTransparentBlack) => true, 467 Some(BorderColor::FloatOpaqueBlack) => true, 468 Some(BorderColor::FloatOpaqueWhite) => true, 469 Some(_) => false, 470 None => true, 471 }, 472 usable_with_int_formats: match border_color { 473 Some(BorderColor::IntTransparentBlack) => true, 474 Some(BorderColor::IntOpaqueBlack) => true, 475 Some(BorderColor::IntOpaqueWhite) => true, 476 Some(_) => false, 477 None => true, 478 }, 479 usable_with_swizzling: match border_color { 480 Some(BorderColor::FloatOpaqueBlack) => false, 481 Some(BorderColor::IntOpaqueBlack) => false, 482 _ => true, 483 }, 484 })) 485 } 486 487 /// Returns true if the sampler is a compare-mode sampler. 488 #[inline] compare_mode(&self) -> bool489 pub fn compare_mode(&self) -> bool { 490 self.compare_mode 491 } 492 493 /// Returns true if the sampler is unnormalized. 494 #[inline] is_unnormalized(&self) -> bool495 pub fn is_unnormalized(&self) -> bool { 496 self.unnormalized 497 } 498 499 /// Returns true if the sampler can be used with floating-point image views. See the 500 /// documentation of the `sampler` module for more info. 501 #[inline] usable_with_float_formats(&self) -> bool502 pub fn usable_with_float_formats(&self) -> bool { 503 self.usable_with_float_formats 504 } 505 506 /// Returns true if the sampler can be used with integer image views. See the documentation of 507 /// the `sampler` module for more info. 508 #[inline] usable_with_int_formats(&self) -> bool509 pub fn usable_with_int_formats(&self) -> bool { 510 self.usable_with_int_formats 511 } 512 513 /// Returns true if the sampler can be used with image views that have non-identity swizzling. 514 /// See the documentation of the `sampler` module for more info. 515 #[inline] usable_with_swizzling(&self) -> bool516 pub fn usable_with_swizzling(&self) -> bool { 517 self.usable_with_swizzling 518 } 519 } 520 521 unsafe impl DeviceOwned for Sampler { 522 #[inline] device(&self) -> &Arc<Device>523 fn device(&self) -> &Arc<Device> { 524 &self.device 525 } 526 } 527 528 unsafe impl VulkanObject for Sampler { 529 type Object = ash::vk::Sampler; 530 531 #[inline] internal_object(&self) -> ash::vk::Sampler532 fn internal_object(&self) -> ash::vk::Sampler { 533 self.sampler 534 } 535 } 536 537 impl fmt::Debug for Sampler { 538 #[inline] fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>539 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 540 write!(fmt, "<Vulkan sampler {:?}>", self.sampler) 541 } 542 } 543 544 impl Drop for Sampler { 545 #[inline] drop(&mut self)546 fn drop(&mut self) { 547 unsafe { 548 let fns = self.device.fns(); 549 fns.v1_0 550 .destroy_sampler(self.device.internal_object(), self.sampler, ptr::null()); 551 } 552 } 553 } 554 555 /// Describes how the color of each pixel should be determined. 556 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 557 #[repr(i32)] 558 pub enum Filter { 559 /// The four pixels whose center surround the requested coordinates are taken, then their 560 /// values are interpolated. 561 Linear = ash::vk::Filter::LINEAR.as_raw(), 562 563 /// The pixel whose center is nearest to the requested coordinates is taken from the source 564 /// and its value is returned as-is. 565 Nearest = ash::vk::Filter::NEAREST.as_raw(), 566 } 567 568 impl From<Filter> for ash::vk::Filter { 569 #[inline] from(val: Filter) -> Self570 fn from(val: Filter) -> Self { 571 Self::from_raw(val as i32) 572 } 573 } 574 575 /// Describes which mipmap from the source to use. 576 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 577 #[repr(i32)] 578 pub enum MipmapMode { 579 /// Use the mipmap whose dimensions are the nearest to the dimensions of the destination. 580 Nearest = ash::vk::SamplerMipmapMode::NEAREST.as_raw(), 581 582 /// Take the mipmap whose dimensions are no greater than that of the destination together 583 /// with the next higher level mipmap, calculate the value for both, and interpolate them. 584 Linear = ash::vk::SamplerMipmapMode::LINEAR.as_raw(), 585 } 586 587 impl From<MipmapMode> for ash::vk::SamplerMipmapMode { 588 #[inline] from(val: MipmapMode) -> Self589 fn from(val: MipmapMode) -> Self { 590 Self::from_raw(val as i32) 591 } 592 } 593 594 /// How the sampler should behave when it needs to access a pixel that is out of range of the 595 /// texture. 596 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 597 pub enum SamplerAddressMode { 598 /// Repeat the texture. In other words, the pixel at coordinate `x + 1.0` is the same as the 599 /// one at coordinate `x`. 600 Repeat, 601 602 /// Repeat the texture but mirror it at every repetition. In other words, the pixel at 603 /// coordinate `x + 1.0` is the same as the one at coordinate `1.0 - x`. 604 MirroredRepeat, 605 606 /// The coordinates are clamped to the valid range. Coordinates below 0.0 have the same value 607 /// as coordinate 0.0. Coordinates over 1.0 have the same value as coordinate 1.0. 608 ClampToEdge, 609 610 /// Any pixel out of range is considered to be part of the "border" of the image, which has a 611 /// specific color of your choice. 612 /// 613 /// Note that if you use `ClampToBorder` multiple times, they must all have the same border 614 /// color. 615 ClampToBorder(BorderColor), 616 617 /// Similar to `MirroredRepeat`, except that coordinates are clamped to the range 618 /// `[-1.0, 1.0]`. 619 MirrorClampToEdge, 620 } 621 622 impl SamplerAddressMode { 623 #[inline] border_color(self) -> Option<BorderColor>624 fn border_color(self) -> Option<BorderColor> { 625 match self { 626 SamplerAddressMode::ClampToBorder(c) => Some(c), 627 _ => None, 628 } 629 } 630 } 631 632 impl From<SamplerAddressMode> for ash::vk::SamplerAddressMode { 633 #[inline] from(val: SamplerAddressMode) -> Self634 fn from(val: SamplerAddressMode) -> Self { 635 match val { 636 SamplerAddressMode::Repeat => ash::vk::SamplerAddressMode::REPEAT, 637 SamplerAddressMode::MirroredRepeat => ash::vk::SamplerAddressMode::MIRRORED_REPEAT, 638 SamplerAddressMode::ClampToEdge => ash::vk::SamplerAddressMode::CLAMP_TO_EDGE, 639 SamplerAddressMode::ClampToBorder(_) => ash::vk::SamplerAddressMode::CLAMP_TO_BORDER, 640 SamplerAddressMode::MirrorClampToEdge => { 641 ash::vk::SamplerAddressMode::MIRROR_CLAMP_TO_EDGE 642 } 643 } 644 } 645 } 646 647 /// How the sampler should behave when it needs to access a pixel that is out of range of the 648 /// texture. 649 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 650 #[repr(u32)] 651 pub enum UnnormalizedSamplerAddressMode { 652 /// The coordinates are clamped to the valid range. Coordinates below 0 have the same value 653 /// as coordinate 0. Coordinates over *size of texture* have the same value as coordinate 654 /// *size of texture*. 655 ClampToEdge, 656 657 /// Any pixel out of range is considered to be part of the "border" of the image, which has a 658 /// specific color of your choice. 659 /// 660 /// Note that if you use `ClampToBorder` multiple times, they must all have the same border 661 /// color. 662 ClampToBorder(BorderColor), 663 } 664 665 impl UnnormalizedSamplerAddressMode { 666 #[inline] border_color(self) -> Option<BorderColor>667 fn border_color(self) -> Option<BorderColor> { 668 match self { 669 UnnormalizedSamplerAddressMode::ClampToEdge => None, 670 UnnormalizedSamplerAddressMode::ClampToBorder(c) => Some(c), 671 } 672 } 673 } 674 675 impl From<UnnormalizedSamplerAddressMode> for ash::vk::SamplerAddressMode { 676 #[inline] from(val: UnnormalizedSamplerAddressMode) -> Self677 fn from(val: UnnormalizedSamplerAddressMode) -> Self { 678 match val { 679 UnnormalizedSamplerAddressMode::ClampToEdge => { 680 ash::vk::SamplerAddressMode::CLAMP_TO_EDGE 681 } 682 UnnormalizedSamplerAddressMode::ClampToBorder(_) => { 683 ash::vk::SamplerAddressMode::CLAMP_TO_BORDER 684 } 685 } 686 } 687 } 688 689 /// The color to use for the border of an image. 690 /// 691 /// Only relevant if you use `ClampToBorder`. 692 /// 693 /// Using a border color restricts the sampler to either floating-point images or integer images. 694 /// See the documentation of the `sampler` module for more info. 695 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 696 #[repr(i32)] 697 pub enum BorderColor { 698 /// The value `(0.0, 0.0, 0.0, 0.0)`. Can only be used with floating-point images. 699 FloatTransparentBlack = ash::vk::BorderColor::FLOAT_TRANSPARENT_BLACK.as_raw(), 700 701 /// The value `(0, 0, 0, 0)`. Can only be used with integer images. 702 IntTransparentBlack = ash::vk::BorderColor::INT_TRANSPARENT_BLACK.as_raw(), 703 704 /// The value `(0.0, 0.0, 0.0, 1.0)`. Can only be used with floating-point identity-swizzled 705 /// images. 706 FloatOpaqueBlack = ash::vk::BorderColor::FLOAT_OPAQUE_BLACK.as_raw(), 707 708 /// The value `(0, 0, 0, 1)`. Can only be used with integer identity-swizzled images. 709 IntOpaqueBlack = ash::vk::BorderColor::INT_OPAQUE_BLACK.as_raw(), 710 711 /// The value `(1.0, 1.0, 1.0, 1.0)`. Can only be used with floating-point images. 712 FloatOpaqueWhite = ash::vk::BorderColor::FLOAT_OPAQUE_WHITE.as_raw(), 713 714 /// The value `(1, 1, 1, 1)`. Can only be used with integer images. 715 IntOpaqueWhite = ash::vk::BorderColor::INT_OPAQUE_WHITE.as_raw(), 716 } 717 718 impl From<BorderColor> for ash::vk::BorderColor { 719 #[inline] from(val: BorderColor) -> Self720 fn from(val: BorderColor) -> Self { 721 Self::from_raw(val as i32) 722 } 723 } 724 725 /// Error that can happen when creating an instance. 726 #[derive(Clone, Debug, PartialEq)] 727 pub enum SamplerCreationError { 728 /// Not enough memory. 729 OomError(OomError), 730 731 /// Too many sampler objects have been created. You must destroy some before creating new ones. 732 /// Note the specs guarantee that at least 4000 samplers can exist simultaneously. 733 TooManyObjects, 734 735 /// Using an anisotropy greater than 1.0 requires enabling the `sampler_anisotropy` feature 736 /// when creating the device. 737 SamplerAnisotropyFeatureNotEnabled, 738 739 /// The requested anisotropy level exceeds the device's limits. 740 AnisotropyLimitExceeded { 741 /// The value that was requested. 742 requested: f32, 743 /// The maximum supported value. 744 maximum: f32, 745 }, 746 747 /// The requested mip lod bias exceeds the device's limits. 748 MipLodBiasLimitExceeded { 749 /// The value that was requested. 750 requested: f32, 751 /// The maximum supported value. 752 maximum: f32, 753 }, 754 755 /// Using `MirrorClampToEdge` requires enabling the `VK_KHR_sampler_mirror_clamp_to_edge` 756 /// extension when creating the device. 757 SamplerMirrorClampToEdgeExtensionNotEnabled, 758 } 759 760 impl error::Error for SamplerCreationError { 761 #[inline] source(&self) -> Option<&(dyn error::Error + 'static)>762 fn source(&self) -> Option<&(dyn error::Error + 'static)> { 763 match *self { 764 SamplerCreationError::OomError(ref err) => Some(err), 765 _ => None, 766 } 767 } 768 } 769 770 impl fmt::Display for SamplerCreationError { 771 #[inline] fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>772 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 773 write!( 774 fmt, 775 "{}", 776 match *self { 777 SamplerCreationError::OomError(_) => "not enough memory available", 778 SamplerCreationError::TooManyObjects => "too many simultaneous sampler objects", 779 SamplerCreationError::SamplerAnisotropyFeatureNotEnabled => { 780 "the `sampler_anisotropy` feature is not enabled" 781 } 782 SamplerCreationError::AnisotropyLimitExceeded { .. } => "anisotropy limit exceeded", 783 SamplerCreationError::MipLodBiasLimitExceeded { .. } => 784 "mip lod bias limit exceeded", 785 SamplerCreationError::SamplerMirrorClampToEdgeExtensionNotEnabled => { 786 "the device extension `VK_KHR_sampler_mirror_clamp_to_edge` is not enabled" 787 } 788 } 789 ) 790 } 791 } 792 793 impl From<OomError> for SamplerCreationError { 794 #[inline] from(err: OomError) -> SamplerCreationError795 fn from(err: OomError) -> SamplerCreationError { 796 SamplerCreationError::OomError(err) 797 } 798 } 799 800 impl From<Error> for SamplerCreationError { 801 #[inline] from(err: Error) -> SamplerCreationError802 fn from(err: Error) -> SamplerCreationError { 803 match err { 804 err @ Error::OutOfHostMemory => SamplerCreationError::OomError(OomError::from(err)), 805 err @ Error::OutOfDeviceMemory => SamplerCreationError::OomError(OomError::from(err)), 806 Error::TooManyObjects => SamplerCreationError::TooManyObjects, 807 _ => panic!("unexpected error: {:?}", err), 808 } 809 } 810 } 811 812 #[cfg(test)] 813 mod tests { 814 use crate::sampler; 815 816 #[test] create_regular()817 fn create_regular() { 818 let (device, queue) = gfx_dev_and_queue!(); 819 820 let s = sampler::Sampler::new( 821 device, 822 sampler::Filter::Linear, 823 sampler::Filter::Linear, 824 sampler::MipmapMode::Nearest, 825 sampler::SamplerAddressMode::Repeat, 826 sampler::SamplerAddressMode::Repeat, 827 sampler::SamplerAddressMode::Repeat, 828 1.0, 829 1.0, 830 0.0, 831 2.0, 832 ) 833 .unwrap(); 834 assert!(!s.compare_mode()); 835 assert!(!s.is_unnormalized()); 836 } 837 838 #[test] create_compare()839 fn create_compare() { 840 let (device, queue) = gfx_dev_and_queue!(); 841 842 let s = sampler::Sampler::compare( 843 device, 844 sampler::Filter::Linear, 845 sampler::Filter::Linear, 846 sampler::MipmapMode::Nearest, 847 sampler::SamplerAddressMode::Repeat, 848 sampler::SamplerAddressMode::Repeat, 849 sampler::SamplerAddressMode::Repeat, 850 1.0, 851 1.0, 852 0.0, 853 2.0, 854 sampler::Compare::Less, 855 ) 856 .unwrap(); 857 858 assert!(s.compare_mode()); 859 assert!(!s.is_unnormalized()); 860 } 861 862 #[test] create_unnormalized()863 fn create_unnormalized() { 864 let (device, queue) = gfx_dev_and_queue!(); 865 866 let s = sampler::Sampler::unnormalized( 867 device, 868 sampler::Filter::Linear, 869 sampler::UnnormalizedSamplerAddressMode::ClampToEdge, 870 sampler::UnnormalizedSamplerAddressMode::ClampToEdge, 871 ) 872 .unwrap(); 873 874 assert!(!s.compare_mode()); 875 assert!(s.is_unnormalized()); 876 } 877 878 #[test] simple_repeat_linear()879 fn simple_repeat_linear() { 880 let (device, queue) = gfx_dev_and_queue!(); 881 let _ = sampler::Sampler::simple_repeat_linear(device); 882 } 883 884 #[test] simple_repeat_linear_no_mipmap()885 fn simple_repeat_linear_no_mipmap() { 886 let (device, queue) = gfx_dev_and_queue!(); 887 let _ = sampler::Sampler::simple_repeat_linear_no_mipmap(device); 888 } 889 890 #[test] min_lod_inferior()891 fn min_lod_inferior() { 892 let (device, queue) = gfx_dev_and_queue!(); 893 894 assert_should_panic!({ 895 let _ = sampler::Sampler::new( 896 device, 897 sampler::Filter::Linear, 898 sampler::Filter::Linear, 899 sampler::MipmapMode::Nearest, 900 sampler::SamplerAddressMode::Repeat, 901 sampler::SamplerAddressMode::Repeat, 902 sampler::SamplerAddressMode::Repeat, 903 1.0, 904 1.0, 905 5.0, 906 2.0, 907 ); 908 }); 909 } 910 911 #[test] max_anisotropy()912 fn max_anisotropy() { 913 let (device, queue) = gfx_dev_and_queue!(); 914 915 assert_should_panic!({ 916 let _ = sampler::Sampler::new( 917 device, 918 sampler::Filter::Linear, 919 sampler::Filter::Linear, 920 sampler::MipmapMode::Nearest, 921 sampler::SamplerAddressMode::Repeat, 922 sampler::SamplerAddressMode::Repeat, 923 sampler::SamplerAddressMode::Repeat, 924 1.0, 925 0.5, 926 0.0, 927 2.0, 928 ); 929 }); 930 } 931 932 #[test] different_borders()933 fn different_borders() { 934 let (device, queue) = gfx_dev_and_queue!(); 935 936 let b1 = sampler::BorderColor::IntTransparentBlack; 937 let b2 = sampler::BorderColor::FloatOpaqueWhite; 938 939 assert_should_panic!({ 940 let _ = sampler::Sampler::new( 941 device, 942 sampler::Filter::Linear, 943 sampler::Filter::Linear, 944 sampler::MipmapMode::Nearest, 945 sampler::SamplerAddressMode::ClampToBorder(b1), 946 sampler::SamplerAddressMode::ClampToBorder(b2), 947 sampler::SamplerAddressMode::Repeat, 948 1.0, 949 1.0, 950 5.0, 951 2.0, 952 ); 953 }); 954 } 955 956 #[test] anisotropy_feature()957 fn anisotropy_feature() { 958 let (device, queue) = gfx_dev_and_queue!(); 959 960 let r = sampler::Sampler::new( 961 device, 962 sampler::Filter::Linear, 963 sampler::Filter::Linear, 964 sampler::MipmapMode::Nearest, 965 sampler::SamplerAddressMode::Repeat, 966 sampler::SamplerAddressMode::Repeat, 967 sampler::SamplerAddressMode::Repeat, 968 1.0, 969 2.0, 970 0.0, 971 2.0, 972 ); 973 974 match r { 975 Err(sampler::SamplerCreationError::SamplerAnisotropyFeatureNotEnabled) => (), 976 _ => panic!(), 977 } 978 } 979 980 #[test] anisotropy_limit()981 fn anisotropy_limit() { 982 let (device, queue) = gfx_dev_and_queue!(sampler_anisotropy); 983 984 let r = sampler::Sampler::new( 985 device, 986 sampler::Filter::Linear, 987 sampler::Filter::Linear, 988 sampler::MipmapMode::Nearest, 989 sampler::SamplerAddressMode::Repeat, 990 sampler::SamplerAddressMode::Repeat, 991 sampler::SamplerAddressMode::Repeat, 992 1.0, 993 100000000.0, 994 0.0, 995 2.0, 996 ); 997 998 match r { 999 Err(sampler::SamplerCreationError::AnisotropyLimitExceeded { .. }) => (), 1000 _ => panic!(), 1001 } 1002 } 1003 1004 #[test] mip_lod_bias_limit()1005 fn mip_lod_bias_limit() { 1006 let (device, queue) = gfx_dev_and_queue!(); 1007 1008 let r = sampler::Sampler::new( 1009 device, 1010 sampler::Filter::Linear, 1011 sampler::Filter::Linear, 1012 sampler::MipmapMode::Nearest, 1013 sampler::SamplerAddressMode::Repeat, 1014 sampler::SamplerAddressMode::Repeat, 1015 sampler::SamplerAddressMode::Repeat, 1016 100000000.0, 1017 1.0, 1018 0.0, 1019 2.0, 1020 ); 1021 1022 match r { 1023 Err(sampler::SamplerCreationError::MipLodBiasLimitExceeded { .. }) => (), 1024 _ => panic!(), 1025 } 1026 } 1027 1028 #[test] sampler_mirror_clamp_to_edge_extension()1029 fn sampler_mirror_clamp_to_edge_extension() { 1030 let (device, queue) = gfx_dev_and_queue!(); 1031 1032 let r = sampler::Sampler::new( 1033 device, 1034 sampler::Filter::Linear, 1035 sampler::Filter::Linear, 1036 sampler::MipmapMode::Nearest, 1037 sampler::SamplerAddressMode::MirrorClampToEdge, 1038 sampler::SamplerAddressMode::MirrorClampToEdge, 1039 sampler::SamplerAddressMode::MirrorClampToEdge, 1040 1.0, 1041 1.0, 1042 0.0, 1043 2.0, 1044 ); 1045 1046 match r { 1047 Err(sampler::SamplerCreationError::SamplerMirrorClampToEdgeExtensionNotEnabled) => (), 1048 _ => panic!(), 1049 } 1050 } 1051 } 1052