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 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 let create_info = InstanceCreateInfo::default()
411 .application_info(&app_info)
412 .enabled_extension_names(&extension_names);
413 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}