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