Skip to content

Commit 70b5e6b

Browse files
committed
Add support for video capabilities and formats
1 parent 230dc1b commit 70b5e6b

File tree

4 files changed

+568
-9
lines changed

4 files changed

+568
-9
lines changed

examples/src/bin/video.rs

Lines changed: 58 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+
VideoDecodeCapabilityFlags, VideoDecodeH264PictureLayoutFlags, VideoDecodeH264ProfileInfo,
9+
VideoFormatInfo, VideoProfileInfo, VideoProfileListInfo,
10+
},
611
VulkanLibrary,
712
};
813

@@ -98,4 +103,57 @@ 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 = VideoProfileInfo {
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::VideoDecodeProfileInfo::H264(
121+
VideoDecodeH264ProfileInfo {
122+
std_profile_idc: 0,
123+
picture_layout: VideoDecodeH264PictureLayoutFlags::PROGRESSIVE,
124+
..Default::default()
125+
},
126+
),
127+
..Default::default()
128+
};
129+
130+
let video_caps = physical_device
131+
.video_capabilities(profile_info.clone())
132+
.unwrap();
133+
println!("Video capabilities: {:#?}", video_caps);
134+
135+
let video_format_info = VideoFormatInfo {
136+
image_usage: if !video_caps
137+
.decode_capabilities
138+
.flags
139+
.intersects(VideoDecodeCapabilityFlags::DPB_AND_OUTPUT_COINCIDE)
140+
{
141+
ImageUsage::VIDEO_DECODE_DPB
142+
} else {
143+
ImageUsage::VIDEO_DECODE_DPB
144+
| ImageUsage::VIDEO_DECODE_DST
145+
| ImageUsage::TRANSFER_SRC
146+
| ImageUsage::SAMPLED
147+
},
148+
profile_list_info: VideoProfileListInfo {
149+
profiles: vec![profile_info.clone()],
150+
..Default::default()
151+
},
152+
};
153+
154+
let formats = physical_device
155+
.video_format_properties(video_format_info)
156+
.unwrap();
157+
158+
println!("video formats: {:#?}", formats);
101159
}

vulkano/src/device/physical.rs

