qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

egl-helpers.c (15830B)


      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 }