Home | History | Annotate | Download | only in vl
      1 /**************************************************************************
      2  *
      3  * Copyright 2016 Advanced Micro Devices, Inc.
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial portions
     16  * of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
     22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  **************************************************************************/
     27 
     28 #include <fcntl.h>
     29 
     30 #include <X11/Xlib-xcb.h>
     31 #include <X11/xshmfence.h>
     32 #include <xcb/dri3.h>
     33 #include <xcb/present.h>
     34 #include <xcb/xfixes.h>
     35 
     36 #include "loader.h"
     37 
     38 #include "pipe/p_screen.h"
     39 #include "pipe/p_state.h"
     40 #include "pipe-loader/pipe_loader.h"
     41 
     42 #include "util/u_memory.h"
     43 #include "util/u_inlines.h"
     44 
     45 #include "vl/vl_compositor.h"
     46 #include "vl/vl_winsys.h"
     47 
     48 #define BACK_BUFFER_NUM 3
     49 
     50 struct vl_dri3_buffer
     51 {
     52    struct pipe_resource *texture;
     53    struct pipe_resource *linear_texture;
     54 
     55    uint32_t pixmap;
     56    uint32_t sync_fence;
     57    struct xshmfence *shm_fence;
     58 
     59    bool busy;
     60    uint32_t width, height, pitch;
     61 };
     62 
     63 struct vl_dri3_screen
     64 {
     65    struct vl_screen base;
     66    xcb_connection_t *conn;
     67    xcb_drawable_t drawable;
     68 
     69    uint32_t width, height, depth;
     70 
     71    xcb_present_event_t eid;
     72    xcb_special_event_t *special_event;
     73 
     74    struct pipe_context *pipe;
     75    struct pipe_resource *output_texture;
     76    uint32_t clip_width, clip_height;
     77 
     78    struct vl_dri3_buffer *back_buffers[BACK_BUFFER_NUM];
     79    int cur_back;
     80    int next_back;
     81 
     82    struct u_rect dirty_areas[BACK_BUFFER_NUM];
     83 
     84    struct vl_dri3_buffer *front_buffer;
     85    bool is_pixmap;
     86 
     87    uint32_t send_msc_serial, recv_msc_serial;
     88    uint64_t send_sbc, recv_sbc;
     89    int64_t last_ust, ns_frame, last_msc, next_msc;
     90 
     91    bool flushed;
     92    int is_different_gpu;
     93 };
     94 
     95 static void
     96 dri3_free_front_buffer(struct vl_dri3_screen *scrn,
     97                         struct vl_dri3_buffer *buffer)
     98 {
     99    xcb_sync_destroy_fence(scrn->conn, buffer->sync_fence);
    100    xshmfence_unmap_shm(buffer->shm_fence);
    101    pipe_resource_reference(&buffer->texture, NULL);
    102    FREE(buffer);
    103 }
    104 
    105 static void
    106 dri3_free_back_buffer(struct vl_dri3_screen *scrn,
    107                         struct vl_dri3_buffer *buffer)
    108 {
    109    xcb_free_pixmap(scrn->conn, buffer->pixmap);
    110    xcb_sync_destroy_fence(scrn->conn, buffer->sync_fence);
    111    xshmfence_unmap_shm(buffer->shm_fence);
    112    if (!scrn->output_texture)
    113       pipe_resource_reference(&buffer->texture, NULL);
    114    if (buffer->linear_texture)
    115        pipe_resource_reference(&buffer->linear_texture, NULL);
    116    FREE(buffer);
    117 }
    118 
    119 static void
    120 dri3_handle_stamps(struct vl_dri3_screen *scrn, uint64_t ust, uint64_t msc)
    121 {
    122    int64_t ust_ns =  ust * 1000;
    123 
    124    if (scrn->last_ust && (ust_ns > scrn->last_ust) &&
    125        scrn->last_msc && (msc > scrn->last_msc))
    126       scrn->ns_frame = (ust_ns - scrn->last_ust) / (msc - scrn->last_msc);
    127 
    128    scrn->last_ust = ust_ns;
    129    scrn->last_msc = msc;
    130 }
    131 
    132 static void
    133 dri3_handle_present_event(struct vl_dri3_screen *scrn,
    134                           xcb_present_generic_event_t *ge)
    135 {
    136    switch (ge->evtype) {
    137    case XCB_PRESENT_CONFIGURE_NOTIFY: {
    138       xcb_present_configure_notify_event_t *ce = (void *) ge;
    139       scrn->width = ce->width;
    140       scrn->height = ce->height;
    141       break;
    142    }
    143    case XCB_PRESENT_COMPLETE_NOTIFY: {
    144       xcb_present_complete_notify_event_t *ce = (void *) ge;
    145       if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) {
    146          scrn->recv_sbc = (scrn->send_sbc & 0xffffffff00000000LL) | ce->serial;
    147          if (scrn->recv_sbc > scrn->send_sbc)
    148             scrn->recv_sbc -= 0x100000000;
    149          dri3_handle_stamps(scrn, ce->ust, ce->msc);
    150       } else if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC) {
    151          scrn->recv_msc_serial = ce->serial;
    152          dri3_handle_stamps(scrn, ce->ust, ce->msc);
    153       }
    154       break;
    155    }
    156    case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
    157       xcb_present_idle_notify_event_t *ie = (void *) ge;
    158       int b;
    159       for (b = 0; b < BACK_BUFFER_NUM; b++) {
    160          struct vl_dri3_buffer *buf = scrn->back_buffers[b];
    161          if (buf && buf->pixmap == ie->pixmap) {
    162             buf->busy = false;
    163             break;
    164          }
    165       }
    166       break;
    167    }
    168    }
    169    free(ge);
    170 }
    171 
    172 static void
    173 dri3_flush_present_events(struct vl_dri3_screen *scrn)
    174 {
    175    if (scrn->special_event) {
    176       xcb_generic_event_t *ev;
    177       while ((ev = xcb_poll_for_special_event(
    178                    scrn->conn, scrn->special_event)) != NULL)
    179          dri3_handle_present_event(scrn, (xcb_present_generic_event_t *)ev);
    180    }
    181 }
    182 
    183 static bool
    184 dri3_wait_present_events(struct vl_dri3_screen *scrn)
    185 {
    186    if (scrn->special_event) {
    187       xcb_generic_event_t *ev;
    188       ev = xcb_wait_for_special_event(scrn->conn, scrn->special_event);
    189       if (!ev)
    190          return false;
    191       dri3_handle_present_event(scrn, (xcb_present_generic_event_t *)ev);
    192       return true;
    193    }
    194    return false;
    195 }
    196 
    197 static int
    198 dri3_find_back(struct vl_dri3_screen *scrn)
    199 {
    200    int b;
    201 
    202    for (;;) {
    203       for (b = 0; b < BACK_BUFFER_NUM; b++) {
    204          int id = (b + scrn->cur_back) % BACK_BUFFER_NUM;
    205          struct vl_dri3_buffer *buffer = scrn->back_buffers[id];
    206          if (!buffer || !buffer->busy)
    207             return id;
    208       }
    209       xcb_flush(scrn->conn);
    210       if (!dri3_wait_present_events(scrn))
    211          return -1;
    212    }
    213 }
    214 
    215 static struct vl_dri3_buffer *
    216 dri3_alloc_back_buffer(struct vl_dri3_screen *scrn)
    217 {
    218    struct vl_dri3_buffer *buffer;
    219    xcb_pixmap_t pixmap;
    220    xcb_sync_fence_t sync_fence;
    221    struct xshmfence *shm_fence;
    222    int buffer_fd, fence_fd;
    223    struct pipe_resource templ, *pixmap_buffer_texture;
    224    struct winsys_handle whandle;
    225    unsigned usage;
    226 
    227    buffer = CALLOC_STRUCT(vl_dri3_buffer);
    228    if (!buffer)
    229       return NULL;
    230 
    231    fence_fd = xshmfence_alloc_shm();
    232    if (fence_fd < 0)
    233       goto free_buffer;
    234 
    235    shm_fence = xshmfence_map_shm(fence_fd);
    236    if (!shm_fence)
    237       goto close_fd;
    238 
    239    memset(&templ, 0, sizeof(templ));
    240    templ.bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
    241    templ.format = PIPE_FORMAT_B8G8R8X8_UNORM;
    242    templ.target = PIPE_TEXTURE_2D;
    243    templ.last_level = 0;
    244    templ.width0 = (scrn->output_texture) ?
    245                   scrn->output_texture->width0 : scrn->width;
    246    templ.height0 = (scrn->output_texture) ?
    247                    scrn->output_texture->height0 : scrn->height;
    248    templ.depth0 = 1;
    249    templ.array_size = 1;
    250 
    251    if (scrn->is_different_gpu) {
    252       buffer->texture = (scrn->output_texture) ? scrn->output_texture :
    253                         scrn->base.pscreen->resource_create(scrn->base.pscreen, &templ);
    254       if (!buffer->texture)
    255          goto unmap_shm;
    256 
    257       templ.bind |= PIPE_BIND_SCANOUT | PIPE_BIND_SHARED |
    258                     PIPE_BIND_LINEAR;
    259       buffer->linear_texture =
    260           scrn->base.pscreen->resource_create(scrn->base.pscreen, &templ);
    261       pixmap_buffer_texture = buffer->linear_texture;
    262 
    263       if (!buffer->linear_texture)
    264          goto no_linear_texture;
    265    } else {
    266       templ.bind |= PIPE_BIND_SCANOUT | PIPE_BIND_SHARED;
    267       buffer->texture = (scrn->output_texture) ? scrn->output_texture :
    268                         scrn->base.pscreen->resource_create(scrn->base.pscreen, &templ);
    269       if (!buffer->texture)
    270          goto unmap_shm;
    271       pixmap_buffer_texture = buffer->texture;
    272    }
    273    memset(&whandle, 0, sizeof(whandle));
    274    whandle.type= DRM_API_HANDLE_TYPE_FD;
    275    usage = PIPE_HANDLE_USAGE_EXPLICIT_FLUSH | PIPE_HANDLE_USAGE_READ;
    276    scrn->base.pscreen->resource_get_handle(scrn->base.pscreen, NULL,
    277                                            pixmap_buffer_texture, &whandle,
    278                                            usage);
    279    buffer_fd = whandle.handle;
    280    buffer->pitch = whandle.stride;
    281    buffer->width = templ.width0;
    282    buffer->height = templ.height0;
    283 
    284    xcb_dri3_pixmap_from_buffer(scrn->conn,
    285                                (pixmap = xcb_generate_id(scrn->conn)),
    286                                scrn->drawable,
    287                                0,
    288                                buffer->width, buffer->height, buffer->pitch,
    289                                scrn->depth, 32,
    290                                buffer_fd);
    291    xcb_dri3_fence_from_fd(scrn->conn,
    292                           pixmap,
    293                           (sync_fence = xcb_generate_id(scrn->conn)),
    294                           false,
    295                           fence_fd);
    296 
    297    buffer->pixmap = pixmap;
    298    buffer->sync_fence = sync_fence;
    299    buffer->shm_fence = shm_fence;
    300 
    301    xshmfence_trigger(buffer->shm_fence);
    302 
    303    return buffer;
    304 
    305 no_linear_texture:
    306    pipe_resource_reference(&buffer->texture, NULL);
    307 unmap_shm:
    308    xshmfence_unmap_shm(shm_fence);
    309 close_fd:
    310    close(fence_fd);
    311 free_buffer:
    312    FREE(buffer);
    313    return NULL;
    314 }
    315 
    316 static struct vl_dri3_buffer *
    317 dri3_get_back_buffer(struct vl_dri3_screen *scrn)
    318 {
    319    struct vl_dri3_buffer *buffer;
    320    struct pipe_resource *texture = NULL;
    321    bool allocate_new_buffer = false;
    322    int b, id;
    323 
    324    assert(scrn);
    325 
    326    scrn->cur_back = dri3_find_back(scrn);
    327    if (scrn->cur_back < 0)
    328       return NULL;
    329    buffer = scrn->back_buffers[scrn->cur_back];
    330 
    331    if (scrn->output_texture) {
    332       if (!buffer || buffer->width < scrn->width ||
    333           buffer->height < scrn->height)
    334          allocate_new_buffer = true;
    335       else if (scrn->is_different_gpu)
    336          /* In case of different gpu we can reuse the linear
    337           * texture so we only need to set the external
    338           * texture for copying
    339           */
    340          buffer->texture = scrn->output_texture;
    341       else {
    342          /* In case of a single gpu we search if the texture is
    343           * already present as buffer if not we get the
    344           * handle and pixmap for the texture that is set
    345           */
    346          for (b = 0; b < BACK_BUFFER_NUM; b++) {
    347             id = (b + scrn->cur_back) % BACK_BUFFER_NUM;
    348             buffer = scrn->back_buffers[id];
    349             if (buffer && !buffer->busy &&
    350                 buffer->texture == scrn->output_texture) {
    351                scrn->cur_back = id;
    352                break;
    353             }
    354          }
    355 
    356          if (b == BACK_BUFFER_NUM) {
    357             allocate_new_buffer = true;
    358             scrn->cur_back = scrn->next_back;
    359             scrn->next_back = (scrn->next_back + 1) % BACK_BUFFER_NUM;
    360             buffer = scrn->back_buffers[scrn->cur_back];
    361          }
    362       }
    363 
    364    } else {
    365       if (!buffer || buffer->width != scrn->width ||
    366           buffer->height != scrn->height)
    367          allocate_new_buffer = true;
    368    }
    369 
    370    if (allocate_new_buffer) {
    371       struct vl_dri3_buffer *new_buffer;
    372 
    373       new_buffer = dri3_alloc_back_buffer(scrn);
    374       if (!new_buffer)
    375          return NULL;
    376 
    377       if (buffer)
    378          dri3_free_back_buffer(scrn, buffer);
    379 
    380       if (!scrn->output_texture)
    381          vl_compositor_reset_dirty_area(&scrn->dirty_areas[scrn->cur_back]);
    382       buffer = new_buffer;
    383       scrn->back_buffers[scrn->cur_back] = buffer;
    384    }
    385 
    386    pipe_resource_reference(&texture, buffer->texture);
    387    xcb_flush(scrn->conn);
    388    xshmfence_await(buffer->shm_fence);
    389 
    390    return buffer;
    391 }
    392 
    393 static bool
    394 dri3_set_drawable(struct vl_dri3_screen *scrn, Drawable drawable)
    395 {
    396    xcb_get_geometry_cookie_t geom_cookie;
    397    xcb_get_geometry_reply_t *geom_reply;
    398    xcb_void_cookie_t cookie;
    399    xcb_generic_error_t *error;
    400    bool ret = true;
    401 
    402    assert(drawable);
    403 
    404    if (scrn->drawable == drawable)
    405       return true;
    406 
    407    scrn->drawable = drawable;
    408 
    409    geom_cookie = xcb_get_geometry(scrn->conn, scrn->drawable);
    410    geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL);
    411    if (!geom_reply)
    412       return false;
    413 
    414    scrn->width = geom_reply->width;
    415    scrn->height = geom_reply->height;
    416    scrn->depth = geom_reply->depth;
    417    free(geom_reply);
    418 
    419    if (scrn->special_event) {
    420       xcb_unregister_for_special_event(scrn->conn, scrn->special_event);
    421       scrn->special_event = NULL;
    422       cookie = xcb_present_select_input_checked(scrn->conn, scrn->eid,
    423                                                 scrn->drawable,
    424                                                 XCB_PRESENT_EVENT_MASK_NO_EVENT);
    425       xcb_discard_reply(scrn->conn, cookie.sequence);
    426    }
    427 
    428    scrn->is_pixmap = false;
    429    scrn->eid = xcb_generate_id(scrn->conn);
    430    cookie =
    431       xcb_present_select_input_checked(scrn->conn, scrn->eid, scrn->drawable,
    432                       XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY |
    433                       XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY |
    434                       XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
    435 
    436    error = xcb_request_check(scrn->conn, cookie);
    437    if (error) {
    438       if (error->error_code != BadWindow)
    439          ret = false;
    440       else {
    441          scrn->is_pixmap = true;
    442          if (scrn->front_buffer) {
    443             dri3_free_front_buffer(scrn, scrn->front_buffer);
    444             scrn->front_buffer = NULL;
    445          }
    446       }
    447       free(error);
    448    } else
    449       scrn->special_event =
    450          xcb_register_for_special_xge(scrn->conn, &xcb_present_id, scrn->eid, 0);
    451 
    452    dri3_flush_present_events(scrn);
    453 
    454    return ret;
    455 }
    456 
    457 static struct vl_dri3_buffer *
    458 dri3_get_front_buffer(struct vl_dri3_screen *scrn)
    459 {
    460    xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
    461    xcb_dri3_buffer_from_pixmap_reply_t *bp_reply;
    462    xcb_sync_fence_t sync_fence;
    463    struct xshmfence *shm_fence;
    464    int fence_fd, *fds;
    465    struct winsys_handle whandle;
    466    struct pipe_resource templ, *texture = NULL;
    467 
    468    if (scrn->front_buffer) {
    469       pipe_resource_reference(&texture, scrn->front_buffer->texture);
    470       return scrn->front_buffer;
    471    }
    472 
    473    scrn->front_buffer = CALLOC_STRUCT(vl_dri3_buffer);
    474    if (!scrn->front_buffer)
    475       return NULL;
    476 
    477    fence_fd = xshmfence_alloc_shm();
    478    if (fence_fd < 0)
    479       goto free_buffer;
    480 
    481    shm_fence = xshmfence_map_shm(fence_fd);
    482    if (!shm_fence)
    483       goto close_fd;
    484 
    485    bp_cookie = xcb_dri3_buffer_from_pixmap(scrn->conn, scrn->drawable);
    486    bp_reply = xcb_dri3_buffer_from_pixmap_reply(scrn->conn, bp_cookie, NULL);
    487    if (!bp_reply)
    488       goto unmap_shm;
    489 
    490    fds = xcb_dri3_buffer_from_pixmap_reply_fds(scrn->conn, bp_reply);
    491    if (fds[0] < 0)
    492       goto free_reply;
    493 
    494    memset(&whandle, 0, sizeof(whandle));
    495    whandle.type = DRM_API_HANDLE_TYPE_FD;
    496    whandle.handle = (unsigned)fds[0];
    497    whandle.stride = bp_reply->stride;
    498    memset(&templ, 0, sizeof(templ));
    499    templ.bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
    500    templ.format = PIPE_FORMAT_B8G8R8X8_UNORM;
    501    templ.target = PIPE_TEXTURE_2D;
    502    templ.last_level = 0;
    503    templ.width0 = bp_reply->width;
    504    templ.height0 = bp_reply->height;
    505    templ.depth0 = 1;
    506    templ.array_size = 1;
    507    scrn->front_buffer->texture =
    508       scrn->base.pscreen->resource_from_handle(scrn->base.pscreen,
    509                                                &templ, &whandle,
    510                                                PIPE_HANDLE_USAGE_READ_WRITE);
    511    close(fds[0]);
    512    if (!scrn->front_buffer->texture)
    513       goto free_reply;
    514 
    515    xcb_dri3_fence_from_fd(scrn->conn,
    516                           scrn->drawable,
    517                           (sync_fence = xcb_generate_id(scrn->conn)),
    518                           false,
    519                           fence_fd);
    520 
    521    pipe_resource_reference(&texture, scrn->front_buffer->texture);
    522    scrn->front_buffer->pixmap = scrn->drawable;
    523    scrn->front_buffer->width = bp_reply->width;
    524    scrn->front_buffer->height = bp_reply->height;
    525    scrn->front_buffer->shm_fence = shm_fence;
    526    scrn->front_buffer->sync_fence = sync_fence;
    527    free(bp_reply);
    528 
    529    return scrn->front_buffer;
    530 
    531 free_reply:
    532    free(bp_reply);
    533 unmap_shm:
    534    xshmfence_unmap_shm(shm_fence);
    535 close_fd:
    536    close(fence_fd);
    537 free_buffer:
    538    FREE(scrn->front_buffer);
    539    return NULL;
    540 }
    541 
    542 static void
    543 vl_dri3_flush_frontbuffer(struct pipe_screen *screen,
    544                           struct pipe_resource *resource,
    545                           unsigned level, unsigned layer,
    546                           void *context_private, struct pipe_box *sub_box)
    547 {
    548    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)context_private;
    549    uint32_t options = XCB_PRESENT_OPTION_NONE;
    550    struct vl_dri3_buffer *back;
    551    struct pipe_box src_box;
    552    xcb_xfixes_region_t region;
    553    xcb_rectangle_t rectangle;
    554 
    555    back = scrn->back_buffers[scrn->cur_back];
    556    if (!back)
    557        return;
    558 
    559    if (scrn->flushed) {
    560       while (scrn->special_event && scrn->recv_sbc < scrn->send_sbc)
    561          if (!dri3_wait_present_events(scrn))
    562             return;
    563    }
    564 
    565    rectangle.x = 0;
    566    rectangle.y = 0;
    567    rectangle.width = (scrn->output_texture) ? scrn->clip_width : scrn->width;
    568    rectangle.height = (scrn->output_texture) ? scrn->clip_height : scrn->height;
    569 
    570    region = xcb_generate_id(scrn->conn);
    571    xcb_xfixes_create_region(scrn->conn, region, 2, &rectangle);
    572 
    573    if (scrn->is_different_gpu) {
    574       u_box_origin_2d(back->width, back->height, &src_box);
    575       scrn->pipe->resource_copy_region(scrn->pipe,
    576                                        back->linear_texture,
    577                                        0, 0, 0, 0,
    578                                        back->texture,
    579                                        0, &src_box);
    580 
    581       scrn->pipe->flush(scrn->pipe, NULL, 0);
    582    }
    583    xshmfence_reset(back->shm_fence);
    584    back->busy = true;
    585 
    586    xcb_present_pixmap(scrn->conn,
    587                       scrn->drawable,
    588                       back->pixmap,
    589                       (uint32_t)(++scrn->send_sbc),
    590                       0, region, 0, 0,
    591                       None, None,
    592                       back->sync_fence,
    593                       options,
    594                       scrn->next_msc,
    595                       0, 0, 0, NULL);
    596 
    597    xcb_flush(scrn->conn);
    598 
    599    scrn->flushed = true;
    600 
    601    return;
    602 }
    603 
    604 static struct pipe_resource *
    605 vl_dri3_screen_texture_from_drawable(struct vl_screen *vscreen, void *drawable)
    606 {
    607    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
    608    struct vl_dri3_buffer *buffer;
    609 
    610    assert(scrn);
    611 
    612    if (!dri3_set_drawable(scrn, (Drawable)drawable))
    613       return NULL;
    614 
    615    if (scrn->flushed) {
    616       while (scrn->special_event && scrn->recv_sbc < scrn->send_sbc)
    617          if (!dri3_wait_present_events(scrn))
    618             return NULL;
    619    }
    620    scrn->flushed = false;
    621 
    622    buffer = (scrn->is_pixmap) ?
    623             dri3_get_front_buffer(scrn) :
    624             dri3_get_back_buffer(scrn);
    625    if (!buffer)
    626       return NULL;
    627 
    628    return buffer->texture;
    629 }
    630 
    631 static struct u_rect *
    632 vl_dri3_screen_get_dirty_area(struct vl_screen *vscreen)
    633 {
    634    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
    635 
    636    assert(scrn);
    637 
    638    return &scrn->dirty_areas[scrn->cur_back];
    639 }
    640 
    641 static uint64_t
    642 vl_dri3_screen_get_timestamp(struct vl_screen *vscreen, void *drawable)
    643 {
    644    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
    645 
    646    assert(scrn);
    647 
    648    if (!dri3_set_drawable(scrn, (Drawable)drawable))
    649       return 0;
    650 
    651    if (!scrn->last_ust) {
    652       xcb_present_notify_msc(scrn->conn,
    653                              scrn->drawable,
    654                              ++scrn->send_msc_serial,
    655                              0, 0, 0);
    656       xcb_flush(scrn->conn);
    657 
    658       while (scrn->special_event &&
    659              scrn->send_msc_serial > scrn->recv_msc_serial) {
    660          if (!dri3_wait_present_events(scrn))
    661             return 0;
    662       }
    663    }
    664 
    665    return scrn->last_ust;
    666 }
    667 
    668 static void
    669 vl_dri3_screen_set_next_timestamp(struct vl_screen *vscreen, uint64_t stamp)
    670 {
    671    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
    672 
    673    assert(scrn);
    674 
    675    if (stamp && scrn->last_ust && scrn->ns_frame && scrn->last_msc)
    676       scrn->next_msc = ((int64_t)stamp - scrn->last_ust + scrn->ns_frame/2) /
    677                        scrn->ns_frame + scrn->last_msc;
    678    else
    679       scrn->next_msc = 0;
    680 }
    681 
    682 static void *
    683 vl_dri3_screen_get_private(struct vl_screen *vscreen)
    684 {
    685    return vscreen;
    686 }
    687 
    688 static void
    689 vl_dri3_screen_set_back_texture_from_output(struct vl_screen *vscreen,
    690                                             struct pipe_resource *buffer,
    691                                             uint32_t width, uint32_t height)
    692 {
    693    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
    694 
    695    assert(scrn);
    696 
    697    scrn->output_texture = buffer;
    698    scrn->clip_width = (width) ? width : scrn->width;
    699    scrn->clip_height = (height) ? height : scrn->height;
    700 }
    701 
    702 static void
    703 vl_dri3_screen_destroy(struct vl_screen *vscreen)
    704 {
    705    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
    706    int i;
    707 
    708    assert(vscreen);
    709 
    710    dri3_flush_present_events(scrn);
    711 
    712    if (scrn->front_buffer) {
    713       dri3_free_front_buffer(scrn, scrn->front_buffer);
    714       scrn->front_buffer = NULL;
    715       return;
    716    }
    717 
    718    for (i = 0; i < BACK_BUFFER_NUM; ++i) {
    719       if (scrn->back_buffers[i]) {
    720          dri3_free_back_buffer(scrn, scrn->back_buffers[i]);
    721          scrn->back_buffers[i] = NULL;
    722       }
    723    }
    724 
    725    if (scrn->special_event) {
    726       xcb_void_cookie_t cookie =
    727          xcb_present_select_input_checked(scrn->conn, scrn->eid,
    728                                           scrn->drawable,
    729                                           XCB_PRESENT_EVENT_MASK_NO_EVENT);
    730 
    731       xcb_discard_reply(scrn->conn, cookie.sequence);
    732       xcb_unregister_for_special_event(scrn->conn, scrn->special_event);
    733    }
    734    scrn->pipe->destroy(scrn->pipe);
    735    scrn->base.pscreen->destroy(scrn->base.pscreen);
    736    pipe_loader_release(&scrn->base.dev, 1);
    737    FREE(scrn);
    738 
    739    return;
    740 }
    741 
    742 struct vl_screen *
    743 vl_dri3_screen_create(Display *display, int screen)
    744 {
    745    struct vl_dri3_screen *scrn;
    746    const xcb_query_extension_reply_t *extension;
    747    xcb_dri3_open_cookie_t open_cookie;
    748    xcb_dri3_open_reply_t *open_reply;
    749    xcb_get_geometry_cookie_t geom_cookie;
    750    xcb_get_geometry_reply_t *geom_reply;
    751    xcb_xfixes_query_version_cookie_t xfixes_cookie;
    752    xcb_xfixes_query_version_reply_t *xfixes_reply;
    753    xcb_generic_error_t *error;
    754    int fd;
    755 
    756    assert(display);
    757 
    758    scrn = CALLOC_STRUCT(vl_dri3_screen);
    759    if (!scrn)
    760       return NULL;
    761 
    762    scrn->conn = XGetXCBConnection(display);
    763    if (!scrn->conn)
    764       goto free_screen;
    765 
    766    xcb_prefetch_extension_data(scrn->conn , &xcb_dri3_id);
    767    xcb_prefetch_extension_data(scrn->conn, &xcb_present_id);
    768    xcb_prefetch_extension_data (scrn->conn, &xcb_xfixes_id);
    769    extension = xcb_get_extension_data(scrn->conn, &xcb_dri3_id);
    770    if (!(extension && extension->present))
    771       goto free_screen;
    772    extension = xcb_get_extension_data(scrn->conn, &xcb_present_id);
    773    if (!(extension && extension->present))
    774       goto free_screen;
    775    extension = xcb_get_extension_data(scrn->conn, &xcb_xfixes_id);
    776    if (!(extension && extension->present))
    777       goto free_screen;
    778 
    779    xfixes_cookie = xcb_xfixes_query_version(scrn->conn, XCB_XFIXES_MAJOR_VERSION,
    780                                             XCB_XFIXES_MINOR_VERSION);
    781    xfixes_reply = xcb_xfixes_query_version_reply(scrn->conn, xfixes_cookie, &error);
    782    if (!xfixes_reply || error || xfixes_reply->major_version < 2) {
    783       free(error);
    784       free(xfixes_reply);
    785       goto free_screen;
    786    }
    787    free(xfixes_reply);
    788 
    789    open_cookie = xcb_dri3_open(scrn->conn, RootWindow(display, screen), None);
    790    open_reply = xcb_dri3_open_reply(scrn->conn, open_cookie, NULL);
    791    if (!open_reply)
    792       goto free_screen;
    793    if (open_reply->nfd != 1) {
    794       free(open_reply);
    795       goto free_screen;
    796    }
    797 
    798    fd = xcb_dri3_open_reply_fds(scrn->conn, open_reply)[0];
    799    if (fd < 0) {
    800       free(open_reply);
    801       goto free_screen;
    802    }
    803    fcntl(fd, F_SETFD, FD_CLOEXEC);
    804    free(open_reply);
    805 
    806    fd = loader_get_user_preferred_fd(fd, &scrn->is_different_gpu);
    807 
    808    geom_cookie = xcb_get_geometry(scrn->conn, RootWindow(display, screen));
    809    geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL);
    810    if (!geom_reply)
    811       goto close_fd;
    812    /* TODO support depth other than 24 */
    813    if (geom_reply->depth != 24) {
    814       free(geom_reply);
    815       goto close_fd;
    816    }
    817    free(geom_reply);
    818 
    819    if (pipe_loader_drm_probe_fd(&scrn->base.dev, fd))
    820       scrn->base.pscreen = pipe_loader_create_screen(scrn->base.dev);
    821 
    822    if (!scrn->base.pscreen)
    823       goto release_pipe;
    824 
    825    scrn->pipe = scrn->base.pscreen->context_create(scrn->base.pscreen,
    826                                                    &scrn->base, 0);
    827    if (!scrn->pipe)
    828        goto no_context;
    829 
    830    scrn->base.destroy = vl_dri3_screen_destroy;
    831    scrn->base.texture_from_drawable = vl_dri3_screen_texture_from_drawable;
    832    scrn->base.get_dirty_area = vl_dri3_screen_get_dirty_area;
    833    scrn->base.get_timestamp = vl_dri3_screen_get_timestamp;
    834    scrn->base.set_next_timestamp = vl_dri3_screen_set_next_timestamp;
    835    scrn->base.get_private = vl_dri3_screen_get_private;
    836    scrn->base.pscreen->flush_frontbuffer = vl_dri3_flush_frontbuffer;
    837    scrn->base.set_back_texture_from_output = vl_dri3_screen_set_back_texture_from_output;
    838 
    839    scrn->next_back = 1;
    840    return &scrn->base;
    841 
    842 no_context:
    843    scrn->base.pscreen->destroy(scrn->base.pscreen);
    844 release_pipe:
    845    if (scrn->base.dev) {
    846       pipe_loader_release(&scrn->base.dev, 1);
    847       fd = -1;
    848    }
    849 close_fd:
    850    if (fd != -1)
    851       close(fd);
    852 free_screen:
    853    FREE(scrn);
    854    return NULL;
    855 }
    856