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)
 | 
						|
 |