Skip to main content

vk_video/device/
caps.rs

1use std::ffi::c_void;
2
3use ash::vk;
4
5use crate::VulkanDecoderError;
6use crate::VulkanInitError;
7use crate::parameters::H264Profile;
8use crate::wrappers::*;
9
10pub(crate) fn query_video_format_properties<'a>(
11    device: vk::PhysicalDevice,
12    video_queue_instance_ext: &ash::khr::video_queue::Instance,
13    profile_info: &vk::VideoProfileInfoKHR<'_>,
14    image_usage: vk::ImageUsageFlags,
15) -> Result<Vec<vk::VideoFormatPropertiesKHR<'a>>, VulkanInitError> {
16    let mut profile_list_info =
17        vk::VideoProfileListInfoKHR::default().profiles(std::slice::from_ref(profile_info));
18
19    let format_info = vk::PhysicalDeviceVideoFormatInfoKHR::default()
20        .image_usage(image_usage)
21        .push_next(&mut profile_list_info);
22
23    let mut format_info_length = 0;
24
25    unsafe {
26        (video_queue_instance_ext
27            .fp()
28            .get_physical_device_video_format_properties_khr)(
29            device,
30            &format_info,
31            &mut format_info_length,
32            std::ptr::null_mut(),
33        )
34        .result()?;
35    }
36
37    let mut format_properties =
38        vec![vk::VideoFormatPropertiesKHR::default(); format_info_length as usize];
39
40    unsafe {
41        (video_queue_instance_ext
42            .fp()
43            .get_physical_device_video_format_properties_khr)(
44            device,
45            &format_info,
46            &mut format_info_length,
47            format_properties.as_mut_ptr(),
48        )
49        .result()?;
50    }
51
52    Ok(format_properties)
53}
54
55/// The device capabilities for encoding
56#[derive(Debug, Clone, Copy)]
57pub struct EncodeCapabilities {
58    pub h264: Option<EncodeH264Capabilities>,
59}
60
61/// The device capabilities for H264 encoding.
62///
63/// See [`H264Profile`] for information about what profiles are.
64#[derive(Debug, Clone, Copy)]
65pub struct EncodeH264Capabilities {
66    pub baseline_profile: Option<EncodeH264ProfileCapabilities>,
67    pub main_profile: Option<EncodeH264ProfileCapabilities>,
68    pub high_profile: Option<EncodeH264ProfileCapabilities>,
69}
70
71/// The device capabilities for H264 encoding in a specific profile
72#[derive(Debug, Clone, Copy)]
73pub struct EncodeH264ProfileCapabilities {
74    /// The minimum width of the coded image
75    pub min_width: u32,
76    /// The maximum width of the coded image
77    pub max_width: u32,
78    /// The minimum height of the coded image
79    pub min_height: u32,
80    /// The maximum height of the coded image
81    pub max_height: u32,
82    /// The supported rate control modes in bitflag form
83    pub supported_rate_control: vk::VideoEncodeRateControlModeFlagsKHR,
84    /// Maximum number of back references a P-frame can have
85    pub max_references: u32,
86    /// The count of [Vulkan Video encode quality levels](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#encode-quality-level)
87    pub quality_levels: u32,
88}
89
90#[derive(Debug, Clone)]
91pub(crate) struct NativeEncodeCapabilities {
92    pub(crate) baseline: Option<NativeEncodeProfileCapabilities>,
93    pub(crate) main: Option<NativeEncodeProfileCapabilities>,
94    pub(crate) high: Option<NativeEncodeProfileCapabilities>,
95}
96
97impl NativeEncodeCapabilities {
98    pub(crate) fn user_facing(&self) -> EncodeH264Capabilities {
99        EncodeH264Capabilities {
100            baseline_profile: self
101                .baseline
102                .as_ref()
103                .map(NativeEncodeProfileCapabilities::user_facing),
104            main_profile: self
105                .main
106                .as_ref()
107                .map(NativeEncodeProfileCapabilities::user_facing),
108            high_profile: self
109                .high
110                .as_ref()
111                .map(NativeEncodeProfileCapabilities::user_facing),
112        }
113    }
114
115    pub(crate) fn query(instance: &Instance, device: vk::PhysicalDevice) -> Self {
116        let baseline = NativeEncodeProfileCapabilities::query(
117            instance,
118            device,
119            vk::native::StdVideoH264ProfileIdc_STD_VIDEO_H264_PROFILE_IDC_BASELINE,
120        )
121        .ok();
122        let main = NativeEncodeProfileCapabilities::query(
123            instance,
124            device,
125            vk::native::StdVideoH264ProfileIdc_STD_VIDEO_H264_PROFILE_IDC_MAIN,
126        )
127        .ok();
128        let high = NativeEncodeProfileCapabilities::query(
129            instance,
130            device,
131            vk::native::StdVideoH264ProfileIdc_STD_VIDEO_H264_PROFILE_IDC_HIGH,
132        )
133        .ok();
134
135        Self {
136            baseline,
137            main,
138            high,
139        }
140    }
141
142    pub(crate) fn profile(&self, profile: H264Profile) -> Option<&NativeEncodeProfileCapabilities> {
143        match profile {
144            H264Profile::Baseline => self.baseline.as_ref(),
145            H264Profile::Main => self.main.as_ref(),
146            H264Profile::High => self.high.as_ref(),
147        }
148    }
149
150    pub(crate) fn max_profile(&self) -> H264Profile {
151        if self.high.is_some() {
152            H264Profile::High
153        } else if self.main.is_some() {
154            H264Profile::Main
155        } else {
156            H264Profile::Baseline
157        }
158    }
159}
160
161#[derive(Debug, Clone)]
162#[allow(dead_code)]
163pub(crate) struct NativeEncodeProfileCapabilities {
164    pub(crate) video_capabilities: vk::VideoCapabilitiesKHR<'static>,
165    pub(crate) encode_capabilities: vk::VideoEncodeCapabilitiesKHR<'static>,
166    pub(crate) h264_encode_capabilities: vk::VideoEncodeH264CapabilitiesKHR<'static>,
167    pub(crate) encode_dpb_properties: Vec<vk::VideoFormatPropertiesKHR<'static>>,
168    pub(crate) encode_src_properties: Vec<vk::VideoFormatPropertiesKHR<'static>>,
169    pub(crate) quality_level_properties: Vec<NativeEncodeQualityLevelProperties>,
170}
171
172impl NativeEncodeProfileCapabilities {
173    fn user_facing(&self) -> EncodeH264ProfileCapabilities {
174        EncodeH264ProfileCapabilities {
175            min_width: self.video_capabilities.min_coded_extent.width,
176            max_width: self.video_capabilities.max_coded_extent.width,
177            min_height: self.video_capabilities.min_coded_extent.height,
178            max_height: self.video_capabilities.max_coded_extent.height,
179            supported_rate_control: self.encode_capabilities.rate_control_modes,
180            max_references: self
181                .h264_encode_capabilities
182                .max_p_picture_l0_reference_count,
183            quality_levels: self.encode_capabilities.max_quality_levels,
184        }
185    }
186
187    fn query(
188        instance: &Instance,
189        device: vk::PhysicalDevice,
190        profile: vk::native::StdVideoH264ProfileIdc,
191    ) -> Result<Self, VulkanInitError> {
192        let mut h264_encode_profile_info =
193            vk::VideoEncodeH264ProfileInfoKHR::default().std_profile_idc(profile);
194
195        let encode_profile_info = vk::VideoProfileInfoKHR::default()
196            .video_codec_operation(vk::VideoCodecOperationFlagsKHR::ENCODE_H264)
197            .chroma_subsampling(vk::VideoChromaSubsamplingFlagsKHR::TYPE_420)
198            .luma_bit_depth(vk::VideoComponentBitDepthFlagsKHR::TYPE_8)
199            .chroma_bit_depth(vk::VideoComponentBitDepthFlagsKHR::TYPE_8)
200            .push_next(&mut h264_encode_profile_info);
201
202        let encode_dpb_properties = query_video_format_properties(
203            device,
204            &instance.video_queue_instance_ext,
205            &encode_profile_info,
206            vk::ImageUsageFlags::VIDEO_ENCODE_DPB_KHR,
207        )?;
208
209        let encode_src_properties = query_video_format_properties(
210            device,
211            &instance.video_queue_instance_ext,
212            &encode_profile_info,
213            vk::ImageUsageFlags::VIDEO_ENCODE_SRC_KHR,
214        )?;
215
216        let mut h264_encode_caps = vk::VideoEncodeH264CapabilitiesKHR::default();
217        let mut encode_caps = vk::VideoEncodeCapabilitiesKHR {
218            p_next: (&mut h264_encode_caps as *mut _) as *mut c_void,
219            ..Default::default()
220        };
221        let mut caps = vk::VideoCapabilitiesKHR::default().push_next(&mut encode_caps);
222
223        unsafe {
224            (instance
225                .video_queue_instance_ext
226                .fp()
227                .get_physical_device_video_capabilities_khr)(
228                device,
229                &encode_profile_info,
230                &mut caps,
231            )
232            .result()?;
233        }
234
235        let video_capabilities = vk::VideoCapabilitiesKHR::default()
236            .flags(caps.flags)
237            .min_bitstream_buffer_offset_alignment(caps.min_bitstream_buffer_offset_alignment)
238            .min_bitstream_buffer_size_alignment(caps.min_bitstream_buffer_size_alignment)
239            .picture_access_granularity(caps.picture_access_granularity)
240            .min_coded_extent(caps.min_coded_extent)
241            .max_coded_extent(caps.max_coded_extent)
242            .max_dpb_slots(caps.max_dpb_slots)
243            .max_active_reference_pictures(caps.max_active_reference_pictures)
244            .std_header_version(caps.std_header_version);
245
246        let encode_capabilities = vk::VideoEncodeCapabilitiesKHR::default()
247            .flags(encode_caps.flags)
248            .rate_control_modes(encode_caps.rate_control_modes)
249            .max_rate_control_layers(encode_caps.max_rate_control_layers)
250            .max_bitrate(encode_caps.max_bitrate)
251            .max_quality_levels(encode_caps.max_quality_levels)
252            .encode_input_picture_granularity(encode_caps.encode_input_picture_granularity)
253            .supported_encode_feedback_flags(encode_caps.supported_encode_feedback_flags);
254
255        let h264_encode_capabilities = vk::VideoEncodeH264CapabilitiesKHR::default()
256            .flags(h264_encode_caps.flags)
257            .max_level_idc(h264_encode_caps.max_level_idc)
258            .max_slice_count(h264_encode_caps.max_slice_count)
259            .max_p_picture_l0_reference_count(h264_encode_caps.max_p_picture_l0_reference_count)
260            .max_b_picture_l0_reference_count(h264_encode_caps.max_b_picture_l0_reference_count)
261            .max_l1_reference_count(h264_encode_caps.max_l1_reference_count)
262            .max_temporal_layer_count(h264_encode_caps.max_temporal_layer_count)
263            .expect_dyadic_temporal_layer_pattern(
264                h264_encode_caps.expect_dyadic_temporal_layer_pattern != 0,
265            )
266            .min_qp(h264_encode_caps.min_qp)
267            .max_qp(h264_encode_caps.max_qp)
268            .prefers_gop_remaining_frames(h264_encode_caps.prefers_gop_remaining_frames != 0)
269            .requires_gop_remaining_frames(h264_encode_caps.requires_gop_remaining_frames != 0)
270            .std_syntax_flags(h264_encode_caps.std_syntax_flags);
271
272        let mut quality_level_properties =
273            Vec::with_capacity(encode_capabilities.max_quality_levels as usize);
274
275        for i in 0..encode_capabilities.max_quality_levels {
276            if let Ok(qlp) =
277                NativeEncodeQualityLevelProperties::query(instance, device, &encode_profile_info, i)
278            {
279                quality_level_properties.push(qlp);
280            }
281        }
282
283        Ok(Self {
284            video_capabilities,
285            encode_capabilities,
286            h264_encode_capabilities,
287            encode_dpb_properties,
288            encode_src_properties,
289            quality_level_properties,
290        })
291    }
292}
293
294#[derive(Debug, Clone)]
295pub(crate) struct NativeEncodeQualityLevelProperties {
296    pub(crate) quality_level_properties: vk::VideoEncodeQualityLevelPropertiesKHR<'static>,
297    pub(crate) h264_quality_level_properties: vk::VideoEncodeH264QualityLevelPropertiesKHR<'static>,
298}
299
300impl NativeEncodeQualityLevelProperties {
301    fn query(
302        instance: &Instance,
303        device: vk::PhysicalDevice,
304        profile_info: &vk::VideoProfileInfoKHR<'_>,
305        quality_level: u32,
306    ) -> Result<Self, VulkanInitError> {
307        let quality_level_info = vk::PhysicalDeviceVideoEncodeQualityLevelInfoKHR::default()
308            .video_profile(profile_info)
309            .quality_level(quality_level);
310
311        let mut h264_qlp = vk::VideoEncodeH264QualityLevelPropertiesKHR::default();
312        let mut qlp = vk::VideoEncodeQualityLevelPropertiesKHR::default().push_next(&mut h264_qlp);
313
314        unsafe {
315            (instance
316                .video_encode_queue_instance_ext
317                .fp()
318                .get_physical_device_video_encode_quality_level_properties_khr)(
319                device,
320                &quality_level_info,
321                &mut qlp,
322            )
323            .result()?;
324        }
325
326        let quality_level_properties = vk::VideoEncodeQualityLevelPropertiesKHR::default()
327            .preferred_rate_control_mode(qlp.preferred_rate_control_mode)
328            .preferred_rate_control_layer_count(qlp.preferred_rate_control_layer_count);
329
330        let h264_quality_level_properties = vk::VideoEncodeH264QualityLevelPropertiesKHR::default()
331            .preferred_rate_control_flags(h264_qlp.preferred_rate_control_flags)
332            .preferred_gop_frame_count(h264_qlp.preferred_gop_frame_count)
333            .preferred_idr_period(h264_qlp.preferred_idr_period)
334            .preferred_consecutive_b_frame_count(h264_qlp.preferred_consecutive_b_frame_count)
335            .preferred_temporal_layer_count(h264_qlp.preferred_temporal_layer_count)
336            .preferred_constant_qp(h264_qlp.preferred_constant_qp)
337            .preferred_max_l0_reference_count(h264_qlp.preferred_max_l0_reference_count)
338            .preferred_max_l1_reference_count(h264_qlp.preferred_max_l1_reference_count)
339            .preferred_std_entropy_coding_mode_flag(
340                h264_qlp.preferred_std_entropy_coding_mode_flag != 0,
341            );
342
343        Ok(Self {
344            quality_level_properties,
345            h264_quality_level_properties,
346        })
347    }
348
349    pub(crate) fn zeroed(&self) -> bool {
350        // this is hideous
351        self.quality_level_properties
352            .preferred_rate_control_mode
353            .as_raw()
354            == 0
355            && self
356                .quality_level_properties
357                .preferred_rate_control_layer_count
358                == 0
359            && self
360                .h264_quality_level_properties
361                .preferred_rate_control_flags
362                .as_raw()
363                == 0
364            && self.h264_quality_level_properties.preferred_gop_frame_count == 0
365            && self.h264_quality_level_properties.preferred_idr_period == 0
366            && self
367                .h264_quality_level_properties
368                .preferred_consecutive_b_frame_count
369                == 0
370            && self
371                .h264_quality_level_properties
372                .preferred_temporal_layer_count
373                == 0
374            && self
375                .h264_quality_level_properties
376                .preferred_constant_qp
377                .qp_i
378                == 0
379            && self
380                .h264_quality_level_properties
381                .preferred_constant_qp
382                .qp_p
383                == 0
384            && self
385                .h264_quality_level_properties
386                .preferred_constant_qp
387                .qp_b
388                == 0
389            && self
390                .h264_quality_level_properties
391                .preferred_max_l0_reference_count
392                == 0
393            && self
394                .h264_quality_level_properties
395                .preferred_max_l1_reference_count
396                == 0
397            && self
398                .h264_quality_level_properties
399                .preferred_std_entropy_coding_mode_flag
400                == 0
401    }
402}
403
404/// The device capabilities for decoding
405#[derive(Debug, Clone, Copy)]
406pub struct DecodeCapabilities {
407    pub h264: Option<DecodeH264Capabilities>,
408}
409
410/// The device capabilities for H264 decoding.
411///
412/// See [`H264Profile`] for information about what profiles are.
413#[derive(Debug, Clone, Copy)]
414pub struct DecodeH264Capabilities {
415    pub baseline_profile: Option<DecodeH264ProfileCapabilities>,
416    pub main_profile: Option<DecodeH264ProfileCapabilities>,
417    pub high_profile: Option<DecodeH264ProfileCapabilities>,
418}
419
420/// The device capabilities for H264 decoding in a specific profile
421#[derive(Debug, Clone, Copy)]
422pub struct DecodeH264ProfileCapabilities {
423    /// The minimum width of the coded image
424    pub min_width: u32,
425    /// The maximum width of the coded image
426    pub max_width: u32,
427    /// The minimum height of the coded image
428    pub min_height: u32,
429    /// The maximum height of the coded image
430    pub max_height: u32,
431    /// The maximum H264 level
432    pub max_level_idc: u8,
433}
434
435#[derive(Debug, Clone)]
436pub(crate) struct NativeDecodeCapabilities {
437    pub(crate) baseline: Option<NativeDecodeProfileCapabilities>,
438    pub(crate) main: Option<NativeDecodeProfileCapabilities>,
439    pub(crate) high: Option<NativeDecodeProfileCapabilities>,
440}
441
442impl NativeDecodeCapabilities {
443    pub(crate) fn user_facing(&self) -> DecodeH264Capabilities {
444        DecodeH264Capabilities {
445            baseline_profile: self
446                .baseline
447                .as_ref()
448                .and_then(|profile| profile.user_facing().ok()),
449            main_profile: self
450                .main
451                .as_ref()
452                .and_then(|profile| profile.user_facing().ok()),
453            high_profile: self
454                .high
455                .as_ref()
456                .and_then(|profile| profile.user_facing().ok()),
457        }
458    }
459
460    pub(crate) fn query(instance: &Instance, device: vk::PhysicalDevice) -> Self {
461        let baseline = NativeDecodeProfileCapabilities::query(
462            instance,
463            device,
464            vk::native::StdVideoH264ProfileIdc_STD_VIDEO_H264_PROFILE_IDC_BASELINE,
465        )
466        .ok();
467        let main = NativeDecodeProfileCapabilities::query(
468            instance,
469            device,
470            vk::native::StdVideoH264ProfileIdc_STD_VIDEO_H264_PROFILE_IDC_MAIN,
471        )
472        .ok();
473        let high = NativeDecodeProfileCapabilities::query(
474            instance,
475            device,
476            vk::native::StdVideoH264ProfileIdc_STD_VIDEO_H264_PROFILE_IDC_HIGH,
477        )
478        .ok();
479
480        Self {
481            baseline,
482            main,
483            high,
484        }
485    }
486
487    pub(crate) fn profile(&self, profile: H264Profile) -> Option<&NativeDecodeProfileCapabilities> {
488        match profile {
489            H264Profile::Baseline => self.baseline.as_ref(),
490            H264Profile::Main => self.main.as_ref(),
491            H264Profile::High => self.high.as_ref(),
492        }
493    }
494
495    pub(crate) fn max_profile(&self) -> H264Profile {
496        if self.high.is_some() {
497            H264Profile::High
498        } else if self.main.is_some() {
499            H264Profile::Main
500        } else {
501            H264Profile::Baseline
502        }
503    }
504}
505
506#[derive(Debug, Clone)]
507pub(crate) struct NativeDecodeProfileCapabilities {
508    pub(crate) video_capabilities: vk::VideoCapabilitiesKHR<'static>,
509    #[allow(dead_code)]
510    pub(crate) decode_capabilities: vk::VideoDecodeCapabilitiesKHR<'static>,
511    pub(crate) h264_decode_capabilities: vk::VideoDecodeH264CapabilitiesKHR<'static>,
512    pub(crate) h264_dpb_format_properties: vk::VideoFormatPropertiesKHR<'static>,
513    pub(crate) h264_dst_format_properties: Option<vk::VideoFormatPropertiesKHR<'static>>,
514}
515
516impl NativeDecodeProfileCapabilities {
517    pub(crate) fn user_facing(&self) -> Result<DecodeH264ProfileCapabilities, VulkanDecoderError> {
518        Ok(DecodeH264ProfileCapabilities {
519            min_width: self.video_capabilities.min_coded_extent.width,
520            max_width: self.video_capabilities.max_coded_extent.width,
521            min_height: self.video_capabilities.min_coded_extent.height,
522            max_height: self.video_capabilities.max_coded_extent.height,
523            max_level_idc: vk_to_h264_level_idc(self.h264_decode_capabilities.max_level_idc)?,
524        })
525    }
526
527    pub(crate) fn query(
528        instance: &Instance,
529        device: vk::PhysicalDevice,
530        profile: vk::native::StdVideoH264ProfileIdc,
531    ) -> Result<Self, VulkanInitError> {
532        let mut h264_decode_profile_info = vk::VideoDecodeH264ProfileInfoKHR::default()
533            .picture_layout(vk::VideoDecodeH264PictureLayoutFlagsKHR::PROGRESSIVE)
534            .std_profile_idc(profile);
535
536        let decode_profile_info = vk::VideoProfileInfoKHR::default()
537            .video_codec_operation(vk::VideoCodecOperationFlagsKHR::DECODE_H264)
538            .chroma_subsampling(vk::VideoChromaSubsamplingFlagsKHR::TYPE_420)
539            .luma_bit_depth(vk::VideoComponentBitDepthFlagsKHR::TYPE_8)
540            .chroma_bit_depth(vk::VideoComponentBitDepthFlagsKHR::TYPE_8)
541            .push_next(&mut h264_decode_profile_info);
542
543        let mut h264_decode_caps = vk::VideoDecodeH264CapabilitiesKHR::default();
544        let mut decode_caps = vk::VideoDecodeCapabilitiesKHR {
545            p_next: (&mut h264_decode_caps as *mut _) as *mut c_void, // why does this not have `.push_next()`? wtf
546            ..Default::default()
547        };
548
549        let mut caps = vk::VideoCapabilitiesKHR::default().push_next(&mut decode_caps);
550
551        unsafe {
552            (instance
553                .video_queue_instance_ext
554                .fp()
555                .get_physical_device_video_capabilities_khr)(
556                device,
557                &decode_profile_info,
558                &mut caps,
559            )
560            .result()?
561        };
562
563        let video_capabilities = vk::VideoCapabilitiesKHR::default()
564            .flags(caps.flags)
565            .min_bitstream_buffer_size_alignment(caps.min_bitstream_buffer_size_alignment)
566            .min_bitstream_buffer_offset_alignment(caps.min_bitstream_buffer_offset_alignment)
567            .picture_access_granularity(caps.picture_access_granularity)
568            .min_coded_extent(caps.min_coded_extent)
569            .max_coded_extent(caps.max_coded_extent)
570            .max_dpb_slots(caps.max_dpb_slots)
571            .max_active_reference_pictures(caps.max_active_reference_pictures)
572            .std_header_version(caps.std_header_version);
573
574        let decode_capabilities =
575            vk::VideoDecodeCapabilitiesKHR::default().flags(decode_caps.flags);
576
577        let h264_decode_capabilities = vk::VideoDecodeH264CapabilitiesKHR::default()
578            .max_level_idc(h264_decode_caps.max_level_idc)
579            .field_offset_granularity(h264_decode_caps.field_offset_granularity);
580
581        let flags = decode_caps.flags;
582
583        let h264_dpb_format_properties =
584            if flags.contains(vk::VideoDecodeCapabilityFlagsKHR::DPB_AND_OUTPUT_COINCIDE) {
585                query_video_format_properties(
586                    device,
587                    &instance.video_queue_instance_ext,
588                    &decode_profile_info,
589                    vk::ImageUsageFlags::VIDEO_DECODE_DST_KHR
590                        | vk::ImageUsageFlags::VIDEO_DECODE_DPB_KHR
591                        | vk::ImageUsageFlags::TRANSFER_SRC,
592                )?
593            } else {
594                query_video_format_properties(
595                    device,
596                    &instance.video_queue_instance_ext,
597                    &decode_profile_info,
598                    vk::ImageUsageFlags::VIDEO_DECODE_DPB_KHR,
599                )?
600            };
601
602        let h264_dst_format_properties =
603            if flags.contains(vk::VideoDecodeCapabilityFlagsKHR::DPB_AND_OUTPUT_COINCIDE) {
604                None
605            } else {
606                Some(query_video_format_properties(
607                    device,
608                    &instance.video_queue_instance_ext,
609                    &decode_profile_info,
610                    vk::ImageUsageFlags::VIDEO_DECODE_DST_KHR | vk::ImageUsageFlags::TRANSFER_SRC,
611                )?)
612            };
613
614        let h264_dpb_format_properties = match h264_dpb_format_properties
615            .into_iter()
616            .find(|f| f.format == vk::Format::G8_B8R8_2PLANE_420_UNORM)
617        {
618            Some(f) => f,
619            None => return Err(VulkanInitError::NoNV12ProfileSupport),
620        };
621
622        let h264_dst_format_properties = match h264_dst_format_properties {
623            Some(format_properties) => match format_properties
624                .into_iter()
625                .find(|f| f.format == vk::Format::G8_B8R8_2PLANE_420_UNORM)
626            {
627                Some(f) => Some(f),
628                None => return Err(VulkanInitError::NoNV12ProfileSupport),
629            },
630            None => None,
631        };
632
633        Ok(Self {
634            video_capabilities,
635            decode_capabilities,
636            h264_decode_capabilities,
637            h264_dpb_format_properties,
638            h264_dst_format_properties,
639        })
640    }
641}