1#![cfg_attr(
22 not(any(target_os = "linux", target_os = "macos", target_os = "windows")),
23 allow(dead_code, unused_variables, clippy::needless_pass_by_value)
24)]
25
26#[cfg(target_os = "windows")]
27mod d3d12va;
28#[cfg(target_os = "macos")]
29mod videotoolbox;
30#[cfg(target_os = "linux")]
31mod vulkan;
32
33use std::path::Path;
34use std::sync::atomic::{AtomicU64, Ordering};
35
36use alpha2_media::{ColorMetadata, EncodeError, VideoRateControl};
37use alpha2_time::{FrameRate, MediaTime};
38
39#[cfg(all(test, target_os = "windows"))]
40pub(crate) use d3d12va::encode_no_rate_control_frames;
41#[cfg(target_os = "windows")]
42pub(crate) use d3d12va::probe_d3d12va_rate_control;
43#[cfg(target_os = "windows")]
44pub use d3d12va::{
45 D3D12EncodeProbe, D3D12RateControlOpen, D3D12RateControlRunner, D3D12TestEncodeRunner,
46 D3D12VA_TEST_ENCODE_SIZE, d3d12va_encoder, open_d3d12va_rate_control, probe_d3d12va_encode,
47 set_d3d12va_rate_control_runner, set_d3d12va_test_encode_runner,
48};
49#[cfg(target_os = "macos")]
50pub use videotoolbox::{VideoToolboxEncodeProbe, probe_videotoolbox_encode};
51#[cfg(target_os = "linux")]
52pub(crate) use vulkan::probe_vulkan_rate_control;
53#[cfg(target_os = "linux")]
54pub use vulkan::{VulkanEncodeProbe, probe_vulkan_encode};
55
56use crate::{EncodeStageTimings, NativeVideoCodec, NativeVideoEncoderSelection};
57
58#[derive(Copy, Clone, Debug, Eq, PartialEq)]
60pub enum HardwareEncodeBackend {
61 Vulkan,
63 VideoToolbox,
66 D3D12Va,
68}
69
70impl HardwareEncodeBackend {
71 #[must_use]
73 pub const fn as_str(self) -> &'static str {
74 match self {
75 Self::Vulkan => "vulkan",
76 Self::VideoToolbox => "videotoolbox",
77 Self::D3D12Va => "d3d12va",
78 }
79 }
80}
81
82#[derive(Copy, Clone, Debug, Eq, PartialEq)]
84pub enum EncoderInputLayout {
85 Nv12,
88}
89
90impl EncoderInputLayout {
91 #[must_use]
93 pub const fn as_str(self) -> &'static str {
94 match self {
95 Self::Nv12 => "nv12",
96 }
97 }
98
99 #[must_use]
101 pub const fn frame_bytes(self, width: u32, height: u32) -> u64 {
102 match self {
103 Self::Nv12 => {
104 let luma = width as u64 * height as u64;
105 luma + luma / 2
106 }
107 }
108 }
109}
110
111#[derive(Copy, Clone, Debug, Eq, PartialEq)]
113pub enum EncoderInputHandoff {
114 GpuCopy,
117 DirectTarget,
120}
121
122impl EncoderInputHandoff {
123 #[must_use]
125 pub const fn as_str(self) -> &'static str {
126 match self {
127 Self::GpuCopy => "gpu-copy",
128 Self::DirectTarget => "direct-target",
129 }
130 }
131}
132
133#[derive(Clone, Debug, Eq, PartialEq)]
135pub struct HardwareEncodeCapability {
136 pub backend: HardwareEncodeBackend,
138 pub encoder: &'static str,
140 pub codec: NativeVideoCodec,
142 pub gpu_input_only: bool,
146 pub input_layout: EncoderInputLayout,
148 pub handoff: EncoderInputHandoff,
150 pub shares_renderer_device: bool,
153 pub minimum: (u32, u32),
155 pub maximum: (u32, u32),
157 pub granularity: (u32, u32),
159 pub max_p_picture_l0_references: Option<u32>,
161 pub max_b_picture_l0_references: Option<u32>,
165 pub max_l1_references: Option<u32>,
167 pub rate_control_modes: Vec<&'static str>,
170 pub plane_ring_slots: usize,
174 pub handoff_retained_surfaces: usize,
179 pub codec_retained_surfaces: usize,
182}
183
184impl HardwareEncodeCapability {
185 #[must_use]
187 pub const fn retained_surfaces(&self) -> usize {
188 self.handoff_retained_surfaces
189 .saturating_add(self.codec_retained_surfaces)
190 }
191}
192
193impl HardwareEncodeCapability {
194 #[must_use]
196 pub const fn coded(&self, width: u32, height: u32) -> (u32, u32) {
197 (
198 align_up(width, self.granularity.0),
199 align_up(height, self.granularity.1),
200 )
201 }
202
203 pub fn accepts(&self, width: u32, height: u32) -> Result<(), String> {
208 if !width.is_multiple_of(2) || !height.is_multiple_of(2) {
209 return Err(format!(
210 "a {width}x{height} output has no 4:2:0 chroma plane"
211 ));
212 }
213 let coded = self.coded(width, height);
214 if coded.0 < self.minimum.0
215 || coded.1 < self.minimum.1
216 || coded.0 > self.maximum.0
217 || coded.1 > self.maximum.1
218 {
219 return Err(format!(
220 "a {width}x{height} output codes as {}x{}, outside {}'s {}x{}..{}x{}",
221 coded.0,
222 coded.1,
223 self.encoder,
224 self.minimum.0,
225 self.minimum.1,
226 self.maximum.0,
227 self.maximum.1
228 ));
229 }
230 Ok(())
231 }
232}
233
234const fn align_up(value: u32, granularity: u32) -> u32 {
235 if granularity <= 1 {
236 return value;
237 }
238 value.div_ceil(granularity).saturating_mul(granularity)
239}
240
241#[derive(Clone)]
243pub struct HardwareEncodeDevice {
244 #[cfg(target_os = "linux")]
245 vulkan: alpha2_native_gpu::SharedVulkanContext,
246 #[cfg(target_os = "macos")]
247 metal: alpha2_native_gpu::SharedMetalContext,
248 #[cfg(target_os = "windows")]
249 d3d12: alpha2_native_gpu::SharedD3D12Context,
250}
251
252impl std::fmt::Debug for HardwareEncodeDevice {
253 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254 formatter
255 .debug_struct("HardwareEncodeDevice")
256 .finish_non_exhaustive()
257 }
258}
259
260#[derive(Clone, Debug)]
262pub struct HardwareEncodeRequest<'a> {
263 pub width: u32,
265 pub height: u32,
267 pub codec: NativeVideoCodec,
270 pub frame_rate: FrameRate,
272 pub rate_control: VideoRateControl,
274 pub color: ColorMetadata,
276 pub output: &'a Path,
278 pub container: alpha2_media::Container,
281 pub audio: Option<crate::AudioStreamConfig>,
283}
284
285pub(crate) fn session_frame_rate(rate: FrameRate) -> Result<(i32, i32), EncodeError> {
290 match (
291 i32::try_from(rate.numerator()),
292 i32::try_from(rate.denominator()),
293 ) {
294 (Ok(numerator), Ok(denominator)) => Ok((numerator, denominator)),
295 _ => Err(EncodeError::Adapter(format!(
296 "frame rate {}/{} is out of range",
297 rate.numerator(),
298 rate.denominator()
299 ))),
300 }
301}
302
303#[derive(Debug)]
305pub enum HardwareEncodeOpenError {
306 Refused(String),
309 Failed(EncodeError),
311}
312
313impl std::fmt::Display for HardwareEncodeOpenError {
314 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 match self {
316 Self::Refused(reason) => formatter.write_str(reason),
317 Self::Failed(error) => write!(formatter, "{error}"),
318 }
319 }
320}
321
322impl HardwareEncodeDevice {
323 #[cfg(target_os = "linux")]
325 #[must_use]
326 pub const fn vulkan(context: alpha2_native_gpu::SharedVulkanContext) -> Self {
327 Self { vulkan: context }
328 }
329
330 #[cfg(target_os = "macos")]
332 #[must_use]
333 pub const fn metal(context: alpha2_native_gpu::SharedMetalContext) -> Self {
334 Self { metal: context }
335 }
336
337 #[cfg(target_os = "windows")]
339 #[must_use]
340 pub const fn d3d12(context: alpha2_native_gpu::SharedD3D12Context) -> Self {
341 Self { d3d12: context }
342 }
343
344 #[cfg_attr(
352 not(any(target_os = "linux", target_os = "macos", target_os = "windows")),
353 allow(clippy::unused_self)
354 )]
355 pub fn capability(&self, codec: NativeVideoCodec) -> Result<HardwareEncodeCapability, String> {
356 #[cfg(target_os = "windows")]
357 return d3d12va::capability(&self.d3d12, codec);
358 #[cfg(any(target_os = "linux", target_os = "macos"))]
359 {
360 let capability = self.preferred_capability()?;
361 if capability.codec == codec {
362 Ok(capability)
363 } else {
364 Err(format!(
365 "{} encodes {}, not {}",
366 capability.encoder,
367 capability.codec.as_str(),
368 codec.as_str()
369 ))
370 }
371 }
372 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
373 Err("this platform has no hardware encode backend yet".to_owned())
374 }
375
376 #[cfg_attr(
382 not(any(target_os = "linux", target_os = "macos", target_os = "windows")),
383 allow(clippy::unused_self)
384 )]
385 pub fn preferred_capability(&self) -> Result<HardwareEncodeCapability, String> {
386 #[cfg(target_os = "linux")]
387 return vulkan::capability(&self.vulkan);
388 #[cfg(target_os = "macos")]
389 return videotoolbox::capability(&self.metal);
390 #[cfg(target_os = "windows")]
391 return d3d12va::preferred_capability(&self.d3d12);
392 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
393 Err("this platform has no hardware encode backend yet".to_owned())
394 }
395
396 #[cfg_attr(
404 not(any(target_os = "linux", target_os = "macos", target_os = "windows")),
405 allow(clippy::unused_self)
406 )]
407 pub fn open(
408 &self,
409 request: &HardwareEncodeRequest<'_>,
410 ) -> Result<(HardwareEncodeSession, HardwareEncodeInput), HardwareEncodeOpenError> {
411 let generation = NEXT_POOL_GENERATION.fetch_add(1, Ordering::Relaxed);
412 #[cfg(any(target_os = "linux", target_os = "macos"))]
414 self.capability(request.codec)
415 .map_err(HardwareEncodeOpenError::Refused)?;
416 #[cfg(target_os = "linux")]
417 {
418 let (session, input) = vulkan::open(&self.vulkan, request, generation)?;
419 Ok((
420 HardwareEncodeSession(SessionBackend::Vulkan(session)),
421 HardwareEncodeInput {
422 backend: InputBackend::Vulkan(input),
423 generation,
424 },
425 ))
426 }
427 #[cfg(target_os = "macos")]
428 {
429 let (session, input) = videotoolbox::open(&self.metal, request, generation)?;
430 Ok((
431 HardwareEncodeSession(SessionBackend::VideoToolbox(session)),
432 HardwareEncodeInput {
433 backend: InputBackend::VideoToolbox(input),
434 generation,
435 },
436 ))
437 }
438 #[cfg(target_os = "windows")]
439 {
440 let (session, input) = d3d12va::open(&self.d3d12, request, generation)?;
441 Ok((
442 HardwareEncodeSession(SessionBackend::D3D12(session)),
443 HardwareEncodeInput {
444 backend: InputBackend::D3D12(input),
445 generation,
446 },
447 ))
448 }
449 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
450 {
451 let _ = (request, generation);
452 Err(HardwareEncodeOpenError::Refused(
453 "this platform has no hardware encode backend yet".to_owned(),
454 ))
455 }
456 }
457}
458
459static NEXT_POOL_GENERATION: AtomicU64 = AtomicU64::new(1);
463
464#[derive(Copy, Clone, Debug, Eq, PartialEq)]
466pub struct EncoderSurfaceDescriptor {
467 pub backend: HardwareEncodeBackend,
469 pub pool_generation: u64,
471 pub layout: EncoderInputLayout,
473 pub visible: (u32, u32),
475 pub color: ColorMetadata,
477}
478
479pub struct EncoderSurface {
484 descriptor: EncoderSurfaceDescriptor,
485 backing: SurfaceBacking,
486}
487
488enum SurfaceBacking {
489 #[cfg(target_os = "linux")]
490 Vulkan(vulkan::VulkanEncoderFrame),
491 #[cfg(target_os = "macos")]
492 VideoToolbox(videotoolbox::VideoToolboxEncoderFrame),
493 #[cfg(target_os = "windows")]
494 D3D12(d3d12va::D3D12EncoderFrame),
495 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
496 Unsupported(std::convert::Infallible),
497}
498
499impl EncoderSurface {
500 #[must_use]
502 pub const fn descriptor(&self) -> &EncoderSurfaceDescriptor {
503 &self.descriptor
504 }
505}
506
507#[must_use = "a begun frame must be submitted, or its planes stay claimed"]
510pub struct InputFrame {
511 #[cfg(any(target_os = "linux", target_os = "windows"))]
513 slot: usize,
514 #[cfg(target_os = "macos")]
516 pixel_buffer: usize,
517}
518
519#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
521pub struct EncoderPoolStats {
522 pub acquired: u64,
524 pub allocated: u64,
528 pub copy_operations: u64,
530 pub copy_bytes: u64,
532}
533
534pub struct HardwareEncodeInput {
536 backend: InputBackend,
537 generation: u64,
538}
539
540enum InputBackend {
541 #[cfg(target_os = "linux")]
542 Vulkan(vulkan::VulkanEncodeInput),
543 #[cfg(target_os = "macos")]
544 VideoToolbox(videotoolbox::VideoToolboxEncodeInput),
545 #[cfg(target_os = "windows")]
546 D3D12(d3d12va::D3D12EncodeInput),
547 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
548 Unsupported(std::convert::Infallible),
549}
550
551impl HardwareEncodeInput {
552 pub fn acquire_surface(&mut self) -> Result<EncoderSurface, EncodeError> {
560 match &mut self.backend {
561 #[cfg(target_os = "linux")]
562 InputBackend::Vulkan(input) => {
563 let frame = input.acquire_frame()?;
564 Ok(EncoderSurface {
565 descriptor: input.descriptor(self.generation),
566 backing: SurfaceBacking::Vulkan(frame),
567 })
568 }
569 #[cfg(target_os = "macos")]
570 InputBackend::VideoToolbox(input) => {
571 let frame = input.acquire_frame()?;
572 Ok(EncoderSurface {
573 descriptor: input.descriptor(self.generation),
574 backing: SurfaceBacking::VideoToolbox(frame),
575 })
576 }
577 #[cfg(target_os = "windows")]
578 InputBackend::D3D12(input) => {
579 let frame = input.acquire_frame()?;
580 Ok(EncoderSurface {
581 descriptor: input.descriptor(self.generation),
582 backing: SurfaceBacking::D3D12(frame),
583 })
584 }
585 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
586 InputBackend::Unsupported(never) => match *never {},
587 }
588 }
589
590 pub fn begin_frame(&mut self, surface: &EncoderSurface) -> Result<InputFrame, EncodeError> {
599 self.check(surface)?;
600 match &mut self.backend {
601 #[cfg(target_os = "linux")]
602 InputBackend::Vulkan(input) => input.begin_frame(),
603 #[cfg(target_os = "macos")]
604 InputBackend::VideoToolbox(input) => match &surface.backing {
605 SurfaceBacking::VideoToolbox(backing) => input.begin_frame(backing),
606 },
607 #[cfg(target_os = "windows")]
608 InputBackend::D3D12(input) => input.begin_frame(),
609 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
610 InputBackend::Unsupported(never) => match *never {},
611 }
612 }
613
614 #[must_use]
616 pub fn planes(&self, frame: &InputFrame) -> (&wgpu::Texture, &wgpu::Texture) {
617 match &self.backend {
618 #[cfg(target_os = "linux")]
619 InputBackend::Vulkan(input) => input.planes(frame),
620 #[cfg(target_os = "macos")]
621 InputBackend::VideoToolbox(input) => input.planes(frame),
622 #[cfg(target_os = "windows")]
623 InputBackend::D3D12(input) => input.planes(frame),
624 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
625 InputBackend::Unsupported(never) => match *never {},
626 }
627 }
628
629 pub fn submit(
636 &mut self,
637 frame: InputFrame,
638 surface: &mut EncoderSurface,
639 ) -> Result<(), EncodeError> {
640 self.check(surface)?;
641 match (&mut self.backend, &mut surface.backing) {
642 #[cfg(target_os = "linux")]
643 (InputBackend::Vulkan(input), SurfaceBacking::Vulkan(backing)) => {
644 input.submit(frame, backing)
645 }
646 #[cfg(target_os = "macos")]
647 (InputBackend::VideoToolbox(input), SurfaceBacking::VideoToolbox(backing)) => {
648 input.submit(frame, backing)
649 }
650 #[cfg(target_os = "windows")]
651 (InputBackend::D3D12(input), SurfaceBacking::D3D12(backing)) => {
652 input.submit(frame, backing)
653 }
654 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
655 (InputBackend::Unsupported(never), _) => match *never {},
656 }
657 }
658
659 pub fn set_time(&self, surface: &mut EncoderSurface, time: MediaTime) {
661 match (&self.backend, &mut surface.backing) {
662 #[cfg(target_os = "linux")]
663 (InputBackend::Vulkan(input), SurfaceBacking::Vulkan(backing)) => {
664 backing.set_pts(input.timestamp(time));
665 }
666 #[cfg(target_os = "macos")]
667 (InputBackend::VideoToolbox(input), SurfaceBacking::VideoToolbox(backing)) => {
668 backing.set_pts(input.timestamp(time));
669 }
670 #[cfg(target_os = "windows")]
671 (InputBackend::D3D12(input), SurfaceBacking::D3D12(backing)) => {
672 backing.set_pts(input.timestamp(time));
673 }
674 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
675 (InputBackend::Unsupported(never), _) => match *never {},
676 }
677 }
678
679 #[must_use]
681 pub fn stats(&self) -> EncoderPoolStats {
682 match &self.backend {
683 #[cfg(target_os = "linux")]
684 InputBackend::Vulkan(input) => input.stats(),
685 #[cfg(target_os = "macos")]
686 InputBackend::VideoToolbox(input) => input.stats(),
687 #[cfg(target_os = "windows")]
688 InputBackend::D3D12(input) => input.stats(),
689 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
690 InputBackend::Unsupported(never) => match *never {},
691 }
692 }
693
694 pub fn finish(self) -> Result<(), EncodeError> {
700 match self.backend {
701 #[cfg(target_os = "linux")]
702 InputBackend::Vulkan(input) => input.finish(),
703 #[cfg(target_os = "macos")]
704 InputBackend::VideoToolbox(input) => input.finish(),
705 #[cfg(target_os = "windows")]
706 InputBackend::D3D12(input) => input.finish(),
707 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
708 InputBackend::Unsupported(never) => match never {},
709 }
710 }
711
712 fn check(&self, surface: &EncoderSurface) -> Result<(), EncodeError> {
713 if surface.descriptor.pool_generation == self.generation {
714 Ok(())
715 } else {
716 Err(EncodeError::Adapter(format!(
717 "encoder surface from pool {} reached pool {}",
718 surface.descriptor.pool_generation, self.generation
719 )))
720 }
721 }
722}
723
724pub struct HardwareEncodeSession(SessionBackend);
726
727enum SessionBackend {
728 #[cfg(target_os = "linux")]
729 Vulkan(vulkan::VulkanEncodeSession),
730 #[cfg(target_os = "macos")]
731 VideoToolbox(videotoolbox::VideoToolboxEncodeSession),
732 #[cfg(target_os = "windows")]
733 D3D12(d3d12va::D3D12EncodeSession),
734 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
735 Unsupported(std::convert::Infallible),
736}
737
738impl HardwareEncodeSession {
739 #[must_use]
742 pub fn selection(&self) -> &NativeVideoEncoderSelection {
743 match &self.0 {
744 #[cfg(target_os = "linux")]
745 SessionBackend::Vulkan(session) => session.selection(),
746 #[cfg(target_os = "macos")]
747 SessionBackend::VideoToolbox(session) => session.selection(),
748 #[cfg(target_os = "windows")]
749 SessionBackend::D3D12(session) => session.selection(),
750 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
751 SessionBackend::Unsupported(never) => match *never {},
752 }
753 }
754
755 #[must_use]
758 pub fn stage_timings(&self) -> EncodeStageTimings {
759 match &self.0 {
760 #[cfg(target_os = "linux")]
761 SessionBackend::Vulkan(session) => session.stage_timings(),
762 #[cfg(target_os = "macos")]
763 SessionBackend::VideoToolbox(session) => session.stage_timings(),
764 #[cfg(target_os = "windows")]
765 SessionBackend::D3D12(session) => session.stage_timings(),
766 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
767 SessionBackend::Unsupported(never) => match *never {},
768 }
769 }
770
771 pub fn encode(&mut self, surface: &EncoderSurface) -> Result<(), EncodeError> {
777 match (&mut self.0, &surface.backing) {
778 #[cfg(target_os = "linux")]
779 (SessionBackend::Vulkan(session), SurfaceBacking::Vulkan(frame)) => {
780 session.encode(surface.descriptor.pool_generation, frame)
781 }
782 #[cfg(target_os = "macos")]
783 (SessionBackend::VideoToolbox(session), SurfaceBacking::VideoToolbox(frame)) => {
784 session.encode(surface.descriptor.pool_generation, frame)
785 }
786 #[cfg(target_os = "windows")]
787 (SessionBackend::D3D12(session), SurfaceBacking::D3D12(frame)) => {
788 session.encode(surface.descriptor.pool_generation, frame)
789 }
790 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
791 (SessionBackend::Unsupported(never), _) => match *never {},
792 }
793 }
794
795 pub fn write_audio(&mut self, block: &alpha2_media::AudioBlock) -> Result<(), EncodeError> {
801 match &mut self.0 {
802 #[cfg(target_os = "linux")]
803 SessionBackend::Vulkan(session) => session.write_audio(block),
804 #[cfg(target_os = "macos")]
805 SessionBackend::VideoToolbox(session) => session.write_audio(block),
806 #[cfg(target_os = "windows")]
807 SessionBackend::D3D12(session) => session.write_audio(block),
808 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
809 SessionBackend::Unsupported(never) => match *never {},
810 }
811 }
812
813 pub fn finish(&mut self) -> Result<(), EncodeError> {
819 match &mut self.0 {
820 #[cfg(target_os = "linux")]
821 SessionBackend::Vulkan(session) => session.finish(),
822 #[cfg(target_os = "macos")]
823 SessionBackend::VideoToolbox(session) => session.finish(),
824 #[cfg(target_os = "windows")]
825 SessionBackend::D3D12(session) => session.finish(),
826 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
827 SessionBackend::Unsupported(never) => match *never {},
828 }
829 }
830
831 pub fn abort(&mut self) {
833 match &mut self.0 {
834 #[cfg(target_os = "linux")]
835 SessionBackend::Vulkan(session) => session.abort(),
836 #[cfg(target_os = "macos")]
837 SessionBackend::VideoToolbox(session) => session.abort(),
838 #[cfg(target_os = "windows")]
839 SessionBackend::D3D12(session) => session.abort(),
840 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
841 SessionBackend::Unsupported(never) => match *never {},
842 }
843 }
844}