• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
dri3_free_front_buffer(struct vl_dri3_screen * scrn,struct vl_dri3_buffer * buffer)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
dri3_free_back_buffer(struct vl_dri3_screen * scrn,struct vl_dri3_buffer * buffer)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
dri3_handle_stamps(struct vl_dri3_screen * scrn,uint64_t ust,uint64_t msc)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
dri3_handle_present_event(struct vl_dri3_screen * scrn,xcb_present_generic_event_t * ge)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
dri3_flush_present_events(struct vl_dri3_screen * scrn)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
dri3_wait_present_events(struct vl_dri3_screen * scrn)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
dri3_find_back(struct vl_dri3_screen * scrn)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 *
dri3_alloc_back_buffer(struct vl_dri3_screen * scrn)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 *
dri3_get_back_buffer(struct vl_dri3_screen * scrn)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
dri3_set_drawable(struct vl_dri3_screen * scrn,Drawable drawable)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 *
dri3_get_front_buffer(struct vl_dri3_screen * scrn)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
vl_dri3_flush_frontbuffer(struct pipe_screen * screen,struct pipe_resource * resource,unsigned level,unsigned layer,void * context_private,struct pipe_box * sub_box)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 *
vl_dri3_screen_texture_from_drawable(struct vl_screen * vscreen,void * drawable)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 *
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       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 *
vl_dri3_screen_create(Display * display,int 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                                                    NULL, 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