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