Skip to content

Commit 1abad0d

Browse files
committed
Add support for video capabilities and formats
1 parent 9c6f84b commit 1abad0d

File tree

4 files changed

+590
-27
lines changed

4 files changed

+590
-27
lines changed

examples/src/bin/video.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ use std::sync::Arc;
22

33
use vulkano::{
44
device::{Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, QueueFlags},
5+
image::ImageUsage,
56
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions},
7+
video::{
8+
H264ProfileInfo, ProfileInfo, ProfileListInfo, VideoDecodeCapabilityFlags,
9+
VideoDecodeH264PictureLayoutFlags, VideoFormatInfo,
10+
},
611
VulkanLibrary,
712
};
813

@@ -98,4 +103,55 @@ fn main() {
98103
"Video queue supports the following codecs: {:?}",
99104
video_properties.video_codec_operations
100105
);
106+
107+
// Video profiles are provided as input to video capability queries such as
108+
// vkGetPhysicalDeviceVideoCapabilitiesKHR or
109+
// vkGetPhysicalDeviceVideoFormatPropertiesKHR, as well as when creating
110+
// resources to be used by video coding operations such as images, buffers,
111+
// query pools, and video sessions.
112+
//
113+
// You must parse the bitstream to correctly construct the profile info.
114+
// This is hardcoded for the bitstream in this example.
115+
let profile_info = ProfileInfo {
116+
video_codec_operation: vulkano::video::VideoCodecOperation::DecodeH264,
117+
chroma_subsampling: vulkano::video::VideoChromaSubsampling::Type420,
118+
luma_bit_depth: vulkano::video::VideoComponentBitDepth::Type8,
119+
chroma_bit_depth: Some(vulkano::video::VideoComponentBitDepth::Type8),
120+
codec_profile_info: vulkano::video::DecodeProfileInfo::H264(H264ProfileInfo {
121+
std_profile_idc: 0,
122+
picture_layout: VideoDecodeH264PictureLayoutFlags::PROGRESSIVE,
123+
..Default::default()
124+
}),
125+
..Default::default()
126+
};
127+
128+
let video_caps = physical_device
129+
.video_capabilities(profile_info.clone())
130+
.unwrap();
131+
println!("Video capabilities: {:#?}", video_caps);
132+
133+
let video_format_info = VideoFormatInfo {
134+
image_usage: if !video_caps
135+
.decode_capabilities
136+
.flags
137+
.intersects(VideoDecodeCapabilityFlags::DPB_AND_OUTPUT_COINCIDE)
138+
{
139+
ImageUsage::VIDEO_DECODE_DPB
140+
} else {
141+
ImageUsage::VIDEO_DECODE_DPB
142+
| ImageUsage::VIDEO_DECODE_DST
143+
| ImageUsage::TRANSFER_SRC
144+
| ImageUsage::SAMPLED
145+
},
146+
profile_list_info: ProfileListInfo {
147+
profiles: vec![profile_info],
148+
..Default::default()
149+
},
150+
};
151+
152+
let formats = physical_device
153+
.video_format_properties(video_format_info)
154+
.unwrap();
155+
156+
println!("video formats: {:#?}", formats);
101157
}

vulkano/src/device/physical.rs

