system/ui: add EGL support to CameraView (#35338)
* add EGL support to CameraView * view 3 cameras * use a more direct approach * add new line * cleanup * cleanup close() * extract EGL to a seperate file * cleanup * add try/except to close() * rename egl_textures * improve implementationpull/35345/head
parent
c24f349807
commit
840ced5005
2 changed files with 301 additions and 28 deletions
@ -0,0 +1,177 @@ |
||||
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) |
Loading…
Reference in new issue