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 is_multiview = self
285            .info
286            .subpasses
287            .iter()
288            .any(|subpass| subpass.view_mask != 0);
289        let layers = if is_multiview {
290            1
291        } else {
292            key.attachments
293                .iter()
294                .map(|attachment| attachment.layer_count)
295                .max()
296                .unwrap_or(1)
297        };
298        let attachments = key
299            .attachments
300            .iter()
301            .map(|attachment| {
302                vk::FramebufferAttachmentImageInfo::default()
303                    .flags(attachment.flags)
304                    .width(attachment.width)
305                    .height(attachment.height)
306                    .layer_count(attachment.layer_count)
307                    .usage(attachment.usage)
308                    .view_formats(&attachment.view_formats)
309            })
310            .collect::<Box<_>>();
311        let mut imageless_info =
312            vk::FramebufferAttachmentsCreateInfoKHR::default().attachment_image_infos(&attachments);
313        let mut create_info = vk::FramebufferCreateInfo::default()
314            .flags(vk::FramebufferCreateFlags::IMAGELESS)
315            .render_pass(self.handle)
316            .width(attachments[0].width)
317            .height(attachments[0].height)
318            .layers(layers)
319            .push_next(&mut imageless_info);
320        create_info.attachment_count = self.info.attachments.len() as _;
321
322        let framebuffer =
323            unsafe { self.device.create_framebuffer(&create_info, None) }.map_err(|err| {
324                warn!("unable to create framebuffer: {err}");
325
326                DriverError::Unsupported
327            })?;
328
329        entry.insert(framebuffer);
330
331        Ok(framebuffer)
332    }
333
334    #[profiling::function]
335    pub(crate) fn pipeline_handle(
336        &mut self,
337        pipeline: &GraphicsPipeline,
338        depth_stencil: Option<DepthStencilInfo>,
339        subpass_idx: u32,
340    ) -> Result<vk::Pipeline, DriverError> {
341        let entry = self.graphic_pipelines.entry(GraphicPipelineKey {
342            depth_stencil,
343            layout: pipeline.inner.layout,
344            subpass_idx,
345        });
346        if let Entry::Occupied(entry) = entry {
347            let pipeline_handle = *entry.get();
348
349            if let Some(name) = Device::private_data_object_name(
350                &self.device,
351                vk::ObjectType::PIPELINE_LAYOUT,
352                pipeline.inner.layout,
353            ) && match Device::private_data_object_name(
354                &self.device,
355                vk::ObjectType::PIPELINE,
356                pipeline_handle,
357            ) {
358                None => true,
359                Some(previous) => previous != name,
360            } {
361                pipeline.set_variant_debug_name(pipeline_handle, self.handle, subpass_idx, &name);
362            }
363
364            return Ok(pipeline_handle);
365        }
366
367        let entry = match entry {
368            Entry::Vacant(entry) => entry,
369            _ => unreachable!(),
370        };
371
372        let color_blend_attachment_states = self.info.subpasses[subpass_idx as usize]
373            .color_attachments
374            .iter()
375            .map(|_| pipeline.inner.info.blend.into())
376            .collect::<Box<_>>();
377        let color_blend_state = vk::PipelineColorBlendStateCreateInfo::default()
378            .attachments(&color_blend_attachment_states);
379        let dynamic_state = vk::PipelineDynamicStateCreateInfo::default()
380            .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]);
381        let multisample_state = vk::PipelineMultisampleStateCreateInfo::default()
382            .alpha_to_coverage_enable(pipeline.inner.multisample.alpha_to_coverage_enable)
383            .alpha_to_one_enable(pipeline.inner.multisample.alpha_to_one_enable)
384            .flags(pipeline.inner.multisample.flags)
385            .min_sample_shading(pipeline.inner.multisample.min_sample_shading)
386            .rasterization_samples(pipeline.inner.multisample.rasterization_samples.into())
387            .sample_shading_enable(pipeline.inner.multisample.sample_shading_enable)
388            .sample_mask(&pipeline.inner.multisample.sample_mask);
389        let specializations = pipeline
390            .inner
391            .shader_stages
392            .iter()
393            .map(|stage| stage.specialization.as_ref().map(Into::into))
394            .collect::<Box<_>>();
395        let stages = pipeline
396            .inner
397            .shader_stages
398            .iter()
399            .zip(specializations.iter())
400            .map(|(stage, specialization)| {
401                let mut info = vk::PipelineShaderStageCreateInfo::default()
402                    .module(stage.module)
403                    .name(&stage.name)
404                    .stage(stage.flags);
405
406                if let Some(specialization) = specialization {
407                    info = info.specialization_info(specialization);
408                }
409
410                info
411            })
412            .collect::<Box<_>>();
413        let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::default()
414            .vertex_attribute_descriptions(
415                &pipeline.inner.vertex_input.vertex_attribute_descriptions,
416            )
417            .vertex_binding_descriptions(&pipeline.inner.vertex_input.vertex_binding_descriptions);
418        let viewport_state = vk::PipelineViewportStateCreateInfo::default()
419            .viewport_count(1)
420            .scissor_count(1);
421        let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo {
422            topology: pipeline.inner.info.topology,
423            ..Default::default()
424        };
425        let depth_stencil = depth_stencil.map(Into::into).unwrap_or_default();
426        let rasterization_state = vk::PipelineRasterizationStateCreateInfo {
427            front_face: pipeline.inner.info.front_face,
428            line_width: 1.0,
429            polygon_mode: pipeline.inner.info.polygon_mode,
430            cull_mode: pipeline.inner.info.cull_mode,
431            ..Default::default()
432        };
433        let create_info = vk::GraphicsPipelineCreateInfo::default()
434            .color_blend_state(&color_blend_state)
435            .depth_stencil_state(&depth_stencil)
436            .dynamic_state(&dynamic_state)
437            .input_assembly_state(&input_assembly_state)
438            .layout(pipeline.inner.layout)
439            .multisample_state(&multisample_state)
440            .rasterization_state(&rasterization_state)
441            .render_pass(self.handle)
442            .stages(&stages)
443            .subpass(subpass_idx)
444            .vertex_input_state(&vertex_input_state)
445            .viewport_state(&viewport_state);
446
447        let pipeline_handle = unsafe {
448            self.device.create_graphics_pipelines(
449                Device::pipeline_cache(&self.device),
450                slice::from_ref(&create_info),
451                None,
452            )
453        }
454        .map_err(|(_, err)| {
455            warn!("create_graphics_pipelines: {err}\n{:#?}", create_info);
456
457            DriverError::Unsupported
458        })?
459        .into_iter()
460        .find(|handle| !handle.is_null())
461        .ok_or_else(|| {
462            warn!("missing pipeline handle");
463
464            DriverError::Unsupported
465        })?;
466
467        if let Some(name) = Device::private_data_object_name(
468            &self.device,
469            vk::ObjectType::PIPELINE_LAYOUT,
470            pipeline.inner.layout,
471        ) && match Device::private_data_object_name(
472            &self.device,
473            vk::ObjectType::PIPELINE,
474            pipeline_handle,
475        ) {
476            None => true,
477            Some(previous) => previous != name,
478        } {
479            pipeline.set_variant_debug_name(pipeline_handle, self.handle, subpass_idx, &name);
480        }
481
482        entry.insert(pipeline_handle);
483
484        Ok(pipeline_handle)
485    }
486}
487
488impl Drop for RenderPass {
489    #[profiling::function]
490    fn drop(&mut self) {
491        if panicking() {
492            return;
493        }
494
495        for (_, framebuffer) in self.framebuffers.drain() {
496            unsafe {
497                self.device.destroy_framebuffer(framebuffer, None);
498            }
499        }
500
501        for (_, pipeline) in self.graphic_pipelines.drain() {
502            Device::clear_private_data_object_name(
503                &self.device,
504                vk::ObjectType::PIPELINE,
505                pipeline,
506            )
507            .unwrap_or_else(|err| warn!("unable to clear private data object name: {err}"));
508
509            unsafe {
510                self.device.destroy_pipeline(pipeline, None);
511            }
512        }
513
514        unsafe {
515            self.device.destroy_render_pass(self.handle, None);
516        }
517    }
518}
519
520/// Attachment, subpass, and dependency information used to create a [`RenderPass`].
521///
522/// See [`VkRenderPassCreateInfo2`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkRenderPassCreateInfo2.html).
523#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
524pub(crate) struct RenderPassInfo {
525    pub(crate) attachments: Vec<AttachmentInfo>,
526    pub(crate) subpasses: Vec<SubpassInfo>,
527    pub(crate) dependencies: Vec<SubpassDependency>,
528}
529
530/// Specifies depth and stencil resolve modes.
531///
532/// See [`VkResolveModeFlagBits`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkResolveModeFlagBits.html).
533#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
534pub enum ResolveMode {
535    /// The result of the resolve operation is the average of the sample values.
536    Average,
537
538    /// The result of the resolve operation is the maximum of the sample values.
539    Maximum,
540
541    /// The result of the resolve operation is the minimum of the sample values.
542    Minimum,
543
544    /// The result of the resolve operation is equal to the value of sample `0`.
545    SampleZero,
546}
547
548impl From<ResolveMode> for vk::ResolveModeFlags {
549    fn from(mode: ResolveMode) -> Self {
550        match mode {
551            ResolveMode::Average => vk::ResolveModeFlags::AVERAGE,
552            ResolveMode::Maximum => vk::ResolveModeFlags::MAX,
553            ResolveMode::Minimum => vk::ResolveModeFlags::MIN,
554            ResolveMode::SampleZero => vk::ResolveModeFlags::SAMPLE_ZERO,
555        }
556    }
557}
558
559#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
560pub(crate) struct SubpassDependency {
561    pub src_subpass: u32,
562    pub dst_subpass: u32,
563    pub src_stage_mask: vk::PipelineStageFlags,
564    pub dst_stage_mask: vk::PipelineStageFlags,
565    pub src_access_mask: vk::AccessFlags,
566    pub dst_access_mask: vk::AccessFlags,
567    pub dependency_flags: vk::DependencyFlags,
568}
569
570impl SubpassDependency {
571    pub fn new(src_subpass: u32, dst_subpass: u32) -> Self {
572        Self {
573            src_subpass,
574            dst_subpass,
575            src_stage_mask: vk::PipelineStageFlags::empty(),
576            dst_stage_mask: vk::PipelineStageFlags::empty(),
577            src_access_mask: vk::AccessFlags::empty(),
578            dst_access_mask: vk::AccessFlags::empty(),
579            dependency_flags: vk::DependencyFlags::empty(),
580        }
581    }
582}
583
584impl From<SubpassDependency> for vk::SubpassDependency2<'_> {
585    fn from(value: SubpassDependency) -> Self {
586        vk::SubpassDependency2::default()
587            .src_subpass(value.src_subpass)
588            .dst_subpass(value.dst_subpass)
589            .src_stage_mask(value.src_stage_mask)
590            .dst_stage_mask(value.dst_stage_mask)
591            .src_access_mask(value.src_access_mask)
592            .dst_access_mask(value.dst_access_mask)
593            .dependency_flags(value.dependency_flags)
594    }
595}
596
597#[derive(Clone, Debug, Eq, Hash, PartialEq)]
598pub(crate) struct SubpassInfo {
599    pub color_attachments: Vec<AttachmentRef>,
600    pub color_resolve_attachments: Vec<AttachmentRef>,
601    pub correlated_view_mask: u32,
602    pub depth_stencil_attachment: Option<AttachmentRef>,
603    pub depth_stencil_resolve_attachment:
604        Option<(AttachmentRef, Option<ResolveMode>, Option<ResolveMode>)>,
605    pub input_attachments: Vec<AttachmentRef>,
606    pub preserve_attachments: Vec<u32>,
607    pub view_mask: u32,
608}
609
610impl SubpassInfo {
611    pub fn with_capacity(capacity: usize) -> Self {
612        Self {
613            color_attachments: Vec::with_capacity(capacity),
614            color_resolve_attachments: Vec::with_capacity(capacity),
615            correlated_view_mask: 0,
616            depth_stencil_attachment: None,
617            depth_stencil_resolve_attachment: None,
618            input_attachments: Vec::with_capacity(capacity),
619            preserve_attachments: Vec::with_capacity(capacity),
620            view_mask: 0,
621        }
622    }
623}