OSDN Git Service

vl/dri: add color depth to vl winsys
[android-x86/external-mesa.git] / src / gallium / auxiliary / vl / vl_winsys_dri3.c
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    bool 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 = vl_dri2_format_for_depth(&scrn->base, scrn->depth);
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= WINSYS_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 = WINSYS_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 = vl_dri2_format_for_depth(&scrn->base, bp_reply->depth);
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 xcb_screen_t *
543 dri3_get_screen_for_root(xcb_connection_t *conn, xcb_window_t root)
544 {
545    xcb_screen_iterator_t screen_iter =
546    xcb_setup_roots_iterator(xcb_get_setup(conn));
547
548    for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {
549       if (screen_iter.data->root == root)
550          return screen_iter.data;
551    }
552
553    return NULL;
554 }
555
556 static void
557 vl_dri3_flush_frontbuffer(struct pipe_screen *screen,
558                           struct pipe_resource *resource,
559                           unsigned level, unsigned layer,
560                           void *context_private, struct pipe_box *sub_box)
561 {
562    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)context_private;
563    uint32_t options = XCB_PRESENT_OPTION_NONE;
564    struct vl_dri3_buffer *back;
565    struct pipe_box src_box;
566    xcb_xfixes_region_t region;
567    xcb_rectangle_t rectangle;
568
569    back = scrn->back_buffers[scrn->cur_back];
570    if (!back)
571        return;
572
573    if (scrn->flushed) {
574       while (scrn->special_event && scrn->recv_sbc < scrn->send_sbc)
575          if (!dri3_wait_present_events(scrn))
576             return;
577    }
578
579    rectangle.x = 0;
580    rectangle.y = 0;
581    rectangle.width = (scrn->output_texture) ? scrn->clip_width : scrn->width;
582    rectangle.height = (scrn->output_texture) ? scrn->clip_height : scrn->height;
583
584    region = xcb_generate_id(scrn->conn);
585    xcb_xfixes_create_region(scrn->conn, region, 2, &rectangle);
586
587    if (scrn->is_different_gpu) {
588       u_box_origin_2d(back->width, back->height, &src_box);
589       scrn->pipe->resource_copy_region(scrn->pipe,
590                                        back->linear_texture,
591                                        0, 0, 0, 0,
592                                        back->texture,
593                                        0, &src_box);
594
595       scrn->pipe->flush(scrn->pipe, NULL, 0);
596    }
597    xshmfence_reset(back->shm_fence);
598    back->busy = true;
599
600    xcb_present_pixmap(scrn->conn,
601                       scrn->drawable,
602                       back->pixmap,
603                       (uint32_t)(++scrn->send_sbc),
604                       0, region, 0, 0,
605                       None, None,
606                       back->sync_fence,
607                       options,
608                       scrn->next_msc,
609                       0, 0, 0, NULL);
610
611    xcb_flush(scrn->conn);
612
613    scrn->flushed = true;
614
615    return;
616 }
617
618 static struct pipe_resource *
619 vl_dri3_screen_texture_from_drawable(struct vl_screen *vscreen, void *drawable)
620 {
621    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
622    struct vl_dri3_buffer *buffer;
623
624    assert(scrn);
625
626    if (!dri3_set_drawable(scrn, (Drawable)drawable))
627       return NULL;
628
629    if (scrn->flushed) {
630       while (scrn->special_event && scrn->recv_sbc < scrn->send_sbc)
631          if (!dri3_wait_present_events(scrn))
632             return NULL;
633    }
634    scrn->flushed = false;
635
636    buffer = (scrn->is_pixmap) ?
637             dri3_get_front_buffer(scrn) :
638             dri3_get_back_buffer(scrn);
639    if (!buffer)
640       return NULL;
641
642    return buffer->texture;
643 }
644
645 static struct u_rect *
646 vl_dri3_screen_get_dirty_area(struct vl_screen *vscreen)
647 {
648    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
649
650    assert(scrn);
651
652    return &scrn->dirty_areas[scrn->cur_back];
653 }
654
655 static uint64_t
656 vl_dri3_screen_get_timestamp(struct vl_screen *vscreen, void *drawable)
657 {
658    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
659
660    assert(scrn);
661
662    if (!dri3_set_drawable(scrn, (Drawable)drawable))
663       return 0;
664
665    if (!scrn->last_ust) {
666       xcb_present_notify_msc(scrn->conn,
667                              scrn->drawable,
668                              ++scrn->send_msc_serial,
669                              0, 0, 0);
670       xcb_flush(scrn->conn);
671
672       while (scrn->special_event &&
673              scrn->send_msc_serial > scrn->recv_msc_serial) {
674          if (!dri3_wait_present_events(scrn))
675             return 0;
676       }
677    }
678
679    return scrn->last_ust;
680 }
681
682 static void
683 vl_dri3_screen_set_next_timestamp(struct vl_screen *vscreen, uint64_t stamp)
684 {
685    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
686
687    assert(scrn);
688
689    if (stamp && scrn->last_ust && scrn->ns_frame && scrn->last_msc)
690       scrn->next_msc = ((int64_t)stamp - scrn->last_ust + scrn->ns_frame/2) /
691                        scrn->ns_frame + scrn->last_msc;
692    else
693       scrn->next_msc = 0;
694 }
695
696 static void *
697 vl_dri3_screen_get_private(struct vl_screen *vscreen)
698 {
699    return vscreen;
700 }
701
702 static void
703 vl_dri3_screen_set_back_texture_from_output(struct vl_screen *vscreen,
704                                             struct pipe_resource *buffer,
705                                             uint32_t width, uint32_t height)
706 {
707    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
708
709    assert(scrn);
710
711    scrn->output_texture = buffer;
712    scrn->clip_width = (width) ? width : scrn->width;
713    scrn->clip_height = (height) ? height : scrn->height;
714 }
715
716 static void
717 vl_dri3_screen_destroy(struct vl_screen *vscreen)
718 {
719    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
720    int i;
721
722    assert(vscreen);
723
724    dri3_flush_present_events(scrn);
725
726    if (scrn->front_buffer) {
727       dri3_free_front_buffer(scrn, scrn->front_buffer);
728       scrn->front_buffer = NULL;
729       return;
730    }
731
732    for (i = 0; i < BACK_BUFFER_NUM; ++i) {
733       if (scrn->back_buffers[i]) {
734          dri3_free_back_buffer(scrn, scrn->back_buffers[i]);
735          scrn->back_buffers[i] = NULL;
736       }
737    }
738
739    if (scrn->special_event) {
740       xcb_void_cookie_t cookie =
741          xcb_present_select_input_checked(scrn->conn, scrn->eid,
742                                           scrn->drawable,
743                                           XCB_PRESENT_EVENT_MASK_NO_EVENT);
744
745       xcb_discard_reply(scrn->conn, cookie.sequence);
746       xcb_unregister_for_special_event(scrn->conn, scrn->special_event);
747    }
748    scrn->pipe->destroy(scrn->pipe);
749    scrn->base.pscreen->destroy(scrn->base.pscreen);
750    pipe_loader_release(&scrn->base.dev, 1);
751    FREE(scrn);
752
753    return;
754 }
755
756 struct vl_screen *
757 vl_dri3_screen_create(Display *display, int screen)
758 {
759    struct vl_dri3_screen *scrn;
760    const xcb_query_extension_reply_t *extension;
761    xcb_dri3_open_cookie_t open_cookie;
762    xcb_dri3_open_reply_t *open_reply;
763    xcb_get_geometry_cookie_t geom_cookie;
764    xcb_get_geometry_reply_t *geom_reply;
765    xcb_xfixes_query_version_cookie_t xfixes_cookie;
766    xcb_xfixes_query_version_reply_t *xfixes_reply;
767    xcb_generic_error_t *error;
768    int fd;
769
770    assert(display);
771
772    scrn = CALLOC_STRUCT(vl_dri3_screen);
773    if (!scrn)
774       return NULL;
775
776    scrn->conn = XGetXCBConnection(display);
777    if (!scrn->conn)
778       goto free_screen;
779
780    xcb_prefetch_extension_data(scrn->conn , &xcb_dri3_id);
781    xcb_prefetch_extension_data(scrn->conn, &xcb_present_id);
782    xcb_prefetch_extension_data (scrn->conn, &xcb_xfixes_id);
783    extension = xcb_get_extension_data(scrn->conn, &xcb_dri3_id);
784    if (!(extension && extension->present))
785       goto free_screen;
786    extension = xcb_get_extension_data(scrn->conn, &xcb_present_id);
787    if (!(extension && extension->present))
788       goto free_screen;
789    extension = xcb_get_extension_data(scrn->conn, &xcb_xfixes_id);
790    if (!(extension && extension->present))
791       goto free_screen;
792
793    xfixes_cookie = xcb_xfixes_query_version(scrn->conn, XCB_XFIXES_MAJOR_VERSION,
794                                             XCB_XFIXES_MINOR_VERSION);
795    xfixes_reply = xcb_xfixes_query_version_reply(scrn->conn, xfixes_cookie, &error);
796    if (!xfixes_reply || error || xfixes_reply->major_version < 2) {
797       free(error);
798       free(xfixes_reply);
799       goto free_screen;
800    }
801    free(xfixes_reply);
802
803    open_cookie = xcb_dri3_open(scrn->conn, RootWindow(display, screen), None);
804    open_reply = xcb_dri3_open_reply(scrn->conn, open_cookie, NULL);
805    if (!open_reply)
806       goto free_screen;
807    if (open_reply->nfd != 1) {
808       free(open_reply);
809       goto free_screen;
810    }
811
812    fd = xcb_dri3_open_reply_fds(scrn->conn, open_reply)[0];
813    if (fd < 0) {
814       free(open_reply);
815       goto free_screen;
816    }
817    fcntl(fd, F_SETFD, FD_CLOEXEC);
818    free(open_reply);
819
820    fd = loader_get_user_preferred_fd(fd, &scrn->is_different_gpu);
821
822    geom_cookie = xcb_get_geometry(scrn->conn, RootWindow(display, screen));
823    geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL);
824    if (!geom_reply)
825       goto close_fd;
826
827    scrn->base.xcb_screen = dri3_get_screen_for_root(scrn->conn, geom_reply->root);
828    if (!scrn->base.xcb_screen) {
829       free(geom_reply);
830       goto close_fd;
831    }
832
833    /* TODO support depth other than 24 or 30 */
834    if (geom_reply->depth != 24 && geom_reply->depth != 30) {
835       free(geom_reply);
836       goto close_fd;
837    }
838    scrn->base.color_depth = geom_reply->depth;
839    free(geom_reply);
840
841    if (pipe_loader_drm_probe_fd(&scrn->base.dev, fd))
842       scrn->base.pscreen = pipe_loader_create_screen(scrn->base.dev);
843
844    if (!scrn->base.pscreen)
845       goto release_pipe;
846
847    scrn->pipe = scrn->base.pscreen->context_create(scrn->base.pscreen,
848                                                    NULL, 0);
849    if (!scrn->pipe)
850        goto no_context;
851
852    scrn->base.destroy = vl_dri3_screen_destroy;
853    scrn->base.texture_from_drawable = vl_dri3_screen_texture_from_drawable;
854    scrn->base.get_dirty_area = vl_dri3_screen_get_dirty_area;
855    scrn->base.get_timestamp = vl_dri3_screen_get_timestamp;
856    scrn->base.set_next_timestamp = vl_dri3_screen_set_next_timestamp;
857    scrn->base.get_private = vl_dri3_screen_get_private;
858    scrn->base.pscreen->flush_frontbuffer = vl_dri3_flush_frontbuffer;
859    scrn->base.set_back_texture_from_output = vl_dri3_screen_set_back_texture_from_output;
860
861    scrn->next_back = 1;
862    return &scrn->base;
863
864 no_context:
865    scrn->base.pscreen->destroy(scrn->base.pscreen);
866 release_pipe:
867    if (scrn->base.dev) {
868       pipe_loader_release(&scrn->base.dev, 1);
869       fd = -1;
870    }
871 close_fd:
872    if (fd != -1)
873       close(fd);
874 free_screen:
875    FREE(scrn);
876    return NULL;
877 }