use { crate::{ format::{Format, FORMATS}, gfx_apis::vulkan::{instance::VulkanInstance, VulkanError}, video::{Modifier, LINEAR_MODIFIER}, }, ahash::AHashMap, ash::{ vk, vk::{ DrmFormatModifierPropertiesEXT, DrmFormatModifierPropertiesListEXT, ExternalImageFormatProperties, ExternalMemoryFeatureFlags, ExternalMemoryHandleTypeFlags, FormatFeatureFlags, FormatProperties, FormatProperties2, ImageFormatProperties2, ImageTiling, ImageType, ImageUsageFlags, PhysicalDevice, PhysicalDeviceExternalImageFormatInfo, PhysicalDeviceImageDrmFormatModifierInfoEXT, PhysicalDeviceImageFormatInfo2, SharingMode, }, }, isnt::std_1::collections::IsntHashMapExt, std::cmp::min, }; #[derive(Debug)] pub struct VulkanFormat { pub format: &'static Format, pub modifiers: AHashMap, pub shm: Option, } #[derive(Debug)] pub struct VulkanModifier { pub modifier: Modifier, pub planes: usize, pub features: FormatFeatureFlags, pub render_limits: Option, pub texture_limits: Option, pub transfer_limits: Option, pub render_needs_bridge: bool, } #[derive(Copy, Clone, Debug)] pub struct VulkanModifierLimits { pub max_width: u32, pub max_height: u32, pub exportable: bool, } #[derive(Debug)] pub struct VulkanInternalFormat { pub limits: VulkanModifierLimits, } const FRAMEBUFFER_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw( 0 | FormatFeatureFlags::COLOR_ATTACHMENT.as_raw() | FormatFeatureFlags::COLOR_ATTACHMENT_BLEND.as_raw(), ); const FRAMEBUFFER_BRIDGED_FEATURES: FormatFeatureFlags = FormatFeatureFlags::TRANSFER_DST; const TEX_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw( 0 | FormatFeatureFlags::SAMPLED_IMAGE.as_raw() | FormatFeatureFlags::TRANSFER_SRC.as_raw() | FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR.as_raw(), ); const TRANSFER_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw( FormatFeatureFlags::TRANSFER_SRC.as_raw() | FormatFeatureFlags::TRANSFER_DST.as_raw(), ); const SHM_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(TRANSFER_FEATURES.as_raw() | TEX_FEATURES.as_raw()); const FRAMEBUFFER_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw( ImageUsageFlags::COLOR_ATTACHMENT.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(), ); const FRAMEBUFFER_BRIDGED_USAGE: ImageUsageFlags = ImageUsageFlags::TRANSFER_DST; const TEX_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw( ImageUsageFlags::SAMPLED.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(), ); const TRANSFER_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw( ImageUsageFlags::TRANSFER_SRC.as_raw() | ImageUsageFlags::TRANSFER_DST.as_raw(), ); const SHM_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(TRANSFER_USAGE.as_raw() | TEX_USAGE.as_raw()); impl VulkanInstance { pub(super) fn load_formats( &self, phy_dev: PhysicalDevice, ) -> Result, VulkanError> { let mut res = AHashMap::new(); for format in FORMATS { self.load_format(phy_dev, format, &mut res)?; } Ok(res) } fn load_format( &self, phy_dev: PhysicalDevice, format: &'static Format, dst: &mut AHashMap, ) -> Result<(), VulkanError> { let mut modifier_props = DrmFormatModifierPropertiesListEXT::default(); let mut format_properties = FormatProperties2::default().push_next(&mut modifier_props); unsafe { self.instance.get_physical_device_format_properties2( phy_dev, format.vk_format, &mut format_properties, ); } let shm = self.load_shm_format(phy_dev, format, &format_properties.format_properties)?; let modifiers = self.load_drm_format( phy_dev, format, &format_properties.format_properties, &modifier_props, )?; if shm.is_some() || modifiers.is_not_empty() { dst.insert( format.drm, VulkanFormat { format, modifiers, shm, }, ); } Ok(()) } fn load_shm_format( &self, phy_dev: PhysicalDevice, format: &Format, props: &FormatProperties, ) -> Result, VulkanError> { if format.shm_info.is_none() { return Ok(None); } self.load_internal_format(phy_dev, format, props, SHM_FEATURES, SHM_USAGE) } fn load_internal_format( &self, phy_dev: PhysicalDevice, format: &Format, props: &FormatProperties, features: FormatFeatureFlags, usage: ImageUsageFlags, ) -> Result, VulkanError> { if !props.optimal_tiling_features.contains(features) { return Ok(None); } let format_info = PhysicalDeviceImageFormatInfo2::default() .ty(ImageType::TYPE_2D) .format(format.vk_format) .tiling(ImageTiling::OPTIMAL) .usage(usage); let mut format_properties = ImageFormatProperties2::default(); let res = unsafe { self.instance.get_physical_device_image_format_properties2( phy_dev, &format_info, &mut format_properties, ) }; if let Err(e) = res { return match e { vk::Result::ERROR_FORMAT_NOT_SUPPORTED => Ok(None), _ => Err(VulkanError::LoadImageProperties(e)), }; } Ok(Some(VulkanInternalFormat { limits: VulkanModifierLimits { max_width: format_properties.image_format_properties.max_extent.width, max_height: format_properties.image_format_properties.max_extent.height, exportable: false, }, })) } fn load_drm_format( &self, phy_dev: PhysicalDevice, format: &Format, internal_format_properties: &FormatProperties, props: &DrmFormatModifierPropertiesListEXT, ) -> Result, VulkanError> { if props.drm_format_modifier_count == 0 { return Ok(AHashMap::new()); } let mut drm_mods = vec![ DrmFormatModifierPropertiesEXT::default(); props.drm_format_modifier_count as usize ]; let mut modifier_props = DrmFormatModifierPropertiesListEXT::default() .drm_format_modifier_properties(&mut drm_mods); let mut format_properties = FormatProperties2::default().push_next(&mut modifier_props); unsafe { self.instance.get_physical_device_format_properties2( phy_dev, format.vk_format, &mut format_properties, ); }; let mut mods = AHashMap::new(); for modifier in drm_mods { let mut render_limits = self.get_max_extents( phy_dev, format, FRAMEBUFFER_FEATURES, FRAMEBUFFER_USAGE, &modifier, )?; let texture_limits = self.get_max_extents(phy_dev, format, TEX_FEATURES, TEX_USAGE, &modifier)?; let transfer_limits = self.get_max_extents( phy_dev, format, TRANSFER_FEATURES, TRANSFER_USAGE, &modifier, )?; let mut render_needs_bridge = false; if render_limits.is_none() && modifier.drm_format_modifier == LINEAR_MODIFIER { render_limits = self.get_fb_bridged_max_extents( phy_dev, format, internal_format_properties, &modifier, )?; if render_limits.is_some() { render_needs_bridge = true; } } mods.insert( modifier.drm_format_modifier, VulkanModifier { modifier: modifier.drm_format_modifier, planes: modifier.drm_format_modifier_plane_count as _, features: modifier.drm_format_modifier_tiling_features, render_limits, texture_limits, transfer_limits, render_needs_bridge, }, ); } Ok(mods) } fn get_fb_bridged_max_extents( &self, phy_dev: PhysicalDevice, format: &Format, internal_format_properties: &FormatProperties, modifier: &DrmFormatModifierPropertiesEXT, ) -> Result, VulkanError> { let transfer_dst_max_extents = self.get_max_extents( phy_dev, format, FRAMEBUFFER_BRIDGED_FEATURES, FRAMEBUFFER_BRIDGED_USAGE, &modifier, )?; let Some(transfer_dst_max_extents) = transfer_dst_max_extents else { return Ok(None); }; let bridge_format = self.load_internal_format( phy_dev, format, internal_format_properties, FRAMEBUFFER_FEATURES, FRAMEBUFFER_USAGE, )?; let Some(bridge_format) = bridge_format else { return Ok(None); }; Ok(Some(VulkanModifierLimits { max_width: min( transfer_dst_max_extents.max_width, bridge_format.limits.max_width, ), max_height: min( transfer_dst_max_extents.max_height, bridge_format.limits.max_height, ), exportable: transfer_dst_max_extents.exportable, })) } fn get_max_extents( &self, phy_dev: PhysicalDevice, format: &Format, features: FormatFeatureFlags, usage: ImageUsageFlags, props: &DrmFormatModifierPropertiesEXT, ) -> Result, VulkanError> { if !props.drm_format_modifier_tiling_features.contains(features) { return Ok(None); } let mut modifier_info = PhysicalDeviceImageDrmFormatModifierInfoEXT::default() .drm_format_modifier(props.drm_format_modifier) .sharing_mode(SharingMode::EXCLUSIVE); let mut external_image_format_info = PhysicalDeviceExternalImageFormatInfo::default() .handle_type(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT); let image_format_info = PhysicalDeviceImageFormatInfo2::default() .ty(ImageType::TYPE_2D) .format(format.vk_format) .usage(usage) .tiling(ImageTiling::DRM_FORMAT_MODIFIER_EXT) .push_next(&mut external_image_format_info) .push_next(&mut modifier_info); let mut external_image_format_props = ExternalImageFormatProperties::default(); let mut image_format_props = ImageFormatProperties2::default().push_next(&mut external_image_format_props); let res = unsafe { self.instance.get_physical_device_image_format_properties2( phy_dev, &image_format_info, &mut image_format_props, ) }; if let Err(e) = res { return match e { vk::Result::ERROR_FORMAT_NOT_SUPPORTED => Ok(None), _ => Err(VulkanError::LoadImageProperties(e)), }; } let image_format_props = &image_format_props.image_format_properties; let external_memory_features = &external_image_format_props .external_memory_properties .external_memory_features; let importable = external_memory_features.contains(ExternalMemoryFeatureFlags::IMPORTABLE); if !importable { return Ok(None); } let exportable = external_memory_features.contains(ExternalMemoryFeatureFlags::EXPORTABLE); Ok(Some(VulkanModifierLimits { max_width: image_format_props.max_extent.width, max_height: image_format_props.max_extent.height, exportable, })) } }