freya_winit/drivers/
vulkan.rs

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
91/// Graphics driver using Vulkan.
92pub struct VulkanDriver {
93    _entry: Entry, // Dont drop until backend is dropped
94    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    // Enable the Vulkan validation layer extension:
411    // const VALIDATION_LAYER_NAME: &CStr = c"VK_LAYER_KHRONOS_validation";
412    // let validation_layer_name_ptr = VALIDATION_LAYER_NAME.as_ptr();
413
414    let create_info = InstanceCreateInfo::default()
415        .application_info(&app_info)
416        .enabled_extension_names(&extension_names);
417    // .enabled_layer_names(std::slice::from_ref(&validation_layer_name_ptr));
418
419    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}