Skip to main content

edgefirst_image/gl/
mod.rs

1// SPDX-FileCopyrightText: Copyright 2025 Au-Zone Technologies
2// SPDX-License-Identifier: Apache-2.0
3
4#![cfg(target_os = "linux")]
5#![cfg(feature = "opengl")]
6
7macro_rules! function {
8    () => {{
9        fn f() {}
10        fn type_name_of<T>(_: T) -> &'static str {
11            std::any::type_name::<T>()
12        }
13        let name = type_name_of(f);
14
15        // Find and cut the rest of the path
16        match &name[..name.len() - 3].rfind(':') {
17            Some(pos) => &name[pos + 1..name.len() - 3],
18            None => &name[..name.len() - 3],
19        }
20    }};
21}
22
23mod cache;
24mod context;
25mod dma_import;
26mod processor;
27mod resources;
28mod shaders;
29mod tests;
30mod threaded;
31
32pub use context::probe_egl_displays;
33// These are accessed by sibling sub-modules via `super::context::` directly.
34// No re-export needed at the mod.rs level.
35pub use threaded::GLProcessorThreaded;
36
37/// Identifies the type of EGL display used for headless OpenGL ES rendering.
38///
39/// The HAL creates a surfaceless GLES 3.0 context
40/// (`EGL_KHR_surfaceless_context` + `EGL_KHR_no_config_context`) and
41/// renders exclusively through FBOs backed by EGLImages imported from
42/// DMA-buf file descriptors. No window or PBuffer surface is created.
43///
44/// Displays are probed in priority order: PlatformDevice first (zero
45/// external dependencies), then GBM, then Default. Use
46/// [`probe_egl_displays`] to discover which are available and
47/// [`ImageProcessorConfig::egl_display`](crate::ImageProcessorConfig::egl_display)
48/// to override the auto-detection.
49///
50/// # Display Types
51///
52/// - **`PlatformDevice`** — Uses `EGL_EXT_device_enumeration` to query
53///   available EGL devices via `eglQueryDevicesEXT`, then selects the first
54///   device with `eglGetPlatformDisplay(EGL_EXT_platform_device, ...)`.
55///   Headless and compositor-free with zero external library dependencies.
56///   Works on NVIDIA GPUs and newer Vivante drivers.
57///
58/// - **`Gbm`** — Opens a DRM render node (e.g. `/dev/dri/renderD128`) and
59///   creates a GBM (Generic Buffer Manager) device, then calls
60///   `eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, gbm_device)`. Requires
61///   `libgbm` and a DRM render node. Needed on ARM Mali (i.MX95) and older
62///   Vivante drivers that do not expose `EGL_EXT_platform_device`.
63///
64/// - **`Default`** — Calls `eglGetDisplay(EGL_DEFAULT_DISPLAY)`, letting the
65///   EGL implementation choose the display. On Wayland systems this connects
66///   to the compositor; on X11 it connects to the X server. May block on
67///   headless systems where a compositor is expected but not running.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
69pub enum EglDisplayKind {
70    Gbm,
71    PlatformDevice,
72    Default,
73}
74
75impl std::fmt::Display for EglDisplayKind {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        match self {
78            EglDisplayKind::Gbm => write!(f, "GBM"),
79            EglDisplayKind::PlatformDevice => write!(f, "PlatformDevice"),
80            EglDisplayKind::Default => write!(f, "Default"),
81        }
82    }
83}
84
85/// A validated, available EGL display discovered by [`probe_egl_displays`].
86#[derive(Debug, Clone)]
87pub struct EglDisplayInfo {
88    /// The type of EGL display.
89    pub kind: EglDisplayKind,
90    /// Human-readable description for logging/diagnostics
91    /// (e.g. "GBM via /dev/dri/renderD128").
92    pub description: String,
93}
94
95/// Tracks which data-transfer method is active for moving pixels
96/// between CPU memory and GPU textures/framebuffers.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub(crate) enum TransferBackend {
99    /// Zero-copy via EGLImage imported from DMA-buf file descriptors.
100    /// Available on i.MX8 (Vivante), i.MX95 (Mali), Jetson, and any
101    /// platform where `EGL_EXT_image_dma_buf_import` is present AND
102    /// the GPU can actually render through DMA-buf-backed textures.
103    DmaBuf,
104
105    /// GPU buffer via Pixel Buffer Object. Used when DMA-buf is unavailable
106    /// but OpenGL is present. Data stays in GPU-accessible memory.
107    Pbo,
108
109    /// Synchronous `glTexSubImage2D` upload + `glReadnPixels` readback.
110    /// Used when DMA-buf is unavailable or when the DMA-buf verification
111    /// probe fails (e.g. NVIDIA discrete GPUs where EGLImage creation
112    /// succeeds but rendered data is all zeros).
113    Sync,
114}
115
116impl TransferBackend {
117    /// Returns `true` if DMA-buf zero-copy is available.
118    pub(crate) fn is_dma(self) -> bool {
119        self == TransferBackend::DmaBuf
120    }
121}
122
123/// Interpolation mode for int8 proto textures (GL_R8I cannot use GL_LINEAR).
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum Int8InterpolationMode {
126    /// texelFetch at nearest texel — simplest, fastest GPU execution.
127    Nearest,
128    /// texelFetch × 4 neighbors with shader-computed bilinear weights (default).
129    Bilinear,
130    /// Two-pass: dequant int8→f16 FBO, then existing f16 shader with GL_LINEAR.
131    TwoPass,
132}
133
134/// A rectangular region of interest expressed as normalised [0, 1] coordinates.
135#[derive(Debug, Clone, Copy)]
136pub(super) struct RegionOfInterest {
137    pub(super) left: f32,
138    pub(super) top: f32,
139    pub(super) right: f32,
140    pub(super) bottom: f32,
141}