tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

egl_dmabuf.cc (27844B)


      1 /*
      2 *  Copyright 2021 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "modules/desktop_capture/linux/wayland/egl_dmabuf.h"
     12 
     13 #include <EGL/egl.h>
     14 #include <EGL/eglext.h>
     15 #include <EGL/eglplatform.h>
     16 #include <GL/gl.h>
     17 #include <GL/glext.h>
     18 #include <asm/ioctl.h>
     19 #include <dlfcn.h>
     20 #include <fcntl.h>
     21 #include <gbm.h>
     22 #include <gdk/gdk.h>
     23 #include <libdrm/drm_fourcc.h>
     24 #include <spa/param/video/raw.h>
     25 #include <unistd.h>
     26 #include <xf86drm.h>
     27 
     28 #include <algorithm>
     29 #include <cerrno>
     30 #include <cstdint>
     31 #include <cstring>
     32 #include <optional>
     33 #include <string>
     34 #include <vector>
     35 
     36 #include "absl/strings/string_view.h"
     37 #include "modules/desktop_capture/desktop_geometry.h"
     38 #include "rtc_base/checks.h"
     39 #include "rtc_base/logging.h"
     40 #include "rtc_base/sanitizer.h"
     41 #include "rtc_base/string_encode.h"
     42 
     43 namespace webrtc {
     44 
     45 // EGL
     46 typedef EGLBoolean (*eglBindAPI_func)(EGLenum api);
     47 typedef EGLContext (*eglCreateContext_func)(EGLDisplay dpy,
     48                                            EGLConfig config,
     49                                            EGLContext share_context,
     50                                            const EGLint* attrib_list);
     51 typedef EGLBoolean (*eglDestroyContext_func)(EGLDisplay display,
     52                                             EGLContext context);
     53 typedef EGLBoolean (*eglTerminate_func)(EGLDisplay display);
     54 typedef EGLImageKHR (*eglCreateImageKHR_func)(EGLDisplay dpy,
     55                                              EGLContext ctx,
     56                                              EGLenum target,
     57                                              EGLClientBuffer buffer,
     58                                              const EGLint* attrib_list);
     59 typedef EGLBoolean (*eglDestroyImageKHR_func)(EGLDisplay dpy,
     60                                              EGLImageKHR image);
     61 typedef EGLint (*eglGetError_func)(void);
     62 typedef void* (*eglGetProcAddress_func)(const char*);
     63 typedef EGLDisplay (*eglGetPlatformDisplayEXT_func)(EGLenum platform,
     64                                                    void* native_display,
     65                                                    const EGLint* attrib_list);
     66 typedef EGLDisplay (*eglGetPlatformDisplay_func)(EGLenum platform,
     67                                                 void* native_display,
     68                                                 const EGLAttrib* attrib_list);
     69 
     70 typedef EGLBoolean (*eglInitialize_func)(EGLDisplay dpy,
     71                                         EGLint* major,
     72                                         EGLint* minor);
     73 typedef EGLBoolean (*eglMakeCurrent_func)(EGLDisplay dpy,
     74                                          EGLSurface draw,
     75                                          EGLSurface read,
     76                                          EGLContext ctx);
     77 typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy,
     78                                                    EGLint max_formats,
     79                                                    EGLint* formats,
     80                                                    EGLint* num_formats);
     81 typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy,
     82                                                      EGLint format,
     83                                                      EGLint max_modifiers,
     84                                                      EGLuint64KHR* modifiers,
     85                                                      EGLBoolean* external_only,
     86                                                      EGLint* num_modifiers);
     87 typedef const char* (*eglQueryString_func)(EGLDisplay dpy, EGLint name);
     88 typedef void (*glEGLImageTargetTexture2DOES_func)(GLenum target,
     89                                                  GLeglImageOES image);
     90 
     91 // This doesn't follow naming conventions in WebRTC, where the naming
     92 // should look like e.g. egl_bind_api instead of EglBindAPI, however
     93 // we named them according to the exported functions they map to for
     94 // consistency.
     95 eglBindAPI_func EglBindAPI = nullptr;
     96 eglCreateContext_func EglCreateContext = nullptr;
     97 eglDestroyContext_func EglDestroyContext = nullptr;
     98 eglTerminate_func EglTerminate = nullptr;
     99 eglCreateImageKHR_func EglCreateImageKHR = nullptr;
    100 eglDestroyImageKHR_func EglDestroyImageKHR = nullptr;
    101 eglGetError_func EglGetError = nullptr;
    102 eglGetProcAddress_func EglGetProcAddress = nullptr;
    103 eglGetPlatformDisplayEXT_func EglGetPlatformDisplayEXT = nullptr;
    104 eglGetPlatformDisplay_func EglGetPlatformDisplay = nullptr;
    105 eglInitialize_func EglInitialize = nullptr;
    106 eglMakeCurrent_func EglMakeCurrent = nullptr;
    107 eglQueryDmaBufFormatsEXT_func EglQueryDmaBufFormatsEXT = nullptr;
    108 eglQueryDmaBufModifiersEXT_func EglQueryDmaBufModifiersEXT = nullptr;
    109 eglQueryString_func EglQueryString = nullptr;
    110 glEGLImageTargetTexture2DOES_func GlEGLImageTargetTexture2DOES = nullptr;
    111 
    112 // GL
    113 typedef void (*glBindTexture_func)(GLenum target, GLuint texture);
    114 typedef void (*glDeleteTextures_func)(GLsizei n, const GLuint* textures);
    115 typedef void (*glGenTextures_func)(GLsizei n, GLuint* textures);
    116 typedef GLenum (*glGetError_func)(void);
    117 typedef const GLubyte* (*glGetString_func)(GLenum name);
    118 typedef void (*glReadPixels_func)(GLint x,
    119                                  GLint y,
    120                                  GLsizei width,
    121                                  GLsizei height,
    122                                  GLenum format,
    123                                  GLenum type,
    124                                  void* data);
    125 typedef void (*glGenFramebuffers_func)(GLsizei n, GLuint* ids);
    126 typedef void (*glDeleteFramebuffers_func)(GLsizei n,
    127                                          const GLuint* framebuffers);
    128 typedef void (*glBindFramebuffer_func)(GLenum target, GLuint framebuffer);
    129 typedef void (*glFramebufferTexture2D_func)(GLenum target,
    130                                            GLenum attachment,
    131                                            GLenum textarget,
    132                                            GLuint texture,
    133                                            GLint level);
    134 typedef GLenum (*glCheckFramebufferStatus_func)(GLenum target);
    135 typedef void (*glTexParameteri_func)(GLenum target, GLenum pname, GLint param);
    136 typedef void* (*glXGetProcAddressARB_func)(const char*);
    137 
    138 // This doesn't follow naming conventions in WebRTC, where the naming
    139 // should look like e.g. egl_bind_api instead of EglBindAPI, however
    140 // we named them according to the exported functions they map to for
    141 // consistency.
    142 glBindTexture_func GlBindTexture = nullptr;
    143 glDeleteTextures_func GlDeleteTextures = nullptr;
    144 glGenTextures_func GlGenTextures = nullptr;
    145 glGetError_func GlGetError = nullptr;
    146 glGetString_func GlGetString = nullptr;
    147 glReadPixels_func GlReadPixels = nullptr;
    148 glGenFramebuffers_func GlGenFramebuffers = nullptr;
    149 glDeleteFramebuffers_func GlDeleteFramebuffers = nullptr;
    150 glBindFramebuffer_func GlBindFramebuffer = nullptr;
    151 glFramebufferTexture2D_func GlFramebufferTexture2D = nullptr;
    152 glCheckFramebufferStatus_func GlCheckFramebufferStatus = nullptr;
    153 glTexParameteri_func GlTexParameteri = nullptr;
    154 glXGetProcAddressARB_func GlXGetProcAddressARB = nullptr;
    155 
    156 static const std::string FormatGLError(GLenum err) {
    157  switch (err) {
    158    case GL_NO_ERROR:
    159      return "GL_NO_ERROR";
    160    case GL_INVALID_ENUM:
    161      return "GL_INVALID_ENUM";
    162    case GL_INVALID_VALUE:
    163      return "GL_INVALID_VALUE";
    164    case GL_INVALID_OPERATION:
    165      return "GL_INVALID_OPERATION";
    166    case GL_STACK_OVERFLOW:
    167      return "GL_STACK_OVERFLOW";
    168    case GL_STACK_UNDERFLOW:
    169      return "GL_STACK_UNDERFLOW";
    170    case GL_OUT_OF_MEMORY:
    171      return "GL_OUT_OF_MEMORY";
    172    default:
    173      return "GL error code: " + std::to_string(err);
    174  }
    175 }
    176 
    177 static const std::string FormatEGLError(EGLint err) {
    178  switch (err) {
    179    case EGL_NOT_INITIALIZED:
    180      return "EGL_NOT_INITIALIZED";
    181    case EGL_BAD_ACCESS:
    182      return "EGL_BAD_ACCESS";
    183    case EGL_BAD_ALLOC:
    184      return "EGL_BAD_ALLOC";
    185    case EGL_BAD_ATTRIBUTE:
    186      return "EGL_BAD_ATTRIBUTE";
    187    case EGL_BAD_CONTEXT:
    188      return "EGL_BAD_CONTEXT";
    189    case EGL_BAD_CONFIG:
    190      return "EGL_BAD_CONFIG";
    191    case EGL_BAD_CURRENT_SURFACE:
    192      return "EGL_BAD_CURRENT_SURFACE";
    193    case EGL_BAD_DISPLAY:
    194      return "EGL_BAD_DISPLAY";
    195    case EGL_BAD_SURFACE:
    196      return "EGL_BAD_SURFACE";
    197    case EGL_BAD_MATCH:
    198      return "EGL_BAD_MATCH";
    199    case EGL_BAD_PARAMETER:
    200      return "EGL_BAD_PARAMETER";
    201    case EGL_BAD_NATIVE_PIXMAP:
    202      return "EGL_BAD_NATIVE_PIXMAP";
    203    case EGL_BAD_NATIVE_WINDOW:
    204      return "EGL_BAD_NATIVE_WINDOW";
    205    case EGL_CONTEXT_LOST:
    206      return "EGL_CONTEXT_LOST";
    207    default:
    208      return "EGL error code: " + std::to_string(err);
    209  }
    210 }
    211 
    212 static uint32_t SpaPixelFormatToDrmFormat(uint32_t spa_format) {
    213  switch (spa_format) {
    214    case SPA_VIDEO_FORMAT_RGBA:
    215      return DRM_FORMAT_ABGR8888;
    216    case SPA_VIDEO_FORMAT_RGBx:
    217      return DRM_FORMAT_XBGR8888;
    218    case SPA_VIDEO_FORMAT_BGRA:
    219      return DRM_FORMAT_ARGB8888;
    220    case SPA_VIDEO_FORMAT_BGRx:
    221      return DRM_FORMAT_XRGB8888;
    222    default:
    223      return DRM_FORMAT_INVALID;
    224  }
    225 }
    226 
    227 static void CloseLibrary(void* library) {
    228  if (library) {
    229    dlclose(library);
    230    library = nullptr;
    231  }
    232 }
    233 
    234 static bool IsWaylandDisplay() {
    235  static auto sGdkWaylandDisplayGetType =
    236    (GType (*)(void))dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_type");
    237  if (!sGdkWaylandDisplayGetType) {
    238    return false;
    239  }
    240  return (G_TYPE_CHECK_INSTANCE_TYPE ((gdk_display_get_default()),
    241                                      sGdkWaylandDisplayGetType()));
    242 }
    243 
    244 static bool IsX11Display() {
    245  static auto sGdkX11DisplayGetType =
    246    (GType (*)(void))dlsym(RTLD_DEFAULT, "gdk_x11_display_get_type");
    247  if (!sGdkX11DisplayGetType) {
    248    return false;
    249  }
    250  return (G_TYPE_CHECK_INSTANCE_TYPE ((gdk_display_get_default()),
    251                                      sGdkX11DisplayGetType()));
    252 }
    253 
    254 static void* g_lib_egl = nullptr;
    255 
    256 RTC_NO_SANITIZE("cfi-icall")
    257 static bool OpenEGL() {
    258  g_lib_egl = dlopen("libEGL.so.1", RTLD_NOW | RTLD_GLOBAL);
    259  if (g_lib_egl) {
    260    EglGetProcAddress =
    261        (eglGetProcAddress_func)dlsym(g_lib_egl, "eglGetProcAddress");
    262    return EglGetProcAddress;
    263  }
    264 
    265  return false;
    266 }
    267 
    268 RTC_NO_SANITIZE("cfi-icall")
    269 static bool LoadEGL() {
    270  if (OpenEGL()) {
    271    EglBindAPI = (eglBindAPI_func)EglGetProcAddress("eglBindAPI");
    272    EglCreateContext =
    273        (eglCreateContext_func)EglGetProcAddress("eglCreateContext");
    274    EglDestroyContext =
    275        (eglDestroyContext_func)EglGetProcAddress("eglDestroyContext");
    276    EglTerminate = (eglTerminate_func)EglGetProcAddress("eglTerminate");
    277    EglCreateImageKHR =
    278        (eglCreateImageKHR_func)EglGetProcAddress("eglCreateImageKHR");
    279    EglDestroyImageKHR =
    280        (eglDestroyImageKHR_func)EglGetProcAddress("eglDestroyImageKHR");
    281    EglGetError = (eglGetError_func)EglGetProcAddress("eglGetError");
    282    EglGetPlatformDisplayEXT = (eglGetPlatformDisplayEXT_func)EglGetProcAddress(
    283        "eglGetPlatformDisplayEXT");
    284    EglGetPlatformDisplay =
    285        (eglGetPlatformDisplay_func)EglGetProcAddress("eglGetPlatformDisplay");
    286    EglInitialize = (eglInitialize_func)EglGetProcAddress("eglInitialize");
    287    EglMakeCurrent = (eglMakeCurrent_func)EglGetProcAddress("eglMakeCurrent");
    288    EglQueryString = (eglQueryString_func)EglGetProcAddress("eglQueryString");
    289    GlEGLImageTargetTexture2DOES =
    290        (glEGLImageTargetTexture2DOES_func)EglGetProcAddress(
    291            "glEGLImageTargetTexture2DOES");
    292 
    293    return EglBindAPI && EglCreateContext && EglCreateImageKHR &&
    294           EglTerminate && EglDestroyContext && EglDestroyImageKHR &&
    295           EglGetError && EglGetPlatformDisplayEXT && EglGetPlatformDisplay &&
    296           EglInitialize && EglMakeCurrent && EglQueryString &&
    297           GlEGLImageTargetTexture2DOES;
    298  }
    299 
    300  return false;
    301 }
    302 
    303 static void* g_lib_gl = nullptr;
    304 
    305 RTC_NO_SANITIZE("cfi-icall")
    306 static bool OpenGL() {
    307  std::vector<std::string> names = {"libGL.so.1", "libGL.so"};
    308  for (const std::string& name : names) {
    309    g_lib_gl = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL);
    310    if (g_lib_gl) {
    311      GlXGetProcAddressARB =
    312          (glXGetProcAddressARB_func)dlsym(g_lib_gl, "glXGetProcAddressARB");
    313      return GlXGetProcAddressARB;
    314    }
    315  }
    316 
    317  return false;
    318 }
    319 
    320 RTC_NO_SANITIZE("cfi-icall")
    321 static bool LoadGL() {
    322  if (OpenGL()) {
    323    GlGetString = (glGetString_func)GlXGetProcAddressARB("glGetString");
    324    if (!GlGetString) {
    325      return false;
    326    }
    327 
    328    GlBindTexture = (glBindTexture_func)GlXGetProcAddressARB("glBindTexture");
    329    GlDeleteTextures =
    330        (glDeleteTextures_func)GlXGetProcAddressARB("glDeleteTextures");
    331    GlGenTextures = (glGenTextures_func)GlXGetProcAddressARB("glGenTextures");
    332    GlGetError = (glGetError_func)GlXGetProcAddressARB("glGetError");
    333    GlReadPixels = (glReadPixels_func)GlXGetProcAddressARB("glReadPixels");
    334    GlGenFramebuffers =
    335        (glGenFramebuffers_func)GlXGetProcAddressARB("glGenFramebuffers");
    336    GlDeleteFramebuffers =
    337        (glDeleteFramebuffers_func)GlXGetProcAddressARB("glDeleteFramebuffers");
    338    GlBindFramebuffer =
    339        (glBindFramebuffer_func)GlXGetProcAddressARB("glBindFramebuffer");
    340    GlFramebufferTexture2D = (glFramebufferTexture2D_func)GlXGetProcAddressARB(
    341        "glFramebufferTexture2D");
    342    GlCheckFramebufferStatus =
    343        (glCheckFramebufferStatus_func)GlXGetProcAddressARB(
    344            "glCheckFramebufferStatus");
    345 
    346    GlTexParameteri =
    347        (glTexParameteri_func)GlXGetProcAddressARB("glTexParameteri");
    348 
    349    return GlBindTexture && GlDeleteTextures && GlGenTextures && GlGetError &&
    350           GlReadPixels && GlGenFramebuffers && GlDeleteFramebuffers &&
    351           GlBindFramebuffer && GlFramebufferTexture2D &&
    352           GlCheckFramebufferStatus && GlTexParameteri;
    353  }
    354 
    355  return false;
    356 }
    357 
    358 RTC_NO_SANITIZE("cfi-icall")
    359 EglDmaBuf::EglDmaBuf() {
    360  if (!LoadEGL()) {
    361    RTC_LOG(LS_ERROR) << "Unable to load EGL entry functions.";
    362    CloseLibrary(g_lib_egl);
    363    return;
    364  }
    365 
    366  if (!LoadGL()) {
    367    RTC_LOG(LS_ERROR) << "Failed to load OpenGL entry functions.";
    368    CloseLibrary(g_lib_gl);
    369    return;
    370  }
    371 
    372  if (!GetClientExtensions(EGL_NO_DISPLAY, EGL_EXTENSIONS)) {
    373    return;
    374  }
    375 
    376  bool has_platform_base_ext = false;
    377  bool has_platform_gbm_ext = false;
    378  bool has_khr_platform_gbm_ext = false;
    379 
    380  for (const auto& extension : egl_.extensions) {
    381    if (extension == "EGL_EXT_platform_base") {
    382      has_platform_base_ext = true;
    383      continue;
    384    } else if (extension == "EGL_MESA_platform_gbm") {
    385      has_platform_gbm_ext = true;
    386      continue;
    387    } else if (extension == "EGL_KHR_platform_gbm") {
    388      has_khr_platform_gbm_ext = true;
    389      continue;
    390    }
    391  }
    392 
    393  if (!has_platform_base_ext || !has_platform_gbm_ext ||
    394      !has_khr_platform_gbm_ext) {
    395    RTC_LOG(LS_ERROR) << "One of required EGL extensions is missing";
    396    return;
    397  }
    398 
    399  if (IsWaylandDisplay()) {
    400    egl_.display = EglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR,
    401                                         (void*)EGL_DEFAULT_DISPLAY, nullptr);
    402  } else if (IsX11Display()) {
    403    egl_.display = EglGetPlatformDisplay(EGL_PLATFORM_X11_KHR,
    404                                         (void*)EGL_DEFAULT_DISPLAY, nullptr);
    405  }
    406 
    407  if (egl_.display == EGL_NO_DISPLAY) {
    408    RTC_LOG(LS_ERROR) << "Failed to obtain default EGL display: "
    409                      << FormatEGLError(EglGetError()) << "\n"
    410                      << "Defaulting to using first available render node";
    411    std::optional<std::string> render_node = GetRenderNode();
    412    if (!render_node) {
    413      return;
    414    }
    415 
    416    drm_fd_ = open(render_node->c_str(), O_RDWR);
    417 
    418    if (drm_fd_ < 0) {
    419      RTC_LOG(LS_ERROR) << "Failed to open drm render node: "
    420                        << strerror(errno);
    421      return;
    422    }
    423 
    424    gbm_device_ = gbm_create_device(drm_fd_);
    425 
    426    if (!gbm_device_) {
    427      RTC_LOG(LS_ERROR) << "Cannot create GBM device: " << strerror(errno);
    428      close(drm_fd_);
    429      return;
    430    }
    431 
    432    // Use eglGetPlatformDisplayEXT() to get the display pointer
    433    // if the implementation supports it.
    434    egl_.display =
    435        EglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, gbm_device_, nullptr);
    436  }
    437 
    438  if (egl_.display == EGL_NO_DISPLAY) {
    439    RTC_LOG(LS_ERROR) << "Error during obtaining EGL display: "
    440                      << FormatEGLError(EglGetError());
    441    return;
    442  }
    443 
    444  EGLint major, minor;
    445  if (EglInitialize(egl_.display, &major, &minor) == EGL_FALSE) {
    446    RTC_LOG(LS_ERROR) << "Error during eglInitialize: "
    447                      << FormatEGLError(EglGetError());
    448    return;
    449  }
    450 
    451  if (EglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
    452    RTC_LOG(LS_ERROR) << "bind OpenGL API failed";
    453    return;
    454  }
    455 
    456  egl_.context =
    457      EglCreateContext(egl_.display, nullptr, EGL_NO_CONTEXT, nullptr);
    458 
    459  if (egl_.context == EGL_NO_CONTEXT) {
    460    RTC_LOG(LS_ERROR) << "Couldn't create EGL context: "
    461                      << FormatGLError(EglGetError());
    462    return;
    463  }
    464 
    465  if (!GetClientExtensions(egl_.display, EGL_EXTENSIONS)) {
    466    return;
    467  }
    468 
    469  bool has_image_dma_buf_import_modifiers_ext = false;
    470 
    471  for (const auto& extension : egl_.extensions) {
    472    if (extension == "EGL_EXT_image_dma_buf_import") {
    473      has_image_dma_buf_import_ext_ = true;
    474      continue;
    475    } else if (extension == "EGL_EXT_image_dma_buf_import_modifiers") {
    476      has_image_dma_buf_import_modifiers_ext = true;
    477      continue;
    478    }
    479  }
    480 
    481  if (has_image_dma_buf_import_ext_ && has_image_dma_buf_import_modifiers_ext) {
    482    EglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)EglGetProcAddress(
    483        "eglQueryDmaBufFormatsEXT");
    484    EglQueryDmaBufModifiersEXT =
    485        (eglQueryDmaBufModifiersEXT_func)EglGetProcAddress(
    486            "eglQueryDmaBufModifiersEXT");
    487  }
    488 
    489  RTC_LOG(LS_INFO) << "Egl initialization succeeded";
    490  egl_initialized_ = true;
    491 }
    492 
    493 RTC_NO_SANITIZE("cfi-icall")
    494 EglDmaBuf::~EglDmaBuf() {
    495  if (gbm_device_) {
    496    gbm_device_destroy(gbm_device_);
    497    close(drm_fd_);
    498  }
    499 
    500  if (egl_.context != EGL_NO_CONTEXT) {
    501    EglDestroyContext(egl_.display, egl_.context);
    502  }
    503 
    504  if (egl_.display != EGL_NO_DISPLAY) {
    505    EglTerminate(egl_.display);
    506  }
    507 
    508  if (fbo_) {
    509    GlDeleteFramebuffers(1, &fbo_);
    510  }
    511 
    512  if (texture_) {
    513    GlDeleteTextures(1, &texture_);
    514  }
    515 
    516  // BUG: crbug.com/1290566
    517  // Closing libEGL.so.1 when using NVidia drivers causes a crash
    518  // when EglGetPlatformDisplayEXT() is used, at least this one is enough
    519  // to be called to make it crash.
    520  // It also looks that libepoxy and glad don't dlclose it either
    521  // CloseLibrary(g_lib_egl);
    522  // CloseLibrary(g_lib_gl);
    523 }
    524 
    525 RTC_NO_SANITIZE("cfi-icall")
    526 bool EglDmaBuf::GetClientExtensions(EGLDisplay dpy, EGLint name) {
    527  // Get the list of client extensions
    528  const char* client_extensions_cstring = EglQueryString(dpy, name);
    529  if (!client_extensions_cstring) {
    530    // If eglQueryString() returned NULL, the implementation doesn't support
    531    // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error.
    532    RTC_LOG(LS_ERROR) << "No client extensions defined! "
    533                      << FormatEGLError(EglGetError());
    534    return false;
    535  }
    536 
    537  std::vector<absl::string_view> client_extensions =
    538      split(client_extensions_cstring, ' ');
    539  for (const auto& extension : client_extensions) {
    540    egl_.extensions.push_back(std::string(extension));
    541  }
    542 
    543  return true;
    544 }
    545 
    546 RTC_NO_SANITIZE("cfi-icall")
    547 bool EglDmaBuf::ImageFromDmaBuf(const DesktopSize& size,
    548                                uint32_t format,
    549                                const std::vector<PlaneData>& plane_datas,
    550                                uint64_t modifier,
    551                                const DesktopVector& offset,
    552                                const DesktopSize& buffer_size,
    553                                uint8_t* data) {
    554  if (!egl_initialized_) {
    555    return false;
    556  }
    557 
    558  if (plane_datas.empty()) {
    559    RTC_LOG(LS_ERROR) << "Failed to process buffer: invalid number of planes";
    560    return false;
    561  }
    562 
    563  EGLint attribs[47];
    564  int atti = 0;
    565 
    566  attribs[atti++] = EGL_WIDTH;
    567  attribs[atti++] = static_cast<EGLint>(size.width());
    568  attribs[atti++] = EGL_HEIGHT;
    569  attribs[atti++] = static_cast<EGLint>(size.height());
    570  attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
    571  attribs[atti++] = SpaPixelFormatToDrmFormat(format);
    572 
    573  if (!plane_datas.empty()) {
    574    attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
    575    attribs[atti++] = plane_datas[0].fd;
    576    attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
    577    attribs[atti++] = plane_datas[0].offset;
    578    attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
    579    attribs[atti++] = plane_datas[0].stride;
    580 
    581    if (modifier != DRM_FORMAT_MOD_INVALID) {
    582      attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
    583      attribs[atti++] = modifier & 0xFFFFFFFF;
    584      attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
    585      attribs[atti++] = modifier >> 32;
    586    }
    587  }
    588 
    589  if (plane_datas.size() > 1) {
    590    attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT;
    591    attribs[atti++] = plane_datas[1].fd;
    592    attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
    593    attribs[atti++] = plane_datas[1].offset;
    594    attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
    595    attribs[atti++] = plane_datas[1].stride;
    596 
    597    if (modifier != DRM_FORMAT_MOD_INVALID) {
    598      attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
    599      attribs[atti++] = modifier & 0xFFFFFFFF;
    600      attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
    601      attribs[atti++] = modifier >> 32;
    602    }
    603  }
    604 
    605  if (plane_datas.size() > 2) {
    606    attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT;
    607    attribs[atti++] = plane_datas[2].fd;
    608    attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
    609    attribs[atti++] = plane_datas[2].offset;
    610    attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
    611    attribs[atti++] = plane_datas[2].stride;
    612 
    613    if (modifier != DRM_FORMAT_MOD_INVALID) {
    614      attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
    615      attribs[atti++] = modifier & 0xFFFFFFFF;
    616      attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
    617      attribs[atti++] = modifier >> 32;
    618    }
    619  }
    620 
    621  if (plane_datas.size() > 3) {
    622    attribs[atti++] = EGL_DMA_BUF_PLANE3_FD_EXT;
    623    attribs[atti++] = plane_datas[3].fd;
    624    attribs[atti++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT;
    625    attribs[atti++] = plane_datas[3].offset;
    626    attribs[atti++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
    627    attribs[atti++] = plane_datas[3].stride;
    628 
    629    if (modifier != DRM_FORMAT_MOD_INVALID) {
    630      attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT;
    631      attribs[atti++] = modifier & 0xFFFFFFFF;
    632      attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT;
    633      attribs[atti++] = modifier >> 32;
    634    }
    635  }
    636 
    637  attribs[atti++] = EGL_NONE;
    638 
    639  // bind context to render thread
    640  EglMakeCurrent(egl_.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_.context);
    641 
    642  // create EGL image from attribute list
    643  EGLImageKHR image = EglCreateImageKHR(
    644      egl_.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs);
    645 
    646  if (image == EGL_NO_IMAGE) {
    647    RTC_LOG(LS_ERROR) << "Failed to record frame: Error creating EGLImage - "
    648                      << FormatEGLError(EglGetError());
    649    return false;
    650  }
    651 
    652  // create GL 2D texture for framebuffer
    653  if (!texture_) {
    654    GlGenTextures(1, &texture_);
    655    GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    656    GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    657    GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    658    GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    659  }
    660  GlBindTexture(GL_TEXTURE_2D, texture_);
    661  GlEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
    662 
    663  if (!fbo_) {
    664    GlGenFramebuffers(1, &fbo_);
    665  }
    666 
    667  GlBindFramebuffer(GL_FRAMEBUFFER, fbo_);
    668  GlFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
    669                         texture_, 0);
    670  if (GlCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
    671    RTC_LOG(LS_ERROR) << "Failed to bind DMA buf framebuffer";
    672    EglDestroyImageKHR(egl_.display, image);
    673    return false;
    674  }
    675 
    676  GLenum gl_format = GL_BGRA;
    677  switch (format) {
    678    case SPA_VIDEO_FORMAT_RGBx:
    679      gl_format = GL_RGBA;
    680      break;
    681    case SPA_VIDEO_FORMAT_RGBA:
    682      gl_format = GL_RGBA;
    683      break;
    684    case SPA_VIDEO_FORMAT_BGRx:
    685      gl_format = GL_BGRA;
    686      break;
    687    default:
    688      gl_format = GL_BGRA;
    689      break;
    690  }
    691 
    692  GlReadPixels(offset.x(), offset.y(), buffer_size.width(),
    693               buffer_size.height(), gl_format, GL_UNSIGNED_BYTE, data);
    694 
    695  const GLenum error = GlGetError();
    696  if (error) {
    697    RTC_LOG(LS_ERROR) << "Failed to get image from DMA buffer.";
    698  }
    699 
    700  EglDestroyImageKHR(egl_.display, image);
    701 
    702  return !error;
    703 }
    704 
    705 RTC_NO_SANITIZE("cfi-icall")
    706 std::vector<uint64_t> EglDmaBuf::QueryDmaBufModifiers(uint32_t format) {
    707  if (!egl_initialized_) {
    708    return {};
    709  }
    710 
    711  // Explicit modifiers not supported, return just DRM_FORMAT_MOD_INVALID as we
    712  // can still use modifier-less DMA-BUFs if we have required extension
    713  if (EglQueryDmaBufFormatsEXT == nullptr ||
    714      EglQueryDmaBufModifiersEXT == nullptr) {
    715    return has_image_dma_buf_import_ext_
    716               ? std::vector<uint64_t>{DRM_FORMAT_MOD_INVALID}
    717               : std::vector<uint64_t>{};
    718  }
    719 
    720  uint32_t drm_format = SpaPixelFormatToDrmFormat(format);
    721  // Should never happen as it's us who controls the list of supported formats
    722  RTC_DCHECK(drm_format != DRM_FORMAT_INVALID);
    723 
    724  EGLint count = 0;
    725  EGLBoolean success =
    726      EglQueryDmaBufFormatsEXT(egl_.display, 0, nullptr, &count);
    727 
    728  if (!success || !count) {
    729    RTC_LOG(LS_WARNING) << "Cannot query the number of formats.";
    730    return {DRM_FORMAT_MOD_INVALID};
    731  }
    732 
    733  std::vector<uint32_t> formats(count);
    734  if (!EglQueryDmaBufFormatsEXT(egl_.display, count,
    735                                reinterpret_cast<EGLint*>(formats.data()),
    736                                &count)) {
    737    RTC_LOG(LS_WARNING) << "Cannot query a list of formats.";
    738    return {DRM_FORMAT_MOD_INVALID};
    739  }
    740 
    741  if (std::find(formats.begin(), formats.end(), drm_format) == formats.end()) {
    742    RTC_LOG(LS_WARNING) << "Format " << drm_format
    743                        << " not supported for modifiers.";
    744    return {DRM_FORMAT_MOD_INVALID};
    745  }
    746 
    747  success = EglQueryDmaBufModifiersEXT(egl_.display, drm_format, 0, nullptr,
    748                                       nullptr, &count);
    749 
    750  if (!success || !count) {
    751    RTC_LOG(LS_WARNING) << "Cannot query the number of modifiers.";
    752    return {DRM_FORMAT_MOD_INVALID};
    753  }
    754 
    755  std::vector<uint64_t> modifiers(count);
    756  if (!EglQueryDmaBufModifiersEXT(egl_.display, drm_format, count,
    757                                  modifiers.data(), nullptr, &count)) {
    758    RTC_LOG(LS_WARNING) << "Cannot query a list of modifiers.";
    759  }
    760 
    761  // Support modifier-less buffers
    762  modifiers.push_back(DRM_FORMAT_MOD_INVALID);
    763  return modifiers;
    764 }
    765 
    766 std::optional<std::string> EglDmaBuf::GetRenderNode() {
    767  int max_devices = drmGetDevices2(0, nullptr, 0);
    768  if (max_devices <= 0) {
    769    RTC_LOG(LS_ERROR) << "drmGetDevices2() has not found any devices (errno="
    770                      << -max_devices << ")";
    771    return std::nullopt;
    772  }
    773 
    774  std::vector<drmDevicePtr> devices(max_devices);
    775  int ret = drmGetDevices2(0, devices.data(), max_devices);
    776  if (ret < 0) {
    777    RTC_LOG(LS_ERROR) << "drmGetDevices2() returned an error " << ret;
    778    return std::nullopt;
    779  }
    780 
    781  std::string render_node;
    782 
    783  for (const drmDevicePtr& device : devices) {
    784    if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
    785      render_node = device->nodes[DRM_NODE_RENDER];
    786      break;
    787    }
    788  }
    789 
    790  drmFreeDevices(devices.data(), ret);
    791  return render_node;
    792 }
    793 
    794 }  // namespace webrtc