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