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#[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#[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#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
81pub enum MissedFrameHandling {
82 #[default]
85 Strict,
86
87 Tolerant,
90}
91
92#[derive(Debug, Default, Clone, Copy)]
94pub struct DecoderParameters {
95 pub missed_frame_handling: MissedFrameHandling,
99
100 pub usage_flags: crate::parameters::DecoderUsageFlags,
104}
105
106#[derive(Debug, Clone, Copy)]
108pub struct VideoParameters {
109 pub width: NonZeroU32,
110 pub height: NonZeroU32,
111 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub enum ColorRange {
152 Full,
154 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#[derive(Debug, Clone, Copy)]
176pub struct EncoderOutputParameters {
177 pub idr_period: Option<NonZeroU32>,
180 pub rate_control: RateControl,
183 pub max_references: Option<NonZeroU32>,
186 pub profile: H264Profile,
188 pub quality_level: u32,
191 pub usage_flags: Option<EncoderUsageFlags>,
195 pub tuning_mode: Option<EncoderTuningMode>,
197 pub content_flags: Option<EncoderContentFlags>,
201 pub inline_stream_params: Option<bool>,
205 pub color_space: Option<ColorSpace>,
208 pub color_range: Option<ColorRange>,
211}
212
213#[derive(Debug, Clone, Copy)]
215pub struct EncoderParameters {
216 pub input_parameters: VideoParameters,
217 pub output_parameters: EncoderOutputParameters,
218}
219
220pub 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 #[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}