1use alloc::string::String;
32use alloc::sync::Arc;
33use alloc::vec;
34use alloc::vec::Vec;
35use core::ffi::c_void;
36use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
37
38use crate::vk_ffi as vk;
39use crate::vk_load::Lib;
40use crate::{AdapterInfo, AdapterKind, Features, GpuError, InstanceDesc, Level, Limits, Result};
41
42pub(crate) fn err(r: vk::VkResult) -> GpuError {
44 match r {
45 vk::ERROR_OUT_OF_HOST_MEMORY | vk::ERROR_OUT_OF_DEVICE_MEMORY | vk::ERROR_OUT_OF_POOL_MEMORY => {
46 GpuError::OutOfMemory
47 }
48 vk::ERROR_DEVICE_LOST => GpuError::DeviceLost,
49 vk::ERROR_SURFACE_LOST_KHR => GpuError::SurfaceLost,
50 vk::TIMEOUT => GpuError::Timeout,
51 vk::NOT_READY => GpuError::NotReady,
52 vk::ERROR_FEATURE_NOT_PRESENT => GpuError::Unsupported("a device feature"),
53 vk::ERROR_EXTENSION_NOT_PRESENT => GpuError::Unsupported("an extension"),
54 vk::ERROR_FORMAT_NOT_SUPPORTED => GpuError::Unsupported("the format"),
55 vk::ERROR_INCOMPATIBLE_DRIVER => GpuError::NoAdapter,
56 _ => GpuError::Failed(r),
57 }
58}
59
60pub(crate) fn env_on(name: &str) -> bool {
61 plat::env(name).is_some_and(|v| v == "1" || v.eq_ignore_ascii_case("on") || v.eq_ignore_ascii_case("true"))
62}
63
64static VAL_ERRORS: AtomicU32 = AtomicU32::new(0);
65static VAL_WARNINGS: AtomicU32 = AtomicU32::new(0);
66static NO_LAYER_NOTE: AtomicBool = AtomicBool::new(false);
67
68pub fn validation_messages() -> (u32, u32) {
71 (VAL_ERRORS.load(Ordering::Relaxed), VAL_WARNINGS.load(Ordering::Relaxed))
72}
73
74unsafe extern "system" fn debug_message(sev: u32, types: u32, data: *const vk::DebugUtilsMessengerCallbackDataEXT,
75 _user: *mut c_void) -> vk::Bool32 {
76 let what = if sev & vk::DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT != 0 {
77 if types & vk::DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT != 0 {
78 VAL_ERRORS.fetch_add(1, Ordering::Relaxed);
79 }
80 "error"
81 } else if sev & vk::DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT != 0 {
82 VAL_WARNINGS.fetch_add(1, Ordering::Relaxed);
83 "warning"
84 } else {
85 "info"
86 };
87 let kind = if types & vk::DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT != 0 {
88 "validation"
89 } else if types & vk::DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT != 0 {
90 "performance"
91 } else {
92 "driver"
93 };
94 let mut s = String::with_capacity(256);
95 s.push_str("gpu: ");
96 s.push_str(kind);
97 s.push(' ');
98 s.push_str(what);
99 s.push_str(": ");
100 unsafe {
102 if !data.is_null() && !(*data).p_message.is_null() {
103 let p = (*data).p_message;
104 let mut n = 0;
105 while *p.add(n) != 0 {
106 n += 1;
107 }
108 s.push_str(core::str::from_utf8(core::slice::from_raw_parts(p, n)).unwrap_or("?"));
109 }
110 }
111 s.push('\n');
112 plat::write_stderr(s.as_bytes());
113 0
114}
115
116pub(crate) struct InstShared {
117 pub inst: vk::Instance,
118 pub f: vk::InstanceFns,
119 pub surface: bool,
121 pub validate: bool,
122 messenger: vk::Handle,
123 _lib: Lib, }
125
126impl Drop for InstShared {
127 fn drop(&mut self) {
128 unsafe {
130 if self.messenger != 0 {
131 if let Some(d) = self.f.destroy_debug_utils_messenger_ext {
132 d(self.inst, self.messenger, core::ptr::null());
133 }
134 }
135 (self.f.destroy_instance)(self.inst, core::ptr::null());
136 }
137 }
138}
139
140#[derive(Clone)]
142pub struct Instance {
143 pub(crate) sh: Arc<InstShared>,
144}
145
146fn instance_extensions(lf: &vk::LoaderFns, layer: *const u8) -> Vec<vk::ExtensionProperties> {
148 let mut n = 0u32;
149 unsafe {
151 if (lf.enumerate_instance_extension_properties)(layer, &mut n, core::ptr::null_mut()) != vk::SUCCESS {
152 return Vec::new();
153 }
154 let mut v = vec![vk::Zero::zero(); n as usize];
155 let r = (lf.enumerate_instance_extension_properties)(layer, &mut n, v.as_mut_ptr());
156 if r != vk::SUCCESS && r != vk::INCOMPLETE {
157 return Vec::new();
158 }
159 v.truncate(n as usize);
160 v
161 }
162}
163
164fn has_ext(list: &[vk::ExtensionProperties], name: &[u8]) -> bool {
165 let name = &name[..name.len() - 1];
166 list.iter().any(|e| vk::cstr(&e.extension_name).as_bytes() == name)
167}
168
169const LAYER_VALIDATION: &[u8] = b"VK_LAYER_KHRONOS_validation\0";
170
171impl Instance {
172 pub fn new() -> Result<Instance> {
174 Instance::with(&InstanceDesc::DEFAULT)
175 }
176
177 pub fn with(desc: &InstanceDesc) -> Result<Instance> {
178 if plat::env("jfgui_GPU").is_some_and(|v| v == "0" || v.eq_ignore_ascii_case("off")) {
179 return Err(GpuError::Disabled);
180 }
181 let dll = plat::env("jfgui_GPU_DLL").filter(|s| !s.is_empty());
182 let lib = Lib::open(dll.as_deref().unwrap_or(crate::vk_load::LOADER))?;
183 let lf = vk::LoaderFns::load(&|n| lib.gipa(0, n)).map_err(|_| GpuError::NoLoader)?;
184 let mut ver = vk::make_api_version(1, 0, 0);
185 if let Some(e) = lf.enumerate_instance_version {
186 unsafe { e(&mut ver) };
188 }
189 if ver < vk::API_VERSION_1_1 {
190 return Err(GpuError::Unsupported("a Vulkan 1.1 loader"));
191 }
192 let exts = instance_extensions(&lf, core::ptr::null());
193 let mut want = desc.validate || env_on("jfgui_GPU_VALIDATE");
194 let debug = env_on("jfgui_GPU_DEBUG");
195 if want && !layer_present(&lf) {
196 if !NO_LAYER_NOTE.swap(true, Ordering::Relaxed) {
197 plat::write_stderr(b"gpu: jfgui_GPU_VALIDATE=1 but VK_LAYER_KHRONOS_validation is not installed: no validation\n");
198 }
199 want = false;
200 }
201 let layer_exts = if want { instance_extensions(&lf, LAYER_VALIDATION.as_ptr()) } else { Vec::new() };
202 let mut names: Vec<*const u8> = Vec::new();
203 let surface_ext: Option<&[u8]> = if cfg!(windows) {
205 Some(b"VK_KHR_win32_surface\0")
206 } else if cfg!(target_os = "android") {
207 Some(b"VK_KHR_android_surface\0")
208 } else {
209 None
210 };
211 let surface = surface_ext.is_some_and(|e| has_ext(&exts, b"VK_KHR_surface\0") && has_ext(&exts, e));
212 if surface {
213 names.push(b"VK_KHR_surface\0".as_ptr());
214 names.extend(surface_ext.map(<[u8]>::as_ptr));
215 }
216 let du = (want || debug) && (has_ext(&exts, b"VK_EXT_debug_utils\0") || has_ext(&layer_exts, b"VK_EXT_debug_utils\0"));
217 if du {
218 names.push(b"VK_EXT_debug_utils\0".as_ptr());
219 }
220 let settings_ext = want && has_ext(&layer_exts, b"VK_EXT_layer_settings\0");
221 let features_ext = want && !settings_ext && has_ext(&layer_exts, b"VK_EXT_validation_features\0");
222 if settings_ext {
223 names.push(b"VK_EXT_layer_settings\0".as_ptr());
224 }
225 if features_ext {
226 names.push(b"VK_EXT_validation_features\0".as_ptr());
227 }
228 let on: vk::Bool32 = 1;
230 let setting = vk::LayerSettingEXT {
231 p_layer_name: LAYER_VALIDATION.as_ptr(),
232 p_setting_name: b"validate_sync\0".as_ptr(),
233 type_: vk::LAYER_SETTING_TYPE_BOOL32_EXT,
234 value_count: 1,
235 p_values: &on as *const u32 as *const c_void,
236 };
237 let settings = vk::LayerSettingsCreateInfoEXT {
238 s_type: vk::STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT,
239 setting_count: 1,
240 p_settings: &setting,
241 ..vk::Zero::zero()
242 };
243 let sync = vk::VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT;
244 let vfeatures = vk::ValidationFeaturesEXT {
245 s_type: vk::STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
246 enabled_validation_feature_count: 1,
247 p_enabled_validation_features: &sync,
248 ..vk::Zero::zero()
249 };
250 let mut app_name: Vec<u8> = desc.app_name.bytes().filter(|&b| b != 0).collect();
251 app_name.push(0);
252 let app = vk::ApplicationInfo {
253 s_type: vk::STRUCTURE_TYPE_APPLICATION_INFO,
254 p_application_name: app_name.as_ptr(),
255 p_engine_name: b"JFLib gpu\0".as_ptr(),
256 engine_version: vk::make_api_version(0, rt::VERSION_MAJOR as u32, rt::VERSION_MINOR as u32),
257 api_version: vk::API_VERSION_1_3,
258 ..vk::Zero::zero()
259 };
260 let layers = [LAYER_VALIDATION.as_ptr()];
261 let info = vk::InstanceCreateInfo {
262 s_type: vk::STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
263 p_next: if settings_ext {
264 &settings as *const _ as *const c_void
265 } else if features_ext {
266 &vfeatures as *const _ as *const c_void
267 } else {
268 core::ptr::null()
269 },
270 p_application_info: &app,
271 enabled_layer_count: want as u32,
272 pp_enabled_layer_names: layers.as_ptr(),
273 enabled_extension_count: names.len() as u32,
274 pp_enabled_extension_names: names.as_ptr(),
275 ..vk::Zero::zero()
276 };
277 let mut inst: vk::Instance = 0;
278 let r = unsafe { (lf.create_instance)(&info, core::ptr::null(), &mut inst) };
280 if r != vk::SUCCESS {
281 return Err(err(r));
282 }
283 let f = match vk::InstanceFns::load(&|n| lib.gipa(inst, n)) {
284 Ok(f) => f,
285 Err(_) => {
286 let d = lib.gipa(inst, b"vkDestroyInstance\0");
287 if d != 0 {
288 unsafe {
290 core::mem::transmute::<usize, unsafe extern "system" fn(vk::Instance, *const c_void)>(d)(
291 inst,
292 core::ptr::null(),
293 )
294 };
295 }
296 return Err(GpuError::Unsupported("a Vulkan 1.1 instance"));
297 }
298 };
299 let mut messenger = 0;
300 if let (true, Some(create)) = (du, f.create_debug_utils_messenger_ext) {
301 let mut sev = vk::DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | vk::DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
302 if debug {
303 sev |= vk::DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
304 }
305 let mi = vk::DebugUtilsMessengerCreateInfoEXT {
306 s_type: vk::STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
307 message_severity: sev,
308 message_type: vk::DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
309 | vk::DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
310 | vk::DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
311 pfn_user_callback: Some(debug_message),
312 ..vk::Zero::zero()
313 };
314 if unsafe { create(inst, &mi, core::ptr::null(), &mut messenger) } != vk::SUCCESS {
316 messenger = 0;
317 }
318 }
319 Ok(Instance { sh: Arc::new(InstShared { inst, f, surface, validate: want, messenger, _lib: lib }) })
320 }
321
322 pub fn validating(&self) -> bool {
324 self.sh.validate
325 }
326
327 pub fn adapters(&self) -> Result<Vec<Adapter>> {
329 let sh = &self.sh;
330 let mut n = 0u32;
331 let r = unsafe { (sh.f.enumerate_physical_devices)(sh.inst, &mut n, core::ptr::null_mut()) };
333 if r != vk::SUCCESS {
334 return Err(err(r));
335 }
336 let mut pds = vec![0usize; n as usize];
337 let r = unsafe { (sh.f.enumerate_physical_devices)(sh.inst, &mut n, pds.as_mut_ptr()) };
339 if r != vk::SUCCESS && r != vk::INCOMPLETE {
340 return Err(err(r));
341 }
342 pds.truncate(n as usize);
343 let rank = |k: AdapterKind| match k {
344 AdapterKind::Discrete => 0,
345 AdapterKind::Integrated => 1,
346 AdapterKind::Virtual => 2,
347 AdapterKind::Cpu => 3,
348 AdapterKind::Other => 4,
349 };
350 Ok(by_rank(pds.into_iter().map(|pd| probe(sh, pd)).map(|a| (rank(a.info.kind), a)).collect()))
351 }
352
353 pub fn prepare(&self) -> Result<bool> {
355 Ok(true)
356 }
357
358 pub fn adapter(&self) -> Result<Adapter> {
360 let list = self.adapters()?;
361 if let Some(sel) = plat::env("jfgui_GPU_ADAPTER").filter(|s| !s.is_empty()) {
362 let pick = match sel.parse::<usize>() {
363 Ok(i) => list.into_iter().nth(i),
364 Err(_) => {
365 let want = sel.to_ascii_lowercase();
366 list.into_iter().find(|a| a.info.name.to_ascii_lowercase().contains(&want))
367 }
368 };
369 let a = pick.ok_or(GpuError::NoAdapter)?;
370 return match a.info.missing {
371 Some(why) => Err(GpuError::Unsupported(why)),
372 None => Ok(a),
373 };
374 }
375 list.into_iter().find(|a| a.info.missing.is_none()).ok_or(GpuError::NoAdapter)
376 }
377
378 pub fn adapters_low_power(&self, display: Option<(u32, u32)>) -> Result<Vec<Adapter>> {
384 if plat::env("jfgui_GPU_ADAPTER").is_some_and(|s| !s.is_empty()) {
385 return self.adapter().map(|a| alloc::vec![a]);
386 }
387 let list: Vec<(u32, Adapter)> = self
388 .adapters()?
389 .into_iter()
390 .filter(|a| a.info.missing.is_none())
391 .filter_map(|a| {
392 let drives = display.is_some_and(|(v, d)| a.info.vendor_id == v && a.info.device_id == d);
393 low_power_rank(a.info.kind, drives).map(|r| (r, a))
394 })
395 .collect();
396 if list.is_empty() {
397 return Err(GpuError::NoAdapter);
398 }
399 Ok(by_rank(list))
400 }
401}
402
403fn by_rank(mut list: Vec<(u32, Adapter)>) -> Vec<Adapter> {
406 let mut out = Vec::with_capacity(list.len());
407 while !list.is_empty() {
408 let mut best = 0;
409 for i in 1..list.len() {
410 if list[i].0 < list[best].0 {
411 best = i;
412 }
413 }
414 out.push(list.remove(best).1);
415 }
416 out
417}
418
419pub fn low_power_rank(kind: AdapterKind, drives_display: bool) -> Option<u32> {
423 let k = match kind {
424 AdapterKind::Integrated => 0,
425 AdapterKind::Discrete => 1,
426 _ => return None,
427 };
428 Some(if drives_display { k } else { 2 + k })
429}
430
431fn layer_present(lf: &vk::LoaderFns) -> bool {
432 let mut n = 0u32;
433 unsafe {
435 if (lf.enumerate_instance_layer_properties)(&mut n, core::ptr::null_mut()) != vk::SUCCESS {
436 return false;
437 }
438 let mut v: Vec<vk::LayerProperties> = vec![vk::Zero::zero(); n as usize];
439 let r = (lf.enumerate_instance_layer_properties)(&mut n, v.as_mut_ptr());
440 if r != vk::SUCCESS && r != vk::INCOMPLETE {
441 return false;
442 }
443 v.truncate(n as usize);
444 let name = &LAYER_VALIDATION[..LAYER_VALIDATION.len() - 1];
445 v.iter().any(|l| vk::cstr(&l.layer_name).as_bytes() == name)
446 }
447}
448
449#[derive(Clone, Copy, Default)]
451pub(crate) struct Exts {
452 pub swapchain: bool,
453 pub timeline_khr: bool,
454 pub dynamic_rendering_khr: bool,
455 pub depth_stencil_resolve_khr: bool,
456 pub create_renderpass2_khr: bool,
457 pub driver_properties_khr: bool,
458 pub dmabuf: bool,
460 pub queue_foreign: bool,
461 #[cfg_attr(not(jfgui_glibc), allow(dead_code))]
462 pub physical_drm: bool,
463}
464
465#[derive(Clone)]
467pub struct Adapter {
468 pub(crate) inst: Arc<InstShared>,
469 pub(crate) pd: vk::PhysicalDevice,
470 pub(crate) info: AdapterInfo,
471 pub(crate) features: Features,
472 pub(crate) limits: Limits,
473 pub(crate) voll: bool,
474 pub(crate) api: u32,
475 pub(crate) family: u32,
476 pub(crate) ts_bits: u32,
477 pub(crate) exts: Exts,
478 pub(crate) f10: [u32; vk::F10_COUNT],
479 pub(crate) f11: [u32; vk::F11_COUNT],
480 pub(crate) f12: [u32; vk::F12_COUNT],
481 pub(crate) f13: [u32; vk::F13_COUNT],
482 pub(crate) mem: vk::PhysicalDeviceMemoryProperties,
483 pub(crate) raw: vk::PhysicalDeviceLimits,
484}
485
486impl Adapter {
487 pub fn info(&self) -> &AdapterInfo {
488 &self.info
489 }
490 pub fn features(&self) -> &Features {
492 &self.features
493 }
494 pub fn limits(&self) -> &Limits {
495 &self.limits
496 }
497 pub fn level(&self) -> Level {
499 if self.voll { Level::Voll } else { Level::Basis }
500 }
501 #[cfg(jfgui_glibc)]
503 pub fn can_present(&self, surface: &crate::Surface) -> bool {
504 crate::vk_swap::can_present(self, surface)
505 }
506
507 #[cfg(not(jfgui_glibc))]
509 pub fn can_present(&self, surface: &crate::Surface) -> bool {
510 let Some(support) = self.inst.f.get_physical_device_surface_support_khr else { return false };
511 if !self.exts.swapchain {
512 return false;
513 }
514 let mut ok = 0;
515 let r = unsafe { support(self.pd, self.family, surface.0.h, &mut ok) };
517 r == vk::SUCCESS && ok != 0
518 }
519}
520
521fn vendor_name(id: u32) -> &'static str {
522 match id {
523 0x10DE => "NVIDIA",
524 0x1002 | 0x1022 => "AMD",
525 0x8086 => "Intel",
526 0x13B5 => "ARM",
527 0x5143 => "Qualcomm",
528 0x1010 => "Imagination",
529 0x106B => "Apple",
530 0x1414 => "Microsoft",
531 0x10005 => "Mesa",
532 _ => "unknown",
533 }
534}
535
536fn driver_version(vendor: u32, v: u32) -> String {
537 match vendor {
538 0x10DE => alloc::format!("{}.{:02}", v >> 22, (v >> 14) & 0xFF),
539 0x8086 if cfg!(windows) => alloc::format!("{}.{}", v >> 14, v & 0x3FFF),
540 _ => alloc::format!("{}.{}.{}", vk::api_major(v), vk::api_minor(v), vk::api_patch(v)),
541 }
542}
543
544fn device_extensions(sh: &InstShared, pd: vk::PhysicalDevice) -> Vec<vk::ExtensionProperties> {
545 let mut n = 0u32;
546 unsafe {
548 if (sh.f.enumerate_device_extension_properties)(pd, core::ptr::null(), &mut n, core::ptr::null_mut()) != vk::SUCCESS {
549 return Vec::new();
550 }
551 let mut v = vec![vk::Zero::zero(); n as usize];
552 let r = (sh.f.enumerate_device_extension_properties)(pd, core::ptr::null(), &mut n, v.as_mut_ptr());
553 if r != vk::SUCCESS && r != vk::INCOMPLETE {
554 return Vec::new();
555 }
556 v.truncate(n as usize);
557 v
558 }
559}
560
561fn probe(sh: &Arc<InstShared>, pd: vk::PhysicalDevice) -> Adapter {
563 let f = &sh.f;
564 let dexts = device_extensions(sh, pd);
565 let ext = |n: &[u8]| has_ext(&dexts, n);
566 let mut exts = Exts {
567 swapchain: sh.surface && ext(b"VK_KHR_swapchain\0"),
568 timeline_khr: ext(b"VK_KHR_timeline_semaphore\0"),
569 dynamic_rendering_khr: ext(b"VK_KHR_dynamic_rendering\0"),
570 depth_stencil_resolve_khr: ext(b"VK_KHR_depth_stencil_resolve\0"),
571 create_renderpass2_khr: ext(b"VK_KHR_create_renderpass2\0"),
572 driver_properties_khr: ext(b"VK_KHR_driver_properties\0"),
573 dmabuf: cfg!(jfgui_glibc)
574 && ext(b"VK_KHR_external_memory_fd\0")
575 && ext(b"VK_EXT_external_memory_dma_buf\0")
576 && ext(b"VK_EXT_image_drm_format_modifier\0"),
577 queue_foreign: ext(b"VK_EXT_queue_family_foreign\0"),
578 physical_drm: ext(b"VK_EXT_physical_device_drm\0"),
579 };
580 let mut dip: vk::PhysicalDeviceDescriptorIndexingProperties = vk::Zero::zero();
582 dip.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES;
583 let mut sub: vk::PhysicalDeviceSubgroupProperties = vk::Zero::zero();
584 sub.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
585 let mut size_ctl: vk::PhysicalDeviceSubgroupSizeControlProperties = vk::Zero::zero();
586 size_ctl.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES;
587 let mut drv: vk::PhysicalDeviceDriverProperties = vk::Zero::zero();
588 drv.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
589 let mut p2: vk::PhysicalDeviceProperties2 = vk::Zero::zero();
590 p2.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
591 unsafe { (f.get_physical_device_properties2)(pd, &mut p2) };
594 let api = p2.properties.api_version;
595 let v12 = api >= vk::API_VERSION_1_2;
596 exts.dmabuf = exts.dmabuf && (v12 || ext(b"VK_KHR_image_format_list\0"));
598 let has_drv = v12 || exts.driver_properties_khr;
599 if api >= vk::API_VERSION_1_3 {
600 dip.p_next = &mut size_ctl as *mut _ as *mut c_void;
601 }
602 sub.p_next = if v12 { &mut dip as *mut _ as *mut c_void } else { core::ptr::null_mut() };
603 drv.p_next = &mut sub as *mut _ as *mut c_void;
604 p2.p_next = if has_drv { &mut drv as *mut _ as *mut c_void } else { &mut sub as *mut _ as *mut c_void };
605 unsafe { (f.get_physical_device_properties2)(pd, &mut p2) };
607 let props = p2.properties;
608 let lim = props.limits;
609
610 let mut f13: vk::PhysicalDeviceVulkan13Features = vk::Zero::zero();
612 f13.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
613 let mut f12: vk::PhysicalDeviceVulkan12Features = vk::Zero::zero();
614 f12.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
615 let mut f11: vk::PhysicalDeviceVulkan11Features = vk::Zero::zero();
616 f11.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
617 let mut dynr: vk::PhysicalDeviceOneFeature = vk::Zero::zero();
618 dynr.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES;
619 let mut tl: vk::PhysicalDeviceOneFeature = vk::Zero::zero();
620 tl.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES;
621 let mut f2: vk::PhysicalDeviceFeatures2 = vk::Zero::zero();
622 f2.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
623 let v13 = api >= vk::API_VERSION_1_3;
624 if v12 {
626 if v13 {
627 f12.p_next = &mut f13 as *mut _ as *mut c_void;
628 } else if exts.dynamic_rendering_khr {
629 f12.p_next = &mut dynr as *mut _ as *mut c_void;
630 }
631 f11.p_next = &mut f12 as *mut _ as *mut c_void;
632 f2.p_next = &mut f11 as *mut _ as *mut c_void;
633 } else {
634 let mut head: *mut c_void = core::ptr::null_mut();
635 if exts.dynamic_rendering_khr {
636 head = &mut dynr as *mut _ as *mut c_void;
637 }
638 if exts.timeline_khr {
639 tl.p_next = head;
640 head = &mut tl as *mut _ as *mut c_void;
641 }
642 f2.p_next = head;
643 }
644 unsafe { (f.get_physical_device_features2)(pd, &mut f2) };
646 if !v12 {
647 f12.f[vk::F12_TIMELINE_SEMAPHORE] = tl.f;
648 }
649 if !v13 {
650 f13.f[vk::F13_DYNAMIC_RENDERING] = dynr.f;
651 }
652 if v13 {
653 exts.dynamic_rendering_khr = false;
654 }
655 if v12 {
656 exts.timeline_khr = false;
657 exts.depth_stencil_resolve_khr = false;
658 exts.create_renderpass2_khr = false;
659 }
660
661 let mut nq = 0u32;
663 unsafe { (f.get_physical_device_queue_family_properties)(pd, &mut nq, core::ptr::null_mut()) };
665 let mut qf: Vec<vk::QueueFamilyProperties> = vec![vk::Zero::zero(); nq as usize];
666 unsafe { (f.get_physical_device_queue_family_properties)(pd, &mut nq, qf.as_mut_ptr()) };
668 qf.truncate(nq as usize);
669 let gc = vk::QUEUE_GRAPHICS_BIT | vk::QUEUE_COMPUTE_BIT;
670 let family = qf.iter().position(|q| q.queue_flags & gc == gc && q.queue_count > 0);
671 let ts_bits = family.map_or(0, |i| qf[i].timestamp_valid_bits);
672
673 let mut mem: vk::PhysicalDeviceMemoryProperties = vk::Zero::zero();
674 unsafe { (f.get_physical_device_memory_properties)(pd, &mut mem) };
676
677 let b10 = |i: usize| f2.f[i] != 0;
678 let b11 = |i: usize| v12 && f11.f[i] != 0;
679 let b12 = |i: usize| f12.f[i] != 0;
680 let dynamic_rendering = f13.f[vk::F13_DYNAMIC_RENDERING] != 0
682 && (v12 || exts.create_renderpass2_khr && exts.depth_stencil_resolve_khr);
683 let timeline = b12(vk::F12_TIMELINE_SEMAPHORE);
684 let missing = if api < vk::API_VERSION_1_1 {
685 Some("Vulkan 1.1")
686 } else if family.is_none() {
687 Some("a graphics + compute queue")
688 } else if !timeline {
689 Some("timeline semaphores")
690 } else if !dynamic_rendering {
691 Some("dynamic rendering")
692 } else {
693 None
694 };
695 let need_ops = vk::SUBGROUP_FEATURE_BASIC_BIT | vk::SUBGROUP_FEATURE_VOTE_BIT | vk::SUBGROUP_FEATURE_ARITHMETIC_BIT
696 | vk::SUBGROUP_FEATURE_BALLOT_BIT | vk::SUBGROUP_FEATURE_SHUFFLE_BIT;
697 let subgroups = sub.supported_stages & vk::SHADER_STAGE_COMPUTE_BIT != 0 && sub.supported_operations & need_ops == need_ops;
698 let webgpu_ops = need_ops | vk::SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT | vk::SUBGROUP_FEATURE_QUAD_BIT;
699 let webgpu_stages = vk::SHADER_STAGE_COMPUTE_BIT | vk::SHADER_STAGE_FRAGMENT_BIT;
700 let subgroups_webgpu = sub.supported_stages & webgpu_stages == webgpu_stages && sub.supported_operations & webgpu_ops == webgpu_ops;
701 let mut subgroup_stages = 0;
702 for (bit, stage) in [(vk::SHADER_STAGE_COMPUTE_BIT, crate::subgroup_stage::COMPUTE), (vk::SHADER_STAGE_FRAGMENT_BIT, crate::subgroup_stage::FRAGMENT), (vk::SHADER_STAGE_VERTEX_BIT, crate::subgroup_stage::VERTEX)] {
703 if sub.supported_stages & bit != 0 {
704 subgroup_stages |= stage;
705 }
706 }
707 let subgroup_size_control = v13 && f13.f[vk::F13_SUBGROUP_SIZE_CONTROL] != 0 && size_ctl.required_subgroup_size_stages & vk::SHADER_STAGE_COMPUTE_BIT != 0;
708 let bindless = v12
709 && [
710 vk::F12_DESCRIPTOR_INDEXING,
711 vk::F12_RUNTIME_DESCRIPTOR_ARRAY,
712 vk::F12_DESCRIPTOR_BINDING_PARTIALLY_BOUND,
713 vk::F12_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING,
714 vk::F12_DESCRIPTOR_BINDING_SAMPLED_IMAGE_UPDATE_AFTER_BIND,
715 vk::F12_DESCRIPTOR_BINDING_STORAGE_IMAGE_UPDATE_AFTER_BIND,
716 vk::F12_DESCRIPTOR_BINDING_STORAGE_BUFFER_UPDATE_AFTER_BIND,
717 vk::F12_SHADER_SAMPLED_IMAGE_ARRAY_NON_UNIFORM_INDEXING,
718 vk::F12_SHADER_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
719 ]
720 .iter()
721 .all(|&i| b12(i))
722 && b10(vk::F10_SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING)
723 && b10(vk::F10_SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING);
724 let features = Features {
725 timestamps: ts_bits > 0 && lim.timestamp_compute_and_graphics != 0,
726 multi_draw_indirect: b10(vk::F10_MULTI_DRAW_INDIRECT),
727 draw_indirect_first_instance: b10(vk::F10_DRAW_INDIRECT_FIRST_INSTANCE),
728 draw_indirect_count: b12(vk::F12_DRAW_INDIRECT_COUNT) && v12,
729 bindless,
730 subgroups,
731 subgroups_webgpu,
732 subgroup_stages,
733 subgroup_ops: sub.supported_operations,
734 subgroup_size_control,
735 subgroup_extended_types: v12 && b12(vk::F12_SHADER_SUBGROUP_EXTENDED_TYPES),
736 draw_parameters: b11(vk::F11_SHADER_DRAW_PARAMETERS),
737 shader_f16: v12 && b12(vk::F12_SHADER_FLOAT16),
738 shader_f64: b10(vk::F10_SHADER_FLOAT64),
739 shader_i16: b10(vk::F10_SHADER_INT16),
740 shader_i64: b10(vk::F10_SHADER_INT64),
741 shader_i8: v12 && b12(vk::F12_SHADER_INT8),
742 storage_16bit: b11(vk::F11_STORAGE_BUFFER_16BIT_ACCESS),
743 uniform_16bit: b11(vk::F11_UNIFORM_AND_STORAGE_BUFFER_16BIT_ACCESS),
744 push_constant_16bit: b11(vk::F11_STORAGE_PUSH_CONSTANT16),
745 uniform_standard_layout: v12 && b12(vk::F12_UNIFORM_BUFFER_STANDARD_LAYOUT),
746 demote: v13 && f13.f[vk::F13_SHADER_DEMOTE_TO_HELPER_INVOCATION] != 0,
747 clip_distances: b10(vk::F10_SHADER_CLIP_DISTANCE),
748 primitive_index: b10(vk::F10_GEOMETRY_SHADER),
749 sample_shading: b10(vk::F10_SAMPLE_RATE_SHADING),
750 storage_8bit: v12 && b12(vk::F12_STORAGE_BUFFER_8BIT_ACCESS),
751 texture_bc: b10(vk::F10_TEXTURE_COMPRESSION_BC),
752 anisotropy: b10(vk::F10_SAMPLER_ANISOTROPY),
753 depth_clamp: b10(vk::F10_DEPTH_CLAMP),
754 vertex_stores: b10(vk::F10_VERTEX_PIPELINE_STORES_AND_ATOMICS),
755 fragment_stores: b10(vk::F10_FRAGMENT_STORES_AND_ATOMICS),
756 storage_write_without_format: b10(vk::F10_SHADER_STORAGE_IMAGE_WRITE_WITHOUT_FORMAT),
757 buffer_address: v12 && b12(vk::F12_BUFFER_DEVICE_ADDRESS),
758 swapchain: exts.swapchain || (exts.dmabuf && props.device_type != vk::PHYSICAL_DEVICE_TYPE_CPU),
759 mesh_shader: ext(b"VK_EXT_mesh_shader\0"),
760 ray_query: ext(b"VK_KHR_ray_query\0") && ext(b"VK_KHR_acceleration_structure\0"),
761 ray_pipeline: ext(b"VK_KHR_ray_tracing_pipeline\0") && ext(b"VK_KHR_acceleration_structure\0"),
762 };
763 let voll = missing.is_none()
764 && v13
765 && bindless
766 && features.draw_indirect_count
767 && features.multi_draw_indirect
768 && features.draw_indirect_first_instance
769 && features.draw_parameters
770 && subgroups;
771 let uab = |set: usize, stage: usize| if bindless { dip.v[set].min(dip.v[stage]) } else { 0 };
772 let device_memory = (0..mem.memory_heap_count.min(16) as usize)
773 .filter(|&i| mem.memory_heaps[i].flags & 1 != 0)
774 .map(|i| mem.memory_heaps[i].size)
775 .sum();
776 let limits = Limits {
777 max_texture_2d: lim.max_image_dimension2_d,
778 max_texture_3d: lim.max_image_dimension3_d,
779 max_texture_layers: lim.max_image_array_layers,
780 max_storage_binding: lim.max_storage_buffer_range,
781 max_uniform_binding: lim.max_uniform_buffer_range,
782 max_push_constants: lim.max_push_constants_size,
783 max_bind_groups: lim.max_bound_descriptor_sets.min(crate::BIND_GROUPS_MAX),
784 max_compute_workgroup_size: lim.max_compute_work_group_size,
785 max_compute_invocations: lim.max_compute_work_group_invocations,
786 max_compute_workgroups: lim.max_compute_work_group_count,
787 max_compute_shared_memory: lim.max_compute_shared_memory_size,
788 min_uniform_offset_alignment: lim.min_uniform_buffer_offset_alignment as u32,
789 min_storage_offset_alignment: lim.min_storage_buffer_offset_alignment as u32,
790 max_color_targets: lim.max_color_attachments.min(crate::COLOR_TARGETS_MAX as u32),
791 max_vertex_buffers: lim.max_vertex_input_bindings,
792 max_vertex_attributes: lim.max_vertex_input_attributes,
793 max_draw_indirect_count: lim.max_draw_indirect_count,
794 max_anisotropy: lim.max_sampler_anisotropy,
795 timestamp_period_ns: lim.timestamp_period,
796 subgroup_size: sub.subgroup_size,
797 subgroup_min_size: if subgroup_size_control { size_ctl.min_subgroup_size } else { sub.subgroup_size },
798 subgroup_max_size: if subgroup_size_control { size_ctl.max_subgroup_size } else { sub.subgroup_size },
799 max_bindless_textures: uab(vk::DIP_MAX_SET_UAB_SAMPLED_IMAGES, vk::DIP_MAX_PER_STAGE_UAB_SAMPLED_IMAGES),
800 max_bindless_samplers: uab(vk::DIP_MAX_SET_UAB_SAMPLERS, vk::DIP_MAX_PER_STAGE_UAB_SAMPLERS),
801 max_bindless_buffers: uab(vk::DIP_MAX_SET_UAB_STORAGE_BUFFERS, vk::DIP_MAX_PER_STAGE_UAB_STORAGE_BUFFERS),
802 max_bindless_storage_textures: uab(vk::DIP_MAX_SET_UAB_STORAGE_IMAGES, vk::DIP_MAX_PER_STAGE_UAB_STORAGE_IMAGES),
803 device_memory,
804 };
805 let kind = match props.device_type {
806 vk::PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => AdapterKind::Discrete,
807 vk::PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU => AdapterKind::Integrated,
808 vk::PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU => AdapterKind::Virtual,
809 vk::PHYSICAL_DEVICE_TYPE_CPU => AdapterKind::Cpu,
810 _ => AdapterKind::Other,
811 };
812 let info = AdapterInfo {
813 name: String::from(vk::cstr(&props.device_name)),
814 vendor_id: props.vendor_id,
815 device_id: props.device_id,
816 vendor: vendor_name(props.vendor_id),
817 kind,
818 driver: if has_drv { String::from(vk::cstr(&drv.driver_name)) } else { String::new() },
819 driver_info: if has_drv { String::from(vk::cstr(&drv.driver_info)) } else { String::new() },
820 driver_version: driver_version(props.vendor_id, props.driver_version),
821 api_version: (vk::api_major(api), vk::api_minor(api), vk::api_patch(api)),
822 backend: "Vulkan",
823 missing,
824 };
825 Adapter {
826 inst: sh.clone(),
827 pd,
828 info,
829 features,
830 limits,
831 voll,
832 api,
833 family: family.unwrap_or(0) as u32,
834 ts_bits,
835 exts,
836 f10: f2.f,
837 f11: f11.f,
838 f12: f12.f,
839 f13: f13.f,
840 mem,
841 raw: lim,
842 }
843}