1
0
Fork 0
forked from wry/wry

render: implement a vulkan renderer

This commit is contained in:
Julian Orth 2024-02-03 15:19:20 +01:00
parent 4ba8550da8
commit cf332e8436
66 changed files with 4287 additions and 239 deletions

View file

@ -0,0 +1,343 @@
use {
crate::{
format::XRGB8888,
gfx_apis::vulkan::{
format::VulkanFormat,
instance::{
map_extension_properties, ApiVersionDisplay, Extensions, VulkanInstance,
API_VERSION,
},
util::OnDrop,
VulkanError,
},
video::{drm::Drm, gbm::GbmDevice},
},
ahash::AHashMap,
arrayvec::ArrayVec,
ash::{
extensions::khr::{ExternalFenceFd, ExternalMemoryFd, ExternalSemaphoreFd, PushDescriptor},
vk::{
DeviceCreateInfo, DeviceMemory, DeviceQueueCreateInfo, ExtExternalMemoryDmaBufFn,
ExtImageDrmFormatModifierFn, ExtPhysicalDeviceDrmFn, ExtQueueFamilyForeignFn,
ExternalSemaphoreFeatureFlags, ExternalSemaphoreHandleTypeFlags,
ExternalSemaphoreProperties, KhrDriverPropertiesFn, KhrExternalFenceFdFn,
KhrExternalMemoryFdFn, KhrExternalSemaphoreFdFn, KhrPushDescriptorFn,
MemoryPropertyFlags, MemoryType, PhysicalDevice, PhysicalDeviceDriverProperties,
PhysicalDeviceDriverPropertiesKHR, PhysicalDeviceDrmPropertiesEXT,
PhysicalDeviceDynamicRenderingFeatures, PhysicalDeviceExternalSemaphoreInfo,
PhysicalDeviceProperties, PhysicalDeviceProperties2,
PhysicalDeviceSynchronization2Features, PhysicalDeviceTimelineSemaphoreFeatures, Queue,
QueueFlags, MAX_MEMORY_TYPES,
},
Device,
},
isnt::std_1::collections::IsntHashMap2Ext,
std::{
ffi::{CStr, CString},
rc::Rc,
},
uapi::Ustr,
};
pub struct VulkanDevice {
pub(super) physical_device: PhysicalDevice,
pub(super) render_node: Rc<CString>,
pub(super) gbm: GbmDevice,
pub(super) instance: Rc<VulkanInstance>,
pub(super) device: Device,
pub(super) external_memory_fd: ExternalMemoryFd,
pub(super) external_semaphore_fd: ExternalSemaphoreFd,
pub(super) external_fence_fd: ExternalFenceFd,
pub(super) push_descriptor: PushDescriptor,
pub(super) formats: AHashMap<u32, VulkanFormat>,
pub(super) memory_types: ArrayVec<MemoryType, MAX_MEMORY_TYPES>,
pub(super) graphics_queue: Queue,
pub(super) graphics_queue_idx: u32,
}
impl Drop for VulkanDevice {
fn drop(&mut self) {
unsafe {
self.device.destroy_device(None);
}
}
}
impl VulkanDevice {
pub(super) fn find_memory_type(
&self,
flags: MemoryPropertyFlags,
memory_type_bits: u32,
) -> Option<u32> {
for (idx, ty) in self.memory_types.iter().enumerate() {
if memory_type_bits & (1 << idx as u32) != 0 {
if ty.property_flags.contains(flags) {
return Some(idx as _);
}
}
}
None
}
}
struct FreeMem<'a>(&'a Device, DeviceMemory);
impl<'a> Drop for FreeMem<'a> {
fn drop(&mut self) {
unsafe {
self.0.free_memory(self.1, None);
}
}
}
impl VulkanInstance {
fn get_device_extensions(&self, phy_dev: PhysicalDevice) -> Result<Extensions, VulkanError> {
unsafe {
self.instance
.enumerate_device_extension_properties(phy_dev)
.map(map_extension_properties)
.map_err(VulkanError::DeviceExtensions)
}
}
fn find_dev(&self, drm: &Drm) -> Result<PhysicalDevice, VulkanError> {
let stat = match uapi::fstat(drm.raw()) {
Ok(s) => s,
Err(e) => return Err(VulkanError::Fstat(e.into())),
};
let dev = stat.st_rdev;
log::info!(
"Searching for vulkan device with devnum {}:{}",
uapi::major(dev),
uapi::minor(dev)
);
let phy_devs = unsafe { self.instance.enumerate_physical_devices() };
let phy_devs = match phy_devs {
Ok(d) => d,
Err(e) => return Err(VulkanError::EnumeratePhysicalDevices(e)),
};
let mut devices = vec![];
for phy_dev in phy_devs {
let props = unsafe { self.instance.get_physical_device_properties(phy_dev) };
if props.api_version < API_VERSION {
devices.push((props, None, None));
continue;
}
let extensions = match self.get_device_extensions(phy_dev) {
Ok(e) => e,
Err(e) => {
log::error!(
"Could not enumerate extensions of device with id {}: {:#}",
props.device_id,
e
);
devices.push((props, None, None));
continue;
}
};
if !extensions.contains_key(ExtPhysicalDeviceDrmFn::name()) {
devices.push((props, Some(extensions), None));
continue;
}
let has_driver_props = extensions.contains_key(KhrDriverPropertiesFn::name());
let mut drm_props = PhysicalDeviceDrmPropertiesEXT::builder().build();
let mut driver_props = PhysicalDeviceDriverPropertiesKHR::builder().build();
let mut props2 = PhysicalDeviceProperties2::builder().push_next(&mut drm_props);
if has_driver_props {
props2 = props2.push_next(&mut driver_props);
}
unsafe {
self.instance
.get_physical_device_properties2(phy_dev, &mut props2);
}
let primary_dev =
uapi::makedev(drm_props.primary_major as _, drm_props.primary_minor as _);
let render_dev =
uapi::makedev(drm_props.render_major as _, drm_props.render_minor as _);
if primary_dev == dev || render_dev == dev {
log::info!("Device with id {} matches", props.device_id);
log_device(&props, Some(&extensions), Some(&driver_props));
return Ok(phy_dev);
}
devices.push((props, Some(extensions), Some(driver_props)));
}
if devices.is_empty() {
log::warn!("Found no devices");
} else {
log::warn!("Found the following devices but none matches:");
for (props, extensions, driver_props) in devices.iter() {
log::warn!("Found the following devices but none matches:");
log::warn!("-----");
log_device(props, extensions.as_ref(), driver_props.as_ref());
}
}
Err(VulkanError::NoDeviceFound(dev))
}
fn find_graphics_queue(&self, phy_dev: PhysicalDevice) -> Result<u32, VulkanError> {
let props = unsafe {
self.instance
.get_physical_device_queue_family_properties(phy_dev)
};
props
.iter()
.position(|p| p.queue_flags.contains(QueueFlags::GRAPHICS))
.map(|v| v as _)
.ok_or(VulkanError::NoGraphicsQueue)
}
fn supports_semaphore_import(&self, phy_dev: PhysicalDevice) -> bool {
let mut props = ExternalSemaphoreProperties::builder().build();
let info = PhysicalDeviceExternalSemaphoreInfo::builder()
.handle_type(ExternalSemaphoreHandleTypeFlags::OPAQUE_FD)
.build();
unsafe {
self.instance
.get_physical_device_external_semaphore_properties(phy_dev, &info, &mut props);
}
props
.external_semaphore_features
.contains(ExternalSemaphoreFeatureFlags::IMPORTABLE)
}
pub fn create_device(self: &Rc<Self>, drm: &Drm) -> Result<Rc<VulkanDevice>, VulkanError> {
let render_node = drm
.get_render_node()
.map_err(VulkanError::FetchRenderNode)?
.ok_or(VulkanError::NoRenderNode)
.map(Rc::new)?;
let gbm = GbmDevice::new(drm).map_err(VulkanError::Gbm)?;
let phy_dev = self.find_dev(drm)?;
let extensions = self.get_device_extensions(phy_dev)?;
for &ext in REQUIRED_DEVICE_EXTENSIONS {
if extensions.not_contains_key(ext) {
return Err(VulkanError::MissingDeviceExtension(ext));
}
}
let graphics_queue_idx = self.find_graphics_queue(phy_dev)?;
if !self.supports_semaphore_import(phy_dev) {
return Err(VulkanError::SyncobjImport);
}
let enabled_extensions: Vec<_> = REQUIRED_DEVICE_EXTENSIONS
.iter()
.map(|n| n.as_ptr())
.collect();
let mut semaphore_features =
PhysicalDeviceTimelineSemaphoreFeatures::builder().timeline_semaphore(true);
let mut synchronization2_features =
PhysicalDeviceSynchronization2Features::builder().synchronization2(true);
let mut dynamic_rendering_features =
PhysicalDeviceDynamicRenderingFeatures::builder().dynamic_rendering(true);
let queue_create_info = DeviceQueueCreateInfo::builder()
.queue_family_index(graphics_queue_idx)
.queue_priorities(&[1.0])
.build();
let device_create_info = DeviceCreateInfo::builder()
.push_next(&mut semaphore_features)
.push_next(&mut synchronization2_features)
.push_next(&mut dynamic_rendering_features)
.queue_create_infos(std::slice::from_ref(&queue_create_info))
.enabled_extension_names(&enabled_extensions);
let device = unsafe {
self.instance
.create_device(phy_dev, &device_create_info, None)
};
let device = match device {
Ok(d) => d,
Err(e) => return Err(VulkanError::CreateDevice(e)),
};
let destroy_device = OnDrop(|| unsafe { device.destroy_device(None) });
let formats = self.load_formats(phy_dev)?;
let supports_xrgb8888 = formats
.get(&XRGB8888.drm)
.map(|f| {
let mut supports_rendering = false;
let mut supports_texturing = false;
f.modifiers.values().for_each(|v| {
supports_rendering |= v.render_max_extents.is_some();
supports_texturing |= v.texture_max_extents.is_some();
});
supports_rendering && supports_texturing
})
.unwrap_or(false);
if !supports_xrgb8888 {
return Err(VulkanError::XRGB8888);
}
destroy_device.forget();
let external_memory_fd = ExternalMemoryFd::new(&self.instance, &device);
let external_semaphore_fd = ExternalSemaphoreFd::new(&self.instance, &device);
let external_fence_fd = ExternalFenceFd::new(&self.instance, &device);
let push_descriptor = PushDescriptor::new(&self.instance, &device);
let memory_properties =
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
let memory_types = memory_properties.memory_types
[..memory_properties.memory_type_count as _]
.iter()
.copied()
.collect();
let graphics_queue = unsafe { device.get_device_queue(graphics_queue_idx, 0) };
Ok(Rc::new(VulkanDevice {
physical_device: phy_dev,
render_node,
gbm,
instance: self.clone(),
device,
external_memory_fd,
external_semaphore_fd,
external_fence_fd,
push_descriptor,
formats,
memory_types,
graphics_queue,
graphics_queue_idx,
}))
}
}
const REQUIRED_DEVICE_EXTENSIONS: &[&CStr] = &[
KhrExternalMemoryFdFn::name(),
KhrExternalSemaphoreFdFn::name(),
KhrExternalFenceFdFn::name(),
ExtExternalMemoryDmaBufFn::name(),
ExtQueueFamilyForeignFn::name(),
ExtImageDrmFormatModifierFn::name(),
KhrPushDescriptorFn::name(),
];
fn log_device(
props: &PhysicalDeviceProperties,
extensions: Option<&Extensions>,
driver_props: Option<&PhysicalDeviceDriverProperties>,
) {
log::info!(" api version: {}", ApiVersionDisplay(props.api_version));
log::info!(
" driver version: {}",
ApiVersionDisplay(props.driver_version)
);
log::info!(" vendor id: {}", props.vendor_id);
log::info!(" device id: {}", props.device_id);
log::info!(" device type: {:?}", props.device_type);
unsafe {
log::info!(
" device name: {}",
Ustr::from_ptr(props.device_name.as_ptr()).display()
);
}
if props.api_version < API_VERSION {
log::warn!(" device does not support vulkan 1.3");
}
if let Some(extensions) = extensions {
if !extensions.contains_key(ExtPhysicalDeviceDrmFn::name()) {
log::warn!(" device does support not the VK_EXT_physical_device_drm extension");
}
}
if let Some(driver_props) = driver_props {
unsafe {
log::info!(
" driver: {} ({})",
Ustr::from_ptr(driver_props.driver_name.as_ptr()).display(),
Ustr::from_ptr(driver_props.driver_info.as_ptr()).display()
);
}
}
}