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