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