render: implement a vulkan renderer
This commit is contained in:
parent
4ba8550da8
commit
cf332e8436
66 changed files with 4287 additions and 239 deletions
343
src/gfx_apis/vulkan/device.rs
Normal file
343
src/gfx_apis/vulkan/device.rs
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue