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 use crate::check_errors; 11 use crate::device::Device; 12 use crate::device::DeviceOwned; 13 use crate::format::FormatTy; 14 use crate::image::ImageLayout; 15 use crate::image::SampleCount; 16 use crate::pipeline::shader::ShaderInterface; 17 use crate::render_pass::AttachmentDesc; 18 use crate::render_pass::LoadOp; 19 use crate::render_pass::RenderPassDesc; 20 use crate::render_pass::SubpassDesc; 21 use crate::Error; 22 use crate::OomError; 23 use crate::VulkanObject; 24 use smallvec::SmallVec; 25 use std::error; 26 use std::fmt; 27 use std::marker::PhantomData; 28 use std::mem::MaybeUninit; 29 use std::ptr; 30 use std::sync::Arc; 31 use std::sync::Mutex; 32 33 /// An object representing the discrete steps in which rendering is done. 34 /// 35 /// A render pass in Vulkan is made up of three parts: 36 /// - A list of attachments, which are image views that are inputs, outputs or intermediate stages 37 /// in the rendering process. 38 /// - One or more subpasses, which are the steps in which the rendering process, takes place, 39 /// and the attachments that are used for each step. 40 /// - Dependencies, which describe how the input and output data of each subpass is to be passed 41 /// from one subpass to the next. 42 /// 43 /// In order to create a render pass, you must create a `RenderPassDesc` object that describes the 44 /// render pass, then pass it to `RenderPass::new`. 45 /// 46 /// ``` 47 /// use vulkano::render_pass::RenderPass; 48 /// use vulkano::render_pass::RenderPassDesc; 49 /// 50 /// # let device: std::sync::Arc<vulkano::device::Device> = return; 51 /// let desc = RenderPassDesc::empty(); 52 /// let render_pass = RenderPass::new(device.clone(), desc).unwrap(); 53 /// ``` 54 /// 55 /// This example creates a render pass with no attachment and one single subpass that doesn't draw 56 /// on anything. While it's sometimes useful, most of the time it's not what you want. 57 /// 58 /// The easiest way to create a "real" render pass is to use the `single_pass_renderpass!` macro. 59 /// 60 /// ``` 61 /// # #[macro_use] extern crate vulkano; 62 /// # fn main() { 63 /// # let device: std::sync::Arc<vulkano::device::Device> = return; 64 /// use vulkano::format::Format; 65 /// 66 /// let render_pass = single_pass_renderpass!(device.clone(), 67 /// attachments: { 68 /// // `foo` is a custom name we give to the first and only attachment. 69 /// foo: { 70 /// load: Clear, 71 /// store: Store, 72 /// format: Format::R8G8B8A8Unorm, 73 /// samples: 1, 74 /// } 75 /// }, 76 /// pass: { 77 /// color: [foo], // Repeat the attachment name here. 78 /// depth_stencil: {} 79 /// } 80 /// ).unwrap(); 81 /// # } 82 /// ``` 83 /// 84 /// See the documentation of the macro for more details. TODO: put link here 85 pub struct RenderPass { 86 // The internal Vulkan object. 87 render_pass: ash::vk::RenderPass, 88 89 // Device this render pass was created from. 90 device: Arc<Device>, 91 92 // Description of the render pass. 93 desc: RenderPassDesc, 94 95 // Cache of the granularity of the render pass. 96 granularity: Mutex<Option<[u32; 2]>>, 97 } 98 99 impl RenderPass { 100 /// Builds a new render pass. 101 /// 102 /// # Panic 103 /// 104 /// - Can panic if it detects some violations in the restrictions. Only inexpensive checks are 105 /// performed. `debug_assert!` is used, so some restrictions are only checked in debug 106 /// mode. 107 /// new( device: Arc<Device>, description: RenderPassDesc, ) -> Result<RenderPass, RenderPassCreationError>108 pub fn new( 109 device: Arc<Device>, 110 description: RenderPassDesc, 111 ) -> Result<RenderPass, RenderPassCreationError> { 112 let fns = device.fns(); 113 114 // If the first use of an attachment in this render pass is as an input attachment, and 115 // the attachment is not also used as a color or depth/stencil attachment in the same 116 // subpass, then loadOp must not be VK_ATTACHMENT_LOAD_OP_CLEAR 117 debug_assert!(description.attachments().into_iter().enumerate().all( 118 |(atch_num, attachment)| { 119 if attachment.load != LoadOp::Clear { 120 return true; 121 } 122 123 for p in description.subpasses() { 124 if p.color_attachments 125 .iter() 126 .find(|&&(a, _)| a == atch_num) 127 .is_some() 128 { 129 return true; 130 } 131 if let Some((a, _)) = p.depth_stencil { 132 if a == atch_num { 133 return true; 134 } 135 } 136 if p.input_attachments 137 .iter() 138 .find(|&&(a, _)| a == atch_num) 139 .is_some() 140 { 141 return false; 142 } 143 } 144 145 true 146 } 147 )); 148 149 let attachments = description 150 .attachments() 151 .iter() 152 .map(|attachment| { 153 ash::vk::AttachmentDescription { 154 flags: ash::vk::AttachmentDescriptionFlags::empty(), // FIXME: may alias flag 155 format: attachment.format.into(), 156 samples: attachment.samples.into(), 157 load_op: attachment.load.into(), 158 store_op: attachment.store.into(), 159 stencil_load_op: attachment.stencil_load.into(), 160 stencil_store_op: attachment.stencil_store.into(), 161 initial_layout: attachment.initial_layout.into(), 162 final_layout: attachment.final_layout.into(), 163 } 164 }) 165 .collect::<SmallVec<[_; 16]>>(); 166 167 // We need to pass pointers to vkAttachmentReference structs when creating the render pass. 168 // Therefore we need to allocate them in advance. 169 // 170 // This block allocates, for each pass, in order, all color attachment references, then all 171 // input attachment references, then all resolve attachment references, then the depth 172 // stencil attachment reference. 173 let attachment_references = description 174 .subpasses() 175 .iter() 176 .flat_map(|pass| { 177 // Performing some validation with debug asserts. 178 debug_assert!( 179 pass.resolve_attachments.is_empty() 180 || pass.resolve_attachments.len() == pass.color_attachments.len() 181 ); 182 debug_assert!(pass 183 .resolve_attachments 184 .iter() 185 .all(|a| attachments[a.0].samples == ash::vk::SampleCountFlags::TYPE_1)); 186 debug_assert!( 187 pass.resolve_attachments.is_empty() 188 || pass 189 .color_attachments 190 .iter() 191 .all(|a| attachments[a.0].samples.as_raw() > 1) 192 ); 193 debug_assert!( 194 pass.resolve_attachments.is_empty() 195 || pass 196 .resolve_attachments 197 .iter() 198 .zip(pass.color_attachments.iter()) 199 .all(|(r, c)| { attachments[r.0].format == attachments[c.0].format }) 200 ); 201 debug_assert!(pass 202 .color_attachments 203 .iter() 204 .cloned() 205 .chain(pass.depth_stencil.clone().into_iter()) 206 .chain(pass.input_attachments.iter().cloned()) 207 .chain(pass.resolve_attachments.iter().cloned()) 208 .all(|(a, _)| { 209 pass.preserve_attachments 210 .iter() 211 .find(|&&b| a == b) 212 .is_none() 213 })); 214 debug_assert!(pass 215 .color_attachments 216 .iter() 217 .cloned() 218 .chain(pass.depth_stencil.clone().into_iter()) 219 .all(|(atch, layout)| { 220 if let Some(r) = pass.input_attachments.iter().find(|r| r.0 == atch) { 221 r.1 == layout 222 } else { 223 true 224 } 225 })); 226 227 let resolve = pass.resolve_attachments.iter().map(|&(offset, img_la)| { 228 debug_assert!(offset < attachments.len()); 229 ash::vk::AttachmentReference { 230 attachment: offset as u32, 231 layout: img_la.into(), 232 } 233 }); 234 235 let color = pass.color_attachments.iter().map(|&(offset, img_la)| { 236 debug_assert!(offset < attachments.len()); 237 ash::vk::AttachmentReference { 238 attachment: offset as u32, 239 layout: img_la.into(), 240 } 241 }); 242 243 let input = pass.input_attachments.iter().map(|&(offset, img_la)| { 244 debug_assert!(offset < attachments.len()); 245 ash::vk::AttachmentReference { 246 attachment: offset as u32, 247 layout: img_la.into(), 248 } 249 }); 250 251 let depthstencil = if let Some((offset, img_la)) = pass.depth_stencil { 252 Some(ash::vk::AttachmentReference { 253 attachment: offset as u32, 254 layout: img_la.into(), 255 }) 256 } else { 257 None 258 } 259 .into_iter(); 260 261 color.chain(input).chain(resolve).chain(depthstencil) 262 }) 263 .collect::<SmallVec<[_; 16]>>(); 264 265 // Same as `attachment_references` but only for the preserve attachments. 266 // This is separate because attachment references are u32s and not `vkAttachmentReference` 267 // structs. 268 let preserve_attachments_references = description 269 .subpasses() 270 .iter() 271 .flat_map(|pass| { 272 pass.preserve_attachments 273 .iter() 274 .map(|&offset| offset as u32) 275 }) 276 .collect::<SmallVec<[_; 16]>>(); 277 278 // Now iterating over passes. 279 let passes = unsafe { 280 // `ref_index` and `preserve_ref_index` are increased during the loop and point to the 281 // next element to use in respectively `attachment_references` and 282 // `preserve_attachments_references`. 283 let mut ref_index = 0usize; 284 let mut preserve_ref_index = 0usize; 285 let mut out: SmallVec<[_; 16]> = SmallVec::new(); 286 287 for pass in description.subpasses() { 288 if pass.color_attachments.len() as u32 289 > device 290 .physical_device() 291 .properties() 292 .max_color_attachments 293 { 294 return Err(RenderPassCreationError::ColorAttachmentsLimitExceeded); 295 } 296 297 let color_attachments = attachment_references.as_ptr().offset(ref_index as isize); 298 ref_index += pass.color_attachments.len(); 299 let input_attachments = attachment_references.as_ptr().offset(ref_index as isize); 300 ref_index += pass.input_attachments.len(); 301 let resolve_attachments = attachment_references.as_ptr().offset(ref_index as isize); 302 ref_index += pass.resolve_attachments.len(); 303 let depth_stencil = if pass.depth_stencil.is_some() { 304 let a = attachment_references.as_ptr().offset(ref_index as isize); 305 ref_index += 1; 306 a 307 } else { 308 ptr::null() 309 }; 310 311 let preserve_attachments = preserve_attachments_references 312 .as_ptr() 313 .offset(preserve_ref_index as isize); 314 preserve_ref_index += pass.preserve_attachments.len(); 315 316 out.push(ash::vk::SubpassDescription { 317 flags: ash::vk::SubpassDescriptionFlags::empty(), 318 pipeline_bind_point: ash::vk::PipelineBindPoint::GRAPHICS, 319 input_attachment_count: pass.input_attachments.len() as u32, 320 p_input_attachments: if pass.input_attachments.is_empty() { 321 ptr::null() 322 } else { 323 input_attachments 324 }, 325 color_attachment_count: pass.color_attachments.len() as u32, 326 p_color_attachments: if pass.color_attachments.is_empty() { 327 ptr::null() 328 } else { 329 color_attachments 330 }, 331 p_resolve_attachments: if pass.resolve_attachments.is_empty() { 332 ptr::null() 333 } else { 334 resolve_attachments 335 }, 336 p_depth_stencil_attachment: depth_stencil, 337 preserve_attachment_count: pass.preserve_attachments.len() as u32, 338 p_preserve_attachments: if pass.preserve_attachments.is_empty() { 339 ptr::null() 340 } else { 341 preserve_attachments 342 }, 343 }); 344 } 345 346 assert!(!out.is_empty()); 347 // If these assertions fails, there's a serious bug in the code above ^. 348 debug_assert!(ref_index == attachment_references.len()); 349 debug_assert!(preserve_ref_index == preserve_attachments_references.len()); 350 351 out 352 }; 353 354 let dependencies = description 355 .dependencies() 356 .iter() 357 .map(|dependency| { 358 debug_assert!( 359 dependency.source_subpass as u32 == ash::vk::SUBPASS_EXTERNAL 360 || dependency.source_subpass < passes.len() 361 ); 362 debug_assert!( 363 dependency.destination_subpass as u32 == ash::vk::SUBPASS_EXTERNAL 364 || dependency.destination_subpass < passes.len() 365 ); 366 367 ash::vk::SubpassDependency { 368 src_subpass: dependency.source_subpass as u32, 369 dst_subpass: dependency.destination_subpass as u32, 370 src_stage_mask: dependency.source_stages.into(), 371 dst_stage_mask: dependency.destination_stages.into(), 372 src_access_mask: dependency.source_access.into(), 373 dst_access_mask: dependency.destination_access.into(), 374 dependency_flags: if dependency.by_region { 375 ash::vk::DependencyFlags::BY_REGION 376 } else { 377 ash::vk::DependencyFlags::empty() 378 }, 379 } 380 }) 381 .collect::<SmallVec<[_; 16]>>(); 382 383 let multiview_create_info = match description.multiview() { 384 Some(multiview) => { 385 debug_assert!(device.enabled_features().multiview); 386 debug_assert!( 387 device 388 .physical_device() 389 .properties() 390 .max_multiview_view_count 391 .unwrap_or(0) 392 >= multiview.used_layer_count() 393 ); 394 395 // each subpass must have a corresponding view mask 396 // or there are no view masks at all (which is probably a bug because 397 // nothing will get drawn) 398 debug_assert!( 399 multiview.view_masks.len() == passes.len() || multiview.view_masks.is_empty() 400 ); 401 402 // either all subpasses must have a non-zero view mask or all must be zero 403 // (multiview is considered to be disabled when all view masks are zero) 404 debug_assert!( 405 multiview.view_masks.iter().all(|&mask| mask != 0) 406 || multiview.view_masks.iter().all(|&mask| mask == 0) 407 ); 408 409 // one view offset for each dependency 410 // or no view offsets at all 411 debug_assert!( 412 dependencies.len() == multiview.view_offsets.len() 413 || multiview.view_offsets.is_empty() 414 ); 415 416 // VUID-VkRenderPassCreateInfo-pNext-02512 417 debug_assert!(dependencies.iter().zip(&multiview.view_offsets).all( 418 |(dependency, &view_offset)| dependency 419 .dependency_flags 420 .contains(ash::vk::DependencyFlags::VIEW_LOCAL) 421 || view_offset == 0 422 )); 423 424 // VUID-VkRenderPassCreateInfo-pNext-02514 425 debug_assert!( 426 multiview.view_masks.iter().any(|&view_mask| view_mask != 0) 427 || dependencies.iter().all(|dependency| !dependency 428 .dependency_flags 429 .contains(ash::vk::DependencyFlags::VIEW_LOCAL)) 430 ); 431 432 // VUID-VkRenderPassCreateInfo-pNext-02515 433 debug_assert!( 434 multiview.view_masks.iter().any(|&view_mask| view_mask != 0) 435 || multiview.correlation_masks.is_empty() 436 ); 437 438 // VUID-VkRenderPassMultiviewCreateInfo-pCorrelationMasks-00841 439 // ensure that each view index is contained in at most one correlation mask 440 // by checking for any overlap in all pairs of correlation masks 441 debug_assert!(multiview 442 .correlation_masks 443 .iter() 444 .enumerate() 445 .all(|(i, &mask)| multiview.correlation_masks[i + 1..] 446 .iter() 447 .all(|&other_mask| other_mask & mask == 0))); 448 449 ash::vk::RenderPassMultiviewCreateInfo { 450 subpass_count: passes.len() as u32, 451 p_view_masks: multiview.view_masks.as_ptr(), 452 dependency_count: dependencies.len() as u32, 453 p_view_offsets: multiview.view_offsets.as_ptr(), 454 correlation_mask_count: multiview.correlation_masks.len() as u32, 455 p_correlation_masks: multiview.correlation_masks.as_ptr(), 456 ..Default::default() 457 } 458 } 459 None => ash::vk::RenderPassMultiviewCreateInfo::default(), 460 }; 461 462 let render_pass = unsafe { 463 let infos = ash::vk::RenderPassCreateInfo { 464 p_next: if description.multiview().is_none() { 465 ptr::null() 466 } else { 467 &multiview_create_info as *const _ as _ 468 }, 469 flags: ash::vk::RenderPassCreateFlags::empty(), 470 attachment_count: attachments.len() as u32, 471 p_attachments: if attachments.is_empty() { 472 ptr::null() 473 } else { 474 attachments.as_ptr() 475 }, 476 subpass_count: passes.len() as u32, 477 p_subpasses: if passes.is_empty() { 478 ptr::null() 479 } else { 480 passes.as_ptr() 481 }, 482 dependency_count: dependencies.len() as u32, 483 p_dependencies: if dependencies.is_empty() { 484 ptr::null() 485 } else { 486 dependencies.as_ptr() 487 }, 488 ..Default::default() 489 }; 490 491 let mut output = MaybeUninit::uninit(); 492 check_errors(fns.v1_0.create_render_pass( 493 device.internal_object(), 494 &infos, 495 ptr::null(), 496 output.as_mut_ptr(), 497 ))?; 498 output.assume_init() 499 }; 500 501 Ok(RenderPass { 502 device: device.clone(), 503 render_pass, 504 desc: description, 505 granularity: Mutex::new(None), 506 }) 507 } 508 509 /// Builds a render pass with one subpass and no attachment. 510 /// 511 /// This method is useful for quick tests. 512 #[inline] empty_single_pass(device: Arc<Device>) -> Result<RenderPass, RenderPassCreationError>513 pub fn empty_single_pass(device: Arc<Device>) -> Result<RenderPass, RenderPassCreationError> { 514 RenderPass::new(device, RenderPassDesc::empty()) 515 } 516 517 #[inline] inner(&self) -> RenderPassSys518 pub fn inner(&self) -> RenderPassSys { 519 RenderPassSys(self.render_pass, PhantomData) 520 } 521 522 /// Returns the granularity of this render pass. 523 /// 524 /// If the render area of a render pass in a command buffer is a multiple of this granularity, 525 /// then the performance will be optimal. Performances are always optimal for render areas 526 /// that cover the whole framebuffer. granularity(&self) -> [u32; 2]527 pub fn granularity(&self) -> [u32; 2] { 528 let mut granularity = self.granularity.lock().unwrap(); 529 530 if let Some(&granularity) = granularity.as_ref() { 531 return granularity; 532 } 533 534 unsafe { 535 let fns = self.device.fns(); 536 let mut out = MaybeUninit::uninit(); 537 fns.v1_0.get_render_area_granularity( 538 self.device.internal_object(), 539 self.render_pass, 540 out.as_mut_ptr(), 541 ); 542 543 let out = out.assume_init(); 544 debug_assert_ne!(out.width, 0); 545 debug_assert_ne!(out.height, 0); 546 let gran = [out.width, out.height]; 547 *granularity = Some(gran); 548 gran 549 } 550 } 551 552 /// Returns the description of the render pass. 553 #[inline] desc(&self) -> &RenderPassDesc554 pub fn desc(&self) -> &RenderPassDesc { 555 &self.desc 556 } 557 } 558 559 unsafe impl DeviceOwned for RenderPass { 560 #[inline] device(&self) -> &Arc<Device>561 fn device(&self) -> &Arc<Device> { 562 &self.device 563 } 564 } 565 566 impl fmt::Debug for RenderPass { fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>567 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 568 fmt.debug_struct("RenderPass") 569 .field("raw", &self.render_pass) 570 .field("device", &self.device) 571 .field("desc", &self.desc) 572 .finish() 573 } 574 } 575 576 impl Drop for RenderPass { 577 #[inline] drop(&mut self)578 fn drop(&mut self) { 579 unsafe { 580 let fns = self.device.fns(); 581 fns.v1_0.destroy_render_pass( 582 self.device.internal_object(), 583 self.render_pass, 584 ptr::null(), 585 ); 586 } 587 } 588 } 589 590 /// Opaque object that represents the render pass' internals. 591 #[derive(Debug, Copy, Clone)] 592 pub struct RenderPassSys<'a>(ash::vk::RenderPass, PhantomData<&'a ()>); 593 594 unsafe impl<'a> VulkanObject for RenderPassSys<'a> { 595 type Object = ash::vk::RenderPass; 596 597 #[inline] internal_object(&self) -> ash::vk::RenderPass598 fn internal_object(&self) -> ash::vk::RenderPass { 599 self.0 600 } 601 } 602 603 /// Error that can happen when creating a compute pipeline. 604 #[derive(Clone, Debug, PartialEq, Eq)] 605 pub enum RenderPassCreationError { 606 /// Not enough memory. 607 OomError(OomError), 608 /// The maximum number of color attachments has been exceeded. 609 ColorAttachmentsLimitExceeded, 610 } 611 612 impl error::Error for RenderPassCreationError { 613 #[inline] source(&self) -> Option<&(dyn error::Error + 'static)>614 fn source(&self) -> Option<&(dyn error::Error + 'static)> { 615 match *self { 616 RenderPassCreationError::OomError(ref err) => Some(err), 617 _ => None, 618 } 619 } 620 } 621 622 impl fmt::Display for RenderPassCreationError { 623 #[inline] fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>624 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 625 write!( 626 fmt, 627 "{}", 628 match *self { 629 RenderPassCreationError::OomError(_) => "not enough memory available", 630 RenderPassCreationError::ColorAttachmentsLimitExceeded => { 631 "the maximum number of color attachments has been exceeded" 632 } 633 } 634 ) 635 } 636 } 637 638 impl From<OomError> for RenderPassCreationError { 639 #[inline] from(err: OomError) -> RenderPassCreationError640 fn from(err: OomError) -> RenderPassCreationError { 641 RenderPassCreationError::OomError(err) 642 } 643 } 644 645 impl From<Error> for RenderPassCreationError { 646 #[inline] from(err: Error) -> RenderPassCreationError647 fn from(err: Error) -> RenderPassCreationError { 648 match err { 649 err @ Error::OutOfHostMemory => RenderPassCreationError::OomError(OomError::from(err)), 650 err @ Error::OutOfDeviceMemory => { 651 RenderPassCreationError::OomError(OomError::from(err)) 652 } 653 _ => panic!("unexpected error: {:?}", err), 654 } 655 } 656 } 657 658 /// Represents a subpass within a `RenderPass` object. 659 /// 660 /// This struct doesn't correspond to anything in Vulkan. It is simply an equivalent to a 661 /// tuple of a render pass and subpass index. Contrary to a tuple, however, the existence of the 662 /// subpass is checked when the object is created. When you have a `Subpass` you are guaranteed 663 /// that the given subpass does exist. 664 #[derive(Debug, Clone)] 665 pub struct Subpass { 666 render_pass: Arc<RenderPass>, 667 subpass_id: u32, 668 } 669 670 impl Subpass { 671 /// Returns a handle that represents a subpass of a render pass. 672 #[inline] from(render_pass: Arc<RenderPass>, id: u32) -> Option<Subpass>673 pub fn from(render_pass: Arc<RenderPass>, id: u32) -> Option<Subpass> { 674 if (id as usize) < render_pass.desc().subpasses().len() { 675 Some(Subpass { 676 render_pass, 677 subpass_id: id, 678 }) 679 } else { 680 None 681 } 682 } 683 684 #[inline] subpass_desc(&self) -> &SubpassDesc685 fn subpass_desc(&self) -> &SubpassDesc { 686 &self.render_pass.desc().subpasses()[self.subpass_id as usize] 687 } 688 689 #[inline] attachment_desc(&self, atch_num: usize) -> &AttachmentDesc690 fn attachment_desc(&self, atch_num: usize) -> &AttachmentDesc { 691 &self.render_pass.desc().attachments()[atch_num] 692 } 693 694 /// Returns the number of color attachments in this subpass. 695 #[inline] num_color_attachments(&self) -> u32696 pub fn num_color_attachments(&self) -> u32 { 697 self.subpass_desc().color_attachments.len() as u32 698 } 699 700 /// Returns true if the subpass has a depth attachment or a depth-stencil attachment. 701 #[inline] has_depth(&self) -> bool702 pub fn has_depth(&self) -> bool { 703 let subpass_desc = self.subpass_desc(); 704 let atch_num = match subpass_desc.depth_stencil { 705 Some((d, _)) => d, 706 None => return false, 707 }; 708 709 match self.attachment_desc(atch_num).format.ty() { 710 FormatTy::Depth => true, 711 FormatTy::Stencil => false, 712 FormatTy::DepthStencil => true, 713 _ => unreachable!(), 714 } 715 } 716 717 /// Returns true if the subpass has a depth attachment or a depth-stencil attachment whose 718 /// layout is not `DepthStencilReadOnlyOptimal`. 719 #[inline] has_writable_depth(&self) -> bool720 pub fn has_writable_depth(&self) -> bool { 721 let subpass_desc = self.subpass_desc(); 722 let atch_num = match subpass_desc.depth_stencil { 723 Some((d, l)) => { 724 if l == ImageLayout::DepthStencilReadOnlyOptimal { 725 return false; 726 } 727 d 728 } 729 None => return false, 730 }; 731 732 match self.attachment_desc(atch_num).format.ty() { 733 FormatTy::Depth => true, 734 FormatTy::Stencil => false, 735 FormatTy::DepthStencil => true, 736 _ => unreachable!(), 737 } 738 } 739 740 /// Returns true if the subpass has a stencil attachment or a depth-stencil attachment. 741 #[inline] has_stencil(&self) -> bool742 pub fn has_stencil(&self) -> bool { 743 let subpass_desc = self.subpass_desc(); 744 let atch_num = match subpass_desc.depth_stencil { 745 Some((d, _)) => d, 746 None => return false, 747 }; 748 749 match self.attachment_desc(atch_num).format.ty() { 750 FormatTy::Depth => false, 751 FormatTy::Stencil => true, 752 FormatTy::DepthStencil => true, 753 _ => unreachable!(), 754 } 755 } 756 757 /// Returns true if the subpass has a stencil attachment or a depth-stencil attachment whose 758 /// layout is not `DepthStencilReadOnlyOptimal`. 759 #[inline] has_writable_stencil(&self) -> bool760 pub fn has_writable_stencil(&self) -> bool { 761 let subpass_desc = self.subpass_desc(); 762 763 let atch_num = match subpass_desc.depth_stencil { 764 Some((d, l)) => { 765 if l == ImageLayout::DepthStencilReadOnlyOptimal { 766 return false; 767 } 768 d 769 } 770 None => return false, 771 }; 772 773 match self.attachment_desc(atch_num).format.ty() { 774 FormatTy::Depth => false, 775 FormatTy::Stencil => true, 776 FormatTy::DepthStencil => true, 777 _ => unreachable!(), 778 } 779 } 780 781 /// Returns true if the subpass has any color or depth/stencil attachment. 782 #[inline] has_color_or_depth_stencil_attachment(&self) -> bool783 pub fn has_color_or_depth_stencil_attachment(&self) -> bool { 784 if self.num_color_attachments() >= 1 { 785 return true; 786 } 787 788 let subpass_desc = self.subpass_desc(); 789 match subpass_desc.depth_stencil { 790 Some((d, _)) => true, 791 None => false, 792 } 793 } 794 795 /// Returns the number of samples in the color and/or depth/stencil attachments. Returns `None` 796 /// if there is no such attachment in this subpass. 797 #[inline] num_samples(&self) -> Option<SampleCount>798 pub fn num_samples(&self) -> Option<SampleCount> { 799 let subpass_desc = self.subpass_desc(); 800 801 // TODO: chain input attachments as well? 802 subpass_desc 803 .color_attachments 804 .iter() 805 .cloned() 806 .chain(subpass_desc.depth_stencil.clone().into_iter()) 807 .filter_map(|a| self.render_pass.desc().attachments().get(a.0)) 808 .next() 809 .map(|a| a.samples) 810 } 811 812 /// Returns the render pass of this subpass. 813 #[inline] render_pass(&self) -> &Arc<RenderPass>814 pub fn render_pass(&self) -> &Arc<RenderPass> { 815 &self.render_pass 816 } 817 818 /// Returns the index of this subpass within the renderpass. 819 #[inline] index(&self) -> u32820 pub fn index(&self) -> u32 { 821 self.subpass_id 822 } 823 824 /// Returns `true` if this subpass is compatible with the fragment output definition. 825 // TODO: return proper error is_compatible_with(&self, shader_interface: &ShaderInterface) -> bool826 pub fn is_compatible_with(&self, shader_interface: &ShaderInterface) -> bool { 827 self.render_pass 828 .desc() 829 .is_compatible_with_shader(self.subpass_id, shader_interface) 830 } 831 } 832 833 impl From<Subpass> for (Arc<RenderPass>, u32) { 834 #[inline] from(value: Subpass) -> (Arc<RenderPass>, u32)835 fn from(value: Subpass) -> (Arc<RenderPass>, u32) { 836 (value.render_pass, value.subpass_id) 837 } 838 } 839 840 #[cfg(test)] 841 mod tests { 842 use crate::format::Format; 843 use crate::render_pass::RenderPass; 844 use crate::render_pass::RenderPassCreationError; 845 846 #[test] empty()847 fn empty() { 848 let (device, _) = gfx_dev_and_queue!(); 849 let _ = RenderPass::empty_single_pass(device).unwrap(); 850 } 851 852 #[test] too_many_color_atch()853 fn too_many_color_atch() { 854 let (device, _) = gfx_dev_and_queue!(); 855 856 if device 857 .physical_device() 858 .properties() 859 .max_color_attachments 860 >= 10 861 { 862 return; // test ignored 863 } 864 865 let rp = single_pass_renderpass! { 866 device.clone(), 867 attachments: { 868 a1: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, 869 a2: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, 870 a3: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, 871 a4: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, 872 a5: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, 873 a6: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, 874 a7: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, 875 a8: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, 876 a9: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, 877 a10: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } 878 }, 879 pass: { 880 color: [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10], 881 depth_stencil: {} 882 } 883 }; 884 885 match rp { 886 Err(RenderPassCreationError::ColorAttachmentsLimitExceeded) => (), 887 _ => panic!(), 888 } 889 } 890 891 #[test] non_zero_granularity()892 fn non_zero_granularity() { 893 let (device, _) = gfx_dev_and_queue!(); 894 895 let rp = single_pass_renderpass! { 896 device.clone(), 897 attachments: { 898 a: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } 899 }, 900 pass: { 901 color: [a], 902 depth_stencil: {} 903 } 904 } 905 .unwrap(); 906 907 let granularity = rp.granularity(); 908 assert_ne!(granularity[0], 0); 909 assert_ne!(granularity[1], 0); 910 } 911 } 912