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