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 <glob.h> 19 #include <dirent.h> 20 21 #include "qemu/error-report.h" 22 #include "ui/console.h" 23 #include "ui/egl-helpers.h" 24 25 EGLDisplay *qemu_egl_display; 26 EGLConfig qemu_egl_config; 27 28 /* ------------------------------------------------------------------ */ 29 30 static void egl_fb_delete_texture(egl_fb *fb) 31 { 32 if (!fb->delete_texture) { 33 return; 34 } 35 36 glDeleteTextures(1, &fb->texture); 37 fb->delete_texture = false; 38 } 39 40 void egl_fb_destroy(egl_fb *fb) 41 { 42 if (!fb->framebuffer) { 43 return; 44 } 45 46 egl_fb_delete_texture(fb); 47 glDeleteFramebuffers(1, &fb->framebuffer); 48 49 fb->width = 0; 50 fb->height = 0; 51 fb->texture = 0; 52 fb->framebuffer = 0; 53 } 54 55 void egl_fb_setup_default(egl_fb *fb, int width, int height) 56 { 57 fb->width = width; 58 fb->height = height; 59 fb->framebuffer = 0; /* default framebuffer */ 60 } 61 62 void egl_fb_setup_for_tex(egl_fb *fb, int width, int height, 63 GLuint texture, bool delete) 64 { 65 egl_fb_delete_texture(fb); 66 67 fb->width = width; 68 fb->height = height; 69 fb->texture = texture; 70 fb->delete_texture = delete; 71 if (!fb->framebuffer) { 72 glGenFramebuffers(1, &fb->framebuffer); 73 } 74 75 glBindFramebuffer(GL_FRAMEBUFFER_EXT, fb->framebuffer); 76 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 77 GL_TEXTURE_2D, fb->texture, 0); 78 } 79 80 void egl_fb_setup_new_tex(egl_fb *fb, int width, int height) 81 { 82 GLuint texture; 83 84 glGenTextures(1, &texture); 85 glBindTexture(GL_TEXTURE_2D, texture); 86 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 87 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); 88 89 egl_fb_setup_for_tex(fb, width, height, texture, true); 90 } 91 92 void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) 93 { 94 GLuint y1, y2; 95 96 glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer); 97 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->framebuffer); 98 glViewport(0, 0, dst->width, dst->height); 99 y1 = flip ? src->height : 0; 100 y2 = flip ? 0 : src->height; 101 glBlitFramebuffer(0, y1, src->width, y2, 102 0, 0, dst->width, dst->height, 103 GL_COLOR_BUFFER_BIT, GL_LINEAR); 104 } 105 106 void egl_fb_read(void *dst, egl_fb *src) 107 { 108 glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer); 109 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); 110 glReadPixels(0, 0, src->width, src->height, 111 GL_BGRA, GL_UNSIGNED_BYTE, dst); 112 } 113 114 void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip) 115 { 116 glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer); 117 glViewport(0, 0, dst->width, dst->height); 118 glEnable(GL_TEXTURE_2D); 119 glBindTexture(GL_TEXTURE_2D, src->texture); 120 qemu_gl_run_texture_blit(gls, flip); 121 } 122 123 void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, 124 int x, int y) 125 { 126 glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer); 127 if (flip) { 128 glViewport(x, y, src->width, src->height); 129 } else { 130 glViewport(x, dst->height - src->height - y, 131 src->width, src->height); 132 } 133 glEnable(GL_TEXTURE_2D); 134 glBindTexture(GL_TEXTURE_2D, src->texture); 135 glEnable(GL_BLEND); 136 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 137 qemu_gl_run_texture_blit(gls, flip); 138 glDisable(GL_BLEND); 139 } 140 141 /* ---------------------------------------------------------------------- */ 142 143 #ifdef CONFIG_OPENGL_DMABUF 144 145 int qemu_egl_rn_fd; 146 struct gbm_device *qemu_egl_rn_gbm_dev; 147 EGLContext qemu_egl_rn_ctx; 148 149 static int qemu_egl_rendernode_open(const char *rendernode) 150 { 151 DIR *dir; 152 struct dirent *e; 153 int r, fd; 154 char *p; 155 156 if (rendernode) { 157 return open(rendernode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); 158 } 159 160 dir = opendir("/dev/dri"); 161 if (!dir) { 162 return -1; 163 } 164 165 fd = -1; 166 while ((e = readdir(dir))) { 167 if (e->d_type != DT_CHR) { 168 continue; 169 } 170 171 if (strncmp(e->d_name, "renderD", 7)) { 172 continue; 173 } 174 175 p = g_strdup_printf("/dev/dri/%s", e->d_name); 176 177 r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); 178 if (r < 0) { 179 g_free(p); 180 continue; 181 } 182 fd = r; 183 g_free(p); 184 break; 185 } 186 187 closedir(dir); 188 if (fd < 0) { 189 return -1; 190 } 191 return fd; 192 } 193 194 int egl_rendernode_init(const char *rendernode) 195 { 196 qemu_egl_rn_fd = -1; 197 int rc; 198 199 qemu_egl_rn_fd = qemu_egl_rendernode_open(rendernode); 200 if (qemu_egl_rn_fd == -1) { 201 error_report("egl: no drm render node available"); 202 goto err; 203 } 204 205 qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd); 206 if (!qemu_egl_rn_gbm_dev) { 207 error_report("egl: gbm_create_device failed"); 208 goto err; 209 } 210 211 rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev); 212 if (rc != 0) { 213 /* qemu_egl_init_dpy_mesa reports error */ 214 goto err; 215 } 216 217 if (!epoxy_has_egl_extension(qemu_egl_display, 218 "EGL_KHR_surfaceless_context")) { 219 error_report("egl: EGL_KHR_surfaceless_context not supported"); 220 goto err; 221 } 222 if (!epoxy_has_egl_extension(qemu_egl_display, 223 "EGL_MESA_image_dma_buf_export")) { 224 error_report("egl: EGL_MESA_image_dma_buf_export not supported"); 225 goto err; 226 } 227 228 qemu_egl_rn_ctx = qemu_egl_init_ctx(); 229 if (!qemu_egl_rn_ctx) { 230 error_report("egl: egl_init_ctx failed"); 231 goto err; 232 } 233 234 return 0; 235 236 err: 237 if (qemu_egl_rn_gbm_dev) { 238 gbm_device_destroy(qemu_egl_rn_gbm_dev); 239 } 240 if (qemu_egl_rn_fd != -1) { 241 close(qemu_egl_rn_fd); 242 } 243 244 return -1; 245 } 246 247 int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc) 248 { 249 EGLImageKHR image; 250 EGLint num_planes, fd; 251 252 image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(), 253 EGL_GL_TEXTURE_2D_KHR, 254 (EGLClientBuffer)(unsigned long)tex_id, 255 NULL); 256 if (!image) { 257 return -1; 258 } 259 260 eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc, 261 &num_planes, NULL); 262 if (num_planes != 1) { 263 eglDestroyImageKHR(qemu_egl_display, image); 264 return -1; 265 } 266 eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL); 267 eglDestroyImageKHR(qemu_egl_display, image); 268 269 return fd; 270 } 271 272 void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) 273 { 274 EGLImageKHR image = EGL_NO_IMAGE_KHR; 275 EGLint attrs[] = { 276 EGL_DMA_BUF_PLANE0_FD_EXT, dmabuf->fd, 277 EGL_DMA_BUF_PLANE0_PITCH_EXT, dmabuf->stride, 278 EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, 279 EGL_WIDTH, dmabuf->width, 280 EGL_HEIGHT, dmabuf->height, 281 EGL_LINUX_DRM_FOURCC_EXT, dmabuf->fourcc, 282 EGL_NONE, /* end of list */ 283 }; 284 285 if (dmabuf->texture != 0) { 286 return; 287 } 288 289 image = eglCreateImageKHR(qemu_egl_display, 290 EGL_NO_CONTEXT, 291 EGL_LINUX_DMA_BUF_EXT, 292 NULL, attrs); 293 if (image == EGL_NO_IMAGE_KHR) { 294 error_report("eglCreateImageKHR failed"); 295 return; 296 } 297 298 glGenTextures(1, &dmabuf->texture); 299 glBindTexture(GL_TEXTURE_2D, dmabuf->texture); 300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 302 303 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); 304 eglDestroyImageKHR(qemu_egl_display, image); 305 } 306 307 void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf) 308 { 309 if (dmabuf->texture == 0) { 310 return; 311 } 312 313 glDeleteTextures(1, &dmabuf->texture); 314 dmabuf->texture = 0; 315 } 316 317 #endif /* CONFIG_OPENGL_DMABUF */ 318 319 /* ---------------------------------------------------------------------- */ 320 321 EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win) 322 { 323 EGLSurface esurface; 324 EGLBoolean b; 325 326 esurface = eglCreateWindowSurface(qemu_egl_display, 327 qemu_egl_config, 328 (EGLNativeWindowType)win, NULL); 329 if (esurface == EGL_NO_SURFACE) { 330 error_report("egl: eglCreateWindowSurface failed"); 331 return NULL; 332 } 333 334 b = eglMakeCurrent(qemu_egl_display, esurface, esurface, ectx); 335 if (b == EGL_FALSE) { 336 error_report("egl: eglMakeCurrent failed"); 337 return NULL; 338 } 339 340 return esurface; 341 } 342 343 /* ---------------------------------------------------------------------- */ 344 345 /* 346 * Taken from glamor_egl.h from the Xorg xserver, which is MIT licensed 347 * 348 * Create an EGLDisplay from a native display type. This is a little quirky 349 * for a few reasons. 350 * 351 * 1: GetPlatformDisplayEXT and GetPlatformDisplay are the API you want to 352 * use, but have different function signatures in the third argument; this 353 * happens not to matter for us, at the moment, but it means epoxy won't alias 354 * them together. 355 * 356 * 2: epoxy 1.3 and earlier don't understand EGL client extensions, which 357 * means you can't call "eglGetPlatformDisplayEXT" directly, as the resolver 358 * will crash. 359 * 360 * 3: You can't tell whether you have EGL 1.5 at this point, because 361 * eglQueryString(EGL_VERSION) is a property of the display, which we don't 362 * have yet. So you have to query for extensions no matter what. Fortunately 363 * epoxy_has_egl_extension _does_ let you query for client extensions, so 364 * we don't have to write our own extension string parsing. 365 * 366 * 4. There is no EGL_KHR_platform_base to complement the EXT one, thus one 367 * needs to know EGL 1.5 is supported in order to use the eglGetPlatformDisplay 368 * function pointer. 369 * We can workaround this (circular dependency) by probing for the EGL 1.5 370 * platform extensions (EGL_KHR_platform_gbm and friends) yet it doesn't seem 371 * like mesa will be able to advertise these (even though it can do EGL 1.5). 372 */ 373 static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native, 374 EGLenum platform) 375 { 376 EGLDisplay dpy = EGL_NO_DISPLAY; 377 378 /* In practise any EGL 1.5 implementation would support the EXT extension */ 379 if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) { 380 PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT = 381 (void *) eglGetProcAddress("eglGetPlatformDisplayEXT"); 382 if (getPlatformDisplayEXT && platform != 0) { 383 dpy = getPlatformDisplayEXT(platform, native, NULL); 384 } 385 } 386 387 if (dpy == EGL_NO_DISPLAY) { 388 /* fallback */ 389 dpy = eglGetDisplay(native); 390 } 391 return dpy; 392 } 393 394 static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, 395 EGLenum platform) 396 { 397 static const EGLint conf_att_gl[] = { 398 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 399 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, 400 EGL_RED_SIZE, 5, 401 EGL_GREEN_SIZE, 5, 402 EGL_BLUE_SIZE, 5, 403 EGL_ALPHA_SIZE, 0, 404 EGL_NONE, 405 }; 406 EGLint major, minor; 407 EGLBoolean b; 408 EGLint n; 409 410 qemu_egl_display = qemu_egl_get_display(dpy, platform); 411 if (qemu_egl_display == EGL_NO_DISPLAY) { 412 error_report("egl: eglGetDisplay failed"); 413 return -1; 414 } 415 416 b = eglInitialize(qemu_egl_display, &major, &minor); 417 if (b == EGL_FALSE) { 418 error_report("egl: eglInitialize failed"); 419 return -1; 420 } 421 422 b = eglBindAPI(EGL_OPENGL_API); 423 if (b == EGL_FALSE) { 424 error_report("egl: eglBindAPI failed"); 425 return -1; 426 } 427 428 b = eglChooseConfig(qemu_egl_display, conf_att_gl, 429 &qemu_egl_config, 1, &n); 430 if (b == EGL_FALSE || n != 1) { 431 error_report("egl: eglChooseConfig failed"); 432 return -1; 433 } 434 return 0; 435 } 436 437 int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy) 438 { 439 #ifdef EGL_KHR_platform_x11 440 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR); 441 #else 442 return qemu_egl_init_dpy(dpy, 0); 443 #endif 444 } 445 446 int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy) 447 { 448 #ifdef EGL_MESA_platform_gbm 449 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA); 450 #else 451 return qemu_egl_init_dpy(dpy, 0); 452 #endif 453 } 454 455 EGLContext qemu_egl_init_ctx(void) 456 { 457 static const EGLint ctx_att_gl[] = { 458 EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, 459 EGL_NONE 460 }; 461 EGLContext ectx; 462 EGLBoolean b; 463 464 ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT, 465 ctx_att_gl); 466 if (ectx == EGL_NO_CONTEXT) { 467 error_report("egl: eglCreateContext failed"); 468 return NULL; 469 } 470 471 b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx); 472 if (b == EGL_FALSE) { 473 error_report("egl: eglMakeCurrent failed"); 474 return NULL; 475 } 476 477 return ectx; 478 } 479