Skip to main content

oximedia_accel/
lib.rs

1//! Hardware acceleration abstraction layer for `OxiMedia`.
2//!
3//! `oximedia-accel` provides GPU-accelerated computation for video processing
4//! operations using Vulkan compute shaders. It includes CPU fallback paths
5//! for systems without GPU support.
6//!
7//! # Features
8//!
9//! - **Device Management**: Automatic GPU device enumeration and selection
10//! - **Buffer Management**: Efficient GPU memory allocation and transfer
11//! - **Compute Kernels**: Image scaling, color conversion, motion estimation
12//! - **CPU Fallback**: Automatic fallback to CPU implementations
13//! - **Safe Vulkan**: Uses vulkano for safe Vulkan API access
14//!
15//! # Architecture
16//!
17//! The acceleration layer is designed around the [`HardwareAccel`] trait,
18//! which provides a unified interface for both GPU and CPU implementations.
19//!
20//! ```text
21//! ┌─────────────────────────────────────────┐
22//! │         HardwareAccel Trait             │
23//! └─────────────────────────────────────────┘
24//!          │                      │
25//!          ▼                      ▼
26//!   ┌────────────┐         ┌────────────┐
27//!   │ VulkanAccel│         │ CpuFallback│
28//!   └────────────┘         └────────────┘
29//! ```
30//!
31//! # Example
32//!
33//! ```no_run
34//! use oximedia_accel::{AccelContext, HardwareAccel, ScaleFilter};
35//! use oximedia_core::types::PixelFormat;
36//!
37//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
38//! // Create acceleration context (automatically selects GPU or CPU)
39//! let accel = AccelContext::new()?;
40//!
41//! // Perform image scaling
42//! let input = vec![0u8; 1920 * 1080 * 3];
43//! let output = accel.scale_image(
44//!     &input,
45//!     1920, 1080,
46//!     1280, 720,
47//!     PixelFormat::Rgb24,
48//!     ScaleFilter::Bilinear,
49//! )?;
50//! # Ok(())
51//! # }
52//! ```
53
54#![warn(missing_docs)]
55#![allow(clippy::module_name_repetitions)]
56#![allow(clippy::too_many_arguments)]
57
58pub mod accel_profile;
59pub mod accel_stats;
60pub mod buffer;
61pub mod cache;
62pub mod cpu_fallback;
63pub mod device;
64pub mod device_caps;
65pub mod dispatch;
66pub mod error;
67pub mod fence_timeline;
68pub mod kernels;
69pub mod memory_arena;
70pub mod memory_bandwidth;
71pub mod ops;
72pub mod pipeline_accel;
73pub mod pool;
74pub mod prefetch;
75pub mod shaders;
76pub mod task_graph;
77pub mod task_scheduler;
78pub mod traits;
79pub mod vulkan;
80pub mod workgroup;
81
82// Re-export commonly used items
83pub use error::{AccelError, AccelResult};
84pub use traits::{HardwareAccel, ScaleFilter};
85
86// Re-export new feature types
87pub use accel_stats::{AccelProfiler, ProfileEntry};
88pub use memory_arena::{MemoryPressureMonitor, MemoryPressurePolicy, PressureLevel};
89pub use ops::convolution::{ConvolutionConfig, ConvolutionFilter, EdgeMode};
90pub use ops::deinterlace::{DeinterlaceConfig, DeinterlaceMethod, FieldOrder};
91
92// Re-export colour / HDR operations
93pub use ops::color::{
94    hlg_to_sdr_tonemap, pq_to_sdr_tonemap, rgb_to_yuv420, yuv420_to_rgb, YuvRange, YuvStandard,
95};
96pub use ops::{alpha_blend, alpha_blend_rgba};
97
98// Re-export workgroup auto-tuning
99pub use workgroup::{compute_optimal_workgroup, OpType};
100
101// Re-export descriptor pool
102pub mod descriptor_pool;
103
104use device::DeviceSelector;
105use std::sync::Arc;
106use vulkan::VulkanAccel;
107
108/// Main acceleration context that automatically selects GPU or CPU backend.
109///
110/// This is the primary entry point for hardware-accelerated operations.
111/// It will attempt to initialize Vulkan compute, falling back to CPU
112/// if GPU acceleration is unavailable.
113pub struct AccelContext {
114    backend: AccelBackend,
115}
116
117enum AccelBackend {
118    Vulkan(Arc<VulkanAccel>),
119    Cpu(Arc<cpu_fallback::CpuAccel>),
120}
121
122impl AccelContext {
123    /// Creates a new acceleration context.
124    ///
125    /// Attempts to initialize GPU acceleration first, falling back to CPU
126    /// if Vulkan is unavailable or device selection fails.
127    ///
128    /// # Errors
129    ///
130    /// Returns an error only if both GPU and CPU initialization fail,
131    /// which should be extremely rare.
132    pub fn new() -> AccelResult<Self> {
133        Self::with_device_selector(&DeviceSelector::default())
134    }
135
136    /// Creates a new acceleration context with a custom device selector.
137    ///
138    /// # Errors
139    ///
140    /// Returns an error if both GPU and CPU initialization fail.
141    pub fn with_device_selector(selector: &DeviceSelector) -> AccelResult<Self> {
142        match VulkanAccel::new(selector) {
143            Ok(vulkan) => {
144                tracing::info!("Hardware acceleration: Vulkan GPU");
145                Ok(Self {
146                    backend: AccelBackend::Vulkan(Arc::new(vulkan)),
147                })
148            }
149            Err(e) => {
150                tracing::warn!("Vulkan initialization failed: {}, using CPU fallback", e);
151                Ok(Self {
152                    backend: AccelBackend::Cpu(Arc::new(cpu_fallback::CpuAccel::new())),
153                })
154            }
155        }
156    }
157
158    /// Forces CPU-only acceleration (no GPU).
159    ///
160    /// Useful for testing or when GPU acceleration is explicitly unwanted.
161    #[must_use]
162    pub fn cpu_only() -> Self {
163        tracing::info!("Hardware acceleration: CPU only (forced)");
164        Self {
165            backend: AccelBackend::Cpu(Arc::new(cpu_fallback::CpuAccel::new())),
166        }
167    }
168
169    /// Returns `true` if using GPU acceleration.
170    #[must_use]
171    pub fn is_gpu_accelerated(&self) -> bool {
172        matches!(self.backend, AccelBackend::Vulkan(_))
173    }
174
175    /// Returns the name of the current backend.
176    #[must_use]
177    pub fn backend_name(&self) -> &str {
178        match &self.backend {
179            AccelBackend::Vulkan(v) => v.device_name(),
180            AccelBackend::Cpu(_) => "CPU",
181        }
182    }
183}
184
185impl HardwareAccel for AccelContext {
186    fn scale_image(
187        &self,
188        input: &[u8],
189        src_width: u32,
190        src_height: u32,
191        dst_width: u32,
192        dst_height: u32,
193        format: oximedia_core::PixelFormat,
194        filter: ScaleFilter,
195    ) -> AccelResult<Vec<u8>> {
196        match &self.backend {
197            AccelBackend::Vulkan(v) => v.scale_image(
198                input, src_width, src_height, dst_width, dst_height, format, filter,
199            ),
200            AccelBackend::Cpu(c) => c.scale_image(
201                input, src_width, src_height, dst_width, dst_height, format, filter,
202            ),
203        }
204    }
205
206    fn convert_color(
207        &self,
208        input: &[u8],
209        width: u32,
210        height: u32,
211        src_format: oximedia_core::PixelFormat,
212        dst_format: oximedia_core::PixelFormat,
213    ) -> AccelResult<Vec<u8>> {
214        match &self.backend {
215            AccelBackend::Vulkan(v) => {
216                v.convert_color(input, width, height, src_format, dst_format)
217            }
218            AccelBackend::Cpu(c) => c.convert_color(input, width, height, src_format, dst_format),
219        }
220    }
221
222    fn motion_estimation(
223        &self,
224        reference: &[u8],
225        current: &[u8],
226        width: u32,
227        height: u32,
228        block_size: u32,
229    ) -> AccelResult<Vec<(i16, i16)>> {
230        match &self.backend {
231            AccelBackend::Vulkan(v) => {
232                v.motion_estimation(reference, current, width, height, block_size)
233            }
234            AccelBackend::Cpu(c) => {
235                c.motion_estimation(reference, current, width, height, block_size)
236            }
237        }
238    }
239}