Skip to main content

blade_graphics/vulkan/
init.rs

1use ash::{amd, ext, khr, vk};
2use naga::back::spv;
3use std::{ffi, fs, sync::Mutex};
4
5use crate::NotSupportedError;
6
7mod db {
8    pub mod intel {
9        pub const VENDOR: u32 = 0x8086;
10    }
11}
12mod layer {
13    use std::ffi::CStr;
14    pub const KHRONOS_VALIDATION: &CStr =
15        unsafe { CStr::from_bytes_with_nul_unchecked(b"VK_LAYER_KHRONOS_validation\0") };
16    pub const MESA_OVERLAY: &CStr =
17        unsafe { CStr::from_bytes_with_nul_unchecked(b"VK_LAYER_MESA_overlay\0") };
18}
19
20const REQUIRED_DEVICE_EXTENSIONS: &[&ffi::CStr] = &[
21    vk::EXT_INLINE_UNIFORM_BLOCK_NAME,
22    vk::KHR_TIMELINE_SEMAPHORE_NAME,
23    vk::KHR_DESCRIPTOR_UPDATE_TEMPLATE_NAME,
24    vk::KHR_DYNAMIC_RENDERING_NAME,
25];
26
27fn is_promoted_instance_extension(name: &ffi::CStr, api_version: u32) -> bool {
28    if api_version < vk::API_VERSION_1_1 {
29        return false;
30    }
31
32    name == vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_NAME
33        || name == vk::KHR_GET_SURFACE_CAPABILITIES2_NAME
34}
35
36#[derive(Debug)]
37struct RayTracingCapabilities {
38    min_scratch_buffer_alignment: u64,
39}
40
41#[derive(Debug)]
42struct SystemBugs {
43    /// https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688
44    intel_unable_to_present: bool,
45    /// https://github.com/kvark/blade/issues/117
46    intel_fix_descriptor_pool_leak: bool,
47}
48
49#[derive(Debug)]
50struct AdapterCapabilities {
51    api_version: u32,
52    properties: vk::PhysicalDeviceProperties,
53    device_information: crate::DeviceInformation,
54    queue_family_index: u32,
55    layered: bool,
56    ray_tracing: Option<RayTracingCapabilities>,
57    buffer_marker: bool,
58    shader_info: bool,
59    full_screen_exclusive: bool,
60    external_memory: bool,
61    timing: bool,
62    bugs: SystemBugs,
63}
64
65// See https://github.com/canonical/nvidia-prime/blob/587c5012be9dddcc17ab4d958f10a24fa3342b4d/prime-select#L56
66fn is_nvidia_prime_forced() -> bool {
67    match fs::read_to_string("/etc/prime-discrete") {
68        Ok(contents) => contents == "on\n",
69        Err(_) => false,
70    }
71}
72
73unsafe fn inspect_adapter(
74    phd: vk::PhysicalDevice,
75    instance: &super::Instance,
76    driver_api_version: u32,
77    desc: &crate::ContextDesc,
78) -> Option<AdapterCapabilities> {
79    let mut inline_uniform_block_properties =
80        vk::PhysicalDeviceInlineUniformBlockPropertiesEXT::default();
81    let mut timeline_semaphore_properties =
82        vk::PhysicalDeviceTimelineSemaphorePropertiesKHR::default();
83    let mut descriptor_indexing_properties =
84        vk::PhysicalDeviceDescriptorIndexingPropertiesEXT::default();
85    let mut acceleration_structure_properties =
86        vk::PhysicalDeviceAccelerationStructurePropertiesKHR::default();
87    let mut portability_subset_properties =
88        vk::PhysicalDevicePortabilitySubsetPropertiesKHR::default();
89
90    let mut driver_properties = vk::PhysicalDeviceDriverPropertiesKHR::default();
91    let mut properties2_khr = vk::PhysicalDeviceProperties2KHR::default()
92        .push_next(&mut inline_uniform_block_properties)
93        .push_next(&mut timeline_semaphore_properties)
94        .push_next(&mut descriptor_indexing_properties)
95        .push_next(&mut acceleration_structure_properties)
96        .push_next(&mut portability_subset_properties)
97        .push_next(&mut driver_properties);
98    instance
99        .get_physical_device_properties2
100        .get_physical_device_properties2(phd, &mut properties2_khr);
101
102    let properties = properties2_khr.properties;
103    let name = ffi::CStr::from_ptr(properties.device_name.as_ptr());
104    log::info!("Adapter: {:?}", name);
105
106    if desc.device_id != 0 && desc.device_id != properties.device_id {
107        log::info!("Rejected device ID 0x{:X}", properties.device_id);
108        return None;
109    }
110
111    let api_version = properties.api_version.min(driver_api_version);
112    if api_version < vk::API_VERSION_1_1 {
113        log::warn!("\tRejected for API version {}", api_version);
114        return None;
115    }
116
117    let supported_extension_properties = instance
118        .core
119        .enumerate_device_extension_properties(phd)
120        .unwrap();
121    let supported_extensions = supported_extension_properties
122        .iter()
123        .map(|ext_prop| ffi::CStr::from_ptr(ext_prop.extension_name.as_ptr()))
124        .collect::<Vec<_>>();
125    for extension in REQUIRED_DEVICE_EXTENSIONS {
126        if !supported_extensions.contains(extension) {
127            log::warn!(
128                "Rejected for device extension {:?} not supported. Please update the driver!",
129                extension
130            );
131            return None;
132        }
133    }
134
135    let bugs = SystemBugs {
136        //Note: this is somewhat broad across X11/Wayland and different drivers.
137        // It could be narrower, but at the end of the day if the user forced Prime
138        // for GLX it should be safe to assume they want it for Vulkan as well.
139        intel_unable_to_present: is_nvidia_prime_forced()
140            && properties.vendor_id == db::intel::VENDOR,
141        intel_fix_descriptor_pool_leak: cfg!(windows) && properties.vendor_id == db::intel::VENDOR,
142    };
143
144    let queue_family_index = 0; //TODO
145    if desc.presentation && bugs.intel_unable_to_present {
146        log::warn!("Rejecting Intel for not presenting when Nvidia is present (on Linux)");
147        return None;
148    }
149
150    let mut inline_uniform_block_features =
151        vk::PhysicalDeviceInlineUniformBlockFeaturesEXT::default();
152    let mut timeline_semaphore_features = vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR::default();
153    let mut dynamic_rendering_features = vk::PhysicalDeviceDynamicRenderingFeaturesKHR::default();
154    let mut descriptor_indexing_features =
155        vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::default();
156    let mut buffer_device_address_features =
157        vk::PhysicalDeviceBufferDeviceAddressFeaturesKHR::default();
158    let mut acceleration_structure_features =
159        vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default();
160    let mut ray_query_features = vk::PhysicalDeviceRayQueryFeaturesKHR::default();
161    let mut features2_khr = vk::PhysicalDeviceFeatures2::default()
162        .push_next(&mut inline_uniform_block_features)
163        .push_next(&mut timeline_semaphore_features)
164        .push_next(&mut dynamic_rendering_features)
165        .push_next(&mut descriptor_indexing_features)
166        .push_next(&mut buffer_device_address_features)
167        .push_next(&mut acceleration_structure_features)
168        .push_next(&mut ray_query_features);
169    instance
170        .get_physical_device_properties2
171        .get_physical_device_features2(phd, &mut features2_khr);
172
173    if inline_uniform_block_properties.max_inline_uniform_block_size
174        < crate::limits::PLAIN_DATA_SIZE
175        || inline_uniform_block_properties.max_descriptor_set_inline_uniform_blocks == 0
176        || inline_uniform_block_features.inline_uniform_block == 0
177    {
178        log::warn!(
179            "\tRejected for inline uniform blocks. Properties = {:?}, Features = {:?}",
180            inline_uniform_block_properties,
181            inline_uniform_block_features,
182        );
183        return None;
184    }
185
186    if timeline_semaphore_features.timeline_semaphore == 0 {
187        log::warn!(
188            "\tRejected for timeline semaphore. Properties = {:?}, Features = {:?}",
189            timeline_semaphore_properties,
190            timeline_semaphore_features,
191        );
192        return None;
193    }
194
195    if dynamic_rendering_features.dynamic_rendering == 0 {
196        log::warn!(
197            "\tRejected for dynamic rendering. Features = {:?}",
198            dynamic_rendering_features,
199        );
200        return None;
201    }
202
203    let external_memory = supported_extensions.contains(&vk::KHR_EXTERNAL_MEMORY_NAME);
204    let external_memory = external_memory
205        && supported_extensions.contains(if cfg!(target_os = "windows") {
206            &vk::KHR_EXTERNAL_MEMORY_WIN32_NAME
207        } else {
208            &vk::KHR_EXTERNAL_MEMORY_FD_NAME
209        });
210
211    let timing = if properties.limits.timestamp_compute_and_graphics == vk::FALSE {
212        log::info!("No timing because of queue support");
213        false
214    } else {
215        true
216    };
217
218    let ray_tracing = if !supported_extensions.contains(&vk::KHR_ACCELERATION_STRUCTURE_NAME)
219        || !supported_extensions.contains(&vk::KHR_RAY_QUERY_NAME)
220    {
221        log::info!("No ray tracing extensions are supported");
222        None
223    } else if descriptor_indexing_properties.max_per_stage_update_after_bind_resources == vk::FALSE
224        || descriptor_indexing_features.descriptor_binding_partially_bound == vk::FALSE
225        || descriptor_indexing_features.shader_storage_buffer_array_non_uniform_indexing
226            == vk::FALSE
227        || descriptor_indexing_features.shader_sampled_image_array_non_uniform_indexing == vk::FALSE
228    {
229        log::info!(
230            "No ray tracing because of the descriptor indexing. Properties = {:?}. Features = {:?}",
231            descriptor_indexing_properties,
232            descriptor_indexing_features
233        );
234        None
235    } else if buffer_device_address_features.buffer_device_address == vk::FALSE {
236        log::info!(
237            "No ray tracing because of the buffer device address. Features = {:?}",
238            buffer_device_address_features
239        );
240        None
241    } else if acceleration_structure_properties.max_geometry_count == 0
242        || acceleration_structure_features.acceleration_structure == vk::FALSE
243    {
244        log::info!("No ray tracing because of the acceleration structure. Properties = {:?}. Features = {:?}",
245            acceleration_structure_properties, acceleration_structure_features);
246        None
247    } else if ray_query_features.ray_query == vk::FALSE {
248        log::info!(
249            "No ray tracing because of the ray query. Features = {:?}",
250            ray_query_features
251        );
252        None
253    } else {
254        log::info!("Ray tracing is supported");
255        log::debug!("Ray tracing properties: {acceleration_structure_properties:#?}");
256        Some(RayTracingCapabilities {
257            min_scratch_buffer_alignment: acceleration_structure_properties
258                .min_acceleration_structure_scratch_offset_alignment
259                as u64,
260        })
261    };
262
263    let buffer_marker = supported_extensions.contains(&vk::AMD_BUFFER_MARKER_NAME);
264    let shader_info = supported_extensions.contains(&vk::AMD_SHADER_INFO_NAME);
265    let full_screen_exclusive = supported_extensions.contains(&vk::EXT_FULL_SCREEN_EXCLUSIVE_NAME);
266
267    let device_information = crate::DeviceInformation {
268        is_software_emulated: properties.device_type == vk::PhysicalDeviceType::CPU,
269        device_name: ffi::CStr::from_ptr(properties.device_name.as_ptr())
270            .to_string_lossy()
271            .to_string(),
272        driver_name: ffi::CStr::from_ptr(driver_properties.driver_name.as_ptr())
273            .to_string_lossy()
274            .to_string(),
275        driver_info: ffi::CStr::from_ptr(driver_properties.driver_info.as_ptr())
276            .to_string_lossy()
277            .to_string(),
278    };
279
280    Some(AdapterCapabilities {
281        api_version,
282        properties,
283        device_information,
284        queue_family_index,
285        layered: portability_subset_properties.min_vertex_input_binding_stride_alignment != 0,
286        ray_tracing,
287        buffer_marker,
288        shader_info,
289        full_screen_exclusive,
290        external_memory,
291        timing,
292        bugs,
293    })
294}
295
296impl super::Context {
297    pub unsafe fn init(desc: crate::ContextDesc) -> Result<Self, NotSupportedError> {
298        let entry = match ash::Entry::load() {
299            Ok(entry) => entry,
300            Err(err) => {
301                log::error!("Missing Vulkan entry points: {:?}", err);
302                return Err(super::PlatformError::Loading(err).into());
303            }
304        };
305        let driver_api_version = match entry.try_enumerate_instance_version() {
306            // Vulkan 1.1+
307            Ok(Some(version)) => version,
308            Ok(None) => return Err(NotSupportedError::NoSupportedDeviceFound),
309            Err(err) => {
310                log::error!("try_enumerate_instance_version: {:?}", err);
311                return Err(super::PlatformError::Init(err).into());
312            }
313        };
314
315        let supported_layers = match entry.enumerate_instance_layer_properties() {
316            Ok(layers) => layers,
317            Err(err) => {
318                log::error!("enumerate_instance_layer_properties: {:?}", err);
319                return Err(super::PlatformError::Init(err).into());
320            }
321        };
322        let supported_layer_names = supported_layers
323            .iter()
324            .map(|properties| ffi::CStr::from_ptr(properties.layer_name.as_ptr()))
325            .collect::<Vec<_>>();
326
327        let mut layers: Vec<&'static ffi::CStr> = Vec::new();
328        let mut requested_layers = Vec::<&ffi::CStr>::new();
329        if desc.validation {
330            requested_layers.push(layer::KHRONOS_VALIDATION);
331        }
332        if desc.overlay {
333            requested_layers.push(layer::MESA_OVERLAY);
334        }
335        for name in requested_layers {
336            if supported_layer_names.contains(&name) {
337                layers.push(name);
338            } else {
339                log::warn!("Requested layer is not found: {:?}", name);
340            }
341        }
342
343        let supported_instance_extension_properties =
344            match entry.enumerate_instance_extension_properties(None) {
345                Ok(extensions) => extensions,
346                Err(err) => {
347                    log::error!("enumerate_instance_extension_properties: {:?}", err);
348                    return Err(super::PlatformError::Init(err).into());
349                }
350            };
351        let supported_instance_extensions = supported_instance_extension_properties
352            .iter()
353            .map(|ext_prop| ffi::CStr::from_ptr(ext_prop.extension_name.as_ptr()))
354            .collect::<Vec<_>>();
355
356        let core_instance = {
357            let mut create_flags = vk::InstanceCreateFlags::empty();
358
359            let mut instance_extensions = vec![
360                vk::EXT_DEBUG_UTILS_NAME,
361                vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_NAME,
362            ];
363            if desc.presentation {
364                instance_extensions.push(vk::KHR_SURFACE_NAME);
365                instance_extensions.push(vk::KHR_GET_SURFACE_CAPABILITIES2_NAME);
366                let candidates = [
367                    vk::KHR_WAYLAND_SURFACE_NAME,
368                    vk::KHR_XCB_SURFACE_NAME,
369                    vk::KHR_XLIB_SURFACE_NAME,
370                    vk::KHR_WIN32_SURFACE_NAME,
371                    vk::KHR_ANDROID_SURFACE_NAME,
372                    vk::EXT_SWAPCHAIN_COLORSPACE_NAME,
373                ];
374                for candidate in candidates.iter() {
375                    if supported_instance_extensions.contains(candidate) {
376                        log::info!("Presentation support: {:?}", candidate);
377                        instance_extensions.push(candidate);
378                    }
379                }
380            }
381
382            let mut enabled_instance_extensions = Vec::with_capacity(instance_extensions.len());
383            for inst_ext in instance_extensions.drain(..) {
384                if supported_instance_extensions.contains(&inst_ext) {
385                    enabled_instance_extensions.push(inst_ext);
386                } else if is_promoted_instance_extension(inst_ext, driver_api_version) {
387                    log::info!(
388                        "Skipping promoted instance extension {:?} (core version {:x})",
389                        inst_ext,
390                        driver_api_version
391                    );
392                } else {
393                    log::error!("Instance extension {:?} is not supported", inst_ext);
394                    return Err(NotSupportedError::NoSupportedDeviceFound);
395                }
396            }
397            if supported_instance_extensions.contains(&vk::KHR_PORTABILITY_ENUMERATION_NAME) {
398                log::info!("Enabling Vulkan Portability");
399                enabled_instance_extensions.push(vk::KHR_PORTABILITY_ENUMERATION_NAME);
400                create_flags |= vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR;
401            }
402
403            let app_info = vk::ApplicationInfo::default()
404                .engine_name(ffi::CStr::from_bytes_with_nul(b"blade\0").unwrap())
405                .engine_version(1)
406                .api_version(vk::HEADER_VERSION_COMPLETE);
407            let str_pointers = layers
408                .iter()
409                .chain(enabled_instance_extensions.iter())
410                .map(|&s| s.as_ptr())
411                .collect::<Vec<_>>();
412            let (layer_strings, extension_strings) = str_pointers.split_at(layers.len());
413            let create_info = vk::InstanceCreateInfo::default()
414                .application_info(&app_info)
415                .flags(create_flags)
416                .enabled_layer_names(layer_strings)
417                .enabled_extension_names(extension_strings);
418            unsafe { entry.create_instance(&create_info, None) }
419                .map_err(super::PlatformError::Init)?
420        };
421
422        let instance =
423            super::Instance {
424                _debug_utils: ext::debug_utils::Instance::new(&entry, &core_instance),
425                get_physical_device_properties2:
426                    khr::get_physical_device_properties2::Instance::new(&entry, &core_instance),
427                get_surface_capabilities2: if desc.presentation {
428                    Some(khr::get_surface_capabilities2::Instance::new(
429                        &entry,
430                        &core_instance,
431                    ))
432                } else {
433                    None
434                },
435                surface: if desc.presentation {
436                    Some(khr::surface::Instance::new(&entry, &core_instance))
437                } else {
438                    None
439                },
440                core: core_instance,
441            };
442
443        let physical_devices = instance
444            .core
445            .enumerate_physical_devices()
446            .map_err(super::PlatformError::Init)?;
447        let (physical_device, capabilities) = physical_devices
448            .into_iter()
449            .find_map(|phd| {
450                inspect_adapter(phd, &instance, driver_api_version, &desc).map(|caps| (phd, caps))
451            })
452            .ok_or_else(|| NotSupportedError::NoSupportedDeviceFound)?;
453
454        log::debug!("Adapter {:#?}", capabilities);
455        let mut min_buffer_alignment = 1;
456        if let Some(ref rt) = capabilities.ray_tracing {
457            min_buffer_alignment = min_buffer_alignment.max(rt.min_scratch_buffer_alignment);
458        }
459
460        let device_core = {
461            let family_info = vk::DeviceQueueCreateInfo::default()
462                .queue_family_index(capabilities.queue_family_index)
463                .queue_priorities(&[1.0]);
464            let family_infos = [family_info];
465
466            let mut device_extensions = REQUIRED_DEVICE_EXTENSIONS.to_vec();
467            if desc.presentation {
468                device_extensions.push(vk::KHR_SWAPCHAIN_NAME);
469            }
470            if capabilities.layered {
471                log::info!("Enabling Vulkan Portability");
472                device_extensions.push(vk::KHR_PORTABILITY_SUBSET_NAME);
473            }
474            if capabilities.ray_tracing.is_some() {
475                if capabilities.api_version < vk::API_VERSION_1_2 {
476                    device_extensions.push(vk::EXT_DESCRIPTOR_INDEXING_NAME);
477                    device_extensions.push(vk::KHR_BUFFER_DEVICE_ADDRESS_NAME);
478                    device_extensions.push(vk::KHR_SHADER_FLOAT_CONTROLS_NAME);
479                    device_extensions.push(vk::KHR_SPIRV_1_4_NAME);
480                }
481                device_extensions.push(vk::KHR_DEFERRED_HOST_OPERATIONS_NAME);
482                device_extensions.push(vk::KHR_ACCELERATION_STRUCTURE_NAME);
483                device_extensions.push(vk::KHR_RAY_QUERY_NAME);
484            }
485            if capabilities.buffer_marker {
486                device_extensions.push(vk::AMD_BUFFER_MARKER_NAME);
487            }
488            if capabilities.shader_info {
489                device_extensions.push(vk::AMD_SHADER_INFO_NAME);
490            }
491            if capabilities.full_screen_exclusive {
492                device_extensions.push(vk::EXT_FULL_SCREEN_EXCLUSIVE_NAME);
493            }
494            if capabilities.external_memory {
495                device_extensions.push(vk::KHR_EXTERNAL_MEMORY_NAME);
496                device_extensions.push(if cfg!(target_os = "windows") {
497                    vk::KHR_EXTERNAL_MEMORY_WIN32_NAME
498                } else {
499                    vk::KHR_EXTERNAL_MEMORY_FD_NAME
500                });
501            }
502
503            let str_pointers = device_extensions
504                .iter()
505                .map(|&s| s.as_ptr())
506                .collect::<Vec<_>>();
507
508            let mut ext_inline_uniform_block = vk::PhysicalDeviceInlineUniformBlockFeaturesEXT {
509                inline_uniform_block: vk::TRUE,
510                ..Default::default()
511            };
512            let mut khr_timeline_semaphore = vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR {
513                timeline_semaphore: vk::TRUE,
514                ..Default::default()
515            };
516            let mut khr_dynamic_rendering = vk::PhysicalDeviceDynamicRenderingFeaturesKHR {
517                dynamic_rendering: vk::TRUE,
518                ..Default::default()
519            };
520            let mut device_create_info = vk::DeviceCreateInfo::default()
521                .queue_create_infos(&family_infos)
522                .enabled_extension_names(&str_pointers)
523                .push_next(&mut ext_inline_uniform_block)
524                .push_next(&mut khr_timeline_semaphore)
525                .push_next(&mut khr_dynamic_rendering);
526
527            let mut ext_descriptor_indexing;
528            let mut khr_buffer_device_address;
529            let mut khr_acceleration_structure;
530            let mut khr_ray_query;
531            if capabilities.ray_tracing.is_some() {
532                ext_descriptor_indexing = vk::PhysicalDeviceDescriptorIndexingFeaturesEXT {
533                    shader_storage_buffer_array_non_uniform_indexing: vk::TRUE,
534                    shader_sampled_image_array_non_uniform_indexing: vk::TRUE,
535                    descriptor_binding_partially_bound: vk::TRUE,
536                    ..Default::default()
537                };
538                khr_buffer_device_address = vk::PhysicalDeviceBufferDeviceAddressFeaturesKHR {
539                    buffer_device_address: vk::TRUE,
540                    ..Default::default()
541                };
542                khr_acceleration_structure = vk::PhysicalDeviceAccelerationStructureFeaturesKHR {
543                    acceleration_structure: vk::TRUE,
544                    ..Default::default()
545                };
546                khr_ray_query = vk::PhysicalDeviceRayQueryFeaturesKHR {
547                    ray_query: vk::TRUE,
548                    ..Default::default()
549                };
550                device_create_info = device_create_info
551                    .push_next(&mut ext_descriptor_indexing)
552                    .push_next(&mut khr_buffer_device_address)
553                    .push_next(&mut khr_acceleration_structure)
554                    .push_next(&mut khr_ray_query);
555            }
556
557            instance
558                .core
559                .create_device(physical_device, &device_create_info, None)
560                .map_err(super::PlatformError::Init)?
561        };
562
563        let device = super::Device {
564            swapchain: if desc.presentation {
565                Some(khr::swapchain::Device::new(&instance.core, &device_core))
566            } else {
567                None
568            },
569            debug_utils: ext::debug_utils::Device::new(&instance.core, &device_core),
570            timeline_semaphore: khr::timeline_semaphore::Device::new(&instance.core, &device_core),
571            dynamic_rendering: khr::dynamic_rendering::Device::new(&instance.core, &device_core),
572            ray_tracing: if let Some(ref caps) = capabilities.ray_tracing {
573                Some(super::RayTracingDevice {
574                    acceleration_structure: khr::acceleration_structure::Device::new(
575                        &instance.core,
576                        &device_core,
577                    ),
578                    scratch_buffer_alignment: caps.min_scratch_buffer_alignment,
579                })
580            } else {
581                None
582            },
583            buffer_marker: if capabilities.buffer_marker && desc.validation {
584                Some(amd::buffer_marker::Device::new(
585                    &instance.core,
586                    &device_core,
587                ))
588            } else {
589                None
590            },
591            shader_info: if capabilities.shader_info {
592                Some(amd::shader_info::Device::new(&instance.core, &device_core))
593            } else {
594                None
595            },
596            full_screen_exclusive: if desc.presentation && capabilities.full_screen_exclusive {
597                Some(ext::full_screen_exclusive::Device::new(
598                    &instance.core,
599                    &device_core,
600                ))
601            } else {
602                None
603            },
604            external_memory: if capabilities.external_memory {
605                #[cfg(not(target_os = "windows"))]
606                use khr::external_memory_fd::Device;
607                #[cfg(target_os = "windows")]
608                use khr::external_memory_win32::Device;
609
610                Some(Device::new(&instance.core, &device_core))
611            } else {
612                None
613            },
614            core: device_core,
615            device_information: capabilities.device_information,
616            command_scope: if desc.capture {
617                Some(super::CommandScopeDevice {})
618            } else {
619                None
620            },
621            timing: if desc.timing && capabilities.timing {
622                Some(super::TimingDevice {
623                    period: capabilities.properties.limits.timestamp_period,
624                })
625            } else {
626                None
627            },
628            //TODO: detect GPU family
629            workarounds: super::Workarounds {
630                extra_sync_src_access: vk::AccessFlags::TRANSFER_WRITE,
631                extra_sync_dst_access: vk::AccessFlags::TRANSFER_WRITE
632                    | vk::AccessFlags::TRANSFER_READ
633                    | if capabilities.ray_tracing.is_some() {
634                        vk::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR
635                    } else {
636                        vk::AccessFlags::NONE
637                    },
638                extra_descriptor_pool_create_flags: if capabilities
639                    .bugs
640                    .intel_fix_descriptor_pool_leak
641                {
642                    vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET
643                } else {
644                    vk::DescriptorPoolCreateFlags::empty()
645                },
646            },
647        };
648
649        let memory_manager = {
650            let mem_properties = instance
651                .core
652                .get_physical_device_memory_properties(physical_device);
653            let memory_types =
654                &mem_properties.memory_types[..mem_properties.memory_type_count as usize];
655            let limits = &capabilities.properties.limits;
656            let config = gpu_alloc::Config::i_am_prototyping(); //TODO?
657
658            let properties = gpu_alloc::DeviceProperties {
659                max_memory_allocation_count: limits.max_memory_allocation_count,
660                max_memory_allocation_size: u64::max_value(), // TODO
661                non_coherent_atom_size: limits.non_coherent_atom_size,
662                memory_types: memory_types
663                    .iter()
664                    .map(|memory_type| gpu_alloc::MemoryType {
665                        props: gpu_alloc::MemoryPropertyFlags::from_bits_truncate(
666                            memory_type.property_flags.as_raw() as u8,
667                        ),
668                        heap: memory_type.heap_index,
669                    })
670                    .collect(),
671                memory_heaps: mem_properties.memory_heaps
672                    [..mem_properties.memory_heap_count as usize]
673                    .iter()
674                    .map(|&memory_heap| gpu_alloc::MemoryHeap {
675                        size: memory_heap.size,
676                    })
677                    .collect(),
678                buffer_device_address: capabilities.ray_tracing.is_some(),
679            };
680
681            let known_memory_flags = vk::MemoryPropertyFlags::DEVICE_LOCAL
682                | vk::MemoryPropertyFlags::HOST_VISIBLE
683                | vk::MemoryPropertyFlags::HOST_COHERENT
684                | vk::MemoryPropertyFlags::HOST_CACHED
685                | vk::MemoryPropertyFlags::LAZILY_ALLOCATED;
686            let valid_ash_memory_types = memory_types.iter().enumerate().fold(0, |u, (i, mem)| {
687                if !known_memory_flags.contains(mem.property_flags) {
688                    log::debug!(
689                        "Skipping memory type={} for having unknown flags: {:?}",
690                        i,
691                        mem.property_flags & !known_memory_flags
692                    );
693                    u
694                } else if mem
695                    .property_flags
696                    .contains(vk::MemoryPropertyFlags::HOST_VISIBLE)
697                    && !mem
698                        .property_flags
699                        .contains(vk::MemoryPropertyFlags::HOST_COHERENT)
700                {
701                    //TODO: see if and how we can support this
702                    log::debug!("Skipping memory type={} for lack of host coherency", i);
703                    u
704                } else {
705                    u | (1 << i)
706                }
707            });
708            super::MemoryManager {
709                allocator: gpu_alloc::GpuAllocator::new(config, properties),
710                slab: slab::Slab::new(),
711                valid_ash_memory_types,
712            }
713        };
714
715        let queue = device
716            .core
717            .get_device_queue(capabilities.queue_family_index, 0);
718        let last_progress = 0;
719        let mut timeline_info = vk::SemaphoreTypeCreateInfo {
720            semaphore_type: vk::SemaphoreType::TIMELINE,
721            initial_value: last_progress,
722            ..Default::default()
723        };
724        let timeline_semaphore_create_info =
725            vk::SemaphoreCreateInfo::default().push_next(&mut timeline_info);
726        let timeline_semaphore = device
727            .core
728            .create_semaphore(&timeline_semaphore_create_info, None)
729            .unwrap();
730
731        let mut naga_flags = spv::WriterFlags::FORCE_POINT_SIZE;
732        let shader_debug_path = if desc.validation || desc.capture {
733            use std::{env, fs};
734            naga_flags |= spv::WriterFlags::DEBUG;
735            let dir = env::temp_dir().join("blade");
736            let _ = fs::create_dir(&dir);
737            Some(dir)
738        } else {
739            None
740        };
741
742        Ok(super::Context {
743            memory: Mutex::new(memory_manager),
744            device,
745            queue_family_index: capabilities.queue_family_index,
746            queue: Mutex::new(super::Queue {
747                raw: queue,
748                timeline_semaphore,
749                last_progress,
750            }),
751            physical_device,
752            naga_flags,
753            shader_debug_path,
754            min_buffer_alignment,
755            sample_count_flags: capabilities
756                .properties
757                .limits
758                .framebuffer_color_sample_counts
759                & capabilities
760                    .properties
761                    .limits
762                    .framebuffer_depth_sample_counts,
763            instance,
764            entry,
765        })
766    }
767
768    pub(super) fn set_object_name<T: vk::Handle>(&self, object: T, name: &str) {
769        let name_cstr = ffi::CString::new(name).unwrap();
770        let name_info = vk::DebugUtilsObjectNameInfoEXT::default()
771            .object_handle(object)
772            .object_name(&name_cstr);
773        let _ = unsafe {
774            self.device
775                .debug_utils
776                .set_debug_utils_object_name(&name_info)
777        };
778    }
779
780    pub fn capabilities(&self) -> crate::Capabilities {
781        crate::Capabilities {
782            ray_query: match self.device.ray_tracing {
783                Some(_) => crate::ShaderVisibility::all(),
784                None => crate::ShaderVisibility::empty(),
785            },
786            sample_count_mask: self.sample_count_flags.as_raw(),
787        }
788    }
789
790    pub fn device_information(&self) -> &crate::DeviceInformation {
791        &self.device.device_information
792    }
793}
794
795impl Drop for super::Context {
796    fn drop(&mut self) {
797        if std::thread::panicking() {
798            return;
799        }
800        unsafe {
801            if let Ok(queue) = self.queue.lock() {
802                let _ = self.device.core.queue_wait_idle(queue.raw);
803                self.device
804                    .core
805                    .destroy_semaphore(queue.timeline_semaphore, None);
806            }
807            self.device.core.destroy_device(None);
808            self.instance.core.destroy_instance(None);
809        }
810    }
811}