Skip to main content

vk_graph/driver/
render_pass.rs

1//! Render pass related types.
2
3use {
4    super::{
5        DriverError,
6        device::Device,
7        graphics::{DepthStencilInfo, GraphicsPipeline},
8        image::SampleCount,
9    },
10    ash::vk::{self, Handle},
11    log::{trace, warn},
12    std::{
13        collections::{HashMap, hash_map::Entry},
14        slice,
15        thread::panicking,
16    },
17};
18
19#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
20pub(crate) struct AttachmentInfo {
21    pub flags: vk::AttachmentDescriptionFlags,
22    pub format: vk::Format,
23    pub sample_count: SampleCount,
24    pub load_op: vk::AttachmentLoadOp,
25    pub store_op: vk::AttachmentStoreOp,
26    pub stencil_load_op: vk::AttachmentLoadOp,
27    pub stencil_store_op: vk::AttachmentStoreOp,
28    pub initial_layout: vk::ImageLayout,
29    pub final_layout: vk::ImageLayout,
30}
31
32impl From<AttachmentInfo> for vk::AttachmentDescription2<'_> {
33    fn from(value: AttachmentInfo) -> Self {
34        vk::AttachmentDescription2::default()
35            .flags(value.flags)
36            .format(value.format)
37            .samples(value.sample_count.into())
38            .load_op(value.load_op)
39            .store_op(value.store_op)
40            .stencil_load_op(value.stencil_load_op)
41            .stencil_store_op(value.stencil_store_op)
42            .initial_layout(value.initial_layout)
43            .final_layout(value.final_layout)
44    }
45}
46
47impl Default for AttachmentInfo {
48    fn default() -> Self {
49        AttachmentInfo {
50            flags: vk::AttachmentDescriptionFlags::MAY_ALIAS,
51            format: vk::Format::UNDEFINED,
52            sample_count: SampleCount::Type1,
53            initial_layout: vk::ImageLayout::UNDEFINED,
54            load_op: vk::AttachmentLoadOp::DONT_CARE,
55            stencil_load_op: vk::AttachmentLoadOp::DONT_CARE,
56            store_op: vk::AttachmentStoreOp::DONT_CARE,
57            stencil_store_op: vk::AttachmentStoreOp::DONT_CARE,
58            final_layout: vk::ImageLayout::UNDEFINED,
59        }
60    }
61}
62
63#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
64pub(crate) struct AttachmentRef {
65    pub attachment: u32,
66    pub aspect_mask: vk::ImageAspectFlags,
67    pub layout: vk::ImageLayout,
68}
69
70impl From<AttachmentRef> for vk::AttachmentReference2<'_> {
71    fn from(attachment_ref: AttachmentRef) -> Self {
72        vk::AttachmentReference2::default()
73            .attachment(attachment_ref.attachment)
74            .aspect_mask(attachment_ref.aspect_mask)
75            .layout(attachment_ref.layout)
76    }
77}
78
79#[derive(Clone, Debug, Eq, Hash, PartialEq)]
80pub(crate) struct FramebufferAttachmentImageInfo {
81    pub flags: vk::ImageCreateFlags,
82    pub usage: vk::ImageUsageFlags,
83    pub width: u32,
84    pub height: u32,
85    pub layer_count: u32,
86    pub view_formats: Vec<vk::Format>,
87}
88
89#[derive(Clone, Debug, Eq, Hash, PartialEq)]
90pub(crate) struct FramebufferInfo {
91    pub attachments: Vec<FramebufferAttachmentImageInfo>,
92}
93
94#[derive(Debug, Eq, Hash, PartialEq)]
95struct GraphicPipelineKey {
96    depth_stencil: Option<DepthStencilInfo>,
97    layout: vk::PipelineLayout,
98    subpass_idx: u32,
99}
100
101/// Vulkan render pass state and cached framebuffer/pipeline objects for compatible attachments.
102///
103/// See [`VkRenderPass`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkRenderPass.html).
104#[derive(Debug)]
105#[read_only::cast]
106pub(crate) struct RenderPass {
107    /// The device which owns this render pass resource.
108    ///
109    /// _Note:_ This field is read-only.
110    #[readonly]
111    pub device: Device,
112
113    framebuffers: HashMap<FramebufferInfo, vk::Framebuffer>,
114    graphic_pipelines: HashMap<GraphicPipelineKey, vk::Pipeline>,
115
116    /// The native Vulkan resource handle of this render pass.
117    ///
118    /// _Note:_ This field is read-only.
119    #[readonly]
120    pub handle: vk::RenderPass,
121
122    /// Information used to create this render pass resource.
123    ///
124    /// _Note:_ This field is read-only.
125    #[readonly]
126    pub(crate) info: RenderPassInfo,
127}
128
129impl RenderPass {
130    #[profiling::function]
131    pub(crate) fn create(device: &Device, info: RenderPassInfo) -> Result<Self, DriverError> {
132        trace!("create");
133
134        let device = device.clone();
135        let attachments = info
136            .attachments
137            .iter()
138            .copied()
139            .map(Into::into)
140            .collect::<Box<_>>();
141        let correlated_view_masks = if info.subpasses.iter().any(|subpass| subpass.view_mask != 0) {
142            {
143                info.subpasses
144                    .iter()
145                    .map(|subpass| subpass.correlated_view_mask)
146                    .collect::<Box<_>>()
147            }
148        } else {
149            Default::default()
150        };
151        let dependencies = info
152            .dependencies
153            .iter()
154            .copied()
155            .map(Into::into)
156            .collect::<Box<_>>();
157
158        let subpass_attachments = info
159            .subpasses
160            .iter()
161            .flat_map(|subpass| {
162                subpass
163                    .color_attachments
164                    .iter()
165                    .chain(subpass.input_attachments.iter())
166                    .chain(subpass.color_resolve_attachments.iter())
167                    .chain(subpass.depth_stencil_attachment.iter())
168                    .chain(
169                        subpass
170                            .depth_stencil_resolve_attachment
171                            .as_ref()
172                            .map(|(resolve_attachment, _, _)| resolve_attachment)
173                            .into_iter(),
174                    )
175                    .copied()
176                    .map(AttachmentRef::into)
177            })
178            .collect::<Box<[vk::AttachmentReference2]>>();
179        let mut subpass_depth_stencil_resolves = info
180            .subpasses
181            .iter()
182            .map(|subpass| {
183                subpass.depth_stencil_resolve_attachment.map(
184                    |(_, depth_resolve_mode, stencil_resolve_mode)| {
185                        vk::SubpassDescriptionDepthStencilResolve::default()
186                            .depth_resolve_mode(
187                                depth_resolve_mode.map(Into::into).unwrap_or_default(),
188                            )
189                            .stencil_resolve_mode(
190                                stencil_resolve_mode.map(Into::into).unwrap_or_default(),
191                            )
192                    },
193                )
194            })
195            .collect::<Box<_>>();
196        let mut subpasses = Vec::with_capacity(info.subpasses.len());
197
198        let mut base_idx = 0;
199        for (subpass, depth_stencil_resolve) in info
200            .subpasses
201            .iter()
202            .zip(subpass_depth_stencil_resolves.iter_mut())
203        {
204            let mut desc = vk::SubpassDescription2::default()
205                .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS);
206
207            debug_assert_eq!(
208                subpass.color_attachments.len(),
209                subpass.color_resolve_attachments.len()
210            );
211
212            let color_idx = base_idx;
213            let input_idx = color_idx + subpass.color_attachments.len();
214            let color_resolve_idx = input_idx + subpass.input_attachments.len();
215            let depth_stencil_idx = color_resolve_idx + subpass.color_resolve_attachments.len();
216            let depth_stencil_resolve_idx =
217                depth_stencil_idx + subpass.depth_stencil_attachment.is_some() as usize;
218            base_idx = depth_stencil_resolve_idx
219                + subpass.depth_stencil_resolve_attachment.is_some() as usize;
220
221            if subpass.depth_stencil_attachment.is_some() {
222                desc = desc.depth_stencil_attachment(&subpass_attachments[depth_stencil_idx]);
223            }
224
225            if let Some(depth_stencil_resolve) = depth_stencil_resolve {
226                *depth_stencil_resolve = depth_stencil_resolve.depth_stencil_resolve_attachment(
227                    &subpass_attachments[depth_stencil_resolve_idx],
228                );
229                desc = desc.push_next(depth_stencil_resolve);
230            }
231
232            subpasses.push(
233                desc.color_attachments(&subpass_attachments[color_idx..input_idx])
234                    .input_attachments(&subpass_attachments[input_idx..color_resolve_idx])
235                    .resolve_attachments(&subpass_attachments[color_resolve_idx..depth_stencil_idx])
236                    .preserve_attachments(&subpass.preserve_attachments)
237                    .view_mask(subpass.view_mask),
238            );
239        }
240
241        let handle = unsafe {
242            device.create_render_pass2(
243                &vk::RenderPassCreateInfo2::default()
244                    .attachments(&attachments)
245                    .correlated_view_masks(&correlated_view_masks)
246                    .dependencies(&dependencies)
247                    .subpasses(&subpasses),
248                None,
249            )
250        }
251        .map_err(|err| {
252            warn!("unable to create render pass: {err}");
253
254            DriverError::Unsupported
255        })?;
256
257        Ok(Self {
258            device,
259            framebuffers: Default::default(),
260            graphic_pipelines: Default::default(),
261            handle,
262            info,
263        })
264    }
265
266    #[profiling::function]
267    pub(crate) fn framebuffer(
268        &mut self,
269        info: FramebufferInfo,
270    ) -> Result<vk::Framebuffer, DriverError> {
271        debug_assert!(!info.attachments.is_empty());
272
273        let entry = self.framebuffers.entry(info);
274        if let Entry::Occupied(entry) = entry {
275            return Ok(*entry.get());
276        }
277
278        let entry = match entry {
279            Entry::Vacant(entry) => entry,
280            _ => unreachable!(),
281        };
282
283        let key = entry.key();
284        let layers = key
285            .attachments
286            .iter()
287            .map(|attachment| attachment.layer_count)
288            .max()
289            .unwrap_or(1);
290        let attachments = key
291            .attachments
292            .iter()
293            .map(|attachment| {
294                vk::FramebufferAttachmentImageInfo::default()
295                    .flags(attachment.flags)
296                    .width(attachment.width)
297                    .height(attachment.height)
298                    .layer_count(attachment.layer_count)
299                    .usage(attachment.usage)
300                    .view_formats(&attachment.view_formats)
301            })
302            .collect::<Box<_>>();
303        let mut imageless_info =
304            vk::FramebufferAttachmentsCreateInfoKHR::default().attachment_image_infos(&attachments);
305        let mut create_info = vk::FramebufferCreateInfo::default()
306            .flags(vk::FramebufferCreateFlags::IMAGELESS)
307            .render_pass(self.handle)
308            .width(attachments[0].width)
309            .height(attachments[0].height)
310            .layers(layers)
311            .push_next(&mut imageless_info);
312        create_info.attachment_count = self.info.attachments.len() as _;
313
314        let framebuffer =
315            unsafe { self.device.create_framebuffer(&create_info, None) }.map_err(|err| {
316                warn!("unable to create framebuffer: {err}");
317
318                DriverError::Unsupported
319            })?;
320
321        entry.insert(framebuffer);
322
323        Ok(framebuffer)
324    }
325
326    #[profiling::function]
327    pub(crate) fn pipeline_handle(
328        &mut self,
329        pipeline: &GraphicsPipeline,
330        depth_stencil: Option<DepthStencilInfo>,
331        subpass_idx: u32,
332    ) -> Result<vk::Pipeline, DriverError> {
333        let entry = self.graphic_pipelines.entry(GraphicPipelineKey {
334            depth_stencil,
335            layout: pipeline.inner.layout,
336            subpass_idx,
337        });
338        if let Entry::Occupied(entry) = entry {
339            let pipeline_handle = *entry.get();
340
341            if let Some(name) = Device::private_data_object_name(
342                &self.device,
343                vk::ObjectType::PIPELINE_LAYOUT,
344                pipeline.inner.layout,
345            ) && match Device::private_data_object_name(
346                &self.device,
347                vk::ObjectType::PIPELINE,
348                pipeline_handle,
349            ) {
350                None => true,
351                Some(previous) => previous != name,
352            } {
353                pipeline.set_variant_debug_name(pipeline_handle, self.handle, subpass_idx, &name);
354            }
355
356            return Ok(pipeline_handle);
357        }
358
359        let entry = match entry {
360            Entry::Vacant(entry) => entry,
361            _ => unreachable!(),
362        };
363
364        let color_blend_attachment_states = self.info.subpasses[subpass_idx as usize]
365            .color_attachments
366            .iter()
367            .map(|_| pipeline.inner.info.blend.into())
368            .collect::<Box<_>>();
369        let color_blend_state = vk::PipelineColorBlendStateCreateInfo::default()
370            .attachments(&color_blend_attachment_states);
371        let dynamic_state = vk::PipelineDynamicStateCreateInfo::default()
372            .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]);
373        let multisample_state = vk::PipelineMultisampleStateCreateInfo::default()
374            .alpha_to_coverage_enable(pipeline.inner.multisample.alpha_to_coverage_enable)
375            .alpha_to_one_enable(pipeline.inner.multisample.alpha_to_one_enable)
376            .flags(pipeline.inner.multisample.flags)
377            .min_sample_shading(pipeline.inner.multisample.min_sample_shading)
378            .rasterization_samples(pipeline.inner.multisample.rasterization_samples.into())
379            .sample_shading_enable(pipeline.inner.multisample.sample_shading_enable)
380            .sample_mask(&pipeline.inner.multisample.sample_mask);
381        let specializations = pipeline
382            .inner
383            .shader_stages
384            .iter()
385            .map(|stage| stage.specialization.as_ref().map(Into::into))
386            .collect::<Box<_>>();
387        let stages = pipeline
388            .inner
389            .shader_stages
390            .iter()
391            .zip(specializations.iter())
392            .map(|(stage, specialization)| {
393                let mut info = vk::PipelineShaderStageCreateInfo::default()
394                    .module(stage.module)
395                    .name(&stage.name)
396                    .stage(stage.flags);
397
398                if let Some(specialization) = specialization {
399                    info = info.specialization_info(specialization);
400                }
401
402                info
403            })
404            .collect::<Box<_>>();
405        let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::default()
406            .vertex_attribute_descriptions(
407                &pipeline.inner.vertex_input.vertex_attribute_descriptions,
408            )
409            .vertex_binding_descriptions(&pipeline.inner.vertex_input.vertex_binding_descriptions);
410        let viewport_state = vk::PipelineViewportStateCreateInfo::default()
411            .viewport_count(1)
412            .scissor_count(1);
413        let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo {
414            topology: pipeline.inner.info.topology,
415            ..Default::default()
416        };
417        let depth_stencil = depth_stencil.map(Into::into).unwrap_or_default();
418        let rasterization_state = vk::PipelineRasterizationStateCreateInfo {
419            front_face: pipeline.inner.info.front_face,
420            line_width: 1.0,
421            polygon_mode: pipeline.inner.info.polygon_mode,
422            cull_mode: pipeline.inner.info.cull_mode,
423            ..Default::default()
424        };
425        let create_info = vk::GraphicsPipelineCreateInfo::default()
426            .color_blend_state(&color_blend_state)
427            .depth_stencil_state(&depth_stencil)
428            .dynamic_state(&dynamic_state)
429            .input_assembly_state(&input_assembly_state)
430            .layout(pipeline.inner.layout)
431            .multisample_state(&multisample_state)
432            .rasterization_state(&rasterization_state)
433            .render_pass(self.handle)
434            .stages(&stages)
435            .subpass(subpass_idx)
436            .vertex_input_state(&vertex_input_state)
437            .viewport_state(&viewport_state);
438
439        let pipeline_handle = unsafe {
440            self.device.create_graphics_pipelines(
441                Device::pipeline_cache(&self.device),
442                slice::from_ref(&create_info),
443                None,
444            )
445        }
446        .map_err(|(_, err)| {
447            warn!("create_graphics_pipelines: {err}\n{:#?}", create_info);
448
449            DriverError::Unsupported
450        })?
451        .into_iter()
452        .find(|handle| !handle.is_null())
453        .ok_or_else(|| {
454            warn!("missing pipeline handle");
455
456            DriverError::Unsupported
457        })?;
458
459        if let Some(name) = Device::private_data_object_name(
460            &self.device,
461            vk::ObjectType::PIPELINE_LAYOUT,
462            pipeline.inner.layout,
463        ) && match Device::private_data_object_name(
464            &self.device,
465            vk::ObjectType::PIPELINE,
466            pipeline_handle,
467        ) {
468            None => true,
469            Some(previous) => previous != name,
470        } {
471            pipeline.set_variant_debug_name(pipeline_handle, self.handle, subpass_idx, &name);
472        }
473
474        entry.insert(pipeline_handle);
475
476        Ok(pipeline_handle)
477    }
478}
479
480impl Drop for RenderPass {
481    #[profiling::function]
482    fn drop(&mut self) {
483        if panicking() {
484            return;
485        }
486
487        for (_, framebuffer) in self.framebuffers.drain() {
488            unsafe {
489                self.device.destroy_framebuffer(framebuffer, None);
490            }
491        }
492
493        for (_, pipeline) in self.graphic_pipelines.drain() {
494            Device::clear_private_data_object_name(
495                &self.device,
496                vk::ObjectType::PIPELINE,
497                pipeline,
498            )
499            .unwrap_or_else(|err| warn!("unable to clear private data object name: {err}"));
500
501            unsafe {
502                self.device.destroy_pipeline(pipeline, None);
503            }
504        }
505
506        unsafe {
507            self.device.destroy_render_pass(self.handle, None);
508        }
509    }
510}
511
512/// Attachment, subpass, and dependency information used to create a [`RenderPass`].
513///
514/// See [`VkRenderPassCreateInfo2`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkRenderPassCreateInfo2.html).
515#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
516pub(crate) struct RenderPassInfo {
517    pub(crate) attachments: Vec<AttachmentInfo>,
518    pub(crate) subpasses: Vec<SubpassInfo>,
519    pub(crate) dependencies: Vec<SubpassDependency>,
520}
521
522/// Specifies depth and stencil resolve modes.
523///
524/// See [`VkResolveModeFlagBits`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkResolveModeFlagBits.html).
525#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
526pub enum ResolveMode {
527    /// The result of the resolve operation is the average of the sample values.
528    Average,
529
530    /// The result of the resolve operation is the maximum of the sample values.
531    Maximum,
532
533    /// The result of the resolve operation is the minimum of the sample values.
534    Minimum,
535
536    /// The result of the resolve operation is equal to the value of sample `0`.
537    SampleZero,
538}
539
540impl From<ResolveMode> for vk::ResolveModeFlags {
541    fn from(mode: ResolveMode) -> Self {
542        match mode {
543            ResolveMode::Average => vk::ResolveModeFlags::AVERAGE,
544            ResolveMode::Maximum => vk::ResolveModeFlags::MAX,
545            ResolveMode::Minimum => vk::ResolveModeFlags::MIN,
546            ResolveMode::SampleZero => vk::ResolveModeFlags::SAMPLE_ZERO,
547        }
548    }
549}
550
551#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
552pub(crate) struct SubpassDependency {
553    pub src_subpass: u32,
554    pub dst_subpass: u32,
555    pub src_stage_mask: vk::PipelineStageFlags,
556    pub dst_stage_mask: vk::PipelineStageFlags,
557    pub src_access_mask: vk::AccessFlags,
558    pub dst_access_mask: vk::AccessFlags,
559    pub dependency_flags: vk::DependencyFlags,
560}
561
562impl SubpassDependency {
563    pub fn new(src_subpass: u32, dst_subpass: u32) -> Self {
564        Self {
565            src_subpass,
566            dst_subpass,
567            src_stage_mask: vk::PipelineStageFlags::empty(),
568            dst_stage_mask: vk::PipelineStageFlags::empty(),
569            src_access_mask: vk::AccessFlags::empty(),
570            dst_access_mask: vk::AccessFlags::empty(),
571            dependency_flags: vk::DependencyFlags::empty(),
572        }
573    }
574}
575
576impl From<SubpassDependency> for vk::SubpassDependency2<'_> {
577    fn from(value: SubpassDependency) -> Self {
578        vk::SubpassDependency2::default()
579            .src_subpass(value.src_subpass)
580            .dst_subpass(value.dst_subpass)
581            .src_stage_mask(value.src_stage_mask)
582            .dst_stage_mask(value.dst_stage_mask)
583            .src_access_mask(value.src_access_mask)
584            .dst_access_mask(value.dst_access_mask)
585            .dependency_flags(value.dependency_flags)
586    }
587}
588
589#[derive(Clone, Debug, Eq, Hash, PartialEq)]
590pub(crate) struct SubpassInfo {
591    pub color_attachments: Vec<AttachmentRef>,
592    pub color_resolve_attachments: Vec<AttachmentRef>,
593    pub correlated_view_mask: u32,
594    pub depth_stencil_attachment: Option<AttachmentRef>,
595    pub depth_stencil_resolve_attachment:
596        Option<(AttachmentRef, Option<ResolveMode>, Option<ResolveMode>)>,
597    pub input_attachments: Vec<AttachmentRef>,
598    pub preserve_attachments: Vec<u32>,
599    pub view_mask: u32,
600}
601
602impl SubpassInfo {
603    pub fn with_capacity(capacity: usize) -> Self {
604        Self {
605            color_attachments: Vec::with_capacity(capacity),
606            color_resolve_attachments: Vec::with_capacity(capacity),
607            correlated_view_mask: 0,
608            depth_stencil_attachment: None,
609            depth_stencil_resolve_attachment: None,
610            input_attachments: Vec::with_capacity(capacity),
611            preserve_attachments: Vec::with_capacity(capacity),
612            view_mask: 0,
613        }
614    }
615}