1use crate::render::PipelineCache;
12use crate::validation::{self, ValidationLog, ValidationMessage, VALIDATION_LAYER};
13use ash::vk;
14use impeller_hal::{Capabilities, DmaBufSupport, Error, Result, SampleCounts, SyncSupport};
15use std::collections::HashSet;
16use std::ffi::{c_char, CStr, CString};
17use std::sync::Arc;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21pub enum DevicePreference {
22 #[default]
24 Auto,
25 Software,
31 Index(usize),
33}
34
35#[derive(Debug, Clone, Copy, Default)]
37pub struct ContextConfig {
38 pub device: DevicePreference,
39 pub validation: bool,
45}
46
47pub fn surface_extensions() -> &'static [&'static str] {
58 &[
59 "VK_KHR_surface",
60 "VK_KHR_wayland_surface",
61 "VK_KHR_xcb_surface",
62 "VK_KHR_xlib_surface",
63 "VK_EXT_headless_surface",
64 ]
65}
66
67#[derive(Debug, Clone, Copy, Default)]
80pub struct FrameSync<'a> {
81 pub wait: &'a [vk::Semaphore],
83 pub signal: &'a [vk::Semaphore],
85 pub presents: bool,
93}
94
95mod ext {
100 pub const EXTERNAL_MEMORY_DMA_BUF: &str = "VK_EXT_external_memory_dma_buf";
101 pub const EXTERNAL_MEMORY_FD: &str = "VK_KHR_external_memory_fd";
102 pub const IMAGE_DRM_FORMAT_MODIFIER: &str = "VK_EXT_image_drm_format_modifier";
103 pub const EXTERNAL_FENCE_FD: &str = "VK_KHR_external_fence_fd";
104 pub const EXTERNAL_SEMAPHORE_FD: &str = "VK_KHR_external_semaphore_fd";
105 pub const PHYSICAL_DEVICE_DRM: &str = "VK_EXT_physical_device_drm";
106 pub const IMAGE_FORMAT_LIST: &str = "VK_KHR_image_format_list";
109 pub const BLEND_OPERATION_ADVANCED: &str = "VK_EXT_blend_operation_advanced";
113 pub const SWAPCHAIN: &str = "VK_KHR_swapchain";
117}
118
119fn required_dependencies(name: &str) -> &'static [&'static str] {
128 match name {
129 ext::IMAGE_DRM_FORMAT_MODIFIER => &[ext::IMAGE_FORMAT_LIST],
130 ext::EXTERNAL_MEMORY_DMA_BUF => &[ext::EXTERNAL_MEMORY_FD],
131 _ => &[],
132 }
133}
134
135fn probe_advanced_blend(
144 instance: &ash::Instance,
145 physical_device: vk::PhysicalDevice,
146 enabled: &HashSet<String>,
147) -> bool {
148 if !enabled.contains(ext::BLEND_OPERATION_ADVANCED) {
149 return false;
150 }
151
152 let mut properties = vk::PhysicalDeviceBlendOperationAdvancedPropertiesEXT::default();
153 let mut properties2 = vk::PhysicalDeviceProperties2::default().push_next(&mut properties);
154 unsafe { instance.get_physical_device_properties2(physical_device, &mut properties2) };
155
156 let mut features = vk::PhysicalDeviceBlendOperationAdvancedFeaturesEXT::default();
157 let mut features2 = vk::PhysicalDeviceFeatures2::default().push_next(&mut features);
158 unsafe { instance.get_physical_device_features2(physical_device, &mut features2) };
159
160 properties.advanced_blend_all_operations == vk::TRUE
161 && features.advanced_blend_coherent_operations == vk::TRUE
162 && properties.advanced_blend_max_color_attachments >= 1
163}
164
165fn resolve_extensions(wanted: &[&str], available: &HashSet<String>) -> HashSet<String> {
168 let mut enabled = HashSet::new();
169 for name in wanted {
170 if !available.contains(*name) {
171 continue;
172 }
173 let deps = required_dependencies(name);
174 if deps.iter().any(|d| !available.contains(*d)) {
175 continue;
179 }
180 enabled.insert((*name).to_string());
181 enabled.extend(deps.iter().map(|d| (*d).to_string()));
182 }
183 enabled
184}
185
186pub struct VulkanContext {
188 debug_messenger: Option<(ash::ext::debug_utils::Instance, vk::DebugUtilsMessengerEXT)>,
191 validation_log: Arc<ValidationLog>,
192 sync_validation: bool,
199 allocator: Option<gpu_allocator::vulkan::Allocator>,
204 pipelines: PipelineCache,
205 command_pool: vk::CommandPool,
206 device: ash::Device,
207 physical_device: vk::PhysicalDevice,
208 queue: vk::Queue,
209 queue_family_index: u32,
210 capabilities: Capabilities,
211 descriptor_layout: Option<vk::DescriptorSetLayout>,
218 material_layout: Option<vk::DescriptorSetLayout>,
220 runtime_programs: Vec<Vec<u32>>,
228 uniform_alignment: u64,
231 sampler: Option<vk::Sampler>,
232 placeholder: Option<crate::resource::VulkanTexture>,
233 placeholder_binding: Option<(vk::DescriptorPool, vk::ImageView, vk::DescriptorSet)>,
238 enabled_extensions: HashSet<String>,
239 instance: ash::Instance,
240 _entry: ash::Entry,
241}
242
243impl VulkanContext {
244 pub fn new(preference: DevicePreference) -> Result<Self> {
246 Self::with_config(ContextConfig {
247 device: preference,
248 validation: false,
249 })
250 }
251
252 pub fn with_config(config: ContextConfig) -> Result<Self> {
254 let preference = config.device;
255 let entry = unsafe { ash::Entry::load() }.map_err(|e| Error::Backend {
258 backend: "vulkan",
259 detail: format!("loader: {e}"),
260 })?;
261
262 let app_name =
263 CString::new("impeller-rs").expect("no interior NUL in an extension or layer name");
264 let app_info = vk::ApplicationInfo::default()
265 .application_name(&app_name)
266 .engine_name(&app_name)
267 .api_version(vk::make_api_version(0, 1, 1, 0));
270
271 let want_validation = config.validation
275 && layer_available(&entry, VALIDATION_LAYER)
276 && instance_extension_available(&entry, "VK_EXT_debug_utils");
277
278 let layer_name =
279 CString::new(VALIDATION_LAYER).expect("no interior NUL in an extension or layer name");
280 let layer_ptrs: Vec<*const c_char> = if want_validation {
281 vec![layer_name.as_ptr()]
282 } else {
283 Vec::new()
284 };
285 let mut instance_extensions: Vec<CString> = Vec::new();
291 if want_validation {
292 instance_extensions.push(
293 CString::new("VK_EXT_debug_utils")
294 .expect("no interior NUL in an extension or layer name"),
295 );
296 if layer_extension_available(&entry, VALIDATION_LAYER, "VK_EXT_validation_features") {
300 instance_extensions.push(
301 CString::new("VK_EXT_validation_features")
302 .expect("no interior NUL in an extension or layer name"),
303 );
304 }
305 }
306 for &name in surface_extensions() {
307 if instance_extension_available(&entry, name) {
308 instance_extensions.push(
309 CString::new(name).expect("no interior NUL in an extension or layer name"),
310 );
311 }
312 }
313 let ext_ptrs: Vec<*const c_char> = instance_extensions.iter().map(|s| s.as_ptr()).collect();
314
315 let sync_validation_available = want_validation
328 && layer_extension_available(&entry, VALIDATION_LAYER, "VK_EXT_validation_features");
329 let sync_validation = [vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION];
335 let mut validation_features =
336 vk::ValidationFeaturesEXT::default().enabled_validation_features(&sync_validation);
337
338 let mut create_info = vk::InstanceCreateInfo::default()
339 .application_info(&app_info)
340 .enabled_layer_names(&layer_ptrs)
341 .enabled_extension_names(&ext_ptrs);
342 if sync_validation_available {
343 create_info = create_info.push_next(&mut validation_features);
344 }
345 let instance = unsafe { entry.create_instance(&create_info, None) }
346 .map_err(|e| backend_err("create_instance", e))?;
347
348 let validation_log = Arc::new(ValidationLog::default());
349 let debug_messenger = if want_validation {
350 let loader = ash::ext::debug_utils::Instance::new(&entry, &instance);
351 let info = validation::messenger_create_info()
352 .user_data(Arc::as_ptr(&validation_log) as *mut std::ffi::c_void);
353 match unsafe { loader.create_debug_utils_messenger(&info, None) } {
354 Ok(m) => Some((loader, m)),
355 Err(e) => {
356 unsafe { instance.destroy_instance(None) };
357 return Err(backend_err("create_debug_utils_messenger", e));
358 }
359 }
360 } else {
361 None
362 };
363
364 let physical_device = match select_physical_device(&instance, preference) {
365 Ok(pd) => pd,
366 Err(e) => {
367 unsafe { instance.destroy_instance(None) };
368 return Err(e);
369 }
370 };
371
372 Self::finish(
373 entry,
374 instance,
375 physical_device,
376 debug_messenger,
377 validation_log,
378 sync_validation_available,
379 )
380 }
381
382 #[allow(clippy::too_many_arguments)]
383 fn finish(
384 entry: ash::Entry,
385 instance: ash::Instance,
386 physical_device: vk::PhysicalDevice,
387 debug_messenger: Option<(ash::ext::debug_utils::Instance, vk::DebugUtilsMessengerEXT)>,
388 validation_log: Arc<ValidationLog>,
389 sync_validation: bool,
390 ) -> Result<Self> {
391 let available = match device_extensions(&instance, physical_device) {
392 Ok(set) => set,
393 Err(e) => {
394 unsafe { instance.destroy_instance(None) };
395 return Err(e);
396 }
397 };
398
399 let wanted = [
403 ext::EXTERNAL_MEMORY_DMA_BUF,
404 ext::EXTERNAL_MEMORY_FD,
405 ext::IMAGE_DRM_FORMAT_MODIFIER,
406 ext::EXTERNAL_FENCE_FD,
407 ext::EXTERNAL_SEMAPHORE_FD,
408 ext::PHYSICAL_DEVICE_DRM,
409 ext::BLEND_OPERATION_ADVANCED,
410 ext::SWAPCHAIN,
411 ];
412 let enabled = resolve_extensions(&wanted, &available);
413 let advanced_blend = probe_advanced_blend(&instance, physical_device, &enabled);
414
415 let queue_family_index = match select_queue_family(&instance, physical_device) {
416 Ok(i) => i,
417 Err(e) => {
418 unsafe { instance.destroy_instance(None) };
419 return Err(e);
420 }
421 };
422
423 let enabled_cstrings: Vec<CString> = enabled
424 .iter()
425 .map(|s| CString::new(s.as_str()).expect("no interior NUL in an extension name"))
426 .collect();
427 let enabled_ptrs: Vec<*const c_char> =
428 enabled_cstrings.iter().map(|s| s.as_ptr()).collect();
429
430 let priorities = [1.0f32];
431 let queue_info = vk::DeviceQueueCreateInfo::default()
432 .queue_family_index(queue_family_index)
433 .queue_priorities(&priorities);
434 let queue_infos = [queue_info];
435 let mut device_info = vk::DeviceCreateInfo::default()
436 .queue_create_infos(&queue_infos)
437 .enabled_extension_names(&enabled_ptrs);
438
439 let mut advanced_features = vk::PhysicalDeviceBlendOperationAdvancedFeaturesEXT::default()
445 .advanced_blend_coherent_operations(true);
446 if advanced_blend {
447 device_info = device_info.push_next(&mut advanced_features);
448 }
449
450 let device = match unsafe { instance.create_device(physical_device, &device_info, None) } {
451 Ok(d) => d,
452 Err(e) => {
453 unsafe { instance.destroy_instance(None) };
454 return Err(backend_err("create_device", e));
455 }
456 };
457
458 let queue = unsafe { device.get_device_queue(queue_family_index, 0) };
459 let capabilities =
460 detect_capabilities(&instance, physical_device, &enabled, advanced_blend);
461
462 let allocator =
463 gpu_allocator::vulkan::Allocator::new(&gpu_allocator::vulkan::AllocatorCreateDesc {
464 instance: instance.clone(),
465 device: device.clone(),
466 physical_device,
467 debug_settings: Default::default(),
468 buffer_device_address: false,
469 allocation_sizes: Default::default(),
470 })
471 .map_err(|e| Error::Backend {
472 backend: "vulkan",
473 detail: format!("allocator: {e}"),
474 })?;
475
476 let pool_info = vk::CommandPoolCreateInfo::default()
477 .queue_family_index(queue_family_index)
478 .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER);
479 let command_pool = unsafe { device.create_command_pool(&pool_info, None) }
480 .map_err(|e| backend_err("create_command_pool", e))?;
481
482 let uniform_alignment = unsafe { instance.get_physical_device_properties(physical_device) }
483 .limits
484 .min_uniform_buffer_offset_alignment;
485
486 Ok(Self {
487 debug_messenger,
488 validation_log,
489 sync_validation,
490 allocator: Some(allocator),
491 pipelines: PipelineCache::default(),
492 command_pool,
493 device,
494 physical_device,
495 queue,
496 queue_family_index,
497 capabilities,
498 descriptor_layout: None,
499 material_layout: None,
500 runtime_programs: Vec::new(),
501 uniform_alignment,
502 sampler: None,
503 placeholder: None,
504 placeholder_binding: None,
505 enabled_extensions: enabled,
506 instance,
507 _entry: entry,
508 })
509 }
510
511 pub fn capabilities(&self) -> &Capabilities {
512 &self.capabilities
513 }
514
515 pub fn register_program(&mut self, program: &impeller_hal::RuntimeProgram) -> Result<u32> {
526 if program.spirv.is_empty() {
527 return Err(impeller_hal::Error::Unsupported(
528 "a runtime program needs a SPIR-V fragment module for this backend",
529 ));
530 }
531 if let Some(existing) = self
535 .runtime_programs
536 .iter()
537 .position(|held| held.as_slice() == program.spirv.as_slice())
538 {
539 return Ok(existing as u32);
540 }
541 self.runtime_programs.push(program.spirv.clone());
542 Ok((self.runtime_programs.len() - 1) as u32)
543 }
544
545 pub(crate) fn runtime_program(&self, id: u32) -> Result<&[u32]> {
547 self.runtime_programs
548 .get(id as usize)
549 .map(Vec::as_slice)
550 .ok_or(impeller_hal::Error::Unsupported(
551 "a draw names a runtime program that was never registered",
552 ))
553 }
554
555 pub(crate) fn material_layout(&mut self) -> Result<vk::DescriptorSetLayout> {
556 if self.material_layout.is_none() {
557 self.material_layout = Some(crate::materials::create_layout(&self.device)?);
558 }
559 Ok(self.material_layout.expect("just created"))
560 }
561
562 pub(crate) fn uniform_alignment(&self) -> u64 {
568 self.uniform_alignment
569 }
570
571 pub(crate) fn descriptor_layout(&mut self) -> Result<vk::DescriptorSetLayout> {
572 if self.descriptor_layout.is_none() {
573 self.descriptor_layout = Some(crate::sampling::create_descriptor_layout(&self.device)?);
574 }
575 Ok(self.descriptor_layout.expect("just created"))
576 }
577
578 pub(crate) fn sampler(&mut self) -> Result<vk::Sampler> {
580 if self.sampler.is_none() {
581 self.sampler = Some(crate::sampling::create_sampler(&self.device)?);
582 }
583 Ok(self.sampler.expect("just created"))
584 }
585
586 pub(crate) fn placeholder_texture(&mut self) -> Result<vk::Image> {
595 if self.placeholder.is_none() {
596 let mut texture = self.create_texture(&impeller_hal::TextureDescriptor::offscreen(
597 impeller_hal::Extent2D::new(1, 1),
598 impeller_hal::PixelFormat::Rgba8Unorm,
599 ))?;
600 self.write_texture(&mut texture, &[255, 255, 255, 255])?;
601
602 let device = self.device.clone();
603 let cmd = self.begin_one_shot()?;
604 crate::resource::transition(
605 &device,
606 cmd,
607 texture.raw_image(),
608 texture.layout(),
609 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
610 );
611 if let Err(e) = self.submit_one_shot(cmd) {
612 self.destroy_texture(texture);
613 return Err(e);
614 }
615 texture.set_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
616 self.placeholder = Some(texture);
617 }
618 Ok(self.placeholder.as_ref().expect("just created").raw_image())
621 }
622
623 pub(crate) fn placeholder_view(&mut self) -> Result<vk::ImageView> {
630 self.placeholder_set()?;
631 Ok(self
632 .placeholder_binding
633 .expect("placeholder_set just made it")
634 .1)
635 }
636
637 pub(crate) fn placeholder_set(&mut self) -> Result<vk::DescriptorSet> {
638 if let Some((_, _, set)) = self.placeholder_binding {
639 return Ok(set);
640 }
641 let image = self.placeholder_texture()?;
642 let layout = self.descriptor_layout()?;
643 let sampler = self.sampler()?;
644 let binding = crate::sampling::create_placeholder_binding(
645 &self.device.clone(),
646 image,
647 layout,
648 sampler,
649 )?;
650 self.placeholder_binding = Some(binding);
651 Ok(binding.2)
652 }
653
654 pub fn sync_validation_active(&self) -> bool {
670 self.sync_validation
671 }
672
673 pub fn validation_active(&self) -> bool {
674 self.debug_messenger.is_some()
675 }
676
677 pub fn validation_messages(&self) -> Vec<ValidationMessage> {
679 self.validation_log.messages()
680 }
681
682 pub fn validation_clean(&self) -> bool {
684 self.validation_log.is_clean()
685 }
686
687 pub fn validation_log(&self) -> Arc<ValidationLog> {
700 Arc::clone(&self.validation_log)
701 }
702
703 pub fn queue_family_index(&self) -> u32 {
704 self.queue_family_index
705 }
706
707 pub fn has_extension(&self, name: &str) -> bool {
709 self.enabled_extensions.contains(name)
710 }
711
712 pub fn raw_device(&self) -> &ash::Device {
713 &self.device
714 }
715
716 pub fn raw_entry(&self) -> &ash::Entry {
719 &self._entry
720 }
721
722 pub fn raw_instance(&self) -> &ash::Instance {
723 &self.instance
724 }
725
726 pub fn raw_physical_device(&self) -> vk::PhysicalDevice {
727 self.physical_device
728 }
729
730 pub fn raw_queue(&self) -> vk::Queue {
731 self.queue
732 }
733
734 pub(crate) fn pipeline_cache(&self) -> &PipelineCache {
735 &self.pipelines
736 }
737
738 pub(crate) fn pipeline_cache_mut(&mut self) -> &mut PipelineCache {
739 &mut self.pipelines
740 }
741
742 pub(crate) fn allocator_mut(&mut self) -> &mut gpu_allocator::vulkan::Allocator {
743 self.allocator
744 .as_mut()
745 .expect("allocator is taken only in Drop")
746 }
747
748 pub(crate) fn submit_exportable(
751 &self,
752 cmd: vk::CommandBuffer,
753 framebuffer: vk::Framebuffer,
754 view: vk::ImageView,
755 sync: FrameSync<'_>,
756 ) -> Result<crate::fence::VulkanFence> {
757 unsafe { self.device.end_command_buffer(cmd) }
758 .map_err(|e| backend_err("end_command_buffer", e))?;
759
760 let info = vk::FenceCreateInfo::default();
763 let fence = unsafe { self.device.create_fence(&info, None) }
764 .map_err(|e| backend_err("create_fence", e))?;
765
766 let can_export = self.has_extension(ext::EXTERNAL_SEMAPHORE_FD);
771 let semaphore = if can_export {
772 let mut export_info = vk::ExportSemaphoreCreateInfo::default()
773 .handle_types(vk::ExternalSemaphoreHandleTypeFlags::SYNC_FD);
774 let info = vk::SemaphoreCreateInfo::default().push_next(&mut export_info);
775 match unsafe { self.device.create_semaphore(&info, None) } {
776 Ok(semaphore) => Some(semaphore),
777 Err(e) => {
778 unsafe { self.device.destroy_fence(fence, None) };
779 return Err(backend_err("create_semaphore", e));
780 }
781 }
782 } else {
783 None
784 };
785
786 let cmds = [cmd];
787 let signals: Vec<vk::Semaphore> = semaphore
792 .into_iter()
793 .chain(sync.signal.iter().copied())
794 .collect();
795 let wait_stages: Vec<vk::PipelineStageFlags> =
800 vec![vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT; sync.wait.len()];
801 let submit = vk::SubmitInfo::default()
802 .command_buffers(&cmds)
803 .wait_semaphores(sync.wait)
804 .wait_dst_stage_mask(&wait_stages)
805 .signal_semaphores(&signals);
806 if let Err(e) = unsafe { self.device.queue_submit(self.queue, &[submit], fence) } {
807 unsafe {
808 if let Some(semaphore) = semaphore {
809 self.device.destroy_semaphore(semaphore, None);
810 }
811 self.device.destroy_fence(fence, None);
812 }
813 return Err(backend_err("queue_submit", e));
814 }
815
816 let loader = can_export
817 .then(|| ash::khr::external_semaphore_fd::Device::new(&self.instance, &self.device));
818 Ok(crate::fence::VulkanFence::new(
819 self.device.clone(),
820 fence,
821 self.command_pool,
822 cmd,
823 framebuffer,
824 view,
825 semaphore,
826 loader,
827 ))
828 }
829
830 pub(crate) fn begin_one_shot(&self) -> Result<vk::CommandBuffer> {
832 let info = vk::CommandBufferAllocateInfo::default()
833 .command_pool(self.command_pool)
834 .level(vk::CommandBufferLevel::PRIMARY)
835 .command_buffer_count(1);
836 let buffers = unsafe { self.device.allocate_command_buffers(&info) }
837 .map_err(|e| backend_err("allocate_command_buffers", e))?;
838 let cmd = buffers[0];
839
840 let begin = vk::CommandBufferBeginInfo::default()
841 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
842 unsafe { self.device.begin_command_buffer(cmd, &begin) }
843 .map_err(|e| backend_err("begin_command_buffer", e))?;
844 Ok(cmd)
845 }
846
847 pub(crate) fn submit_one_shot(&self, cmd: vk::CommandBuffer) -> Result<()> {
853 let result = self.submit_and_wait(cmd);
854 unsafe { self.device.free_command_buffers(self.command_pool, &[cmd]) };
857 result
858 }
859
860 fn submit_and_wait(&self, cmd: vk::CommandBuffer) -> Result<()> {
861 unsafe { self.device.end_command_buffer(cmd) }
862 .map_err(|e| backend_err("end_command_buffer", e))?;
863
864 let fence_info = vk::FenceCreateInfo::default();
865 let fence = unsafe { self.device.create_fence(&fence_info, None) }
866 .map_err(|e| backend_err("create_fence", e))?;
867
868 let cmds = [cmd];
869 let submit = vk::SubmitInfo::default().command_buffers(&cmds);
870 let submitted = unsafe { self.device.queue_submit(self.queue, &[submit], fence) };
871
872 let result = match submitted {
873 Ok(()) => unsafe {
874 self.device
875 .wait_for_fences(&[fence], true, u64::MAX)
876 .map_err(|e| match e {
877 vk::Result::TIMEOUT => Error::Timeout {
878 what: "a fence to signal",
879 },
880 vk::Result::ERROR_DEVICE_LOST => Error::DeviceLost,
881 other => backend_err("wait_for_fences", other),
882 })
883 },
884 Err(e) => Err(backend_err("queue_submit", e)),
885 };
886
887 unsafe { self.device.destroy_fence(fence, None) };
888 result
889 }
890}
891
892impl Drop for VulkanContext {
893 fn drop(&mut self) {
894 self.pipelines.destroy(&self.device);
897 if let Some(texture) = self.placeholder.take() {
899 self.destroy_texture(texture);
900 }
901 unsafe {
905 if let Some((pool, view, _)) = self.placeholder_binding.take() {
906 self.device.destroy_descriptor_pool(pool, None);
907 self.device.destroy_image_view(view, None);
908 }
909 if let Some(sampler) = self.sampler.take() {
910 self.device.destroy_sampler(sampler, None);
911 }
912 for layout in [self.descriptor_layout.take(), self.material_layout.take()]
921 .into_iter()
922 .flatten()
923 {
924 self.device.destroy_descriptor_set_layout(layout, None);
925 }
926 }
927 drop(self.allocator.take());
928 unsafe {
933 self.device.destroy_command_pool(self.command_pool, None);
934 self.device.destroy_device(None);
935 if let Some((loader, messenger)) = self.debug_messenger.take() {
937 loader.destroy_debug_utils_messenger(messenger, None);
938 }
939 self.instance.destroy_instance(None);
940 }
941 }
942}
943
944impl std::fmt::Debug for VulkanContext {
945 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
946 f.debug_struct("VulkanContext")
947 .field("device", &self.capabilities.device_name)
948 .field("driver", &self.capabilities.driver_name)
949 .finish_non_exhaustive()
950 }
951}
952
953fn layer_available(entry: &ash::Entry, name: &str) -> bool {
954 let Ok(layers) = (unsafe { entry.enumerate_instance_layer_properties() }) else {
955 return false;
956 };
957 layers.iter().any(|l| {
958 unsafe { CStr::from_ptr(l.layer_name.as_ptr()) }
960 .to_str()
961 .map(|s| s == name)
962 .unwrap_or(false)
963 })
964}
965
966fn layer_extension_available(entry: &ash::Entry, layer: &str, name: &str) -> bool {
972 let Ok(layer_name) = CString::new(layer) else {
973 return false;
974 };
975 let Ok(exts) = (unsafe { entry.enumerate_instance_extension_properties(Some(&layer_name)) })
976 else {
977 return false;
978 };
979 exts.iter().any(|e| {
980 unsafe { CStr::from_ptr(e.extension_name.as_ptr()) }
982 .to_str()
983 .map(|s| s == name)
984 .unwrap_or(false)
985 })
986}
987
988fn instance_extension_available(entry: &ash::Entry, name: &str) -> bool {
989 let Ok(exts) = (unsafe { entry.enumerate_instance_extension_properties(None) }) else {
990 return false;
991 };
992 exts.iter().any(|e| {
993 unsafe { CStr::from_ptr(e.extension_name.as_ptr()) }
995 .to_str()
996 .map(|s| s == name)
997 .unwrap_or(false)
998 })
999}
1000
1001fn backend_err(what: &str, e: vk::Result) -> Error {
1002 Error::Backend {
1003 backend: "vulkan",
1004 detail: format!("{what}: {e:?}"),
1005 }
1006}
1007
1008fn device_extensions(instance: &ash::Instance, pd: vk::PhysicalDevice) -> Result<HashSet<String>> {
1009 let props = unsafe { instance.enumerate_device_extension_properties(pd) }
1010 .map_err(|e| backend_err("enumerate_device_extension_properties", e))?;
1011 Ok(props
1012 .iter()
1013 .filter_map(|p| {
1014 let raw = unsafe { CStr::from_ptr(p.extension_name.as_ptr()) };
1016 raw.to_str().ok().map(|s| s.to_owned())
1017 })
1018 .collect())
1019}
1020
1021fn select_physical_device(
1022 instance: &ash::Instance,
1023 preference: DevicePreference,
1024) -> Result<vk::PhysicalDevice> {
1025 let devices = unsafe { instance.enumerate_physical_devices() }
1026 .map_err(|e| backend_err("enumerate_physical_devices", e))?;
1027 if devices.is_empty() {
1028 return Err(Error::Backend {
1029 backend: "vulkan",
1030 detail: "no physical devices".into(),
1031 });
1032 }
1033
1034 match preference {
1035 DevicePreference::Index(i) => devices.get(i).copied().ok_or(Error::Backend {
1036 backend: "vulkan",
1037 detail: format!("device index {i} out of range ({} present)", devices.len()),
1038 }),
1039 DevicePreference::Auto => Ok(*devices
1040 .iter()
1041 .max_by_key(|pd| {
1042 let props = unsafe { instance.get_physical_device_properties(**pd) };
1043 match props.device_type {
1044 vk::PhysicalDeviceType::DISCRETE_GPU => 3,
1045 vk::PhysicalDeviceType::INTEGRATED_GPU => 2,
1046 vk::PhysicalDeviceType::VIRTUAL_GPU => 1,
1047 _ => 0,
1048 }
1049 })
1050 .expect("non-empty")),
1051 DevicePreference::Software => devices
1052 .iter()
1053 .find(|pd| {
1054 let props = unsafe { instance.get_physical_device_properties(**pd) };
1055 props.device_type == vk::PhysicalDeviceType::CPU
1056 })
1057 .copied()
1058 .ok_or(Error::Unsupported("no software rasterizer present")),
1059 }
1060}
1061
1062fn select_queue_family(instance: &ash::Instance, pd: vk::PhysicalDevice) -> Result<u32> {
1063 let families = unsafe { instance.get_physical_device_queue_family_properties(pd) };
1064 families
1065 .iter()
1066 .position(|f| f.queue_flags.contains(vk::QueueFlags::GRAPHICS))
1067 .map(|i| i as u32)
1068 .ok_or(Error::Unsupported("no graphics queue family"))
1069}
1070
1071fn detect_capabilities(
1072 instance: &ash::Instance,
1073 pd: vk::PhysicalDevice,
1074 enabled: &HashSet<String>,
1075 advanced_blend: bool,
1076) -> Capabilities {
1077 let modifiers_known = enabled.contains(ext::IMAGE_DRM_FORMAT_MODIFIER);
1080 let props = unsafe { instance.get_physical_device_properties(pd) };
1081 let limits = props.limits;
1082
1083 let sample_mask =
1087 limits.framebuffer_color_sample_counts & limits.framebuffer_depth_sample_counts;
1088
1089 let fd = enabled.contains(ext::EXTERNAL_MEMORY_FD);
1093 let dma_buf = enabled.contains(ext::EXTERNAL_MEMORY_DMA_BUF);
1094 let modifiers = enabled.contains(ext::IMAGE_DRM_FORMAT_MODIFIER);
1095
1096 let wanted = vk::FormatFeatureFlags::COLOR_ATTACHMENT
1102 | vk::FormatFeatureFlags::COLOR_ATTACHMENT_BLEND
1103 | vk::FormatFeatureFlags::SAMPLED_IMAGE
1104 | vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR;
1105 let float_render_targets = unsafe {
1106 instance
1107 .get_physical_device_format_properties(pd, vk::Format::R16G16B16A16_SFLOAT)
1108 .optimal_tiling_features
1109 .contains(wanted)
1110 };
1111
1112 Capabilities {
1113 advanced_blend,
1114 float_render_targets,
1115 max_texture_size: limits.max_image_dimension2_d,
1116 sample_counts: SampleCounts::from_mask(sample_mask.as_raw()),
1117 dma_buf: DmaBufSupport {
1118 import: fd && dma_buf,
1119 export: fd && dma_buf,
1120 modifiers,
1121 },
1122 sync: SyncSupport {
1123 export_sync_file: enabled.contains(ext::EXTERNAL_SEMAPHORE_FD),
1127 import_sync_file: enabled.contains(ext::EXTERNAL_FENCE_FD),
1130 },
1131 render_formats: if modifiers_known {
1132 crate::external::render_formats(instance, pd)
1133 } else {
1134 Vec::new()
1135 },
1136 device_name: device_name(&props),
1137 driver_name: format!("vulkan {}", api_version_string(props.api_version)),
1138 software: props.device_type == vk::PhysicalDeviceType::CPU,
1139 }
1140}
1141
1142fn device_name(props: &vk::PhysicalDeviceProperties) -> String {
1143 unsafe { CStr::from_ptr(props.device_name.as_ptr()) }
1145 .to_string_lossy()
1146 .into_owned()
1147}
1148
1149fn api_version_string(v: u32) -> String {
1150 format!(
1151 "{}.{}.{}",
1152 vk::api_version_major(v),
1153 vk::api_version_minor(v),
1154 vk::api_version_patch(v)
1155 )
1156}