1use std::{
2 ffi::{
3 CStr,
4 CString,
5 },
6 sync::Arc,
7};
8
9use ash::{
10 Device,
11 Entry,
12 Instance,
13 khr::{
14 surface::Instance as InstanceSurfaceFns,
15 swapchain::Device as DeviceSwapchainFns,
16 },
17 vk::{
18 API_VERSION_1_1,
19 AccessFlags,
20 ApplicationInfo,
21 ColorSpaceKHR,
22 CommandBuffer,
23 CommandBufferAllocateInfo,
24 CommandBufferBeginInfo,
25 CommandBufferLevel,
26 CommandPoolCreateFlags,
27 CommandPoolCreateInfo,
28 CompositeAlphaFlagsKHR,
29 DependencyFlags,
30 DeviceCreateInfo,
31 DeviceQueueCreateInfo,
32 Extent2D,
33 Fence,
34 FenceCreateFlags,
35 FenceCreateInfo,
36 Format,
37 Handle,
38 Image,
39 ImageAspectFlags,
40 ImageLayout,
41 ImageMemoryBarrier,
42 ImageSubresourceRange,
43 ImageUsageFlags,
44 InstanceCreateInfo,
45 KHR_SWAPCHAIN_NAME,
46 PhysicalDevice,
47 PhysicalDeviceFeatures,
48 PipelineStageFlags,
49 PresentInfoKHR,
50 PresentModeKHR,
51 Queue,
52 QueueFlags,
53 Semaphore,
54 SemaphoreCreateInfo,
55 SharingMode,
56 SubmitInfo,
57 SurfaceKHR,
58 SwapchainCreateInfoKHR,
59 SwapchainKHR,
60 make_api_version,
61 },
62};
63use ash_window::enumerate_required_extensions;
64use freya_engine::prelude::{
65 ColorType,
66 DirectContext,
67 Surface as SkiaSurface,
68 SurfaceOrigin,
69 backend_render_targets,
70 direct_contexts,
71 gpu::ContextOptions,
72 vk,
73 wrap_backend_render_target,
74};
75use raw_window_handle::{
76 DisplayHandle,
77 HasDisplayHandle,
78 HasWindowHandle,
79};
80use winit::{
81 dpi::PhysicalSize,
82 event_loop::ActiveEventLoop,
83 window::{
84 Window,
85 WindowAttributes,
86 },
87};
88
89use crate::config::WindowConfig;
90
91pub struct VulkanDriver {
93 _entry: Entry, instance: Instance,
95 surface_fns: InstanceSurfaceFns,
96 surface: SurfaceKHR,
97 physical_device: PhysicalDevice,
98 queue_family_index: u32,
99 device: Arc<Device>,
100 queue: Queue,
101 swapchain: SwapchainKHR,
102 swapchain_fns: DeviceSwapchainFns,
103 swapchain_images: Vec<Image>,
104 swapchain_format: Format,
105 swapchain_extent: Extent2D,
106 swapchain_image_index: u32,
107 swapchain_suboptimal: bool,
108 swapchain_size: PhysicalSize<u32>,
109 transparent: bool,
110 gr_context: DirectContext,
111 image_available_semaphore: Semaphore,
112 render_finished_semaphore: Semaphore,
113 in_flight_fence: Fence,
114 cmd_buf: CommandBuffer,
115}
116
117impl VulkanDriver {
118 pub fn new(
119 event_loop: &ActiveEventLoop,
120 window_attributes: WindowAttributes,
121 window_config: &WindowConfig,
122 ) -> (Self, Window) {
123 let window = event_loop
124 .create_window(window_attributes)
125 .expect("Could not create window with Vulkan context");
126
127 let entry = unsafe { Entry::load().unwrap() };
128
129 let instance = create_instance(&entry, window.display_handle().unwrap());
130 let surface_fns = InstanceSurfaceFns::new(&entry, &instance);
131 let surface = unsafe {
132 ash_window::create_surface(
133 &entry,
134 &instance,
135 window.display_handle().unwrap().as_raw(),
136 window.window_handle().unwrap().as_raw(),
137 None,
138 )
139 .unwrap()
140 };
141
142 let (physical_device, queue_family_index) =
143 pick_physical_device(&instance, &surface_fns, surface);
144
145 let (device, queue) = create_logical_device(&instance, physical_device, queue_family_index);
146 let device = Arc::new(device);
147
148 let swapchain_size = window.inner_size();
149
150 let (swapchain, swapchain_fns, swapchain_images, swapchain_format, swapchain_extent) =
151 create_swapchain(
152 &instance,
153 &device,
154 physical_device,
155 &surface_fns,
156 surface,
157 queue_family_index,
158 swapchain_size,
159 None,
160 window_config.transparent,
161 );
162
163 let gr_context = create_gr_context(
164 &entry,
165 &instance,
166 physical_device,
167 device.clone(),
168 queue,
169 queue_family_index,
170 );
171
172 let (image_available_semaphore, render_finished_semaphore, in_flight_fence) =
173 create_sync_objects(&device);
174
175 let cmd_pool = unsafe {
176 device
177 .create_command_pool(
178 &CommandPoolCreateInfo::default().flags(
179 CommandPoolCreateFlags::TRANSIENT
180 | CommandPoolCreateFlags::RESET_COMMAND_BUFFER,
181 ),
182 None,
183 )
184 .unwrap()
185 };
186
187 let cmd_buf = unsafe {
188 device
189 .allocate_command_buffers(
190 &CommandBufferAllocateInfo::default()
191 .command_pool(cmd_pool)
192 .level(CommandBufferLevel::PRIMARY)
193 .command_buffer_count(1),
194 )
195 .unwrap()[0]
196 };
197
198 let driver = Self {
199 _entry: entry,
200 instance,
201 surface_fns,
202 surface,
203 physical_device,
204 queue_family_index,
205 device,
206 queue,
207 swapchain,
208 swapchain_fns,
209 swapchain_images,
210 swapchain_format,
211 swapchain_extent,
212 swapchain_image_index: 0,
213 swapchain_suboptimal: false,
214 swapchain_size,
215 transparent: window_config.transparent,
216 gr_context,
217 image_available_semaphore,
218 render_finished_semaphore,
219 in_flight_fence,
220 cmd_buf,
221 };
222
223 (driver, window)
224 }
225
226 fn recreate_swapchain(&mut self) {
227 unsafe {
228 self.device.device_wait_idle().unwrap();
229 }
230 let old_swapchain = self.swapchain;
231 let (swapchain, swapchain_fns, swapchain_images, swapchain_format, swapchain_extent) =
232 create_swapchain(
233 &self.instance,
234 &self.device,
235 self.physical_device,
236 &self.surface_fns,
237 self.surface,
238 self.queue_family_index,
239 self.swapchain_size,
240 Some(old_swapchain),
241 self.transparent,
242 );
243 self.swapchain = swapchain;
244 self.swapchain_fns = swapchain_fns;
245 self.swapchain_images = swapchain_images;
246 self.swapchain_format = swapchain_format;
247 self.swapchain_extent = swapchain_extent;
248 self.swapchain_suboptimal = false;
249 unsafe {
250 self.swapchain_fns.destroy_swapchain(old_swapchain, None);
251 }
252 }
253
254 pub fn present(
255 &mut self,
256 size: PhysicalSize<u32>,
257 window: &Window,
258 render: impl FnOnce(&mut SkiaSurface),
259 ) {
260 let mut surface = unsafe {
261 self.device
262 .wait_for_fences(&[self.in_flight_fence], true, u64::MAX)
263 .unwrap();
264
265 self.device.reset_fences(&[self.in_flight_fence]).unwrap();
266
267 let (image_index, suboptimal) = self
268 .swapchain_fns
269 .acquire_next_image(
270 self.swapchain,
271 u64::MAX,
272 self.image_available_semaphore,
273 Fence::null(),
274 )
275 .unwrap();
276
277 self.swapchain_image_index = image_index;
278 self.swapchain_suboptimal = suboptimal;
279
280 let image = self.swapchain_images[image_index as usize];
281
282 let alloc = vk::Alloc::default();
283 let sk_image_info = vk::ImageInfo::new(
284 image.as_raw() as _,
285 alloc,
286 vk::ImageTiling::OPTIMAL,
287 vk::ImageLayout::UNDEFINED,
288 vk::Format::B8G8R8A8_UNORM,
289 1,
290 None,
291 None,
292 None,
293 vk::SharingMode::EXCLUSIVE,
294 );
295 let render_target = backend_render_targets::make_vk(
296 (
297 self.swapchain_extent.width as i32,
298 self.swapchain_extent.height as i32,
299 ),
300 &sk_image_info,
301 );
302
303 wrap_backend_render_target(
304 &mut self.gr_context,
305 &render_target,
306 SurfaceOrigin::TopLeft,
307 ColorType::BGRA8888,
308 None,
309 None,
310 )
311 .unwrap()
312 };
313
314 render(&mut surface);
315
316 window.pre_present_notify();
317
318 self.gr_context.flush_and_submit();
319
320 let image = self.swapchain_images[self.swapchain_image_index as usize];
321
322 unsafe {
323 self.device
324 .begin_command_buffer(self.cmd_buf, &CommandBufferBeginInfo::default())
325 .unwrap();
326
327 let image_barrier = ImageMemoryBarrier::default()
328 .src_access_mask(AccessFlags::COLOR_ATTACHMENT_WRITE)
329 .dst_access_mask(AccessFlags::MEMORY_READ)
330 .old_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
331 .new_layout(ImageLayout::PRESENT_SRC_KHR)
332 .image(image)
333 .subresource_range(ImageSubresourceRange {
334 aspect_mask: ImageAspectFlags::COLOR,
335 base_mip_level: 0,
336 level_count: 1,
337 base_array_layer: 0,
338 layer_count: 1,
339 });
340
341 self.device.cmd_pipeline_barrier(
342 self.cmd_buf,
343 PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
344 PipelineStageFlags::BOTTOM_OF_PIPE,
345 DependencyFlags::empty(),
346 &[],
347 &[],
348 &[image_barrier],
349 );
350
351 self.device.end_command_buffer(self.cmd_buf).unwrap();
352 };
353
354 let wait_semaphores = [self.image_available_semaphore];
355 let wait_stages = [PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT];
356
357 let signal_semaphores = [self.render_finished_semaphore];
358
359 let submit_infos = [SubmitInfo::default()
360 .wait_semaphores(&wait_semaphores)
361 .wait_dst_stage_mask(&wait_stages)
362 .command_buffers(std::slice::from_ref(&self.cmd_buf))
363 .signal_semaphores(&signal_semaphores)];
364
365 unsafe {
366 self.device
367 .queue_submit(self.queue, &submit_infos, self.in_flight_fence)
368 .unwrap();
369 };
370
371 let swapchains = [self.swapchain];
372 let image_indices = [self.swapchain_image_index];
373 let present_info = PresentInfoKHR::default()
374 .wait_semaphores(&signal_semaphores)
375 .swapchains(&swapchains)
376 .image_indices(&image_indices);
377
378 let result = unsafe { self.swapchain_fns.queue_present(self.queue, &present_info) };
379
380 drop(surface);
381
382 if self.swapchain_suboptimal
383 || matches!(result, Err(ash::vk::Result::ERROR_OUT_OF_DATE_KHR))
384 {
385 self.swapchain_size = size;
386 self.recreate_swapchain();
387 }
388 }
389
390 pub fn resize(&mut self, size: PhysicalSize<u32>) {
391 self.swapchain_size = size;
392 self.recreate_swapchain();
393 }
394}
395
396fn create_instance(entry: &Entry, display_handle: DisplayHandle<'_>) -> Instance {
397 let app_name = CString::new("AnyRender").unwrap();
398 let engine_name = CString::new("No Engine").unwrap();
399 let app_info = ApplicationInfo::default()
400 .application_name(&app_name)
401 .application_version(make_api_version(0, 1, 0, 0))
402 .engine_name(&engine_name)
403 .engine_version(make_api_version(0, 1, 0, 0))
404 .api_version(API_VERSION_1_1);
405
406 let extension_names = enumerate_required_extensions(display_handle.as_raw())
407 .unwrap()
408 .to_vec();
409
410 let create_info = InstanceCreateInfo::default()
415 .application_info(&app_info)
416 .enabled_extension_names(&extension_names);
417 unsafe { entry.create_instance(&create_info, None).unwrap() }
420}
421
422fn pick_physical_device(
423 instance: &Instance,
424 surface_fns: &InstanceSurfaceFns,
425 surface: SurfaceKHR,
426) -> (PhysicalDevice, u32) {
427 let devices = unsafe { instance.enumerate_physical_devices().unwrap() };
428 devices
429 .into_iter()
430 .find_map(|physical_device| {
431 let queue_family_index = unsafe {
432 instance
433 .get_physical_device_queue_family_properties(physical_device)
434 .iter()
435 .enumerate()
436 .find_map(|(index, props)| {
437 let supports_graphics = props.queue_flags.contains(QueueFlags::GRAPHICS);
438 let supports_surface = surface_fns
439 .get_physical_device_surface_support(
440 physical_device,
441 index as u32,
442 surface,
443 )
444 .unwrap();
445 if supports_graphics && supports_surface {
446 Some(index as u32)
447 } else {
448 None
449 }
450 })
451 .unwrap()
452 };
453 let extensions_supported = unsafe {
454 instance
455 .enumerate_device_extension_properties(physical_device)
456 .map(|exts| {
457 exts.iter().any(|ext| {
458 CStr::from_ptr(ext.extension_name.as_ptr()) == KHR_SWAPCHAIN_NAME
459 })
460 })
461 .unwrap_or(false)
462 };
463
464 if extensions_supported {
465 Some((physical_device, queue_family_index))
466 } else {
467 None
468 }
469 })
470 .unwrap()
471}
472
473fn create_logical_device(
474 instance: &Instance,
475 physical_device: PhysicalDevice,
476 queue_family_index: u32,
477) -> (Device, Queue) {
478 let queue_priorities = [1.0f32];
479 let queue_create_info = DeviceQueueCreateInfo::default()
480 .queue_family_index(queue_family_index)
481 .queue_priorities(&queue_priorities);
482
483 let features = PhysicalDeviceFeatures::default().sample_rate_shading(true);
484
485 let extensions = [KHR_SWAPCHAIN_NAME.as_ptr()];
486
487 let create_info = DeviceCreateInfo::default()
488 .queue_create_infos(std::slice::from_ref(&queue_create_info))
489 .enabled_extension_names(&extensions)
490 .enabled_features(&features);
491
492 let device = unsafe {
493 instance
494 .create_device(physical_device, &create_info, None)
495 .unwrap()
496 };
497
498 let queue = unsafe { device.get_device_queue(queue_family_index, 0) };
499
500 (device, queue)
501}
502
503#[allow(clippy::too_many_arguments)]
504fn create_swapchain(
505 instance: &Instance,
506 device: &Device,
507 physical_device: PhysicalDevice,
508 surface_fns: &InstanceSurfaceFns,
509 surface: SurfaceKHR,
510 queue_family_index: u32,
511 size: PhysicalSize<u32>,
512 old_swapchain: Option<SwapchainKHR>,
513 transparent: bool,
514) -> (
515 SwapchainKHR,
516 DeviceSwapchainFns,
517 Vec<Image>,
518 Format,
519 Extent2D,
520) {
521 let surface_caps = unsafe {
522 surface_fns
523 .get_physical_device_surface_capabilities(physical_device, surface)
524 .unwrap()
525 };
526
527 let surface_formats = unsafe {
528 surface_fns
529 .get_physical_device_surface_formats(physical_device, surface)
530 .unwrap()
531 };
532
533 let present_modes = unsafe {
534 surface_fns
535 .get_physical_device_surface_present_modes(physical_device, surface)
536 .unwrap()
537 };
538
539 let format = surface_formats
540 .iter()
541 .find(|f| {
542 f.format == Format::B8G8R8A8_UNORM && f.color_space == ColorSpaceKHR::SRGB_NONLINEAR
543 })
544 .unwrap();
545
546 let present_mode = present_modes
547 .iter()
548 .cloned()
549 .find(|&m| m == PresentModeKHR::MAILBOX)
550 .unwrap_or(PresentModeKHR::FIFO);
551
552 let extent = if surface_caps.current_extent.width == u32::MAX {
553 Extent2D {
554 width: size
555 .width
556 .max(surface_caps.min_image_extent.width)
557 .min(surface_caps.max_image_extent.width),
558 height: size
559 .height
560 .max(surface_caps.min_image_extent.height)
561 .min(surface_caps.max_image_extent.height),
562 }
563 } else {
564 surface_caps.current_extent
565 };
566 let image_count = surface_caps.min_image_count.max(2);
567
568 let composite_alpha = if transparent
569 && surface_caps
570 .supported_composite_alpha
571 .contains(CompositeAlphaFlagsKHR::PRE_MULTIPLIED)
572 {
573 CompositeAlphaFlagsKHR::PRE_MULTIPLIED
574 } else {
575 CompositeAlphaFlagsKHR::OPAQUE
576 };
577
578 let create_info = SwapchainCreateInfoKHR::default()
579 .surface(surface)
580 .min_image_count(image_count)
581 .image_format(format.format)
582 .image_color_space(format.color_space)
583 .image_extent(extent)
584 .image_array_layers(1)
585 .image_usage(
586 ImageUsageFlags::COLOR_ATTACHMENT
587 | ImageUsageFlags::SAMPLED
588 | ImageUsageFlags::TRANSFER_SRC
589 | ImageUsageFlags::TRANSFER_DST,
590 )
591 .image_sharing_mode(SharingMode::EXCLUSIVE)
592 .queue_family_indices(std::slice::from_ref(&queue_family_index))
593 .pre_transform(surface_caps.current_transform)
594 .composite_alpha(composite_alpha)
595 .present_mode(present_mode)
596 .clipped(true)
597 .old_swapchain(old_swapchain.unwrap_or(SwapchainKHR::null()));
598
599 let swapchain_fns = DeviceSwapchainFns::new(instance, device);
600 let swapchain = unsafe { swapchain_fns.create_swapchain(&create_info, None).unwrap() };
601 let images = unsafe { swapchain_fns.get_swapchain_images(swapchain).unwrap() };
602
603 (swapchain, swapchain_fns, images, format.format, extent)
604}
605
606fn create_gr_context(
607 entry: &Entry,
608 instance: &Instance,
609 physical_device: PhysicalDevice,
610 device: Arc<Device>,
611 queue: Queue,
612 queue_family_index: u32,
613) -> DirectContext {
614 let get_proc = unsafe {
615 |gpo: vk::GetProcOf| {
616 let get_device_proc_addr = instance.fp_v1_0().get_device_proc_addr;
617
618 match gpo {
619 vk::GetProcOf::Instance(instance, name) => {
620 let vk_instance = ash::vk::Instance::from_raw(instance as _);
621 entry.get_instance_proc_addr(vk_instance, name)
622 }
623 vk::GetProcOf::Device(device, name) => {
624 let vk_device = ash::vk::Device::from_raw(device as _);
625 get_device_proc_addr(vk_device, name)
626 }
627 }
628 .map(|f| f as _)
629 .unwrap()
630 }
631 };
632
633 let mut backend_context = unsafe {
634 vk::BackendContext::new(
635 instance.handle().as_raw() as _,
636 physical_device.as_raw() as _,
637 device.handle().as_raw() as _,
638 (queue.as_raw() as _, queue_family_index as usize),
639 &get_proc,
640 )
641 };
642 backend_context.set_max_api_version(vk::Version::new(1, 1, 0));
643
644 let context_options = ContextOptions::default();
645
646 direct_contexts::make_vulkan(&backend_context, &context_options).unwrap()
647}
648
649fn create_sync_objects(device: &Device) -> (Semaphore, Semaphore, Fence) {
650 let semaphore_info = SemaphoreCreateInfo::default();
651 let fence_info = FenceCreateInfo::default().flags(FenceCreateFlags::SIGNALED);
652
653 let image_available_semaphore =
654 unsafe { device.create_semaphore(&semaphore_info, None).unwrap() };
655 let render_finished_semaphore =
656 unsafe { device.create_semaphore(&semaphore_info, None).unwrap() };
657 let in_flight_fence = unsafe { device.create_fence(&fence_info, None).unwrap() };
658
659 (
660 image_available_semaphore,
661 render_finished_semaphore,
662 in_flight_fence,
663 )
664}