diff --git a/examples/src/bin/video.rs b/examples/src/bin/video.rs index 70f91323ac..065c1f3390 100644 --- a/examples/src/bin/video.rs +++ b/examples/src/bin/video.rs @@ -2,7 +2,9 @@ use std::sync::Arc; use vulkano::{ device::{Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, QueueFlags}, + image::ImageUsage, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, + video::{H264ProfileInfo, ProfileInfo, VideoDecodeH264PictureLayoutFlags, VideoFormatInfo}, VulkanLibrary, }; @@ -99,5 +101,42 @@ fn main() { video_properties.video_codec_operations ); - // let video_capabilities = //vkGetPhysicalDeviceVideoCapabilitiesKHR + 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).unwrap(); + println!("Video capabilities: {:#?}", video_caps); + + // let h264_caps = match video_caps.decode_capabilities.codec_capabilities { + // vulkano::video::VideoDecodeCodecCapabilities::H264(ref caps) => caps, + // _ => panic!("unexpected codec capabilities"), + // }; + + let video_format_info = VideoFormatInfo { + image_usage: if video_caps.decode_capabilities.flags + & vulkano::video::VideoDecodeCapabilityFlags::DPB_AND_OUTPUT_COINCIDE + == 0 + { + ImageUsage::VIDEO_DECODE_DPB + } else { + ImageUsage::VIDEO_DECODE_DPB + | ImageUsage::VIDEO_DECODE_DST + | ImageUsage::TRANSFER_SRC + | ImageUsage::SAMPLED + }, + }; + + let formats = physical_device + .video_format_properties(video_format_info) + .unwrap(); } diff --git a/vulkano/src/device/physical.rs b/vulkano/src/device/physical.rs index bec1a38678..c55488f469 100644 --- a/vulkano/src/device/physical.rs +++ b/vulkano/src/device/physical.rs @@ -29,6 +29,11 @@ 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, }; @@ -2654,6 +2659,182 @@ impl PhysicalDevice { visual_id, ) != 0 } + + pub fn video_format_properties( + &self, + video_format_info: VideoFormatInfo, + ) -> Result, Validated> { + 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> { + 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, VulkanError> { + let mut video_format_properties = vec![]; + + Ok(video_format_properties) + } + + pub fn video_capabilities( + &self, + profile_info: ProfileInfo, + ) -> Result> { + self.validate_video_capabilities(&profile_info)?; + + unsafe { Ok(self.video_capabilities_unchecked(profile_info)?) } + } + + fn validate_video_capabilities( + &self, + profile_info: &ProfileInfo, + ) -> Result<(), Box> { + 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 { + 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 { diff --git a/vulkano/src/image/usage.rs b/vulkano/src/image/usage.rs index e62ea0dced..86284c2fcd 100644 --- a/vulkano/src/image/usage.rs +++ b/vulkano/src/image/usage.rs @@ -49,26 +49,23 @@ vulkan_bitflags! { /// The image can be used as an input attachment in a render pass/framebuffer. INPUT_ATTACHMENT = INPUT_ATTACHMENT, - /* TODO: enable // TODO: document VIDEO_DECODE_DST = VIDEO_DECODE_DST_KHR RequiresOneOf([ RequiresAllOf([DeviceExtension(khr_video_decode_queue)]), - ]),*/ + ]), - /* TODO: enable // TODO: document VIDEO_DECODE_SRC = VIDEO_DECODE_SRC_KHR RequiresOneOf([ RequiresAllOf([DeviceExtension(khr_video_decode_queue)]), - ]),*/ + ]), - /* TODO: enable // TODO: document VIDEO_DECODE_DPB = VIDEO_DECODE_DPB_KHR RequiresOneOf([ RequiresAllOf([DeviceExtension(khr_video_decode_queue)]), - ]),*/ + ]), /* TODO: enable // TODO: document diff --git a/vulkano/src/video.rs b/vulkano/src/video.rs index 48a50177ac..c15ecdaef1 100644 --- a/vulkano/src/video.rs +++ b/vulkano/src/video.rs @@ -7,42 +7,257 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use crate::macros::vulkan_bitflags; +use ash::vk::{ + native::{StdVideoH264LevelIdc, StdVideoH264ProfileIdc}, + DeviceSize, +}; -vulkan_bitflags! { +use crate::{ + format::Format, + image::{sampler::ComponentMapping, ImageCreateFlags, ImageTiling, ImageType, ImageUsage}, + macros::{vulkan_bitflags, vulkan_bitflags_enum}, + ExtensionProperties, ValidationError, +}; + +vulkan_bitflags_enum! { #[non_exhaustive] /// The type of video coding operation and video compression standard used /// by a video profile - VideoCodecOperations = VideoCodecOperationFlagsKHR(u32); + VideoCodecOperations, - /// Indicates no support for any video codec operations. - NONE = NONE - RequiresOneOf([ - RequiresAllOf([DeviceExtension(khr_video_decode_queue)]), - ]), + VideoCodecOperation, + + = VideoCodecOperationFlagsKHR(u32); /// Specifies support for H.264 video encode operations - ENCODE_H264 = ENCODE_H264_EXT + ENCODE_H264, EncodeH264 = ENCODE_H264_EXT RequiresOneOf([ - RequiresAllOf([DeviceExtension(khr_video_decode_queue)]), - ]), + RequiresAllOf([DeviceExtension(khr_video_encode_queue)]), + RequiresAllOf([DeviceExtension(ext_video_encode_h264)])] + ), /// Specifies support for H.265 video encode operations - ENCODE_H265 = ENCODE_H265_EXT + ENCODE_H265, EncodeH265 = ENCODE_H265_EXT RequiresOneOf([ - RequiresAllOf([DeviceExtension(khr_video_decode_queue)]), - ]), + RequiresAllOf([DeviceExtension(khr_video_encode_queue)]), + RequiresAllOf([DeviceExtension(ext_video_encode_h265)])] + ), /// Specifies support for H.264 video decode operations - DECODE_H264 = DECODE_H264 + DECODE_H264, DecodeH264 = DECODE_H264 RequiresOneOf([ RequiresAllOf([DeviceExtension(khr_video_decode_queue)]), - ]), + RequiresAllOf([DeviceExtension(khr_video_decode_h264)])] + ), /// Specifies support for H.265 video decode operations - DECODE_H265 = DECODE_H265 + DECODE_H265, DecodeH265 = DECODE_H265 RequiresOneOf([ - RequiresAllOf([DeviceExtension(khr_video_decode_queue)]), - ]), + RequiresAllOf([DeviceExtension(khr_video_encode_queue)]), + RequiresAllOf([DeviceExtension(khr_video_decode_h265)])] + ), +} + +vulkan_bitflags_enum! { + #[non_exhaustive] + + VideoChromaSubsamplings, + + VideoChromaSubsampling, + + = VideoChromaSubsamplingFlagsKHR(u32); + + /// Specifies that the format is monochrome. + MONOCHROME, Monochrome = MONOCHROME, + /// Specified that the format is 4:2:0 chroma subsampled, i.e. the two + /// chroma components are sampled horizontally and vertically at half the + /// sample rate of the luma component. + TYPE_420, Type420 = TYPE_420, + /// The format is 4:2:2 chroma subsampled, i.e. the two chroma components + /// are sampled horizontally at half the sample rate of luma component. + TYPE_422, Type422 = TYPE_422, + /// The format is 4:4:4 chroma sampled, i.e. all three components of the + /// Y′CBCR format are sampled at the same rate, thus there is no chroma + /// subsampling. + TYPE_444, Type444 = TYPE_444, +} + +vulkan_bitflags_enum! { + #[non_exhaustive] + + VideoComponentBitDepths, + + VideoComponentBitDepth, + + = VideoComponentBitDepthFlagsKHR(u32); + + /// Specifies a component bit depth of 8 bits. + TYPE_8, Type8 = TYPE_8, + /// Specifies a component bit depth of 10 bits. + TYPE_10, Type10 = TYPE_10, + /// Specifies a component bit depth of 12 bits. + TYPE_12, Type12 = TYPE_12, +} + +vulkan_bitflags! { + #[non_exhaustive] + + VideoCapabilityFlags = VideoCapabilityFlagsKHR(u32); + + PROTECTED_CONTENT = PROTECTED_CONTENT, + SEPARATE_REFERENCE_IMAGES = SEPARATE_REFERENCE_IMAGES, +} + +vulkan_bitflags! { + #[non_exhaustive] + + VideoDecodeCapabilityFlags = VideoDecodeCapabilityFlagsKHR(u32); + + DPB_AND_OUTPUT_COINCIDE = DPB_AND_OUTPUT_COINCIDE, + DPB_AND_OUTPUT_DISTINCT = DPB_AND_OUTPUT_DISTINCT, +} + +vulkan_bitflags! { + #[non_exhaustive] + + VideoDecodeH264PictureLayoutFlags = VideoDecodeH264PictureLayoutFlagsKHR(u32); + + PROGRESSIVE = PROGRESSIVE, + INTERLACED_INTERLEAVED_LINES_BIT_KHR = INTERLACED_INTERLEAVED_LINES, + INTERLACED_SEPARATE_PLANES_BIT_KHR = INTERLACED_SEPARATE_PLANES, +} + +#[derive(Clone, Debug)] +pub struct ProfileInfo { + pub video_codec_operation: VideoCodecOperation, + pub chroma_subsampling: VideoChromaSubsampling, + pub luma_bit_depth: VideoComponentBitDepth, + pub chroma_bit_depth: Option, + pub codec_profile_info: DecodeProfileInfo, + pub _ne: crate::NonExhaustive, +} + +impl ProfileInfo { + pub(crate) fn validate(&self) -> Result<(), Box> { + let &Self { + chroma_subsampling, + chroma_bit_depth, + _ne, + .. + } = self; + + if !matches!(chroma_subsampling, VideoChromaSubsampling::Monochrome) + && chroma_bit_depth.is_none() + { + return Err(Box::new(ValidationError { + context: "chroma_bit_depth".into(), + problem: "is `Invalid`".into(), + vuids: &["VUID-VkVideoProfileInfoKHR-chromaSubsampling-07015"], + ..Default::default() + })); + } + + Ok(()) + } +} + +impl Default for ProfileInfo { + fn default() -> Self { + Self { + video_codec_operation: VideoCodecOperation::EncodeH265, + chroma_subsampling: VideoChromaSubsampling::Monochrome, + luma_bit_depth: VideoComponentBitDepth::Type8, + chroma_bit_depth: Some(VideoComponentBitDepth::Type8), + codec_profile_info: DecodeProfileInfo::H264(H264ProfileInfo { + std_profile_idc: 66, + picture_layout: VideoDecodeH264PictureLayoutFlags::PROGRESSIVE, + _ne: crate::NonExhaustive(()), + }), + _ne: crate::NonExhaustive(()), + } + } +} + +#[derive(Clone, Debug)] +pub struct H264ProfileInfo { + pub std_profile_idc: StdVideoH264ProfileIdc, + pub picture_layout: VideoDecodeH264PictureLayoutFlags, + pub _ne: crate::NonExhaustive, +} + +impl Default for H264ProfileInfo { + fn default() -> Self { + Self { + std_profile_idc: Default::default(), + picture_layout: Default::default(), + _ne: crate::NonExhaustive(()), + } + } +} + +#[derive(Clone, Debug)] +pub enum DecodeProfileInfo { + H264(H264ProfileInfo), +} + +#[derive(Clone, Debug)] +pub struct VideoCapabilities { + pub flags: VideoCapabilityFlags, + pub min_bitstream_buffer_offset_alignment: DeviceSize, + pub min_bitstream_buffer_size_alignment: DeviceSize, + pub picture_access_granularity: [u32; 2], + pub min_coded_extent: [u32; 2], + pub max_coded_extent: [u32; 2], + pub max_dpb_slots: u32, + pub max_active_reference_pictures: u32, + pub std_header_version: ExtensionProperties, + pub decode_capabilities: VideoDecodeCapabilities, + pub _ne: crate::NonExhaustive, +} + +#[derive(Clone, Debug)] +pub struct VideoDecodeCapabilities { + pub flags: VideoDecodeCapabilityFlags, + pub codec_capabilities: VideoDecodeCodecCapabilities, + pub _ne: crate::NonExhaustive, +} + +#[derive(Clone, Debug)] +#[non_exhaustive] +pub enum VideoDecodeCodecCapabilities { + H264(VideoDecodeH264Capabilities), + /* todo */ +} + +#[derive(Clone, Debug)] +pub struct VideoDecodeH264Capabilities { + pub max_level_idc: StdVideoH264LevelIdc, + pub field_offset_granularity: [i32; 2], + pub _ne: crate::NonExhaustive, +} + +pub struct ProfileListInfo { + pub profiles: Vec, + pub _ne: crate::NonExhaustive, +} + +pub struct VideoFormatInfo { + pub image_usage: ImageUsage, + pub profile_list_info: ProfileListInfo, +} + +impl VideoFormatInfo { + pub(crate) fn validate(&self) -> Result<(), Box> { + Ok(()) + } +} + +pub struct VideoFormatProperties { + format: Format, + component_mapping: ComponentMapping, + image_create_flags: ImageCreateFlags, + image_type: ImageType, + image_tiling: ImageTiling, + image_usage_flags: ImageUsage, }