Skip to main content

librashader_test/render/vk/
mod.rs

1use crate::render::vk::base::VulkanBase;
2use crate::render::vk::memory::{VulkanBuffer, VulkanImageMemory};
3use crate::render::{CommonFrameOptions, RenderTest};
4use anyhow::anyhow;
5use ash::vk;
6use gpu_allocator::MemoryLocation;
7use image::RgbaImage;
8use librashader::presets::ShaderPreset;
9use librashader::runtime::vk::{FilterChain, FilterChainOptions, FrameOptions, VulkanImage};
10use librashader::runtime::{FilterChainParameters, RuntimeParameters};
11use librashader::runtime::{Size, Viewport};
12use librashader_runtime::image::{Image, UVDirection, BGRA8};
13use std::path::Path;
14
15mod base;
16mod memory;
17mod physical_device;
18mod util;
19
20pub struct Vulkan {
21    vk: VulkanBase,
22    image_bytes: Image<BGRA8>,
23    image: vk::Image,
24    _image_alloc: VulkanImageMemory,
25}
26
27impl RenderTest for Vulkan {
28    fn new(path: &Path) -> anyhow::Result<Self>
29    where
30        Self: Sized,
31    {
32        Vulkan::new(path)
33    }
34
35    fn image_size(&self) -> Size<u32> {
36        self.image_bytes.size
37    }
38
39    fn render_with_preset_and_params(
40        &mut self,
41        preset: ShaderPreset,
42        frame_count: usize,
43        output_size: Option<Size<u32>>,
44        param_setter: Option<&dyn Fn(&RuntimeParameters)>,
45        frame_options: Option<CommonFrameOptions>,
46    ) -> anyhow::Result<image::RgbaImage> {
47        unsafe {
48            let mut filter_chain = FilterChain::load_from_preset(
49                preset,
50                &self.vk,
51                Some(&FilterChainOptions {
52                    frames_in_flight: 3,
53                    force_no_mipmaps: false,
54                    use_dynamic_rendering: false,
55                    disable_cache: false,
56                    ..Default::default()
57                }),
58            )?;
59
60            if let Some(setter) = param_setter {
61                setter(filter_chain.parameters());
62            }
63
64            let image_info = vk::ImageCreateInfo::default()
65                .image_type(vk::ImageType::TYPE_2D)
66                .format(vk::Format::B8G8R8A8_UNORM)
67                .extent(output_size.map_or(self.image_bytes.size.into(), |size| size.into()))
68                .mip_levels(1)
69                .array_layers(1)
70                .samples(vk::SampleCountFlags::TYPE_1)
71                .tiling(vk::ImageTiling::OPTIMAL)
72                .usage(vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_SRC)
73                .initial_layout(vk::ImageLayout::UNDEFINED);
74
75            let render_texture = { self.vk.device().create_image(&image_info, None)? };
76
77            // This just needs to stay alive until the read.
78            let _memory = {
79                let mem_reqs = self
80                    .vk
81                    .device()
82                    .get_image_memory_requirements(render_texture);
83                VulkanImageMemory::new(
84                    self.vk.device(),
85                    &self.vk.allocator(),
86                    mem_reqs,
87                    &render_texture,
88                    MemoryLocation::GpuOnly,
89                )?
90            };
91
92            let transfer_texture = self.vk.device().create_image(
93                &vk::ImageCreateInfo::default()
94                    .image_type(vk::ImageType::TYPE_2D)
95                    .format(vk::Format::R8G8B8A8_UNORM)
96                    .extent(self.image_bytes.size.into())
97                    .mip_levels(1)
98                    .array_layers(1)
99                    .samples(vk::SampleCountFlags::TYPE_1)
100                    .tiling(vk::ImageTiling::LINEAR)
101                    .usage(vk::ImageUsageFlags::TRANSFER_DST)
102                    .initial_layout(vk::ImageLayout::UNDEFINED),
103                None,
104            )?;
105
106            let mut transfer_memory = {
107                let mem_reqs = self
108                    .vk
109                    .device()
110                    .get_image_memory_requirements(transfer_texture);
111                VulkanImageMemory::new(
112                    self.vk.device(),
113                    &self.vk.allocator(),
114                    mem_reqs,
115                    &transfer_texture,
116                    MemoryLocation::GpuToCpu,
117                )?
118            };
119
120            let options = frame_options.map(|options| FrameOptions {
121                clear_history: options.clear_history,
122                frame_direction: options.frame_direction,
123                rotation: options.rotation,
124                total_subframes: options.total_subframes,
125                current_subframe: options.current_subframe,
126                aspect_ratio: options.aspect_ratio,
127                frametime_delta: options.frametime_delta,
128                frames_per_second: options.frames_per_second,
129                ..Default::default()
130            });
131
132            let viewport = Viewport::new_render_target_sized_origin(
133                VulkanImage {
134                    image: render_texture,
135                    size: self.image_bytes.size.into(),
136                    format: vk::Format::B8G8R8A8_UNORM,
137                },
138                None,
139            )?;
140
141            let input = VulkanImage {
142                image: self.image,
143                size: self.image_bytes.size,
144                format: vk::Format::B8G8R8A8_UNORM,
145            };
146
147            self.vk.queue_work(|cmd| {
148                util::vulkan_image_layout_transition_levels(
149                    &self.vk.device(),
150                    cmd,
151                    render_texture,
152                    vk::REMAINING_MIP_LEVELS,
153                    vk::ImageLayout::UNDEFINED,
154                    vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
155                    vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
156                    vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
157                    vk::PipelineStageFlags::ALL_GRAPHICS,
158                    vk::PipelineStageFlags::ALL_GRAPHICS,
159                    vk::QUEUE_FAMILY_IGNORED,
160                    vk::QUEUE_FAMILY_IGNORED,
161                );
162
163                util::vulkan_image_layout_transition_levels(
164                    &self.vk.device(),
165                    cmd,
166                    transfer_texture,
167                    vk::REMAINING_MIP_LEVELS,
168                    vk::ImageLayout::UNDEFINED,
169                    vk::ImageLayout::GENERAL,
170                    vk::AccessFlags::TRANSFER_WRITE | vk::AccessFlags::TRANSFER_READ,
171                    vk::AccessFlags::TRANSFER_WRITE | vk::AccessFlags::TRANSFER_READ,
172                    vk::PipelineStageFlags::ALL_GRAPHICS,
173                    vk::PipelineStageFlags::TRANSFER,
174                    vk::QUEUE_FAMILY_IGNORED,
175                    vk::QUEUE_FAMILY_IGNORED,
176                );
177            })?;
178
179            for frame in 0..=frame_count {
180                self.vk.queue_work(|cmd| {
181                    filter_chain.frame(&input, &viewport, cmd, frame, options.as_ref())?;
182                    Ok::<_, anyhow::Error>(())
183                })??;
184            }
185
186            self.vk.queue_work(|cmd| {
187                util::vulkan_image_layout_transition_levels(
188                    &self.vk.device(),
189                    cmd,
190                    render_texture,
191                    vk::REMAINING_MIP_LEVELS,
192                    vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
193                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
194                    vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
195                    vk::AccessFlags::TRANSFER_READ,
196                    vk::PipelineStageFlags::ALL_GRAPHICS,
197                    vk::PipelineStageFlags::TRANSFER,
198                    vk::QUEUE_FAMILY_IGNORED,
199                    vk::QUEUE_FAMILY_IGNORED,
200                );
201
202                let offsets = [
203                    vk::Offset3D { x: 0, y: 0, z: 0 },
204                    vk::Offset3D {
205                        x: self.image_bytes.size.width as i32,
206                        y: self.image_bytes.size.height as i32,
207                        z: 1,
208                    },
209                ];
210
211                let subresource = vk::ImageSubresourceLayers::default()
212                    .aspect_mask(vk::ImageAspectFlags::COLOR)
213                    .base_array_layer(0)
214                    .layer_count(1);
215
216                let image_blit = vk::ImageBlit::default()
217                    .src_subresource(subresource.clone())
218                    .src_offsets(offsets.clone())
219                    .dst_subresource(subresource)
220                    .dst_offsets(offsets);
221
222                self.vk.device().cmd_blit_image(
223                    cmd,
224                    render_texture,
225                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
226                    transfer_texture,
227                    vk::ImageLayout::GENERAL,
228                    &[image_blit],
229                    vk::Filter::NEAREST,
230                );
231
232                Ok::<_, anyhow::Error>(())
233            })??;
234
235            // should have read now.
236            let mut memory = transfer_memory
237                .allocation
238                .mapped_slice_mut()
239                .ok_or(anyhow!("readback buffer was not mapped"))?;
240
241            let layout = self.vk.device().get_image_subresource_layout(
242                transfer_texture,
243                vk::ImageSubresource::default()
244                    .aspect_mask(vk::ImageAspectFlags::COLOR)
245                    .array_layer(0),
246            );
247            memory = &mut memory[layout.offset as usize..];
248
249            // The linear transfer image may have a row pitch larger than the
250            // tightly-packed row size, so repack row-by-row to drop the padding.
251            let width = self.image_bytes.size.width as usize;
252            let height = self.image_bytes.size.height as usize;
253            let row_bytes = width * 4;
254            let row_pitch = layout.row_pitch as usize;
255
256            let mut pixels = Vec::with_capacity(row_bytes * height);
257            for row in 0..height {
258                let start = row * row_pitch;
259                pixels.extend_from_slice(&memory[start..start + row_bytes]);
260            }
261
262            let image = RgbaImage::from_raw(
263                self.image_bytes.size.width,
264                self.image_bytes.size.height,
265                pixels,
266            )
267            .ok_or(anyhow!("failed to create image from slice"));
268
269            self.vk.device().destroy_image(transfer_texture, None);
270            self.vk.device().destroy_image(render_texture, None);
271
272            Ok(image?)
273        }
274    }
275}
276
277impl Vulkan {
278    pub fn new(image_path: &Path) -> anyhow::Result<Self> {
279        let vk = VulkanBase::new()?;
280
281        let (image_bytes, image_alloc, image, _view) = Self::load_image(&vk, image_path)?;
282
283        Ok(Self {
284            vk,
285            image,
286            image_bytes,
287            _image_alloc: image_alloc,
288        })
289    }
290
291    pub fn load_image(
292        vk: &VulkanBase,
293        image_path: &Path,
294    ) -> anyhow::Result<(Image<BGRA8>, VulkanImageMemory, vk::Image, vk::ImageView)> {
295        let image: Image<BGRA8> = Image::load(image_path, UVDirection::TopLeft)?;
296
297        let image_info = vk::ImageCreateInfo::default()
298            .image_type(vk::ImageType::TYPE_2D)
299            .format(vk::Format::B8G8R8A8_UNORM)
300            .extent(image.size.into())
301            .mip_levels(1)
302            .array_layers(1)
303            .samples(vk::SampleCountFlags::TYPE_1)
304            .tiling(vk::ImageTiling::OPTIMAL)
305            .usage(
306                vk::ImageUsageFlags::SAMPLED
307                    | vk::ImageUsageFlags::TRANSFER_SRC
308                    | vk::ImageUsageFlags::TRANSFER_DST,
309            )
310            .initial_layout(vk::ImageLayout::UNDEFINED);
311
312        let texture = unsafe { vk.device().create_image(&image_info, None)? };
313
314        let memory = unsafe {
315            let mem_reqs = vk.device().get_image_memory_requirements(texture);
316            VulkanImageMemory::new(
317                vk.device(),
318                &vk.allocator(),
319                mem_reqs,
320                &texture,
321                MemoryLocation::GpuOnly,
322            )?
323        };
324
325        let image_subresource = vk::ImageSubresourceRange::default()
326            .level_count(image_info.mip_levels)
327            .layer_count(1)
328            .aspect_mask(vk::ImageAspectFlags::COLOR);
329
330        let swizzle_components = vk::ComponentMapping::default()
331            .r(vk::ComponentSwizzle::R)
332            .g(vk::ComponentSwizzle::G)
333            .b(vk::ComponentSwizzle::B)
334            .a(vk::ComponentSwizzle::A);
335
336        let view_info = vk::ImageViewCreateInfo::default()
337            .view_type(vk::ImageViewType::TYPE_2D)
338            .format(vk::Format::B8G8R8A8_UNORM)
339            .image(texture)
340            .subresource_range(image_subresource)
341            .components(swizzle_components);
342
343        let texture_view = unsafe { vk.device().create_image_view(&view_info, None)? };
344
345        let mut staging = VulkanBuffer::new(
346            &vk.device(),
347            &vk.allocator(),
348            vk::BufferUsageFlags::TRANSFER_SRC,
349            image.bytes.len(),
350        )?;
351
352        staging.as_mut_slice()?.copy_from_slice(&image.bytes);
353
354        vk.queue_work(|cmd| unsafe {
355            util::vulkan_image_layout_transition_levels(
356                &vk.device(),
357                cmd,
358                texture,
359                vk::REMAINING_MIP_LEVELS,
360                vk::ImageLayout::UNDEFINED,
361                vk::ImageLayout::TRANSFER_DST_OPTIMAL,
362                vk::AccessFlags::empty(),
363                vk::AccessFlags::TRANSFER_WRITE,
364                vk::PipelineStageFlags::TOP_OF_PIPE,
365                vk::PipelineStageFlags::TRANSFER,
366                vk::QUEUE_FAMILY_IGNORED,
367                vk::QUEUE_FAMILY_IGNORED,
368            );
369
370            let builder = vk::BufferImageCopy::default()
371                .image_subresource(
372                    vk::ImageSubresourceLayers::default()
373                        .aspect_mask(vk::ImageAspectFlags::COLOR)
374                        .mip_level(0)
375                        .base_array_layer(0)
376                        .layer_count(1),
377                )
378                .image_extent(image.size.into());
379
380            vk.device().cmd_copy_buffer_to_image(
381                cmd,
382                staging.handle,
383                texture,
384                vk::ImageLayout::TRANSFER_DST_OPTIMAL,
385                &[builder],
386            );
387
388            util::vulkan_image_layout_transition_levels(
389                &vk.device(),
390                cmd,
391                texture,
392                vk::REMAINING_MIP_LEVELS,
393                vk::ImageLayout::TRANSFER_DST_OPTIMAL,
394                vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
395                vk::AccessFlags::TRANSFER_WRITE,
396                vk::AccessFlags::SHADER_READ,
397                vk::PipelineStageFlags::TRANSFER,
398                vk::PipelineStageFlags::ALL_GRAPHICS,
399                vk::QUEUE_FAMILY_IGNORED,
400                vk::QUEUE_FAMILY_IGNORED,
401            );
402        })?;
403
404        Ok((image, memory, texture, texture_view))
405    }
406}