Skip to content

Commit

Permalink
Add support for video capabilities and formats
Browse files Browse the repository at this point in the history
  • Loading branch information
dwlsalmeida committed Oct 2, 2023
1 parent f1bd196 commit 9a7b581
Show file tree
Hide file tree
Showing 4 changed files with 590 additions and 27 deletions.
56 changes: 56 additions & 0 deletions examples/src/bin/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ use std::sync::Arc;

use vulkano::{
device::{Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, QueueFlags},
image::ImageUsage,
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions},
video::{
H264ProfileInfo, ProfileInfo, ProfileListInfo, VideoDecodeCapabilityFlags,
VideoDecodeH264PictureLayoutFlags, VideoFormatInfo,
},
VulkanLibrary,
};

Expand Down Expand Up @@ -98,4 +103,55 @@ fn main() {
"Video queue supports the following codecs: {:?}",
video_properties.video_codec_operations
);

// Video profiles are provided as input to video capability queries such as
// vkGetPhysicalDeviceVideoCapabilitiesKHR or
// vkGetPhysicalDeviceVideoFormatPropertiesKHR, as well as when creating
// resources to be used by video coding operations such as images, buffers,
// query pools, and video sessions.
//
// You must parse the bitstream to correctly construct the profile info.
// This is hardcoded for the bitstream in this example.
let profile_info = ProfileInfo {
video_codec_operation: vulkano::video::VideoCodecOperation::DecodeH264,
chroma_subsampling: vulkano::video::VideoChromaSubsampling::Type420,
luma_bit_depth: vulkano::video::VideoComponentBitDepth::Type8,
chroma_bit_depth: Some(vulkano::video::VideoComponentBitDepth::Type8),
codec_profile_info: vulkano::video::DecodeProfileInfo::H264(H264ProfileInfo {
std_profile_idc: 0,
picture_layout: VideoDecodeH264PictureLayoutFlags::PROGRESSIVE,
..Default::default()
}),
..Default::default()
};

let video_caps = physical_device
.video_capabilities(profile_info.clone())
.unwrap();
println!("Video capabilities: {:#?}", video_caps);

let video_format_info = VideoFormatInfo {
image_usage: if !video_caps
.decode_capabilities
.flags
.intersects(VideoDecodeCapabilityFlags::DPB_AND_OUTPUT_COINCIDE)
{
ImageUsage::VIDEO_DECODE_DPB
} else {
ImageUsage::VIDEO_DECODE_DPB
| ImageUsage::VIDEO_DECODE_DST
| ImageUsage::TRANSFER_SRC
| ImageUsage::SAMPLED
},
profile_list_info: ProfileListInfo {
profiles: vec![profile_info],
..Default::default()
},
};

let formats = physical_device
.video_format_properties(video_format_info)
.unwrap();

println!("video formats: {:#?}", formats);
}
293 changes: 291 additions & 2 deletions vulkano/src/device/physical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use crate::{
device::{properties::Properties, DeviceExtensions, Features, FeaturesFfi, PropertiesFfi},
format::{DrmFormatModifierProperties, Format, FormatProperties},
image::{
ImageAspects, ImageDrmFormatModifierInfo, ImageFormatInfo, ImageFormatProperties,
ImageUsage, SparseImageFormatInfo, SparseImageFormatProperties,
sampler::ComponentMapping, ImageAspects, ImageDrmFormatModifierInfo, ImageFormatInfo,
ImageFormatProperties, ImageUsage, SparseImageFormatInfo, SparseImageFormatProperties,
},
instance::{Instance, InstanceOwned},
macros::{impl_id_counter, vulkan_bitflags, vulkan_enum},
Expand All @@ -29,9 +29,15 @@ use crate::{
semaphore::{ExternalSemaphoreInfo, ExternalSemaphoreProperties},
Sharing,
},
video::{
ProfileInfo, VideoCapabilities, VideoCodecOperation, VideoDecodeCapabilities,
VideoDecodeCodecCapabilities, VideoDecodeH264Capabilities, VideoFormatInfo,
VideoFormatProperties,
},
DebugWrapper, ExtensionProperties, Requires, RequiresAllOf, RequiresOneOf, Validated,
ValidationError, Version, VulkanError, VulkanObject,
};
use ash::vk::VideoFormatPropertiesKHR;
use bytemuck::cast_slice;
use std::{
fmt::{Debug, Display, Error as FmtError, Formatter},
Expand Down Expand Up @@ -2654,6 +2660,289 @@ impl PhysicalDevice {
visual_id,
) != 0
}

