1 /* 2 * Copyright (C) 2015-2016 Gerd Hoffmann <kraxel@redhat.com> 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 16 */ 17 #include "qemu/osdep.h" 18 #include "qemu/drm.h" 19 #include "qemu/error-report.h" 20 #include "ui/console.h" 21 #include "ui/egl-helpers.h" 22 23 EGLDisplay *qemu_egl_display; 24 EGLConfig qemu_egl_config; 25 DisplayGLMode qemu_egl_mode; 26 27 /* ------------------------------------------------------------------ */ 28 29 static void egl_fb_delete_texture(egl_fb *fb) 30 { 31 if (!fb->delete_texture) { 32 return; 33 } 34 35 glDeleteTextures(1, &fb->texture); 36 fb->delete_texture = false; 37 } 38 39 void egl_fb_destroy(egl_fb *fb) 40 { 41 if (!fb->framebuffer) { 42 return; 43 } 44 45 egl_fb_delete_texture(fb); 46 glDeleteFramebuffers(1, &fb->framebuffer); 47 48 fb->width = 0; 49 fb->height = 0; 50 fb->texture = 0; 51 fb->framebuffer = 0; 52 } 53 54 void egl_fb_setup_default(egl_fb *fb, int width, int height) 55 { 56 fb->width = width; 57 fb->height = height; 58 fb->framebuffer = 0; /* default framebuffer */ 59 } 60 61 void egl_fb_setup_for_tex(egl_fb *fb, int width, int height, 62 GLuint texture, bool delete) 63 { 64 egl_fb_delete_texture(fb); 65 66 fb->width = width; 67 fb->height = height; 68 fb->texture = texture; 69 fb->delete_texture = delete; 70 if (!fb->framebuffer) { 71 glGenFramebuffers(1, &fb->framebuffer); 72 } 73 74 glBindFramebuffer(GL_FRAMEBUFFER_EXT, fb->framebuffer); 75 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 76 GL_TEXTURE_2D, fb->texture, 0); 77 } 78 79 void egl_fb_setup_new_tex(egl_fb *fb, int width, int height) 80 { 81 GLuint texture; 82 83 glGenTextures(1, &texture); 84 glBindTexture(GL_TEXTURE_2D, texture); 85 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 86 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); 87 88 egl_fb_setup_for_tex(fb, width, height, texture, true); 89 } 90 91 void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) 92 { 93 GLuint x1 = 0; 94 GLuint y1 = 0; 95 GLuint x2, y2; 96 GLuint w = src->width; 97 GLuint h = src->height; 98 99 glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer); 100 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->framebuffer); 101 glViewport(0, 0, dst->width, dst->height); 102 103 if (src->dmabuf) { 104 x1 = src->dmabuf->x; 105 y1 = src->dmabuf->y; 106 w = src->dmabuf->scanout_width; 107 h = src->dmabuf->scanout_height; 108 } 109 110 w = (x1 + w) > src->width ? src->width - x1 : w; 111 h = (y1 + h) > src->height ? src->height - y1 : h; 112 113 y2 = flip ? y1 : h + y1; 114 y1 = flip ? h + y1 : y1; 115 x2 = x1 + w; 116 117 glBlitFramebuffer(x1, y1, x2, y2, 118 0, 0, dst->width, dst->height, 119 GL_COLOR_BUFFER_BIT, GL_LINEAR); 120 } 121 122 void egl_fb_read(DisplaySurface *dst, egl_fb *src) 123 { 124 glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer); 125 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); 126 glReadPixels(0, 0, surface_width(dst), surface_height(dst), 127 GL_BGRA, GL_UNSIGNED_BYTE, surface_data(dst)); 128 } 129 130 void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip) 131 { 132 glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer); 133 glViewport(0, 0, dst->width, dst->height); 134 glEnable(GL_TEXTURE_2D); 135 glBindTexture(GL_TEXTURE_2D, src->texture); 136 qemu_gl_run_texture_blit(gls, flip); 137 } 138 139 void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, 140 int x, int y, double scale_x, double scale_y) 141 { 142 glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer); 143 int w = scale_x * src->width; 144 int h = scale_y * src->height; 145 if (flip) { 146 glViewport(x, y, w, h); 147 } else { 148 glViewport(x, dst->height - h - y, w, h); 149 } 150 glEnable(GL_TEXTURE_2D); 151 glBindTexture(GL_TEXTURE_2D, src->texture); 152 glEnable(GL_BLEND); 153 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 154 qemu_gl_run_texture_blit(gls, flip); 155 glDisable(GL_BLEND); 156 } 157 158 /* ---------------------------------------------------------------------- */ 159 160 #ifdef CONFIG_GBM 161 162 int qemu_egl_rn_fd; 163 struct gbm_device *qemu_egl_rn_gbm_dev; 164 EGLContext qemu_egl_rn_ctx; 165 166 int egl_rendernode_init(const char *rendernode, DisplayGLMode mode) 167 { 168 qemu_egl_rn_fd = -1; 169 int rc; 170 171 qemu_egl_rn_fd = qemu_drm_rendernode_open(rendernode); 172 if (qemu_egl_rn_fd == -1) { 173 error_report("egl: no drm render node available"); 174 goto err; 175 } 176 177 qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd); 178 if (!qemu_egl_rn_gbm_dev) { 179 error_report("egl: gbm_create_device failed"); 180 goto err; 181 } 182 183 rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, 184 mode); 185 if (rc != 0) { 186 /* qemu_egl_init_dpy_mesa reports error */ 187 goto err; 188 } 189 190 if (!epoxy_has_egl_extension(qemu_egl_display, 191 "EGL_KHR_surfaceless_context")) { 192 error_report("egl: EGL_KHR_surfaceless_context not supported"); 193 goto err; 194 } 195 if (!epoxy_has_egl_extension(qemu_egl_display, 196 "EGL_MESA_image_dma_buf_export")) { 197 error_report("egl: EGL_MESA_image_dma_buf_export not supported"); 198 goto err; 199 } 200 201 qemu_egl_rn_ctx = qemu_egl_init_ctx(); 202 if (!qemu_egl_rn_ctx) { 203 error_report("egl: egl_init_ctx failed"); 204 goto err; 205 } 206 207 return 0; 208 209 err: 210 if (qemu_egl_rn_gbm_dev) { 211 gbm_device_destroy(qemu_egl_rn_gbm_dev); 212 } 213 if (qemu_egl_rn_fd != -1) { 214 close(qemu_egl_rn_fd); 215 } 216 217 return -1; 218 } 219 220 int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc, 221 EGLuint64KHR *modifier) 222 { 223 EGLImageKHR image; 224 EGLint num_planes, fd; 225 226 image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(), 227 EGL_GL_TEXTURE_2D_KHR, 228 (EGLClientBuffer)(unsigned long)tex_id, 229 NULL); 230 if (!image) { 231 return -1; 232 } 233 234 eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc, 235 &num_planes, modifier); 236 if (num_planes != 1) { 237 eglDestroyImageKHR(qemu_egl_display, image); 238 return -1; 239 } 240 eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL); 241 eglDestroyImageKHR(qemu_egl_display, image); 242 243 return fd; 244 } 245 246 void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) 247 { 248 EGLImageKHR image = EGL_NO_IMAGE_KHR; 249 EGLint attrs[64]; 250 int i = 0; 251 252 if (dmabuf->texture != 0) { 253 return; 254 } 255 256 attrs[i++] = EGL_WIDTH; 257 attrs[i++] = dmabuf->width; 258 attrs[i++] = EGL_HEIGHT; 259 attrs[i++] = dmabuf->height; 260 attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT; 261 attrs[i++] = dmabuf->fourcc; 262 263 attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT; 264 attrs[i++] = dmabuf->fd; 265 attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; 266 attrs[i++] = dmabuf->stride; 267 attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; 268 attrs[i++] = 0; 269 #ifdef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 270 if (dmabuf->modifier) { 271 attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; 272 attrs[i++] = (dmabuf->modifier >> 0) & 0xffffffff; 273 attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; 274 attrs[i++] = (dmabuf->modifier >> 32) & 0xffffffff; 275 } 276 #endif 277 attrs[i++] = EGL_NONE; 278 279 image = eglCreateImageKHR(qemu_egl_display, 280 EGL_NO_CONTEXT, 281 EGL_LINUX_DMA_BUF_EXT, 282 NULL, attrs); 283 if (image == EGL_NO_IMAGE_KHR) { 284 error_report("eglCreateImageKHR failed"); 285 return; 286 } 287 288 glGenTextures(1, &dmabuf->texture); 289 glBindTexture(GL_TEXTURE_2D, dmabuf->texture); 290 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 291 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 292 293 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); 294 eglDestroyImageKHR(qemu_egl_display, image); 295 } 296 297 void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf) 298 { 299 if (dmabuf->texture == 0) { 300 return; 301 } 302 303 glDeleteTextures(1, &dmabuf->texture); 304 dmabuf->texture = 0; 305 } 306 307 void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf) 308 { 309 EGLSyncKHR sync; 310 311 if (epoxy_has_egl_extension(qemu_egl_display, 312 "EGL_KHR_fence_sync") && 313 epoxy_has_egl_extension(qemu_egl_display, 314 "EGL_ANDROID_native_fence_sync")) { 315 sync = eglCreateSyncKHR(qemu_egl_display, 316 EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); 317 if (sync != EGL_NO_SYNC_KHR) { 318 dmabuf->sync = sync; 319 } 320 } 321 } 322 323 void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf) 324 { 325 if (dmabuf->sync) { 326 dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display, 327 dmabuf->sync); 328 eglDestroySyncKHR(qemu_egl_display, dmabuf->sync); 329 dmabuf->sync = NULL; 330 } 331 } 332 333 #endif /* CONFIG_GBM */ 334 335 /* ---------------------------------------------------------------------- */ 336 337 EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win) 338 { 339 EGLSurface esurface; 340 EGLBoolean b; 341 342 esurface = eglCreateWindowSurface(qemu_egl_display, 343 qemu_egl_config, 344 win, NULL); 345 if (esurface == EGL_NO_SURFACE) { 346 error_report("egl: eglCreateWindowSurface failed"); 347 return NULL; 348 } 349 350 b = eglMakeCurrent(qemu_egl_display, esurface, esurface, ectx); 351 if (b == EGL_FALSE) { 352 error_report("egl: eglMakeCurrent failed"); 353 return NULL; 354 } 355 356 return esurface; 357 } 358 359 /* ---------------------------------------------------------------------- */ 360 361 #if defined(CONFIG_X11) || defined(CONFIG_GBM) 362 363 /* 364 * Taken from glamor_egl.h from the Xorg xserver, which is MIT licensed 365 * 366 * Create an EGLDisplay from a native display type. This is a little quirky 367 * for a few reasons. 368 * 369 * 1: GetPlatformDisplayEXT and GetPlatformDisplay are the API you want to 370 * use, but have different function signatures in the third argument; this 371 * happens not to matter for us, at the moment, but it means epoxy won't alias 372 * them together. 373 * 374 * 2: epoxy 1.3 and earlier don't understand EGL client extensions, which 375 * means you can't call "eglGetPlatformDisplayEXT" directly, as the resolver 376 * will crash. 377 * 378 * 3: You can't tell whether you have EGL 1.5 at this point, because 379 * eglQueryString(EGL_VERSION) is a property of the display, which we don't 380 * have yet. So you have to query for extensions no matter what. Fortunately 381 * epoxy_has_egl_extension _does_ let you query for client extensions, so 382 * we don't have to write our own extension string parsing. 383 * 384 * 4. There is no EGL_KHR_platform_base to complement the EXT one, thus one 385 * needs to know EGL 1.5 is supported in order to use the eglGetPlatformDisplay 386 * function pointer. 387 * We can workaround this (circular dependency) by probing for the EGL 1.5 388 * platform extensions (EGL_KHR_platform_gbm and friends) yet it doesn't seem 389 * like mesa will be able to advertise these (even though it can do EGL 1.5). 390 */ 391 static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native, 392 EGLenum platform) 393 { 394 EGLDisplay dpy = EGL_NO_DISPLAY; 395 396 /* In practise any EGL 1.5 implementation would support the EXT extension */ 397 if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) { 398 PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT = 399 (void *) eglGetProcAddress("eglGetPlatformDisplayEXT"); 400 if (getPlatformDisplayEXT && platform != 0) { 401 dpy = getPlatformDisplayEXT(platform, native, NULL); 402 } 403 } 404 405 if (dpy == EGL_NO_DISPLAY) { 406 /* fallback */ 407 dpy = eglGetDisplay(native); 408 } 409 return dpy; 410 } 411 412 static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, 413 EGLenum platform, 414 DisplayGLMode mode) 415 { 416 static const EGLint conf_att_core[] = { 417 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 418 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, 419 EGL_RED_SIZE, 5, 420 EGL_GREEN_SIZE, 5, 421 EGL_BLUE_SIZE, 5, 422 EGL_ALPHA_SIZE, 0, 423 EGL_NONE, 424 }; 425 static const EGLint conf_att_gles[] = { 426 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 427 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 428 EGL_RED_SIZE, 5, 429 EGL_GREEN_SIZE, 5, 430 EGL_BLUE_SIZE, 5, 431 EGL_ALPHA_SIZE, 0, 432 EGL_NONE, 433 }; 434 EGLint major, minor; 435 EGLBoolean b; 436 EGLint n; 437 bool gles = (mode == DISPLAYGL_MODE_ES); 438 439 qemu_egl_display = qemu_egl_get_display(dpy, platform); 440 if (qemu_egl_display == EGL_NO_DISPLAY) { 441 error_report("egl: eglGetDisplay failed"); 442 return -1; 443 } 444 445 b = eglInitialize(qemu_egl_display, &major, &minor); 446 if (b == EGL_FALSE) { 447 error_report("egl: eglInitialize failed"); 448 return -1; 449 } 450 451 b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API); 452 if (b == EGL_FALSE) { 453 error_report("egl: eglBindAPI failed (%s mode)", 454 gles ? "gles" : "core"); 455 return -1; 456 } 457 458 b = eglChooseConfig(qemu_egl_display, 459 gles ? conf_att_gles : conf_att_core, 460 &qemu_egl_config, 1, &n); 461 if (b == EGL_FALSE || n != 1) { 462 error_report("egl: eglChooseConfig failed (%s mode)", 463 gles ? "gles" : "core"); 464 return -1; 465 } 466 467 qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE; 468 return 0; 469 } 470 471 int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode) 472 { 473 #ifdef EGL_KHR_platform_x11 474 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode); 475 #else 476 return qemu_egl_init_dpy(dpy, 0, mode); 477 #endif 478 } 479 480 int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode) 481 { 482 #ifdef EGL_MESA_platform_gbm 483 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode); 484 #else 485 return qemu_egl_init_dpy(dpy, 0, mode); 486 #endif 487 } 488 489 #endif 490 491 bool qemu_egl_has_dmabuf(void) 492 { 493 if (qemu_egl_display == EGL_NO_DISPLAY) { 494 return false; 495 } 496 497 return epoxy_has_egl_extension(qemu_egl_display, 498 "EGL_EXT_image_dma_buf_import"); 499 } 500 501 EGLContext qemu_egl_init_ctx(void) 502 { 503 static const EGLint ctx_att_core[] = { 504 EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, 505 EGL_NONE 506 }; 507 static const EGLint ctx_att_gles[] = { 508 EGL_CONTEXT_CLIENT_VERSION, 2, 509 EGL_NONE 510 }; 511 bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES); 512 EGLContext ectx; 513 EGLBoolean b; 514 515 ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT, 516 gles ? ctx_att_gles : ctx_att_core); 517 if (ectx == EGL_NO_CONTEXT) { 518 error_report("egl: eglCreateContext failed"); 519 return NULL; 520 } 521 522 b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx); 523 if (b == EGL_FALSE) { 524 error_report("egl: eglMakeCurrent failed"); 525 return NULL; 526 } 527 528 return ectx; 529 } 530