• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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