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