1 /*
2 * Copyright © 2013 Keith Packard
3 * Copyright © 2015 Boyan Ding
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no representations
12 * about the suitability of this software for any purpose. It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28
29 #include <X11/xshmfence.h>
30 #include <xcb/xcb.h>
31 #include <xcb/dri3.h>
32 #include <xcb/present.h>
33 #include <xcb/xfixes.h>
34
35 #include <X11/Xlib-xcb.h>
36
37 #include "loader_dri_helper.h"
38 #include "loader_dri3_helper.h"
39 #include "util/macros.h"
40 #include "drm-uapi/drm_fourcc.h"
41
42 /* From driconf.h, user exposed so should be stable */
43 #define DRI_CONF_VBLANK_NEVER 0
44 #define DRI_CONF_VBLANK_DEF_INTERVAL_0 1
45 #define DRI_CONF_VBLANK_DEF_INTERVAL_1 2
46 #define DRI_CONF_VBLANK_ALWAYS_SYNC 3
47
48 /**
49 * A cached blit context.
50 */
51 struct loader_dri3_blit_context {
52 mtx_t mtx;
53 __DRIcontext *ctx;
54 __DRIscreen *cur_screen;
55 const __DRIcoreExtension *core;
56 };
57
58 /* For simplicity we maintain the cache only for a single screen at a time */
59 static struct loader_dri3_blit_context blit_context = {
60 _MTX_INITIALIZER_NP, NULL
61 };
62
63 static void
64 dri3_flush_present_events(struct loader_dri3_drawable *draw);
65
66 static struct loader_dri3_buffer *
67 dri3_find_back_alloc(struct loader_dri3_drawable *draw);
68
69 static xcb_screen_t *
get_screen_for_root(xcb_connection_t * conn,xcb_window_t root)70 get_screen_for_root(xcb_connection_t *conn, xcb_window_t root)
71 {
72 xcb_screen_iterator_t screen_iter =
73 xcb_setup_roots_iterator(xcb_get_setup(conn));
74
75 for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {
76 if (screen_iter.data->root == root)
77 return screen_iter.data;
78 }
79
80 return NULL;
81 }
82
83 static xcb_visualtype_t *
get_xcb_visualtype_for_depth(struct loader_dri3_drawable * draw,int depth)84 get_xcb_visualtype_for_depth(struct loader_dri3_drawable *draw, int depth)
85 {
86 xcb_visualtype_iterator_t visual_iter;
87 xcb_screen_t *screen = draw->screen;
88 xcb_depth_iterator_t depth_iter;
89
90 if (!screen)
91 return NULL;
92
93 depth_iter = xcb_screen_allowed_depths_iterator(screen);
94 for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
95 if (depth_iter.data->depth != depth)
96 continue;
97
98 visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
99 if (visual_iter.rem)
100 return visual_iter.data;
101 }
102
103 return NULL;
104 }
105
106 /* Sets the adaptive sync window property state. */
107 static void
set_adaptive_sync_property(xcb_connection_t * conn,xcb_drawable_t drawable,uint32_t state)108 set_adaptive_sync_property(xcb_connection_t *conn, xcb_drawable_t drawable,
109 uint32_t state)
110 {
111 static char const name[] = "_VARIABLE_REFRESH";
112 xcb_intern_atom_cookie_t cookie;
113 xcb_intern_atom_reply_t* reply;
114 xcb_void_cookie_t check;
115
116 cookie = xcb_intern_atom(conn, 0, strlen(name), name);
117 reply = xcb_intern_atom_reply(conn, cookie, NULL);
118 if (reply == NULL)
119 return;
120
121 if (state)
122 check = xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE,
123 drawable, reply->atom,
124 XCB_ATOM_CARDINAL, 32, 1, &state);
125 else
126 check = xcb_delete_property_checked(conn, drawable, reply->atom);
127
128 xcb_discard_reply(conn, check.sequence);
129 free(reply);
130 }
131
132 /* Get red channel mask for given drawable at given depth. */
133 static unsigned int
dri3_get_red_mask_for_depth(struct loader_dri3_drawable * draw,int depth)134 dri3_get_red_mask_for_depth(struct loader_dri3_drawable *draw, int depth)
135 {
136 xcb_visualtype_t *visual = get_xcb_visualtype_for_depth(draw, depth);
137
138 if (visual)
139 return visual->red_mask;
140
141 return 0;
142 }
143
144 /**
145 * Do we have blit functionality in the image blit extension?
146 *
147 * \param draw[in] The drawable intended to blit from / to.
148 * \return true if we have blit functionality. false otherwise.
149 */
loader_dri3_have_image_blit(const struct loader_dri3_drawable * draw)150 static bool loader_dri3_have_image_blit(const struct loader_dri3_drawable *draw)
151 {
152 return draw->ext->image->base.version >= 9 &&
153 draw->ext->image->blitImage != NULL;
154 }
155
156 /**
157 * Get and lock (for use with the current thread) a dri context associated
158 * with the drawable's dri screen. The context is intended to be used with
159 * the dri image extension's blitImage method.
160 *
161 * \param draw[in] Pointer to the drawable whose dri screen we want a
162 * dri context for.
163 * \return A dri context or NULL if context creation failed.
164 *
165 * When the caller is done with the context (even if the context returned was
166 * NULL), the caller must call loader_dri3_blit_context_put.
167 */
168 static __DRIcontext *
loader_dri3_blit_context_get(struct loader_dri3_drawable * draw)169 loader_dri3_blit_context_get(struct loader_dri3_drawable *draw)
170 {
171 mtx_lock(&blit_context.mtx);
172
173 if (blit_context.ctx && blit_context.cur_screen != draw->dri_screen) {
174 blit_context.core->destroyContext(blit_context.ctx);
175 blit_context.ctx = NULL;
176 }
177
178 if (!blit_context.ctx) {
179 blit_context.ctx = draw->ext->core->createNewContext(draw->dri_screen,
180 NULL, NULL, NULL);
181 blit_context.cur_screen = draw->dri_screen;
182 blit_context.core = draw->ext->core;
183 }
184
185 return blit_context.ctx;
186 }
187
188 /**
189 * Release (for use with other threads) a dri context previously obtained using
190 * loader_dri3_blit_context_get.
191 */
192 static void
loader_dri3_blit_context_put(void)193 loader_dri3_blit_context_put(void)
194 {
195 mtx_unlock(&blit_context.mtx);
196 }
197
198 /**
199 * Blit (parts of) the contents of a DRI image to another dri image
200 *
201 * \param draw[in] The drawable which owns the images.
202 * \param dst[in] The destination image.
203 * \param src[in] The source image.
204 * \param dstx0[in] Start destination coordinate.
205 * \param dsty0[in] Start destination coordinate.
206 * \param width[in] Blit width.
207 * \param height[in] Blit height.
208 * \param srcx0[in] Start source coordinate.
209 * \param srcy0[in] Start source coordinate.
210 * \param flush_flag[in] Image blit flush flag.
211 * \return true iff successful.
212 */
213 static bool
loader_dri3_blit_image(struct loader_dri3_drawable * draw,__DRIimage * dst,__DRIimage * src,int dstx0,int dsty0,int width,int height,int srcx0,int srcy0,int flush_flag)214 loader_dri3_blit_image(struct loader_dri3_drawable *draw,
215 __DRIimage *dst, __DRIimage *src,
216 int dstx0, int dsty0, int width, int height,
217 int srcx0, int srcy0, int flush_flag)
218 {
219 __DRIcontext *dri_context;
220 bool use_blit_context = false;
221
222 if (!loader_dri3_have_image_blit(draw))
223 return false;
224
225 dri_context = draw->vtable->get_dri_context(draw);
226
227 if (!dri_context || !draw->vtable->in_current_context(draw)) {
228 dri_context = loader_dri3_blit_context_get(draw);
229 use_blit_context = true;
230 flush_flag |= __BLIT_FLAG_FLUSH;
231 }
232
233 if (dri_context)
234 draw->ext->image->blitImage(dri_context, dst, src, dstx0, dsty0,
235 width, height, srcx0, srcy0,
236 width, height, flush_flag);
237
238 if (use_blit_context)
239 loader_dri3_blit_context_put();
240
241 return dri_context != NULL;
242 }
243
244 static inline void
dri3_fence_reset(xcb_connection_t * c,struct loader_dri3_buffer * buffer)245 dri3_fence_reset(xcb_connection_t *c, struct loader_dri3_buffer *buffer)
246 {
247 xshmfence_reset(buffer->shm_fence);
248 }
249
250 static inline void
dri3_fence_set(struct loader_dri3_buffer * buffer)251 dri3_fence_set(struct loader_dri3_buffer *buffer)
252 {
253 xshmfence_trigger(buffer->shm_fence);
254 }
255
256 static inline void
dri3_fence_trigger(xcb_connection_t * c,struct loader_dri3_buffer * buffer)257 dri3_fence_trigger(xcb_connection_t *c, struct loader_dri3_buffer *buffer)
258 {
259 xcb_sync_trigger_fence(c, buffer->sync_fence);
260 }
261
262 static inline void
dri3_fence_await(xcb_connection_t * c,struct loader_dri3_drawable * draw,struct loader_dri3_buffer * buffer)263 dri3_fence_await(xcb_connection_t *c, struct loader_dri3_drawable *draw,
264 struct loader_dri3_buffer *buffer)
265 {
266 xcb_flush(c);
267 xshmfence_await(buffer->shm_fence);
268 if (draw) {
269 mtx_lock(&draw->mtx);
270 dri3_flush_present_events(draw);
271 mtx_unlock(&draw->mtx);
272 }
273 }
274
275 static void
dri3_update_max_num_back(struct loader_dri3_drawable * draw)276 dri3_update_max_num_back(struct loader_dri3_drawable *draw)
277 {
278 switch (draw->last_present_mode) {
279 case XCB_PRESENT_COMPLETE_MODE_FLIP: {
280 int new_max;
281
282 if (draw->swap_interval == 0)
283 new_max = 4;
284 else
285 new_max = 3;
286
287 assert(new_max <= LOADER_DRI3_MAX_BACK);
288
289 if (new_max != draw->max_num_back) {
290 /* On transition from swap interval == 0 to != 0, start with two
291 * buffers again. Otherwise keep the current number of buffers. Either
292 * way, more will be allocated if needed.
293 */
294 if (new_max < draw->max_num_back)
295 draw->cur_num_back = 2;
296
297 draw->max_num_back = new_max;
298 }
299
300 break;
301 }
302
303 case XCB_PRESENT_COMPLETE_MODE_SKIP:
304 break;
305
306 default:
307 /* On transition from flips to copies, start with a single buffer again,
308 * a second one will be allocated if needed
309 */
310 if (draw->max_num_back != 2)
311 draw->cur_num_back = 1;
312
313 draw->max_num_back = 2;
314 }
315 }
316
317 void
loader_dri3_set_swap_interval(struct loader_dri3_drawable * draw,int interval)318 loader_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval)
319 {
320 /* Wait all previous swap done before changing swap interval.
321 *
322 * This is for preventing swap out of order in the following cases:
323 * 1. Change from sync swap mode (>0) to async mode (=0), so async swap occurs
324 * before previous pending sync swap.
325 * 2. Change from value A to B and A > B, so the target_msc for the previous
326 * pending swap may be bigger than newer swap.
327 *
328 * PS. changing from value A to B and A < B won't cause swap out of order but
329 * may still gets wrong target_msc value at the beginning.
330 */
331 if (draw->swap_interval != interval)
332 loader_dri3_swapbuffer_barrier(draw);
333
334 draw->swap_interval = interval;
335 }
336
337 /** dri3_free_render_buffer
338 *
339 * Free everything associated with one render buffer including pixmap, fence
340 * stuff and the driver image
341 */
342 static void
dri3_free_render_buffer(struct loader_dri3_drawable * draw,struct loader_dri3_buffer * buffer)343 dri3_free_render_buffer(struct loader_dri3_drawable *draw,
344 struct loader_dri3_buffer *buffer)
345 {
346 if (buffer->own_pixmap)
347 xcb_free_pixmap(draw->conn, buffer->pixmap);
348 xcb_sync_destroy_fence(draw->conn, buffer->sync_fence);
349 xshmfence_unmap_shm(buffer->shm_fence);
350 draw->ext->image->destroyImage(buffer->image);
351 if (buffer->linear_buffer)
352 draw->ext->image->destroyImage(buffer->linear_buffer);
353 free(buffer);
354 }
355
356 void
loader_dri3_drawable_fini(struct loader_dri3_drawable * draw)357 loader_dri3_drawable_fini(struct loader_dri3_drawable *draw)
358 {
359 int i;
360
361 draw->ext->core->destroyDrawable(draw->dri_drawable);
362
363 for (i = 0; i < ARRAY_SIZE(draw->buffers); i++) {
364 if (draw->buffers[i])
365 dri3_free_render_buffer(draw, draw->buffers[i]);
366 }
367
368 if (draw->special_event) {
369 xcb_void_cookie_t cookie =
370 xcb_present_select_input_checked(draw->conn, draw->eid, draw->drawable,
371 XCB_PRESENT_EVENT_MASK_NO_EVENT);
372
373 xcb_discard_reply(draw->conn, cookie.sequence);
374 xcb_unregister_for_special_event(draw->conn, draw->special_event);
375 }
376
377 if (draw->region)
378 xcb_xfixes_destroy_region(draw->conn, draw->region);
379
380 cnd_destroy(&draw->event_cnd);
381 mtx_destroy(&draw->mtx);
382 }
383
384 int
loader_dri3_drawable_init(xcb_connection_t * conn,xcb_drawable_t drawable,__DRIscreen * dri_screen,bool is_different_gpu,bool multiplanes_available,bool prefer_back_buffer_reuse,const __DRIconfig * dri_config,struct loader_dri3_extensions * ext,const struct loader_dri3_vtable * vtable,struct loader_dri3_drawable * draw)385 loader_dri3_drawable_init(xcb_connection_t *conn,
386 xcb_drawable_t drawable,
387 __DRIscreen *dri_screen,
388 bool is_different_gpu,
389 bool multiplanes_available,
390 bool prefer_back_buffer_reuse,
391 const __DRIconfig *dri_config,
392 struct loader_dri3_extensions *ext,
393 const struct loader_dri3_vtable *vtable,
394 struct loader_dri3_drawable *draw)
395 {
396 xcb_get_geometry_cookie_t cookie;
397 xcb_get_geometry_reply_t *reply;
398 xcb_generic_error_t *error;
399 GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
400 int swap_interval;
401
402 draw->conn = conn;
403 draw->ext = ext;
404 draw->vtable = vtable;
405 draw->drawable = drawable;
406 draw->region = 0;
407 draw->dri_screen = dri_screen;
408 draw->is_different_gpu = is_different_gpu;
409 draw->multiplanes_available = multiplanes_available;
410 draw->prefer_back_buffer_reuse = prefer_back_buffer_reuse;
411
412 draw->have_back = 0;
413 draw->have_fake_front = 0;
414 draw->first_init = true;
415 draw->adaptive_sync = false;
416 draw->adaptive_sync_active = false;
417
418 draw->cur_blit_source = -1;
419 draw->back_format = __DRI_IMAGE_FORMAT_NONE;
420 mtx_init(&draw->mtx, mtx_plain);
421 cnd_init(&draw->event_cnd);
422
423 if (draw->ext->config) {
424 unsigned char adaptive_sync = 0;
425
426 draw->ext->config->configQueryi(draw->dri_screen,
427 "vblank_mode", &vblank_mode);
428
429 draw->ext->config->configQueryb(draw->dri_screen,
430 "adaptive_sync",
431 &adaptive_sync);
432
433 draw->adaptive_sync = adaptive_sync;
434 }
435
436 if (!draw->adaptive_sync)
437 set_adaptive_sync_property(conn, draw->drawable, false);
438
439 switch (vblank_mode) {
440 case DRI_CONF_VBLANK_NEVER:
441 case DRI_CONF_VBLANK_DEF_INTERVAL_0:
442 swap_interval = 0;
443 break;
444 case DRI_CONF_VBLANK_DEF_INTERVAL_1:
445 case DRI_CONF_VBLANK_ALWAYS_SYNC:
446 default:
447 swap_interval = 1;
448 break;
449 }
450 draw->swap_interval = swap_interval;
451
452 dri3_update_max_num_back(draw);
453
454 /* Create a new drawable */
455 draw->dri_drawable =
456 draw->ext->image_driver->createNewDrawable(dri_screen,
457 dri_config,
458 draw);
459
460 if (!draw->dri_drawable)
461 return 1;
462
463 cookie = xcb_get_geometry(draw->conn, draw->drawable);
464 reply = xcb_get_geometry_reply(draw->conn, cookie, &error);
465 if (reply == NULL || error != NULL) {
466 draw->ext->core->destroyDrawable(draw->dri_drawable);
467 return 1;
468 }
469
470 draw->screen = get_screen_for_root(draw->conn, reply->root);
471 draw->width = reply->width;
472 draw->height = reply->height;
473 draw->depth = reply->depth;
474 draw->vtable->set_drawable_size(draw, draw->width, draw->height);
475 free(reply);
476
477 draw->swap_method = __DRI_ATTRIB_SWAP_UNDEFINED;
478 if (draw->ext->core->base.version >= 2) {
479 (void )draw->ext->core->getConfigAttrib(dri_config,
480 __DRI_ATTRIB_SWAP_METHOD,
481 &draw->swap_method);
482 }
483
484 /*
485 * Make sure server has the same swap interval we do for the new
486 * drawable.
487 */
488 loader_dri3_set_swap_interval(draw, swap_interval);
489
490 return 0;
491 }
492
493 /*
494 * Process one Present event
495 */
496 static void
dri3_handle_present_event(struct loader_dri3_drawable * draw,xcb_present_generic_event_t * ge)497 dri3_handle_present_event(struct loader_dri3_drawable *draw,
498 xcb_present_generic_event_t *ge)
499 {
500 switch (ge->evtype) {
501 case XCB_PRESENT_CONFIGURE_NOTIFY: {
502 xcb_present_configure_notify_event_t *ce = (void *) ge;
503
504 draw->width = ce->width;
505 draw->height = ce->height;
506 draw->vtable->set_drawable_size(draw, draw->width, draw->height);
507 draw->ext->flush->invalidate(draw->dri_drawable);
508 break;
509 }
510 case XCB_PRESENT_COMPLETE_NOTIFY: {
511 xcb_present_complete_notify_event_t *ce = (void *) ge;
512
513 /* Compute the processed SBC number from the received 32-bit serial number
514 * merged with the upper 32-bits of the sent 64-bit serial number while
515 * checking for wrap.
516 */
517 if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) {
518 uint64_t recv_sbc = (draw->send_sbc & 0xffffffff00000000LL) | ce->serial;
519
520 /* Only assume wraparound if that results in exactly the previous
521 * SBC + 1, otherwise ignore received SBC > sent SBC (those are
522 * probably from a previous loader_dri3_drawable instance) to avoid
523 * calculating bogus target MSC values in loader_dri3_swap_buffers_msc
524 */
525 if (recv_sbc <= draw->send_sbc)
526 draw->recv_sbc = recv_sbc;
527 else if (recv_sbc == (draw->recv_sbc + 0x100000001ULL))
528 draw->recv_sbc = recv_sbc - 0x100000000ULL;
529
530 /* When moving from flip to copy, we assume that we can allocate in
531 * a more optimal way if we don't need to cater for the display
532 * controller.
533 */
534 if (ce->mode == XCB_PRESENT_COMPLETE_MODE_COPY &&
535 draw->last_present_mode == XCB_PRESENT_COMPLETE_MODE_FLIP) {
536 for (int b = 0; b < ARRAY_SIZE(draw->buffers); b++) {
537 if (draw->buffers[b])
538 draw->buffers[b]->reallocate = true;
539 }
540 }
541
542 /* If the server tells us that our allocation is suboptimal, we
543 * reallocate once.
544 */
545 #ifdef HAVE_DRI3_MODIFIERS
546 if (ce->mode == XCB_PRESENT_COMPLETE_MODE_SUBOPTIMAL_COPY &&
547 draw->last_present_mode != ce->mode) {
548 for (int b = 0; b < ARRAY_SIZE(draw->buffers); b++) {
549 if (draw->buffers[b])
550 draw->buffers[b]->reallocate = true;
551 }
552 }
553 #endif
554 draw->last_present_mode = ce->mode;
555
556 if (draw->vtable->show_fps)
557 draw->vtable->show_fps(draw, ce->ust);
558
559 draw->ust = ce->ust;
560 draw->msc = ce->msc;
561 } else if (ce->serial == draw->eid) {
562 draw->notify_ust = ce->ust;
563 draw->notify_msc = ce->msc;
564 }
565 break;
566 }
567 case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
568 xcb_present_idle_notify_event_t *ie = (void *) ge;
569 int b;
570
571 for (b = 0; b < ARRAY_SIZE(draw->buffers); b++) {
572 struct loader_dri3_buffer *buf = draw->buffers[b];
573
574 if (buf && buf->pixmap == ie->pixmap)
575 buf->busy = 0;
576 }
577 break;
578 }
579 }
580 free(ge);
581 }
582
583 static bool
dri3_wait_for_event_locked(struct loader_dri3_drawable * draw,unsigned * full_sequence)584 dri3_wait_for_event_locked(struct loader_dri3_drawable *draw,
585 unsigned *full_sequence)
586 {
587 xcb_generic_event_t *ev;
588 xcb_present_generic_event_t *ge;
589
590 xcb_flush(draw->conn);
591
592 /* Only have one thread waiting for events at a time */
593 if (draw->has_event_waiter) {
594 cnd_wait(&draw->event_cnd, &draw->mtx);
595 if (full_sequence)
596 *full_sequence = draw->last_special_event_sequence;
597 /* Another thread has updated the protected info, so retest. */
598 return true;
599 } else {
600 draw->has_event_waiter = true;
601 /* Allow other threads access to the drawable while we're waiting. */
602 mtx_unlock(&draw->mtx);
603 ev = xcb_wait_for_special_event(draw->conn, draw->special_event);
604 mtx_lock(&draw->mtx);
605 draw->has_event_waiter = false;
606 cnd_broadcast(&draw->event_cnd);
607 }
608 if (!ev)
609 return false;
610 draw->last_special_event_sequence = ev->full_sequence;
611 if (full_sequence)
612 *full_sequence = ev->full_sequence;
613 ge = (void *) ev;
614 dri3_handle_present_event(draw, ge);
615 return true;
616 }
617
618 /** loader_dri3_wait_for_msc
619 *
620 * Get the X server to send an event when the target msc/divisor/remainder is
621 * reached.
622 */
623 bool
loader_dri3_wait_for_msc(struct loader_dri3_drawable * draw,int64_t target_msc,int64_t divisor,int64_t remainder,int64_t * ust,int64_t * msc,int64_t * sbc)624 loader_dri3_wait_for_msc(struct loader_dri3_drawable *draw,
625 int64_t target_msc,
626 int64_t divisor, int64_t remainder,
627 int64_t *ust, int64_t *msc, int64_t *sbc)
628 {
629 xcb_void_cookie_t cookie = xcb_present_notify_msc(draw->conn,
630 draw->drawable,
631 draw->eid,
632 target_msc,
633 divisor,
634 remainder);
635 unsigned full_sequence;
636
637 mtx_lock(&draw->mtx);
638
639 /* Wait for the event */
640 do {
641 if (!dri3_wait_for_event_locked(draw, &full_sequence)) {
642 mtx_unlock(&draw->mtx);
643 return false;
644 }
645 } while (full_sequence != cookie.sequence || draw->notify_msc < target_msc);
646
647 *ust = draw->notify_ust;
648 *msc = draw->notify_msc;
649 *sbc = draw->recv_sbc;
650 mtx_unlock(&draw->mtx);
651
652 return true;
653 }
654
655 /** loader_dri3_wait_for_sbc
656 *
657 * Wait for the completed swap buffer count to reach the specified
658 * target. Presumably the application knows that this will be reached with
659 * outstanding complete events, or we're going to be here awhile.
660 */
661 int
loader_dri3_wait_for_sbc(struct loader_dri3_drawable * draw,int64_t target_sbc,int64_t * ust,int64_t * msc,int64_t * sbc)662 loader_dri3_wait_for_sbc(struct loader_dri3_drawable *draw,
663 int64_t target_sbc, int64_t *ust,
664 int64_t *msc, int64_t *sbc)
665 {
666 /* From the GLX_OML_sync_control spec:
667 *
668 * "If <target_sbc> = 0, the function will block until all previous
669 * swaps requested with glXSwapBuffersMscOML for that window have
670 * completed."
671 */
672 mtx_lock(&draw->mtx);
673 if (!target_sbc)
674 target_sbc = draw->send_sbc;
675
676 while (draw->recv_sbc < target_sbc) {
677 if (!dri3_wait_for_event_locked(draw, NULL)) {
678 mtx_unlock(&draw->mtx);
679 return 0;
680 }
681 }
682
683 *ust = draw->ust;
684 *msc = draw->msc;
685 *sbc = draw->recv_sbc;
686 mtx_unlock(&draw->mtx);
687 return 1;
688 }
689
690 /** loader_dri3_find_back
691 *
692 * Find an idle back buffer. If there isn't one, then
693 * wait for a present idle notify event from the X server
694 */
695 static int
dri3_find_back(struct loader_dri3_drawable * draw,bool prefer_a_different)696 dri3_find_back(struct loader_dri3_drawable *draw, bool prefer_a_different)
697 {
698 int b;
699 int num_to_consider;
700 int max_num;
701
702 mtx_lock(&draw->mtx);
703 /* Increase the likelyhood of reusing current buffer */
704 dri3_flush_present_events(draw);
705
706 /* Check whether we need to reuse the current back buffer as new back.
707 * In that case, wait until it's not busy anymore.
708 */
709 if (!loader_dri3_have_image_blit(draw) && draw->cur_blit_source != -1) {
710 num_to_consider = 1;
711 max_num = 1;
712 draw->cur_blit_source = -1;
713 } else {
714 num_to_consider = draw->cur_num_back;
715 max_num = draw->max_num_back;
716 }
717
718 /* In a DRI_PRIME situation, if prefer_a_different is true, we first try
719 * to find an idle buffer that is not the last used one.
720 * This is useful if we receive a XCB_PRESENT_EVENT_IDLE_NOTIFY event
721 * for a pixmap but it's not actually idle (eg: the DRI_PRIME blit is
722 * still in progress).
723 * Unigine Superposition hits this and this allows to use 2 back buffers
724 * instead of reusing the same one all the time, causing the next frame
725 * to wait for the copy to finish.
726 */
727 int current_back_id = draw->cur_back;
728 for (;;) {
729 for (b = 0; b < num_to_consider; b++) {
730 int id = LOADER_DRI3_BACK_ID((b + draw->cur_back) % draw->cur_num_back);
731 struct loader_dri3_buffer *buffer = draw->buffers[id];
732
733 if (!buffer || (!buffer->busy &&
734 (!prefer_a_different || id != current_back_id))) {
735 draw->cur_back = id;
736 mtx_unlock(&draw->mtx);
737 return id;
738 }
739 }
740
741 if (num_to_consider < max_num) {
742 num_to_consider = ++draw->cur_num_back;
743 } else if (prefer_a_different) {
744 prefer_a_different = false;
745 } else if (!dri3_wait_for_event_locked(draw, NULL)) {
746 mtx_unlock(&draw->mtx);
747 return -1;
748 }
749 }
750 }
751
752 static xcb_gcontext_t
dri3_drawable_gc(struct loader_dri3_drawable * draw)753 dri3_drawable_gc(struct loader_dri3_drawable *draw)
754 {
755 if (!draw->gc) {
756 uint32_t v = 0;
757 xcb_create_gc(draw->conn,
758 (draw->gc = xcb_generate_id(draw->conn)),
759 draw->drawable,
760 XCB_GC_GRAPHICS_EXPOSURES,
761 &v);
762 }
763 return draw->gc;
764 }
765
766
767 static struct loader_dri3_buffer *
dri3_back_buffer(struct loader_dri3_drawable * draw)768 dri3_back_buffer(struct loader_dri3_drawable *draw)
769 {
770 return draw->buffers[LOADER_DRI3_BACK_ID(draw->cur_back)];
771 }
772
773 static struct loader_dri3_buffer *
dri3_fake_front_buffer(struct loader_dri3_drawable * draw)774 dri3_fake_front_buffer(struct loader_dri3_drawable *draw)
775 {
776 return draw->buffers[LOADER_DRI3_FRONT_ID];
777 }
778
779 static void
dri3_copy_area(xcb_connection_t * c,xcb_drawable_t src_drawable,xcb_drawable_t dst_drawable,xcb_gcontext_t gc,int16_t src_x,int16_t src_y,int16_t dst_x,int16_t dst_y,uint16_t width,uint16_t height)780 dri3_copy_area(xcb_connection_t *c,
781 xcb_drawable_t src_drawable,
782 xcb_drawable_t dst_drawable,
783 xcb_gcontext_t gc,
784 int16_t src_x,
785 int16_t src_y,
786 int16_t dst_x,
787 int16_t dst_y,
788 uint16_t width,
789 uint16_t height)
790 {
791 xcb_void_cookie_t cookie;
792
793 cookie = xcb_copy_area_checked(c,
794 src_drawable,
795 dst_drawable,
796 gc,
797 src_x,
798 src_y,
799 dst_x,
800 dst_y,
801 width,
802 height);
803 xcb_discard_reply(c, cookie.sequence);
804 }
805
806 /**
807 * Asks the driver to flush any queued work necessary for serializing with the
808 * X command stream, and optionally the slightly more strict requirement of
809 * glFlush() equivalence (which would require flushing even if nothing had
810 * been drawn to a window system framebuffer, for example).
811 */
812 void
loader_dri3_flush(struct loader_dri3_drawable * draw,unsigned flags,enum __DRI2throttleReason throttle_reason)813 loader_dri3_flush(struct loader_dri3_drawable *draw,
814 unsigned flags,
815 enum __DRI2throttleReason throttle_reason)
816 {
817 /* NEED TO CHECK WHETHER CONTEXT IS NULL */
818 __DRIcontext *dri_context = draw->vtable->get_dri_context(draw);
819
820 if (dri_context) {
821 draw->ext->flush->flush_with_flags(dri_context, draw->dri_drawable,
822 flags, throttle_reason);
823 }
824 }
825
826 void
loader_dri3_copy_sub_buffer(struct loader_dri3_drawable * draw,int x,int y,int width,int height,bool flush)827 loader_dri3_copy_sub_buffer(struct loader_dri3_drawable *draw,
828 int x, int y,
829 int width, int height,
830 bool flush)
831 {
832 struct loader_dri3_buffer *back;
833 unsigned flags = __DRI2_FLUSH_DRAWABLE;
834
835 /* Check we have the right attachments */
836 if (!draw->have_back || draw->is_pixmap)
837 return;
838
839 if (flush)
840 flags |= __DRI2_FLUSH_CONTEXT;
841 loader_dri3_flush(draw, flags, __DRI2_THROTTLE_COPYSUBBUFFER);
842
843 back = dri3_find_back_alloc(draw);
844 if (!back)
845 return;
846
847 y = draw->height - y - height;
848
849 if (draw->is_different_gpu) {
850 /* Update the linear buffer part of the back buffer
851 * for the dri3_copy_area operation
852 */
853 (void) loader_dri3_blit_image(draw,
854 back->linear_buffer,
855 back->image,
856 0, 0, back->width, back->height,
857 0, 0, __BLIT_FLAG_FLUSH);
858 }
859
860 loader_dri3_swapbuffer_barrier(draw);
861 dri3_fence_reset(draw->conn, back);
862 dri3_copy_area(draw->conn,
863 back->pixmap,
864 draw->drawable,
865 dri3_drawable_gc(draw),
866 x, y, x, y, width, height);
867 dri3_fence_trigger(draw->conn, back);
868 /* Refresh the fake front (if present) after we just damaged the real
869 * front.
870 */
871 if (draw->have_fake_front &&
872 !loader_dri3_blit_image(draw,
873 dri3_fake_front_buffer(draw)->image,
874 back->image,
875 x, y, width, height,
876 x, y, __BLIT_FLAG_FLUSH) &&
877 !draw->is_different_gpu) {
878 dri3_fence_reset(draw->conn, dri3_fake_front_buffer(draw));
879 dri3_copy_area(draw->conn,
880 back->pixmap,
881 dri3_fake_front_buffer(draw)->pixmap,
882 dri3_drawable_gc(draw),
883 x, y, x, y, width, height);
884 dri3_fence_trigger(draw->conn, dri3_fake_front_buffer(draw));
885 dri3_fence_await(draw->conn, NULL, dri3_fake_front_buffer(draw));
886 }
887 dri3_fence_await(draw->conn, draw, back);
888 }
889
890 void
loader_dri3_copy_drawable(struct loader_dri3_drawable * draw,xcb_drawable_t dest,xcb_drawable_t src)891 loader_dri3_copy_drawable(struct loader_dri3_drawable *draw,
892 xcb_drawable_t dest,
893 xcb_drawable_t src)
894 {
895 loader_dri3_flush(draw, __DRI2_FLUSH_DRAWABLE, __DRI2_THROTTLE_COPYSUBBUFFER);
896
897 dri3_fence_reset(draw->conn, dri3_fake_front_buffer(draw));
898 dri3_copy_area(draw->conn,
899 src, dest,
900 dri3_drawable_gc(draw),
901 0, 0, 0, 0, draw->width, draw->height);
902 dri3_fence_trigger(draw->conn, dri3_fake_front_buffer(draw));
903 dri3_fence_await(draw->conn, draw, dri3_fake_front_buffer(draw));
904 }
905
906 void
loader_dri3_wait_x(struct loader_dri3_drawable * draw)907 loader_dri3_wait_x(struct loader_dri3_drawable *draw)
908 {
909 struct loader_dri3_buffer *front;
910
911 if (draw == NULL || !draw->have_fake_front)
912 return;
913
914 front = dri3_fake_front_buffer(draw);
915
916 loader_dri3_copy_drawable(draw, front->pixmap, draw->drawable);
917
918 /* In the psc->is_different_gpu case, the linear buffer has been updated,
919 * but not yet the tiled buffer.
920 * Copy back to the tiled buffer we use for rendering.
921 * Note that we don't need flushing.
922 */
923 if (draw->is_different_gpu)
924 (void) loader_dri3_blit_image(draw,
925 front->image,
926 front->linear_buffer,
927 0, 0, front->width, front->height,
928 0, 0, 0);
929 }
930
931 void
loader_dri3_wait_gl(struct loader_dri3_drawable * draw)932 loader_dri3_wait_gl(struct loader_dri3_drawable *draw)
933 {
934 struct loader_dri3_buffer *front;
935
936 if (draw == NULL || !draw->have_fake_front)
937 return;
938
939 front = dri3_fake_front_buffer(draw);
940
941 /* In the psc->is_different_gpu case, we update the linear_buffer
942 * before updating the real front.
943 */
944 if (draw->is_different_gpu)
945 (void) loader_dri3_blit_image(draw,
946 front->linear_buffer,
947 front->image,
948 0, 0, front->width, front->height,
949 0, 0, __BLIT_FLAG_FLUSH);
950 loader_dri3_swapbuffer_barrier(draw);
951 loader_dri3_copy_drawable(draw, draw->drawable, front->pixmap);
952 }
953
954 /** dri3_flush_present_events
955 *
956 * Process any present events that have been received from the X server
957 */
958 static void
dri3_flush_present_events(struct loader_dri3_drawable * draw)959 dri3_flush_present_events(struct loader_dri3_drawable *draw)
960 {
961 /* Check to see if any configuration changes have occurred
962 * since we were last invoked
963 */
964 if (draw->has_event_waiter)
965 return;
966
967 if (draw->special_event) {
968 xcb_generic_event_t *ev;
969
970 while ((ev = xcb_poll_for_special_event(draw->conn,
971 draw->special_event)) != NULL) {
972 xcb_present_generic_event_t *ge = (void *) ev;
973 dri3_handle_present_event(draw, ge);
974 }
975 }
976 }
977
978 /** loader_dri3_swap_buffers_msc
979 *
980 * Make the current back buffer visible using the present extension
981 */
982 int64_t
loader_dri3_swap_buffers_msc(struct loader_dri3_drawable * draw,int64_t target_msc,int64_t divisor,int64_t remainder,unsigned flush_flags,const int * rects,int n_rects,bool force_copy)983 loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw,
984 int64_t target_msc, int64_t divisor,
985 int64_t remainder, unsigned flush_flags,
986 const int *rects, int n_rects,
987 bool force_copy)
988 {
989 struct loader_dri3_buffer *back;
990 int64_t ret = 0;
991 uint32_t options = XCB_PRESENT_OPTION_NONE;
992
993 draw->vtable->flush_drawable(draw, flush_flags);
994
995 back = dri3_find_back_alloc(draw);
996
997 mtx_lock(&draw->mtx);
998
999 if (draw->adaptive_sync && !draw->adaptive_sync_active) {
1000 set_adaptive_sync_property(draw->conn, draw->drawable, true);
1001 draw->adaptive_sync_active = true;
1002 }
1003
1004 if (draw->is_different_gpu && back) {
1005 /* Update the linear buffer before presenting the pixmap */
1006 (void) loader_dri3_blit_image(draw,
1007 back->linear_buffer,
1008 back->image,
1009 0, 0, back->width, back->height,
1010 0, 0, __BLIT_FLAG_FLUSH);
1011 }
1012
1013 /* If we need to preload the new back buffer, remember the source.
1014 * The force_copy parameter is used by EGL to attempt to preserve
1015 * the back buffer across a call to this function.
1016 */
1017 if (draw->swap_method != __DRI_ATTRIB_SWAP_UNDEFINED || force_copy)
1018 draw->cur_blit_source = LOADER_DRI3_BACK_ID(draw->cur_back);
1019
1020 /* Exchange the back and fake front. Even though the server knows about these
1021 * buffers, it has no notion of back and fake front.
1022 */
1023 if (back && draw->have_fake_front) {
1024 struct loader_dri3_buffer *tmp;
1025
1026 tmp = dri3_fake_front_buffer(draw);
1027 draw->buffers[LOADER_DRI3_FRONT_ID] = back;
1028 draw->buffers[LOADER_DRI3_BACK_ID(draw->cur_back)] = tmp;
1029
1030 if (draw->swap_method == __DRI_ATTRIB_SWAP_COPY || force_copy)
1031 draw->cur_blit_source = LOADER_DRI3_FRONT_ID;
1032 }
1033
1034 dri3_flush_present_events(draw);
1035
1036 if (back && !draw->is_pixmap) {
1037 dri3_fence_reset(draw->conn, back);
1038
1039 /* Compute when we want the frame shown by taking the last known
1040 * successful MSC and adding in a swap interval for each outstanding swap
1041 * request. target_msc=divisor=remainder=0 means "Use glXSwapBuffers()
1042 * semantic"
1043 */
1044 ++draw->send_sbc;
1045 if (target_msc == 0 && divisor == 0 && remainder == 0)
1046 target_msc = draw->msc + abs(draw->swap_interval) *
1047 (draw->send_sbc - draw->recv_sbc);
1048 else if (divisor == 0 && remainder > 0) {
1049 /* From the GLX_OML_sync_control spec:
1050 * "If <divisor> = 0, the swap will occur when MSC becomes
1051 * greater than or equal to <target_msc>."
1052 *
1053 * Note that there's no mention of the remainder. The Present
1054 * extension throws BadValue for remainder != 0 with divisor == 0, so
1055 * just drop the passed in value.
1056 */
1057 remainder = 0;
1058 }
1059
1060 /* From the GLX_EXT_swap_control spec
1061 * and the EGL 1.4 spec (page 53):
1062 *
1063 * "If <interval> is set to a value of 0, buffer swaps are not
1064 * synchronized to a video frame."
1065 *
1066 * From GLX_EXT_swap_control_tear:
1067 *
1068 * "If <interval> is negative, the minimum number of video frames
1069 * between buffer swaps is the absolute value of <interval>. In this
1070 * case, if abs(<interval>) video frames have already passed from
1071 * the previous swap when the swap is ready to be performed, the
1072 * swap will occur without synchronization to a video frame."
1073 *
1074 * Implementation note: It is possible to enable triple buffering
1075 * behaviour by not using XCB_PRESENT_OPTION_ASYNC, but this should not be
1076 * the default.
1077 */
1078 if (draw->swap_interval <= 0)
1079 options |= XCB_PRESENT_OPTION_ASYNC;
1080
1081 /* If we need to populate the new back, but need to reuse the back
1082 * buffer slot due to lack of local blit capabilities, make sure
1083 * the server doesn't flip and we deadlock.
1084 */
1085 if (!loader_dri3_have_image_blit(draw) && draw->cur_blit_source != -1)
1086 options |= XCB_PRESENT_OPTION_COPY;
1087 #ifdef HAVE_DRI3_MODIFIERS
1088 if (draw->multiplanes_available)
1089 options |= XCB_PRESENT_OPTION_SUBOPTIMAL;
1090 #endif
1091 back->busy = 1;
1092 back->last_swap = draw->send_sbc;
1093
1094 if (!draw->region) {
1095 draw->region = xcb_generate_id(draw->conn);
1096 xcb_xfixes_create_region(draw->conn, draw->region, 0, NULL);
1097 }
1098
1099 xcb_xfixes_region_t region = 0;
1100 xcb_rectangle_t xcb_rects[64];
1101
1102 if (n_rects > 0 && n_rects <= ARRAY_SIZE(xcb_rects)) {
1103 for (int i = 0; i < n_rects; i++) {
1104 const int *rect = &rects[i * 4];
1105 xcb_rects[i].x = rect[0];
1106 xcb_rects[i].y = draw->height - rect[1] - rect[3];
1107 xcb_rects[i].width = rect[2];
1108 xcb_rects[i].height = rect[3];
1109 }
1110
1111 region = draw->region;
1112 xcb_xfixes_set_region(draw->conn, region, n_rects, xcb_rects);
1113 }
1114
1115 xcb_present_pixmap(draw->conn,
1116 draw->drawable,
1117 back->pixmap,
1118 (uint32_t) draw->send_sbc,
1119 0, /* valid */
1120 region, /* update */
1121 0, /* x_off */
1122 0, /* y_off */
1123 None, /* target_crtc */
1124 None,
1125 back->sync_fence,
1126 options,
1127 target_msc,
1128 divisor,
1129 remainder, 0, NULL);
1130 ret = (int64_t) draw->send_sbc;
1131
1132 /* Schedule a server-side back-preserving blit if necessary.
1133 * This happens iff all conditions below are satisfied:
1134 * a) We have a fake front,
1135 * b) We need to preserve the back buffer,
1136 * c) We don't have local blit capabilities.
1137 */
1138 if (!loader_dri3_have_image_blit(draw) && draw->cur_blit_source != -1 &&
1139 draw->cur_blit_source != LOADER_DRI3_BACK_ID(draw->cur_back)) {
1140 struct loader_dri3_buffer *new_back = dri3_back_buffer(draw);
1141 struct loader_dri3_buffer *src = draw->buffers[draw->cur_blit_source];
1142
1143 dri3_fence_reset(draw->conn, new_back);
1144 dri3_copy_area(draw->conn, src->pixmap,
1145 new_back->pixmap,
1146 dri3_drawable_gc(draw),
1147 0, 0, 0, 0, draw->width, draw->height);
1148 dri3_fence_trigger(draw->conn, new_back);
1149 new_back->last_swap = src->last_swap;
1150 }
1151
1152 xcb_flush(draw->conn);
1153 if (draw->stamp)
1154 ++(*draw->stamp);
1155 }
1156 mtx_unlock(&draw->mtx);
1157
1158 draw->ext->flush->invalidate(draw->dri_drawable);
1159
1160 return ret;
1161 }
1162
1163 int
loader_dri3_query_buffer_age(struct loader_dri3_drawable * draw)1164 loader_dri3_query_buffer_age(struct loader_dri3_drawable *draw)
1165 {
1166 struct loader_dri3_buffer *back = dri3_find_back_alloc(draw);
1167 int ret;
1168
1169 mtx_lock(&draw->mtx);
1170 ret = (!back || back->last_swap == 0) ? 0 :
1171 draw->send_sbc - back->last_swap + 1;
1172 mtx_unlock(&draw->mtx);
1173
1174 return ret;
1175 }
1176
1177 /** loader_dri3_open
1178 *
1179 * Wrapper around xcb_dri3_open
1180 */
1181 int
loader_dri3_open(xcb_connection_t * conn,xcb_window_t root,uint32_t provider)1182 loader_dri3_open(xcb_connection_t *conn,
1183 xcb_window_t root,
1184 uint32_t provider)
1185 {
1186 xcb_dri3_open_cookie_t cookie;
1187 xcb_dri3_open_reply_t *reply;
1188 xcb_xfixes_query_version_cookie_t fixes_cookie;
1189 xcb_xfixes_query_version_reply_t *fixes_reply;
1190 int fd;
1191
1192 cookie = xcb_dri3_open(conn,
1193 root,
1194 provider);
1195
1196 reply = xcb_dri3_open_reply(conn, cookie, NULL);
1197 if (!reply)
1198 return -1;
1199
1200 if (reply->nfd != 1) {
1201 free(reply);
1202 return -1;
1203 }
1204
1205 fd = xcb_dri3_open_reply_fds(conn, reply)[0];
1206 free(reply);
1207 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
1208
1209 /* let the server know our xfixes level */
1210 fixes_cookie = xcb_xfixes_query_version(conn,
1211 XCB_XFIXES_MAJOR_VERSION,
1212 XCB_XFIXES_MINOR_VERSION);
1213 fixes_reply = xcb_xfixes_query_version_reply(conn, fixes_cookie, NULL);
1214 free(fixes_reply);
1215
1216 return fd;
1217 }
1218
1219 static uint32_t
dri3_cpp_for_format(uint32_t format)1220 dri3_cpp_for_format(uint32_t format) {
1221 switch (format) {
1222 case __DRI_IMAGE_FORMAT_R8:
1223 return 1;
1224 case __DRI_IMAGE_FORMAT_RGB565:
1225 case __DRI_IMAGE_FORMAT_GR88:
1226 return 2;
1227 case __DRI_IMAGE_FORMAT_XRGB8888:
1228 case __DRI_IMAGE_FORMAT_ARGB8888:
1229 case __DRI_IMAGE_FORMAT_ABGR8888:
1230 case __DRI_IMAGE_FORMAT_XBGR8888:
1231 case __DRI_IMAGE_FORMAT_XRGB2101010:
1232 case __DRI_IMAGE_FORMAT_ARGB2101010:
1233 case __DRI_IMAGE_FORMAT_XBGR2101010:
1234 case __DRI_IMAGE_FORMAT_ABGR2101010:
1235 case __DRI_IMAGE_FORMAT_SARGB8:
1236 case __DRI_IMAGE_FORMAT_SABGR8:
1237 case __DRI_IMAGE_FORMAT_SXRGB8:
1238 return 4;
1239 case __DRI_IMAGE_FORMAT_XBGR16161616F:
1240 case __DRI_IMAGE_FORMAT_ABGR16161616F:
1241 return 8;
1242 case __DRI_IMAGE_FORMAT_NONE:
1243 default:
1244 return 0;
1245 }
1246 }
1247
1248 /* Map format of render buffer to corresponding format for the linear_buffer
1249 * used for sharing with the display gpu of a Prime setup (== is_different_gpu).
1250 * Usually linear_format == format, except for depth >= 30 formats, where
1251 * different gpu vendors have different preferences wrt. color channel ordering.
1252 */
1253 static uint32_t
dri3_linear_format_for_format(struct loader_dri3_drawable * draw,uint32_t format)1254 dri3_linear_format_for_format(struct loader_dri3_drawable *draw, uint32_t format)
1255 {
1256 switch (format) {
1257 case __DRI_IMAGE_FORMAT_XRGB2101010:
1258 case __DRI_IMAGE_FORMAT_XBGR2101010:
1259 /* Different preferred formats for different hw */
1260 if (dri3_get_red_mask_for_depth(draw, 30) == 0x3ff)
1261 return __DRI_IMAGE_FORMAT_XBGR2101010;
1262 else
1263 return __DRI_IMAGE_FORMAT_XRGB2101010;
1264
1265 case __DRI_IMAGE_FORMAT_ARGB2101010:
1266 case __DRI_IMAGE_FORMAT_ABGR2101010:
1267 /* Different preferred formats for different hw */
1268 if (dri3_get_red_mask_for_depth(draw, 30) == 0x3ff)
1269 return __DRI_IMAGE_FORMAT_ABGR2101010;
1270 else
1271 return __DRI_IMAGE_FORMAT_ARGB2101010;
1272
1273 default:
1274 return format;
1275 }
1276 }
1277
1278 /* the DRIimage createImage function takes __DRI_IMAGE_FORMAT codes, while
1279 * the createImageFromFds call takes DRM_FORMAT codes. To avoid
1280 * complete confusion, just deal in __DRI_IMAGE_FORMAT codes for now and
1281 * translate to DRM_FORMAT codes in the call to createImageFromFds
1282 */
1283 static int
image_format_to_fourcc(int format)1284 image_format_to_fourcc(int format)
1285 {
1286
1287 /* Convert from __DRI_IMAGE_FORMAT to DRM_FORMAT (sigh) */
1288 switch (format) {
1289 case __DRI_IMAGE_FORMAT_SARGB8: return __DRI_IMAGE_FOURCC_SARGB8888;
1290 case __DRI_IMAGE_FORMAT_SABGR8: return __DRI_IMAGE_FOURCC_SABGR8888;
1291 case __DRI_IMAGE_FORMAT_SXRGB8: return __DRI_IMAGE_FOURCC_SXRGB8888;
1292 case __DRI_IMAGE_FORMAT_RGB565: return DRM_FORMAT_RGB565;
1293 case __DRI_IMAGE_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888;
1294 case __DRI_IMAGE_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888;
1295 case __DRI_IMAGE_FORMAT_ABGR8888: return DRM_FORMAT_ABGR8888;
1296 case __DRI_IMAGE_FORMAT_XBGR8888: return DRM_FORMAT_XBGR8888;
1297 case __DRI_IMAGE_FORMAT_XRGB2101010: return DRM_FORMAT_XRGB2101010;
1298 case __DRI_IMAGE_FORMAT_ARGB2101010: return DRM_FORMAT_ARGB2101010;
1299 case __DRI_IMAGE_FORMAT_XBGR2101010: return DRM_FORMAT_XBGR2101010;
1300 case __DRI_IMAGE_FORMAT_ABGR2101010: return DRM_FORMAT_ABGR2101010;
1301 case __DRI_IMAGE_FORMAT_XBGR16161616F: return DRM_FORMAT_XBGR16161616F;
1302 case __DRI_IMAGE_FORMAT_ABGR16161616F: return DRM_FORMAT_ABGR16161616F;
1303 }
1304 return 0;
1305 }
1306
1307 #ifdef HAVE_DRI3_MODIFIERS
1308 static bool
has_supported_modifier(struct loader_dri3_drawable * draw,unsigned int format,uint64_t * modifiers,uint32_t count)1309 has_supported_modifier(struct loader_dri3_drawable *draw, unsigned int format,
1310 uint64_t *modifiers, uint32_t count)
1311 {
1312 uint64_t *supported_modifiers;
1313 int32_t supported_modifiers_count;
1314 bool found = false;
1315 int i, j;
1316
1317 if (!draw->ext->image->queryDmaBufModifiers(draw->dri_screen,
1318 format, 0, NULL, NULL,
1319 &supported_modifiers_count) ||
1320 supported_modifiers_count == 0)
1321 return false;
1322
1323 supported_modifiers = malloc(supported_modifiers_count * sizeof(uint64_t));
1324 if (!supported_modifiers)
1325 return false;
1326
1327 draw->ext->image->queryDmaBufModifiers(draw->dri_screen, format,
1328 supported_modifiers_count,
1329 supported_modifiers, NULL,
1330 &supported_modifiers_count);
1331
1332 for (i = 0; !found && i < supported_modifiers_count; i++) {
1333 for (j = 0; !found && j < count; j++) {
1334 if (supported_modifiers[i] == modifiers[j])
1335 found = true;
1336 }
1337 }
1338
1339 free(supported_modifiers);
1340 return found;
1341 }
1342 #endif
1343
1344 /** loader_dri3_alloc_render_buffer
1345 *
1346 * Use the driver createImage function to construct a __DRIimage, then
1347 * get a file descriptor for that and create an X pixmap from that
1348 *
1349 * Allocate an xshmfence for synchronization
1350 */
1351 static struct loader_dri3_buffer *
dri3_alloc_render_buffer(struct loader_dri3_drawable * draw,unsigned int format,int width,int height,int depth)1352 dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
1353 int width, int height, int depth)
1354 {
1355 struct loader_dri3_buffer *buffer;
1356 __DRIimage *pixmap_buffer = NULL, *linear_buffer_display_gpu = NULL;
1357 xcb_pixmap_t pixmap;
1358 xcb_sync_fence_t sync_fence;
1359 struct xshmfence *shm_fence;
1360 int buffer_fds[4], fence_fd;
1361 int num_planes = 0;
1362 uint64_t *modifiers = NULL;
1363 uint32_t count = 0;
1364 int i, mod;
1365 int ret;
1366
1367 /* Create an xshmfence object and
1368 * prepare to send that to the X server
1369 */
1370
1371 fence_fd = xshmfence_alloc_shm();
1372 if (fence_fd < 0)
1373 return NULL;
1374
1375 shm_fence = xshmfence_map_shm(fence_fd);
1376 if (shm_fence == NULL)
1377 goto no_shm_fence;
1378
1379 /* Allocate the image from the driver
1380 */
1381 buffer = calloc(1, sizeof *buffer);
1382 if (!buffer)
1383 goto no_buffer;
1384
1385 buffer->cpp = dri3_cpp_for_format(format);
1386 if (!buffer->cpp)
1387 goto no_image;
1388
1389 if (!draw->is_different_gpu) {
1390 #ifdef HAVE_DRI3_MODIFIERS
1391 if (draw->multiplanes_available &&
1392 draw->ext->image->base.version >= 15 &&
1393 draw->ext->image->queryDmaBufModifiers &&
1394 draw->ext->image->createImageWithModifiers) {
1395 xcb_dri3_get_supported_modifiers_cookie_t mod_cookie;
1396 xcb_dri3_get_supported_modifiers_reply_t *mod_reply;
1397 xcb_generic_error_t *error = NULL;
1398
1399 mod_cookie = xcb_dri3_get_supported_modifiers(draw->conn,
1400 draw->window,
1401 depth, buffer->cpp * 8);
1402 mod_reply = xcb_dri3_get_supported_modifiers_reply(draw->conn,
1403 mod_cookie,
1404 &error);
1405 if (!mod_reply)
1406 goto no_image;
1407
1408 if (mod_reply->num_window_modifiers) {
1409 count = mod_reply->num_window_modifiers;
1410 modifiers = malloc(count * sizeof(uint64_t));
1411 if (!modifiers) {
1412 free(mod_reply);
1413 goto no_image;
1414 }
1415
1416 memcpy(modifiers,
1417 xcb_dri3_get_supported_modifiers_window_modifiers(mod_reply),
1418 count * sizeof(uint64_t));
1419
1420 if (!has_supported_modifier(draw, image_format_to_fourcc(format),
1421 modifiers, count)) {
1422 free(modifiers);
1423 count = 0;
1424 modifiers = NULL;
1425 }
1426 }
1427
1428 if (mod_reply->num_screen_modifiers && modifiers == NULL) {
1429 count = mod_reply->num_screen_modifiers;
1430 modifiers = malloc(count * sizeof(uint64_t));
1431 if (!modifiers) {
1432 free(modifiers);
1433 free(mod_reply);
1434 goto no_image;
1435 }
1436
1437 memcpy(modifiers,
1438 xcb_dri3_get_supported_modifiers_screen_modifiers(mod_reply),
1439 count * sizeof(uint64_t));
1440 }
1441
1442 free(mod_reply);
1443 }
1444 #endif
1445 buffer->image = loader_dri_create_image(draw->dri_screen, draw->ext->image,
1446 width, height, format,
1447 __DRI_IMAGE_USE_SHARE |
1448 __DRI_IMAGE_USE_SCANOUT |
1449 __DRI_IMAGE_USE_BACKBUFFER |
1450 (draw->is_protected_content ?
1451 __DRI_IMAGE_USE_PROTECTED : 0),
1452 modifiers, count, buffer);
1453 free(modifiers);
1454
1455 pixmap_buffer = buffer->image;
1456
1457 if (!buffer->image)
1458 goto no_image;
1459 } else {
1460 buffer->image = draw->ext->image->createImage(draw->dri_screen,
1461 width, height,
1462 format,
1463 0,
1464 buffer);
1465
1466 if (!buffer->image)
1467 goto no_image;
1468
1469 /* if driver name is same only then dri_screen_display_gpu is set.
1470 * This check is needed because for simplicity render gpu image extension
1471 * is also used for display gpu.
1472 */
1473 if (draw->dri_screen_display_gpu) {
1474 linear_buffer_display_gpu =
1475 draw->ext->image->createImage(draw->dri_screen_display_gpu,
1476 width, height,
1477 dri3_linear_format_for_format(draw, format),
1478 __DRI_IMAGE_USE_SHARE |
1479 __DRI_IMAGE_USE_LINEAR |
1480 __DRI_IMAGE_USE_BACKBUFFER |
1481 __DRI_IMAGE_USE_SCANOUT,
1482 buffer);
1483 pixmap_buffer = linear_buffer_display_gpu;
1484 }
1485
1486 if (!pixmap_buffer) {
1487 buffer->linear_buffer =
1488 draw->ext->image->createImage(draw->dri_screen,
1489 width, height,
1490 dri3_linear_format_for_format(draw, format),
1491 __DRI_IMAGE_USE_SHARE |
1492 __DRI_IMAGE_USE_LINEAR |
1493 __DRI_IMAGE_USE_BACKBUFFER |
1494 __DRI_IMAGE_USE_SCANOUT,
1495 buffer);
1496
1497 pixmap_buffer = buffer->linear_buffer;
1498 if (!buffer->linear_buffer) {
1499 goto no_linear_buffer;
1500 }
1501 }
1502 }
1503
1504 /* X want some information about the planes, so ask the image for it
1505 */
1506 if (!draw->ext->image->queryImage(pixmap_buffer, __DRI_IMAGE_ATTRIB_NUM_PLANES,
1507 &num_planes))
1508 num_planes = 1;
1509
1510 for (i = 0; i < num_planes; i++) {
1511 __DRIimage *image = draw->ext->image->fromPlanar(pixmap_buffer, i, NULL);
1512
1513 if (!image) {
1514 assert(i == 0);
1515 image = pixmap_buffer;
1516 }
1517
1518 buffer_fds[i] = -1;
1519
1520 ret = draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_FD,
1521 &buffer_fds[i]);
1522 ret &= draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE,
1523 &buffer->strides[i]);
1524 ret &= draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_OFFSET,
1525 &buffer->offsets[i]);
1526 if (image != pixmap_buffer)
1527 draw->ext->image->destroyImage(image);
1528
1529 if (!ret)
1530 goto no_buffer_attrib;
1531 }
1532
1533 ret = draw->ext->image->queryImage(pixmap_buffer,
1534 __DRI_IMAGE_ATTRIB_MODIFIER_UPPER, &mod);
1535 buffer->modifier = (uint64_t) mod << 32;
1536 ret &= draw->ext->image->queryImage(pixmap_buffer,
1537 __DRI_IMAGE_ATTRIB_MODIFIER_LOWER, &mod);
1538 buffer->modifier |= (uint64_t)(mod & 0xffffffff);
1539
1540 if (!ret)
1541 buffer->modifier = DRM_FORMAT_MOD_INVALID;
1542
1543 if (draw->is_different_gpu && draw->dri_screen_display_gpu &&
1544 linear_buffer_display_gpu) {
1545 /* The linear buffer was created in the display GPU's vram, so we
1546 * need to make it visible to render GPU
1547 */
1548 buffer->linear_buffer =
1549 draw->ext->image->createImageFromFds(draw->dri_screen,
1550 width,
1551 height,
1552 image_format_to_fourcc(format),
1553 &buffer_fds[0], num_planes,
1554 &buffer->strides[0],
1555 &buffer->offsets[0],
1556 buffer);
1557 if (!buffer->linear_buffer)
1558 goto no_buffer_attrib;
1559
1560 draw->ext->image->destroyImage(linear_buffer_display_gpu);
1561 }
1562
1563 pixmap = xcb_generate_id(draw->conn);
1564 #ifdef HAVE_DRI3_MODIFIERS
1565 if (draw->multiplanes_available &&
1566 buffer->modifier != DRM_FORMAT_MOD_INVALID) {
1567 xcb_dri3_pixmap_from_buffers(draw->conn,
1568 pixmap,
1569 draw->window,
1570 num_planes,
1571 width, height,
1572 buffer->strides[0], buffer->offsets[0],
1573 buffer->strides[1], buffer->offsets[1],
1574 buffer->strides[2], buffer->offsets[2],
1575 buffer->strides[3], buffer->offsets[3],
1576 depth, buffer->cpp * 8,
1577 buffer->modifier,
1578 buffer_fds);
1579 } else
1580 #endif
1581 {
1582 xcb_dri3_pixmap_from_buffer(draw->conn,
1583 pixmap,
1584 draw->drawable,
1585 buffer->size,
1586 width, height, buffer->strides[0],
1587 depth, buffer->cpp * 8,
1588 buffer_fds[0]);
1589 }
1590
1591 xcb_dri3_fence_from_fd(draw->conn,
1592 pixmap,
1593 (sync_fence = xcb_generate_id(draw->conn)),
1594 false,
1595 fence_fd);
1596
1597 buffer->pixmap = pixmap;
1598 buffer->own_pixmap = true;
1599 buffer->sync_fence = sync_fence;
1600 buffer->shm_fence = shm_fence;
1601 buffer->width = width;
1602 buffer->height = height;
1603
1604 /* Mark the buffer as idle
1605 */
1606 dri3_fence_set(buffer);
1607
1608 return buffer;
1609
1610 no_buffer_attrib:
1611 do {
1612 if (buffer_fds[i] != -1)
1613 close(buffer_fds[i]);
1614 } while (--i >= 0);
1615 draw->ext->image->destroyImage(pixmap_buffer);
1616 no_linear_buffer:
1617 if (draw->is_different_gpu)
1618 draw->ext->image->destroyImage(buffer->image);
1619 no_image:
1620 free(buffer);
1621 no_buffer:
1622 xshmfence_unmap_shm(shm_fence);
1623 no_shm_fence:
1624 close(fence_fd);
1625 return NULL;
1626 }
1627
1628 /** loader_dri3_update_drawable
1629 *
1630 * Called the first time we use the drawable and then
1631 * after we receive present configure notify events to
1632 * track the geometry of the drawable
1633 */
1634 static int
dri3_update_drawable(struct loader_dri3_drawable * draw)1635 dri3_update_drawable(struct loader_dri3_drawable *draw)
1636 {
1637 mtx_lock(&draw->mtx);
1638 if (draw->first_init) {
1639 xcb_get_geometry_cookie_t geom_cookie;
1640 xcb_get_geometry_reply_t *geom_reply;
1641 xcb_void_cookie_t cookie;
1642 xcb_generic_error_t *error;
1643 xcb_present_query_capabilities_cookie_t present_capabilities_cookie;
1644 xcb_present_query_capabilities_reply_t *present_capabilities_reply;
1645 xcb_window_t root_win;
1646
1647 draw->first_init = false;
1648
1649 /* Try to select for input on the window.
1650 *
1651 * If the drawable is a window, this will get our events
1652 * delivered.
1653 *
1654 * Otherwise, we'll get a BadWindow error back from this request which
1655 * will let us know that the drawable is a pixmap instead.
1656 */
1657
1658 draw->eid = xcb_generate_id(draw->conn);
1659 cookie =
1660 xcb_present_select_input_checked(draw->conn, draw->eid, draw->drawable,
1661 XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY |
1662 XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY |
1663 XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
1664
1665 present_capabilities_cookie =
1666 xcb_present_query_capabilities(draw->conn, draw->drawable);
1667
1668 /* Create an XCB event queue to hold present events outside of the usual
1669 * application event queue
1670 */
1671 draw->special_event = xcb_register_for_special_xge(draw->conn,
1672 &xcb_present_id,
1673 draw->eid,
1674 draw->stamp);
1675 geom_cookie = xcb_get_geometry(draw->conn, draw->drawable);
1676
1677 geom_reply = xcb_get_geometry_reply(draw->conn, geom_cookie, NULL);
1678
1679 if (!geom_reply) {
1680 mtx_unlock(&draw->mtx);
1681 return false;
1682 }
1683 draw->width = geom_reply->width;
1684 draw->height = geom_reply->height;
1685 draw->depth = geom_reply->depth;
1686 draw->vtable->set_drawable_size(draw, draw->width, draw->height);
1687 root_win = geom_reply->root;
1688
1689 free(geom_reply);
1690
1691 draw->is_pixmap = false;
1692
1693 /* Check to see if our select input call failed. If it failed with a
1694 * BadWindow error, then assume the drawable is a pixmap. Destroy the
1695 * special event queue created above and mark the drawable as a pixmap
1696 */
1697
1698 error = xcb_request_check(draw->conn, cookie);
1699
1700 present_capabilities_reply =
1701 xcb_present_query_capabilities_reply(draw->conn,
1702 present_capabilities_cookie,
1703 NULL);
1704
1705 if (present_capabilities_reply) {
1706 draw->present_capabilities = present_capabilities_reply->capabilities;
1707 free(present_capabilities_reply);
1708 } else
1709 draw->present_capabilities = 0;
1710
1711 if (error) {
1712 if (error->error_code != BadWindow) {
1713 free(error);
1714 mtx_unlock(&draw->mtx);
1715 return false;
1716 }
1717 free(error);
1718 draw->is_pixmap = true;
1719 xcb_unregister_for_special_event(draw->conn, draw->special_event);
1720 draw->special_event = NULL;
1721 }
1722
1723 if (draw->is_pixmap)
1724 draw->window = root_win;
1725 else
1726 draw->window = draw->drawable;
1727 }
1728 dri3_flush_present_events(draw);
1729 mtx_unlock(&draw->mtx);
1730 return true;
1731 }
1732
1733 __DRIimage *
loader_dri3_create_image(xcb_connection_t * c,xcb_dri3_buffer_from_pixmap_reply_t * bp_reply,unsigned int format,__DRIscreen * dri_screen,const __DRIimageExtension * image,void * loaderPrivate)1734 loader_dri3_create_image(xcb_connection_t *c,
1735 xcb_dri3_buffer_from_pixmap_reply_t *bp_reply,
1736 unsigned int format,
1737 __DRIscreen *dri_screen,
1738 const __DRIimageExtension *image,
1739 void *loaderPrivate)
1740 {
1741 int *fds;
1742 __DRIimage *image_planar, *ret;
1743 int stride, offset;
1744
1745 /* Get an FD for the pixmap object
1746 */
1747 fds = xcb_dri3_buffer_from_pixmap_reply_fds(c, bp_reply);
1748
1749 stride = bp_reply->stride;
1750 offset = 0;
1751
1752 /* createImageFromFds creates a wrapper __DRIimage structure which
1753 * can deal with multiple planes for things like Yuv images. So, once
1754 * we've gotten the planar wrapper, pull the single plane out of it and
1755 * discard the wrapper.
1756 */
1757 image_planar = image->createImageFromFds(dri_screen,
1758 bp_reply->width,
1759 bp_reply->height,
1760 image_format_to_fourcc(format),
1761 fds, 1,
1762 &stride, &offset, loaderPrivate);
1763 close(fds[0]);
1764 if (!image_planar)
1765 return NULL;
1766
1767 ret = image->fromPlanar(image_planar, 0, loaderPrivate);
1768
1769 if (!ret)
1770 ret = image_planar;
1771 else
1772 image->destroyImage(image_planar);
1773
1774 return ret;
1775 }
1776
1777 #ifdef HAVE_DRI3_MODIFIERS
1778 __DRIimage *
loader_dri3_create_image_from_buffers(xcb_connection_t * c,xcb_dri3_buffers_from_pixmap_reply_t * bp_reply,unsigned int format,__DRIscreen * dri_screen,const __DRIimageExtension * image,void * loaderPrivate)1779 loader_dri3_create_image_from_buffers(xcb_connection_t *c,
1780 xcb_dri3_buffers_from_pixmap_reply_t *bp_reply,
1781 unsigned int format,
1782 __DRIscreen *dri_screen,
1783 const __DRIimageExtension *image,
1784 void *loaderPrivate)
1785 {
1786 __DRIimage *ret;
1787 int *fds;
1788 uint32_t *strides_in, *offsets_in;
1789 int strides[4], offsets[4];
1790 unsigned error;
1791 int i;
1792
1793 if (bp_reply->nfd > 4)
1794 return NULL;
1795
1796 fds = xcb_dri3_buffers_from_pixmap_reply_fds(c, bp_reply);
1797 strides_in = xcb_dri3_buffers_from_pixmap_strides(bp_reply);
1798 offsets_in = xcb_dri3_buffers_from_pixmap_offsets(bp_reply);
1799 for (i = 0; i < bp_reply->nfd; i++) {
1800 strides[i] = strides_in[i];
1801 offsets[i] = offsets_in[i];
1802 }
1803
1804 ret = image->createImageFromDmaBufs2(dri_screen,
1805 bp_reply->width,
1806 bp_reply->height,
1807 image_format_to_fourcc(format),
1808 bp_reply->modifier,
1809 fds, bp_reply->nfd,
1810 strides, offsets,
1811 0, 0, 0, 0, /* UNDEFINED */
1812 &error, loaderPrivate);
1813
1814 for (i = 0; i < bp_reply->nfd; i++)
1815 close(fds[i]);
1816
1817 return ret;
1818 }
1819 #endif
1820
1821 /** dri3_get_pixmap_buffer
1822 *
1823 * Get the DRM object for a pixmap from the X server and
1824 * wrap that with a __DRIimage structure using createImageFromFds
1825 */
1826 static struct loader_dri3_buffer *
dri3_get_pixmap_buffer(__DRIdrawable * driDrawable,unsigned int format,enum loader_dri3_buffer_type buffer_type,struct loader_dri3_drawable * draw)1827 dri3_get_pixmap_buffer(__DRIdrawable *driDrawable, unsigned int format,
1828 enum loader_dri3_buffer_type buffer_type,
1829 struct loader_dri3_drawable *draw)
1830 {
1831 int buf_id = loader_dri3_pixmap_buf_id(buffer_type);
1832 struct loader_dri3_buffer *buffer = draw->buffers[buf_id];
1833 xcb_drawable_t pixmap;
1834 xcb_sync_fence_t sync_fence;
1835 struct xshmfence *shm_fence;
1836 int width;
1837 int height;
1838 int fence_fd;
1839 __DRIscreen *cur_screen;
1840
1841 if (buffer)
1842 return buffer;
1843
1844 pixmap = draw->drawable;
1845
1846 buffer = calloc(1, sizeof *buffer);
1847 if (!buffer)
1848 goto no_buffer;
1849
1850 fence_fd = xshmfence_alloc_shm();
1851 if (fence_fd < 0)
1852 goto no_fence;
1853 shm_fence = xshmfence_map_shm(fence_fd);
1854 if (shm_fence == NULL) {
1855 close (fence_fd);
1856 goto no_fence;
1857 }
1858
1859 /* Get the currently-bound screen or revert to using the drawable's screen if
1860 * no contexts are currently bound. The latter case is at least necessary for
1861 * obs-studio, when using Window Capture (Xcomposite) as a Source.
1862 */
1863 cur_screen = draw->vtable->get_dri_screen();
1864 if (!cur_screen) {
1865 cur_screen = draw->dri_screen;
1866 }
1867
1868 xcb_dri3_fence_from_fd(draw->conn,
1869 pixmap,
1870 (sync_fence = xcb_generate_id(draw->conn)),
1871 false,
1872 fence_fd);
1873 #ifdef HAVE_DRI3_MODIFIERS
1874 if (draw->multiplanes_available &&
1875 draw->ext->image->base.version >= 15 &&
1876 draw->ext->image->createImageFromDmaBufs2) {
1877 xcb_dri3_buffers_from_pixmap_cookie_t bps_cookie;
1878 xcb_dri3_buffers_from_pixmap_reply_t *bps_reply;
1879
1880 bps_cookie = xcb_dri3_buffers_from_pixmap(draw->conn, pixmap);
1881 bps_reply = xcb_dri3_buffers_from_pixmap_reply(draw->conn, bps_cookie,
1882 NULL);
1883 if (!bps_reply)
1884 goto no_image;
1885 buffer->image =
1886 loader_dri3_create_image_from_buffers(draw->conn, bps_reply, format,
1887 cur_screen, draw->ext->image,
1888 buffer);
1889 width = bps_reply->width;
1890 height = bps_reply->height;
1891 free(bps_reply);
1892 } else
1893 #endif
1894 {
1895 xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
1896 xcb_dri3_buffer_from_pixmap_reply_t *bp_reply;
1897
1898 bp_cookie = xcb_dri3_buffer_from_pixmap(draw->conn, pixmap);
1899 bp_reply = xcb_dri3_buffer_from_pixmap_reply(draw->conn, bp_cookie, NULL);
1900 if (!bp_reply)
1901 goto no_image;
1902
1903 buffer->image = loader_dri3_create_image(draw->conn, bp_reply, format,
1904 cur_screen, draw->ext->image,
1905 buffer);
1906 width = bp_reply->width;
1907 height = bp_reply->height;
1908 free(bp_reply);
1909 }
1910
1911 if (!buffer->image)
1912 goto no_image;
1913
1914 buffer->pixmap = pixmap;
1915 buffer->own_pixmap = false;
1916 buffer->width = width;
1917 buffer->height = height;
1918 buffer->shm_fence = shm_fence;
1919 buffer->sync_fence = sync_fence;
1920
1921 draw->buffers[buf_id] = buffer;
1922
1923 return buffer;
1924
1925 no_image:
1926 xcb_sync_destroy_fence(draw->conn, sync_fence);
1927 xshmfence_unmap_shm(shm_fence);
1928 no_fence:
1929 free(buffer);
1930 no_buffer:
1931 return NULL;
1932 }
1933
1934 /** dri3_get_buffer
1935 *
1936 * Find a front or back buffer, allocating new ones as necessary
1937 */
1938 static struct loader_dri3_buffer *
dri3_get_buffer(__DRIdrawable * driDrawable,unsigned int format,enum loader_dri3_buffer_type buffer_type,struct loader_dri3_drawable * draw)1939 dri3_get_buffer(__DRIdrawable *driDrawable,
1940 unsigned int format,
1941 enum loader_dri3_buffer_type buffer_type,
1942 struct loader_dri3_drawable *draw)
1943 {
1944 struct loader_dri3_buffer *buffer;
1945 bool fence_await = buffer_type == loader_dri3_buffer_back;
1946 int buf_id;
1947
1948 if (buffer_type == loader_dri3_buffer_back) {
1949 draw->back_format = format;
1950
1951 buf_id = dri3_find_back(draw, !draw->prefer_back_buffer_reuse);
1952
1953 if (buf_id < 0)
1954 return NULL;
1955 } else {
1956 buf_id = LOADER_DRI3_FRONT_ID;
1957 }
1958
1959 buffer = draw->buffers[buf_id];
1960
1961 /* Allocate a new buffer if there isn't an old one, if that
1962 * old one is the wrong size, or if it's suboptimal
1963 */
1964 if (!buffer || buffer->width != draw->width ||
1965 buffer->height != draw->height ||
1966 buffer->reallocate) {
1967 struct loader_dri3_buffer *new_buffer;
1968
1969 /* Allocate the new buffers
1970 */
1971 new_buffer = dri3_alloc_render_buffer(draw,
1972 format,
1973 draw->width,
1974 draw->height,
1975 draw->depth);
1976 if (!new_buffer)
1977 return NULL;
1978
1979 /* When resizing, copy the contents of the old buffer, waiting for that
1980 * copy to complete using our fences before proceeding
1981 */
1982 if ((buffer_type == loader_dri3_buffer_back ||
1983 (buffer_type == loader_dri3_buffer_front && draw->have_fake_front))
1984 && buffer) {
1985
1986 /* Fill the new buffer with data from an old buffer */
1987 if (!loader_dri3_blit_image(draw,
1988 new_buffer->image,
1989 buffer->image,
1990 0, 0,
1991 MIN2(buffer->width, new_buffer->width),
1992 MIN2(buffer->height, new_buffer->height),
1993 0, 0, 0) &&
1994 !buffer->linear_buffer) {
1995 dri3_fence_reset(draw->conn, new_buffer);
1996 dri3_copy_area(draw->conn,
1997 buffer->pixmap,
1998 new_buffer->pixmap,
1999 dri3_drawable_gc(draw),
2000 0, 0, 0, 0,
2001 draw->width, draw->height);
2002 dri3_fence_trigger(draw->conn, new_buffer);
2003 fence_await = true;
2004 }
2005 dri3_free_render_buffer(draw, buffer);
2006 } else if (buffer_type == loader_dri3_buffer_front) {
2007 /* Fill the new fake front with data from a real front */
2008 loader_dri3_swapbuffer_barrier(draw);
2009 dri3_fence_reset(draw->conn, new_buffer);
2010 dri3_copy_area(draw->conn,
2011 draw->drawable,
2012 new_buffer->pixmap,
2013 dri3_drawable_gc(draw),
2014 0, 0, 0, 0,
2015 draw->width, draw->height);
2016 dri3_fence_trigger(draw->conn, new_buffer);
2017
2018 if (new_buffer->linear_buffer) {
2019 dri3_fence_await(draw->conn, draw, new_buffer);
2020 (void) loader_dri3_blit_image(draw,
2021 new_buffer->image,
2022 new_buffer->linear_buffer,
2023 0, 0, draw->width, draw->height,
2024 0, 0, 0);
2025 } else
2026 fence_await = true;
2027 }
2028 buffer = new_buffer;
2029 draw->buffers[buf_id] = buffer;
2030 }
2031
2032 if (fence_await)
2033 dri3_fence_await(draw->conn, draw, buffer);
2034
2035 /*
2036 * Do we need to preserve the content of a previous buffer?
2037 *
2038 * Note that this blit is needed only to avoid a wait for a buffer that
2039 * is currently in the flip chain or being scanned out from. That's really
2040 * a tradeoff. If we're ok with the wait we can reduce the number of back
2041 * buffers to 1 for SWAP_EXCHANGE, and 1 for SWAP_COPY,
2042 * but in the latter case we must disallow page-flipping.
2043 */
2044 if (buffer_type == loader_dri3_buffer_back &&
2045 draw->cur_blit_source != -1 &&
2046 draw->buffers[draw->cur_blit_source] &&
2047 buffer != draw->buffers[draw->cur_blit_source]) {
2048
2049 struct loader_dri3_buffer *source = draw->buffers[draw->cur_blit_source];
2050
2051 /* Avoid flushing here. Will propably do good for tiling hardware. */
2052 (void) loader_dri3_blit_image(draw,
2053 buffer->image,
2054 source->image,
2055 0, 0, draw->width, draw->height,
2056 0, 0, 0);
2057 buffer->last_swap = source->last_swap;
2058 draw->cur_blit_source = -1;
2059 }
2060 /* Return the requested buffer */
2061 return buffer;
2062 }
2063
2064 /** dri3_free_buffers
2065 *
2066 * Free the front bufffer or all of the back buffers. Used
2067 * when the application changes which buffers it needs
2068 */
2069 static void
dri3_free_buffers(__DRIdrawable * driDrawable,enum loader_dri3_buffer_type buffer_type,struct loader_dri3_drawable * draw)2070 dri3_free_buffers(__DRIdrawable *driDrawable,
2071 enum loader_dri3_buffer_type buffer_type,
2072 struct loader_dri3_drawable *draw)
2073 {
2074 struct loader_dri3_buffer *buffer;
2075 int first_id;
2076 int n_id;
2077 int buf_id;
2078
2079 switch (buffer_type) {
2080 case loader_dri3_buffer_back:
2081 first_id = LOADER_DRI3_BACK_ID(0);
2082 n_id = LOADER_DRI3_MAX_BACK;
2083 draw->cur_blit_source = -1;
2084 break;
2085 case loader_dri3_buffer_front:
2086 first_id = LOADER_DRI3_FRONT_ID;
2087 /* Don't free a fake front holding new backbuffer content. */
2088 n_id = (draw->cur_blit_source == LOADER_DRI3_FRONT_ID) ? 0 : 1;
2089 break;
2090 default:
2091 unreachable("unhandled buffer_type");
2092 }
2093
2094 for (buf_id = first_id; buf_id < first_id + n_id; buf_id++) {
2095 buffer = draw->buffers[buf_id];
2096 if (buffer) {
2097 dri3_free_render_buffer(draw, buffer);
2098 draw->buffers[buf_id] = NULL;
2099 }
2100 }
2101 }
2102
2103 /** loader_dri3_get_buffers
2104 *
2105 * The published buffer allocation API.
2106 * Returns all of the necessary buffers, allocating
2107 * as needed.
2108 */
2109 int
loader_dri3_get_buffers(__DRIdrawable * driDrawable,unsigned int format,uint32_t * stamp,void * loaderPrivate,uint32_t buffer_mask,struct __DRIimageList * buffers)2110 loader_dri3_get_buffers(__DRIdrawable *driDrawable,
2111 unsigned int format,
2112 uint32_t *stamp,
2113 void *loaderPrivate,
2114 uint32_t buffer_mask,
2115 struct __DRIimageList *buffers)
2116 {
2117 struct loader_dri3_drawable *draw = loaderPrivate;
2118 struct loader_dri3_buffer *front, *back;
2119 int buf_id;
2120
2121 buffers->image_mask = 0;
2122 buffers->front = NULL;
2123 buffers->back = NULL;
2124
2125 front = NULL;
2126 back = NULL;
2127
2128 if (!dri3_update_drawable(draw))
2129 return false;
2130
2131 dri3_update_max_num_back(draw);
2132
2133 /* Free no longer needed back buffers */
2134 for (buf_id = draw->cur_num_back; buf_id < LOADER_DRI3_MAX_BACK; buf_id++) {
2135 if (draw->cur_blit_source != buf_id && draw->buffers[buf_id]) {
2136 dri3_free_render_buffer(draw, draw->buffers[buf_id]);
2137 draw->buffers[buf_id] = NULL;
2138 }
2139 }
2140
2141 /* pixmaps always have front buffers.
2142 * Exchange swaps also mandate fake front buffers.
2143 */
2144 if (draw->is_pixmap || draw->swap_method == __DRI_ATTRIB_SWAP_EXCHANGE)
2145 buffer_mask |= __DRI_IMAGE_BUFFER_FRONT;
2146
2147 if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
2148 /* All pixmaps are owned by the server gpu.
2149 * When we use a different gpu, we can't use the pixmap
2150 * as buffer since it is potentially tiled a way
2151 * our device can't understand. In this case, use
2152 * a fake front buffer. Hopefully the pixmap
2153 * content will get synced with the fake front
2154 * buffer.
2155 */
2156 if (draw->is_pixmap && !draw->is_different_gpu)
2157 front = dri3_get_pixmap_buffer(driDrawable,
2158 format,
2159 loader_dri3_buffer_front,
2160 draw);
2161 else
2162 front = dri3_get_buffer(driDrawable,
2163 format,
2164 loader_dri3_buffer_front,
2165 draw);
2166
2167 if (!front)
2168 return false;
2169 } else {
2170 dri3_free_buffers(driDrawable, loader_dri3_buffer_front, draw);
2171 draw->have_fake_front = 0;
2172 }
2173
2174 if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
2175 back = dri3_get_buffer(driDrawable,
2176 format,
2177 loader_dri3_buffer_back,
2178 draw);
2179 if (!back)
2180 return false;
2181 draw->have_back = 1;
2182 } else {
2183 dri3_free_buffers(driDrawable, loader_dri3_buffer_back, draw);
2184 draw->have_back = 0;
2185 }
2186
2187 if (front) {
2188 buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
2189 buffers->front = front->image;
2190 draw->have_fake_front = draw->is_different_gpu || !draw->is_pixmap;
2191 }
2192
2193 if (back) {
2194 buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
2195 buffers->back = back->image;
2196 }
2197
2198 draw->stamp = stamp;
2199
2200 return true;
2201 }
2202
2203 /** loader_dri3_update_drawable_geometry
2204 *
2205 * Get the current drawable geometry.
2206 */
2207 void
loader_dri3_update_drawable_geometry(struct loader_dri3_drawable * draw)2208 loader_dri3_update_drawable_geometry(struct loader_dri3_drawable *draw)
2209 {
2210 xcb_get_geometry_cookie_t geom_cookie;
2211 xcb_get_geometry_reply_t *geom_reply;
2212
2213 geom_cookie = xcb_get_geometry(draw->conn, draw->drawable);
2214
2215 geom_reply = xcb_get_geometry_reply(draw->conn, geom_cookie, NULL);
2216
2217 if (geom_reply) {
2218 draw->width = geom_reply->width;
2219 draw->height = geom_reply->height;
2220 draw->vtable->set_drawable_size(draw, draw->width, draw->height);
2221 draw->ext->flush->invalidate(draw->dri_drawable);
2222
2223 free(geom_reply);
2224 }
2225 }
2226
2227
2228 /**
2229 * Make sure the server has flushed all pending swap buffers to hardware
2230 * for this drawable. Ideally we'd want to send an X protocol request to
2231 * have the server block our connection until the swaps are complete. That
2232 * would avoid the potential round-trip here.
2233 */
2234 void
loader_dri3_swapbuffer_barrier(struct loader_dri3_drawable * draw)2235 loader_dri3_swapbuffer_barrier(struct loader_dri3_drawable *draw)
2236 {
2237 int64_t ust, msc, sbc;
2238
2239 (void) loader_dri3_wait_for_sbc(draw, 0, &ust, &msc, &sbc);
2240 }
2241
2242 /**
2243 * Perform any cleanup associated with a close screen operation.
2244 * \param dri_screen[in,out] Pointer to __DRIscreen about to be closed.
2245 *
2246 * This function destroys the screen's cached swap context if any.
2247 */
2248 void
loader_dri3_close_screen(__DRIscreen * dri_screen)2249 loader_dri3_close_screen(__DRIscreen *dri_screen)
2250 {
2251 mtx_lock(&blit_context.mtx);
2252 if (blit_context.ctx && blit_context.cur_screen == dri_screen) {
2253 blit_context.core->destroyContext(blit_context.ctx);
2254 blit_context.ctx = NULL;
2255 }
2256 mtx_unlock(&blit_context.mtx);
2257 }
2258
2259 /**
2260 * Find a backbuffer slot - potentially allocating a back buffer
2261 *
2262 * \param draw[in,out] Pointer to the drawable for which to find back.
2263 * \return Pointer to a new back buffer or NULL if allocation failed or was
2264 * not mandated.
2265 *
2266 * Find a potentially new back buffer, and if it's not been allocated yet and
2267 * in addition needs initializing, then try to allocate and initialize it.
2268 */
2269 #include <stdio.h>
2270 static struct loader_dri3_buffer *
dri3_find_back_alloc(struct loader_dri3_drawable * draw)2271 dri3_find_back_alloc(struct loader_dri3_drawable *draw)
2272 {
2273 struct loader_dri3_buffer *back;
2274 int id;
2275
2276 id = dri3_find_back(draw, false);
2277 if (id < 0)
2278 return NULL;
2279
2280 back = draw->buffers[id];
2281 /* Allocate a new back if we haven't got one */
2282 if (!back && draw->back_format != __DRI_IMAGE_FORMAT_NONE &&
2283 dri3_update_drawable(draw))
2284 back = dri3_alloc_render_buffer(draw, draw->back_format,
2285 draw->width, draw->height, draw->depth);
2286
2287 if (!back)
2288 return NULL;
2289
2290 draw->buffers[id] = back;
2291
2292 /* If necessary, prefill the back with data according to swap_method mode. */
2293 if (draw->cur_blit_source != -1 &&
2294 draw->buffers[draw->cur_blit_source] &&
2295 back != draw->buffers[draw->cur_blit_source]) {
2296 struct loader_dri3_buffer *source = draw->buffers[draw->cur_blit_source];
2297
2298 dri3_fence_await(draw->conn, draw, source);
2299 dri3_fence_await(draw->conn, draw, back);
2300 (void) loader_dri3_blit_image(draw,
2301 back->image,
2302 source->image,
2303 0, 0, draw->width, draw->height,
2304 0, 0, 0);
2305 back->last_swap = source->last_swap;
2306 draw->cur_blit_source = -1;
2307 }
2308
2309 return back;
2310 }
2311