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        graphic::{DepthStencilInfo, GraphicPipeline},
8        image::SampleCount,
9    },
10    ash::vk,
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 fmt: 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.fmt)
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            fmt: 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#[derive(Debug)]
103#[read_only::cast]
104pub struct RenderPass {
105    /// The device which owns this render pass resource.
106    ///
107    /// _Note:_ This field is read-only.
108    #[readonly]
109    pub device: Device,
110
111    framebuffers: HashMap<FramebufferInfo, vk::Framebuffer>,
112    graphic_pipelines: HashMap<GraphicPipelineKey, vk::Pipeline>,
113
114    /// The native Vulkan resource handle of this render pass.
115    ///
116    /// _Note:_ This field is read-only.
117    #[readonly]
118    pub handle: vk::RenderPass,
119
120    /// Information used to create this render pass resource.
121    ///
122    /// _Note:_ This field is read-only.
123    #[readonly]
124    pub info: RenderPassInfo,
125}
126
127impl RenderPass {
128    #[profiling::function]
129    pub(crate) fn create(device: &Device, info: RenderPassInfo) -> Result<Self, DriverError> {
130        trace!("create");
131
132        let device = device.clone();
133        let attachments = info
134            .attachments
135            .iter()
136            .copied()
137            .map(Into::into)
138            .collect::<Box<_>>();
139        let correlated_view_masks = if info.subpasses.iter().any(|subpass| subpass.view_mask != 0) {
140            {
141                info.subpasses
142                    .iter()
143                    .map(|subpass| subpass.correlated_view_mask)
144                    .collect::<Box<_>>()
145            }
146        } else {
147            Default::default()
148        };
149        let dependencies = info
150            .dependencies
151            .iter()
152            .copied()
153            .map(Into::into)
154            .collect::<Box<_>>();
155
156        let subpass_attachments = info
157            .subpasses
158            .iter()
159            .flat_map(|subpass| {
160                subpass
161                    .color_attachments
162                    .iter()
163                    .chain(subpass.input_attachments.iter())
164                    .chain(subpass.color_resolve_attachments.iter())
165                    .chain(subpass.depth_stencil_attachment.iter())
166                    .chain(
167                        subpass
168                            .depth_stencil_resolve_attachment
169                            .as_ref()
170                            .map(|(resolve_attachment, _, _)| resolve_attachment)
171                            .into_iter(),
172                    )
173                    .copied()
174                    .map(AttachmentRef::into)
175            })
176            .collect::<Box<[vk::AttachmentReference2]>>();
177        let mut subpass_depth_stencil_resolves = info
178            .subpasses
179            .iter()
180            .map(|subpass| {
181                subpass.depth_stencil_resolve_attachment.map(
182                    |(_, depth_resolve_mode, stencil_resolve_mode)| {
183                        vk::SubpassDescriptionDepthStencilResolve::default()
184                            .depth_resolve_mode(
185                                depth_resolve_mode.map(Into::into).unwrap_or_default(),
186                            )
187                            .stencil_resolve_mode(
188                                stencil_resolve_mode.map(Into::into).unwrap_or_default(),
189                            )
190                    },
191                )
192            })
193            .collect::<Box<_>>();
194        let mut subpasses = Vec::with_capacity(info.subpasses.len());
195
196        let mut base_idx = 0;
197        for (subpass, depth_stencil_resolve) in info
198            .subpasses
199            .iter()
200            .zip(subpass_depth_stencil_resolves.iter_mut())
201        {
202            let mut desc = vk::SubpassDescription2::default()
203                .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS);
204
205            debug_assert_eq!(
206                subpass.color_attachments.len(),
207                subpass.color_resolve_attachments.len()
208            );
209
210            let color_idx = base_idx;
211            let input_idx = color_idx + subpass.color_attachments.len();
212            let color_resolve_idx = input_idx + subpass.input_attachments.len();
213            let depth_stencil_idx = color_resolve_idx + subpass.color_resolve_attachments.len();
214            let depth_stencil_resolve_idx =
215                depth_stencil_idx + subpass.depth_stencil_attachment.is_some() as usize;
216            base_idx = depth_stencil_resolve_idx
217                + subpass.depth_stencil_resolve_attachment.is_some() as usize;
218
219            if subpass.depth_stencil_attachment.is_some() {
220                desc = desc.depth_stencil_attachment(&subpass_attachments[depth_stencil_idx]);
221            }
222
223            if let Some(depth_stencil_resolve) = depth_stencil_resolve {
224                *depth_stencil_resolve = depth_stencil_resolve.depth_stencil_resolve_attachment(
225                    &subpass_attachments[depth_stencil_resolve_idx],
226                );
227                desc = desc.push_next(depth_stencil_resolve);
228            }
229
230            subpasses.push(
231                desc.color_attachments(&subpass_attachments[color_idx..input_idx])
232                    .input_attachments(&subpass_attachments[input_idx..color_resolve_idx])
233                    .resolve_attachments(&subpass_attachments[color_resolve_idx..depth_stencil_idx])
234                    .preserve_attachments(&subpass.preserve_attachments)
235                    .view_mask(subpass.view_mask),
236            );
237        }
238
239        let handle = unsafe {
240            device.create_render_pass2(
241                &vk::RenderPassCreateInfo2::default()
242                    .attachments(&attachments)
243                    .correlated_view_masks(&correlated_view_masks)
244                    .dependencies(&dependencies)
245                    .subpasses(&subpasses),
246                None,
247            )
248        }
249        .map_err(|err| {
250            warn!("unable to create render pass: {err}");
251
252            DriverError::Unsupported
253        })?;
254
255        Ok(Self {
256            device,
257            framebuffers: Default::default(),
258            graphic_pipelines: Default::default(),
259            handle,
260            info,
261        })
262    }
263
264    #[profiling::function]
265    pub(crate) fn framebuffer(
266        &mut self,
267        info: FramebufferInfo,
268    ) -> Result<vk::Framebuffer, DriverError> {
269        debug_assert!(!info.attachments.is_empty());
270
271        let entry = self.framebuffers.entry(info);
272        if let Entry::Occupied(entry) = entry {
273            return Ok(*entry.get());
274        }
275
276        let entry = match entry {
277            Entry::Vacant(entry) => entry,
278            _ => unreachable!(),
279        };
280
281        let key = entry.key();
282        let layers = key
283            .attachments
284            .iter()
285            .map(|attachment| attachment.layer_count)
286            .max()
287            .unwrap_or(1);
288        let attachments = key
289            .attachments
290            .iter()
291            .map(|attachment| {
292                vk::FramebufferAttachmentImageInfo::default()
293                    .flags(attachment.flags)
294                    .width(attachment.width)
295                    .height(attachment.height)
296                    .layer_count(attachment.layer_count)
297                    .usage(attachment.usage)
298                    .view_formats(&attachment.view_formats)
299            })
300            .collect::<Box<_>>();
301        let mut imageless_info =
302            vk::FramebufferAttachmentsCreateInfoKHR::default().attachment_image_infos(&attachments);
303        let mut create_info = vk::FramebufferCreateInfo::default()
304            .flags(vk::FramebufferCreateFlags::IMAGELESS)
305            .render_pass(self.handle)
306            .width(attachments[0].width)
307            .height(attachments[0].height)
308            .layers(layers)
309            .push_next(&mut imageless_info);
310        create_info.attachment_count = self.info.attachments.len() as _;
311
312        let framebuffer =
313            unsafe { self.device.create_framebuffer(&create_info, None) }.map_err(|err| {
314                warn!("unable to create framebuffer: {err}");
315
316                DriverError::Unsupported
317            })?;
318
319        entry.insert(framebuffer);
320
321        Ok(framebuffer)
322    }
323
324    #[profiling::function]
325    pub(crate) fn pipeline_handle(
326        &mut self,
327        pipeline: &GraphicPipeline,
328        depth_stencil: Option<DepthStencilInfo>,
329        subpass_idx: u32,
330    ) -> Result<vk::Pipeline, DriverError> {
331        let entry = self.graphic_pipelines.entry(GraphicPipelineKey {
332            depth_stencil,
333            layout: pipeline.inner.layout,
334            subpass_idx,
335        });
336        if let Entry::Occupied(entry) = entry {
337            return Ok(*entry.get());
338        }
339
340        let entry = match entry {
341            Entry::Vacant(entry) => entry,
342            _ => unreachable!(),
343        };
344
345        let color_blend_attachment_states = self.info.subpasses[subpass_idx as usize]
346            .color_attachments
347            .iter()
348            .map(|_| pipeline.inner.info.blend.into())
349            .collect::<Box<_>>();
350        let color_blend_state = vk::PipelineColorBlendStateCreateInfo::default()
351            .attachments(&color_blend_attachment_states);
352        let dynamic_state = vk::PipelineDynamicStateCreateInfo::default()
353            .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]);
354        let multisample_state = vk::PipelineMultisampleStateCreateInfo::default()
355            .alpha_to_coverage_enable(pipeline.inner.multisample.alpha_to_coverage_enable)
356            .alpha_to_one_enable(pipeline.inner.multisample.alpha_to_one_enable)
357            .flags(pipeline.inner.multisample.flags)
358            .min_sample_shading(pipeline.inner.multisample.min_sample_shading)
359            .rasterization_samples(pipeline.inner.multisample.rasterization_samples.into())
360            .sample_shading_enable(pipeline.inner.multisample.sample_shading_enable)
361            .sample_mask(&pipeline.inner.multisample.sample_mask);
362        let specializations = pipeline
363            .inner
364            .shader_stages
365            .iter()
366            .map(|stage| stage.specialization.as_ref().map(Into::into))
367            .collect::<Box<_>>();
368        let stages = pipeline
369            .inner
370            .shader_stages
371            .iter()
372            .zip(specializations.iter())
373            .map(|(stage, specialization)| {
374                let mut info = vk::PipelineShaderStageCreateInfo::default()
375                    .module(stage.module)
376                    .name(&stage.name)
377                    .stage(stage.flags);
378
379                if let Some(specialization) = specialization {
380                    info = info.specialization_info(specialization);
381                }
382
383                info
384            })
385            .collect::<Box<_>>();
386        let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::default()
387            .vertex_attribute_descriptions(
388                &pipeline.inner.vertex_input.vertex_attribute_descriptions,
389            )
390            .vertex_binding_descriptions(&pipeline.inner.vertex_input.vertex_binding_descriptions);
391        let viewport_state = vk::PipelineViewportStateCreateInfo::default()
392            .viewport_count(1)
393            .scissor_count(1);
394        let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo {
395            topology: pipeline.inner.info.topology,
396            ..Default::default()
397        };
398        let depth_stencil = depth_stencil.map(Into::into).unwrap_or_default();
399        let rasterization_state = vk::PipelineRasterizationStateCreateInfo {
400            front_face: pipeline.inner.info.front_face,
401            line_width: 1.0,
402            polygon_mode: pipeline.inner.info.polygon_mode,
403            cull_mode: pipeline.inner.info.cull_mode,
404            ..Default::default()
405        };
406        let create_info = vk::GraphicsPipelineCreateInfo::default()
407            .color_blend_state(&color_blend_state)
408            .depth_stencil_state(&depth_stencil)
409            .dynamic_state(&dynamic_state)
410            .input_assembly_state(&input_assembly_state)
411            .layout(pipeline.inner.layout)
412            .multisample_state(&multisample_state)
413            .rasterization_state(&rasterization_state)
414            .render_pass(self.handle)
415            .stages(&stages)
416            .subpass(subpass_idx)
417            .vertex_input_state(&vertex_input_state)
418            .viewport_state(&viewport_state);
419
420        let pipeline = unsafe {
421            self.device.create_graphics_pipelines(
422                Device::pipeline_cache(&self.device),
423                slice::from_ref(&create_info),
424                None,
425            )
426        }
427        .map_err(|(_, err)| {
428            warn!("create_graphics_pipelines: {err}\n{:#?}", create_info);
429
430            DriverError::Unsupported
431        })?[0];
432
433        entry.insert(pipeline);
434
435        Ok(pipeline)
436    }
437}
438
439impl Drop for RenderPass {
440    #[profiling::function]
441    fn drop(&mut self) {
442        if panicking() {
443            return;
444        }
445
446        for (_, framebuffer) in self.framebuffers.drain() {
447            unsafe {
448                self.device.destroy_framebuffer(framebuffer, None);
449            }
450        }
451
452        for (_, pipeline) in self.graphic_pipelines.drain() {
453            unsafe {
454                self.device.destroy_pipeline(pipeline, None);
455            }
456        }
457
458        unsafe {
459            self.device.destroy_render_pass(self.handle, None);
460        }
461    }
462}
463
464/// Attachment, subpass, and dependency information used to create a [`RenderPass`].
465#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
466pub struct RenderPassInfo {
467    pub(crate) attachments: Vec<AttachmentInfo>,
468    pub(crate) subpasses: Vec<SubpassInfo>,
469    pub(crate) dependencies: Vec<SubpassDependency>,
470}
471
472/// Specifying depth and stencil resolve modes.
473#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
474pub enum ResolveMode {
475    /// The result of the resolve operation is the average of the sample values.
476    Average,
477
478    /// The result of the resolve operation is the maximum of the sample values.
479    Maximum,
480
481    /// The result of the resolve operation is the minimum of the sample values.
482    Minimum,
483
484    /// The result of the resolve operation is equal to the value of sample `0`.
485    SampleZero,
486}
487
488impl From<ResolveMode> for vk::ResolveModeFlags {
489    fn from(mode: ResolveMode) -> Self {
490        match mode {
491            ResolveMode::Average => vk::ResolveModeFlags::AVERAGE,
492            ResolveMode::Maximum => vk::ResolveModeFlags::MAX,
493            ResolveMode::Minimum => vk::ResolveModeFlags::MIN,
494            ResolveMode::SampleZero => vk::ResolveModeFlags::SAMPLE_ZERO,
495        }
496    }
497}
498
499#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
500pub(crate) struct SubpassDependency {
501    pub src_subpass: u32,
502    pub dst_subpass: u32,
503    pub src_stage_mask: vk::PipelineStageFlags,
504    pub dst_stage_mask: vk::PipelineStageFlags,
505    pub src_access_mask: vk::AccessFlags,
506    pub dst_access_mask: vk::AccessFlags,
507    pub dependency_flags: vk::DependencyFlags,
508}
509
510impl SubpassDependency {
511    pub fn new(src_subpass: u32, dst_subpass: u32) -> Self {
512        Self {
513            src_subpass,
514            dst_subpass,
515            src_stage_mask: vk::PipelineStageFlags::empty(),
516            dst_stage_mask: vk::PipelineStageFlags::empty(),
517            src_access_mask: vk::AccessFlags::empty(),
518            dst_access_mask: vk::AccessFlags::empty(),
519            dependency_flags: vk::DependencyFlags::empty(),
520        }
521    }
522}
523
524impl From<SubpassDependency> for vk::SubpassDependency2<'_> {
525    fn from(value: SubpassDependency) -> Self {
526        vk::SubpassDependency2::default()
527            .src_subpass(value.src_subpass)
528            .dst_subpass(value.dst_subpass)
529            .src_stage_mask(value.src_stage_mask)
530            .dst_stage_mask(value.dst_stage_mask)
531            .src_access_mask(value.src_access_mask)
532            .dst_access_mask(value.dst_access_mask)
533            .dependency_flags(value.dependency_flags)
534    }
535}
536
537#[derive(Clone, Debug, Eq, Hash, PartialEq)]
538pub(crate) struct SubpassInfo {
539    pub color_attachments: Vec<AttachmentRef>,
540    pub color_resolve_attachments: Vec<AttachmentRef>,
541    pub correlated_view_mask: u32,
542    pub depth_stencil_attachment: Option<AttachmentRef>,
543    pub depth_stencil_resolve_attachment:
544        Option<(AttachmentRef, Option<ResolveMode>, Option<ResolveMode>)>,
545    pub input_attachments: Vec<AttachmentRef>,
546    pub preserve_attachments: Vec<u32>,
547    pub view_mask: u32,
548}
549
550impl SubpassInfo {
551    pub fn with_capacity(capacity: usize) -> Self {
552        Self {
553            color_attachments: Vec::with_capacity(capacity),
554            color_resolve_attachments: Vec::with_capacity(capacity),
555            correlated_view_mask: 0,
556            depth_stencil_attachment: None,
557            depth_stencil_resolve_attachment: None,
558            input_attachments: Vec::with_capacity(capacity),
559            preserve_attachments: Vec::with_capacity(capacity),
560            view_mask: 0,
561        }
562    }
563}