1#![cfg_attr(
17 not(any(target_os = "linux", target_os = "macos", target_os = "windows")),
18 allow(dead_code, unused_variables, clippy::needless_pass_by_value)
19)]
20
21#[cfg(target_os = "windows")]
22mod d3d12va;
23#[cfg(target_os = "macos")]
24mod videotoolbox;
25#[cfg(target_os = "linux")]
26mod vulkan;
27
28use std::sync::atomic::{AtomicU64, Ordering};
29
30use alpha2_media::{ColorMetadata, EncodeError, VideoRateControl};
31use alpha2_time::{FrameRate, MediaTime};
32
33#[cfg(all(test, target_os = "windows"))]
34pub(crate) use d3d12va::encode_no_rate_control_frames;
35#[cfg(target_os = "windows")]
36pub(crate) use d3d12va::probe_d3d12va_rate_control;
37#[cfg(target_os = "windows")]
38pub use d3d12va::{
39 D3D12EncodeProbe, D3D12RateControlOpen, D3D12RateControlRunner, D3D12TestEncodeRunner,
40 D3D12VA_TEST_ENCODE_SIZE, d3d12va_encoder, open_d3d12va_rate_control, probe_d3d12va_encode,
41 set_d3d12va_rate_control_runner, set_d3d12va_test_encode_runner,
42};
43#[cfg(target_os = "macos")]
44pub use videotoolbox::{VideoToolboxEncodeProbe, probe_videotoolbox_encode};
45#[cfg(target_os = "linux")]
46pub(crate) use vulkan::probe_vulkan_rate_control;
47#[cfg(target_os = "linux")]
48pub use vulkan::{VulkanEncodeProbe, probe_vulkan_encode};
49
50use crate::{EncodeStageTimings, NativeVideoCodec, NativeVideoEncoderSelection};
51
52#[derive(Copy, Clone, Debug, Eq, PartialEq)]
54pub enum HardwareEncodeBackend {
55 Vulkan,
57 VideoToolbox,
59 D3D12Va,
61}
62
63impl HardwareEncodeBackend {
64 #[must_use]
66 pub const fn as_str(self) -> &'static str {
67 match self {
68 Self::Vulkan => "vulkan",
69 Self::VideoToolbox => "videotoolbox",
70 Self::D3D12Va => "d3d12va",
71 }
72 }
73}
74
75#[derive(Copy, Clone, Debug, Eq, PartialEq)]
77pub enum EncoderInputLayout {
78 Nv12,
80}
81
82impl EncoderInputLayout {
83 #[must_use]
85 pub const fn as_str(self) -> &'static str {
86 match self {
87 Self::Nv12 => "nv12",
88 }
89 }
90
91 #[must_use]
93 pub const fn frame_bytes(self, width: u32, height: u32) -> u64 {
94 match self {
95 Self::Nv12 => {
96 let luma = width as u64 * height as u64;
97 luma + luma / 2
98 }
99 }
100 }
101}
102
103#[derive(Copy, Clone, Debug, Eq, PartialEq)]
105pub enum EncoderInputHandoff {
106 GpuCopy,
108 DirectTarget,
110}
111
112impl EncoderInputHandoff {
113 #[must_use]
115 pub const fn as_str(self) -> &'static str {
116 match self {
117 Self::GpuCopy => "gpu-copy",
118 Self::DirectTarget => "direct-target",
119 }
120 }
121}
122
123#[derive(Clone, Debug, Eq, PartialEq)]
125pub struct HardwareEncodeCapability {
126 pub backend: HardwareEncodeBackend,
128 pub encoder: &'static str,
130 pub codec: NativeVideoCodec,
132 pub gpu_input_only: bool,
135 pub input_layout: EncoderInputLayout,
137 pub handoff: EncoderInputHandoff,
139 pub shares_renderer_device: bool,
142 pub minimum: (u32, u32),
144 pub maximum: (u32, u32),
146 pub granularity: (u32, u32),
148 pub max_p_picture_l0_references: Option<u32>,
150 pub max_b_picture_l0_references: Option<u32>,
153 pub max_l1_references: Option<u32>,
155 pub rate_control_modes: Vec<&'static str>,
158 pub plane_ring_slots: usize,
161 pub handoff_retained_surfaces: usize,
164 pub codec_retained_surfaces: usize,
166 pub default_channel_depth: usize,
169}
170
171impl HardwareEncodeCapability {
172 #[must_use]
174 pub const fn retained_surfaces(&self) -> usize {
175 self.handoff_retained_surfaces
176 .saturating_add(self.codec_retained_surfaces)
177 }
178}
179
180impl HardwareEncodeCapability {
181 #[must_use]
183 pub const fn coded(&self, width: u32, height: u32) -> (u32, u32) {
184 (
185 align_up(width, self.granularity.0),
186 align_up(height, self.granularity.1),
187 )
188 }
189
190 pub fn accepts(&self, width: u32, height: u32) -> Result<(), String> {
195 if !width.is_multiple_of(2) || !height.is_multiple_of(2) {
196 return Err(format!(
197 "a {width}x{height} output has no 4:2:0 chroma plane"
198 ));
199 }
200 let coded = self.coded(width, height);
201 if coded.0 < self.minimum.0
202 || coded.1 < self.minimum.1
203 || coded.0 > self.maximum.0
204 || coded.1 > self.maximum.1
205 {
206 return Err(format!(
207 "a {width}x{height} output codes as {}x{}, outside {}'s {}x{}..{}x{}",
208 coded.0,
209 coded.1,
210 self.encoder,
211 self.minimum.0,
212 self.minimum.1,
213 self.maximum.0,
214 self.maximum.1
215 ));
216 }
217 Ok(())
218 }
219}
220
221const fn align_up(value: u32, granularity: u32) -> u32 {
222 if granularity <= 1 {
223 return value;
224 }
225 value.div_ceil(granularity).saturating_mul(granularity)
226}
227
228#[derive(Clone)]
230pub struct HardwareEncodeDevice {
231 #[cfg(target_os = "linux")]
232 vulkan: alpha2_native_gpu::SharedVulkanContext,
233 #[cfg(target_os = "macos")]
234 metal: alpha2_native_gpu::SharedMetalContext,
235 #[cfg(target_os = "windows")]
236 d3d12: alpha2_native_gpu::SharedD3D12Context,
237}
238
239impl std::fmt::Debug for HardwareEncodeDevice {
240 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 formatter
242 .debug_struct("HardwareEncodeDevice")
243 .finish_non_exhaustive()
244 }
245}
246
247#[derive(Clone, Debug)]
249pub struct HardwareEncodeRequest<'a> {
250 pub width: u32,
252 pub height: u32,
254 pub codec: NativeVideoCodec,
256 pub frame_rate: FrameRate,
258 pub rate_control: VideoRateControl,
260 pub color: ColorMetadata,
262 pub output: &'a crate::OutputTarget,
265 pub container: alpha2_media::Container,
267 pub audio: Option<crate::AudioStreamConfig>,
269}
270
271pub(crate) fn session_frame_rate(rate: FrameRate) -> Result<(i32, i32), EncodeError> {
274 match (
275 i32::try_from(rate.numerator()),
276 i32::try_from(rate.denominator()),
277 ) {
278 (Ok(numerator), Ok(denominator)) => Ok((numerator, denominator)),
279 _ => Err(EncodeError::Adapter(format!(
280 "frame rate {}/{} is out of range",
281 rate.numerator(),
282 rate.denominator()
283 ))),
284 }
285}
286
287#[cfg(any(target_os = "linux", target_os = "macos"))]
290fn open_refusal(
291 encoder: &str,
292 mark: alpha2_ffmpeg_bridge::LogMark,
293 error: &EncodeError,
294) -> HardwareEncodeOpenError {
295 let lines = alpha2_ffmpeg_bridge::log_since(mark, &[encoder]);
296 let error = error.to_string();
297 let reason = format!("{error}{}", alpha2_ffmpeg_bridge::log_detail(&lines));
298 match crate::capabilities::classify_open_failure(encoder, &error, &lines) {
299 alpha2_media::OpenFailureKind::Busy => HardwareEncodeOpenError::Busy(reason),
300 _ => HardwareEncodeOpenError::Refused(reason),
301 }
302}
303
304#[derive(Debug)]
306pub enum HardwareEncodeOpenError {
307 Refused(String),
309 Busy(String),
312 Failed(EncodeError),
314}
315
316impl std::fmt::Display for HardwareEncodeOpenError {
317 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318 match self {
319 Self::Refused(reason) | Self::Busy(reason) => formatter.write_str(reason),
320 Self::Failed(error) => write!(formatter, "{error}"),
321 }
322 }
323}
324
325impl HardwareEncodeDevice {
326 #[cfg(target_os = "linux")]
328 #[must_use]
329 pub const fn vulkan(context: alpha2_native_gpu::SharedVulkanContext) -> Self {
330 Self { vulkan: context }
331 }
332
333 #[cfg(target_os = "macos")]
335 #[must_use]
336 pub const fn metal(context: alpha2_native_gpu::SharedMetalContext) -> Self {
337 Self { metal: context }
338 }
339
340 #[cfg(target_os = "windows")]
342 #[must_use]
343 pub const fn d3d12(context: alpha2_native_gpu::SharedD3D12Context) -> Self {
344 Self { d3d12: context }
345 }
346
347 #[cfg_attr(
354 not(any(target_os = "linux", target_os = "macos", target_os = "windows")),
355 allow(clippy::unused_self)
356 )]
357 pub fn capability(&self, codec: NativeVideoCodec) -> Result<HardwareEncodeCapability, String> {
358 #[cfg(target_os = "windows")]
359 return d3d12va::capability(&self.d3d12, codec);
360 #[cfg(target_os = "linux")]
361 return vulkan::capability(&self.vulkan, codec);
362 #[cfg(target_os = "macos")]
363 {
364 let capability = self.preferred_capability()?;
365 if capability.codec == codec {
366 Ok(capability)
367 } else {
368 Err(format!(
369 "{} encodes {}, not {}",
370 capability.encoder,
371 capability.codec.as_str(),
372 codec.as_str()
373 ))
374 }
375 }
376 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
377 Err("this platform has no hardware encode backend yet".to_owned())
378 }
379
380 #[cfg_attr(
386 not(any(target_os = "linux", target_os = "macos", target_os = "windows")),
387 allow(clippy::unused_self)
388 )]
389 pub fn preferred_capability(&self) -> Result<HardwareEncodeCapability, String> {
390 #[cfg(target_os = "linux")]
391 return vulkan::preferred_capability(&self.vulkan);
392 #[cfg(target_os = "macos")]
393 return videotoolbox::capability(&self.metal);
394 #[cfg(target_os = "windows")]
395 return d3d12va::preferred_capability(&self.d3d12);
396 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
397 Err("this platform has no hardware encode backend yet".to_owned())
398 }
399
400 #[cfg_attr(
406 not(any(target_os = "linux", target_os = "macos", target_os = "windows")),
407 allow(clippy::unused_self)
408 )]
409 pub fn open(
410 &self,
411 request: &HardwareEncodeRequest<'_>,
412 ) -> Result<(HardwareEncodeSession, HardwareEncodeInput), HardwareEncodeOpenError> {
413 let generation = NEXT_POOL_GENERATION.fetch_add(1, Ordering::Relaxed);
414 #[cfg(any(target_os = "linux", target_os = "macos"))]
416 self.capability(request.codec)
417 .map_err(HardwareEncodeOpenError::Refused)?;
418 #[cfg(target_os = "linux")]
419 {
420 let (session, input) = vulkan::open(&self.vulkan, request, generation)?;
421 Ok((
422 HardwareEncodeSession(SessionBackend::Vulkan(session)),
423 HardwareEncodeInput {
424 backend: InputBackend::Vulkan(input),
425 generation,
426 },
427 ))
428 }
429 #[cfg(target_os = "macos")]
430 {
431 let (session, input) = videotoolbox::open(&self.metal, request, generation)?;
432 Ok((
433 HardwareEncodeSession(SessionBackend::VideoToolbox(session)),
434 HardwareEncodeInput {
435 backend: InputBackend::VideoToolbox(input),
436 generation,
437 },
438 ))
439 }
440 #[cfg(target_os = "windows")]
441 {
442 let (session, input) = d3d12va::open(&self.d3d12, request, generation)?;
443 Ok((
444 HardwareEncodeSession(SessionBackend::D3D12(session)),
445 HardwareEncodeInput {
446 backend: InputBackend::D3D12(input),
447 generation,
448 },
449 ))
450 }
451 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
452 {
453 let _ = (request, generation);
454 Err(HardwareEncodeOpenError::Refused(
455 "this platform has no hardware encode backend yet".to_owned(),
456 ))
457 }
458 }
459}
460
461static NEXT_POOL_GENERATION: AtomicU64 = AtomicU64::new(1);
464
465#[derive(Copy, Clone, Debug, Eq, PartialEq)]
467pub struct EncoderSurfaceDescriptor {
468 pub backend: HardwareEncodeBackend,
470 pub pool_generation: u64,
472 pub layout: EncoderInputLayout,
474 pub visible: (u32, u32),
476 pub color: ColorMetadata,
478}
479
480pub struct EncoderSurface {
485 descriptor: EncoderSurfaceDescriptor,
486 backing: SurfaceBacking,
487}
488
489enum SurfaceBacking {
490 #[cfg(target_os = "linux")]
491 Vulkan(vulkan::VulkanEncoderFrame),
492 #[cfg(target_os = "macos")]
493 VideoToolbox(videotoolbox::VideoToolboxEncoderFrame),
494 #[cfg(target_os = "windows")]
495 D3D12(d3d12va::D3D12EncoderFrame),
496 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
497 Unsupported(std::convert::Infallible),
498}
499
500impl EncoderSurface {
501 #[must_use]
503 pub const fn descriptor(&self) -> &EncoderSurfaceDescriptor {
504 &self.descriptor
505 }
506}
507
508#[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,
527 pub copy_operations: u64,
529 pub copy_bytes: u64,
531}
532
533pub struct HardwareEncodeInput {
535 backend: InputBackend,
536 generation: u64,
537}
538
539enum InputBackend {
540 #[cfg(target_os = "linux")]
541 Vulkan(vulkan::VulkanEncodeInput),
542 #[cfg(target_os = "macos")]
543 VideoToolbox(videotoolbox::VideoToolboxEncodeInput),
544 #[cfg(target_os = "windows")]
545 D3D12(d3d12va::D3D12EncodeInput),
546 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
547 Unsupported(std::convert::Infallible),
548}
549
550impl HardwareEncodeInput {
551 pub fn acquire_surface(&mut self) -> Result<EncoderSurface, EncodeError> {
558 match &mut self.backend {
559 #[cfg(target_os = "linux")]
560 InputBackend::Vulkan(input) => {
561 let frame = input.acquire_frame()?;
562 Ok(EncoderSurface {
563 descriptor: input.descriptor(self.generation),
564 backing: SurfaceBacking::Vulkan(frame),
565 })
566 }
567 #[cfg(target_os = "macos")]
568 InputBackend::VideoToolbox(input) => {
569 let frame = input.acquire_frame()?;
570 Ok(EncoderSurface {
571 descriptor: input.descriptor(self.generation),
572 backing: SurfaceBacking::VideoToolbox(frame),
573 })
574 }
575 #[cfg(target_os = "windows")]
576 InputBackend::D3D12(input) => {
577 let frame = input.acquire_frame()?;
578 Ok(EncoderSurface {
579 descriptor: input.descriptor(self.generation),
580 backing: SurfaceBacking::D3D12(frame),
581 })
582 }
583 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
584 InputBackend::Unsupported(never) => match *never {},
585 }
586 }
587
588 pub fn begin_frame(&mut self, surface: &EncoderSurface) -> Result<InputFrame, EncodeError> {
595 self.check(surface)?;
596 match &mut self.backend {
597 #[cfg(target_os = "linux")]
598 InputBackend::Vulkan(input) => input.begin_frame(),
599 #[cfg(target_os = "macos")]
600 InputBackend::VideoToolbox(input) => match &surface.backing {
601 SurfaceBacking::VideoToolbox(backing) => input.begin_frame(backing),
602 },
603 #[cfg(target_os = "windows")]
604 InputBackend::D3D12(input) => input.begin_frame(),
605 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
606 InputBackend::Unsupported(never) => match *never {},
607 }
608 }
609
610 #[must_use]
612 pub fn planes(&self, frame: &InputFrame) -> (&wgpu::Texture, &wgpu::Texture) {
613 match &self.backend {
614 #[cfg(target_os = "linux")]
615 InputBackend::Vulkan(input) => input.planes(frame),
616 #[cfg(target_os = "macos")]
617 InputBackend::VideoToolbox(input) => input.planes(frame),
618 #[cfg(target_os = "windows")]
619 InputBackend::D3D12(input) => input.planes(frame),
620 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
621 InputBackend::Unsupported(never) => match *never {},
622 }
623 }
624
625 pub fn submit(
631 &mut self,
632 frame: InputFrame,
633 surface: &mut EncoderSurface,
634 ) -> Result<(), EncodeError> {
635 self.check(surface)?;
636 match (&mut self.backend, &mut surface.backing) {
637 #[cfg(target_os = "linux")]
638 (InputBackend::Vulkan(input), SurfaceBacking::Vulkan(backing)) => {
639 input.submit(frame, backing)
640 }
641 #[cfg(target_os = "macos")]
642 (InputBackend::VideoToolbox(input), SurfaceBacking::VideoToolbox(backing)) => {
643 input.submit(frame, backing)
644 }
645 #[cfg(target_os = "windows")]
646 (InputBackend::D3D12(input), SurfaceBacking::D3D12(backing)) => {
647 input.submit(frame, backing)
648 }
649 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
650 (InputBackend::Unsupported(never), _) => match *never {},
651 }
652 }
653
654 pub fn set_time(&self, surface: &mut EncoderSurface, time: MediaTime) {
656 match (&self.backend, &mut surface.backing) {
657 #[cfg(target_os = "linux")]
658 (InputBackend::Vulkan(input), SurfaceBacking::Vulkan(backing)) => {
659 backing.set_pts(input.timestamp(time));
660 }
661 #[cfg(target_os = "macos")]
662 (InputBackend::VideoToolbox(input), SurfaceBacking::VideoToolbox(backing)) => {
663 backing.set_pts(input.timestamp(time));
664 }
665 #[cfg(target_os = "windows")]
666 (InputBackend::D3D12(input), SurfaceBacking::D3D12(backing)) => {
667 backing.set_pts(input.timestamp(time));
668 }
669 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
670 (InputBackend::Unsupported(never), _) => match *never {},
671 }
672 }
673
674 #[must_use]
676 pub fn stats(&self) -> EncoderPoolStats {
677 match &self.backend {
678 #[cfg(target_os = "linux")]
679 InputBackend::Vulkan(input) => input.stats(),
680 #[cfg(target_os = "macos")]
681 InputBackend::VideoToolbox(input) => input.stats(),
682 #[cfg(target_os = "windows")]
683 InputBackend::D3D12(input) => input.stats(),
684 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
685 InputBackend::Unsupported(never) => match *never {},
686 }
687 }
688
689 pub fn finish(self) -> Result<(), EncodeError> {
694 match self.backend {
695 #[cfg(target_os = "linux")]
696 InputBackend::Vulkan(input) => input.finish(),
697 #[cfg(target_os = "macos")]
698 InputBackend::VideoToolbox(input) => input.finish(),
699 #[cfg(target_os = "windows")]
700 InputBackend::D3D12(input) => input.finish(),
701 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
702 InputBackend::Unsupported(never) => match never {},
703 }
704 }
705
706 fn check(&self, surface: &EncoderSurface) -> Result<(), EncodeError> {
707 if surface.descriptor.pool_generation == self.generation {
708 Ok(())
709 } else {
710 Err(EncodeError::Adapter(format!(
711 "encoder surface from pool {} reached pool {}",
712 surface.descriptor.pool_generation, self.generation
713 )))
714 }
715 }
716}
717
718pub struct HardwareEncodeSession(SessionBackend);
720
721enum SessionBackend {
722 #[cfg(target_os = "linux")]
723 Vulkan(vulkan::VulkanEncodeSession),
724 #[cfg(target_os = "macos")]
725 VideoToolbox(videotoolbox::VideoToolboxEncodeSession),
726 #[cfg(target_os = "windows")]
727 D3D12(d3d12va::D3D12EncodeSession),
728 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
729 Unsupported(std::convert::Infallible),
730}
731
732impl HardwareEncodeSession {
733 #[must_use]
735 pub fn selection(&self) -> &NativeVideoEncoderSelection {
736 match &self.0 {
737 #[cfg(target_os = "linux")]
738 SessionBackend::Vulkan(session) => session.selection(),
739 #[cfg(target_os = "macos")]
740 SessionBackend::VideoToolbox(session) => session.selection(),
741 #[cfg(target_os = "windows")]
742 SessionBackend::D3D12(session) => session.selection(),
743 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
744 SessionBackend::Unsupported(never) => match *never {},
745 }
746 }
747
748 #[must_use]
750 pub fn stage_timings(&self) -> EncodeStageTimings {
751 match &self.0 {
752 #[cfg(target_os = "linux")]
753 SessionBackend::Vulkan(session) => session.stage_timings(),
754 #[cfg(target_os = "macos")]
755 SessionBackend::VideoToolbox(session) => session.stage_timings(),
756 #[cfg(target_os = "windows")]
757 SessionBackend::D3D12(session) => session.stage_timings(),
758 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
759 SessionBackend::Unsupported(never) => match *never {},
760 }
761 }
762
763 pub fn encode(&mut self, surface: &EncoderSurface) -> Result<(), EncodeError> {
768 match (&mut self.0, &surface.backing) {
769 #[cfg(target_os = "linux")]
770 (SessionBackend::Vulkan(session), SurfaceBacking::Vulkan(frame)) => {
771 session.encode(surface.descriptor.pool_generation, frame)
772 }
773 #[cfg(target_os = "macos")]
774 (SessionBackend::VideoToolbox(session), SurfaceBacking::VideoToolbox(frame)) => {
775 session.encode(surface.descriptor.pool_generation, frame)
776 }
777 #[cfg(target_os = "windows")]
778 (SessionBackend::D3D12(session), SurfaceBacking::D3D12(frame)) => {
779 session.encode(surface.descriptor.pool_generation, frame)
780 }
781 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
782 (SessionBackend::Unsupported(never), _) => match *never {},
783 }
784 }
785
786 pub fn write_audio(&mut self, block: &alpha2_media::AudioBlock) -> Result<(), EncodeError> {
791 match &mut self.0 {
792 #[cfg(target_os = "linux")]
793 SessionBackend::Vulkan(session) => session.write_audio(block),
794 #[cfg(target_os = "macos")]
795 SessionBackend::VideoToolbox(session) => session.write_audio(block),
796 #[cfg(target_os = "windows")]
797 SessionBackend::D3D12(session) => session.write_audio(block),
798 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
799 SessionBackend::Unsupported(never) => match *never {},
800 }
801 }
802
803 pub fn finish(&mut self, cancel: &alpha2_media::CancelToken) -> Result<(), EncodeError> {
810 match &mut self.0 {
811 #[cfg(target_os = "linux")]
812 SessionBackend::Vulkan(session) => session.finish(cancel),
813 #[cfg(target_os = "macos")]
814 SessionBackend::VideoToolbox(session) => session.finish(cancel),
815 #[cfg(target_os = "windows")]
816 SessionBackend::D3D12(session) => session.finish(cancel),
817 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
818 SessionBackend::Unsupported(never) => match *never {},
819 }
820 }
821
822 #[must_use]
824 pub fn publish_report(&self) -> Option<crate::PublishReport> {
825 match &self.0 {
826 #[cfg(target_os = "linux")]
827 SessionBackend::Vulkan(session) => session.publish_report(),
828 #[cfg(target_os = "macos")]
829 SessionBackend::VideoToolbox(session) => session.publish_report(),
830 #[cfg(target_os = "windows")]
831 SessionBackend::D3D12(session) => session.publish_report(),
832 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
833 SessionBackend::Unsupported(never) => match *never {},
834 }
835 }
836
837 pub fn abort(&mut self) {
839 match &mut self.0 {
840 #[cfg(target_os = "linux")]
841 SessionBackend::Vulkan(session) => session.abort(),
842 #[cfg(target_os = "macos")]
843 SessionBackend::VideoToolbox(session) => session.abort(),
844 #[cfg(target_os = "windows")]
845 SessionBackend::D3D12(session) => session.abort(),
846 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
847 SessionBackend::Unsupported(never) => match *never {},
848 }
849 }
850}