The open source OpenXR runtime
at mr/scanout-values 619 lines 16 kB view raw
1// Copyright 2019-2021, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Glue code to EGL client side glue code. 6 * @author Drew DeVault <sir@cmpwn.com> 7 * @author Simon Ser <contact@emersion.fr> 8 * @author Jakob Bornecrantz <jakob@collabora.com> 9 * @ingroup comp_client 10 */ 11 12#include "xrt/xrt_config_os.h" 13#include "xrt/xrt_config_have.h" 14#include "xrt/xrt_gfx_egl.h" 15#include "xrt/xrt_handles.h" 16 17#include "util/u_misc.h" 18#include "util/u_logging.h" 19#include "util/u_debug.h" 20 21#include "ogl/egl_api.h" 22#include "ogl/ogl_api.h" 23 24#include "client/comp_gl_client.h" 25#include "client/comp_egl_client.h" 26#include "client/comp_gl_memobj_swapchain.h" 27#include "client/comp_gl_eglimage_swapchain.h" 28 29#include <stdio.h> 30#include <stdlib.h> 31 32#ifndef XRT_HAVE_EGL 33#error "This file shouldn't be compiled without EGL" 34#endif 35 36 37/* 38 * 39 * Logging. 40 * 41 */ 42 43static enum u_logging_level log_level; 44 45#define EGL_TRACE(...) U_LOG_IFL_T(log_level, __VA_ARGS__) 46#define EGL_DEBUG(...) U_LOG_IFL_D(log_level, __VA_ARGS__) 47#define EGL_INFO(...) U_LOG_IFL_I(log_level, __VA_ARGS__) 48#define EGL_WARN(...) U_LOG_IFL_W(log_level, __VA_ARGS__) 49#define EGL_ERROR(...) U_LOG_IFL_E(log_level, __VA_ARGS__) 50 51DEBUG_GET_ONCE_LOG_OPTION(egl_log, "EGL_LOG", U_LOGGING_INFO) 52 53 54/* 55 * 56 * Declarations. 57 * 58 */ 59 60#ifdef XRT_OS_ANDROID 61typedef const char *EGLAPIENTRY (*PFNEGLQUERYSTRINGIMPLEMENTATIONANDROIDPROC)(EGLDisplay dpy, EGLint name); 62#endif 63 64// Not forward declared by mesa 65typedef EGLBoolean 66 EGLAPIENTRY (*PFNEGLMAKECURRENTPROC)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); 67 68static xrt_result_t 69client_egl_insert_fence(struct xrt_compositor *xc, xrt_graphics_sync_handle_t *out_handle); 70 71 72/* 73 * 74 * Old helper. 75 * 76 */ 77 78static inline void 79save_context(struct client_egl_context *ctx) 80{ 81 ctx->dpy = eglGetCurrentDisplay(); 82 ctx->ctx = EGL_NO_CONTEXT; 83 ctx->read = EGL_NO_SURFACE; 84 ctx->draw = EGL_NO_SURFACE; 85 86 if (ctx->dpy != EGL_NO_DISPLAY) { 87 ctx->ctx = eglGetCurrentContext(); 88 ctx->read = eglGetCurrentSurface(EGL_READ); 89 ctx->draw = eglGetCurrentSurface(EGL_DRAW); 90 } 91} 92 93static inline bool 94restore_context(struct client_egl_context *ctx) 95{ 96 /* We're using the current display if we're trying to restore a null context */ 97 EGLDisplay dpy = ctx->dpy == EGL_NO_DISPLAY ? eglGetCurrentDisplay() : ctx->dpy; 98 99 if (dpy == EGL_NO_DISPLAY) { 100 /* If the current display is also null then the call is a no-op */ 101 return true; 102 } 103 104 return eglMakeCurrent(dpy, ctx->draw, ctx->read, ctx->ctx); 105} 106 107 108/* 109 * 110 * Helper functions. 111 * 112 */ 113 114static const char * 115egl_error_str(EGLint ret) 116{ 117 switch (ret) { 118 case EGL_SUCCESS: return "EGL_SUCCESS"; 119 case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; 120 case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; 121 case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; 122 case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; 123 case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; 124 case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; 125 case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; 126 case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; 127 case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; 128 case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; 129 case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; 130 case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; 131 case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; 132 case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; 133 default: return "EGL_<UNKNOWN>"; 134 } 135} 136 137static inline void 138destroy_context_with_check(EGLDisplay display, EGLContext context, const char *func) 139{ 140 EGLBoolean eret = eglDestroyContext(display, context); 141 if (eret == EGL_FALSE) { 142 U_LOG_E("eglDestroyContext: %s (%s)", egl_error_str(eglGetError()), func); 143 } 144} 145 146#define DESTROY_CONTEXT(DPY, CTX) destroy_context_with_check(DPY, CTX, __func__) 147 148XRT_MAYBE_UNUSED static bool 149has_extension(const char *extensions, const char *ext) 150{ 151 const char *loc = NULL; 152 const char *terminator = NULL; 153 154 if (extensions == NULL) { 155 return false; 156 } 157 158 while (1) { 159 loc = strstr(extensions, ext); 160 if (loc == NULL) { 161 return false; 162 } 163 164 terminator = loc + strlen(ext); 165 if ((loc == extensions || *(loc - 1) == ' ') && (*terminator == ' ' || *terminator == '\0')) { 166 return true; 167 } 168 extensions = terminator; 169 } 170} 171 172 173/* 174 * 175 * Creation helper functions. 176 * 177 */ 178 179static void 180ensure_native_fence_is_loaded(EGLDisplay dpy, PFNEGLGETPROCADDRESSPROC get_gl_procaddr) 181{ 182#ifdef XRT_OS_ANDROID 183 // clang-format off 184 PFNEGLQUERYSTRINGIMPLEMENTATIONANDROIDPROC eglQueryStringImplementationANDROID; 185 // clang-format on 186 187 eglQueryStringImplementationANDROID = 188 (PFNEGLQUERYSTRINGIMPLEMENTATIONANDROIDPROC)get_gl_procaddr("eglQueryStringImplementationANDROID"); 189 190 // On Android, EGL_ANDROID_native_fence_sync only shows up in this 191 // extension list, not the normal one. 192 const char *ext = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); 193 if (!has_extension(ext, "EGL_ANDROID_native_fence_sync")) { 194 return; 195 } 196 197 GLAD_EGL_ANDROID_native_fence_sync = true; 198 glad_eglDupNativeFenceFDANDROID = 199 (PFNEGLDUPNATIVEFENCEFDANDROIDPROC)get_gl_procaddr("eglDupNativeFenceFDANDROID"); 200#endif 201} 202 203static xrt_result_t 204create_context( 205 EGLDisplay display, EGLConfig config, EGLContext app_context, EGLint api_type, EGLContext *out_our_context) 206{ 207 EGLint old_api_type = eglQueryAPI(); 208 209 eglBindAPI(api_type); 210 211 size_t attrc = 0; 212 EGLint attrs[9] = {0}; 213 214 attrs[attrc++] = EGL_CONTEXT_MAJOR_VERSION; 215 attrs[attrc++] = 3; 216 // Panfrost only supports 3.1 217 attrs[attrc++] = EGL_CONTEXT_MINOR_VERSION; 218 attrs[attrc++] = 1; 219 220 if (api_type == EGL_OPENGL_API) { 221 attrs[attrc++] = EGL_CONTEXT_OPENGL_PROFILE_MASK; 222 attrs[attrc++] = EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT; 223 } 224 225 EGLint strategy; 226 if (api_type == EGL_OPENGL_ES_API && 227 eglQueryContext(display, app_context, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, &strategy)) { 228 attrs[attrc++] = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT; 229 attrs[attrc++] = strategy; 230 } 231 232 attrs[attrc++] = EGL_NONE; 233 assert(attrc <= ARRAY_SIZE(attrs)); 234 235 EGLContext our_context = eglCreateContext(display, config, app_context, attrs); 236 237 // Restore old API type. 238 if (old_api_type == EGL_NONE) { 239 eglBindAPI(old_api_type); 240 } 241 242 if (our_context == EGL_NO_CONTEXT) { 243 EGL_ERROR("eglCreateContext: %s", egl_error_str(eglGetError())); 244 return XRT_ERROR_OPENGL; 245 } 246 247 *out_our_context = our_context; 248 249 return XRT_SUCCESS; 250} 251 252static xrt_result_t 253load_gl_functions(EGLint egl_client_type, PFNEGLGETPROCADDRESSPROC get_gl_procaddr) 254{ 255 switch (egl_client_type) { 256 case EGL_OPENGL_API: 257#if defined(XRT_HAVE_OPENGL) 258 EGL_DEBUG("Loading GL functions"); 259 gladLoadGL(get_gl_procaddr); 260 break; 261#else 262 EGL_ERROR("OpenGL support not including in this runtime build"); 263 return XRT_ERROR_OPENGL; 264#endif 265 266 case EGL_OPENGL_ES_API: 267#if defined(XRT_HAVE_OPENGLES) 268 EGL_DEBUG("Loading GLES2 functions"); 269 gladLoadGLES2(get_gl_procaddr); 270 break; 271#else 272 EGL_ERROR("OpenGL|ES support not including in this runtime build"); 273 return XRT_ERROR_OPENGL; 274#endif 275 default: EGL_ERROR("Unsupported EGL client type: 0x%x", egl_client_type); return XRT_ERROR_OPENGL; 276 } 277 278 if (glGetString == NULL) { 279 EGL_ERROR("glGetString not loaded!"); 280 return XRT_ERROR_OPENGL; 281 } 282 283 return XRT_SUCCESS; 284} 285 286static xrt_result_t 287check_context_and_debug_print(EGLint egl_client_type) 288{ 289 EGL_DEBUG( // 290 "OpenGL context:" // 291 "\n\tGL_VERSION: %s" // 292 "\n\tGL_RENDERER: %s" // 293 "\n\tGL_VENDOR: %s", // 294 glGetString(GL_VERSION), // 295 glGetString(GL_RENDERER), // 296 glGetString(GL_VENDOR)); // 297 298 299 /* 300 * If a renderer is old enough to not support OpenGL(ES) 3 or above 301 * it won't support Monado at all, it's not a hard requirement and 302 * lets us detect weird errors early on some platforms. 303 */ 304 if (!GLAD_GL_VERSION_3_0 && !GLAD_GL_ES_VERSION_3_0) { 305 switch (egl_client_type) { 306 default: EGL_ERROR("Unknown OpenGL version!"); break; 307 case EGL_OPENGL_API: EGL_ERROR("Must have OpenGL 3.0 or above!"); break; 308 case EGL_OPENGL_ES_API: EGL_ERROR("Must have OpenGL ES 3.0 or above!"); break; 309 } 310 311 return XRT_ERROR_OPENGL; 312 } 313 314 315 EGL_DEBUG("Extension availability:"); 316#define DUMP_EXTENSION_STATUS(EXT) EGL_DEBUG(" - " #EXT ": %s", GLAD_##EXT ? "true" : "false") 317 318 DUMP_EXTENSION_STATUS(GL_EXT_memory_object); 319 DUMP_EXTENSION_STATUS(GL_EXT_memory_object_fd); 320 DUMP_EXTENSION_STATUS(GL_EXT_memory_object_win32); 321 DUMP_EXTENSION_STATUS(GL_OES_EGL_image_external); 322 323 DUMP_EXTENSION_STATUS(EGL_ANDROID_get_native_client_buffer); 324 DUMP_EXTENSION_STATUS(EGL_ANDROID_native_fence_sync); 325 DUMP_EXTENSION_STATUS(EGL_EXT_image_dma_buf_import_modifiers); 326 DUMP_EXTENSION_STATUS(EGL_KHR_fence_sync); 327 DUMP_EXTENSION_STATUS(EGL_KHR_image); 328 DUMP_EXTENSION_STATUS(EGL_KHR_image_base); 329 DUMP_EXTENSION_STATUS(EGL_KHR_reusable_sync); 330 DUMP_EXTENSION_STATUS(EGL_KHR_wait_sync); 331 332#undef DUMP_EXTENSION_STATUS 333 334 335 return XRT_SUCCESS; 336} 337 338static xrt_result_t 339get_client_gl_functions(client_gl_swapchain_create_func_t *out_sc_create_func, 340 client_gl_insert_fence_func_t *out_insert_fence) 341{ 342 client_gl_swapchain_create_func_t sc_create_func = NULL; 343 client_gl_insert_fence_func_t insert_fence_func = NULL; 344 345 346#if defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_FD) 347 348 if (GLAD_GL_EXT_memory_object && GLAD_GL_EXT_memory_object_fd) { 349 EGL_DEBUG("Using GL memory object swapchain implementation"); 350 sc_create_func = client_gl_memobj_swapchain_create; 351 } 352 353 if (sc_create_func == NULL && GLAD_EGL_EXT_image_dma_buf_import) { 354 EGL_DEBUG("Using EGL_Image swapchain implementation"); 355 sc_create_func = client_gl_eglimage_swapchain_create; 356 } 357 358 if (sc_create_func == NULL) { 359 EGL_ERROR( 360 "Could not find a required extension: need either EGL_EXT_image_dma_buf_import or " 361 "GL_EXT_memory_object_fd"); 362 return XRT_ERROR_OPENGL; 363 } 364 365#elif defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER) 366 367 EGL_DEBUG("Using EGL_Image swapchain implementation with AHardwareBuffer"); 368 sc_create_func = client_gl_eglimage_swapchain_create; 369 370#endif 371 372 /* 373 * For now, only use the insert_fence callback only if 374 * EGL_ANDROID_native_fence_sync is available, revisit this when a more 375 * generic synchronization mechanism is implemented. 376 */ 377 if (GLAD_EGL_ANDROID_native_fence_sync) { 378 insert_fence_func = client_egl_insert_fence; 379 } 380 381 *out_sc_create_func = sc_create_func; 382 *out_insert_fence = insert_fence_func; 383 384 return XRT_SUCCESS; 385} 386 387 388/* 389 * 390 * GL callback functions. 391 * 392 */ 393 394static xrt_result_t 395client_egl_insert_fence(struct xrt_compositor *xc, xrt_graphics_sync_handle_t *out_handle) 396{ 397 struct client_egl_compositor *ceglc = client_egl_compositor(xc); 398 399 *out_handle = XRT_GRAPHICS_SYNC_HANDLE_INVALID; 400 EGLDisplay dpy = ceglc->current.dpy; 401 402#ifdef XRT_GRAPHICS_SYNC_HANDLE_IS_FD 403 // https://registry.khronos.org/EGL/extensions/ANDROID/EGL_ANDROID_native_fence_sync.txt 404 // create also inserts the fence in the command stream 405 EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); 406 if (sync == EGL_NO_SYNC_KHR) { 407 EGL_ERROR("Failed to insert fence!"); 408 return XRT_ERROR_FENCE_CREATE_FAILED; 409 } 410 411 // Flush needed to create native FD 412 glFlush(); 413 414 int fence_fd = eglDupNativeFenceFDANDROID(dpy, sync); 415 eglDestroySyncKHR(dpy, sync); 416 417 if (fence_fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { 418 EGL_ERROR("Failed to get FD from fence!"); 419 return XRT_ERROR_NATIVE_HANDLE_FENCE_ERROR; 420 } 421 422 *out_handle = fence_fd; 423 424#else 425 (void)cglc; 426#endif 427 428 return XRT_SUCCESS; 429} 430 431static xrt_result_t 432client_egl_context_begin(struct xrt_compositor *xc, enum client_gl_context_reason reason) 433{ 434 struct client_egl_compositor *eglc = client_egl_compositor(xc); 435 436 //! @todo Handle this better, don't just assume that the context is current. 437 if (reason == CLIENT_GL_CONTEXT_REASON_SYNCHRONIZE) { 438 return XRT_SUCCESS; 439 } 440 441 save_context(&eglc->previous); 442 struct client_egl_context *cur = &eglc->current; 443 444 if (!eglMakeCurrent(cur->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, cur->ctx)) { 445 return XRT_ERROR_OPENGL; 446 } 447 return XRT_SUCCESS; 448} 449 450static void 451client_egl_context_end(struct xrt_compositor *xc, enum client_gl_context_reason reason) 452{ 453 struct client_egl_compositor *eglc = client_egl_compositor(xc); 454 455 //! @todo Handle this better, don't just assume that the context is current. 456 if (reason == CLIENT_GL_CONTEXT_REASON_SYNCHRONIZE) { 457 return; 458 } 459 460 restore_context(&eglc->previous); 461} 462 463static void 464client_egl_compositor_destroy(struct xrt_compositor *xc) 465{ 466 struct client_egl_compositor *ceglc = client_egl_compositor(xc); 467 468 client_gl_compositor_fini(&ceglc->base); 469 470 DESTROY_CONTEXT(ceglc->current.dpy, ceglc->current.ctx); 471 ceglc->current.ctx = EGL_NO_CONTEXT; 472 ceglc->current.dpy = EGL_NO_DISPLAY; 473 474 free(ceglc); 475} 476 477 478/* 479 * 480 * 'Exported' functions. 481 * 482 */ 483 484xrt_result_t 485xrt_gfx_provider_create_gl_egl(struct xrt_compositor_native *xcn, 486 EGLDisplay display, 487 EGLConfig config, 488 EGLContext context, 489 PFNEGLGETPROCADDRESSPROC get_gl_procaddr, 490 bool renderdoc_enabled, 491 struct xrt_compositor_gl **out_xcgl) 492{ 493 log_level = debug_get_log_option_egl_log(); 494 xrt_result_t xret; 495 496 497 /* 498 * Init EGL functions 499 */ 500 501 gladLoadEGL(display, get_gl_procaddr); 502 503 if (config == EGL_NO_CONFIG_KHR && !EGL_KHR_no_config_context) { 504 EGL_ERROR("config == EGL_NO_CONFIG_KHR && !EGL_KHR_no_config_context"); 505 return XRT_ERROR_EGL_CONFIG_MISSING; 506 } 507 508 // On Android this extension is 'hidden'. 509 ensure_native_fence_is_loaded(display, get_gl_procaddr); 510 511 512 /* 513 * Get client type. 514 */ 515 516 EGLint egl_client_type; 517 if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_TYPE, &egl_client_type)) { 518 EGL_ERROR("Could not query EGL client API type from context: %p", (void *)context); 519 return XRT_ERROR_OPENGL; 520 } 521 522 523 /* 524 * Create context. 525 */ 526 527 xret = create_context(display, config, context, egl_client_type, &context); 528 if (xret != XRT_SUCCESS) { 529 return xret; 530 } 531 532 533 /* 534 * Make current. 535 */ 536 537 // Save old EGL display, context and drawables. 538 struct client_egl_context old = {0}; 539 save_context(&old); 540 541 if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context)) { 542 EGL_ERROR( 543 "eglMakeCurrent: %s" 544 "\n\tFailed to make EGL context current" 545 "\n\told - dpy: %p, ctx: %p, read: %p, draw: %p" 546 "\n\tnew - dpy: %p, ctx: %p, read: %p, draw: %p", 547 egl_error_str(eglGetError()), // 548 (void *)old.dpy, (void *)old.ctx, (void *)old.read, (void *)old.draw, // 549 (void *)display, (void *)context, NULL, NULL); // 550 551 DESTROY_CONTEXT(display, context); 552 553 // No need to restore on failure. 554 return XRT_ERROR_OPENGL; 555 } 556 557 558 /* 559 * Use helpers to do all setup. 560 */ 561 562 // Load GL functions, only EGL functions where loaded above. 563 xret = load_gl_functions(egl_client_type, get_gl_procaddr); 564 if (xret != XRT_SUCCESS) { 565 restore_context(&old); 566 DESTROY_CONTEXT(display, context); 567 return xret; 568 } 569 570 // Some consistency/extension availability checking. 571 xret = check_context_and_debug_print(egl_client_type); 572 if (xret != XRT_SUCCESS) { 573 restore_context(&old); 574 DESTROY_CONTEXT(display, context); 575 return xret; 576 } 577 578 // Get functions. 579 client_gl_swapchain_create_func_t sc_create_func = NULL; 580 client_gl_insert_fence_func_t insert_fence_func = NULL; 581 582 xret = get_client_gl_functions(&sc_create_func, &insert_fence_func); 583 if (xret != XRT_SUCCESS) { 584 restore_context(&old); 585 DESTROY_CONTEXT(display, context); 586 return xret; 587 } 588 589 590 /* 591 * Now do the allocation and init. 592 */ 593 594 struct client_egl_compositor *ceglc = U_TYPED_CALLOC(struct client_egl_compositor); 595 ceglc->current.dpy = display; 596 ceglc->current.ctx = context; 597 ceglc->base.renderdoc_enabled = renderdoc_enabled; 598 599 bool bret = client_gl_compositor_init( // 600 &ceglc->base, // c 601 xcn, // xcn 602 client_egl_context_begin, // context_begin 603 client_egl_context_end, // context_end 604 sc_create_func, // create_swapchain 605 insert_fence_func); // insert_fence 606 if (!bret) { 607 free(ceglc); 608 EGL_ERROR("Failed to initialize compositor"); 609 restore_context(&old); 610 DESTROY_CONTEXT(display, context); 611 return XRT_ERROR_OPENGL; 612 } 613 614 ceglc->base.base.base.destroy = client_egl_compositor_destroy; 615 restore_context(&old); 616 *out_xcgl = &ceglc->base.base; 617 618 return XRT_SUCCESS; 619}