Skip to main content

alpha2_native_gpu/
lib.rs

1//! Platform GPU interop for native decoded video surfaces.
2//!
3//! `FFmpeg` ownership stays in `alpha2-ffmpeg-bridge`; this crate owns raw GPU handles,
4//! barriers, synchronization, and bounded surface accounting.
5
6#[cfg(all(
7    test,
8    feature = "hwdevice",
9    any(target_os = "linux", target_os = "windows", target_os = "macos")
10))]
11mod conformance;
12mod context;
13/// The fixed-width ABI shared with the C helpers, generated from their headers by the build
14/// script with bindgen's layout assertions. Structs only Windows or macOS use are also pinned by
15/// hand below.
16#[allow(
17    dead_code,
18    non_camel_case_types,
19    unreachable_pub,
20    clippy::all,
21    clippy::pedantic,
22    clippy::undocumented_unsafe_blocks
23)]
24mod interop {
25    include!(concat!(env!("OUT_DIR"), "/interop.rs"));
26}
27
28// Pins the layouts of interop structs that only Windows and macOS builds compile, so a header
29// edit to them fails on every target.
30const _: () = {
31    use interop::{
32        Alpha2D3D12CopyInfo as D3D12, Alpha2D3D12EncodeCaps as Caps,
33        Alpha2D3D12EncodeCopy as Encode, Alpha2MetalFrameInfo as Metal,
34    };
35    use std::mem::{align_of, offset_of, size_of};
36    assert!(size_of::<D3D12>() == 40 && align_of::<D3D12>() == 8);
37    assert!(offset_of!(D3D12, resource) == 0);
38    assert!(offset_of!(D3D12, fence) == 8);
39    assert!(offset_of!(D3D12, fence_value) == 16);
40    assert!(offset_of!(D3D12, width) == 24);
41    assert!(offset_of!(D3D12, height) == 28);
42    assert!(offset_of!(D3D12, dxgi_format) == 32);
43    assert!(size_of::<Encode>() == 48 && align_of::<Encode>() == 8);
44    assert!(offset_of!(Encode, luma) == 0);
45    assert!(offset_of!(Encode, chroma) == 8);
46    assert!(offset_of!(Encode, destination) == 16);
47    assert!(offset_of!(Encode, frame_fence) == 24);
48    assert!(offset_of!(Encode, frame_value) == 32);
49    assert!(offset_of!(Encode, width) == 40);
50    assert!(offset_of!(Encode, height) == 44);
51    assert!(size_of::<Caps>() == 76 && align_of::<Caps>() == 4);
52    assert!(offset_of!(Caps, supported) == 0);
53    assert!(offset_of!(Caps, refused_check) == 4);
54    assert!(offset_of!(Caps, refused_status) == 8);
55    assert!(offset_of!(Caps, validation_flags) == 12);
56    assert!(offset_of!(Caps, min_width) == 16);
57    assert!(offset_of!(Caps, min_height) == 20);
58    assert!(offset_of!(Caps, max_width) == 24);
59    assert!(offset_of!(Caps, max_height) == 28);
60    assert!(offset_of!(Caps, width_multiple) == 32);
61    assert!(offset_of!(Caps, height_multiple) == 36);
62    assert!(offset_of!(Caps, max_p_l0_references) == 40);
63    assert!(offset_of!(Caps, max_b_l0_references) == 44);
64    assert!(offset_of!(Caps, max_l1_references) == 48);
65    assert!(offset_of!(Caps, rate_control_modes) == 52);
66    assert!(offset_of!(Caps, refused_rate_control_modes) == 56);
67    assert!(offset_of!(Caps, rate_control_status) == 60);
68    assert!(size_of::<Metal>() == 56 && align_of::<Metal>() == 8);
69    assert!(offset_of!(Metal, width) == 0);
70    assert!(offset_of!(Metal, height) == 4);
71    assert!(offset_of!(Metal, plane_width) == 8);
72    assert!(offset_of!(Metal, plane_height) == 16);
73    assert!(offset_of!(Metal, clean_x) == 24);
74    assert!(offset_of!(Metal, clean_y) == 28);
75    assert!(offset_of!(Metal, clean_width) == 32);
76    assert!(offset_of!(Metal, clean_height) == 36);
77    assert!(offset_of!(Metal, pixel_format) == 40);
78    assert!(offset_of!(Metal, plane_count) == 44);
79    assert!(offset_of!(Metal, io_surface) == 48);
80};
81#[cfg(all(target_os = "windows", feature = "hwdevice"))]
82mod d3d12;
83mod import;
84mod lifetime;
85#[cfg(feature = "hwdevice")]
86mod memory_budget;
87#[cfg(all(target_os = "macos", feature = "hwdevice"))]
88mod metal;
89#[cfg(feature = "hwdevice")]
90mod status;
91mod surface_budget;
92mod sync;
93#[cfg(all(target_os = "linux", feature = "hwdevice"))]
94mod vulkan;
95
96pub use context::NativeGpuIdentity;
97#[cfg(all(target_os = "windows", feature = "hwdevice"))]
98pub use d3d12::{
99    D3D12AdapterIdentity, D3D12CopyLease, D3D12EncodeCodec, D3D12EncodeCopier, D3D12EncodeExtent,
100    ImportedD3D12Frame, SharedD3D12Context, SharedD3D12Error, d3d12_adapter_identity,
101    gpu_wait_timeout as d3d12_wait_timeout, hresult_text,
102};
103pub use import::{ImportedPlaneDescriptor, NativePlaneLayout};
104pub use lifetime::{NativeSurfaceLease, SurfaceIdentity};
105#[cfg(feature = "hwdevice")]
106pub use memory_budget::adapter_memory_budget;
107#[cfg(all(target_os = "macos", feature = "hwdevice"))]
108pub use metal::{
109    ImportedMetalFrame, MetalEncoderPlanes, MetalImportLease, SharedMetalContext, SharedMetalError,
110    VideoToolboxCodec, gpu_wait_timeout as metal_wait_timeout, videotoolbox_hardware_decodes,
111    videotoolbox_surface_id,
112};
113pub use surface_budget::{SurfaceBudget, SurfaceBudgetError, SurfaceReservation, SurfaceUsage};
114pub use sync::{ExternalSyncPoint, NativeImageState};
115#[cfg(all(target_os = "linux", feature = "hwdevice"))]
116pub use vulkan::{
117    ImportTicketStats, ImportedVulkanFrame, SharedVulkanChoice, SharedVulkanContext,
118    SharedVulkanError, VideoEncodeExtent, VulkanCandidate, VulkanEncodeCopier, VulkanImportLease,
119    VulkanLayerVersion, adapter_pinned, choose_shared_vulkan, probe_vulkan_submission,
120    shared_device_index, vulkan_candidates,
121};
122
123/// Sets how long the platform interop helpers wait on GPU work before failing with a timeout.
124///
125/// Defaults to 30 seconds. A helper whose work did not finish in time leaks its GPU objects
126/// rather than freeing memory the device may still use.
127pub fn set_gpu_wait_timeout(timeout: std::time::Duration) {
128    #[cfg(all(target_os = "linux", feature = "hwdevice"))]
129    vulkan::set_wait_timeout(timeout);
130    #[cfg(all(target_os = "windows", feature = "hwdevice"))]
131    d3d12::set_wait_timeout(timeout);
132    #[cfg(all(target_os = "macos", feature = "hwdevice"))]
133    metal::set_wait_timeout(timeout);
134    let _ = timeout;
135}
136
137/// `timeout` in whole milliseconds for a Win32 wait. `0xFFFFFFFF` means `INFINITE`, so longer
138/// values clamp one below it and no setting makes a wait unbounded.
139#[cfg_attr(
140    not(all(target_os = "windows", feature = "hwdevice")),
141    allow(dead_code)
142)]
143fn win32_wait_milliseconds(timeout: std::time::Duration) -> u32 {
144    const INFINITE: u32 = u32::MAX;
145    u32::try_from(timeout.as_millis())
146        .unwrap_or(INFINITE)
147        .min(INFINITE - 1)
148}
149
150#[cfg(test)]
151mod tests {
152    use std::time::Duration;
153
154    #[test]
155    fn no_wait_timeout_reaches_win32_as_infinite() {
156        let milliseconds =
157            |value: u64| super::win32_wait_milliseconds(Duration::from_millis(value));
158        assert_eq!(milliseconds(30_000), 30_000);
159        assert_eq!(milliseconds(u64::from(u32::MAX) - 1), u32::MAX - 1);
160        assert_eq!(milliseconds(u64::from(u32::MAX)), u32::MAX - 1);
161        assert_eq!(milliseconds(u64::from(u32::MAX) + 1), u32::MAX - 1);
162        assert_eq!(milliseconds(u64::MAX), u32::MAX - 1);
163        assert_eq!(super::win32_wait_milliseconds(Duration::MAX), u32::MAX - 1);
164        // Below a millisecond is no wait, as `Duration::as_millis` rounds it.
165        assert_eq!(
166            super::win32_wait_milliseconds(Duration::from_micros(999)),
167            0
168        );
169    }
170}