1
0
Fork 0
forked from wry/wry
wry/src/gfx_apis/vulkan/format.rs

348 lines
12 KiB
Rust

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<Modifier, VulkanModifier>,
pub shm: Option<VulkanInternalFormat>,
}
#[derive(Debug)]
pub struct VulkanModifier {
pub modifier: Modifier,
pub planes: usize,
pub features: FormatFeatureFlags,
pub render_limits: Option<VulkanModifierLimits>,
pub texture_limits: Option<VulkanModifierLimits>,
pub transfer_limits: Option<VulkanModifierLimits>,
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<AHashMap<u32, VulkanFormat>, 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<u32, VulkanFormat>,
) -> 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<Option<VulkanInternalFormat>, 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<Option<VulkanInternalFormat>, 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<AHashMap<Modifier, VulkanModifier>, 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<Option<VulkanModifierLimits>, 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<Option<VulkanModifierLimits>, 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,
}))
}
}