Lines changed: 287 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+
VideoCapabilities, VideoCodecOperation, VideoDecodeCapabilities,
35+
VideoDecodeCodecCapabilities, VideoDecodeH264Capabilities, VideoFormatInfo,
36+
VideoFormatProperties, VideoProfileInfo,
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,285 @@ 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::DecodeH264 => {
3169+
let crate::video::VideoDecodeProfileInfo::H264(codec_profile_info) =
3170+
&profile.codec_profile_info
3171+
else {
3172+
panic!("Unexpected profile");
3173+
};
3174+
3175+
let next = h264_profile_info_vk.insert(
3176+
ash::vk::VideoDecodeH264ProfileInfoKHR {
3177+
std_profile_idc: codec_profile_info.std_profile_idc,
3178+
picture_layout: codec_profile_info.picture_layout.into(),
3179+
..Default::default()
3180+
},
3181+
);
3182+
3183+
// VUID-VkVideoProfileInfoKHR-videoCodecOperation-07179
3184+
profile_info_vk.p_next = next as *const _ as *const _;
3185+
}
3186+
VideoCodecOperation::DecodeH265 => todo!(),
3187+
}
3188+
3189+
profile_info_vk
3190+
})
3191+
.collect::<Vec<_>>();
3192+
3193+
let profile_list_info_vk = ash::vk::VideoProfileListInfoKHR {
3194+
profile_count: video_profile_info_vk.len() as _,
3195+
p_profiles: video_profile_info_vk.as_ptr() as _,
3196+
..Default::default()
3197+
};
3198+
3199+
let video_format_info_vk = ash::vk::PhysicalDeviceVideoFormatInfoKHR {
3200+
p_next: &profile_list_info_vk as *const _ as _,
3201+
image_usage: video_format_info.image_usage.into(),
3202+
..Default::default()
3203+
};
3204+
3205+
let fns = self.instance().fns();
3206+
3207+
(fns.khr_video_queue
3208+
.get_physical_device_video_format_properties_khr)(
3209+
self.handle(),
3210+
&video_format_info_vk,
3211+
&mut num_formats,
3212+
std::ptr::null_mut(),
3213+
)
3214+
.result()
3215+
.map_err(VulkanError::from)?;
3216+
3217+
let mut video_format_properties_vk =
3218+
vec![VideoFormatPropertiesKHR::default(); num_formats as usize];
3219+
3220+
let result = (fns
3221+
.khr_video_queue
3222+
.get_physical_device_video_format_properties_khr)(
3223+
self.handle(),
3224+
&video_format_info_vk,
3225+
&mut num_formats,
3226+
video_format_properties_vk.as_mut_ptr(),
3227+
);
3228+
3229+
match result {
3230+
ash::vk::Result::SUCCESS => {
3231+
video_format_properties_vk.set_len(num_formats as usize);
3232+
return Ok(video_format_properties_vk
3233+
.into_iter()
3234+
.filter_map(|vk| {
3235+
Some(VideoFormatProperties {
3236+
format: vk.format.try_into().ok()?,
3237+
component_mapping: ComponentMapping {
3238+
r: vk.component_mapping.r.try_into().ok()?,
3239+
g: vk.component_mapping.g.try_into().ok()?,
3240+
b: vk.component_mapping.b.try_into().ok()?,
3241+
a: vk.component_mapping.a.try_into().ok()?,
3242+
},
3243+
image_create_flags: vk.image_create_flags.try_into().ok()?,
3244+
image_type: vk.image_type.try_into().ok()?,
3245+
image_tiling: vk.image_tiling.try_into().ok()?,
3246+
image_usage_flags: vk.image_usage_flags.try_into().ok()?,
3247+
})
3248+
})
3249+
.collect());
3250+
}
3251+
ash::vk::Result::INCOMPLETE => (),
3252+
err => return Err(VulkanError::from(err)),
3253+
}
3254+
}
3255+
}
3256+
3257+
pub fn video_capabilities(
3258+
&self,
3259+
profile_info: VideoProfileInfo,
3260+
) -> Result<VideoCapabilities, Validated<VulkanError>> {
3261+
self.validate_video_capabilities(&profile_info)?;
3262+
3263+
unsafe { Ok(self.video_capabilities_unchecked(profile_info)?) }
3264+
}
3265+
3266+
fn validate_video_capabilities(
3267+
&self,
3268+
profile_info: &VideoProfileInfo,
3269+
) -> Result<(), Box<ValidationError>> {
3270+
if !self.supported_extensions.khr_video_queue || self.api_version() < Version::V1_3 {
3271+
return Err(Box::new(ValidationError {
3272+
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
3273+
Requires::DeviceExtension("khr_video_queue"),
3274+
// Requires::APIVersion(Version::V1_3), // ?
3275+
])]),
3276+
..Default::default()
3277+
}));
3278+
}
3279+
3280+
profile_info
3281+
.validate()
3282+
.map_err(|err| err.add_context("profile_info"))
3283+
}
3284+
3285+
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
3286+
pub unsafe fn video_capabilities_unchecked(
3287+
&self,
3288+
profile_info: VideoProfileInfo,
3289+
) -> Result<VideoCapabilities, VulkanError> {
3290+
let mut video_capabilities = ash::vk::VideoCapabilitiesKHR::default();
3291+
let mut decode_capabilities = ash::vk::VideoDecodeCapabilitiesKHR::default();
3292+
let mut h264_decode_capabilities = None;
3293+
let mut h264_profile_info = None;
3294+
3295+
let VideoProfileInfo {
3296+
video_codec_operation,
3297+
chroma_subsampling,
3298+
luma_bit_depth,
3299+
chroma_bit_depth,
3300+
codec_profile_info,
3301+
_ne: _,
3302+
} = profile_info;
3303+
3304+
let mut profile_info = ash::vk::VideoProfileInfoKHR {
3305+
video_codec_operation: video_codec_operation.into(),
3306+
chroma_subsampling: chroma_subsampling.into(),
3307+
luma_bit_depth: luma_bit_depth.into(),
3308+
chroma_bit_depth: if let Some(chroma_bit_depth) = chroma_bit_depth {
3309+
chroma_bit_depth.into()
3310+
} else {
3311+
ash::vk::VideoComponentBitDepthFlagsKHR::INVALID
3312+
},
3313+
..Default::default()
3314+
};
3315+
3316+
match video_codec_operation {
3317+
VideoCodecOperation::DecodeH264 => {
3318+
let h264_decode_capabilities = h264_decode_capabilities
3319+
.insert(ash::vk::VideoDecodeH264CapabilitiesKHR::default());
3320+
3321+
let codec_profile_info = match codec_profile_info {
3322+
crate::video::VideoDecodeProfileInfo::H264(p) => p,
3323+
_ => panic!("invalid profile info for H264"),
3324+
};
3325+
3326+
decode_capabilities.p_next = h264_decode_capabilities as *mut _ as *mut _;
3327+
video_capabilities.p_next = &mut decode_capabilities as *mut _ as *mut _;
3328+
3329+
let codec_profile_info =
3330+
h264_profile_info.insert(ash::vk::VideoDecodeH264ProfileInfoKHR {
3331+
std_profile_idc: codec_profile_info.std_profile_idc,
3332+
picture_layout: codec_profile_info.picture_layout.into(),
3333+
..Default::default()
3334+
});
3335+
3336+
// VUID-VkVideoProfileInfoKHR-videoCodecOperation-07179
3337+
profile_info.p_next = codec_profile_info as *const _ as *const _;
3338+
}
3339+
VideoCodecOperation::DecodeH265 => todo!(),
3340+
}
3341+
3342+
let fns = self.instance().fns();
3343+
(fns.khr_video_queue
3344+
.get_physical_device_video_capabilities_khr)(
3345+
self.handle(),
3346+
&mut profile_info,
3347+
&mut video_capabilities,
3348+
)
3349+
.result()
3350+
.map_err(VulkanError::from)?;
3351+
3352+
Ok(VideoCapabilities {
3353+
flags: video_capabilities.flags.into(),
3354+
min_bitstream_buffer_offset_alignment: video_capabilities
3355+
.min_bitstream_buffer_offset_alignment,
3356+
min_bitstream_buffer_size_alignment: video_capabilities
3357+
.min_bitstream_buffer_size_alignment,
3358+
picture_access_granularity: [
3359+
video_capabilities.picture_access_granularity.width,
3360+
video_capabilities.picture_access_granularity.height,
3361+
],
3362+
min_coded_extent: [
3363+
video_capabilities.min_coded_extent.width,
3364+
video_capabilities.min_coded_extent.height,
3365+
],
3366+
max_coded_extent: [
3367+
video_capabilities.max_coded_extent.width,
3368+
video_capabilities.max_coded_extent.height,
3369+
],
3370+
max_dpb_slots: video_capabilities.max_dpb_slots,
3371+
max_active_reference_pictures: video_capabilities.max_active_reference_pictures,
3372+
std_header_version: video_capabilities.std_header_version.into(),
3373+
decode_capabilities: VideoDecodeCapabilities {
3374+
flags: decode_capabilities.flags.into(),
3375+
codec_capabilities: match video_codec_operation {
3376+
VideoCodecOperation::DecodeH264 => {
3377+
let h264_decode_capabilities = h264_decode_capabilities.unwrap();
3378+
VideoDecodeCodecCapabilities::H264(VideoDecodeH264Capabilities {
3379+
max_level_idc: h264_decode_capabilities.max_level_idc,
3380+
field_offset_granularity: [
3381+
h264_decode_capabilities.field_offset_granularity.x,
3382+
h264_decode_capabilities.field_offset_granularity.y,
3383+
],
3384+
_ne: crate::NonExhaustive(()),
3385+
})
3386+
}
3387+
_ => unimplemented!(),
3388+
},
3389+
_ne: crate::NonExhaustive(()),
3390+
},
3391+
_ne: crate::NonExhaustive(()),
3392+
})
3393+
}
31093394
}
31103395

31113396
impl Debug for PhysicalDevice {

0 commit comments

Comments
 (0)