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