use std::ffi::{CStr, c_char};
use ash::{Entry, Instance, vk};
mod render;
mod target;
mod text;
#[allow(unused_imports)] pub use render::Renderer;
#[allow(unused_imports)]
pub use target::Target;
#[allow(unused_imports)]
pub use text::{Frame, Text, Thumb, THUMB_H, THUMB_SLOTS, THUMB_W};
pub(crate) use text::{slot_origin, Quad, THUMB_ATLAS_H, THUMB_ATLAS_W};
fn required() -> [&'static CStr; 4] {
[
ash::khr::external_memory_fd::NAME,
ash::ext::external_memory_dma_buf::NAME,
ash::ext::image_drm_format_modifier::NAME,
ash::khr::external_semaphore_fd::NAME,
]
}
#[allow(dead_code)] pub struct Gpu {
pub entry: Entry,
pub instance: Instance,
pub physical: vk::PhysicalDevice,
pub device: ash::Device,
pub queue_family: u32,
pub queue: vk::Queue,
pub name: String,
}
impl Gpu {
pub fn new() -> Result<Gpu, String> {
let entry = unsafe { Entry::load() }.map_err(|e| format!("load libvulkan: {e}"))?;
let app = vk::ApplicationInfo::default().api_version(vk::API_VERSION_1_3);
let ci = vk::InstanceCreateInfo::default().application_info(&app);
let instance =
unsafe { entry.create_instance(&ci, None) }.map_err(|e| format!("instance: {e}"))?;
let result = (|| {
let physicals = unsafe { instance.enumerate_physical_devices() }
.map_err(|e| format!("enumerate: {e}"))?;
let mut chosen = None;
for pd in physicals {
let props = unsafe { instance.get_physical_device_properties(pd) };
let name = cstr(&props.device_name);
let exts = unsafe { instance.enumerate_device_extension_properties(pd) }
.unwrap_or_default();
let have: Vec<&CStr> = exts
.iter()
.map(|e| unsafe { CStr::from_ptr(e.extension_name.as_ptr()) })
.collect();
let missing: Vec<&str> = required()
.iter()
.filter(|r| !have.contains(r))
.map(|r| r.to_str().unwrap_or("?"))
.collect();
let gfx = graphics_family(&instance, pd);
println!(
" gpu: {name} [{:?}] api {} gfxq={:?} import-exts={}",
props.device_type,
api_str(props.api_version),
gfx,
if missing.is_empty() {
"all".into()
} else {
format!("MISSING {missing:?}")
}
);
if chosen.is_none() && missing.is_empty() {
if let Some(q) = gfx {
chosen = Some((pd, q, name));
}
}
}
let (physical, queue_family, name) =
chosen.ok_or("no device supports the dmabuf import path")?;
let prio = [1.0f32];
let qci = [vk::DeviceQueueCreateInfo::default()
.queue_family_index(queue_family)
.queue_priorities(&prio)];
let ext_ptrs: Vec<*const c_char> = required().iter().map(|c| c.as_ptr()).collect();
let dci = vk::DeviceCreateInfo::default()
.queue_create_infos(&qci)
.enabled_extension_names(&ext_ptrs);
let device = unsafe { instance.create_device(physical, &dci, None) }
.map_err(|e| format!("create device: {e}"))?;
let queue = unsafe { device.get_device_queue(queue_family, 0) };
Ok(Gpu {
entry: entry.clone(),
instance: instance.clone(),
physical,
device,
queue_family,
queue,
name,
})
})();
if result.is_err() {
unsafe { instance.destroy_instance(None) };
}
result
}
}
impl Drop for Gpu {
fn drop(&mut self) {
unsafe {
self.device.destroy_device(None);
self.instance.destroy_instance(None);
}
}
}
fn graphics_family(instance: &Instance, pd: vk::PhysicalDevice) -> Option<u32> {
let families = unsafe { instance.get_physical_device_queue_family_properties(pd) };
families
.iter()
.position(|f| f.queue_flags.contains(vk::QueueFlags::GRAPHICS))
.map(|i| i as u32)
}
fn cstr(raw: &[c_char]) -> String {
unsafe { CStr::from_ptr(raw.as_ptr()) }
.to_string_lossy()
.into_owned()
}
fn api_str(v: u32) -> String {
format!(
"{}.{}.{}",
vk::api_version_major(v),
vk::api_version_minor(v),
vk::api_version_patch(v)
)
}