Skip to main content

vk_graph_fx/
presenter.rs

1use {
2    bytemuck::cast_slice,
3    glam::{Mat4, vec3},
4    vk_graph::{
5        Graph,
6        cmd::{LoadOp, StoreOp},
7        driver::{
8            DriverError,
9            compute::{ComputePipeline, ComputePipelineInfo},
10            device::Device,
11            graphics::{GraphicsPipeline, GraphicsPipelineInfo},
12            shader::Shader,
13            sync::AccessType,
14        },
15        node::{AnyImageNode, SwapchainImageNode},
16    },
17    vk_shader_macros::include_glsl,
18};
19
20/// Compute-based presenter for copying one or two images into a swapchain image.
21pub struct ComputePresenter([ComputePipeline; 2]);
22
23impl ComputePresenter {
24    /// Creates compute pipelines used to present images into a swapchain target.
25    pub fn new(device: &Device) -> Result<Self, DriverError> {
26        let pipeline1 = ComputePipeline::create(
27            device,
28            ComputePipelineInfo::default(),
29            Shader::new_compute(include_glsl!("res/shader/compute/present1.comp").as_slice()),
30        )?;
31        let pipeline2 = ComputePipeline::create(
32            device,
33            ComputePipelineInfo::default(),
34            Shader::new_compute(include_glsl!("res/shader/compute/present2.comp").as_slice()),
35        )?;
36
37        Ok(Self([pipeline1, pipeline2]))
38    }
39
40    /// Presents a single source image to the given swapchain image using a compute shader.
41    pub fn present_image(
42        &self,
43        graph: &mut Graph,
44        image: impl Into<AnyImageNode>,
45        swapchain: SwapchainImageNode,
46    ) {
47        let image = image.into();
48        // let image_info = graph.node_info(image);
49        let swapchain_info = graph.resource(swapchain).info;
50
51        // TODO: Notice non-sRGB images and run a different pipeline
52
53        graph
54            .begin_cmd()
55            .debug_name("present (from compute)")
56            .bind_pipeline(&self.0[0])
57            .shader_resource_access(0, image, AccessType::ComputeShaderReadOther)
58            .shader_resource_access(1, swapchain, AccessType::ComputeShaderWrite)
59            .record_cmd(move |cmd| {
60                cmd.dispatch(swapchain_info.width, swapchain_info.height, 1);
61            });
62    }
63
64    /// Presents two stacked source images to the given swapchain image using a compute shader.
65    pub fn present_images(
66        &self,
67        graph: &mut Graph,
68        top_image: impl Into<AnyImageNode>,
69        bottom_image: impl Into<AnyImageNode>,
70        swapchain: SwapchainImageNode,
71    ) {
72        let top_image = top_image.into();
73        let bottom_image = bottom_image.into();
74        // let top_image_info = graph.node_info(top_image);
75        // let bottom_image_info = graph.node_info(bottom_image);
76        let swapchain_info = graph.resource(swapchain).info;
77
78        // TODO: Notice non-sRGB images and run a different pipeline
79
80        graph
81            .begin_cmd()
82            .debug_name("present (from compute)")
83            .bind_pipeline(&self.0[1])
84            .shader_resource_access((0, [0]), top_image, AccessType::ComputeShaderReadOther)
85            .shader_resource_access((0, [1]), bottom_image, AccessType::ComputeShaderReadOther)
86            .shader_resource_access(1, swapchain, AccessType::ComputeShaderWrite)
87            .record_cmd(move |cmd| {
88                cmd.dispatch(swapchain_info.width, swapchain_info.height, 1);
89            });
90    }
91}
92
93/// Graphics pipeline presenter for drawing an image into a swapchain image.
94pub struct GraphicPresenter {
95    pipeline: GraphicsPipeline,
96}
97
98impl GraphicPresenter {
99    /// Creates the graphics pipeline used for fullscreen image presentation.
100    pub fn new(device: &Device) -> Result<Self, DriverError> {
101        let pipeline = GraphicsPipeline::create(
102            device,
103            GraphicsPipelineInfo::default(),
104            [
105                Shader::new_vertex(include_glsl!("res/shader/graphics/present.vert").as_slice()),
106                Shader::new_fragment(include_glsl!("res/shader/graphics/present.frag").as_slice()),
107            ],
108        )?;
109
110        Ok(Self { pipeline })
111    }
112
113    /// Draws the given image into the swapchain image using a fullscreen graphics pass.
114    pub fn present_image(
115        &self,
116        graph: &mut Graph,
117        image: impl Into<AnyImageNode>,
118        swapchain: SwapchainImageNode,
119    ) {
120        let image = image.into();
121        let image_info = graph.resource(image).info;
122        let swapchain_info = graph.resource(swapchain).info;
123
124        let (image_width, image_height) = (image_info.width as f32, image_info.height as f32);
125        let (swapchain_width, swapchain_height) =
126            (swapchain_info.width as f32, swapchain_info.height as f32);
127
128        let scale = (swapchain_width / image_width).max(swapchain_height / image_height);
129        let transform = Mat4::from_scale(vec3(
130            scale * image_width / swapchain_width,
131            scale * image_height / swapchain_height,
132            1.0,
133        ));
134
135        graph
136            .begin_cmd()
137            .debug_name("present (from graphics)")
138            .bind_pipeline(&self.pipeline)
139            .shader_resource_access(
140                0,
141                image,
142                AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer,
143            )
144            .color_attachment_image(0, swapchain, LoadOp::DontCare, StoreOp::Store)
145            .record_cmd(move |cmd| {
146                // Draw a quad with implicit vertices (no buffer)
147                cmd.push_constants(0, cast_slice(&transform.to_cols_array()))
148                    .draw(6, 1, 0, 0);
149            });
150    }
151}