Home | History | Annotate | Download | only in src
      1 /**************************************************************************
      2  *
      3  * Copyright (C) 2014 Red Hat Inc.
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining a
      6  * copy of this software and associated documentation files (the "Software"),
      7  * to deal in the Software without restriction, including without limitation
      8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      9  * and/or sell copies of the Software, and to permit persons to whom the
     10  * Software is furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice shall be included
     13  * in all copies or substantial portions of the Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     21  * OTHER DEALINGS IN THE SOFTWARE.
     22  *
     23  **************************************************************************/
     24 /* create our own EGL offscreen rendering context via gbm and rendernodes */
     25 
     26 
     27 /* if we are using EGL and rendernodes then we talk via file descriptors to the remote
     28    node */
     29 #ifdef HAVE_CONFIG_H
     30 #include "config.h"
     31 #endif
     32 
     33 #define EGL_EGLEXT_PROTOTYPES
     34 #include <dirent.h>
     35 #include <fcntl.h>
     36 #include <unistd.h>
     37 #include <stdio.h>
     38 #include <string.h>
     39 #include <stdlib.h>
     40 #include <errno.h>
     41 #include <stdbool.h>
     42 #include <epoxy/egl.h>
     43 #include <gbm.h>
     44 #include <xf86drm.h>
     45 #include "virglrenderer.h"
     46 #include "virgl_egl.h"
     47 
     48 #include "virgl_hw.h"
     49 struct virgl_egl {
     50    int fd;
     51    struct gbm_device *gbm_dev;
     52    EGLDisplay egl_display;
     53    EGLConfig egl_conf;
     54    EGLContext egl_ctx;
     55    bool have_mesa_drm_image;
     56    bool have_mesa_dma_buf_img_export;
     57 };
     58 
     59 static int egl_rendernode_open(void)
     60 {
     61    DIR *dir;
     62    struct dirent *e;
     63    int r, fd;
     64    char *p;
     65    dir = opendir("/dev/dri");
     66    if (!dir)
     67       return -1;
     68 
     69    fd = -1;
     70    while ((e = readdir(dir))) {
     71       if (e->d_type != DT_CHR)
     72          continue;
     73 
     74       if (strncmp(e->d_name, "renderD", 7))
     75          continue;
     76 
     77       r = asprintf(&p, "/dev/dri/%s", e->d_name);
     78       if (r < 0)
     79          return -1;
     80 
     81       r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
     82       if (r < 0){
     83          free(p);
     84          continue;
     85       }
     86       fd = r;
     87       free(p);
     88       break;
     89    }
     90 
     91    closedir(dir);
     92    if (fd < 0)
     93       return -1;
     94    return fd;
     95 }
     96 
     97 static bool virgl_egl_has_extension_in_string(const char *haystack, const char *needle)
     98 {
     99    const unsigned needle_len = strlen(needle);
    100 
    101    if (needle_len == 0)
    102       return false;
    103 
    104    while (true) {
    105       const char *const s = strstr(haystack, needle);
    106 
    107       if (s == NULL)
    108          return false;
    109 
    110       if (s[needle_len] == ' ' || s[needle_len] == '\0') {
    111          return true;
    112       }
    113 
    114       /* strstr found an extension whose name begins with
    115        * needle, but whose name is not equal to needle.
    116        * Restart the search at s + needle_len so that we
    117        * don't just find the same extension again and go
    118        * into an infinite loop.
    119        */
    120       haystack = s + needle_len;
    121    }
    122 
    123    return false;
    124 }
    125 
    126 struct virgl_egl *virgl_egl_init(int fd, bool surfaceless, bool gles)
    127 {
    128    static EGLint conf_att[] = {
    129       EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
    130       EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
    131       EGL_RED_SIZE, 1,
    132       EGL_GREEN_SIZE, 1,
    133       EGL_BLUE_SIZE, 1,
    134       EGL_ALPHA_SIZE, 0,
    135       EGL_NONE,
    136    };
    137    static const EGLint ctx_att[] = {
    138       EGL_CONTEXT_CLIENT_VERSION, 2,
    139       EGL_NONE
    140    };
    141    EGLBoolean b;
    142    EGLenum api;
    143    EGLint major, minor, n;
    144    const char *extension_list;
    145    struct virgl_egl *d;
    146 
    147    d = malloc(sizeof(struct virgl_egl));
    148    if (!d)
    149       return NULL;
    150 
    151    if (gles)
    152       conf_att[3] = EGL_OPENGL_ES_BIT;
    153 
    154    if (surfaceless) {
    155       conf_att[1] = EGL_PBUFFER_BIT;
    156       d->fd = -1;
    157       d->gbm_dev = NULL;
    158    } else {
    159       if (fd >= 0) {
    160          d->fd = fd;
    161       } else {
    162          d->fd = egl_rendernode_open();
    163       }
    164       if (d->fd == -1)
    165          goto fail;
    166       d->gbm_dev = gbm_create_device(d->fd);
    167       if (!d->gbm_dev)
    168          goto fail;
    169    }
    170 
    171    const char *client_extensions = eglQueryString (NULL, EGL_EXTENSIONS);
    172 
    173    if (strstr (client_extensions, "EGL_KHR_platform_base")) {
    174       PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display =
    175          (PFNEGLGETPLATFORMDISPLAYEXTPROC) eglGetProcAddress ("eglGetPlatformDisplay");
    176 
    177       if (!get_platform_display)
    178         goto fail;
    179 
    180       if (surfaceless) {
    181          d->egl_display = get_platform_display (EGL_PLATFORM_SURFACELESS_MESA,
    182                                                 EGL_DEFAULT_DISPLAY, NULL);
    183       } else
    184          d->egl_display = get_platform_display (EGL_PLATFORM_GBM_KHR,
    185                                                 (EGLNativeDisplayType)d->gbm_dev, NULL);
    186    } else if (strstr (client_extensions, "EGL_EXT_platform_base")) {
    187       PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display =
    188          (PFNEGLGETPLATFORMDISPLAYEXTPROC) eglGetProcAddress ("eglGetPlatformDisplayEXT");
    189 
    190       if (!get_platform_display)
    191         goto fail;
    192 
    193       if (surfaceless) {
    194          d->egl_display = get_platform_display (EGL_PLATFORM_SURFACELESS_MESA,
    195                                                 EGL_DEFAULT_DISPLAY, NULL);
    196       } else
    197          d->egl_display = get_platform_display (EGL_PLATFORM_GBM_KHR,
    198                                                 (EGLNativeDisplayType)d->gbm_dev, NULL);
    199    } else {
    200       d->egl_display = eglGetDisplay((EGLNativeDisplayType)d->gbm_dev);
    201    }
    202 
    203    if (!d->egl_display)
    204       goto fail;
    205 
    206    b = eglInitialize(d->egl_display, &major, &minor);
    207    if (!b)
    208       goto fail;
    209 
    210    extension_list = eglQueryString(d->egl_display, EGL_EXTENSIONS);
    211 #ifdef VIRGL_EGL_DEBUG
    212    fprintf(stderr, "EGL major/minor: %d.%d\n", major, minor);
    213    fprintf(stderr, "EGL version: %s\n",
    214            eglQueryString(d->egl_display, EGL_VERSION));
    215    fprintf(stderr, "EGL vendor: %s\n",
    216            eglQueryString(d->egl_display, EGL_VENDOR));
    217    fprintf(stderr, "EGL extensions: %s\n", extension_list);
    218 #endif
    219    /* require surfaceless context */
    220    if (!virgl_egl_has_extension_in_string(extension_list, "EGL_KHR_surfaceless_context"))
    221       goto fail;
    222 
    223    d->have_mesa_drm_image = false;
    224    d->have_mesa_dma_buf_img_export = false;
    225    if (virgl_egl_has_extension_in_string(extension_list, "EGL_MESA_drm_image"))
    226       d->have_mesa_drm_image = true;
    227 
    228    if (virgl_egl_has_extension_in_string(extension_list, "EGL_MESA_image_dma_buf_export"))
    229       d->have_mesa_dma_buf_img_export = true;
    230 
    231    if (d->have_mesa_drm_image == false && d->have_mesa_dma_buf_img_export == false) {
    232       fprintf(stderr, "failed to find drm image extensions\n");
    233       goto fail;
    234    }
    235 
    236    if (gles)
    237       api = EGL_OPENGL_ES_API;
    238    else
    239       api = EGL_OPENGL_API;
    240    b = eglBindAPI(api);
    241    if (!b)
    242       goto fail;
    243 
    244    b = eglChooseConfig(d->egl_display, conf_att, &d->egl_conf,
    245                        1, &n);
    246 
    247    if (!b || n != 1)
    248       goto fail;
    249 
    250    d->egl_ctx = eglCreateContext(d->egl_display,
    251                                  d->egl_conf,
    252                                  EGL_NO_CONTEXT,
    253                                  ctx_att);
    254    if (!d->egl_ctx)
    255       goto fail;
    256 
    257 
    258    eglMakeCurrent(d->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
    259                   d->egl_ctx);
    260    return d;
    261  fail:
    262    free(d);
    263    return NULL;
    264 }
    265 
    266 void virgl_egl_destroy(struct virgl_egl *d)
    267 {
    268    eglMakeCurrent(d->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
    269                   EGL_NO_CONTEXT);
    270    eglDestroyContext(d->egl_display, d->egl_ctx);
    271    eglTerminate(d->egl_display);
    272    if (d->gbm_dev)
    273       gbm_device_destroy(d->gbm_dev);
    274    if (d->fd >= 0)
    275       close(d->fd);
    276    free(d);
    277 }
    278 
    279 virgl_renderer_gl_context virgl_egl_create_context(struct virgl_egl *ve, struct virgl_gl_ctx_param *vparams)
    280 {
    281    EGLContext eglctx;
    282    EGLint ctx_att[] = {
    283       EGL_CONTEXT_CLIENT_VERSION, vparams->major_ver,
    284       EGL_CONTEXT_MINOR_VERSION_KHR, vparams->minor_ver,
    285       EGL_NONE
    286    };
    287    eglctx = eglCreateContext(ve->egl_display,
    288                              ve->egl_conf,
    289                              vparams->shared ? eglGetCurrentContext() : EGL_NO_CONTEXT,
    290                              ctx_att);
    291    return (virgl_renderer_gl_context)eglctx;
    292 }
    293 
    294 void virgl_egl_destroy_context(struct virgl_egl *ve, virgl_renderer_gl_context virglctx)
    295 {
    296    EGLContext eglctx = (EGLContext)virglctx;
    297    eglDestroyContext(ve->egl_display, eglctx);
    298 }
    299 
    300 int virgl_egl_make_context_current(struct virgl_egl *ve, virgl_renderer_gl_context virglctx)
    301 {
    302    EGLContext eglctx = (EGLContext)virglctx;
    303 
    304    return eglMakeCurrent(ve->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
    305                          eglctx);
    306 }
    307 
    308 virgl_renderer_gl_context virgl_egl_get_current_context(UNUSED struct virgl_egl *ve)
    309 {
    310    EGLContext eglctx = eglGetCurrentContext();
    311    return (virgl_renderer_gl_context)eglctx;
    312 }
    313 
    314 int virgl_egl_get_fourcc_for_texture(struct virgl_egl *ve, uint32_t tex_id, uint32_t format, int *fourcc)
    315 {
    316    int ret = EINVAL;
    317 
    318 #ifndef EGL_MESA_image_dma_buf_export
    319    ret = 0;
    320    goto fallback;
    321 #else
    322    EGLImageKHR image;
    323    EGLBoolean b;
    324 
    325    if (!ve->have_mesa_dma_buf_img_export)
    326       goto fallback;
    327 
    328    image = eglCreateImageKHR(ve->egl_display, eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(unsigned long)tex_id, NULL);
    329 
    330    if (!image)
    331       return EINVAL;
    332 
    333    b = eglExportDMABUFImageQueryMESA(ve->egl_display, image, fourcc, NULL, NULL);
    334    if (!b)
    335       goto out_destroy;
    336    ret = 0;
    337  out_destroy:
    338    eglDestroyImageKHR(ve->egl_display, image);
    339    return ret;
    340 
    341 #endif
    342 
    343  fallback:
    344    *fourcc = virgl_egl_get_gbm_format(format);
    345    return ret;
    346 }
    347 
    348 int virgl_egl_get_fd_for_texture2(struct virgl_egl *ve, uint32_t tex_id, int *fd,
    349                                   int *stride, int *offset)
    350 {
    351    int ret = EINVAL;
    352    EGLImageKHR image = eglCreateImageKHR(ve->egl_display, eglGetCurrentContext(),
    353                                          EGL_GL_TEXTURE_2D_KHR,
    354                                          (EGLClientBuffer)(unsigned long)tex_id, NULL);
    355    if (!image)
    356       return EINVAL;
    357    if (!ve->have_mesa_dma_buf_img_export)
    358       goto out_destroy;
    359 
    360    if (!eglExportDMABUFImageMESA(ve->egl_display, image, fd,
    361                                  stride, offset))
    362       goto out_destroy;
    363 
    364    ret = 0;
    365 
    366 out_destroy:
    367    eglDestroyImageKHR(ve->egl_display, image);
    368    return ret;
    369 }
    370 
    371 int virgl_egl_get_fd_for_texture(struct virgl_egl *ve, uint32_t tex_id, int *fd)
    372 {
    373    EGLImageKHR image;
    374    EGLint stride;
    375 #ifdef EGL_MESA_image_dma_buf_export
    376    EGLint offset;
    377 #endif
    378    EGLBoolean b;
    379    int ret;
    380    image = eglCreateImageKHR(ve->egl_display, eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(unsigned long)tex_id, NULL);
    381 
    382    if (!image)
    383       return EINVAL;
    384 
    385    ret = EINVAL;
    386    if (ve->have_mesa_dma_buf_img_export) {
    387 #ifdef EGL_MESA_image_dma_buf_export
    388       b = eglExportDMABUFImageMESA(ve->egl_display,
    389                                    image,
    390                                    fd,
    391                                    &stride,
    392                                    &offset);
    393       if (!b)
    394          goto out_destroy;
    395 #else
    396       goto out_destroy;
    397 #endif
    398    } else {
    399 #ifdef EGL_MESA_drm_image
    400       EGLint handle;
    401       int r;
    402       b = eglExportDRMImageMESA(ve->egl_display,
    403                                 image,
    404                                 NULL, &handle,
    405                                 &stride);
    406 
    407       if (!b)
    408 	 goto out_destroy;
    409 
    410       fprintf(stderr,"image exported %d %d\n", handle, stride);
    411 
    412       r = drmPrimeHandleToFD(ve->fd, handle, DRM_CLOEXEC, fd);
    413       if (r < 0)
    414 	 goto out_destroy;
    415 #else
    416       goto out_destroy;
    417 #endif
    418    }
    419    ret = 0;
    420  out_destroy:
    421    eglDestroyImageKHR(ve->egl_display, image);
    422    return ret;
    423 }
    424 
    425 uint32_t virgl_egl_get_gbm_format(uint32_t format)
    426 {
    427    switch (format) {
    428    case VIRGL_FORMAT_B8G8R8X8_UNORM:
    429       return GBM_FORMAT_XRGB8888;
    430    case VIRGL_FORMAT_B8G8R8A8_UNORM:
    431       return GBM_FORMAT_ARGB8888;
    432    default:
    433       fprintf(stderr, "unknown format to convert to GBM %d\n", format);
    434       return 0;
    435    }
    436 }
    437