Skip to main content

blade_graphics/vulkan/
init.rs

1use ash::vk::Handle as _;
2use ash::{amd, ext, khr, vk};
3use naga::back::spv;
4use std::{ffi, sync::Mutex};
5
6use crate::NotSupportedError;
7
8// TODO: Remove once `ash` includes VK_KHR_unified_image_layouts bindings.
9// See https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_unified_image_layouts.html
10mod unified_image_layouts {
11    use ash::vk;
12    use std::ffi;
13
14    pub const NAME: &ffi::CStr = c"VK_KHR_unified_image_layouts";
15    const STRUCTURE_TYPE: i32 = 1000466000;
16
17    #[repr(C)]
18    pub struct PhysicalDeviceFeatures {
19        pub s_type: vk::StructureType,
20        pub p_next: *mut ffi::c_void,
21        pub unified_image_layouts: vk::Bool32,
22        pub unified_image_layouts_video: vk::Bool32,
23    }
24    impl Default for PhysicalDeviceFeatures {
25        fn default() -> Self {
26            Self {
27                s_type: vk::StructureType::from_raw(STRUCTURE_TYPE),
28                p_next: std::ptr::null_mut(),
29                unified_image_layouts: vk::FALSE,
30                unified_image_layouts_video: vk::FALSE,
31            }
32        }
33    }
34    unsafe impl vk::TaggedStructure for PhysicalDeviceFeatures {
35        const STRUCTURE_TYPE: vk::StructureType = vk::StructureType::from_raw(STRUCTURE_TYPE);
36    }
37    unsafe impl vk::ExtendsPhysicalDeviceFeatures2 for PhysicalDeviceFeatures {}
38    unsafe impl vk::ExtendsDeviceCreateInfo for PhysicalDeviceFeatures {}
39}
40
41mod db {
42    pub mod intel {
43        pub const VENDOR: u32 = 0x8086;
44    }
45    pub mod nvidia {
46        pub const VENDOR: u32 = 0x10DE;
47    }
48    pub mod qualcomm {
49        pub const VENDOR: u32 = 0x5143;
50    }
51    pub mod pci_class {
52        /// VGA compatible controller
53        pub const VGA: &str = "0x0300";
54        /// 3D controller (e.g. NVIDIA dGPU in PRIME configurations)
55        pub const DISPLAY_3D: &str = "0x0302";
56    }
57}
58mod layer {
59    use std::ffi::CStr;
60    pub const KHRONOS_VALIDATION: &CStr = c"VK_LAYER_KHRONOS_validation";
61    pub const MESA_OVERLAY: &CStr = c"VK_LAYER_MESA_overlay";
62}
63
64const REQUIRED_DEVICE_EXTENSIONS: &[&ffi::CStr] = &[
65    vk::KHR_TIMELINE_SEMAPHORE_NAME,
66    vk::KHR_DESCRIPTOR_UPDATE_TEMPLATE_NAME,
67    vk::KHR_DYNAMIC_RENDERING_NAME,
68];
69
70fn is_promoted_instance_extension(name: &ffi::CStr, api_version: u32) -> bool {
71    if api_version < vk::API_VERSION_1_1 {
72        return false;
73    }
74
75    name == vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_NAME
76        || name == vk::KHR_GET_SURFACE_CAPABILITIES2_NAME
77}
78
79#[derive(Debug)]
80struct RayTracingCapabilities {
81    min_scratch_buffer_alignment: u64,
82}
83
84#[derive(Debug)]
85struct SystemBugs {
86    /// https://github.com/kvark/blade/issues/117
87    intel_fix_descriptor_pool_leak: bool,
88}
89
90#[derive(Debug)]
91struct AdapterCapabilities {
92    api_version: u32,
93    properties: vk::PhysicalDeviceProperties,
94    device_information: crate::DeviceInformation,
95    queue_family_index: u32,
96    layered: bool,
97    binding_array: bool,
98    ray_tracing: Option<RayTracingCapabilities>,
99    buffer_device_address: bool,
100    max_inline_uniform_block_size: u32,
101    buffer_marker: bool,
102    shader_info: bool,
103    full_screen_exclusive: bool,
104    external_memory: bool,
105    timing: bool,
106    dual_source_blending: bool,
107    shader_float16: bool,
108    cooperative_matrix: crate::CooperativeMatrix,
109    unified_image_layouts: bool,
110    memory_budget: bool,
111    bugs: SystemBugs,
112}
113
114impl AdapterCapabilities {
115    fn to_capabilities(&self) -> crate::Capabilities {
116        crate::Capabilities {
117            binding_array: self.binding_array,
118            ray_query: match self.ray_tracing {
119                Some(_) => crate::ShaderVisibility::all(),
120                None => crate::ShaderVisibility::empty(),
121            },
122            sample_count_mask: (self.properties.limits.framebuffer_color_sample_counts
123                & self.properties.limits.framebuffer_depth_sample_counts)
124                .as_raw(),
125            dual_source_blending: self.dual_source_blending,
126            shader_float16: self.shader_float16,
127            cooperative_matrix: self.cooperative_matrix,
128        }
129    }
130}
131
132/// Display server on the current Linux system.
133/// Used to determine which GPU can present in PRIME configurations.
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub(super) enum DisplayServer {
136    X11,
137    Wayland,
138    Other,
139}
140
141/// Detect the active display server from environment variables.
142fn detect_display_server() -> DisplayServer {
143    // WAYLAND_DISPLAY is set by Wayland compositors for native clients
144    if std::env::var_os("WAYLAND_DISPLAY").is_some() {
145        return DisplayServer::Wayland;
146    }
147    // DISPLAY is set by X11
148    if std::env::var_os("DISPLAY").is_some() {
149        return DisplayServer::X11;
150    }
151    DisplayServer::Other
152}
153
154/// Read GPU vendor IDs from sysfs for all PCI display devices.
155///
156/// Scans `/sys/bus/pci/devices/*/class` for VGA controllers and
157/// 3D controllers, then reads their vendor IDs.
158/// Returns a deduplicated list of vendor IDs.
159fn detect_gpu_vendors() -> Vec<u32> {
160    let mut vendors = Vec::new();
161    let entries = match std::fs::read_dir("/sys/bus/pci/devices") {
162        Ok(entries) => entries,
163        Err(_) => return vendors,
164    };
165    for entry in entries.flatten() {
166        let path = entry.path();
167        let class = match std::fs::read_to_string(path.join("class")) {
168            Ok(c) => c,
169            Err(_) => continue,
170        };
171        let class = class.trim();
172        let is_gpu =
173            class.starts_with(db::pci_class::VGA) || class.starts_with(db::pci_class::DISPLAY_3D);
174        if !is_gpu {
175            continue;
176        }
177        let vendor = match std::fs::read_to_string(path.join("vendor")) {
178            Ok(v) => v,
179            Err(_) => continue,
180        };
181        if let Ok(id) = u32::from_str_radix(vendor.trim().trim_start_matches("0x"), 16)
182            && !vendors.contains(&id)
183        {
184            vendors.push(id);
185        }
186    }
187    vendors
188}
189
190/// Check if a physical device cannot present in the current multi-GPU configuration.
191///
192/// On Linux systems with both Intel and NVIDIA GPUs (PRIME topology):
193/// - X11: Intel iGPU cannot present (Mesa bug <https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688>)
194/// - Wayland: NVIDIA dGPU cannot present (surface owned by compositor's GPU)
195///
196/// Returns `true` if the device must be rejected for presentation.
197fn is_presentation_broken(
198    vendor_id: u32,
199    gpu_vendors: &[u32],
200    display_server: DisplayServer,
201) -> bool {
202    let has_intel = gpu_vendors.contains(&db::intel::VENDOR);
203    let has_nvidia = gpu_vendors.contains(&db::nvidia::VENDOR);
204    if !has_intel || !has_nvidia {
205        return false;
206    }
207    match (display_server, vendor_id) {
208        // Intel cannot present on X11 when NVIDIA is also present
209        (DisplayServer::X11, db::intel::VENDOR) => true,
210        // NVIDIA cannot present on Wayland when Intel is also present
211        (DisplayServer::Wayland, db::nvidia::VENDOR) => true,
212        _ => false,
213    }
214}
215
216fn inspect_adapter(
217    phd: vk::PhysicalDevice,
218    instance: &super::Instance,
219    driver_api_version: u32,
220    desc: &crate::ContextDesc,
221    gpu_vendors: &[u32],
222    display_server: DisplayServer,
223) -> Result<AdapterCapabilities, String> {
224    let mut inline_uniform_block_properties =
225        vk::PhysicalDeviceInlineUniformBlockPropertiesEXT::default();
226    let mut timeline_semaphore_properties =
227        vk::PhysicalDeviceTimelineSemaphorePropertiesKHR::default();
228    let mut descriptor_indexing_properties =
229        vk::PhysicalDeviceDescriptorIndexingPropertiesEXT::default();
230    let mut acceleration_structure_properties =
231        vk::PhysicalDeviceAccelerationStructurePropertiesKHR::default();
232    let mut portability_subset_properties =
233        vk::PhysicalDevicePortabilitySubsetPropertiesKHR::default();
234
235    let mut driver_properties = vk::PhysicalDeviceDriverPropertiesKHR::default();
236    let mut properties2_khr = vk::PhysicalDeviceProperties2KHR::default()
237        .push_next(&mut inline_uniform_block_properties)
238        .push_next(&mut timeline_semaphore_properties)
239        .push_next(&mut descriptor_indexing_properties)
240        .push_next(&mut acceleration_structure_properties)
241        .push_next(&mut portability_subset_properties)
242        .push_next(&mut driver_properties);
243    unsafe {
244        instance
245            .get_physical_device_properties2
246            .get_physical_device_properties2(phd, &mut properties2_khr);
247    }
248
249    let properties = properties2_khr.properties;
250    let name = unsafe { ffi::CStr::from_ptr(properties.device_name.as_ptr()) };
251    log::info!("Adapter: {:?}", name);
252
253    if let Some(device_id) = desc.device_id
254        && device_id != properties.device_id
255    {
256        return Err(format!(
257            "device ID 0x{:X} doesn't match requested 0x{:X}",
258            properties.device_id, device_id,
259        ));
260    }
261
262    let api_version = properties.api_version.min(driver_api_version);
263    if api_version < vk::API_VERSION_1_1 {
264        return Err(format!("Vulkan API version {} is below 1.1", api_version));
265    }
266
267    let supported_extension_properties = unsafe {
268        instance
269            .core
270            .enumerate_device_extension_properties(phd)
271            .unwrap()
272    };
273    let supported_extensions = supported_extension_properties
274        .iter()
275        .map(|ext_prop| unsafe { ffi::CStr::from_ptr(ext_prop.extension_name.as_ptr()) })
276        .collect::<Vec<_>>();
277    for extension in REQUIRED_DEVICE_EXTENSIONS {
278        if !supported_extensions.contains(extension) {
279            return Err(format!(
280                "required device extension {:?} is not supported",
281                extension
282            ));
283        }
284    }
285
286    let bugs = SystemBugs {
287        intel_fix_descriptor_pool_leak: cfg!(windows) && properties.vendor_id == db::intel::VENDOR,
288    };
289
290    let queue_family_index = 0; //TODO
291    if desc.presentation
292        && is_presentation_broken(properties.vendor_id, gpu_vendors, display_server)
293    {
294        return Err(format!(
295            "cannot present on {:?} in Intel+NVIDIA PRIME configuration",
296            display_server,
297        ));
298    }
299
300    let mut inline_uniform_block_features =
301        vk::PhysicalDeviceInlineUniformBlockFeaturesEXT::default();
302    let mut timeline_semaphore_features = vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR::default();
303    let mut dynamic_rendering_features = vk::PhysicalDeviceDynamicRenderingFeaturesKHR::default();
304    let mut descriptor_indexing_features =
305        vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::default();
306    let mut buffer_device_address_features =
307        vk::PhysicalDeviceBufferDeviceAddressFeaturesKHR::default();
308    let mut acceleration_structure_features =
309        vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default();
310    let mut ray_query_features = vk::PhysicalDeviceRayQueryFeaturesKHR::default();
311    let mut cooperative_matrix_features = vk::PhysicalDeviceCooperativeMatrixFeaturesKHR::default();
312    let mut vulkan_memory_model_features = vk::PhysicalDeviceVulkanMemoryModelFeatures::default();
313    let mut float16_int8_features = vk::PhysicalDeviceShaderFloat16Int8Features::default();
314    let mut storage_16bit_features = vk::PhysicalDevice16BitStorageFeatures::default();
315    let mut unified_image_layouts_features =
316        unified_image_layouts::PhysicalDeviceFeatures::default();
317    let mut features2_khr = vk::PhysicalDeviceFeatures2::default()
318        .push_next(&mut inline_uniform_block_features)
319        .push_next(&mut timeline_semaphore_features)
320        .push_next(&mut dynamic_rendering_features)
321        .push_next(&mut descriptor_indexing_features)
322        .push_next(&mut buffer_device_address_features)
323        .push_next(&mut acceleration_structure_features)
324        .push_next(&mut ray_query_features)
325        .push_next(&mut cooperative_matrix_features)
326        .push_next(&mut vulkan_memory_model_features)
327        .push_next(&mut float16_int8_features)
328        .push_next(&mut storage_16bit_features)
329        .push_next(&mut unified_image_layouts_features);
330    unsafe {
331        instance
332            .get_physical_device_properties2
333            .get_physical_device_features2(phd, &mut features2_khr)
334    };
335
336    let dual_source_blending = features2_khr.features.dual_src_blend != 0;
337    let shader_float16 = float16_int8_features.shader_float16 != 0;
338
339    let has_inline_ub = supported_extensions.contains(&vk::EXT_INLINE_UNIFORM_BLOCK_NAME)
340        && inline_uniform_block_properties.max_descriptor_set_inline_uniform_blocks > 0
341        && inline_uniform_block_features.inline_uniform_block != 0;
342    // Adreno 740 (Qualcomm) has a driver bug: inline uniform blocks combined
343    // with inter-stage varyings cause "Failed to link shaders" at pipeline creation.
344    let max_inline_uniform_block_size =
345        if has_inline_ub && properties.vendor_id != db::qualcomm::VENDOR {
346            log::info!(
347                "Inline uniform blocks enabled (max size per binding: {} bytes)",
348                inline_uniform_block_properties.max_inline_uniform_block_size,
349            );
350            inline_uniform_block_properties.max_inline_uniform_block_size
351        } else {
352            log::info!(
353                "Inline uniform blocks disabled (supported={}, vendor=0x{:X}). Using UBO fallback.",
354                has_inline_ub,
355                properties.vendor_id,
356            );
357            0
358        };
359
360    if timeline_semaphore_features.timeline_semaphore == 0 {
361        return Err("timeline semaphore feature is not supported".to_string());
362    }
363
364    if dynamic_rendering_features.dynamic_rendering == 0 {
365        return Err("dynamic rendering feature is not supported".to_string());
366    }
367
368    let external_memory = supported_extensions.contains(&vk::KHR_EXTERNAL_MEMORY_NAME);
369    let external_memory = external_memory
370        && supported_extensions.contains(if cfg!(target_os = "windows") {
371            &vk::KHR_EXTERNAL_MEMORY_WIN32_NAME
372        } else {
373            &vk::KHR_EXTERNAL_MEMORY_FD_NAME
374        });
375
376    let timing = if properties.limits.timestamp_compute_and_graphics == vk::FALSE {
377        log::info!("No timing because of queue support");
378        false
379    } else {
380        true
381    };
382
383    let buffer_device_address = buffer_device_address_features.buffer_device_address == vk::TRUE
384        && (properties.api_version >= vk::API_VERSION_1_2
385            || supported_extensions.contains(&vk::KHR_BUFFER_DEVICE_ADDRESS_NAME));
386
387    let supports_descriptor_indexing = api_version >= vk::API_VERSION_1_2
388        || supported_extensions.contains(&vk::EXT_DESCRIPTOR_INDEXING_NAME);
389    let binding_array = supports_descriptor_indexing
390        && descriptor_indexing_features.descriptor_binding_partially_bound == vk::TRUE
391        && descriptor_indexing_features.shader_storage_buffer_array_non_uniform_indexing
392            == vk::TRUE
393        && descriptor_indexing_features.shader_sampled_image_array_non_uniform_indexing == vk::TRUE;
394
395    let ray_tracing = if !desc.ray_tracing {
396        log::info!("Ray tracing disabled by configuration");
397        None
398    } else if !supported_extensions.contains(&vk::KHR_ACCELERATION_STRUCTURE_NAME)
399        || !supported_extensions.contains(&vk::KHR_RAY_QUERY_NAME)
400    {
401        log::info!("No ray tracing extensions are supported");
402        None
403    } else if descriptor_indexing_properties.max_per_stage_update_after_bind_resources == vk::FALSE
404        || descriptor_indexing_features.descriptor_binding_partially_bound == vk::FALSE
405        || descriptor_indexing_features.shader_storage_buffer_array_non_uniform_indexing
406            == vk::FALSE
407        || descriptor_indexing_features.shader_sampled_image_array_non_uniform_indexing == vk::FALSE
408    {
409        log::info!(
410            "No ray tracing because of the descriptor indexing. Properties = {:?}. Features = {:?}",
411            descriptor_indexing_properties,
412            descriptor_indexing_features
413        );
414        None
415    } else if buffer_device_address_features.buffer_device_address == vk::FALSE {
416        log::info!(
417            "No ray tracing because of the buffer device address. Features = {:?}",
418            buffer_device_address_features
419        );
420        None
421    } else if acceleration_structure_properties.max_geometry_count == 0
422        || acceleration_structure_features.acceleration_structure == vk::FALSE
423    {
424        log::info!(
425            "No ray tracing because of the acceleration structure. Properties = {:?}. Features = {:?}",
426            acceleration_structure_properties,
427            acceleration_structure_features
428        );
429        None
430    } else if ray_query_features.ray_query == vk::FALSE {
431        log::info!(
432            "No ray tracing because of the ray query. Features = {:?}",
433            ray_query_features
434        );
435        None
436    } else {
437        log::info!("Ray tracing is supported");
438        log::debug!("Ray tracing properties: {acceleration_structure_properties:#?}");
439        Some(RayTracingCapabilities {
440            min_scratch_buffer_alignment: acceleration_structure_properties
441                .min_acceleration_structure_scratch_offset_alignment
442                as u64,
443        })
444    };
445
446    let cooperative_matrix = if !supported_extensions.contains(&vk::KHR_COOPERATIVE_MATRIX_NAME) {
447        log::info!("No cooperative matrix extension support");
448        crate::CooperativeMatrix::default()
449    } else if cooperative_matrix_features.cooperative_matrix == vk::FALSE {
450        log::info!(
451            "No cooperative matrix feature support. Features = {:?}",
452            cooperative_matrix_features
453        );
454        crate::CooperativeMatrix::default()
455    } else if vulkan_memory_model_features.vulkan_memory_model == vk::FALSE {
456        log::info!(
457            "No Vulkan memory model support (required for cooperative matrix). Features = {:?}",
458            vulkan_memory_model_features
459        );
460        crate::CooperativeMatrix::default()
461    } else {
462        // Query supported cooperative matrix configurations and find
463        // square float configurations (Naga supports 8x8 and 16x16).
464        let coop_props = unsafe {
465            instance
466                .cooperative_matrix
467                .get_physical_device_cooperative_matrix_properties(phd)
468                .unwrap_or_default()
469        };
470        let find_tile = |a_type, b_type, c_type, result_type| {
471            [8u32, 16].into_iter().find(|&size| {
472                coop_props.iter().any(|p| {
473                    p.m_size == size
474                        && p.n_size == size
475                        && p.k_size == size
476                        && p.a_type == a_type
477                        && p.b_type == b_type
478                        && p.c_type == c_type
479                        && p.result_type == result_type
480                        && p.scope == vk::ScopeKHR::SUBGROUP
481                })
482            })
483        };
484        let f32t = vk::ComponentTypeKHR::FLOAT32;
485        let f16t = vk::ComponentTypeKHR::FLOAT16;
486        let f32_tile = find_tile(f32t, f32t, f32t, f32t).unwrap_or(0);
487        let f16_tile = if float16_int8_features.shader_float16 != 0
488            && storage_16bit_features.storage_buffer16_bit_access != 0
489        {
490            find_tile(f16t, f16t, f32t, f32t).unwrap_or(0)
491        } else {
492            0
493        };
494        let cm = crate::CooperativeMatrix { f32_tile, f16_tile };
495        if cm.is_supported() {
496            log::info!(
497                "Cooperative matrix: f32 tile={}, f16 tile={}",
498                cm.f32_tile,
499                cm.f16_tile,
500            );
501        } else {
502            log::info!(
503                "Cooperative matrix extension present but no usable config. Properties: {:?}",
504                coop_props
505            );
506        }
507        cm
508    };
509    // Auto-enable shader_float16 when cooperative matrix has f16 support.
510    let shader_float16 = shader_float16 || cooperative_matrix.f16_tile > 0;
511
512    let buffer_marker = supported_extensions.contains(&vk::AMD_BUFFER_MARKER_NAME);
513    let shader_info = supported_extensions.contains(&vk::AMD_SHADER_INFO_NAME);
514    let full_screen_exclusive = supported_extensions.contains(&vk::EXT_FULL_SCREEN_EXCLUSIVE_NAME);
515    let memory_budget = supported_extensions.contains(&vk::EXT_MEMORY_BUDGET_NAME);
516
517    let device_information = unsafe {
518        crate::DeviceInformation {
519            is_software_emulated: properties.device_type == vk::PhysicalDeviceType::CPU,
520            device_name: ffi::CStr::from_ptr(properties.device_name.as_ptr())
521                .to_string_lossy()
522                .to_string(),
523            driver_name: ffi::CStr::from_ptr(driver_properties.driver_name.as_ptr())
524                .to_string_lossy()
525                .to_string(),
526            driver_info: ffi::CStr::from_ptr(driver_properties.driver_info.as_ptr())
527                .to_string_lossy()
528                .to_string(),
529        }
530    };
531
532    Ok(AdapterCapabilities {
533        api_version,
534        properties,
535        device_information,
536        queue_family_index,
537        layered: portability_subset_properties.min_vertex_input_binding_stride_alignment != 0,
538        binding_array,
539        ray_tracing,
540        buffer_device_address,
541        max_inline_uniform_block_size,
542        buffer_marker,
543        shader_info,
544        full_screen_exclusive,
545        external_memory,
546        timing,
547        dual_source_blending,
548        shader_float16,
549        cooperative_matrix,
550        unified_image_layouts: supported_extensions.contains(&unified_image_layouts::NAME)
551            && unified_image_layouts_features.unified_image_layouts == vk::TRUE,
552        memory_budget,
553        bugs,
554    })
555}
556
557impl super::VulkanInstance {
558    unsafe fn create(desc: &crate::ContextDesc) -> Result<Self, NotSupportedError> {
559        let entry = match unsafe { ash::Entry::load() } {
560            Ok(entry) => entry,
561            Err(err) => {
562                log::error!("Missing Vulkan entry points: {:?}", err);
563                return Err(crate::PlatformError::loading(err).into());
564            }
565        };
566        let driver_api_version = match unsafe { entry.try_enumerate_instance_version() } {
567            // Vulkan 1.1+
568            Ok(Some(version)) => version,
569            Ok(None) => return Err(NotSupportedError::NoSupportedDeviceFound),
570            Err(err) => {
571                log::error!("try_enumerate_instance_version: {:?}", err);
572                return Err(crate::PlatformError::init(err).into());
573            }
574        };
575
576        if let Some(ref xr) = desc.xr {
577            let reqs = xr
578                .instance
579                .graphics_requirements::<openxr::Vulkan>(xr.system_id)
580                .unwrap();
581            let driver_api_version_xr = openxr::Version::new(
582                vk::api_version_major(driver_api_version) as u16,
583                vk::api_version_minor(driver_api_version) as u16,
584                vk::api_version_patch(driver_api_version),
585            );
586            if driver_api_version_xr < reqs.min_api_version_supported
587                || driver_api_version_xr.major() > reqs.max_api_version_supported.major()
588            {
589                return Err(NotSupportedError::NoSupportedDeviceFound);
590            }
591        }
592
593        let supported_layers = match unsafe { entry.enumerate_instance_layer_properties() } {
594            Ok(layers) => layers,
595            Err(err) => {
596                log::error!("enumerate_instance_layer_properties: {:?}", err);
597                return Err(crate::PlatformError::init(err).into());
598            }
599        };
600        let supported_layer_names = supported_layers
601            .iter()
602            .map(|properties| unsafe { ffi::CStr::from_ptr(properties.layer_name.as_ptr()) })
603            .collect::<Vec<_>>();
604
605        let mut layers: Vec<&'static ffi::CStr> = Vec::new();
606        let mut requested_layers = Vec::<&ffi::CStr>::new();
607        if desc.validation {
608            requested_layers.push(layer::KHRONOS_VALIDATION);
609        }
610        if desc.overlay {
611            requested_layers.push(layer::MESA_OVERLAY);
612        }
613        for name in requested_layers {
614            if supported_layer_names.contains(&name) {
615                layers.push(name);
616            } else {
617                log::warn!("Requested layer is not found: {:?}", name);
618            }
619        }
620
621        let supported_instance_extension_properties =
622            match unsafe { entry.enumerate_instance_extension_properties(None) } {
623                Ok(extensions) => extensions,
624                Err(err) => {
625                    log::error!("enumerate_instance_extension_properties: {:?}", err);
626                    return Err(crate::PlatformError::init(err).into());
627                }
628            };
629        let supported_instance_extensions = supported_instance_extension_properties
630            .iter()
631            .map(|ext_prop| unsafe { ffi::CStr::from_ptr(ext_prop.extension_name.as_ptr()) })
632            .collect::<Vec<_>>();
633
634        let core_instance = {
635            let mut create_flags = vk::InstanceCreateFlags::empty();
636
637            let mut instance_extensions = vec![
638                vk::EXT_DEBUG_UTILS_NAME,
639                vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_NAME,
640            ];
641            if desc.presentation {
642                instance_extensions.push(vk::KHR_SURFACE_NAME);
643                instance_extensions.push(vk::KHR_GET_SURFACE_CAPABILITIES2_NAME);
644                let candidates = [
645                    vk::KHR_WAYLAND_SURFACE_NAME,
646                    vk::KHR_XCB_SURFACE_NAME,
647                    vk::KHR_XLIB_SURFACE_NAME,
648                    vk::KHR_WIN32_SURFACE_NAME,
649                    vk::KHR_ANDROID_SURFACE_NAME,
650                    vk::EXT_SWAPCHAIN_COLORSPACE_NAME,
651                ];
652                for candidate in candidates.iter() {
653                    if supported_instance_extensions.contains(candidate) {
654                        log::info!("Presentation support: {:?}", candidate);
655                        instance_extensions.push(candidate);
656                    }
657                }
658            }
659
660            let mut enabled_instance_extensions = Vec::with_capacity(instance_extensions.len());
661            for inst_ext in instance_extensions.drain(..) {
662                if supported_instance_extensions.contains(&inst_ext) {
663                    enabled_instance_extensions.push(inst_ext);
664                } else if is_promoted_instance_extension(inst_ext, driver_api_version) {
665                    log::info!(
666                        "Skipping promoted instance extension {:?} (core version {:x})",
667                        inst_ext,
668                        driver_api_version
669                    );
670                } else {
671                    log::error!("Instance extension {:?} is not supported", inst_ext);
672                    return Err(NotSupportedError::NoSupportedDeviceFound);
673                }
674            }
675            if supported_instance_extensions.contains(&vk::KHR_PORTABILITY_ENUMERATION_NAME) {
676                log::info!("Enabling Vulkan Portability");
677                enabled_instance_extensions.push(vk::KHR_PORTABILITY_ENUMERATION_NAME);
678                create_flags |= vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR;
679            }
680
681            let app_info = vk::ApplicationInfo::default()
682                .engine_name(c"blade")
683                .engine_version(1)
684                .api_version(vk::HEADER_VERSION_COMPLETE);
685            let str_pointers = layers
686                .iter()
687                .chain(enabled_instance_extensions.iter())
688                .map(|&s| s.as_ptr())
689                .collect::<Vec<_>>();
690            let (layer_strings, extension_strings) = str_pointers.split_at(layers.len());
691            let create_info = vk::InstanceCreateInfo::default()
692                .application_info(&app_info)
693                .flags(create_flags)
694                .enabled_layer_names(layer_strings)
695                .enabled_extension_names(extension_strings);
696            if let Some(ref xr_desc) = desc.xr {
697                let get_instance_proc_addr: openxr::sys::platform::VkGetInstanceProcAddr =
698                    unsafe { std::mem::transmute(entry.static_fn().get_instance_proc_addr) };
699                let raw_instance = unsafe {
700                    xr_desc
701                        .instance
702                        .create_vulkan_instance(
703                            xr_desc.system_id,
704                            get_instance_proc_addr,
705                            &create_info as *const _ as *const _,
706                        )
707                        .map_err(|_| NotSupportedError::NoSupportedDeviceFound)?
708                        .map_err(|raw| crate::PlatformError::init(vk::Result::from_raw(raw)))?
709                };
710                unsafe {
711                    ash::Instance::load(
712                        entry.static_fn(),
713                        vk::Instance::from_raw(raw_instance as _),
714                    )
715                }
716            } else {
717                unsafe { entry.create_instance(&create_info, None) }
718                    .map_err(crate::PlatformError::init)?
719            }
720        };
721
722        let instance =
723            super::Instance {
724                _debug_utils: ext::debug_utils::Instance::new(&entry, &core_instance),
725                get_physical_device_properties2:
726                    khr::get_physical_device_properties2::Instance::new(&entry, &core_instance),
727                cooperative_matrix: khr::cooperative_matrix::Instance::new(&entry, &core_instance),
728                get_surface_capabilities2: if desc.presentation {
729                    Some(khr::get_surface_capabilities2::Instance::new(
730                        &entry,
731                        &core_instance,
732                    ))
733                } else {
734                    None
735                },
736                surface: if desc.presentation {
737                    Some(khr::surface::Instance::new(&entry, &core_instance))
738                } else {
739                    None
740                },
741                core: core_instance,
742            };
743
744        Ok(super::VulkanInstance {
745            entry,
746            instance,
747            driver_api_version,
748        })
749    }
750}
751
752fn inspect_devices(
753    instance: &super::Instance,
754    driver_api_version: u32,
755    default_phd: Option<vk::PhysicalDevice>,
756) -> Vec<crate::DeviceReport> {
757    let physical_devices =
758        unsafe { instance.core.enumerate_physical_devices() }.unwrap_or_default();
759
760    // Probe all capabilities unconditionally:
761    //  - `ray_tracing` must be enabled to discover RT support
762    //  - `presentation` is left off, so the PRIME topology check is skipped
763    //    (it would reject devices based on the display server, not device features)
764    let inspect_desc = crate::ContextDesc {
765        ray_tracing: true,
766        ..Default::default()
767    };
768
769    physical_devices
770        .into_iter()
771        .map(|phd| {
772            let properties = unsafe { instance.core.get_physical_device_properties(phd) };
773            let device_id = properties.device_id;
774            let device_name = unsafe {
775                ffi::CStr::from_ptr(properties.device_name.as_ptr())
776                    .to_string_lossy()
777                    .to_string()
778            };
779
780            let is_default = default_phd == Some(phd);
781            let result = inspect_adapter(
782                phd,
783                instance,
784                driver_api_version,
785                &inspect_desc,
786                &[],
787                DisplayServer::Other,
788            );
789
790            let (status, information) = match result {
791                Ok(caps) => {
792                    let capabilities = caps.to_capabilities();
793                    let status = crate::DeviceReportStatus::Available {
794                        is_default,
795                        caps: capabilities,
796                    };
797                    (status, caps.device_information)
798                }
799                Err(reason) => (
800                    crate::DeviceReportStatus::Rejected(reason),
801                    crate::DeviceInformation {
802                        is_software_emulated: properties.device_type == vk::PhysicalDeviceType::CPU,
803                        device_name,
804                        driver_name: String::new(),
805                        driver_info: String::new(),
806                    },
807                ),
808            };
809
810            crate::DeviceReport {
811                device_id,
812                information,
813                status,
814            }
815        })
816        .collect()
817}
818
819impl Drop for super::VulkanInstance {
820    fn drop(&mut self) {
821        unsafe {
822            self.instance.core.destroy_instance(None);
823        }
824    }
825}
826
827impl super::Context {
828    pub unsafe fn init(desc: crate::ContextDesc) -> Result<Self, NotSupportedError> {
829        let inner = unsafe { super::VulkanInstance::create(&desc)? };
830
831        // Detect GPU vendors from sysfs to identify PRIME multi-GPU topologies.
832        // Only needed when presentation is requested (for the PRIME rejection check).
833        let gpu_vendors = if desc.presentation {
834            let vendors = detect_gpu_vendors();
835            log::info!(
836                "PCI GPU vendors: {:?}",
837                vendors
838                    .iter()
839                    .map(|v| format!("0x{v:04X}"))
840                    .collect::<Vec<_>>(),
841            );
842            vendors
843        } else {
844            Vec::new()
845        };
846        let display_server = detect_display_server();
847
848        let (physical_device, capabilities) = if let Some(ref xr_desc) = desc.xr {
849            let xr_physical_device = unsafe {
850                xr_desc
851                    .instance
852                    .vulkan_graphics_device(
853                        xr_desc.system_id,
854                        inner.instance.core.handle().as_raw() as _,
855                    )
856                    .map_err(|_| NotSupportedError::NoSupportedDeviceFound)?
857            };
858            let physical_device = vk::PhysicalDevice::from_raw(xr_physical_device as _);
859            let capabilities = inspect_adapter(
860                physical_device,
861                &inner.instance,
862                inner.driver_api_version,
863                &desc,
864                &gpu_vendors,
865                display_server,
866            )
867            .map_err(|_| NotSupportedError::NoSupportedDeviceFound)?;
868            (physical_device, capabilities)
869        } else {
870            unsafe { inner.instance.core.enumerate_physical_devices() }
871                .map_err(crate::PlatformError::init)?
872                .into_iter()
873                .find_map(|phd| {
874                    inspect_adapter(
875                        phd,
876                        &inner.instance,
877                        inner.driver_api_version,
878                        &desc,
879                        &gpu_vendors,
880                        display_server,
881                    )
882                    .ok()
883                    .map(|caps| (phd, caps))
884                })
885                .ok_or(NotSupportedError::NoSupportedDeviceFound)?
886        };
887
888        log::debug!("Adapter {:#?}", capabilities);
889        let mut min_buffer_alignment = 1;
890        if let Some(ref rt) = capabilities.ray_tracing {
891            min_buffer_alignment = min_buffer_alignment.max(rt.min_scratch_buffer_alignment);
892        }
893
894        let device_core = {
895            let family_info = vk::DeviceQueueCreateInfo::default()
896                .queue_family_index(capabilities.queue_family_index)
897                .queue_priorities(&[1.0]);
898            let family_infos = [family_info];
899
900            let mut device_extensions = REQUIRED_DEVICE_EXTENSIONS.to_vec();
901            if capabilities.max_inline_uniform_block_size > 0 {
902                device_extensions.push(vk::EXT_INLINE_UNIFORM_BLOCK_NAME);
903            }
904            if desc.presentation {
905                device_extensions.push(vk::KHR_SWAPCHAIN_NAME);
906            }
907            if capabilities.layered {
908                log::info!("Enabling Vulkan Portability");
909                device_extensions.push(vk::KHR_PORTABILITY_SUBSET_NAME);
910            }
911            let needs_descriptor_indexing =
912                capabilities.binding_array || capabilities.ray_tracing.is_some();
913            if needs_descriptor_indexing && capabilities.api_version < vk::API_VERSION_1_2 {
914                device_extensions.push(vk::EXT_DESCRIPTOR_INDEXING_NAME);
915            }
916            if capabilities.ray_tracing.is_some() {
917                if capabilities.api_version < vk::API_VERSION_1_2 {
918                    device_extensions.push(vk::KHR_BUFFER_DEVICE_ADDRESS_NAME);
919                    device_extensions.push(vk::KHR_SHADER_FLOAT_CONTROLS_NAME);
920                    device_extensions.push(vk::KHR_SPIRV_1_4_NAME);
921                }
922                device_extensions.push(vk::KHR_DEFERRED_HOST_OPERATIONS_NAME);
923                device_extensions.push(vk::KHR_ACCELERATION_STRUCTURE_NAME);
924                device_extensions.push(vk::KHR_RAY_QUERY_NAME);
925            } else if capabilities.buffer_device_address
926                && capabilities.api_version < vk::API_VERSION_1_2
927            {
928                device_extensions.push(vk::KHR_BUFFER_DEVICE_ADDRESS_NAME);
929            }
930            if capabilities.buffer_marker {
931                device_extensions.push(vk::AMD_BUFFER_MARKER_NAME);
932            }
933            if capabilities.shader_info {
934                device_extensions.push(vk::AMD_SHADER_INFO_NAME);
935            }
936            if capabilities.full_screen_exclusive {
937                device_extensions.push(vk::EXT_FULL_SCREEN_EXCLUSIVE_NAME);
938            }
939            if capabilities.external_memory {
940                device_extensions.push(vk::KHR_EXTERNAL_MEMORY_NAME);
941                device_extensions.push(if cfg!(target_os = "windows") {
942                    vk::KHR_EXTERNAL_MEMORY_WIN32_NAME
943                } else {
944                    vk::KHR_EXTERNAL_MEMORY_FD_NAME
945                });
946            }
947            if capabilities.cooperative_matrix.is_supported() {
948                device_extensions.push(vk::KHR_COOPERATIVE_MATRIX_NAME);
949                if capabilities.api_version < vk::API_VERSION_1_2 {
950                    device_extensions.push(vk::KHR_VULKAN_MEMORY_MODEL_NAME);
951                }
952            }
953            if capabilities.memory_budget {
954                device_extensions.push(vk::EXT_MEMORY_BUDGET_NAME);
955            }
956            if capabilities.unified_image_layouts {
957                // TODO: Replace with ash constant once available.
958                device_extensions.push(unified_image_layouts::NAME);
959            }
960
961            let str_pointers = device_extensions
962                .iter()
963                .map(|&s| s.as_ptr())
964                .collect::<Vec<_>>();
965
966            let mut ext_inline_uniform_block = vk::PhysicalDeviceInlineUniformBlockFeaturesEXT {
967                inline_uniform_block: vk::TRUE,
968                ..Default::default()
969            };
970            let mut khr_timeline_semaphore = vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR {
971                timeline_semaphore: vk::TRUE,
972                ..Default::default()
973            };
974            let mut khr_dynamic_rendering = vk::PhysicalDeviceDynamicRenderingFeaturesKHR {
975                dynamic_rendering: vk::TRUE,
976                ..Default::default()
977            };
978            let mut device_create_info = vk::DeviceCreateInfo::default()
979                .queue_create_infos(&family_infos)
980                .enabled_extension_names(&str_pointers)
981                .push_next(&mut khr_timeline_semaphore)
982                .push_next(&mut khr_dynamic_rendering);
983            if capabilities.max_inline_uniform_block_size > 0 {
984                device_create_info = device_create_info.push_next(&mut ext_inline_uniform_block);
985            }
986
987            let mut ext_descriptor_indexing;
988            if needs_descriptor_indexing {
989                ext_descriptor_indexing = vk::PhysicalDeviceDescriptorIndexingFeaturesEXT {
990                    shader_storage_buffer_array_non_uniform_indexing: vk::TRUE,
991                    shader_sampled_image_array_non_uniform_indexing: vk::TRUE,
992                    descriptor_binding_partially_bound: vk::TRUE,
993                    ..Default::default()
994                };
995                device_create_info = device_create_info.push_next(&mut ext_descriptor_indexing);
996            }
997
998            let mut khr_buffer_device_address;
999            if capabilities.buffer_device_address {
1000                khr_buffer_device_address = vk::PhysicalDeviceBufferDeviceAddressFeaturesKHR {
1001                    buffer_device_address: vk::TRUE,
1002                    ..Default::default()
1003                };
1004                device_create_info = device_create_info.push_next(&mut khr_buffer_device_address);
1005            }
1006
1007            let mut khr_acceleration_structure;
1008            let mut khr_ray_query;
1009            if capabilities.ray_tracing.is_some() {
1010                khr_acceleration_structure = vk::PhysicalDeviceAccelerationStructureFeaturesKHR {
1011                    acceleration_structure: vk::TRUE,
1012                    ..Default::default()
1013                };
1014                khr_ray_query = vk::PhysicalDeviceRayQueryFeaturesKHR {
1015                    ray_query: vk::TRUE,
1016                    ..Default::default()
1017                };
1018                device_create_info = device_create_info
1019                    .push_next(&mut khr_acceleration_structure)
1020                    .push_next(&mut khr_ray_query);
1021            }
1022
1023            let mut khr_float16_int8;
1024            let mut storage_16bit;
1025            if capabilities.shader_float16 {
1026                khr_float16_int8 = vk::PhysicalDeviceShaderFloat16Int8Features {
1027                    shader_float16: vk::TRUE,
1028                    ..Default::default()
1029                };
1030                device_create_info = device_create_info.push_next(&mut khr_float16_int8);
1031            }
1032            if capabilities.cooperative_matrix.f16_tile > 0 {
1033                storage_16bit = vk::PhysicalDevice16BitStorageFeatures {
1034                    storage_buffer16_bit_access: vk::TRUE,
1035                    uniform_and_storage_buffer16_bit_access: vk::TRUE,
1036                    ..Default::default()
1037                };
1038                device_create_info = device_create_info.push_next(&mut storage_16bit);
1039            }
1040
1041            let mut khr_cooperative_matrix;
1042            let mut vulkan_memory_model;
1043            if capabilities.cooperative_matrix.is_supported() {
1044                khr_cooperative_matrix = vk::PhysicalDeviceCooperativeMatrixFeaturesKHR {
1045                    cooperative_matrix: vk::TRUE,
1046                    ..Default::default()
1047                };
1048                vulkan_memory_model = vk::PhysicalDeviceVulkanMemoryModelFeatures {
1049                    vulkan_memory_model: vk::TRUE,
1050                    ..Default::default()
1051                };
1052                device_create_info = device_create_info
1053                    .push_next(&mut khr_cooperative_matrix)
1054                    .push_next(&mut vulkan_memory_model);
1055            }
1056
1057            // TODO: Replace with ash typed struct once available.
1058            let mut khr_unified_image_layouts;
1059            if capabilities.unified_image_layouts {
1060                khr_unified_image_layouts = unified_image_layouts::PhysicalDeviceFeatures {
1061                    unified_image_layouts: vk::TRUE,
1062                    ..Default::default()
1063                };
1064                device_create_info = device_create_info.push_next(&mut khr_unified_image_layouts);
1065            }
1066
1067            let mut core_features = vk::PhysicalDeviceFeatures::default();
1068            if capabilities.dual_source_blending {
1069                core_features.dual_src_blend = vk::TRUE;
1070            }
1071
1072            let mut device_features2 =
1073                vk::PhysicalDeviceFeatures2::default().features(core_features);
1074
1075            device_create_info = device_create_info.push_next(&mut device_features2);
1076
1077            if let Some(ref xr_desc) = desc.xr {
1078                let get_instance_proc_addr: openxr::sys::platform::VkGetInstanceProcAddr =
1079                    unsafe { std::mem::transmute(inner.entry.static_fn().get_instance_proc_addr) };
1080                unsafe {
1081                    let raw_device = xr_desc
1082                        .instance
1083                        .create_vulkan_device(
1084                            xr_desc.system_id,
1085                            get_instance_proc_addr,
1086                            physical_device.as_raw() as *const ffi::c_void,
1087                            &device_create_info as *const _ as *const ffi::c_void,
1088                        )
1089                        .map_err(|_| NotSupportedError::NoSupportedDeviceFound)?
1090                        .map_err(|raw| crate::PlatformError::init(vk::Result::from_raw(raw)))?;
1091                    ash::Device::load(
1092                        inner.instance.core.fp_v1_0(),
1093                        vk::Device::from_raw(raw_device as _),
1094                    )
1095                }
1096            } else {
1097                unsafe {
1098                    inner
1099                        .instance
1100                        .core
1101                        .create_device(physical_device, &device_create_info, None)
1102                        .map_err(crate::PlatformError::init)?
1103                }
1104            }
1105        };
1106
1107        let instance = &inner.instance;
1108        let device = super::Device {
1109            swapchain: if desc.presentation {
1110                Some(khr::swapchain::Device::new(&instance.core, &device_core))
1111            } else {
1112                None
1113            },
1114            debug_utils: ext::debug_utils::Device::new(&instance.core, &device_core),
1115            timeline_semaphore: khr::timeline_semaphore::Device::new(&instance.core, &device_core),
1116            dynamic_rendering: khr::dynamic_rendering::Device::new(&instance.core, &device_core),
1117            ray_tracing: if let Some(ref caps) = capabilities.ray_tracing {
1118                Some(super::RayTracingDevice {
1119                    acceleration_structure: khr::acceleration_structure::Device::new(
1120                        &instance.core,
1121                        &device_core,
1122                    ),
1123                    scratch_buffer_alignment: caps.min_scratch_buffer_alignment,
1124                })
1125            } else {
1126                None
1127            },
1128            buffer_device_address: capabilities.buffer_device_address,
1129            max_inline_uniform_block_size: capabilities.max_inline_uniform_block_size,
1130            buffer_marker: if capabilities.buffer_marker && desc.validation {
1131                Some(amd::buffer_marker::Device::new(
1132                    &instance.core,
1133                    &device_core,
1134                ))
1135            } else {
1136                None
1137            },
1138            shader_info: if capabilities.shader_info {
1139                Some(amd::shader_info::Device::new(&instance.core, &device_core))
1140            } else {
1141                None
1142            },
1143            full_screen_exclusive: if desc.presentation && capabilities.full_screen_exclusive {
1144                Some(ext::full_screen_exclusive::Device::new(
1145                    &instance.core,
1146                    &device_core,
1147                ))
1148            } else {
1149                None
1150            },
1151            external_memory: if capabilities.external_memory {
1152                #[cfg(not(target_os = "windows"))]
1153                use khr::external_memory_fd::Device;
1154                #[cfg(target_os = "windows")]
1155                use khr::external_memory_win32::Device;
1156
1157                Some(Device::new(&instance.core, &device_core))
1158            } else {
1159                None
1160            },
1161            core: device_core,
1162            device_information: capabilities.device_information,
1163            command_scope: if desc.capture {
1164                Some(super::CommandScopeDevice {})
1165            } else {
1166                None
1167            },
1168            timing: if desc.timing && capabilities.timing {
1169                Some(super::TimingDevice {
1170                    period: capabilities.properties.limits.timestamp_period,
1171                })
1172            } else {
1173                None
1174            },
1175            //TODO: detect GPU family
1176            workarounds: super::Workarounds {
1177                extra_sync_src_access: vk::AccessFlags::TRANSFER_WRITE,
1178                extra_sync_dst_access: vk::AccessFlags::TRANSFER_WRITE
1179                    | vk::AccessFlags::TRANSFER_READ
1180                    | if capabilities.ray_tracing.is_some() {
1181                        vk::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR
1182                    } else {
1183                        vk::AccessFlags::NONE
1184                    },
1185                extra_descriptor_pool_create_flags: if capabilities
1186                    .bugs
1187                    .intel_fix_descriptor_pool_leak
1188                {
1189                    vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET
1190                } else {
1191                    vk::DescriptorPoolCreateFlags::empty()
1192                },
1193            },
1194        };
1195
1196        let memory_manager = {
1197            let mem_properties = unsafe {
1198                inner
1199                    .instance
1200                    .core
1201                    .get_physical_device_memory_properties(physical_device)
1202            };
1203            let memory_types =
1204                &mem_properties.memory_types[..mem_properties.memory_type_count as usize];
1205            let limits = &capabilities.properties.limits;
1206            let config = gpu_alloc::Config::i_am_prototyping(); //TODO?
1207
1208            let properties = gpu_alloc::DeviceProperties {
1209                max_memory_allocation_count: limits.max_memory_allocation_count,
1210                max_memory_allocation_size: u64::MAX, // TODO
1211                non_coherent_atom_size: limits.non_coherent_atom_size,
1212                memory_types: memory_types
1213                    .iter()
1214                    .map(|memory_type| gpu_alloc::MemoryType {
1215                        props: gpu_alloc::MemoryPropertyFlags::from_bits_truncate(
1216                            memory_type.property_flags.as_raw() as u8,
1217                        ),
1218                        heap: memory_type.heap_index,
1219                    })
1220                    .collect(),
1221                memory_heaps: mem_properties.memory_heaps
1222                    [..mem_properties.memory_heap_count as usize]
1223                    .iter()
1224                    .map(|&memory_heap| gpu_alloc::MemoryHeap {
1225                        size: memory_heap.size,
1226                    })
1227                    .collect(),
1228                buffer_device_address: capabilities.buffer_device_address,
1229            };
1230
1231            let known_memory_flags = vk::MemoryPropertyFlags::DEVICE_LOCAL
1232                | vk::MemoryPropertyFlags::HOST_VISIBLE
1233                | vk::MemoryPropertyFlags::HOST_COHERENT
1234                | vk::MemoryPropertyFlags::HOST_CACHED
1235                | vk::MemoryPropertyFlags::LAZILY_ALLOCATED;
1236            let valid_ash_memory_types = memory_types.iter().enumerate().fold(0, |u, (i, mem)| {
1237                if !known_memory_flags.contains(mem.property_flags) {
1238                    log::debug!(
1239                        "Skipping memory type={} for having unknown flags: {:?}",
1240                        i,
1241                        mem.property_flags & !known_memory_flags
1242                    );
1243                    u
1244                } else if mem
1245                    .property_flags
1246                    .contains(vk::MemoryPropertyFlags::HOST_VISIBLE)
1247                    && !mem
1248                        .property_flags
1249                        .contains(vk::MemoryPropertyFlags::HOST_COHERENT)
1250                {
1251                    //TODO: see if and how we can support this
1252                    log::debug!("Skipping memory type={} for lack of host coherency", i);
1253                    u
1254                } else {
1255                    u | (1 << i)
1256                }
1257            });
1258            super::MemoryManager {
1259                allocator: gpu_alloc::GpuAllocator::new(config, properties),
1260                slab: slab::Slab::new(),
1261                valid_ash_memory_types,
1262            }
1263        };
1264
1265        let queue = unsafe {
1266            device
1267                .core
1268                .get_device_queue(capabilities.queue_family_index, 0)
1269        };
1270        let last_progress = 0;
1271        let mut timeline_info = vk::SemaphoreTypeCreateInfo {
1272            semaphore_type: vk::SemaphoreType::TIMELINE,
1273            initial_value: last_progress,
1274            ..Default::default()
1275        };
1276        let timeline_semaphore_create_info =
1277            vk::SemaphoreCreateInfo::default().push_next(&mut timeline_info);
1278        let timeline_semaphore = unsafe {
1279            device
1280                .core
1281                .create_semaphore(&timeline_semaphore_create_info, None)
1282                .unwrap()
1283        };
1284
1285        let mut naga_flags = spv::WriterFlags::FORCE_POINT_SIZE;
1286        let shader_debug_path = if desc.validation || desc.capture {
1287            use std::{env, fs};
1288            naga_flags |= spv::WriterFlags::DEBUG;
1289            let dir = env::temp_dir().join("blade");
1290            let _ = fs::create_dir(&dir);
1291            Some(dir)
1292        } else {
1293            None
1294        };
1295
1296        let xr = if let Some(ref xr_desc) = desc.xr {
1297            let session_info = openxr::vulkan::SessionCreateInfo {
1298                instance: inner.instance.core.handle().as_raw() as _,
1299                physical_device: physical_device.as_raw() as _,
1300                device: device.core.handle().as_raw() as _,
1301                queue_family_index: capabilities.queue_family_index,
1302                queue_index: 0,
1303            };
1304            match unsafe {
1305                xr_desc
1306                    .instance
1307                    .create_session::<openxr::Vulkan>(xr_desc.system_id, &session_info)
1308            } {
1309                Ok((session, frame_wait, frame_stream)) => {
1310                    let view_type = openxr::ViewConfigurationType::PRIMARY_STEREO;
1311                    let environment_blend_mode = xr_desc
1312                        .instance
1313                        .enumerate_environment_blend_modes(xr_desc.system_id, view_type)
1314                        .ok()
1315                        .and_then(|modes| modes.first().copied())
1316                        .unwrap_or(openxr::EnvironmentBlendMode::OPAQUE);
1317                    let space = match session.create_reference_space(
1318                        openxr::ReferenceSpaceType::LOCAL,
1319                        openxr::Posef::IDENTITY,
1320                    ) {
1321                        Ok(space) => space,
1322                        Err(e) => {
1323                            log::error!("Failed to create OpenXR local space: {e}");
1324                            return Err(NotSupportedError::NoSupportedDeviceFound);
1325                        }
1326                    };
1327                    Some(Mutex::new(super::XrSessionState {
1328                        instance: xr_desc.instance.clone(),
1329                        system_id: xr_desc.system_id,
1330                        session,
1331                        frame_wait,
1332                        frame_stream,
1333                        view_type,
1334                        environment_blend_mode,
1335                        space: Some(space),
1336                        predicted_display_time: None,
1337                    }))
1338                }
1339                Err(e) => {
1340                    log::error!("Failed to create OpenXR session: {e}");
1341                    return Err(NotSupportedError::NoSupportedDeviceFound);
1342                }
1343            }
1344        } else {
1345            None
1346        };
1347
1348        Ok(super::Context {
1349            memory: Mutex::new(memory_manager),
1350            device,
1351            queue_family_index: capabilities.queue_family_index,
1352            queue: Mutex::new(super::Queue {
1353                raw: queue,
1354                timeline_semaphore,
1355                last_progress,
1356            }),
1357            physical_device,
1358            naga_flags,
1359            shader_debug_path,
1360            min_buffer_alignment,
1361            min_uniform_buffer_offset_alignment: capabilities
1362                .properties
1363                .limits
1364                .min_uniform_buffer_offset_alignment,
1365            sample_count_flags: capabilities
1366                .properties
1367                .limits
1368                .framebuffer_color_sample_counts
1369                & capabilities
1370                    .properties
1371                    .limits
1372                    .framebuffer_depth_sample_counts,
1373            dual_source_blending: capabilities.dual_source_blending,
1374            shader_float16: capabilities.shader_float16,
1375            cooperative_matrix: capabilities.cooperative_matrix,
1376            binding_array: capabilities.binding_array,
1377            memory_budget: capabilities.memory_budget,
1378            inner,
1379            xr,
1380        })
1381    }
1382
1383    pub(super) fn set_object_name<T: vk::Handle>(&self, object: T, name: &str) {
1384        let name_cstr = ffi::CString::new(name).unwrap();
1385        let name_info = vk::DebugUtilsObjectNameInfoEXT::default()
1386            .object_handle(object)
1387            .object_name(&name_cstr);
1388        let _ = unsafe {
1389            self.device
1390                .debug_utils
1391                .set_debug_utils_object_name(&name_info)
1392        };
1393    }
1394
1395    pub fn capabilities(&self) -> crate::Capabilities {
1396        crate::Capabilities {
1397            binding_array: self.binding_array,
1398            ray_query: match self.device.ray_tracing {
1399                Some(_) => crate::ShaderVisibility::all(),
1400                None => crate::ShaderVisibility::empty(),
1401            },
1402            sample_count_mask: self.sample_count_flags.as_raw(),
1403            dual_source_blending: self.dual_source_blending,
1404            shader_float16: self.shader_float16,
1405            cooperative_matrix: self.cooperative_matrix,
1406        }
1407    }
1408
1409    pub fn device_information(&self) -> &crate::DeviceInformation {
1410        &self.device.device_information
1411    }
1412
1413    pub fn enumerate() -> Result<Vec<crate::DeviceReport>, NotSupportedError> {
1414        let desc = crate::ContextDesc::default();
1415        let inner = unsafe { super::VulkanInstance::create(&desc)? };
1416        Ok(inspect_devices(
1417            &inner.instance,
1418            inner.driver_api_version,
1419            None,
1420        ))
1421    }
1422
1423    pub fn enumerate_devices(&self) -> Vec<crate::DeviceReport> {
1424        inspect_devices(
1425            &self.inner.instance,
1426            self.inner.driver_api_version,
1427            Some(self.physical_device),
1428        )
1429    }
1430
1431    pub fn memory_stats(&self) -> crate::MemoryStats {
1432        if !self.memory_budget {
1433            return crate::MemoryStats::default();
1434        }
1435
1436        let mut budget_properties = vk::PhysicalDeviceMemoryBudgetPropertiesEXT::default();
1437        let mut mem_properties2 =
1438            vk::PhysicalDeviceMemoryProperties2::default().push_next(&mut budget_properties);
1439
1440        unsafe {
1441            self.inner
1442                .instance
1443                .get_physical_device_properties2
1444                .get_physical_device_memory_properties2(self.physical_device, &mut mem_properties2);
1445        }
1446
1447        // Copy what we need before accessing budget_properties
1448        let heap_count = mem_properties2.memory_properties.memory_heap_count as usize;
1449        let heap_flags: Vec<_> = mem_properties2.memory_properties.memory_heaps[..heap_count]
1450            .iter()
1451            .map(|h| h.flags)
1452            .collect();
1453        // Now mem_properties2 borrow is released, we can access budget_properties
1454        let _ = mem_properties2;
1455
1456        let mut total_budget = 0u64;
1457        let mut total_usage = 0u64;
1458        for (i, flags) in heap_flags.iter().enumerate() {
1459            if flags.contains(vk::MemoryHeapFlags::DEVICE_LOCAL) {
1460                total_budget += budget_properties.heap_budget[i];
1461                total_usage += budget_properties.heap_usage[i];
1462            }
1463        }
1464
1465        crate::MemoryStats {
1466            budget: total_budget,
1467            usage: total_usage,
1468        }
1469    }
1470}
1471
1472impl Drop for super::Context {
1473    fn drop(&mut self) {
1474        unsafe {
1475            self.xr = None;
1476            if let Ok(queue) = self.queue.lock() {
1477                let _ = self.device.core.queue_wait_idle(queue.raw);
1478                self.device
1479                    .core
1480                    .destroy_semaphore(queue.timeline_semaphore, None);
1481            }
1482            self.device.core.destroy_device(None);
1483            // inner.drop() destroys the Vulkan instance
1484        }
1485    }
1486}