Home | History | Annotate | Download | only in dri2
      1 /*
      2  * Copyright  2015 Boyan Ding
      3  *
      4  * Permission to use, copy, modify, distribute, and sell this software and its
      5  * documentation for any purpose is hereby granted without fee, provided that
      6  * the above copyright notice appear in all copies and that both that copyright
      7  * notice and this permission notice appear in supporting documentation, and
      8  * that the name of the copyright holders not be used in advertising or
      9  * publicity pertaining to distribution of the software without specific,
     10  * written prior permission.  The copyright holders make no representations
     11  * about the suitability of this software for any purpose.  It is provided "as
     12  * is" without express or implied warranty.
     13  *
     14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
     17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
     18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
     19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
     20  * OF THIS SOFTWARE.
     21  */
     22 
     23 #include <stdbool.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <unistd.h>
     27 
     28 #include <xcb/xcb.h>
     29 #include <xcb/dri3.h>
     30 #include <xcb/present.h>
     31 
     32 #include <xf86drm.h>
     33 
     34 #include "egl_dri2.h"
     35 #include "egl_dri2_fallbacks.h"
     36 #include "platform_x11_dri3.h"
     37 
     38 #include "loader.h"
     39 #include "loader_dri3_helper.h"
     40 
     41 static struct dri3_egl_surface *
     42 loader_drawable_to_egl_surface(struct loader_dri3_drawable *draw) {
     43    size_t offset = offsetof(struct dri3_egl_surface, loader_drawable);
     44    return (struct dri3_egl_surface *)(((void*) draw) - offset);
     45 }
     46 
     47 static int
     48 egl_dri3_get_swap_interval(struct loader_dri3_drawable *draw)
     49 {
     50    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
     51 
     52    return dri3_surf->base.SwapInterval;
     53 }
     54 
     55 static int
     56 egl_dri3_clamp_swap_interval(struct loader_dri3_drawable *draw, int interval)
     57 {
     58    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
     59 
     60    if (interval > dri3_surf->base.Config->MaxSwapInterval)
     61       interval = dri3_surf->base.Config->MaxSwapInterval;
     62    else if (interval < dri3_surf->base.Config->MinSwapInterval)
     63       interval = dri3_surf->base.Config->MinSwapInterval;
     64 
     65    return interval;
     66 }
     67 
     68 static void
     69 egl_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval)
     70 {
     71    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
     72 
     73    dri3_surf->base.SwapInterval = interval;
     74 }
     75 
     76 static void
     77 egl_dri3_set_drawable_size(struct loader_dri3_drawable *draw,
     78                            int width, int height)
     79 {
     80    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
     81 
     82    dri3_surf->base.Width = width;
     83    dri3_surf->base.Height = height;
     84 }
     85 
     86 static bool
     87 egl_dri3_in_current_context(struct loader_dri3_drawable *draw)
     88 {
     89    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
     90    _EGLContext *ctx = _eglGetCurrentContext();
     91 
     92    return ctx->Resource.Display == dri3_surf->base.Resource.Display;
     93 }
     94 
     95 static __DRIcontext *
     96 egl_dri3_get_dri_context(struct loader_dri3_drawable *draw)
     97 {
     98    _EGLContext *ctx = _eglGetCurrentContext();
     99    struct dri2_egl_context *dri2_ctx;
    100    if (!ctx)
    101       return NULL;
    102    dri2_ctx = dri2_egl_context(ctx);
    103    return dri2_ctx->dri_context;
    104 }
    105 
    106 static __DRIscreen *
    107 egl_dri3_get_dri_screen(struct loader_dri3_drawable *draw)
    108 {
    109    _EGLContext *ctx = _eglGetCurrentContext();
    110    struct dri2_egl_context *dri2_ctx;
    111    if (!ctx)
    112       return NULL;
    113    dri2_ctx = dri2_egl_context(ctx);
    114    return dri2_egl_display(dri2_ctx->base.Resource.Display)->dri_screen;
    115 }
    116 
    117 static void
    118 egl_dri3_flush_drawable(struct loader_dri3_drawable *draw, unsigned flags)
    119 {
    120    struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
    121    _EGLDisplay *disp = dri3_surf->base.Resource.Display;
    122 
    123    dri2_flush_drawable_for_swapbuffers(disp, &dri3_surf->base);
    124 }
    125 
    126 static const struct loader_dri3_vtable egl_dri3_vtable = {
    127    .get_swap_interval = egl_dri3_get_swap_interval,
    128    .clamp_swap_interval = egl_dri3_clamp_swap_interval,
    129    .set_swap_interval = egl_dri3_set_swap_interval,
    130    .set_drawable_size = egl_dri3_set_drawable_size,
    131    .in_current_context = egl_dri3_in_current_context,
    132    .get_dri_context = egl_dri3_get_dri_context,
    133    .get_dri_screen = egl_dri3_get_dri_screen,
    134    .flush_drawable = egl_dri3_flush_drawable,
    135    .show_fps = NULL,
    136 };
    137 
    138 static EGLBoolean
    139 dri3_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
    140 {
    141    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
    142 
    143    (void) drv;
    144 
    145    loader_dri3_drawable_fini(&dri3_surf->loader_drawable);
    146 
    147    free(surf);
    148 
    149    return EGL_TRUE;
    150 }
    151 
    152 static EGLBoolean
    153 dri3_set_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
    154                        EGLint interval)
    155 {
    156    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
    157 
    158    loader_dri3_set_swap_interval(&dri3_surf->loader_drawable, interval);
    159 
    160    return EGL_TRUE;
    161 }
    162 
    163 static _EGLSurface *
    164 dri3_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
    165                     _EGLConfig *conf, void *native_surface,
    166                     const EGLint *attrib_list)
    167 {
    168    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
    169    struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
    170    struct dri3_egl_surface *dri3_surf;
    171    const __DRIconfig *dri_config;
    172    xcb_drawable_t drawable;
    173 
    174    STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface));
    175    drawable = (uintptr_t) native_surface;
    176 
    177    (void) drv;
    178 
    179    dri3_surf = calloc(1, sizeof *dri3_surf);
    180    if (!dri3_surf) {
    181       _eglError(EGL_BAD_ALLOC, "dri3_create_surface");
    182       return NULL;
    183    }
    184 
    185    if (!_eglInitSurface(&dri3_surf->base, disp, type, conf, attrib_list))
    186       goto cleanup_surf;
    187 
    188    if (type == EGL_PBUFFER_BIT) {
    189       drawable = xcb_generate_id(dri2_dpy->conn);
    190       xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize,
    191                         drawable, dri2_dpy->screen->root,
    192                         dri3_surf->base.Width, dri3_surf->base.Height);
    193    }
    194 
    195    dri_config = dri2_get_dri_config(dri2_conf, type,
    196                                     dri3_surf->base.GLColorspace);
    197 
    198    if (loader_dri3_drawable_init(dri2_dpy->conn, drawable,
    199                                  dri2_dpy->dri_screen,
    200                                  dri2_dpy->is_different_gpu, dri_config,
    201                                  &dri2_dpy->loader_dri3_ext,
    202                                  &egl_dri3_vtable,
    203                                  &dri3_surf->loader_drawable)) {
    204       _eglError(EGL_BAD_ALLOC, "dri3_surface_create");
    205       goto cleanup_pixmap;
    206    }
    207 
    208    return &dri3_surf->base;
    209 
    210  cleanup_pixmap:
    211    if (type == EGL_PBUFFER_BIT)
    212       xcb_free_pixmap(dri2_dpy->conn, drawable);
    213  cleanup_surf:
    214    free(dri3_surf);
    215 
    216    return NULL;
    217 }
    218 
    219 static int
    220 dri3_authenticate(_EGLDisplay *disp, uint32_t id)
    221 {
    222 #ifdef HAVE_WAYLAND_PLATFORM
    223    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
    224 
    225    if (dri2_dpy->device_name) {
    226       _eglLog(_EGL_WARNING,
    227               "Wayland client render node authentication is unnecessary");
    228       return 0;
    229    }
    230 
    231    _eglLog(_EGL_WARNING,
    232            "Wayland client primary node authentication isn't supported");
    233 #endif
    234 
    235    return -1;
    236 }
    237 
    238 /**
    239  * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
    240  */
    241 static _EGLSurface *
    242 dri3_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
    243                            _EGLConfig *conf, void *native_window,
    244                            const EGLint *attrib_list)
    245 {
    246    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
    247    _EGLSurface *surf;
    248 
    249    surf = dri3_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
    250                               native_window, attrib_list);
    251    if (surf != NULL)
    252       dri3_set_swap_interval(drv, disp, surf, dri2_dpy->default_swap_interval);
    253 
    254    return surf;
    255 }
    256 
    257 static _EGLSurface *
    258 dri3_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp,
    259                            _EGLConfig *conf, void *native_pixmap,
    260                            const EGLint *attrib_list)
    261 {
    262    return dri3_create_surface(drv, disp, EGL_PIXMAP_BIT, conf,
    263                               native_pixmap, attrib_list);
    264 }
    265 
    266 static _EGLSurface *
    267 dri3_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp,
    268                                 _EGLConfig *conf, const EGLint *attrib_list)
    269 {
    270    return dri3_create_surface(drv, disp, EGL_PBUFFER_BIT, conf,
    271                               XCB_WINDOW_NONE, attrib_list);
    272 }
    273 
    274 static EGLBoolean
    275 dri3_get_sync_values(_EGLDisplay *display, _EGLSurface *surface,
    276                      EGLuint64KHR *ust, EGLuint64KHR *msc,
    277                      EGLuint64KHR *sbc)
    278 {
    279    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surface);
    280 
    281    return loader_dri3_wait_for_msc(&dri3_surf->loader_drawable, 0, 0, 0,
    282                                    (int64_t *) ust, (int64_t *) msc,
    283                                    (int64_t *) sbc) ? EGL_TRUE : EGL_FALSE;
    284 }
    285 
    286 static _EGLImage *
    287 dri3_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx,
    288                              EGLClientBuffer buffer, const EGLint *attr_list)
    289 {
    290    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
    291    struct dri2_egl_image *dri2_img;
    292    xcb_drawable_t drawable;
    293    xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
    294    xcb_dri3_buffer_from_pixmap_reply_t  *bp_reply;
    295    unsigned int format;
    296 
    297    drawable = (xcb_drawable_t) (uintptr_t) buffer;
    298    bp_cookie = xcb_dri3_buffer_from_pixmap(dri2_dpy->conn, drawable);
    299    bp_reply = xcb_dri3_buffer_from_pixmap_reply(dri2_dpy->conn,
    300                                                 bp_cookie, NULL);
    301    if (!bp_reply) {
    302       _eglError(EGL_BAD_ALLOC, "xcb_dri3_buffer_from_pixmap");
    303       return NULL;
    304    }
    305 
    306    switch (bp_reply->depth) {
    307    case 16:
    308       format = __DRI_IMAGE_FORMAT_RGB565;
    309       break;
    310    case 24:
    311       format = __DRI_IMAGE_FORMAT_XRGB8888;
    312       break;
    313    case 32:
    314       format = __DRI_IMAGE_FORMAT_ARGB8888;
    315       break;
    316    default:
    317       _eglError(EGL_BAD_PARAMETER,
    318                 "dri3_create_image_khr: unsupported pixmap depth");
    319       free(bp_reply);
    320       return EGL_NO_IMAGE_KHR;
    321    }
    322 
    323    dri2_img = malloc(sizeof *dri2_img);
    324    if (!dri2_img) {
    325       _eglError(EGL_BAD_ALLOC, "dri3_create_image_khr");
    326       return EGL_NO_IMAGE_KHR;
    327    }
    328 
    329    if (!_eglInitImage(&dri2_img->base, disp)) {
    330       free(dri2_img);
    331       return EGL_NO_IMAGE_KHR;
    332    }
    333 
    334    dri2_img->dri_image = loader_dri3_create_image(dri2_dpy->conn,
    335                                                   bp_reply,
    336                                                   format,
    337                                                   dri2_dpy->dri_screen,
    338                                                   dri2_dpy->image,
    339                                                   dri2_img);
    340 
    341    free(bp_reply);
    342 
    343    return &dri2_img->base;
    344 }
    345 
    346 static _EGLImage *
    347 dri3_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp,
    348                       _EGLContext *ctx, EGLenum target,
    349                       EGLClientBuffer buffer, const EGLint *attr_list)
    350 {
    351    (void) drv;
    352 
    353    switch (target) {
    354    case EGL_NATIVE_PIXMAP_KHR:
    355       return dri3_create_image_khr_pixmap(disp, ctx, buffer, attr_list);
    356    default:
    357       return dri2_create_image_khr(drv, disp, ctx, target, buffer, attr_list);
    358    }
    359 }
    360 
    361 /**
    362  * Called by the driver when it needs to update the real front buffer with the
    363  * contents of its fake front buffer.
    364  */
    365 static void
    366 dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate)
    367 {
    368    /* There does not seem to be any kind of consensus on whether we should
    369     * support front-buffer rendering or not:
    370     * http://lists.freedesktop.org/archives/mesa-dev/2013-June/040129.html
    371     */
    372    _eglLog(_EGL_WARNING, "FIXME: egl/x11 doesn't support front buffer rendering.");
    373    (void) driDrawable;
    374    (void) loaderPrivate;
    375 }
    376 
    377 const __DRIimageLoaderExtension dri3_image_loader_extension = {
    378    .base = { __DRI_IMAGE_LOADER, 1 },
    379 
    380    .getBuffers          = loader_dri3_get_buffers,
    381    .flushFrontBuffer    = dri3_flush_front_buffer,
    382 };
    383 
    384 static EGLBoolean
    385 dri3_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
    386 {
    387    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(draw);
    388 
    389    /* No-op for a pixmap or pbuffer surface */
    390    if (draw->Type == EGL_PIXMAP_BIT || draw->Type == EGL_PBUFFER_BIT)
    391       return EGL_FALSE;
    392 
    393    return loader_dri3_swap_buffers_msc(&dri3_surf->loader_drawable,
    394                                        0, 0, 0, 0,
    395                                        draw->SwapBehavior == EGL_BUFFER_PRESERVED) != -1;
    396 }
    397 
    398 static EGLBoolean
    399 dri3_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
    400                   void *native_pixmap_target)
    401 {
    402    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
    403    xcb_pixmap_t target;
    404 
    405    STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_pixmap_target));
    406    target = (uintptr_t) native_pixmap_target;
    407 
    408    loader_dri3_copy_drawable(&dri3_surf->loader_drawable, target,
    409                              dri3_surf->loader_drawable.drawable);
    410 
    411    return EGL_TRUE;
    412 }
    413 
    414 static int
    415 dri3_query_buffer_age(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf)
    416 {
    417    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
    418 
    419    return loader_dri3_query_buffer_age(&dri3_surf->loader_drawable);
    420 }
    421 
    422 static EGLBoolean
    423 dri3_query_surface(_EGLDriver *drv, _EGLDisplay *dpy,
    424                    _EGLSurface *surf, EGLint attribute,
    425                    EGLint *value)
    426 {
    427    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
    428 
    429    switch (attribute) {
    430    case EGL_WIDTH:
    431    case EGL_HEIGHT:
    432       loader_dri3_update_drawable_geometry(&dri3_surf->loader_drawable);
    433       break;
    434    default:
    435       break;
    436    }
    437 
    438    return _eglQuerySurface(drv, dpy, surf, attribute, value);
    439 }
    440 
    441 static __DRIdrawable *
    442 dri3_get_dri_drawable(_EGLSurface *surf)
    443 {
    444    struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
    445 
    446    return dri3_surf->loader_drawable.dri_drawable;
    447 }
    448 
    449 struct dri2_egl_display_vtbl dri3_x11_display_vtbl = {
    450    .authenticate = dri3_authenticate,
    451    .create_window_surface = dri3_create_window_surface,
    452    .create_pixmap_surface = dri3_create_pixmap_surface,
    453    .create_pbuffer_surface = dri3_create_pbuffer_surface,
    454    .destroy_surface = dri3_destroy_surface,
    455    .create_image = dri3_create_image_khr,
    456    .swap_interval = dri3_set_swap_interval,
    457    .swap_buffers = dri3_swap_buffers,
    458    .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage,
    459    .swap_buffers_region = dri2_fallback_swap_buffers_region,
    460    .post_sub_buffer = dri2_fallback_post_sub_buffer,
    461    .copy_buffers = dri3_copy_buffers,
    462    .query_buffer_age = dri3_query_buffer_age,
    463    .query_surface = dri3_query_surface,
    464    .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image,
    465    .get_sync_values = dri3_get_sync_values,
    466    .get_dri_drawable = dri3_get_dri_drawable,
    467 };
    468 
    469 EGLBoolean
    470 dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
    471 {
    472    xcb_dri3_query_version_reply_t *dri3_query;
    473    xcb_dri3_query_version_cookie_t dri3_query_cookie;
    474    xcb_present_query_version_reply_t *present_query;
    475    xcb_present_query_version_cookie_t present_query_cookie;
    476    xcb_generic_error_t *error;
    477    const xcb_query_extension_reply_t *extension;
    478 
    479    xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_dri3_id);
    480    xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_present_id);
    481 
    482    extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_dri3_id);
    483    if (!(extension && extension->present))
    484       return EGL_FALSE;
    485 
    486    extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_present_id);
    487    if (!(extension && extension->present))
    488       return EGL_FALSE;
    489 
    490    dri3_query_cookie = xcb_dri3_query_version(dri2_dpy->conn,
    491                                               XCB_DRI3_MAJOR_VERSION,
    492                                               XCB_DRI3_MINOR_VERSION);
    493 
    494    present_query_cookie = xcb_present_query_version(dri2_dpy->conn,
    495                                                     XCB_PRESENT_MAJOR_VERSION,
    496                                                     XCB_PRESENT_MINOR_VERSION);
    497 
    498    dri3_query =
    499       xcb_dri3_query_version_reply(dri2_dpy->conn, dri3_query_cookie, &error);
    500    if (dri3_query == NULL || error != NULL) {
    501       _eglLog(_EGL_WARNING, "DRI3: failed to query the version");
    502       free(dri3_query);
    503       free(error);
    504       return EGL_FALSE;
    505    }
    506    free(dri3_query);
    507 
    508    present_query =
    509       xcb_present_query_version_reply(dri2_dpy->conn,
    510                                       present_query_cookie, &error);
    511    if (present_query == NULL || error != NULL) {
    512       _eglLog(_EGL_WARNING, "DRI3: failed to query Present version");
    513       free(present_query);
    514       free(error);
    515       return EGL_FALSE;
    516    }
    517    free(present_query);
    518 
    519    dri2_dpy->fd = loader_dri3_open(dri2_dpy->conn, dri2_dpy->screen->root, 0);
    520    if (dri2_dpy->fd < 0) {
    521       int conn_error = xcb_connection_has_error(dri2_dpy->conn);
    522       _eglLog(_EGL_WARNING, "DRI3: Screen seems not DRI3 capable");
    523 
    524       if (conn_error)
    525          _eglLog(_EGL_WARNING, "DRI3: Failed to initialize");
    526 
    527       return EGL_FALSE;
    528    }
    529 
    530    dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu);
    531 
    532    dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
    533    if (!dri2_dpy->driver_name) {
    534       _eglLog(_EGL_WARNING, "DRI3: No driver found");
    535       close(dri2_dpy->fd);
    536       return EGL_FALSE;
    537    }
    538 
    539 #ifdef HAVE_WAYLAND_PLATFORM
    540    /* Only try to get a render device name since dri3 doesn't provide a
    541     * mechanism for authenticating client opened device node fds. If this
    542     * fails then don't advertise the extension. */
    543    dri2_dpy->device_name = drmGetRenderDeviceNameFromFd(dri2_dpy->fd);
    544 #endif
    545 
    546    return EGL_TRUE;
    547 }
    548