tizen 2.4 release
[sdk/emulator-yagl.git] / EGL / x11 / yagl_dri3.c
1 #include <X11/Xlib.h>
2 #include <X11/Xutil.h>
3 #include <X11/Xlib-xcb.h>
4 #include <X11/xshmfence.h>
5 #include <xcb/xcb.h>
6 #include <xcb/dri3.h>
7 #include <xcb/present.h>
8 #include <xf86drm.h>
9 #include <sys/fcntl.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 #include "yagl_dri3.h"
14 #include "yagl_x11_drawable.h"
15 #include "yagl_x11_display.h"
16 #include "yagl_x11_image.h"
17 #include "yagl_malloc.h"
18 #include "yagl_log.h"
19 #include "vigs.h"
20
21 static inline void yagl_dri3_fence_reset(xcb_connection_t *c,
22                                          struct yagl_dri3_buffer *buffer)
23 {
24     xshmfence_reset(buffer->shm_fence);
25 }
26
27 static inline void yagl_dri3_fence_set(struct yagl_dri3_buffer *buffer)
28 {
29     xshmfence_trigger(buffer->shm_fence);
30 }
31
32 static inline void yagl_dri3_fence_trigger(xcb_connection_t *c,
33                                            struct yagl_dri3_buffer *buffer)
34 {
35     xcb_sync_trigger_fence(c, buffer->sync_fence);
36 }
37
38 static inline void yagl_dri3_fence_await(xcb_connection_t *c,
39                                          struct yagl_dri3_buffer *buffer)
40 {
41     xcb_flush(c);
42     xshmfence_await(buffer->shm_fence);
43 }
44
45 static inline Bool yagl_dri3_fence_triggered(struct yagl_dri3_buffer *buffer)
46 {
47     return xshmfence_query(buffer->shm_fence);
48 }
49
50 /** yagl_dri3_alloc_render_buffer
51  *
52  * Allocate a render buffer and create an X pixmap from that
53  *
54  * Allocate an xshmfence for synchronization
55  */
56 static struct yagl_dri3_buffer *yagl_dri3_alloc_render_buffer(struct yagl_dri3_drawable *drawable,
57                                                               unsigned int format,
58                                                               int width,
59                                                               int height,
60                                                               int depth)
61 {
62     Drawable x_drawable = YAGL_X11_DRAWABLE(drawable->base.os_drawable);
63     Display *x_dpy = YAGL_X11_DPY(drawable->base.dpy->os_dpy);
64     xcb_connection_t *c = XGetXCBConnection(x_dpy);
65     struct yagl_dri3_buffer *buffer;
66     struct vigs_drm_surface *sfc;
67     xcb_pixmap_t pixmap;
68     xcb_sync_fence_t sync_fence;
69     xcb_void_cookie_t cookie;
70     xcb_generic_error_t *error;
71     struct xshmfence *shm_fence;
72     int buffer_fd, fence_fd;
73     int stride, cpp, ret;
74
75     YAGL_LOG_FUNC_SET(yagl_dri3_alloc_render_buffer);
76
77     fence_fd = xshmfence_alloc_shm();
78
79     if (fence_fd < 0) {
80         YAGL_LOG_ERROR("DRI3 Fence object allocation failure %s",
81                        strerror(errno));
82         return NULL;
83     }
84
85     shm_fence = xshmfence_map_shm(fence_fd);
86
87     if (shm_fence == NULL) {
88         YAGL_LOG_ERROR("DRI3 Fence object map failure %s",
89                        strerror(errno));
90         goto err_shm_fence;
91     }
92
93     buffer = yagl_malloc0(sizeof(*buffer));
94
95     if (!buffer) {
96         YAGL_LOG_ERROR("DRI3 Buffer object allocation failure %s",
97                        strerror(errno));
98         goto err_buffer;
99     }
100
101     switch (format) {
102     case vigs_drm_surface_bgrx8888:
103     case vigs_drm_surface_bgra8888:
104         cpp = 4;
105         stride = width * cpp;
106         break;
107     default:
108         cpp = 0;
109         break;
110     }
111
112     if (!cpp) {
113         YAGL_LOG_ERROR("DRI3 buffer format %d invalid", format);
114         goto err_format;
115     }
116
117     ret = vigs_drm_surface_create(drawable->base.dpy->drm_dev,
118                                   width,
119                                   height,
120                                   stride,
121                                   format,
122                                   0, /* scanout */
123                                   &sfc);
124
125     if (ret) {
126         YAGL_LOG_ERROR("DRI3 surface creation failure %s", strerror(errno));
127         goto err_sfc;
128     }
129
130     /* Export allocated buffer */
131     ret = vigs_drm_prime_export_fd(drawable->base.dpy->drm_dev,
132                                    sfc,
133                                    &buffer_fd);
134
135     if (ret) {
136         YAGL_LOG_ERROR("DRI3 fd export failure %s", strerror(errno));
137         goto err_export;
138     }
139
140     /* Import fd at X side */
141     cookie = xcb_dri3_pixmap_from_buffer_checked(c,
142                                                  (pixmap = xcb_generate_id(c)),
143                                                  x_drawable,
144                                                  sfc->gem.size,
145                                                  width,
146                                                  height,
147                                                  stride,
148                                                  depth,
149                                                  cpp * 8, /* bpp */
150                                                  buffer_fd);
151     error = xcb_request_check(c, cookie);
152
153     if (error) {
154         YAGL_LOG_ERROR("DRI3 pixmap_from_buffer failed (pixmap = %p, errcode = %d)",
155                        (void *)pixmap, (int)error->error_code);
156         free(error);
157         goto err_export;
158     }
159
160     cookie = xcb_dri3_fence_from_fd_checked(c,
161                                             pixmap,
162                                             (sync_fence = xcb_generate_id(c)),
163                                             false,
164                                             fence_fd);
165     error = xcb_request_check(c, cookie);
166
167     if (error) {
168         YAGL_LOG_ERROR("DRI3 fence_from_fd failed (pixmap = %p, errcode = %d)",
169                        (void *)pixmap, (int)error->error_code);
170         free(error);
171         goto err_export;
172     }
173
174     buffer->pixmap = pixmap;
175     buffer->own_pixmap = true;
176     buffer->sync_fence = sync_fence;
177     buffer->shm_fence = shm_fence;
178     buffer->width = width;
179     buffer->height = height;
180     buffer->pitch = stride;
181     buffer->sfc = sfc;
182
183     /* Mark the buffer as idle
184      */
185     yagl_dri3_fence_set(buffer);
186
187     return buffer;
188
189 err_export:
190     vigs_drm_gem_unref(&sfc->gem);
191
192 err_sfc:
193     /* nothing to do here */
194
195 err_format:
196     yagl_free(buffer);
197
198 err_buffer:
199     xshmfence_unmap_shm(shm_fence);
200
201 err_shm_fence:
202     close(fence_fd);
203
204     YAGL_LOG_ERROR("DRI3 alloc_render_buffer failed\n");
205
206     return NULL;
207 }
208
209 /** yagl_dri3_free_render_buffer
210  *
211  * Free everything associated with one render buffer including pixmap, fence
212  * stuff and the driver surface
213  */
214 static void yagl_dri3_free_render_buffer(struct yagl_dri3_drawable *drawable,
215                                          struct yagl_dri3_buffer *buffer)
216 {
217     Display *x_dpy = YAGL_X11_DPY(drawable->base.dpy->os_dpy);
218     xcb_connection_t *c = XGetXCBConnection(x_dpy);
219
220     if (buffer->own_pixmap) {
221         xcb_free_pixmap(c, buffer->pixmap);
222     }
223
224     if (buffer->sfc) {
225         vigs_drm_gem_unref(&buffer->sfc->gem);
226     }
227
228     xcb_sync_destroy_fence(c, buffer->sync_fence);
229     xshmfence_unmap_shm(buffer->shm_fence);
230
231     yagl_free(buffer);
232 }
233
234 static void yagl_dri3_update_num_back(struct yagl_dri3_drawable *drawable)
235 {
236     drawable->num_back = 1;
237
238     if (drawable->flipping) {
239         if (!drawable->is_pixmap && !(drawable->present_capabilities & XCB_PRESENT_CAPABILITY_ASYNC)) {
240            drawable->num_back++;
241         }
242
243         drawable->num_back++;
244     }
245
246     if (drawable->swap_interval == 0) {
247         drawable->num_back++;
248     }
249
250     if (drawable->num_back < 2) {
251         drawable->num_back = 2;
252     }
253 }
254
255 /*
256  * Process one Present event
257  */
258 static void yagl_dri3_handle_present_event(struct yagl_dri3_drawable *drawable,
259                                            xcb_present_generic_event_t *ge)
260 {
261     YAGL_LOG_FUNC_SET(yagl_dri3_handle_present_event);
262
263     switch (ge->evtype) {
264     case XCB_PRESENT_CONFIGURE_NOTIFY: {
265         xcb_present_configure_notify_event_t *ce = (void *)ge;
266
267         YAGL_LOG_DEBUG("XCB_PRESENT_CONFIGURE_NOTIFY: %dx%d => %dx%d",
268                        drawable->width,
269                        drawable->height,
270                        ce->width,
271                        ce->height);
272
273         drawable->width = ce->width;
274         drawable->height = ce->height;
275
276         break;
277     }
278     case XCB_PRESENT_COMPLETE_NOTIFY: {
279         xcb_present_complete_notify_event_t *ce = (void *)ge;
280
281         YAGL_LOG_DEBUG("XCB_PRESENT_COMPLETE_NOTIFY");
282
283         /* Compute the processed SBC number from the received 32-bit serial number merged
284          * with the upper 32-bits of the sent 64-bit serial number while checking for
285          * wrap
286          */
287         if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) {
288             drawable->recv_sbc = (drawable->send_sbc & 0xffffffff00000000LL) | ce->serial;
289             if (drawable->recv_sbc > drawable->send_sbc) {
290                 drawable->recv_sbc -= 0x100000000;
291             }
292
293             switch (ce->mode) {
294             case XCB_PRESENT_COMPLETE_MODE_FLIP:
295                 drawable->flipping = true;
296                 break;
297             case XCB_PRESENT_COMPLETE_MODE_COPY:
298                 drawable->flipping = false;
299                 break;
300             }
301
302             yagl_dri3_update_num_back(drawable);
303
304             drawable->ust = ce->ust;
305             drawable->msc = ce->msc;
306         } else {
307             drawable->recv_msc_serial = ce->serial;
308             drawable->notify_ust = ce->ust;
309             drawable->notify_msc = ce->msc;
310         }
311
312         break;
313     }
314     case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
315         xcb_present_idle_notify_event_t *ie = (void *)ge;
316         int b;
317
318         YAGL_LOG_DEBUG("XCB_PRESENT_EVENT_IDLE_NOTIFY");
319
320         for (b = 0; b < sizeof(drawable->buffers) / sizeof(drawable->buffers[0]); b++) {
321             struct yagl_dri3_buffer *buf = drawable->buffers[b];
322
323             if (buf && buf->pixmap == ie->pixmap) {
324                 buf->busy = 0;
325
326                 if (drawable->num_back <= b && b < DRI3_MAX_BACK) {
327                    yagl_dri3_free_render_buffer(drawable, buf);
328                    drawable->buffers[b] = NULL;
329                 }
330
331                 break;
332             }
333         }
334
335         break;
336     }
337     }
338
339     free(ge);
340 }
341
342 /** yagl_dri3_flush_present_events
343  *
344  * Process any present events that have been received from the X server
345  */
346 static void yagl_dri3_flush_present_events(struct yagl_dri3_drawable *drawable)
347 {
348     Display *x_dpy = YAGL_X11_DPY(drawable->base.dpy->os_dpy);
349     xcb_connection_t *c = XGetXCBConnection(x_dpy);
350
351     /* Check to see if any configuration changes have occurred
352      * since we were last invoked
353      */
354     if (drawable->special_event) {
355         xcb_generic_event_t *ev;
356
357         while ((ev = xcb_poll_for_special_event(c, drawable->special_event)) != NULL) {
358             xcb_present_generic_event_t *ge = (void *)ev;
359             yagl_dri3_handle_present_event(drawable, ge);
360         }
361     }
362 }
363
364 /** yagl_dri3_update_drawable
365  *
366  * Called the first time we use the drawable and then
367  * after we receive present configure notify events to
368  * track the geometry of the drawable
369  */
370 static int yagl_dri3_update_drawable(struct yagl_dri3_drawable *drawable)
371 {
372     Display *x_dpy = YAGL_X11_DPY(drawable->base.dpy->os_dpy);
373     xcb_connection_t *c = XGetXCBConnection(x_dpy);
374
375     YAGL_LOG_FUNC_SET(yagl_dri3_update_drawable);
376
377     /* First time through, go get the current drawable geometry
378      */
379     if (drawable->width == 0 || drawable->height == 0 || drawable->depth == 0) {
380         xcb_get_geometry_cookie_t geom_cookie;
381         xcb_get_geometry_reply_t *geom_reply;
382         xcb_void_cookie_t cookie;
383         xcb_generic_error_t *error;
384         xcb_present_query_capabilities_cookie_t present_capabilities_cookie;
385         xcb_present_query_capabilities_reply_t *present_capabilities_reply;
386         Drawable x_drawable = YAGL_X11_DRAWABLE(drawable->base.os_drawable);
387
388         /* Try to select for input on the window.
389          *
390          * If the drawable is a window, this will get our events
391          * delivered.
392          *
393          * Otherwise, we'll get a BadWindow error back from this request which
394          * will let us know that the drawable is a pixmap instead.
395          */
396
397         cookie = xcb_present_select_input_checked(c,
398                                                   (drawable->eid = xcb_generate_id(c)),
399                                                   x_drawable,
400                                                   XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY|
401                                                   XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY|
402                                                   XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
403
404         present_capabilities_cookie = xcb_present_query_capabilities(c, x_drawable);
405
406         /* Create an XCB event queue to hold present events outside of the usual
407          * application event queue
408          */
409         drawable->special_event = xcb_register_for_special_xge(c,
410                                                                &xcb_present_id,
411                                                                drawable->eid,
412                                                                &drawable->base.stamp);
413
414         geom_cookie = xcb_get_geometry(c, x_drawable);
415         geom_reply = xcb_get_geometry_reply(c, geom_cookie, NULL);
416
417         if (!geom_reply) {
418             return false;
419         }
420
421         drawable->width = geom_reply->width;
422         drawable->height = geom_reply->height;
423         drawable->depth = geom_reply->depth;
424         drawable->is_pixmap = false;
425
426         free(geom_reply);
427
428         /* Check to see if our select input call failed. If it failed with a
429          * BadWindow error, then assume the drawable is a pixmap. Destroy the
430          * special event queue created above and mark the drawable as a pixmap
431          */
432
433         error = xcb_request_check(c, cookie);
434
435         present_capabilities_reply = xcb_present_query_capabilities_reply(c,
436                                                                           present_capabilities_cookie,
437                                                                           NULL);
438
439         if (present_capabilities_reply) {
440             drawable->present_capabilities = present_capabilities_reply->capabilities;
441             free(present_capabilities_reply);
442         } else {
443             drawable->present_capabilities = 0;
444         }
445
446         if (error) {
447             if (error->error_code != BadWindow) {
448                 free(error);
449                 return false;
450             }
451
452             drawable->is_pixmap = true;
453             xcb_unregister_for_special_event(c, drawable->special_event);
454             drawable->special_event = NULL;
455         }
456
457         YAGL_LOG_DEBUG("%s 0x%X initial geometry: %ux%u %u",
458                        drawable->is_pixmap ? "Pixmap": "Window",
459                        x_drawable,
460                        drawable->width,
461                        drawable->height,
462                        drawable->depth);
463     }
464
465     yagl_dri3_flush_present_events(drawable);
466
467     return true;
468 }
469
470 static xcb_gcontext_t yagl_dri3_drawable_gc(struct yagl_dri3_drawable *drawable)
471 {
472     if (!drawable->gc) {
473         Display *x_dpy = YAGL_X11_DPY(drawable->base.dpy->os_dpy);
474         Drawable x_drawable = YAGL_X11_DRAWABLE(drawable->base.os_drawable);
475         xcb_connection_t *c = XGetXCBConnection(x_dpy);
476         uint32_t v = 0;
477
478         xcb_create_gc(c,
479                       (drawable->gc = xcb_generate_id(c)),
480                       x_drawable,
481                       XCB_GC_GRAPHICS_EXPOSURES,
482                       &v);
483     }
484
485     return drawable->gc;
486 }
487
488 static struct yagl_dri3_buffer *yagl_dri3_back_buffer(struct yagl_dri3_drawable *drawable)
489 {
490    return drawable->buffers[DRI3_BACK_ID(drawable->cur_back)];
491 }
492
493 static struct yagl_dri3_buffer *yagl_dri3_front_buffer(struct yagl_dri3_drawable *drawable)
494 {
495    return drawable->buffers[DRI3_FRONT_ID];
496 }
497
498 static void yagl_dri3_copy_area(xcb_connection_t *c,
499                                 xcb_drawable_t src_drawable,
500                                 xcb_drawable_t dst_drawable,
501                                 xcb_gcontext_t gc,
502                                 int16_t src_x,
503                                 int16_t src_y,
504                                 int16_t dst_x,
505                                 int16_t dst_y,
506                                 uint16_t width,
507                                 uint16_t height)
508 {
509    xcb_void_cookie_t cookie;
510
511    cookie = xcb_copy_area_checked(c,
512                                   src_drawable,
513                                   dst_drawable,
514                                   gc,
515                                   src_x,
516                                   src_y,
517                                   dst_x,
518                                   dst_y,
519                                   width,
520                                   height);
521    xcb_discard_reply(c, cookie.sequence);
522 }
523
524 static void yagl_dri3_copy_drawable(struct yagl_dri3_drawable *drawable,
525                                     Drawable dest,
526                                     Drawable src)
527 {
528     Display *x_dpy = YAGL_X11_DPY(drawable->base.dpy->os_dpy);
529     xcb_connection_t *c = XGetXCBConnection(x_dpy);
530     struct yagl_dri3_buffer *front = yagl_dri3_front_buffer(drawable);
531
532     yagl_dri3_fence_reset(c, front);
533     yagl_dri3_copy_area(c,
534                         src,
535                         dest,
536                         yagl_dri3_drawable_gc(drawable),
537                         0,
538                         0,
539                         0,
540                         0,
541                         drawable->width,
542                         drawable->height);
543     yagl_dri3_fence_trigger(c, front);
544     yagl_dri3_fence_await(c, front);
545 }
546
547 static inline int yagl_dri3_pixmap_buf_id(enum yagl_dri3_buffer_type buffer_type)
548 {
549    if (buffer_type == yagl_dri3_buffer_back) {
550        return DRI3_BACK_ID(0);
551    } else {
552        return DRI3_FRONT_ID;
553    }
554 }
555
556 /** yagl_dri3_get_pixmap_buffer
557  *
558  * Get the DRM object for a pixmap from the X server
559  */
560 static struct yagl_dri3_buffer *yagl_dri3_get_pixmap_buffer(struct yagl_dri3_drawable *drawable,
561                                                             unsigned int format,
562                                                             enum yagl_dri3_buffer_type buffer_type)
563 {
564     Display *x_dpy = YAGL_X11_DPY(drawable->base.dpy->os_dpy);
565     xcb_connection_t *c = XGetXCBConnection(x_dpy);
566     int buf_id = yagl_dri3_pixmap_buf_id(buffer_type);
567     struct yagl_dri3_buffer *buffer = drawable->buffers[buf_id];
568     Pixmap pixmap = YAGL_X11_DRAWABLE(drawable->base.os_drawable);
569     xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
570     xcb_dri3_buffer_from_pixmap_reply_t  *bp_reply;
571     xcb_sync_fence_t sync_fence;
572     struct xshmfence *shm_fence;
573     struct vigs_drm_surface *sfc;
574     int fence_fd, *fds;
575     int ret;
576
577     YAGL_LOG_FUNC_SET(yagl_dri3_get_pixmap_buffer);
578
579     if (buffer) {
580         return buffer;
581     }
582
583     buffer = yagl_malloc0(sizeof(*buffer));
584
585     if (!buffer) {
586         YAGL_LOG_ERROR("DRI3 buffer object allocation failure");
587         return NULL;
588     }
589
590     fence_fd = xshmfence_alloc_shm();
591
592     if (fence_fd < 0) {
593         YAGL_LOG_ERROR("DRI3 Fence object allocation failure %s",
594                        strerror(errno));
595         goto err_alloc_fence;
596     }
597
598     shm_fence = xshmfence_map_shm(fence_fd);
599
600     if (shm_fence == NULL) {
601         YAGL_LOG_ERROR("DRI3 Fence object map failure %s",
602                        strerror(errno));
603         goto err_map_shm;
604     }
605
606     xcb_dri3_fence_from_fd(c,
607                            pixmap,
608                            (sync_fence = xcb_generate_id(c)),
609                            false,
610                            fence_fd);
611
612     /* Get an FD for the pixmap object
613      */
614     bp_cookie = xcb_dri3_buffer_from_pixmap(c, pixmap);
615     bp_reply = xcb_dri3_buffer_from_pixmap_reply(c, bp_cookie, NULL);
616
617     if (!bp_reply) {
618         YAGL_LOG_ERROR("DRI3 buffer_from_pixmap failed");
619         goto err_get_fd;
620     }
621
622     fds = xcb_dri3_buffer_from_pixmap_reply_fds(c, bp_reply);
623
624     /* Import exported FD */
625     ret = vigs_drm_prime_import_fd(drawable->base.dpy->drm_dev,
626                                    fds[0],
627                                    &sfc);
628
629     if (ret) {
630         YAGL_LOG_ERROR("DRI3 fd import failure %s", strerror(errno));
631         goto err_import;
632     }
633
634     close(fds[0]);
635
636     buffer->pixmap = pixmap;
637     buffer->own_pixmap = false;
638     buffer->sync_fence = sync_fence;
639     buffer->shm_fence = shm_fence;
640     buffer->width = bp_reply->width;
641     buffer->height = bp_reply->height;
642     buffer->pitch = bp_reply->stride;
643     buffer->buffer_type = buffer_type;
644     buffer->sfc = sfc;
645
646     drawable->buffers[buf_id] = buffer;
647
648     return buffer;
649
650 err_import:
651     close(fds[0]);
652
653 err_get_fd:
654     /* nothing to do here */
655
656 err_map_shm:
657     close(fence_fd);
658
659 err_alloc_fence:
660     yagl_free(buffer);
661
662     return NULL;
663 }
664
665 /** yagl_dri3_find_back
666  *
667  * Find an idle back buffer. If there isn't one, then
668  * wait for a present idle notify event from the X server
669  */
670 static int yagl_dri3_find_back(xcb_connection_t *c,
671                                struct yagl_dri3_drawable *drawable)
672 {
673    int  b;
674    xcb_generic_event_t *ev;
675    xcb_present_generic_event_t *ge;
676
677    for (;;) {
678        for (b = 0; b < drawable->num_back; b++) {
679            int id = DRI3_BACK_ID((b + drawable->cur_back) % drawable->num_back);
680            struct yagl_dri3_buffer *buffer = drawable->buffers[id];
681
682            if (!buffer || !buffer->busy) {
683                drawable->cur_back = id;
684                return id;
685            }
686        }
687
688        xcb_flush(c);
689        ev = xcb_wait_for_special_event(c, drawable->special_event);
690
691        if (!ev) {
692           return -1;
693        }
694
695        ge = (void *)ev;
696        yagl_dri3_handle_present_event(drawable, ge);
697    }
698 }
699
700 /** yagl_dri3_get_buffer
701  *
702  * Find a front or back buffer, allocating new ones as necessary
703  */
704 static struct yagl_dri3_buffer *yagl_dri3_get_buffer(struct yagl_dri3_drawable *drawable,
705                                                      unsigned int format,
706                                                      enum yagl_dri3_buffer_type buffer_type)
707 {
708     Display *x_dpy = YAGL_X11_DPY(drawable->base.dpy->os_dpy);
709     xcb_connection_t *c = XGetXCBConnection(x_dpy);
710     struct yagl_dri3_buffer *buffer;
711     int buf_id;
712
713     YAGL_LOG_FUNC_SET(yagl_dri3_get_buffer);
714
715     if (buffer_type == yagl_dri3_buffer_back) {
716         buf_id = yagl_dri3_find_back(c, drawable);
717
718         if (buf_id < 0) {
719             return NULL;
720         }
721     } else {
722         buf_id = DRI3_FRONT_ID;
723     }
724
725     buffer = drawable->buffers[buf_id];
726
727     if (!buffer ||
728         buffer->width != drawable->width ||
729         buffer->height != drawable->height) {
730         struct yagl_dri3_buffer *new_buffer = yagl_dri3_alloc_render_buffer(drawable,
731                                                                             format,
732                                                                             drawable->width,
733                                                                             drawable->height,
734                                                                             drawable->depth);
735
736         if (!new_buffer) {
737             YAGL_LOG_ERROR("DRI3 buffer object allocation failure");
738             return NULL;
739         }
740
741         /* When resizing, copy the contents of the old buffer, waiting for that
742          * copy to complete using our fences before proceeding
743          */
744         switch (buffer_type) {
745         case yagl_dri3_buffer_back:
746             if (buffer) {
747                 yagl_dri3_fence_reset(c, new_buffer);
748                 yagl_dri3_fence_await(c, buffer);
749                 yagl_dri3_copy_area(c,
750                                     buffer->pixmap,
751                                     new_buffer->pixmap,
752                                     yagl_dri3_drawable_gc(drawable),
753                                     0,
754                                     0,
755                                     0,
756                                     0,
757                                     drawable->width,
758                                     drawable->height);
759                 yagl_dri3_fence_trigger(c, new_buffer);
760                 yagl_dri3_free_render_buffer(drawable, buffer);
761             }
762             break;
763         case yagl_dri3_buffer_front:
764             yagl_dri3_fence_reset(c, new_buffer);
765             yagl_dri3_copy_area(c,
766                                 YAGL_X11_DRAWABLE(drawable->base.os_drawable),
767                                 new_buffer->pixmap,
768                                 yagl_dri3_drawable_gc(drawable),
769                                 0,
770                                 0,
771                                 0,
772                                 0,
773                                 drawable->width,
774                                 drawable->height);
775             yagl_dri3_fence_trigger(c, new_buffer);
776             break;
777         }
778
779
780         buffer = new_buffer;
781         buffer->buffer_type = buffer_type;
782         drawable->buffers[buf_id] = buffer;
783     }
784
785     yagl_dri3_fence_await(c, buffer);
786
787     return buffer;
788 }
789
790 static int yagl_dri3_drawable_get_buffer(struct yagl_native_drawable *drawable,
791                                          yagl_native_attachment attachment,
792                                          uint32_t *buffer_name,
793                                          struct vigs_drm_surface **buffer_sfc)
794 {
795     struct yagl_dri3_drawable *dri3_drawable = (struct yagl_dri3_drawable *)drawable;
796     struct yagl_dri3_buffer *buffer;
797     vigs_drm_surface_format format;
798
799     YAGL_LOG_FUNC_SET(yagl_dri3_drawable_get_buffer);
800
801     YAGL_LOG_DEBUG("enter: attachment = %d", attachment);
802
803     if (!yagl_dri3_update_drawable(dri3_drawable)) {
804         YAGL_LOG_ERROR("DRI3 drawable update failure");
805         return 0;
806     }
807
808     switch (dri3_drawable->depth) {
809     case 24:
810         format = vigs_drm_surface_bgrx8888;
811         break;
812     case 32:
813         format = vigs_drm_surface_bgra8888;
814         break;
815     default:
816         YAGL_LOG_ERROR("DRI3 bad drawable depth %d", dri3_drawable->depth);
817         return 0;
818     }
819
820     switch (attachment) {
821     case yagl_native_attachment_front:
822         buffer = yagl_dri3_get_pixmap_buffer(dri3_drawable,
823                                              format,
824                                              yagl_dri3_buffer_front);
825         break;
826     case yagl_native_attachment_back:
827         buffer = yagl_dri3_get_buffer(dri3_drawable,
828                                       format,
829                                       yagl_dri3_buffer_back);
830         break;
831     default:
832         YAGL_LOG_ERROR("DRI3 bad attachment %u", attachment);
833         return 0;
834     }
835
836     if (!buffer) {
837         YAGL_LOG_ERROR("DRI3 get_buffer failed for attachement %d", attachment);
838         return 0;
839     }
840
841     vigs_drm_gem_ref(&buffer->sfc->gem);
842     *buffer_sfc = buffer->sfc;
843
844     return 1;
845 }
846
847 static int yagl_dri3_drawable_get_buffer_age(struct yagl_native_drawable *drawable)
848 {
849     Display *x_dpy = YAGL_X11_DPY(drawable->dpy->os_dpy);
850     xcb_connection_t *c = XGetXCBConnection(x_dpy);
851     struct yagl_dri3_drawable *dri3_drawable = (struct yagl_dri3_drawable *)drawable;
852     int back_id = DRI3_BACK_ID(yagl_dri3_find_back(c, dri3_drawable));
853     struct yagl_dri3_buffer *back = dri3_drawable->buffers[back_id];
854
855     YAGL_LOG_FUNC_SET(yagl_dri3_drawable_get_buffer_age);
856
857     YAGL_LOG_DEBUG("enter");
858
859     if (back_id < 0 || !back) {
860         return 0;
861     }
862
863     if (back->last_swap != 0) {
864         return dri3_drawable->send_sbc - back->last_swap + 1;
865     } else {
866         return 0;
867     }
868 }
869
870 static void yagl_dri3_drawable_swap_buffers(struct yagl_native_drawable *drawable)
871 {
872     Display *x_dpy = YAGL_X11_DPY(drawable->dpy->os_dpy);
873     xcb_connection_t *c = XGetXCBConnection(x_dpy);
874     struct yagl_dri3_drawable *dri3_drawable = (struct yagl_dri3_drawable *)drawable;
875     struct yagl_dri3_buffer *back = yagl_dri3_back_buffer(dri3_drawable);
876     int64_t target_msc = 0;
877     int64_t divisor = 0;
878     int64_t remainder = 0;
879
880     YAGL_LOG_FUNC_SET(yagl_dri3_drawable_swap_buffers);
881
882     yagl_dri3_flush_present_events(dri3_drawable);
883
884     if (back && !dri3_drawable->is_pixmap) {
885         yagl_dri3_fence_reset(c, back);
886
887         ++dri3_drawable->send_sbc;
888
889         target_msc = dri3_drawable->msc + dri3_drawable->swap_interval *
890                      (dri3_drawable->send_sbc - dri3_drawable->recv_sbc);
891
892         YAGL_LOG_DEBUG("msc = %llu, swap_interval = %d, "
893                        "send_sbc = %llu, recv_sbc = %llu, "
894                        "target_msc = %llu",
895                        dri3_drawable->msc,
896                        dri3_drawable->swap_interval,
897                        dri3_drawable->send_sbc,
898                        dri3_drawable->recv_sbc,
899                        target_msc);
900
901         back->busy = 1;
902         back->last_swap = dri3_drawable->send_sbc;
903
904         xcb_present_pixmap(c,
905                            YAGL_X11_DRAWABLE(drawable->os_drawable), /* dst*/
906                            back->pixmap, /* src */
907                            (uint32_t)dri3_drawable->send_sbc, /* serial */
908                            0, /* valid */
909                            0, /* update */
910                            0, /* x_off */
911                            0, /* y_off */
912                            None, /* target_crtc */
913                            None, /* wait_fence */
914                            back->sync_fence, /* idle_fence */
915                            XCB_PRESENT_OPTION_NONE, /* options */
916                            target_msc, /* target_msc */
917                            divisor, /* divisor */
918                            remainder, /* remainder */
919                            0, /* notifies_len */
920                            NULL /* notifies */);
921
922         xcb_flush(c);
923
924         drawable->stamp++;
925     }
926 }
927
928 static void yagl_dri3_drawable_wait(struct yagl_native_drawable *drawable,
929                                     uint32_t width,
930                                     uint32_t height)
931 {
932     struct yagl_dri3_drawable *dri3_drawable = (struct yagl_dri3_drawable *)drawable;
933     struct yagl_dri3_buffer *front;
934
935     YAGL_LOG_FUNC_SET(yagl_dri3_drawable_wait);
936
937     YAGL_LOG_DEBUG("enter");
938
939     if (!dri3_drawable->is_pixmap) {
940         return;
941     }
942
943     front = yagl_dri3_front_buffer(dri3_drawable);
944
945     yagl_dri3_copy_drawable(dri3_drawable,
946                             front->pixmap,
947                             YAGL_X11_DRAWABLE(drawable->os_drawable));
948 }
949
950 static void yagl_dri3_drawable_copy_to_pixmap(struct yagl_native_drawable *drawable,
951                                               yagl_os_pixmap os_pixmap,
952                                               uint32_t from_x,
953                                               uint32_t from_y,
954                                               uint32_t to_x,
955                                               uint32_t to_y,
956                                               uint32_t width,
957                                               uint32_t height)
958 {
959     struct yagl_dri3_drawable *dri3_drawable = (struct yagl_dri3_drawable *)drawable;
960     struct yagl_dri3_buffer *front = yagl_dri3_front_buffer(dri3_drawable);
961     Display *x_dpy = YAGL_X11_DPY(drawable->dpy->os_dpy);
962     xcb_connection_t *c = XGetXCBConnection(x_dpy);
963     xcb_gcontext_t gc;
964     uint32_t v = 0;
965
966     YAGL_LOG_FUNC_SET(yagl_dri3_drawable_copy_to_pixmap);
967
968     YAGL_LOG_DEBUG("enter");
969
970     xcb_create_gc(c,
971                   (gc = xcb_generate_id(c)),
972                   YAGL_X11_DRAWABLE(os_pixmap),
973                   XCB_GC_GRAPHICS_EXPOSURES,
974                   &v);
975
976     yagl_dri3_fence_reset(c, front);
977     yagl_dri3_copy_area(c,
978                         YAGL_X11_DRAWABLE(drawable->os_drawable),
979                         YAGL_X11_DRAWABLE(os_pixmap),
980                         gc,
981                         from_x,
982                         from_y,
983                         to_x,
984                         to_y,
985                         width,
986                         height);
987     yagl_dri3_fence_trigger(c, front);
988     yagl_dri3_fence_await(c, front);
989
990     xcb_free_gc(c, gc);
991 }
992
993 static void yagl_dri3_drawable_set_swap_interval(struct yagl_native_drawable *drawable,
994                                                  int interval)
995 {
996     struct yagl_dri3_drawable *dri3_drawable = (struct yagl_dri3_drawable *)drawable;
997
998     YAGL_LOG_FUNC_SET(yagl_dri3_drawable_set_swap_interval);
999
1000     YAGL_LOG_DEBUG("is_pixmap = %d, interval = %d",
1001                    dri3_drawable->is_pixmap,
1002                    interval);
1003
1004     if (dri3_drawable->is_pixmap) {
1005         return;
1006     }
1007
1008     dri3_drawable->swap_interval = interval;
1009     yagl_dri3_update_num_back(dri3_drawable);
1010 }
1011
1012 static void yagl_dri3_drawable_get_geometry(struct yagl_native_drawable *drawable,
1013                                             uint32_t *width,
1014                                             uint32_t *height,
1015                                             uint32_t *depth)
1016 {
1017     struct yagl_dri3_drawable *dri3_drawable = (struct yagl_dri3_drawable *)drawable;
1018
1019     YAGL_LOG_FUNC_SET(yagl_dri3_drawable_get_geometry);
1020
1021     YAGL_LOG_DEBUG("enter");
1022
1023     *width = dri3_drawable->width;
1024     *height = dri3_drawable->height;
1025     *depth = dri3_drawable->depth;
1026 }
1027
1028 static struct yagl_native_image *yagl_dri3_drawable_get_image(struct yagl_native_drawable *drawable,
1029                                                               uint32_t width,
1030                                                               uint32_t height)
1031 {
1032     return NULL;
1033 }
1034
1035 static void yagl_dri3_drawable_destroy(struct yagl_native_drawable *drawable)
1036 {
1037     struct yagl_dri3_drawable *dri3_drawable = (struct yagl_dri3_drawable *)drawable;
1038     Display *x_dpy = YAGL_X11_DPY(drawable->dpy->os_dpy);
1039     Drawable x_drawable = YAGL_X11_DRAWABLE(drawable->os_drawable);
1040     xcb_connection_t *c = XGetXCBConnection(x_dpy);
1041     int i;
1042
1043     YAGL_LOG_FUNC_SET(yagl_dri3_drawable_destroy);
1044
1045     YAGL_LOG_DEBUG("enter");
1046
1047     for (i = 0; i < DRI3_NUM_BUFFERS; i++) {
1048         if (dri3_drawable->buffers[i]) {
1049             yagl_dri3_free_render_buffer(dri3_drawable,
1050                                          dri3_drawable->buffers[i]);
1051         }
1052     }
1053
1054     if (dri3_drawable->special_event) {
1055         xcb_unregister_for_special_event(c, dri3_drawable->special_event);
1056     }
1057
1058     if (dri3_drawable->gc) {
1059         xcb_free_gc(c, dri3_drawable->gc);
1060     }
1061
1062     if (dri3_drawable->own_drawable) {
1063         if (dri3_drawable->is_pixmap) {
1064             xcb_free_pixmap(c, x_drawable);
1065         } else {
1066             xcb_destroy_window(c, x_drawable);
1067         }
1068     }
1069
1070     yagl_native_drawable_cleanup(drawable);
1071
1072     yagl_free(dri3_drawable);
1073 }
1074
1075 struct yagl_native_drawable *yagl_dri3_drawable_create(struct yagl_native_display *dpy,
1076                                                        yagl_os_drawable os_drawable,
1077                                                        int own_drawable,
1078                                                        int is_pixmap)
1079 {
1080     struct yagl_dri3_drawable *drawable;
1081
1082     YAGL_LOG_FUNC_SET(yagl_dri3_drawable_create);
1083
1084     drawable = yagl_malloc0(sizeof(*drawable));
1085
1086     if (!drawable) {
1087         YAGL_LOG_ERROR("DRI3 Drawable object allocation failure %s",
1088                        strerror(errno));
1089         return NULL;
1090     }
1091
1092     yagl_native_drawable_init(&drawable->base,
1093                               dpy,
1094                               os_drawable);
1095
1096     drawable->base.get_buffer = &yagl_dri3_drawable_get_buffer;
1097     drawable->base.get_buffer_age = &yagl_dri3_drawable_get_buffer_age;
1098     drawable->base.swap_buffers = &yagl_dri3_drawable_swap_buffers;
1099     drawable->base.wait = &yagl_dri3_drawable_wait;
1100     drawable->base.copy_to_pixmap = &yagl_dri3_drawable_copy_to_pixmap;
1101     drawable->base.set_swap_interval = &yagl_dri3_drawable_set_swap_interval;
1102     drawable->base.get_geometry = &yagl_dri3_drawable_get_geometry;
1103     drawable->base.get_image = &yagl_dri3_drawable_get_image;
1104     drawable->base.destroy = &yagl_dri3_drawable_destroy;
1105
1106     drawable->swap_interval = 1; /* default */
1107     drawable->own_drawable = own_drawable;
1108     drawable->is_pixmap = is_pixmap;
1109
1110     yagl_dri3_update_num_back(drawable);
1111
1112     YAGL_LOG_DEBUG("os_drawable = %p, is_pixmap = %d, own_drawable = %d",
1113                    (void *)os_drawable,
1114                    is_pixmap,
1115                    own_drawable);
1116
1117     return &drawable->base;
1118 }
1119
1120 static int yagl_dri3_display_check(Display *x_dpy)
1121 {
1122     xcb_connection_t *c = XGetXCBConnection(x_dpy);
1123     xcb_dri3_query_version_cookie_t dri3_cookie;
1124     xcb_dri3_query_version_reply_t *dri3_reply;
1125     xcb_present_query_version_cookie_t present_cookie;
1126     xcb_present_query_version_reply_t *present_reply;
1127     xcb_generic_error_t *error;
1128     const xcb_query_extension_reply_t *extension;
1129
1130     YAGL_LOG_FUNC_SET(yagl_dri3_display_check);
1131
1132     xcb_prefetch_extension_data(c, &xcb_dri3_id);
1133     xcb_prefetch_extension_data(c, &xcb_present_id);
1134
1135     extension = xcb_get_extension_data(c, &xcb_dri3_id);
1136
1137     if (!(extension && extension->present)) {
1138         YAGL_LOG_DEBUG("dri3 extension not supported");
1139         return 0;
1140     }
1141
1142     extension = xcb_get_extension_data(c, &xcb_present_id);
1143
1144     if (!(extension && extension->present)) {
1145         YAGL_LOG_DEBUG("present extension not supported");
1146         return 0;
1147     }
1148
1149     dri3_cookie = xcb_dri3_query_version(c,
1150                                          XCB_DRI3_MAJOR_VERSION,
1151                                          XCB_DRI3_MINOR_VERSION);
1152
1153
1154     present_cookie = xcb_present_query_version(c,
1155                                                XCB_PRESENT_MAJOR_VERSION,
1156                                                XCB_PRESENT_MINOR_VERSION);
1157
1158     dri3_reply = xcb_dri3_query_version_reply(c, dri3_cookie, &error);
1159
1160     if (!dri3_reply) {
1161         YAGL_LOG_DEBUG("dri3: version query failed");
1162         free(error);
1163         return 0;
1164     }
1165
1166     YAGL_LOG_DEBUG("dri3: major = %d, minor = %d",
1167                    dri3_reply->major_version,
1168                    dri3_reply->minor_version);
1169
1170     free(dri3_reply);
1171
1172     present_reply = xcb_present_query_version_reply(c, present_cookie, &error);
1173
1174     if (!present_reply) {
1175         YAGL_LOG_DEBUG("present: version query failed");
1176         free(error);
1177         return 0;
1178     }
1179
1180     YAGL_LOG_DEBUG("present: major = %d, minor = %d",
1181                    present_reply->major_version,
1182                    present_reply->minor_version);
1183
1184     free(present_reply);
1185
1186     return 1;
1187 }
1188
1189 int yagl_dri3_display_init(Display *x_dpy, char **dri_device)
1190 {
1191     xcb_dri3_open_cookie_t cookie;
1192     xcb_dri3_open_reply_t *reply;
1193     xcb_connection_t *c = XGetXCBConnection(x_dpy);
1194     int fd;
1195
1196     YAGL_LOG_FUNC_SET(yagl_x11_display_dri3_init);
1197
1198     if (!yagl_dri3_display_check(x_dpy)) {
1199         YAGL_LOG_ERROR("Error: yagl_dri3_display_check failed\n");
1200         return -1;
1201     }
1202
1203     cookie = xcb_dri3_open(c, RootWindow(x_dpy, DefaultScreen(x_dpy)), None);
1204
1205     reply = xcb_dri3_open_reply(c, cookie, NULL);
1206
1207     if (!reply) {
1208         YAGL_LOG_ERROR("Error: xcb_dri3_open_reply failed\n");
1209         return -1;
1210     }
1211
1212     if (reply->nfd != 1) {
1213         YAGL_LOG_ERROR("Error: reply->nfd != 1 (%d)\n", reply->nfd);
1214         free(reply);
1215         return -1;
1216     }
1217
1218     fd = xcb_dri3_open_reply_fds(c, reply)[0];
1219     fcntl(fd, F_SETFD, FD_CLOEXEC);
1220     *dri_device = drmGetDeviceNameFromFd(fd);
1221
1222     YAGL_LOG_DEBUG("dri3 display init: fd = %d, dri_device = %s",
1223                    fd,
1224                    *dri_device);
1225
1226     return fd;
1227 }