You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
5.0 KiB
177 lines
5.0 KiB
import os
|
|
import cffi
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
from openpilot.common.swaglog import cloudlog
|
|
|
|
|
|
# EGL constants
|
|
EGL_LINUX_DMA_BUF_EXT = 0x3270
|
|
EGL_WIDTH = 0x3057
|
|
EGL_HEIGHT = 0x3056
|
|
EGL_LINUX_DRM_FOURCC_EXT = 0x3271
|
|
EGL_DMA_BUF_PLANE0_FD_EXT = 0x3272
|
|
EGL_DMA_BUF_PLANE0_OFFSET_EXT = 0x3273
|
|
EGL_DMA_BUF_PLANE0_PITCH_EXT = 0x3274
|
|
EGL_DMA_BUF_PLANE1_FD_EXT = 0x3275
|
|
EGL_DMA_BUF_PLANE1_OFFSET_EXT = 0x3276
|
|
EGL_DMA_BUF_PLANE1_PITCH_EXT = 0x3277
|
|
EGL_NONE = 0x3038
|
|
GL_TEXTURE0 = 0x84C0
|
|
GL_TEXTURE_EXTERNAL_OES = 0x8D65
|
|
|
|
# DRM Format for NV12
|
|
DRM_FORMAT_NV12 = 842094158
|
|
|
|
@dataclass
|
|
class EGLImage:
|
|
"""Container for EGL image and associated resources"""
|
|
|
|
egl_image: Any
|
|
fd: int
|
|
|
|
|
|
@dataclass
|
|
class EGLState:
|
|
"""Container for all EGL-related state"""
|
|
|
|
initialized: bool = False
|
|
ffi: Any = None
|
|
egl_lib: Any = None
|
|
gles_lib: Any = None
|
|
|
|
# EGL display connection - shared across all users
|
|
display: Any = None
|
|
|
|
# Constants
|
|
NO_CONTEXT: Any = None
|
|
NO_DISPLAY: Any = None
|
|
NO_IMAGE_KHR: Any = None
|
|
|
|
# Function pointers
|
|
get_current_display: Any = None
|
|
create_image_khr: Any = None
|
|
destroy_image_khr: Any = None
|
|
image_target_texture: Any = None
|
|
get_error: Any = None
|
|
bind_texture: Any = None
|
|
active_texture: Any = None
|
|
|
|
|
|
# Create a single instance of the state
|
|
_egl = EGLState()
|
|
|
|
|
|
def init_egl() -> bool:
|
|
"""Initialize EGL and load necessary functions"""
|
|
global _egl
|
|
|
|
# Don't re-initialize if already done
|
|
if _egl.initialized:
|
|
return True
|
|
|
|
try:
|
|
_egl.ffi = cffi.FFI()
|
|
_egl.ffi.cdef("""
|
|
typedef int EGLint;
|
|
typedef unsigned int EGLBoolean;
|
|
typedef unsigned int EGLenum;
|
|
typedef unsigned int GLenum;
|
|
typedef void *EGLContext;
|
|
typedef void *EGLDisplay;
|
|
typedef void *EGLClientBuffer;
|
|
typedef void *EGLImageKHR;
|
|
typedef void *GLeglImageOES;
|
|
|
|
EGLDisplay eglGetCurrentDisplay(void);
|
|
EGLint eglGetError(void);
|
|
EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx,
|
|
EGLenum target, EGLClientBuffer buffer,
|
|
const EGLint *attrib_list);
|
|
EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image);
|
|
void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image);
|
|
void glBindTexture(GLenum target, unsigned int texture);
|
|
void glActiveTexture(GLenum texture);
|
|
""")
|
|
|
|
# Load libraries
|
|
_egl.egl_lib = _egl.ffi.dlopen("libEGL.so")
|
|
_egl.gles_lib = _egl.ffi.dlopen("libGLESv2.so")
|
|
|
|
# Cast NULL pointers
|
|
_egl.NO_CONTEXT = _egl.ffi.cast("void *", 0)
|
|
_egl.NO_DISPLAY = _egl.ffi.cast("void *", 0)
|
|
_egl.NO_IMAGE_KHR = _egl.ffi.cast("void *", 0)
|
|
|
|
# Bind functions
|
|
_egl.get_current_display = _egl.egl_lib.eglGetCurrentDisplay
|
|
_egl.create_image_khr = _egl.egl_lib.eglCreateImageKHR
|
|
_egl.destroy_image_khr = _egl.egl_lib.eglDestroyImageKHR
|
|
_egl.image_target_texture = _egl.gles_lib.glEGLImageTargetTexture2DOES
|
|
_egl.get_error = _egl.egl_lib.eglGetError
|
|
_egl.bind_texture = _egl.gles_lib.glBindTexture
|
|
_egl.active_texture = _egl.gles_lib.glActiveTexture
|
|
|
|
# Initialize EGL display once here
|
|
_egl.display = _egl.get_current_display()
|
|
if _egl.display == _egl.NO_DISPLAY:
|
|
raise RuntimeError("Failed to get EGL display")
|
|
|
|
_egl.initialized = True
|
|
return True
|
|
except Exception as e:
|
|
cloudlog.exception(f"EGL initialization failed: {e}")
|
|
_egl.initialized = False
|
|
return False
|
|
|
|
|
|
def create_egl_image(width: int, height: int, stride: int, fd: int, uv_offset: int) -> EGLImage | None:
|
|
assert _egl.initialized, "EGL not initialized"
|
|
|
|
# Duplicate fd since EGL needs it
|
|
dup_fd = os.dup(fd)
|
|
|
|
# Create image attributes for EGL
|
|
img_attrs = [
|
|
EGL_WIDTH, width,
|
|
EGL_HEIGHT, height,
|
|
EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_NV12,
|
|
EGL_DMA_BUF_PLANE0_FD_EXT, dup_fd,
|
|
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
|
|
EGL_DMA_BUF_PLANE0_PITCH_EXT, stride,
|
|
EGL_DMA_BUF_PLANE1_FD_EXT, dup_fd,
|
|
EGL_DMA_BUF_PLANE1_OFFSET_EXT, uv_offset,
|
|
EGL_DMA_BUF_PLANE1_PITCH_EXT, stride,
|
|
EGL_NONE
|
|
]
|
|
|
|
attr_array = _egl.ffi.new("int[]", img_attrs)
|
|
egl_image = _egl.create_image_khr(_egl.display, _egl.NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, _egl.ffi.NULL, attr_array)
|
|
|
|
if egl_image == _egl.NO_IMAGE_KHR:
|
|
cloudlog.error(f"Failed to create EGL image: {_egl.get_error()}")
|
|
os.close(dup_fd)
|
|
return None
|
|
|
|
return EGLImage(egl_image=egl_image, fd=dup_fd)
|
|
|
|
|
|
def destroy_egl_image(egl_image: EGLImage) -> None:
|
|
assert _egl.initialized, "EGL not initialized"
|
|
|
|
_egl.destroy_image_khr(_egl.display, egl_image.egl_image)
|
|
|
|
# Close the duplicated fd we created in create_egl_image()
|
|
# We need to handle OSError since the fd might already be closed
|
|
try:
|
|
os.close(egl_image.fd)
|
|
except OSError:
|
|
pass
|
|
|
|
|
|
def bind_egl_image_to_texture(texture_id: int, egl_image: EGLImage) -> None:
|
|
assert _egl.initialized, "EGL not initialized"
|
|
|
|
_egl.active_texture(GL_TEXTURE0)
|
|
_egl.bind_texture(GL_TEXTURE_EXTERNAL_OES, texture_id)
|
|
_egl.image_target_texture(GL_TEXTURE_EXTERNAL_OES, egl_image.egl_image)
|
|
|