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