Skip to main content

vk_video/
device.rs

1use std::ffi::CStr;
2use std::num::NonZeroU32;
3use std::ops::Deref;
4use std::sync::Arc;
5
6use ash::vk;
7
8use crate::adapter::VulkanAdapter;
9use crate::capabilities::AdapterInfo;
10use crate::device::caps::{
11    DecodeCapabilities, EncodeCapabilities, NativeDecodeCapabilities,
12    NativeDecodeProfileCapabilities, NativeEncodeCapabilities,
13};
14use crate::device::queues::{Queue, QueueIndex, Queues, VideoQueues};
15use crate::parameters::{
16    EncoderContentFlags, EncoderTuningMode, EncoderUsageFlags, H264Profile, RateControl,
17};
18use crate::parser::{h264::H264Parser, reference_manager::ReferenceContext};
19use crate::vulkan_decoder::{FrameSorter, ImageModifiers, VulkanDecoder};
20use crate::vulkan_encoder::{FullEncoderParameters, VulkanEncoder};
21#[cfg(feature = "transcoder")]
22use crate::vulkan_transcoder::TranscoderParameters;
23use crate::{
24    BytesDecoder, BytesEncoder, DecoderError, RawFrameData, VulkanDecoderError, VulkanEncoderError,
25    VulkanInitError, VulkanInstance, wrappers::*,
26};
27
28pub(crate) mod caps;
29pub(crate) mod queues;
30
31#[cfg(feature = "wgpu")]
32mod wgpu_api;
33#[cfg(feature = "wgpu")]
34pub(crate) use wgpu_api::*;
35
36pub(crate) const REQUIRED_EXTENSIONS: &[&CStr] =
37    &[vk::KHR_VIDEO_QUEUE_NAME, vk::KHR_VIDEO_MAINTENANCE1_NAME];
38
39pub(crate) const DECODE_EXTENSIONS: &[&CStr] = &[
40    vk::KHR_VIDEO_DECODE_QUEUE_NAME,
41    vk::KHR_VIDEO_DECODE_H264_NAME,
42];
43
44pub(crate) const ENCODE_EXTENSIONS: &[&CStr] = &[
45    vk::KHR_VIDEO_ENCODE_QUEUE_NAME,
46    vk::KHR_VIDEO_ENCODE_H264_NAME,
47];
48
49/// Describes a [`VulkanDevice`].
50/// Used by [`VulkanAdapter::create_device`]
51#[derive(Default, Clone)]
52pub struct VulkanDeviceDescriptor {
53    #[cfg(feature = "wgpu")]
54    pub wgpu_features: wgpu::Features,
55
56    #[cfg(feature = "wgpu")]
57    pub wgpu_experimental_features: wgpu::ExperimentalFeatures,
58
59    #[cfg(feature = "wgpu")]
60    pub wgpu_limits: wgpu::Limits,
61}
62
63/// A fraction
64#[derive(Debug, Clone, Copy)]
65pub struct Rational {
66    pub numerator: u32,
67    pub denominator: NonZeroU32,
68}
69
70impl From<u32> for Rational {
71    fn from(value: u32) -> Self {
72        Rational {
73            numerator: value,
74            denominator: std::num::NonZeroU32::new(1).unwrap(),
75        }
76    }
77}
78
79/// An enum used to specify how the decoder should handle missing frames
80#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
81pub enum MissedFrameHandling {
82    /// When missed frames are detected, error on every subsequent frame that depends on them
83    /// (i. e. fail on every frame until an IDR frame arrives)
84    #[default]
85    Strict,
86
87    /// When missed frames are detected, try to decode later frames that depend on them anyway.
88    /// This can produce decoded frames with very visible artifacts.
89    Tolerant,
90}
91
92/// Parameters for decoder creation
93#[derive(Debug, Default, Clone, Copy)]
94pub struct DecoderParameters {
95    /// See [`MissedFrameHandling`] for description of different handling approaches.
96    ///
97    /// **Defaults to [`MissedFrameHandling::Strict`]**
98    pub missed_frame_handling: MissedFrameHandling,
99
100    /// A hint indicating what kind of content the decoder is going to be used for.
101    ///
102    /// Multiple flags can be combined using the `|` operator to indicate multiple usages.
103    pub usage_flags: crate::parameters::DecoderUsageFlags,
104}
105
106/// Things the encoder needs to know about the video
107#[derive(Debug, Clone, Copy)]
108pub struct VideoParameters {
109    pub width: NonZeroU32,
110    pub height: NonZeroU32,
111    /// The expected/approximate framerate of the encoded video
112    pub target_framerate: Rational,
113}
114
115#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
116pub enum ColorSpace {
117    #[default]
118    Unspecified,
119    BT709,
120    BT601Ntsc,
121    BT601Pal,
122}
123
124impl From<&h264_reader::nal::sps::SeqParameterSet> for ColorSpace {
125    fn from(sps: &h264_reader::nal::sps::SeqParameterSet) -> Self {
126        let Some(vui) = &sps.vui_parameters else {
127            return ColorSpace::Unspecified;
128        };
129        let Some(vst) = &vui.video_signal_type else {
130            return ColorSpace::Unspecified;
131        };
132        let Some(cd) = &vst.colour_description else {
133            return ColorSpace::Unspecified;
134        };
135
136        match (
137            cd.colour_primaries,
138            cd.transfer_characteristics,
139            cd.matrix_coefficients,
140        ) {
141            (1, 1, 1) => ColorSpace::BT709,
142            (6, 6, 6) => ColorSpace::BT601Ntsc,
143            (5, 6, 5) => ColorSpace::BT601Pal,
144            _ => ColorSpace::Unspecified,
145        }
146    }
147}
148
149/// Whether the video signal uses the full or limited range of sample values.
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub enum ColorRange {
152    /// Luma and chroma use the full [0, 255] range.
153    Full,
154    /// Luma is restricted to [16, 235] and chroma to [16, 240].
155    Limited,
156}
157
158impl From<&h264_reader::nal::sps::SeqParameterSet> for ColorRange {
159    fn from(sps: &h264_reader::nal::sps::SeqParameterSet) -> Self {
160        sps.vui_parameters
161            .as_ref()
162            .and_then(|v| v.video_signal_type.as_ref())
163            .map(|vst| {
164                if vst.video_full_range_flag {
165                    ColorRange::Full
166                } else {
167                    ColorRange::Limited
168                }
169            })
170            .unwrap_or(ColorRange::Limited)
171    }
172}
173
174/// Parameters that describe an encoded output.
175#[derive(Debug, Clone, Copy)]
176pub struct EncoderOutputParameters {
177    /// Number of frames between IDRs. If [`None`], this will be set to an encoder preferred value,
178    /// or, if the encoder doesn't provide a preferred value, to 30.
179    pub idr_period: Option<NonZeroU32>,
180    /// See [`RateControl`] for description of different rate control modes. The selected mode must
181    /// be supported by the device.
182    pub rate_control: RateControl,
183    /// Max number of references a P-frame can have. If [`None`], this value will be set
184    /// to the max value supported by the device.
185    pub max_references: Option<NonZeroU32>,
186    /// The profile must be supported by the device
187    pub profile: H264Profile,
188    /// The value must be less than
189    /// [`EncodeH264ProfileCapabilities::quality_levels`](crate::capabilities::EncodeH264ProfileCapabilities::quality_levels)
190    pub quality_level: u32,
191    /// A hint indicating what the encoded content is going to be used for.
192    ///
193    /// Multiple flags can be combined using the `|` operator to indicate multiple usages.
194    pub usage_flags: Option<EncoderUsageFlags>,
195    /// A hint indicating how to tune the encoder implementation.
196    pub tuning_mode: Option<EncoderTuningMode>,
197    /// A hint indicating what kind of content the encoder is going to be used for.
198    ///
199    /// Multiple flags can be combined using the `|` operator to indicate multiple usages.
200    pub content_flags: Option<EncoderContentFlags>,
201    /// Whether to prepend SPS/PPS NAL units inline before IDR frames.
202    /// If `false`, SPS/PPS can be retrieved separately using methods defined on the encoder.
203    /// If [`None`], defaults to `true`.
204    pub inline_stream_params: Option<bool>,
205    /// Color space of the encoded output.
206    /// If [`None`], defaults to [`ColorSpace::Unspecified`].
207    pub color_space: Option<ColorSpace>,
208    /// Color range of the encoded output.
209    /// If [`None`], defaults to [`ColorRange::Limited`].
210    pub color_range: Option<ColorRange>,
211}
212
213/// Parameters for encoder creation
214#[derive(Debug, Clone, Copy)]
215pub struct EncoderParameters {
216    pub input_parameters: VideoParameters,
217    pub output_parameters: EncoderOutputParameters,
218}
219
220/// Open connection to a coding-capable device. Also contains a [`wgpu::Device`], a [`wgpu::Queue`] and
221/// a [`wgpu::Adapter`].
222pub struct VulkanDevice {
223    #[cfg(feature = "wgpu")]
224    pub(crate) wgpu_ctx: WgpuContext,
225
226    pub(crate) _physical_device: vk::PhysicalDevice,
227    pub(crate) allocator: Arc<Allocator>,
228    pub(crate) queues: Queues,
229    pub(crate) native_decode_capabilities: Option<NativeDecodeCapabilities>,
230    pub(crate) native_encode_capabilities: Option<NativeEncodeCapabilities>,
231    pub(crate) adapter_info: AdapterInfo,
232    pub(crate) device: Arc<Device>,
233}
234
235impl VulkanDevice {
236    pub(crate) fn new(
237        instance: &VulkanInstance,
238        adapter: VulkanAdapter<'_>,
239        #[allow(unused)] descriptor: &VulkanDeviceDescriptor,
240    ) -> Result<Self, VulkanInitError> {
241        let mut required_extensions = REQUIRED_EXTENSIONS
242            .iter()
243            .copied()
244            .chain(match adapter.supports_decoding() {
245                true => DECODE_EXTENSIONS.iter().copied(),
246                false => [].iter().copied(),
247            })
248            .chain(match adapter.supports_encoding() {
249                true => ENCODE_EXTENSIONS.iter().copied(),
250                false => [].iter().copied(),
251            })
252            .collect::<Vec<_>>();
253
254        #[cfg(feature = "wgpu")]
255        append_wgpu_device_extensions(&adapter, descriptor.wgpu_features, &mut required_extensions);
256
257        #[cfg(not(feature = "wgpu"))]
258        required_extensions.push(ash::khr::timeline_semaphore::NAME);
259
260        let required_extensions_as_ptrs = required_extensions
261            .iter()
262            .map(|e| e.as_ptr())
263            .collect::<Vec<_>>();
264
265        let VulkanAdapter {
266            physical_device,
267            queue_indices,
268            decode_capabilities,
269            encode_capabilities,
270            info,
271            ..
272        } = adapter;
273
274        let queue_create_infos = queue_indices.queue_create_infos();
275        let queue_create_infos = queue_create_infos
276            .iter()
277            .map(|q| q.info)
278            .collect::<Vec<_>>();
279
280        let mut vk_synch_2_feature =
281            vk::PhysicalDeviceSynchronization2Features::default().synchronization2(true);
282        let mut vk_video_maintenance1_feature =
283            vk::PhysicalDeviceVideoMaintenance1FeaturesKHR::default().video_maintenance1(true);
284
285        let mut vk_descriptor_feature = vk::PhysicalDeviceDescriptorIndexingFeatures::default()
286            .descriptor_binding_partially_bound(true);
287
288        let device_create_info = vk::DeviceCreateInfo::default()
289            .queue_create_infos(&queue_create_infos)
290            .enabled_extension_names(&required_extensions_as_ptrs);
291
292        let device_create_info = device_create_info
293            .push_next(&mut vk_synch_2_feature)
294            .push_next(&mut vk_video_maintenance1_feature)
295            .push_next(&mut vk_descriptor_feature);
296
297        #[cfg(feature = "wgpu")]
298        let mut wgpu_physical_device_features = adapter
299            .wgpu_adapter
300            .adapter
301            .physical_device_features(&required_extensions, descriptor.wgpu_features);
302        #[cfg(feature = "wgpu")]
303        let device_create_info =
304            wgpu_physical_device_features.add_to_device_create(device_create_info);
305
306        #[cfg(not(feature = "wgpu"))]
307        let mut timeline_semaphore_feature =
308            vk::PhysicalDeviceTimelineSemaphoreFeatures::default().timeline_semaphore(true);
309        #[cfg(not(feature = "wgpu"))]
310        let device_create_info = device_create_info.push_next(&mut timeline_semaphore_feature);
311
312        let device = unsafe {
313            instance
314                .instance
315                .create_device(physical_device, &device_create_info, None)?
316        };
317
318        let video_queue_ext = ash::khr::video_queue::Device::new(&instance.instance, &device);
319        let video_decode_queue_ext =
320            ash::khr::video_decode_queue::Device::new(&instance.instance, &device);
321
322        let video_encode_queue_ext =
323            ash::khr::video_encode_queue::Device::new(&instance.instance, &device);
324
325        #[cfg(feature = "vk-validation")]
326        let debug_utils_ext = ash::ext::debug_utils::Device::new(&instance.instance, &device);
327
328        let device = Arc::new(Device {
329            device,
330            video_queue_ext,
331            video_decode_queue_ext,
332            video_encode_queue_ext,
333            #[cfg(feature = "vk-validation")]
334            debug_utils_ext,
335            _instance: instance.instance.clone(),
336        });
337
338        let h264_decode_queues =
339            queue_indices
340                .h264_decode
341                .as_ref()
342                .map_or(Vec::new(), |queue_family_index| {
343                    (0..queue_family_index.queue_count)
344                        .map(|idx| queue_from_device(device.clone(), queue_family_index, idx))
345                        .collect::<Vec<_>>()
346                });
347        let h264_encode_queues =
348            queue_indices
349                .h264_encode
350                .as_ref()
351                .map_or(Vec::new(), |queue_family_index| {
352                    (0..queue_family_index.queue_count)
353                        .map(|idx| queue_from_device(device.clone(), queue_family_index, idx))
354                        .collect::<Vec<_>>()
355                });
356        let transfer_queue = queue_from_device(device.clone(), &queue_indices.transfer, 0);
357        let compute_queue =
358            if queue_indices.compute.family_index == queue_indices.transfer.family_index {
359                if queue_indices.transfer.queue_count > 1 {
360                    queue_from_device(device.clone(), &queue_indices.transfer, 1)
361                } else {
362                    transfer_queue.clone()
363                }
364            } else {
365                queue_from_device(device.clone(), &queue_indices.compute, 0)
366            };
367        let wgpu_queue =
368            queue_from_device(device.clone(), &queue_indices.graphics_transfer_compute, 0);
369
370        let queues = Queues {
371            transfer: transfer_queue,
372            compute: compute_queue,
373            h264_decode: VideoQueues::new(h264_decode_queues.into_boxed_slice()).map(Arc::new),
374            h264_encode: VideoQueues::new(h264_encode_queues.into_boxed_slice()).map(Arc::new),
375            wgpu: wgpu_queue,
376        };
377
378        let allocator = Arc::new(Allocator::new(
379            instance.instance.clone(),
380            physical_device,
381            device.clone(),
382        )?);
383
384        #[cfg(feature = "wgpu")]
385        let wgpu_ctx = WgpuContext::new(
386            adapter.instance,
387            adapter.wgpu_adapter,
388            queue_indices.graphics_transfer_compute.family_index as u32,
389            descriptor,
390            device.clone(),
391            required_extensions,
392        )?;
393
394        Ok(VulkanDevice {
395            #[cfg(feature = "wgpu")]
396            wgpu_ctx,
397            _physical_device: physical_device,
398            device,
399            allocator,
400            queues,
401            native_decode_capabilities: decode_capabilities,
402            native_encode_capabilities: encode_capabilities,
403            adapter_info: info,
404        })
405    }
406
407    pub(crate) fn decoding_device(self: &Arc<Self>) -> Result<DecodingDevice, VulkanDecoderError> {
408        let decode_caps = self
409            .native_decode_capabilities
410            .as_ref()
411            .ok_or(VulkanDecoderError::VulkanDecoderUnsupported)?;
412        let max_profile = decode_caps.max_profile();
413
414        Ok(DecodingDevice {
415            vulkan_device: self.clone(),
416            h264_decode_queues: self
417                .queues
418                .h264_decode
419                .clone()
420                .ok_or(VulkanDecoderError::VulkanDecoderUnsupported)?,
421            profile_capabilities: decode_caps
422                .profile(max_profile)
423                .cloned()
424                .ok_or(VulkanDecoderError::VulkanDecoderUnsupported)?,
425        })
426    }
427
428    pub fn create_bytes_decoder(
429        self: &Arc<Self>,
430        parameters: DecoderParameters,
431    ) -> Result<BytesDecoder, DecoderError> {
432        let parser = H264Parser::default();
433        let reference_ctx = ReferenceContext::new(parameters.missed_frame_handling);
434
435        let vulkan_decoder = VulkanDecoder::new(
436            Arc::new(self.decoding_device()?),
437            parameters.usage_flags,
438            ImageModifiers {
439                additional_queue_index: self.queues.transfer.family_index,
440                create_flags: Default::default(),
441                usage_flags: Default::default(),
442            },
443        )?;
444        let frame_sorter = FrameSorter::<RawFrameData>::new();
445
446        Ok(BytesDecoder {
447            parser,
448            reference_ctx,
449            vulkan_decoder,
450            frame_sorter,
451        })
452    }
453
454    /// Create a single-input multiple-output transcoder.
455    /// Each item in `parameters.output_parameters` corresponds to one output.
456    #[cfg(feature = "transcoder")]
457    pub fn create_transcoder(
458        self: &Arc<Self>,
459        parameters: TranscoderParameters,
460    ) -> Result<crate::vulkan_transcoder::Transcoder, crate::vulkan_transcoder::TranscoderError>
461    {
462        crate::vulkan_transcoder::Transcoder::new(self.clone(), parameters)
463    }
464
465    pub(crate) fn encoding_device(self: &Arc<Self>) -> Result<EncodingDevice, VulkanEncoderError> {
466        Ok(EncodingDevice {
467            vulkan_device: self.clone(),
468            h264_encode_queues: self
469                .queues
470                .h264_encode
471                .clone()
472                .ok_or(VulkanEncoderError::VulkanEncoderUnsupported)?,
473            native_encode_capabilities: self
474                .native_encode_capabilities
475                .clone()
476                .ok_or(VulkanEncoderError::VulkanEncoderUnsupported)?,
477        })
478    }
479
480    pub fn create_bytes_encoder(
481        self: &Arc<Self>,
482        parameters: EncoderParameters,
483    ) -> Result<BytesEncoder, VulkanEncoderError> {
484        let parameters = self.validate_and_fill_encoder_parameters(
485            parameters.output_parameters,
486            parameters.input_parameters.width,
487            parameters.input_parameters.height,
488            parameters.input_parameters.target_framerate,
489        )?;
490        let encoder = VulkanEncoder::new(Arc::new(self.encoding_device()?), parameters)?;
491
492        Ok(BytesEncoder {
493            vulkan_encoder: encoder,
494        })
495    }
496
497    pub fn decode_capabilities(&self) -> DecodeCapabilities {
498        self.adapter_info.decode_capabilities
499    }
500
501    pub fn encode_capabilities(&self) -> EncodeCapabilities {
502        self.adapter_info.encode_capabilities
503    }
504
505    pub fn encoder_output_parameters_low_latency(
506        &self,
507        rate_control: RateControl,
508    ) -> Result<EncoderOutputParameters, VulkanEncoderError> {
509        let Some(caps) = self.native_encode_capabilities.as_ref() else {
510            return Err(VulkanEncoderError::VulkanEncoderUnsupported);
511        };
512
513        Ok(EncoderOutputParameters {
514            profile: caps.max_profile(),
515            idr_period: None,
516            max_references: None,
517            rate_control,
518            quality_level: 0,
519            usage_flags: Some(EncoderUsageFlags::DEFAULT),
520            content_flags: Some(EncoderContentFlags::DEFAULT),
521            tuning_mode: Some(EncoderTuningMode::LOW_LATENCY),
522            inline_stream_params: None,
523            color_space: None,
524            color_range: None,
525        })
526    }
527
528    pub fn encoder_output_parameters_high_quality(
529        &self,
530        rate_control: RateControl,
531    ) -> Result<EncoderOutputParameters, VulkanEncoderError> {
532        let Some(caps) = self.native_encode_capabilities.as_ref() else {
533            return Err(VulkanEncoderError::VulkanEncoderUnsupported);
534        };
535
536        Ok(EncoderOutputParameters {
537            profile: caps.max_profile(),
538            idr_period: None,
539            max_references: None,
540            rate_control,
541            quality_level: caps
542                .profile(caps.max_profile())
543                .unwrap()
544                .encode_capabilities
545                .max_quality_levels
546                - 1,
547            usage_flags: Some(EncoderUsageFlags::DEFAULT),
548            content_flags: Some(EncoderContentFlags::DEFAULT),
549            tuning_mode: Some(EncoderTuningMode::HIGH_QUALITY),
550            inline_stream_params: None,
551            color_space: None,
552            color_range: None,
553        })
554    }
555
556    pub(crate) fn validate_and_fill_encoder_parameters(
557        &self,
558        encoder_parameters: EncoderOutputParameters,
559        width: NonZeroU32,
560        height: NonZeroU32,
561        framerate: Rational,
562    ) -> Result<FullEncoderParameters, VulkanEncoderError> {
563        let Some(caps) = self.native_encode_capabilities.as_ref() else {
564            return Err(VulkanEncoderError::VulkanEncoderUnsupported);
565        };
566        let native_profile_caps = caps.profile(encoder_parameters.profile).ok_or(
567            VulkanEncoderError::ParametersError {
568                field: "profile",
569                problem: format!(
570                    "Profile {:?} is not supported by this device.",
571                    encoder_parameters.profile
572                ),
573            },
574        )?;
575
576        let native_quality_level_properties = native_profile_caps
577            .quality_level_properties
578            .get(encoder_parameters.quality_level as usize)
579            .ok_or(VulkanEncoderError::ParametersError {
580                field: "quality_level",
581                problem: format!(
582                    "Quality level is {}, should be < {}",
583                    encoder_parameters.quality_level,
584                    native_profile_caps.quality_level_properties.len()
585                ),
586            })?;
587
588        let idr_period = encoder_parameters.idr_period.unwrap_or(
589            if native_quality_level_properties
590                .h264_quality_level_properties
591                .preferred_idr_period
592                > 0
593            {
594                NonZeroU32::new(
595                    native_quality_level_properties
596                        .h264_quality_level_properties
597                        .preferred_idr_period,
598                )
599                .unwrap()
600            } else {
601                NonZeroU32::new(30).unwrap()
602            },
603        );
604
605        let min_extent = native_profile_caps.video_capabilities.min_coded_extent;
606        let max_extent = native_profile_caps.video_capabilities.max_coded_extent;
607
608        if width.get() < min_extent.width || width.get() > max_extent.width {
609            return Err(VulkanEncoderError::ParametersError {
610                field: "width",
611                problem: format!(
612                    "Width is {}, should be between {} and {}.",
613                    width, min_extent.width, max_extent.width
614                ),
615            });
616        }
617
618        if height.get() < min_extent.height || height.get() > max_extent.height {
619            return Err(VulkanEncoderError::ParametersError {
620                field: "height",
621                problem: format!(
622                    "Height is {}, should be between {} and {}.",
623                    height, min_extent.height, max_extent.height
624                ),
625            });
626        }
627
628        let rate_control = encoder_parameters.rate_control;
629        if !native_profile_caps
630            .encode_capabilities
631            .rate_control_modes
632            .contains(rate_control.to_vk())
633        {
634            return Err(VulkanEncoderError::ParametersError {
635                field: "rate_control",
636                problem: format!(
637                    "Rate control has mode {:?}. Supported modes are: {:?}.",
638                    rate_control.to_vk(),
639                    native_profile_caps.encode_capabilities.rate_control_modes
640                ),
641            });
642        }
643
644        let max_references = encoder_parameters.max_references.unwrap_or(
645            if native_quality_level_properties
646                .h264_quality_level_properties
647                .preferred_max_l0_reference_count
648                > 0
649            {
650                NonZeroU32::new(
651                    native_quality_level_properties
652                        .h264_quality_level_properties
653                        .preferred_max_l0_reference_count,
654                )
655                .unwrap()
656            } else {
657                NonZeroU32::new(
658                    native_profile_caps
659                        .h264_encode_capabilities
660                        .max_p_picture_l0_reference_count,
661                )
662                .unwrap()
663            },
664        );
665
666        if max_references.get()
667            > native_profile_caps
668                .h264_encode_capabilities
669                .max_p_picture_l0_reference_count
670        {
671            return Err(VulkanEncoderError::ParametersError {
672                field: "max_references",
673                problem: format!(
674                    "Max references is {}, should be != 0 and <= {}",
675                    max_references,
676                    native_profile_caps
677                        .h264_encode_capabilities
678                        .max_p_picture_l0_reference_count
679                ),
680            });
681        }
682
683        if framerate.numerator == 0 {
684            return Err(VulkanEncoderError::ParametersError {
685                field: "framerate",
686                problem: format!("Framerate is {framerate:?}. The numerator should be != 0.",),
687            });
688        }
689        let usage_flags = encoder_parameters
690            .usage_flags
691            .unwrap_or(vk::VideoEncodeUsageFlagsKHR::DEFAULT);
692        let tuning_mode = encoder_parameters
693            .tuning_mode
694            .unwrap_or(vk::VideoEncodeTuningModeKHR::DEFAULT);
695        let content_flags = encoder_parameters
696            .content_flags
697            .unwrap_or(vk::VideoEncodeContentFlagsKHR::DEFAULT);
698        let color_space = encoder_parameters.color_space.unwrap_or_default();
699        let color_range = encoder_parameters
700            .color_range
701            .unwrap_or(ColorRange::Limited);
702
703        Ok(FullEncoderParameters {
704            idr_period,
705            width,
706            height,
707            rate_control,
708            max_references,
709            quality_level: encoder_parameters.quality_level,
710            profile: encoder_parameters.profile,
711            framerate,
712            usage_flags,
713            tuning_mode,
714            content_flags,
715            inline_stream_params: encoder_parameters.inline_stream_params.unwrap_or(true),
716            color_space,
717            color_range,
718        })
719    }
720
721    pub fn supports_decoding(&self) -> bool {
722        self.adapter_info.supports_decoding
723    }
724
725    pub fn supports_encoding(&self) -> bool {
726        self.adapter_info.supports_encoding
727    }
728}
729
730impl std::fmt::Debug for VulkanDevice {
731    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
732        f.debug_struct("VulkanDevice").finish()
733    }
734}
735
736pub(crate) struct DecodingDevice {
737    pub(crate) vulkan_device: Arc<VulkanDevice>,
738    pub(crate) h264_decode_queues: Arc<VideoQueues>,
739    pub(crate) profile_capabilities: NativeDecodeProfileCapabilities,
740}
741
742impl Deref for DecodingDevice {
743    type Target = VulkanDevice;
744
745    fn deref(&self) -> &Self::Target {
746        &self.vulkan_device
747    }
748}
749
750pub(crate) struct EncodingDevice {
751    pub(crate) vulkan_device: Arc<VulkanDevice>,
752    pub(crate) h264_encode_queues: Arc<VideoQueues>,
753    pub(crate) native_encode_capabilities: NativeEncodeCapabilities,
754}
755
756impl Deref for EncodingDevice {
757    type Target = VulkanDevice;
758
759    fn deref(&self) -> &Self::Target {
760        &self.vulkan_device
761    }
762}
763
764fn queue_from_device(
765    device: Arc<Device>,
766    queue_family_index: &QueueIndex<'static>,
767    queue_index: usize,
768) -> Queue {
769    let queue = unsafe {
770        device.get_device_queue(queue_family_index.family_index as u32, queue_index as u32)
771    };
772    Queue {
773        queue: Arc::new(queue.into()),
774        family_index: queue_family_index.family_index,
775        _video_properties: queue_family_index.video_properties,
776        query_result_status_properties: queue_family_index.query_result_status_properties,
777        device,
778    }
779}