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