pub fn video_format_properties(
&self,
video_format_info: VideoFormatInfo,
) -> Result<Vec<VideoFormatProperties>, Validated<VulkanError>> {
self.validate_video_format_info(&video_format_info)?;

unsafe { Ok(self.video_format_properties_unchecked(video_format_info)?) }
}

fn validate_video_format_info(
&self,
video_format_info: &VideoFormatInfo,
) -> Result<(), Box<ValidationError>> {
if !self.supported_extensions.khr_video_queue || self.api_version() < Version::V1_3 {
return Err(Box::new(ValidationError {
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
Requires::DeviceExtension("khr_video_queue"),
// Requires::APIVersion(Version::V1_3), // ?
])]),
..Default::default()
}));
} else {
Ok(())
}
}

#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn video_format_properties_unchecked(
&self,
video_format_info: VideoFormatInfo,
) -> Result<Vec<VideoFormatProperties>, VulkanError> {
loop {
let mut num_formats = 0;
let mut h264_profile_info_vk = None;
let video_profile_info_vk = video_format_info
.profile_list_info
.profiles
.iter()
.map(|profile| {
let mut profile_info_vk = ash::vk::VideoProfileInfoKHR {
video_codec_operation: profile.video_codec_operation.into(),
chroma_subsampling: profile.chroma_subsampling.into(),
luma_bit_depth: profile.luma_bit_depth.into(),
chroma_bit_depth: if let Some(chroma_bit_depth) = profile.chroma_bit_depth {
chroma_bit_depth.into()
} else {
ash::vk::VideoComponentBitDepthFlagsKHR::INVALID
},
..Default::default()
};

match profile.video_codec_operation {
VideoCodecOperation::EncodeH264 => todo!(),
VideoCodecOperation::EncodeH265 => todo!(),
VideoCodecOperation::DecodeH264 => {
let crate::video::DecodeProfileInfo::H264(codec_profile_info) =
&profile.codec_profile_info
else {
panic!("Unexpected profile");
};

let next = h264_profile_info_vk.insert(
ash::vk::VideoDecodeH264ProfileInfoKHR {
std_profile_idc: codec_profile_info.std_profile_idc,
picture_layout: codec_profile_info.picture_layout.into(),
..Default::default()
},
);

// VUID-VkVideoProfileInfoKHR-videoCodecOperation-07179
profile_info_vk.p_next = next as *const _ as *const _;
}
VideoCodecOperation::DecodeH265 => todo!(),
}

profile_info_vk
})
.collect::<Vec<_>>();

let profile_list_info_vk = ash::vk::VideoProfileListInfoKHR {
profile_count: video_profile_info_vk.len() as _,
p_profiles: video_profile_info_vk.as_ptr() as _,
..Default::default()
};

let video_format_info_vk = ash::vk::PhysicalDeviceVideoFormatInfoKHR {
p_next: &profile_list_info_vk as *const _ as _,
image_usage: video_format_info.image_usage.into(),
..Default::default()
};

let fns = self.instance().fns();

(fns.khr_video_queue
.get_physical_device_video_format_properties_khr)(
self.handle(),
&video_format_info_vk,
&mut num_formats,
std::ptr::null_mut(),
)
.result()
.map_err(VulkanError::from)?;

let mut video_format_properties_vk =
vec![VideoFormatPropertiesKHR::default(); num_formats as usize];

let result = (fns
.khr_video_queue
.get_physical_device_video_format_properties_khr)(
self.handle(),
&video_format_info_vk,
&mut num_formats,
video_format_properties_vk.as_mut_ptr(),
);

match result {
ash::vk::Result::SUCCESS => {
video_format_properties_vk.set_len(num_formats as usize);
return Ok(video_format_properties_vk
.into_iter()
.filter_map(|vk| {
Some(VideoFormatProperties {
format: vk.format.try_into().ok()?,
component_mapping: ComponentMapping {
r: vk.component_mapping.r.try_into().ok()?,
g: vk.component_mapping.g.try_into().ok()?,
b: vk.component_mapping.b.try_into().ok()?,
a: vk.component_mapping.a.try_into().ok()?,
},
image_create_flags: vk.image_create_flags.try_into().ok()?,
image_type: vk.image_type.try_into().ok()?,
image_tiling: vk.image_tiling.try_into().ok()?,
image_usage_flags: vk.image_usage_flags.try_into().ok()?,
})
})
.collect());
}
ash::vk::Result::INCOMPLETE => (),
err => return Err(VulkanError::from(err)),
}
}
}

pub fn video_capabilities(
&self,
profile_info: ProfileInfo,
) -> Result<VideoCapabilities, Validated<VulkanError>> {
self.validate_video_capabilities(&profile_info)?;

unsafe { Ok(self.video_capabilities_unchecked(profile_info)?) }
}

