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 is_different_gpu;
92 };
93 
94 static void
dri3_free_front_buffer(struct vl_dri3_screen * scrn,struct vl_dri3_buffer * buffer)95 dri3_free_front_buffer(struct vl_dri3_screen *scrn,
96                         struct vl_dri3_buffer *buffer)
97 {
98    xcb_sync_destroy_fence(scrn->conn, buffer->sync_fence);
99    xshmfence_unmap_shm(buffer->shm_fence);
100    pipe_resource_reference(&buffer->texture, NULL);
101    FREE(buffer);
102 }
103 
104 static void
dri3_free_back_buffer(struct vl_dri3_screen * scrn,struct vl_dri3_buffer * buffer)105 dri3_free_back_buffer(struct vl_dri3_screen *scrn,
106                         struct vl_dri3_buffer *buffer)
107 {
108    xcb_free_pixmap(scrn->conn, buffer->pixmap);
109    xcb_sync_destroy_fence(scrn->conn, buffer->sync_fence);
110    xshmfence_unmap_shm(buffer->shm_fence);
111    if (!scrn->output_texture)
112       pipe_resource_reference(&buffer->texture, NULL);
113    if (buffer->linear_texture)
114        pipe_resource_reference(&buffer->linear_texture, NULL);
115    FREE(buffer);
116 }
117 
118 static void
dri3_handle_stamps(struct vl_dri3_screen * scrn,uint64_t ust,uint64_t msc)119 dri3_handle_stamps(struct vl_dri3_screen *scrn, uint64_t ust, uint64_t msc)
120 {
121    int64_t ust_ns =  ust * 1000;
122 
123    if (scrn->last_ust && (ust_ns > scrn->last_ust) &&
124        scrn->last_msc && (msc > scrn->last_msc))
125       scrn->ns_frame = (ust_ns - scrn->last_ust) / (msc - scrn->last_msc);
126 
127    scrn->last_ust = ust_ns;
128    scrn->last_msc = msc;
129 }
130 
131 static void
dri3_handle_present_event(struct vl_dri3_screen * scrn,xcb_present_generic_event_t * ge)132 dri3_handle_present_event(struct vl_dri3_screen *scrn,
133                           xcb_present_generic_event_t *ge)
134 {
135    switch (ge->evtype) {
136    case XCB_PRESENT_CONFIGURE_NOTIFY: {
137       xcb_present_configure_notify_event_t *ce = (void *) ge;
138       scrn->width = ce->width;
139       scrn->height = ce->height;
140       break;
141    }
142    case XCB_PRESENT_COMPLETE_NOTIFY: {
143       xcb_present_complete_notify_event_t *ce = (void *) ge;
144       if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) {
145          scrn->recv_sbc = (scrn->send_sbc & 0xffffffff00000000LL) | ce->serial;
146          if (scrn->recv_sbc > scrn->send_sbc)
147             scrn->recv_sbc -= 0x100000000;
148          dri3_handle_stamps(scrn, ce->ust, ce->msc);
149       } else if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC) {
150          scrn->recv_msc_serial = ce->serial;
151          dri3_handle_stamps(scrn, ce->ust, ce->msc);
152       }
153       break;
154    }
155    case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
156       xcb_present_idle_notify_event_t *ie = (void *) ge;
157       int b;
158       for (b = 0; b < BACK_BUFFER_NUM; b++) {
159          struct vl_dri3_buffer *buf = scrn->back_buffers[b];
160          if (buf && buf->pixmap == ie->pixmap) {
161             buf->busy = false;
162             break;
163          }
164       }
165       break;
166    }
167    }
168    free(ge);
169 }
170 
171 static void
dri3_flush_present_events(struct vl_dri3_screen * scrn)172 dri3_flush_present_events(struct vl_dri3_screen *scrn)
173 {
174    if (scrn->special_event) {
175       xcb_generic_event_t *ev;
176       while ((ev = xcb_poll_for_special_event(
177                    scrn->conn, scrn->special_event)) != NULL)
178          dri3_handle_present_event(scrn, (xcb_present_generic_event_t *)ev);
179    }
180 }
181 
182 static bool
dri3_wait_present_events(struct vl_dri3_screen * scrn)183 dri3_wait_present_events(struct vl_dri3_screen *scrn)
184 {
185    if (scrn->special_event) {
186       xcb_generic_event_t *ev;
187       ev = xcb_wait_for_special_event(scrn->conn, scrn->special_event);
188       if (!ev)
189          return false;
190       dri3_handle_present_event(scrn, (xcb_present_generic_event_t *)ev);
191       return true;
192    }
193    return false;
194 }
195 
196 static int
dri3_find_back(struct vl_dri3_screen * scrn)197 dri3_find_back(struct vl_dri3_screen *scrn)
198 {
199    int b;
200 
201    for (;;) {
202       for (b = 0; b < BACK_BUFFER_NUM; b++) {
203          int id = (b + scrn->cur_back) % BACK_BUFFER_NUM;
204          struct vl_dri3_buffer *buffer = scrn->back_buffers[id];
205          if (!buffer || !buffer->busy)
206             return id;
207       }
208       xcb_flush(scrn->conn);
209       if (!dri3_wait_present_events(scrn))
210          return -1;
211    }
212 }
213 
214 static struct vl_dri3_buffer *
dri3_alloc_back_buffer(struct vl_dri3_screen * scrn)215 dri3_alloc_back_buffer(struct vl_dri3_screen *scrn)
216 {
217    struct vl_dri3_buffer *buffer;
218    xcb_pixmap_t pixmap;
219    xcb_sync_fence_t sync_fence;
220    struct xshmfence *shm_fence;
221    int buffer_fd, fence_fd;
222    struct pipe_resource templ, *pixmap_buffer_texture;
223    struct winsys_handle whandle;
224 
225    buffer = CALLOC_STRUCT(vl_dri3_buffer);
226    if (!buffer)
227       return NULL;
228 
229    fence_fd = xshmfence_alloc_shm();
230    if (fence_fd < 0)
231       goto free_buffer;
232 
233    shm_fence = xshmfence_map_shm(fence_fd);
234    if (!shm_fence)
235       goto close_fd;
236 
237    memset(&templ, 0, sizeof(templ));
238    templ.bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
239    templ.format = vl_dri2_format_for_depth(&scrn->base, scrn->depth);
240    templ.target = PIPE_TEXTURE_2D;
241    templ.last_level = 0;
242    templ.width0 = (scrn->output_texture) ?
243                   scrn->output_texture->width0 : scrn->width;
244    templ.height0 = (scrn->output_texture) ?
245                    scrn->output_texture->height0 : scrn->height;
246    templ.depth0 = 1;
247    templ.array_size = 1;
248 
249    if (scrn->is_different_gpu) {
250       buffer->texture = (scrn->output_texture) ? scrn->output_texture :
251                         scrn->base.pscreen->resource_create(scrn->base.pscreen, &templ);
252       if (!buffer->texture)
253          goto unmap_shm;
254 
255       templ.bind |= PIPE_BIND_SCANOUT | PIPE_BIND_SHARED |
256                     PIPE_BIND_LINEAR;
257       buffer->linear_texture =
258           scrn->base.pscreen->resource_create(scrn->base.pscreen, &templ);
259       pixmap_buffer_texture = buffer->linear_texture;
260 
261       if (!buffer->linear_texture)
262          goto no_linear_texture;
263    } else {
264       templ.bind |= PIPE_BIND_SCANOUT | PIPE_BIND_SHARED;
265       buffer->texture = (scrn->output_texture) ? scrn->output_texture :
266                         scrn->base.pscreen->resource_create(scrn->base.pscreen, &templ);
267       if (!buffer->texture)
268          goto unmap_shm;
269       pixmap_buffer_texture = buffer->texture;
270    }
271    memset(&whandle, 0, sizeof(whandle));
272    whandle.type= WINSYS_HANDLE_TYPE_FD;
273    scrn->base.pscreen->resource_get_handle(scrn->base.pscreen, NULL,
274                                            pixmap_buffer_texture, &whandle, 0);
275    buffer_fd = whandle.handle;
276    buffer->pitch = whandle.stride;
277    buffer->width = templ.width0;
278    buffer->height = templ.height0;
279 
280    xcb_dri3_pixmap_from_buffer(scrn->conn,
281                                (pixmap = xcb_generate_id(scrn->conn)),
282                                scrn->drawable,
283                                0,
284                                buffer->width, buffer->height, buffer->pitch,
285                                scrn->depth, 32,
286                                buffer_fd);
287    xcb_dri3_fence_from_fd(scrn->conn,
288                           pixmap,
289                           (sync_fence = xcb_generate_id(scrn->conn)),
290                           false,
291                           fence_fd);
292 
293    buffer->pixmap = pixmap;
294    buffer->sync_fence = sync_fence;
295    buffer->shm_fence = shm_fence;
296 
297    xshmfence_trigger(buffer->shm_fence);
298 
299    return buffer;
300 
301 no_linear_texture:
302    pipe_resource_reference(&buffer->texture, NULL);
303 unmap_shm:
304    xshmfence_unmap_shm(shm_fence);
305 close_fd:
306    close(fence_fd);
307 free_buffer:
308    FREE(buffer);
309    return NULL;
310 }
311 
312 static struct vl_dri3_buffer *
dri3_get_back_buffer(struct vl_dri3_screen * scrn)313 dri3_get_back_buffer(struct vl_dri3_screen *scrn)
314 {
315    struct vl_dri3_buffer *buffer;
316    struct pipe_resource *texture = NULL;
317    bool allocate_new_buffer = false;
318    int b, id;
319 
320    assert(scrn);
321 
322    scrn->cur_back = dri3_find_back(scrn);
323    if (scrn->cur_back < 0)
324       return NULL;
325    buffer = scrn->back_buffers[scrn->cur_back];
326 
327    if (scrn->output_texture) {
328       if (!buffer || buffer->width < scrn->width ||
329           buffer->height < scrn->height)
330          allocate_new_buffer = true;
331       else if (scrn->is_different_gpu)
332          /* In case of different gpu we can reuse the linear
333           * texture so we only need to set the external
334           * texture for copying
335           */
336          buffer->texture = scrn->output_texture;
337       else {
338          /* In case of a single gpu we search if the texture is
339           * already present as buffer if not we get the
340           * handle and pixmap for the texture that is set
341           */
342          for (b = 0; b < BACK_BUFFER_NUM; b++) {
343             id = (b + scrn->cur_back) % BACK_BUFFER_NUM;
344             buffer = scrn->back_buffers[id];
345             if (buffer && !buffer->busy &&
346                 buffer->texture == scrn->output_texture) {
347                scrn->cur_back = id;
348                break;
349             }
350          }
351 
352          if (b == BACK_BUFFER_NUM) {
353             allocate_new_buffer = true;
354             scrn->cur_back = scrn->next_back;
355             scrn->next_back = (scrn->next_back + 1) % BACK_BUFFER_NUM;
356             buffer = scrn->back_buffers[scrn->cur_back];
357          }
358       }
359 
360    } else {
361       if (!buffer || buffer->width != scrn->width ||
362           buffer->height != scrn->height)
363          allocate_new_buffer = true;
364    }
365 
366    if (allocate_new_buffer) {
367       struct vl_dri3_buffer *new_buffer;
368 
369       new_buffer = dri3_alloc_back_buffer(scrn);
370       if (!new_buffer)
371          return NULL;
372 
373       if (buffer)
374          dri3_free_back_buffer(scrn, buffer);
375 
376       if (!scrn->output_texture)
377          vl_compositor_reset_dirty_area(&scrn->dirty_areas[scrn->cur_back]);
378       buffer = new_buffer;
379       scrn->back_buffers[scrn->cur_back] = buffer;
380    }
381 
382    pipe_resource_reference(&texture, buffer->texture);
383    xcb_flush(scrn->conn);
384    xshmfence_await(buffer->shm_fence);
385 
386    return buffer;
387 }
388 
389 static bool
dri3_set_drawable(struct vl_dri3_screen * scrn,Drawable drawable)390 dri3_set_drawable(struct vl_dri3_screen *scrn, Drawable drawable)
391 {
392    xcb_get_geometry_cookie_t geom_cookie;
393    xcb_get_geometry_reply_t *geom_reply;
394    xcb_void_cookie_t cookie;
395    xcb_generic_error_t *error;
396    bool ret = true;
397 
398    assert(drawable);
399 
400    if (scrn->drawable == drawable)
401       return true;
402 
403    scrn->drawable = drawable;
404 
405    geom_cookie = xcb_get_geometry(scrn->conn, scrn->drawable);
406    geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL);
407    if (!geom_reply)
408       return false;
409 
410    scrn->width = geom_reply->width;
411    scrn->height = geom_reply->height;
412    scrn->depth = geom_reply->depth;
413    free(geom_reply);
414 
415    if (scrn->special_event) {
416       xcb_unregister_for_special_event(scrn->conn, scrn->special_event);
417       scrn->special_event = NULL;
418       cookie = xcb_present_select_input_checked(scrn->conn, scrn->eid,
419                                                 scrn->drawable,
420                                                 XCB_PRESENT_EVENT_MASK_NO_EVENT);
421       xcb_discard_reply(scrn->conn, cookie.sequence);
422    }
423 
424    scrn->is_pixmap = false;
425    scrn->eid = xcb_generate_id(scrn->conn);
426    cookie =
427       xcb_present_select_input_checked(scrn->conn, scrn->eid, scrn->drawable,
428                       XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY |
429                       XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY |
430                       XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
431 
432    error = xcb_request_check(scrn->conn, cookie);
433    if (error) {
434       if (error->error_code != BadWindow)
435          ret = false;
436       else {
437          scrn->is_pixmap = true;
438          scrn->base.set_back_texture_from_output = NULL;
439          if (scrn->front_buffer) {
440             dri3_free_front_buffer(scrn, scrn->front_buffer);
441             scrn->front_buffer = NULL;
442          }
443       }
444       free(error);
445    } else
446       scrn->special_event =
447          xcb_register_for_special_xge(scrn->conn, &xcb_present_id, scrn->eid, 0);
448 
449    dri3_flush_present_events(scrn);
450 
451    return ret;
452 }
453 
454 static struct vl_dri3_buffer *
dri3_get_front_buffer(struct vl_dri3_screen * scrn)455 dri3_get_front_buffer(struct vl_dri3_screen *scrn)
456 {
457    xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
458    xcb_dri3_buffer_from_pixmap_reply_t *bp_reply;
459    xcb_sync_fence_t sync_fence;
460    struct xshmfence *shm_fence;
461    int fence_fd, *fds;
462    struct winsys_handle whandle;
463    struct pipe_resource templ, *texture = NULL;
464 
465    if (scrn->front_buffer) {
466       pipe_resource_reference(&texture, scrn->front_buffer->texture);
467       return scrn->front_buffer;
468    }
469 
470    scrn->front_buffer = CALLOC_STRUCT(vl_dri3_buffer);
471    if (!scrn->front_buffer)
472       return NULL;
473 
474    fence_fd = xshmfence_alloc_shm();
475    if (fence_fd < 0)
476       goto free_buffer;
477 
478    shm_fence = xshmfence_map_shm(fence_fd);
479    if (!shm_fence)
480       goto close_fd;
481 
482    bp_cookie = xcb_dri3_buffer_from_pixmap(scrn->conn, scrn->drawable);
483    bp_reply = xcb_dri3_buffer_from_pixmap_reply(scrn->conn, bp_cookie, NULL);
484    if (!bp_reply)
485       goto unmap_shm;
486 
487    fds = xcb_dri3_buffer_from_pixmap_reply_fds(scrn->conn, bp_reply);
488    if (fds[0] < 0)
489       goto free_reply;
490 
491    memset(&whandle, 0, sizeof(whandle));
492    whandle.type = WINSYS_HANDLE_TYPE_FD;
493    whandle.handle = (unsigned)fds[0];
494    whandle.stride = bp_reply->stride;
495    memset(&templ, 0, sizeof(templ));
496    templ.bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
497    templ.format = vl_dri2_format_for_depth(&scrn->base, bp_reply->depth);
498    templ.target = PIPE_TEXTURE_2D;
499    templ.last_level = 0;
500    templ.width0 = bp_reply->width;
501    templ.height0 = bp_reply->height;
502    templ.depth0 = 1;
503    templ.array_size = 1;
504    scrn->front_buffer->texture =
505       scrn->base.pscreen->resource_from_handle(scrn->base.pscreen,
506                                                &templ, &whandle,
507                                                PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE);
508    close(fds[0]);
509    if (!scrn->front_buffer->texture)
510       goto free_reply;
511 
512    xcb_dri3_fence_from_fd(scrn->conn,
513                           scrn->drawable,
514                           (sync_fence = xcb_generate_id(scrn->conn)),
515                           false,
516                           fence_fd);
517 
518    pipe_resource_reference(&texture, scrn->front_buffer->texture);
519    scrn->front_buffer->pixmap = scrn->drawable;
520    scrn->front_buffer->width = bp_reply->width;
521    scrn->front_buffer->height = bp_reply->height;
522    scrn->front_buffer->shm_fence = shm_fence;
523    scrn->front_buffer->sync_fence = sync_fence;
524    free(bp_reply);
525 
526    return scrn->front_buffer;
527 
528 free_reply:
529    free(bp_reply);
530 unmap_shm:
531    xshmfence_unmap_shm(shm_fence);
532 close_fd:
533    close(fence_fd);
534 free_buffer:
535    FREE(scrn->front_buffer);
536    return NULL;
537 }
538 
539 static xcb_screen_t *
dri3_get_screen_for_root(xcb_connection_t * conn,xcb_window_t root)540 dri3_get_screen_for_root(xcb_connection_t *conn, xcb_window_t root)
541 {
542    xcb_screen_iterator_t screen_iter =
543    xcb_setup_roots_iterator(xcb_get_setup(conn));
544 
545    for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {
546       if (screen_iter.data->root == root)
547          return screen_iter.data;
548    }
549 
550    return NULL;
551 }
552 
553 static void
vl_dri3_flush_frontbuffer(struct pipe_screen * screen,struct pipe_resource * resource,unsigned level,unsigned layer,void * context_private,struct pipe_box * sub_box)554 vl_dri3_flush_frontbuffer(struct pipe_screen *screen,
555                           struct pipe_resource *resource,
556                           unsigned level, unsigned layer,
557                           void *context_private, struct pipe_box *sub_box)
558 {
559    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)context_private;
560    uint32_t options = XCB_PRESENT_OPTION_NONE;
561    struct vl_dri3_buffer *back;
562    struct pipe_box src_box;
563    xcb_xfixes_region_t region;
564    xcb_rectangle_t rectangle;
565 
566    back = scrn->back_buffers[scrn->cur_back];
567    if (!back)
568        return;
569 
570    while (scrn->special_event && scrn->recv_sbc < scrn->send_sbc)
571       if (!dri3_wait_present_events(scrn))
572          return;
573 
574    rectangle.x = 0;
575    rectangle.y = 0;
576    rectangle.width = (scrn->output_texture) ? scrn->clip_width : scrn->width;
577    rectangle.height = (scrn->output_texture) ? scrn->clip_height : scrn->height;
578 
579    region = xcb_generate_id(scrn->conn);
580    xcb_xfixes_create_region(scrn->conn, region, 2, &rectangle);
581 
582    if (scrn->is_different_gpu) {
583       u_box_origin_2d(back->width, back->height, &src_box);
584       scrn->pipe->resource_copy_region(scrn->pipe,
585                                        back->linear_texture,
586                                        0, 0, 0, 0,
587                                        back->texture,
588                                        0, &src_box);
589 
590       scrn->pipe->flush(scrn->pipe, NULL, 0);
591    }
592    xshmfence_reset(back->shm_fence);
593    back->busy = true;
594 
595    xcb_present_pixmap(scrn->conn,
596                       scrn->drawable,
597                       back->pixmap,
598                       (uint32_t)(++scrn->send_sbc),
599                       0, region, 0, 0,
600                       None, None,
601                       back->sync_fence,
602                       options,
603                       scrn->next_msc,
604                       0, 0, 0, NULL);
605 
606    xcb_flush(scrn->conn);
607 
608    return;
609 }
610 
611 static struct pipe_resource *
vl_dri3_screen_texture_from_drawable(struct vl_screen * vscreen,void * drawable)612 vl_dri3_screen_texture_from_drawable(struct vl_screen *vscreen, void *drawable)
613 {
614    struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen;
615    struct vl_dri3_buffer *buffer;
616 
617    assert(scrn);
618 
619    if (!dri3_set_drawable(scrn, (Drawable)drawable))
620       return NULL;
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 *
vl_dri3_screen_get_dirty_area(struct vl_screen * vscreen)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
vl_dri3_screen_get_timestamp(struct vl_screen * vscreen,void * drawable)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
vl_dri3_screen_set_next_timestamp(struct vl_screen * vscreen,uint64_t stamp)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 *
vl_dri3_screen_get_private(struct vl_screen * vscreen)683 vl_dri3_screen_get_private(struct vl_screen *vscreen)
684 {
685    return vscreen;
686 }
687 
688 static void
vl_dri3_screen_set_back_texture_from_output(struct vl_screen * vscreen,struct pipe_resource * buffer,uint32_t width,uint32_t height)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
vl_dri3_screen_destroy(struct vl_screen * vscreen)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    }
716 
717    for (i = 0; i < BACK_BUFFER_NUM; ++i) {
718       if (scrn->back_buffers[i]) {
719          dri3_free_back_buffer(scrn, scrn->back_buffers[i]);
720          scrn->back_buffers[i] = NULL;
721       }
722    }
723 
724    if (scrn->special_event) {
725       xcb_void_cookie_t cookie =
726          xcb_present_select_input_checked(scrn->conn, scrn->eid,
727                                           scrn->drawable,
728                                           XCB_PRESENT_EVENT_MASK_NO_EVENT);
729 
730       xcb_discard_reply(scrn->conn, cookie.sequence);
731       xcb_unregister_for_special_event(scrn->conn, scrn->special_event);
732    }
733    scrn->pipe->destroy(scrn->pipe);
734    scrn->base.pscreen->destroy(scrn->base.pscreen);
735    pipe_loader_release(&scrn->base.dev, 1);
736    FREE(scrn);
737 
738    return;
739 }
740 
741 struct vl_screen *
vl_dri3_screen_create(Display * display,int screen)742 vl_dri3_screen_create(Display *display, int screen)
743 {
744    struct vl_dri3_screen *scrn;
745    const xcb_query_extension_reply_t *extension;
746    xcb_dri3_open_cookie_t open_cookie;
747    xcb_dri3_open_reply_t *open_reply;
748    xcb_get_geometry_cookie_t geom_cookie;
749    xcb_get_geometry_reply_t *geom_reply;
750    xcb_xfixes_query_version_cookie_t xfixes_cookie;
751    xcb_xfixes_query_version_reply_t *xfixes_reply;
752    xcb_generic_error_t *error;
753    int fd;
754 
755    assert(display);
756 
757    scrn = CALLOC_STRUCT(vl_dri3_screen);
758    if (!scrn)
759       return NULL;
760 
761    scrn->conn = XGetXCBConnection(display);
762    if (!scrn->conn)
763       goto free_screen;
764 
765    xcb_prefetch_extension_data(scrn->conn , &xcb_dri3_id);
766    xcb_prefetch_extension_data(scrn->conn, &xcb_present_id);
767    xcb_prefetch_extension_data (scrn->conn, &xcb_xfixes_id);
768    extension = xcb_get_extension_data(scrn->conn, &xcb_dri3_id);
769    if (!(extension && extension->present))
770       goto free_screen;
771    extension = xcb_get_extension_data(scrn->conn, &xcb_present_id);
772    if (!(extension && extension->present))
773       goto free_screen;
774    extension = xcb_get_extension_data(scrn->conn, &xcb_xfixes_id);
775    if (!(extension && extension->present))
776       goto free_screen;
777 
778    xfixes_cookie = xcb_xfixes_query_version(scrn->conn, XCB_XFIXES_MAJOR_VERSION,
779                                             XCB_XFIXES_MINOR_VERSION);
780    xfixes_reply = xcb_xfixes_query_version_reply(scrn->conn, xfixes_cookie, &error);
781    if (!xfixes_reply || error || xfixes_reply->major_version < 2) {
782       free(error);
783       free(xfixes_reply);
784       goto free_screen;
785    }
786    free(xfixes_reply);
787 
788    open_cookie = xcb_dri3_open(scrn->conn, RootWindow(display, screen), None);
789    open_reply = xcb_dri3_open_reply(scrn->conn, open_cookie, NULL);
790    if (!open_reply)
791       goto free_screen;
792    if (open_reply->nfd != 1) {
793       free(open_reply);
794       goto free_screen;
795    }
796 
797    fd = xcb_dri3_open_reply_fds(scrn->conn, open_reply)[0];
798    if (fd < 0) {
799       free(open_reply);
800       goto free_screen;
801    }
802    fcntl(fd, F_SETFD, FD_CLOEXEC);
803    free(open_reply);
804 
805    fd = loader_get_user_preferred_fd(fd, &scrn->is_different_gpu);
806 
807    geom_cookie = xcb_get_geometry(scrn->conn, RootWindow(display, screen));
808    geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL);
809    if (!geom_reply)
810       goto close_fd;
811 
812    scrn->base.xcb_screen = dri3_get_screen_for_root(scrn->conn, geom_reply->root);
813    if (!scrn->base.xcb_screen) {
814       free(geom_reply);
815       goto close_fd;
816    }
817 
818    /* TODO support depth other than 24 or 30 */
819    if (geom_reply->depth != 24 && geom_reply->depth != 30) {
820       free(geom_reply);
821       goto close_fd;
822    }
823    scrn->base.color_depth = geom_reply->depth;
824    free(geom_reply);
825 
826    if (pipe_loader_drm_probe_fd(&scrn->base.dev, fd))
827       scrn->base.pscreen = pipe_loader_create_screen(scrn->base.dev);
828 
829    if (!scrn->base.pscreen)
830       goto release_pipe;
831 
832    scrn->pipe = pipe_create_multimedia_context(scrn->base.pscreen);
833    if (!scrn->pipe)
834        goto no_context;
835 
836    scrn->base.destroy = vl_dri3_screen_destroy;
837    scrn->base.texture_from_drawable = vl_dri3_screen_texture_from_drawable;
838    scrn->base.get_dirty_area = vl_dri3_screen_get_dirty_area;
839    scrn->base.get_timestamp = vl_dri3_screen_get_timestamp;
840    scrn->base.set_next_timestamp = vl_dri3_screen_set_next_timestamp;
841    scrn->base.get_private = vl_dri3_screen_get_private;
842    scrn->base.pscreen->flush_frontbuffer = vl_dri3_flush_frontbuffer;
843    scrn->base.set_back_texture_from_output = vl_dri3_screen_set_back_texture_from_output;
844 
845    scrn->next_back = 1;
846    return &scrn->base;
847 
848 no_context:
849    scrn->base.pscreen->destroy(scrn->base.pscreen);
850 release_pipe:
851    if (scrn->base.dev) {
852       pipe_loader_release(&scrn->base.dev, 1);
853       fd = -1;
854    }
855 close_fd:
856    if (fd != -1)
857       close(fd);
858 free_screen:
859    FREE(scrn);
860    return NULL;
861 }
862