Lines changed: 291 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use crate::{
1515
display::{Display, DisplayPlaneProperties, DisplayPlanePropertiesRaw, DisplayProperties},
1616
format::{DrmFormatModifierProperties, Format, FormatProperties},
1717
image::{
18-
ImageDrmFormatModifierInfo, ImageFormatInfo, ImageFormatProperties, ImageUsage,
19-
SparseImageFormatInfo, SparseImageFormatProperties,
18+
sampler::ComponentMapping, ImageDrmFormatModifierInfo, ImageFormatInfo,
19+
ImageFormatProperties, ImageUsage, SparseImageFormatInfo, SparseImageFormatProperties,
2020
},
2121
instance::{Instance, InstanceOwned},
2222
macros::{impl_id_counter, vulkan_bitflags, vulkan_enum},
@@ -30,9 +30,15 @@ use crate::{
3030
semaphore::{ExternalSemaphoreInfo, ExternalSemaphoreProperties},
3131
Sharing,
3232
},
33+
video::{
34+
ProfileInfo, VideoCapabilities, VideoCodecOperation, VideoDecodeCapabilities,
35+
VideoDecodeCodecCapabilities, VideoDecodeH264Capabilities, VideoFormatInfo,
36+
VideoFormatProperties,
37+
},
3338
DebugWrapper, ExtensionProperties, Requires, RequiresAllOf, RequiresOneOf, Validated,
3439
ValidationError, Version, VulkanError, VulkanObject,
3540
};
41+
use ash::vk::VideoFormatPropertiesKHR;
3642
use bytemuck::cast_slice;
3743
use parking_lot::RwLock;
3844
use std::{
@@ -3106,6 +3112,289 @@ impl PhysicalDevice {
31063112
visual_id,
31073113
) != 0
31083114
}
3115+
3116+
pub fn video_format_properties(
3117+
&self,
3118+
video_format_info: VideoFormatInfo,
3119+
) -> Result<Vec<VideoFormatProperties>, Validated<VulkanError>> {
3120+
self.validate_video_format_info(&video_format_info)?;
3121+
3122+
unsafe { Ok(self.video_format_properties_unchecked(video_format_info)?) }
3123+
}
3124+
3125+
fn validate_video_format_info(
3126+
&self,
3127+
video_format_info: &VideoFormatInfo,
3128+
) -> Result<(), Box<ValidationError>> {
3129+
if !self.supported_extensions.khr_video_queue || self.api_version() < Version::V1_3 {
3130+
return Err(Box::new(ValidationError {
3131+
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
3132+
Requires::DeviceExtension("khr_video_queue"),
3133+
// Requires::APIVersion(Version::V1_3), // ?
3134+
])]),
3135+
..Default::default()
3136+
}));
3137+
} else {
3138+
Ok(())
3139+
}
3140+
}
3141+
3142+
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
3143+
pub unsafe fn video_format_properties_unchecked(
3144+
&self,
3145+
video_format_info: VideoFormatInfo,
3146+
) -> Result<Vec<VideoFormatProperties>, VulkanError> {
3147+
loop {
3148+
let mut num_formats = 0;
3149+
let mut h264_profile_info_vk = None;
3150+
let video_profile_info_vk = video_format_info
3151+
.profile_list_info
3152+
.profiles
3153+
.iter()
3154+
.map(|profile| {
3155+
let mut profile_info_vk = ash::vk::VideoProfileInfoKHR {
3156+
video_codec_operation: profile.video_codec_operation.into(),
3157+
chroma_subsampling: profile.chroma_subsampling.into(),
3158+
luma_bit_depth: profile.luma_bit_depth.into(),
3159+
chroma_bit_depth: if let Some(chroma_bit_depth) = profile.chroma_bit_depth {
3160+
chroma_bit_depth.into()
3161+
} else {
3162+
ash::vk::VideoComponentBitDepthFlagsKHR::INVALID
3163+
},
3164+
..Default::default()
3165+
};
3166+
3167+
match profile.video_codec_operation {
3168+
VideoCodecOperation::EncodeH264 => todo!(),
3169+
VideoCodecOperation::EncodeH265 => todo!(),
3170+
VideoCodecOperation::DecodeH264 => {
3171+
let crate::video::DecodeProfileInfo::H264(codec_profile_info) =
3172+
&profile.codec_profile_info
3173+
else {
3174+
panic!("Unexpected profile");
3175+
};
3176+
3177+
let next = h264_profile_info_vk.insert(
3178+
ash::vk::VideoDecodeH264ProfileInfoKHR {
3179+
std_profile_idc: codec_profile_info.std_profile_idc,
3180+
picture_layout: codec_profile_info.picture_layout.into(),
3181+
..Default::default()
3182+
},
3183+
);
3184+
3185+
// VUID-VkVideoProfileInfoKHR-videoCodecOperation-07179
3186+
profile_info_vk.p_next = next as *const _ as *const _;
3187+
}
3188+
VideoCodecOperation::DecodeH265 => todo!(),
3189+
}
3190+
3191+
profile_info_vk
3192+
})
3193+
.collect::<Vec<_>>();
3194+
3195+
let profile_list_info_vk = ash::vk::VideoProfileListInfoKHR {
3196+
profile_count: video_profile_info_vk.len() as _,
3197+
p_profiles: video_profile_info_vk.as_ptr() as _,
3198+
..Default::default()
3199+
};
3200+
3201+
let video_format_info_vk = ash::vk::PhysicalDeviceVideoFormatInfoKHR {
3202+
p_next: &profile_list_info_vk as *const _ as _,
3203+
image_usage: video_format_info.image_usage.into(),
3204+
..Default::default()
3205+
};
3206+
3207+
let fns = self.instance().fns();
3208+
3209+
(fns.khr_video_queue
3210+
.get_physical_device_video_format_properties_khr)(
3211+
self.handle(),
3212+
&video_format_info_vk,
3213+
&mut num_formats,
3214+
std::ptr::null_mut(),
3215+
)
3216+
.result()
3217+
.map_err(VulkanError::from)?;
3218+
3219+
let mut video_format_properties_vk =
3220+
vec![VideoFormatPropertiesKHR::default(); num_formats as usize];
3221+
3222+
let result = (fns
3223+
.khr_video_queue
3224+
.get_physical_device_video_format_properties_khr)(
3225+
self.handle(),
3226+
&video_format_info_vk,
3227+
&mut num_formats,
3228+
video_format_properties_vk.as_mut_ptr(),
3229+
);
3230+
3231+
match result {
3232+
ash::vk::Result::SUCCESS => {
3233+
video_format_properties_vk.set_len(num_formats as usize);
3234+
return Ok(video_format_properties_vk
3235+
.into_iter()
3236+
.filter_map(|vk| {
3237+
Some(VideoFormatProperties {
3238+
format: vk.format.try_into().ok()?,
3239+
component_mapping: ComponentMapping {
3240+
r: vk.component_mapping.r.try_into().ok()?,
3241+
g: vk.component_mapping.g.try_into().ok()?,
3242+
b: vk.component_mapping.b.try_into().ok()?,
3243+
a: vk.component_mapping.a.try_into().ok()?,
3244+
},
3245+
image_create_flags: vk.image_create_flags.try_into().ok()?,
3246+
image_type: vk.image_type.try_into().ok()?,
3247+
image_tiling: vk.image_tiling.try_into().ok()?,
3248+
image_usage_flags: vk.image_usage_flags.try_into().ok()?,
3249+
})
3250+
})
3251+
.collect());
3252+
}
3253+
ash::vk::Result::INCOMPLETE => (),
3254+
err => return Err(VulkanError::from(err)),
3255+
}
3256+
}
3257+
}
3258+
3259+
pub fn video_capabilities(
3260+
&self,
3261+
profile_info: ProfileInfo,
3262+
) -> Result<VideoCapabilities, Validated<VulkanError>> {
3263+
self.validate_video_capabilities(&profile_info)?;
3264+
3265+
unsafe { Ok(self.video_capabilities_unchecked(profile_info)?) }
3266+
}
3267+
3268+
fn validate_video_capabilities(
3269+
&self,
3270+
profile_info: &ProfileInfo,
3271+
) -> Result<(), Box<ValidationError>> {
3272+
if !self.supported_extensions.khr_video_queue || self.api_version() < Version::V1_3 {
3273+
return Err(Box::new(ValidationError {
3274+
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
3275+
Requires::DeviceExtension("khr_video_queue"),
3276+
// Requires::APIVersion(Version::V1_3), // ?
3277+
])]),
3278+
..Default::default()
3279+
}));
3280+
}
3281+
3282+
profile_info
3283+
.validate()
3284+
.map_err(|err| err.add_context("profile_info"))
3285+
}
3286+
3287+
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
3288+
pub unsafe fn video_capabilities_unchecked(
3289+
&self,
3290+
profile_info: ProfileInfo,
3291+
) -> Result<VideoCapabilities, VulkanError> {
3292+
let mut video_capabilities = ash::vk::VideoCapabilitiesKHR::default();
3293+
let mut decode_capabilities = ash::vk::VideoDecodeCapabilitiesKHR::default();
3294+
let mut h264_decode_capabilities = None;
3295+
let mut h264_profile_info = None;
3296+
3297+
let ProfileInfo {
3298+
video_codec_operation,
3299+
chroma_subsampling,
3300+
luma_bit_depth,
3301+
chroma_bit_depth,
3302+
codec_profile_info,
3303+
_ne: _,
3304+
} = profile_info;
3305+
3306+
let mut profile_info = ash::vk::VideoProfileInfoKHR {
3307+
video_codec_operation: video_codec_operation.into(),
3308+
chroma_subsampling: chroma_subsampling.into(),
3309+
luma_bit_depth: luma_bit_depth.into(),
3310+
chroma_bit_depth: if let Some(chroma_bit_depth) = chroma_bit_depth {
3311+
chroma_bit_depth.into()
3312+
} else {
3313+
ash::vk::VideoComponentBitDepthFlagsKHR::INVALID
3314+
},
3315+
..Default::default()
3316+
};
3317+
3318+
match video_codec_operation {
3319+
VideoCodecOperation::EncodeH264 => todo!(),
3320+
VideoCodecOperation::EncodeH265 => todo!(),
3321+
VideoCodecOperation::DecodeH264 => {
3322+
let h264_decode_capabilities = h264_decode_capabilities
3323+
.insert(ash::vk::VideoDecodeH264CapabilitiesKHR::default());
3324+
3325+
let codec_profile_info = match codec_profile_info {
3326+
crate::video::DecodeProfileInfo::H264(p) => p,
3327+
_ => panic!("invalid profile info for H264"),
3328+
};
3329+
3330+
decode_capabilities.p_next = h264_decode_capabilities as *mut _ as *mut _;
3331+
video_capabilities.p_next = &mut decode_capabilities as *mut _ as *mut _;
3332+
3333+
let codec_profile_info =
3334+
h264_profile_info.insert(ash::vk::VideoDecodeH264ProfileInfoKHR {
3335+
std_profile_idc: codec_profile_info.std_profile_idc,
3336+
picture_layout: codec_profile_info.picture_layout.into(),
3337+
..Default::default()
3338+
});
3339+
3340+
// VUID-VkVideoProfileInfoKHR-videoCodecOperation-07179
3341+
profile_info.p_next = codec_profile_info as *const _ as *const _;
3342+
}
3343+
VideoCodecOperation::DecodeH265 => todo!(),
3344+
}
3345+
3346+
let fns = self.instance().fns();
3347+
(fns.khr_video_queue
3348+
.get_physical_device_video_capabilities_khr)(
3349+
self.handle(),
3350+
&mut profile_info,
3351+
&mut video_capabilities,
3352+
)
3353+
.result()
3354+
.map_err(VulkanError::from)?;
3355+
3356+
Ok(VideoCapabilities {
3357+
flags: video_capabilities.flags.into(),
3358+
min_bitstream_buffer_offset_alignment: video_capabilities
3359+
.min_bitstream_buffer_offset_alignment,
3360+
min_bitstream_buffer_size_alignment: video_capabilities
3361+
.min_bitstream_buffer_size_alignment,
3362+
picture_access_granularity: [
3363+
video_capabilities.picture_access_granularity.width,
3364+
video_capabilities.picture_access_granularity.height,
3365+
],
3366+
min_coded_extent: [
3367+
video_capabilities.min_coded_extent.width,
3368+
video_capabilities.min_coded_extent.height,
3369+
],
3370+
max_coded_extent: [
3371+
video_capabilities.max_coded_extent.width,
3372+
video_capabilities.max_coded_extent.height,
3373+
],
3374+
max_dpb_slots: video_capabilities.max_dpb_slots,
3375+
max_active_reference_pictures: video_capabilities.max_active_reference_pictures,
3376+
std_header_version: video_capabilities.std_header_version.into(),
3377+
decode_capabilities: VideoDecodeCapabilities {
3378+
flags: decode_capabilities.flags.into(),
3379+
codec_capabilities: match video_codec_operation {
3380+
VideoCodecOperation::DecodeH264 => {
3381+
let h264_decode_capabilities = h264_decode_capabilities.unwrap();
3382+
VideoDecodeCodecCapabilities::H264(VideoDecodeH264Capabilities {
3383+
max_level_idc: h264_decode_capabilities.max_level_idc,
3384+
field_offset_granularity: [
3385+
h264_decode_capabilities.field_offset_granularity.x,
3386+
h264_decode_capabilities.field_offset_granularity.y,
3387+
],
3388+
_ne: crate::NonExhaustive(()),
3389+
})
3390+
}
3391+
_ => unimplemented!(),
3392+
},
3393+
_ne: crate::NonExhaustive(()),
3394+
},
3395+
_ne: crate::NonExhaustive(()),
3396+
})
3397+
}
31093398
}
31103399

31113400
impl Debug for PhysicalDevice {

0 commit comments

Comments
 (0)