fn validate_video_capabilities(
&self,
profile_info: &ProfileInfo,
) -> Result<(), Box<ValidationError>> {
if !self.supported_extensions.khr_video_queue || self.api_version() < Version::V1_3 {
return Err(Box::new(ValidationError {
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
Requires::DeviceExtension("khr_video_queue"),
// Requires::APIVersion(Version::V1_3), // ?
])]),
..Default::default()
}));
}

profile_info
.validate()
.map_err(|err| err.add_context("profile_info"))
}

#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn video_capabilities_unchecked(
&self,
profile_info: ProfileInfo,
) -> Result<VideoCapabilities, VulkanError> {
let mut video_capabilities = ash::vk::VideoCapabilitiesKHR::default();
let mut decode_capabilities = ash::vk::VideoDecodeCapabilitiesKHR::default();
let mut h264_decode_capabilities = None;
let mut h264_profile_info = None;

let ProfileInfo {
video_codec_operation,
chroma_subsampling,
luma_bit_depth,
chroma_bit_depth,
codec_profile_info,
_ne: _,
} = profile_info;

let mut profile_info = ash::vk::VideoProfileInfoKHR {
video_codec_operation: video_codec_operation.into(),
chroma_subsampling: chroma_subsampling.into(),
luma_bit_depth: luma_bit_depth.into(),
chroma_bit_depth: if let Some(chroma_bit_depth) = chroma_bit_depth {
chroma_bit_depth.into()
} else {
ash::vk::VideoComponentBitDepthFlagsKHR::INVALID
},
..Default::default()
};

match video_codec_operation {
VideoCodecOperation::EncodeH264 => todo!(),
VideoCodecOperation::EncodeH265 => todo!(),
VideoCodecOperation::DecodeH264 => {
let h264_decode_capabilities = h264_decode_capabilities
.insert(ash::vk::VideoDecodeH264CapabilitiesKHR::default());

let codec_profile_info = match codec_profile_info {
crate::video::DecodeProfileInfo::H264(p) => p,
_ => panic!("invalid profile info for H264"),
};

decode_capabilities.p_next = h264_decode_capabilities as *mut _ as *mut _;
video_capabilities.p_next = &mut decode_capabilities as *mut _ as *mut _;

let codec_profile_info =
h264_profile_info.insert(ash::vk::VideoDecodeH264ProfileInfoKHR {
std_profile_idc: codec_profile_info.std_profile_idc,
picture_layout: codec_profile_info.picture_layout.into(),
..Default::default()
});

// VUID-VkVideoProfileInfoKHR-videoCodecOperation-07179
profile_info.p_next = codec_profile_info as *const _ as *const _;
}
VideoCodecOperation::DecodeH265 => todo!(),
}

let fns = self.instance().fns();
(fns.khr_video_queue
.get_physical_device_video_capabilities_khr)(
self.handle(),
&mut profile_info,
&mut video_capabilities,
)
.result()
.map_err(VulkanError::from)?;

Ok(VideoCapabilities {
flags: video_capabilities.flags.into(),
min_bitstream_buffer_offset_alignment: video_capabilities
.min_bitstream_buffer_offset_alignment,
min_bitstream_buffer_size_alignment: video_capabilities
.min_bitstream_buffer_size_alignment,
picture_access_granularity: [
video_capabilities.picture_access_granularity.width,
video_capabilities.picture_access_granularity.height,
],
min_coded_extent: [
video_capabilities.min_coded_extent.width,
video_capabilities.min_coded_extent.height,
],
max_coded_extent: [
video_capabilities.max_coded_extent.width,
video_capabilities.max_coded_extent.height,
],
max_dpb_slots: video_capabilities.max_dpb_slots,
max_active_reference_pictures: video_capabilities.max_active_reference_pictures,
std_header_version: video_capabilities.std_header_version.into(),
decode_capabilities: VideoDecodeCapabilities {
flags: decode_capabilities.flags.into(),
codec_capabilities: match video_codec_operation {
VideoCodecOperation::DecodeH264 => {
let h264_decode_capabilities = h264_decode_capabilities.unwrap();
VideoDecodeCodecCapabilities::H264(VideoDecodeH264Capabilities {
max_level_idc: h264_decode_capabilities.max_level_idc,
field_offset_granularity: [
h264_decode_capabilities.field_offset_granularity.x,
h264_decode_capabilities.field_offset_granularity.y,
],
_ne: crate::NonExhaustive(()),
})
}
_ => unimplemented!(),
},
_ne: crate::NonExhaustive(()),
},
_ne: crate::NonExhaustive(()),
})
}
}

impl Debug for PhysicalDevice {
Expand Down
Loading

0 comments on commit 9a7b581

Please sign in to comment.