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 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 self.vk.queue_work(|cmd| {
121 util::vulkan_image_layout_transition_levels(
122 &self.vk.device(),
123 cmd,
124 render_texture,
125 vk::REMAINING_MIP_LEVELS,
126 vk::ImageLayout::UNDEFINED,
127 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
128 vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
129 vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
130 vk::PipelineStageFlags::ALL_GRAPHICS,
131 vk::PipelineStageFlags::ALL_GRAPHICS,
132 vk::QUEUE_FAMILY_IGNORED,
133 vk::QUEUE_FAMILY_IGNORED,
134 );
135
136 util::vulkan_image_layout_transition_levels(
137 &self.vk.device(),
138 cmd,
139 transfer_texture,
140 vk::REMAINING_MIP_LEVELS,
141 vk::ImageLayout::UNDEFINED,
142 vk::ImageLayout::GENERAL,
143 vk::AccessFlags::TRANSFER_WRITE | vk::AccessFlags::TRANSFER_READ,
144 vk::AccessFlags::TRANSFER_WRITE | vk::AccessFlags::TRANSFER_READ,
145 vk::PipelineStageFlags::ALL_GRAPHICS,
146 vk::PipelineStageFlags::TRANSFER,
147 vk::QUEUE_FAMILY_IGNORED,
148 vk::QUEUE_FAMILY_IGNORED,
149 );
150
151 let options = frame_options.map(|options| FrameOptions {
152 clear_history: options.clear_history,
153 frame_direction: options.frame_direction,
154 rotation: options.rotation,
155 total_subframes: options.total_subframes,
156 current_subframe: options.current_subframe,
157 aspect_ratio: options.aspect_ratio,
158 frametime_delta: options.frametime_delta,
159 frames_per_second: options.frames_per_second,
160 ..Default::default()
161 });
162
163 let viewport = Viewport::new_render_target_sized_origin(
164 VulkanImage {
165 image: render_texture,
166 size: self.image_bytes.size.into(),
167 format: vk::Format::B8G8R8A8_UNORM,
168 },
169 None,
170 )?;
171
172 for frame in 0..=frame_count {
173 filter_chain.frame(
174 &VulkanImage {
175 image: self.image,
176 size: self.image_bytes.size,
177 format: vk::Format::B8G8R8A8_UNORM,
178 },
179 &viewport,
180 cmd,
181 frame,
182 options.as_ref(),
183 )?;
184 }
185
186 {
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
203 let offsets = [
204 vk::Offset3D { x: 0, y: 0, z: 0 },
205 vk::Offset3D {
206 x: self.image_bytes.size.width as i32,
207 y: self.image_bytes.size.height as i32,
208 z: 1,
209 },
210 ];
211
212 let subresource = vk::ImageSubresourceLayers::default()
213 .aspect_mask(vk::ImageAspectFlags::COLOR)
214 .base_array_layer(0)
215 .layer_count(1);
216
217 let image_blit = vk::ImageBlit::default()
218 .src_subresource(subresource.clone())
219 .src_offsets(offsets.clone())
220 .dst_subresource(subresource)
221 .dst_offsets(offsets);
222
223 self.vk.device().cmd_blit_image(
224 cmd,
225 render_texture,
226 vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
227 transfer_texture,
228 vk::ImageLayout::GENERAL,
229 &[image_blit],
230 vk::Filter::NEAREST,
231 );
232
233 Ok::<_, anyhow::Error>(())
234 })??;
235
236 let mut memory = transfer_memory
238 .allocation
239 .mapped_slice_mut()
240 .ok_or(anyhow!("readback buffer was not mapped"))?;
241
242 let layout = self.vk.device().get_image_subresource_layout(
243 transfer_texture,
244 vk::ImageSubresource::default()
245 .aspect_mask(vk::ImageAspectFlags::COLOR)
246 .array_layer(0),
247 );
248 memory = &mut memory[layout.offset as usize..];
249
250 let image = RgbaImage::from_raw(
253 self.image_bytes.size.width,
254 self.image_bytes.size.height,
255 Vec::from(memory),
256 )
257 .ok_or(anyhow!("failed to create image from slice"));
258
259 self.vk.device().destroy_image(transfer_texture, None);
260 self.vk.device().destroy_image(render_texture, None);
261
262 Ok(image?)
263 }
264 }
265}
266
267impl Vulkan {
268 pub fn new(image_path: &Path) -> anyhow::Result<Self> {
269 let vk = VulkanBase::new()?;
270
271 let (image_bytes, image_alloc, image, _view) = Self::load_image(&vk, image_path)?;
272
273 Ok(Self {
274 vk,
275 image,
276 image_bytes,
277 _image_alloc: image_alloc,
278 })
279 }
280
281 pub fn load_image(
282 vk: &VulkanBase,
283 image_path: &Path,
284 ) -> anyhow::Result<(Image<BGRA8>, VulkanImageMemory, vk::Image, vk::ImageView)> {
285 let image: Image<BGRA8> = Image::load(image_path, UVDirection::TopLeft)?;
286
287 let image_info = vk::ImageCreateInfo::default()
288 .image_type(vk::ImageType::TYPE_2D)
289 .format(vk::Format::B8G8R8A8_UNORM)
290 .extent(image.size.into())
291 .mip_levels(1)
292 .array_layers(1)
293 .samples(vk::SampleCountFlags::TYPE_1)
294 .tiling(vk::ImageTiling::OPTIMAL)
295 .usage(
296 vk::ImageUsageFlags::SAMPLED
297 | vk::ImageUsageFlags::TRANSFER_SRC
298 | vk::ImageUsageFlags::TRANSFER_DST,
299 )
300 .initial_layout(vk::ImageLayout::UNDEFINED);
301
302 let texture = unsafe { vk.device().create_image(&image_info, None)? };
303
304 let memory = unsafe {
305 let mem_reqs = vk.device().get_image_memory_requirements(texture);
306 VulkanImageMemory::new(
307 vk.device(),
308 &vk.allocator(),
309 mem_reqs,
310 &texture,
311 MemoryLocation::GpuOnly,
312 )?
313 };
314
315 let image_subresource = vk::ImageSubresourceRange::default()
316 .level_count(image_info.mip_levels)
317 .layer_count(1)
318 .aspect_mask(vk::ImageAspectFlags::COLOR);
319
320 let swizzle_components = vk::ComponentMapping::default()
321 .r(vk::ComponentSwizzle::R)
322 .g(vk::ComponentSwizzle::G)
323 .b(vk::ComponentSwizzle::B)
324 .a(vk::ComponentSwizzle::A);
325
326 let view_info = vk::ImageViewCreateInfo::default()
327 .view_type(vk::ImageViewType::TYPE_2D)
328 .format(vk::Format::B8G8R8A8_UNORM)
329 .image(texture)
330 .subresource_range(image_subresource)
331 .components(swizzle_components);
332
333 let texture_view = unsafe { vk.device().create_image_view(&view_info, None)? };
334
335 let mut staging = VulkanBuffer::new(
336 &vk.device(),
337 &vk.allocator(),
338 vk::BufferUsageFlags::TRANSFER_SRC,
339 image.bytes.len(),
340 )?;
341
342 staging.as_mut_slice()?.copy_from_slice(&image.bytes);
343
344 vk.queue_work(|cmd| unsafe {
345 util::vulkan_image_layout_transition_levels(
346 &vk.device(),
347 cmd,
348 texture,
349 vk::REMAINING_MIP_LEVELS,
350 vk::ImageLayout::UNDEFINED,
351 vk::ImageLayout::TRANSFER_DST_OPTIMAL,
352 vk::AccessFlags::empty(),
353 vk::AccessFlags::TRANSFER_WRITE,
354 vk::PipelineStageFlags::TOP_OF_PIPE,
355 vk::PipelineStageFlags::TRANSFER,
356 vk::QUEUE_FAMILY_IGNORED,
357 vk::QUEUE_FAMILY_IGNORED,
358 );
359
360 let builder = vk::BufferImageCopy::default()
361 .image_subresource(
362 vk::ImageSubresourceLayers::default()
363 .aspect_mask(vk::ImageAspectFlags::COLOR)
364 .mip_level(0)
365 .base_array_layer(0)
366 .layer_count(1),
367 )
368 .image_extent(image.size.into());
369
370 vk.device().cmd_copy_buffer_to_image(
371 cmd,
372 staging.handle,
373 texture,
374 vk::ImageLayout::TRANSFER_DST_OPTIMAL,
375 &[builder],
376 );
377
378 util::vulkan_image_layout_transition_levels(
379 &vk.device(),
380 cmd,
381 texture,
382 vk::REMAINING_MIP_LEVELS,
383 vk::ImageLayout::TRANSFER_DST_OPTIMAL,
384 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
385 vk::AccessFlags::TRANSFER_WRITE,
386 vk::AccessFlags::SHADER_READ,
387 vk::PipelineStageFlags::TRANSFER,
388 vk::PipelineStageFlags::ALL_GRAPHICS,
389 vk::QUEUE_FAMILY_IGNORED,
390 vk::QUEUE_FAMILY_IGNORED,
391 );
392 })?;
393
394 Ok((image, memory, texture, texture_view))
395 }
396}