101e04c3fSmrg/*
201e04c3fSmrg * Copyright © 2013 Keith Packard
301e04c3fSmrg * Copyright © 2015 Boyan Ding
401e04c3fSmrg *
501e04c3fSmrg * Permission to use, copy, modify, distribute, and sell this software and its
601e04c3fSmrg * documentation for any purpose is hereby granted without fee, provided that
701e04c3fSmrg * the above copyright notice appear in all copies and that both that copyright
801e04c3fSmrg * notice and this permission notice appear in supporting documentation, and
901e04c3fSmrg * that the name of the copyright holders not be used in advertising or
1001e04c3fSmrg * publicity pertaining to distribution of the software without specific,
1101e04c3fSmrg * written prior permission.  The copyright holders make no representations
1201e04c3fSmrg * about the suitability of this software for any purpose.  It is provided "as
1301e04c3fSmrg * is" without express or implied warranty.
1401e04c3fSmrg *
1501e04c3fSmrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1601e04c3fSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1701e04c3fSmrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1801e04c3fSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1901e04c3fSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
2001e04c3fSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
2101e04c3fSmrg * OF THIS SOFTWARE.
2201e04c3fSmrg */
2301e04c3fSmrg
2401e04c3fSmrg#include <fcntl.h>
2501e04c3fSmrg#include <stdlib.h>
2601e04c3fSmrg#include <unistd.h>
2701e04c3fSmrg#include <string.h>
2801e04c3fSmrg
2901e04c3fSmrg#include <X11/xshmfence.h>
3001e04c3fSmrg#include <xcb/xcb.h>
3101e04c3fSmrg#include <xcb/dri3.h>
3201e04c3fSmrg#include <xcb/present.h>
337ec681f3Smrg#include <xcb/xfixes.h>
3401e04c3fSmrg
3501e04c3fSmrg#include <X11/Xlib-xcb.h>
3601e04c3fSmrg
377ec681f3Smrg#include "loader_dri_helper.h"
3801e04c3fSmrg#include "loader_dri3_helper.h"
3901e04c3fSmrg#include "util/macros.h"
409f464c52Smaya#include "drm-uapi/drm_fourcc.h"
4101e04c3fSmrg
427ec681f3Smrg/* From driconf.h, user exposed so should be stable */
4301e04c3fSmrg#define DRI_CONF_VBLANK_NEVER 0
4401e04c3fSmrg#define DRI_CONF_VBLANK_DEF_INTERVAL_0 1
4501e04c3fSmrg#define DRI_CONF_VBLANK_DEF_INTERVAL_1 2
4601e04c3fSmrg#define DRI_CONF_VBLANK_ALWAYS_SYNC 3
4701e04c3fSmrg
4801e04c3fSmrg/**
4901e04c3fSmrg * A cached blit context.
5001e04c3fSmrg */
5101e04c3fSmrgstruct loader_dri3_blit_context {
5201e04c3fSmrg   mtx_t mtx;
5301e04c3fSmrg   __DRIcontext *ctx;
5401e04c3fSmrg   __DRIscreen *cur_screen;
5501e04c3fSmrg   const __DRIcoreExtension *core;
5601e04c3fSmrg};
5701e04c3fSmrg
5801e04c3fSmrg/* For simplicity we maintain the cache only for a single screen at a time */
5901e04c3fSmrgstatic struct loader_dri3_blit_context blit_context = {
6001e04c3fSmrg   _MTX_INITIALIZER_NP, NULL
6101e04c3fSmrg};
6201e04c3fSmrg
6301e04c3fSmrgstatic void
6401e04c3fSmrgdri3_flush_present_events(struct loader_dri3_drawable *draw);
6501e04c3fSmrg
6601e04c3fSmrgstatic struct loader_dri3_buffer *
6701e04c3fSmrgdri3_find_back_alloc(struct loader_dri3_drawable *draw);
6801e04c3fSmrg
6901e04c3fSmrgstatic xcb_screen_t *
7001e04c3fSmrgget_screen_for_root(xcb_connection_t *conn, xcb_window_t root)
7101e04c3fSmrg{
7201e04c3fSmrg   xcb_screen_iterator_t screen_iter =
7301e04c3fSmrg   xcb_setup_roots_iterator(xcb_get_setup(conn));
7401e04c3fSmrg
7501e04c3fSmrg   for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {
7601e04c3fSmrg      if (screen_iter.data->root == root)
7701e04c3fSmrg         return screen_iter.data;
7801e04c3fSmrg   }
7901e04c3fSmrg
8001e04c3fSmrg   return NULL;
8101e04c3fSmrg}
8201e04c3fSmrg
8301e04c3fSmrgstatic xcb_visualtype_t *
8401e04c3fSmrgget_xcb_visualtype_for_depth(struct loader_dri3_drawable *draw, int depth)
8501e04c3fSmrg{
8601e04c3fSmrg   xcb_visualtype_iterator_t visual_iter;
8701e04c3fSmrg   xcb_screen_t *screen = draw->screen;
8801e04c3fSmrg   xcb_depth_iterator_t depth_iter;
8901e04c3fSmrg
9001e04c3fSmrg   if (!screen)
9101e04c3fSmrg      return NULL;
9201e04c3fSmrg
9301e04c3fSmrg   depth_iter = xcb_screen_allowed_depths_iterator(screen);
9401e04c3fSmrg   for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
9501e04c3fSmrg      if (depth_iter.data->depth != depth)
9601e04c3fSmrg         continue;
9701e04c3fSmrg
9801e04c3fSmrg      visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
9901e04c3fSmrg      if (visual_iter.rem)
10001e04c3fSmrg         return visual_iter.data;
10101e04c3fSmrg   }
10201e04c3fSmrg
10301e04c3fSmrg   return NULL;
10401e04c3fSmrg}
10501e04c3fSmrg
1069f464c52Smaya/* Sets the adaptive sync window property state. */
1079f464c52Smayastatic void
1089f464c52Smayaset_adaptive_sync_property(xcb_connection_t *conn, xcb_drawable_t drawable,
1099f464c52Smaya                           uint32_t state)
1109f464c52Smaya{
1119f464c52Smaya   static char const name[] = "_VARIABLE_REFRESH";
1129f464c52Smaya   xcb_intern_atom_cookie_t cookie;
1139f464c52Smaya   xcb_intern_atom_reply_t* reply;
1149f464c52Smaya   xcb_void_cookie_t check;
1159f464c52Smaya
1169f464c52Smaya   cookie = xcb_intern_atom(conn, 0, strlen(name), name);
1179f464c52Smaya   reply = xcb_intern_atom_reply(conn, cookie, NULL);
1189f464c52Smaya   if (reply == NULL)
1199f464c52Smaya      return;
1209f464c52Smaya
1219f464c52Smaya   if (state)
1229f464c52Smaya      check = xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE,
1239f464c52Smaya                                          drawable, reply->atom,
1249f464c52Smaya                                          XCB_ATOM_CARDINAL, 32, 1, &state);
1259f464c52Smaya   else
1269f464c52Smaya      check = xcb_delete_property_checked(conn, drawable, reply->atom);
1279f464c52Smaya
1289f464c52Smaya   xcb_discard_reply(conn, check.sequence);
1299f464c52Smaya   free(reply);
1309f464c52Smaya}
1319f464c52Smaya
13201e04c3fSmrg/* Get red channel mask for given drawable at given depth. */
13301e04c3fSmrgstatic unsigned int
13401e04c3fSmrgdri3_get_red_mask_for_depth(struct loader_dri3_drawable *draw, int depth)
13501e04c3fSmrg{
13601e04c3fSmrg   xcb_visualtype_t *visual = get_xcb_visualtype_for_depth(draw, depth);
13701e04c3fSmrg
13801e04c3fSmrg   if (visual)
13901e04c3fSmrg      return visual->red_mask;
14001e04c3fSmrg
14101e04c3fSmrg   return 0;
14201e04c3fSmrg}
14301e04c3fSmrg
14401e04c3fSmrg/**
14501e04c3fSmrg * Do we have blit functionality in the image blit extension?
14601e04c3fSmrg *
14701e04c3fSmrg * \param draw[in]  The drawable intended to blit from / to.
14801e04c3fSmrg * \return  true if we have blit functionality. false otherwise.
14901e04c3fSmrg */
15001e04c3fSmrgstatic bool loader_dri3_have_image_blit(const struct loader_dri3_drawable *draw)
15101e04c3fSmrg{
15201e04c3fSmrg   return draw->ext->image->base.version >= 9 &&
15301e04c3fSmrg      draw->ext->image->blitImage != NULL;
15401e04c3fSmrg}
15501e04c3fSmrg
15601e04c3fSmrg/**
15701e04c3fSmrg * Get and lock (for use with the current thread) a dri context associated
15801e04c3fSmrg * with the drawable's dri screen. The context is intended to be used with
15901e04c3fSmrg * the dri image extension's blitImage method.
16001e04c3fSmrg *
16101e04c3fSmrg * \param draw[in]  Pointer to the drawable whose dri screen we want a
16201e04c3fSmrg * dri context for.
16301e04c3fSmrg * \return A dri context or NULL if context creation failed.
16401e04c3fSmrg *
16501e04c3fSmrg * When the caller is done with the context (even if the context returned was
16601e04c3fSmrg * NULL), the caller must call loader_dri3_blit_context_put.
16701e04c3fSmrg */
16801e04c3fSmrgstatic __DRIcontext *
16901e04c3fSmrgloader_dri3_blit_context_get(struct loader_dri3_drawable *draw)
17001e04c3fSmrg{
17101e04c3fSmrg   mtx_lock(&blit_context.mtx);
17201e04c3fSmrg
17301e04c3fSmrg   if (blit_context.ctx && blit_context.cur_screen != draw->dri_screen) {
17401e04c3fSmrg      blit_context.core->destroyContext(blit_context.ctx);
17501e04c3fSmrg      blit_context.ctx = NULL;
17601e04c3fSmrg   }
17701e04c3fSmrg
17801e04c3fSmrg   if (!blit_context.ctx) {
17901e04c3fSmrg      blit_context.ctx = draw->ext->core->createNewContext(draw->dri_screen,
18001e04c3fSmrg                                                           NULL, NULL, NULL);
18101e04c3fSmrg      blit_context.cur_screen = draw->dri_screen;
18201e04c3fSmrg      blit_context.core = draw->ext->core;
18301e04c3fSmrg   }
18401e04c3fSmrg
18501e04c3fSmrg   return blit_context.ctx;
18601e04c3fSmrg}
18701e04c3fSmrg
18801e04c3fSmrg/**
18901e04c3fSmrg * Release (for use with other threads) a dri context previously obtained using
19001e04c3fSmrg * loader_dri3_blit_context_get.
19101e04c3fSmrg */
19201e04c3fSmrgstatic void
19301e04c3fSmrgloader_dri3_blit_context_put(void)
19401e04c3fSmrg{
19501e04c3fSmrg   mtx_unlock(&blit_context.mtx);
19601e04c3fSmrg}
19701e04c3fSmrg
19801e04c3fSmrg/**
19901e04c3fSmrg * Blit (parts of) the contents of a DRI image to another dri image
20001e04c3fSmrg *
20101e04c3fSmrg * \param draw[in]  The drawable which owns the images.
20201e04c3fSmrg * \param dst[in]  The destination image.
20301e04c3fSmrg * \param src[in]  The source image.
20401e04c3fSmrg * \param dstx0[in]  Start destination coordinate.
20501e04c3fSmrg * \param dsty0[in]  Start destination coordinate.
20601e04c3fSmrg * \param width[in]  Blit width.
20701e04c3fSmrg * \param height[in] Blit height.
20801e04c3fSmrg * \param srcx0[in]  Start source coordinate.
20901e04c3fSmrg * \param srcy0[in]  Start source coordinate.
21001e04c3fSmrg * \param flush_flag[in]  Image blit flush flag.
21101e04c3fSmrg * \return true iff successful.
21201e04c3fSmrg */
21301e04c3fSmrgstatic bool
21401e04c3fSmrgloader_dri3_blit_image(struct loader_dri3_drawable *draw,
21501e04c3fSmrg                       __DRIimage *dst, __DRIimage *src,
21601e04c3fSmrg                       int dstx0, int dsty0, int width, int height,
21701e04c3fSmrg                       int srcx0, int srcy0, int flush_flag)
21801e04c3fSmrg{
21901e04c3fSmrg   __DRIcontext *dri_context;
22001e04c3fSmrg   bool use_blit_context = false;
22101e04c3fSmrg
22201e04c3fSmrg   if (!loader_dri3_have_image_blit(draw))
22301e04c3fSmrg      return false;
22401e04c3fSmrg
22501e04c3fSmrg   dri_context = draw->vtable->get_dri_context(draw);
22601e04c3fSmrg
22701e04c3fSmrg   if (!dri_context || !draw->vtable->in_current_context(draw)) {
22801e04c3fSmrg      dri_context = loader_dri3_blit_context_get(draw);
22901e04c3fSmrg      use_blit_context = true;
23001e04c3fSmrg      flush_flag |= __BLIT_FLAG_FLUSH;
23101e04c3fSmrg   }
23201e04c3fSmrg
23301e04c3fSmrg   if (dri_context)
23401e04c3fSmrg      draw->ext->image->blitImage(dri_context, dst, src, dstx0, dsty0,
23501e04c3fSmrg                                  width, height, srcx0, srcy0,
23601e04c3fSmrg                                  width, height, flush_flag);
23701e04c3fSmrg
23801e04c3fSmrg   if (use_blit_context)
23901e04c3fSmrg      loader_dri3_blit_context_put();
24001e04c3fSmrg
24101e04c3fSmrg   return dri_context != NULL;
24201e04c3fSmrg}
24301e04c3fSmrg
24401e04c3fSmrgstatic inline void
24501e04c3fSmrgdri3_fence_reset(xcb_connection_t *c, struct loader_dri3_buffer *buffer)
24601e04c3fSmrg{
24701e04c3fSmrg   xshmfence_reset(buffer->shm_fence);
24801e04c3fSmrg}
24901e04c3fSmrg
25001e04c3fSmrgstatic inline void
25101e04c3fSmrgdri3_fence_set(struct loader_dri3_buffer *buffer)
25201e04c3fSmrg{
25301e04c3fSmrg   xshmfence_trigger(buffer->shm_fence);
25401e04c3fSmrg}
25501e04c3fSmrg
25601e04c3fSmrgstatic inline void
25701e04c3fSmrgdri3_fence_trigger(xcb_connection_t *c, struct loader_dri3_buffer *buffer)
25801e04c3fSmrg{
25901e04c3fSmrg   xcb_sync_trigger_fence(c, buffer->sync_fence);
26001e04c3fSmrg}
26101e04c3fSmrg
26201e04c3fSmrgstatic inline void
26301e04c3fSmrgdri3_fence_await(xcb_connection_t *c, struct loader_dri3_drawable *draw,
26401e04c3fSmrg                 struct loader_dri3_buffer *buffer)
26501e04c3fSmrg{
26601e04c3fSmrg   xcb_flush(c);
26701e04c3fSmrg   xshmfence_await(buffer->shm_fence);
26801e04c3fSmrg   if (draw) {
26901e04c3fSmrg      mtx_lock(&draw->mtx);
27001e04c3fSmrg      dri3_flush_present_events(draw);
27101e04c3fSmrg      mtx_unlock(&draw->mtx);
27201e04c3fSmrg   }
27301e04c3fSmrg}
27401e04c3fSmrg
27501e04c3fSmrgstatic void
2767ec681f3Smrgdri3_update_max_num_back(struct loader_dri3_drawable *draw)
27701e04c3fSmrg{
2787ec681f3Smrg   switch (draw->last_present_mode) {
2797ec681f3Smrg   case XCB_PRESENT_COMPLETE_MODE_FLIP: {
2807ec681f3Smrg      int new_max;
2817ec681f3Smrg
2827ec681f3Smrg      if (draw->swap_interval == 0)
2837ec681f3Smrg         new_max = 4;
2847ec681f3Smrg      else
2857ec681f3Smrg         new_max = 3;
2867ec681f3Smrg
2877ec681f3Smrg      assert(new_max <= LOADER_DRI3_MAX_BACK);
2887ec681f3Smrg
2897ec681f3Smrg      if (new_max != draw->max_num_back) {
2907ec681f3Smrg         /* On transition from swap interval == 0 to != 0, start with two
2917ec681f3Smrg          * buffers again. Otherwise keep the current number of buffers. Either
2927ec681f3Smrg          * way, more will be allocated if needed.
2937ec681f3Smrg          */
2947ec681f3Smrg         if (new_max < draw->max_num_back)
2957ec681f3Smrg            draw->cur_num_back = 2;
2967ec681f3Smrg
2977ec681f3Smrg         draw->max_num_back = new_max;
2987ec681f3Smrg      }
2997ec681f3Smrg
3007ec681f3Smrg      break;
3017ec681f3Smrg   }
3027ec681f3Smrg
3037ec681f3Smrg   case XCB_PRESENT_COMPLETE_MODE_SKIP:
3047ec681f3Smrg      break;
3057ec681f3Smrg
3067ec681f3Smrg   default:
3077ec681f3Smrg      /* On transition from flips to copies, start with a single buffer again,
3087ec681f3Smrg       * a second one will be allocated if needed
3097ec681f3Smrg       */
3107ec681f3Smrg      if (draw->max_num_back != 2)
3117ec681f3Smrg         draw->cur_num_back = 1;
3127ec681f3Smrg
3137ec681f3Smrg      draw->max_num_back = 2;
3147ec681f3Smrg   }
31501e04c3fSmrg}
31601e04c3fSmrg
31701e04c3fSmrgvoid
31801e04c3fSmrgloader_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval)
31901e04c3fSmrg{
3207ec681f3Smrg   /* Wait all previous swap done before changing swap interval.
3217ec681f3Smrg    *
3227ec681f3Smrg    * This is for preventing swap out of order in the following cases:
3237ec681f3Smrg    *   1. Change from sync swap mode (>0) to async mode (=0), so async swap occurs
3247ec681f3Smrg    *      before previous pending sync swap.
3257ec681f3Smrg    *   2. Change from value A to B and A > B, so the target_msc for the previous
3267ec681f3Smrg    *      pending swap may be bigger than newer swap.
3277ec681f3Smrg    *
3287ec681f3Smrg    * PS. changing from value A to B and A < B won't cause swap out of order but
3297ec681f3Smrg    * may still gets wrong target_msc value at the beginning.
3307ec681f3Smrg    */
3317ec681f3Smrg   if (draw->swap_interval != interval)
3327ec681f3Smrg      loader_dri3_swapbuffer_barrier(draw);
3337ec681f3Smrg
33401e04c3fSmrg   draw->swap_interval = interval;
33501e04c3fSmrg}
33601e04c3fSmrg
33701e04c3fSmrg/** dri3_free_render_buffer
33801e04c3fSmrg *
33901e04c3fSmrg * Free everything associated with one render buffer including pixmap, fence
34001e04c3fSmrg * stuff and the driver image
34101e04c3fSmrg */
34201e04c3fSmrgstatic void
34301e04c3fSmrgdri3_free_render_buffer(struct loader_dri3_drawable *draw,
34401e04c3fSmrg                        struct loader_dri3_buffer *buffer)
34501e04c3fSmrg{
34601e04c3fSmrg   if (buffer->own_pixmap)
34701e04c3fSmrg      xcb_free_pixmap(draw->conn, buffer->pixmap);
34801e04c3fSmrg   xcb_sync_destroy_fence(draw->conn, buffer->sync_fence);
34901e04c3fSmrg   xshmfence_unmap_shm(buffer->shm_fence);
35001e04c3fSmrg   draw->ext->image->destroyImage(buffer->image);
35101e04c3fSmrg   if (buffer->linear_buffer)
35201e04c3fSmrg      draw->ext->image->destroyImage(buffer->linear_buffer);
35301e04c3fSmrg   free(buffer);
35401e04c3fSmrg}
35501e04c3fSmrg
35601e04c3fSmrgvoid
35701e04c3fSmrgloader_dri3_drawable_fini(struct loader_dri3_drawable *draw)
35801e04c3fSmrg{
35901e04c3fSmrg   int i;
36001e04c3fSmrg
36101e04c3fSmrg   draw->ext->core->destroyDrawable(draw->dri_drawable);
36201e04c3fSmrg
36301e04c3fSmrg   for (i = 0; i < ARRAY_SIZE(draw->buffers); i++) {
36401e04c3fSmrg      if (draw->buffers[i])
36501e04c3fSmrg         dri3_free_render_buffer(draw, draw->buffers[i]);
36601e04c3fSmrg   }
36701e04c3fSmrg
36801e04c3fSmrg   if (draw->special_event) {
36901e04c3fSmrg      xcb_void_cookie_t cookie =
37001e04c3fSmrg         xcb_present_select_input_checked(draw->conn, draw->eid, draw->drawable,
37101e04c3fSmrg                                          XCB_PRESENT_EVENT_MASK_NO_EVENT);
37201e04c3fSmrg
37301e04c3fSmrg      xcb_discard_reply(draw->conn, cookie.sequence);
37401e04c3fSmrg      xcb_unregister_for_special_event(draw->conn, draw->special_event);
37501e04c3fSmrg   }
37601e04c3fSmrg
3777ec681f3Smrg   if (draw->region)
3787ec681f3Smrg      xcb_xfixes_destroy_region(draw->conn, draw->region);
3797ec681f3Smrg
38001e04c3fSmrg   cnd_destroy(&draw->event_cnd);
38101e04c3fSmrg   mtx_destroy(&draw->mtx);
38201e04c3fSmrg}
38301e04c3fSmrg
38401e04c3fSmrgint
38501e04c3fSmrgloader_dri3_drawable_init(xcb_connection_t *conn,
38601e04c3fSmrg                          xcb_drawable_t drawable,
38701e04c3fSmrg                          __DRIscreen *dri_screen,
38801e04c3fSmrg                          bool is_different_gpu,
38901e04c3fSmrg                          bool multiplanes_available,
3907ec681f3Smrg                          bool prefer_back_buffer_reuse,
39101e04c3fSmrg                          const __DRIconfig *dri_config,
39201e04c3fSmrg                          struct loader_dri3_extensions *ext,
39301e04c3fSmrg                          const struct loader_dri3_vtable *vtable,
39401e04c3fSmrg                          struct loader_dri3_drawable *draw)
39501e04c3fSmrg{
39601e04c3fSmrg   xcb_get_geometry_cookie_t cookie;
39701e04c3fSmrg   xcb_get_geometry_reply_t *reply;
39801e04c3fSmrg   xcb_generic_error_t *error;
39901e04c3fSmrg   GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
40001e04c3fSmrg   int swap_interval;
40101e04c3fSmrg
40201e04c3fSmrg   draw->conn = conn;
40301e04c3fSmrg   draw->ext = ext;
40401e04c3fSmrg   draw->vtable = vtable;
40501e04c3fSmrg   draw->drawable = drawable;
4067ec681f3Smrg   draw->region = 0;
40701e04c3fSmrg   draw->dri_screen = dri_screen;
40801e04c3fSmrg   draw->is_different_gpu = is_different_gpu;
40901e04c3fSmrg   draw->multiplanes_available = multiplanes_available;
4107ec681f3Smrg   draw->prefer_back_buffer_reuse = prefer_back_buffer_reuse;
41101e04c3fSmrg
41201e04c3fSmrg   draw->have_back = 0;
41301e04c3fSmrg   draw->have_fake_front = 0;
41401e04c3fSmrg   draw->first_init = true;
4159f464c52Smaya   draw->adaptive_sync = false;
4169f464c52Smaya   draw->adaptive_sync_active = false;
41701e04c3fSmrg
41801e04c3fSmrg   draw->cur_blit_source = -1;
41901e04c3fSmrg   draw->back_format = __DRI_IMAGE_FORMAT_NONE;
42001e04c3fSmrg   mtx_init(&draw->mtx, mtx_plain);
42101e04c3fSmrg   cnd_init(&draw->event_cnd);
42201e04c3fSmrg
4239f464c52Smaya   if (draw->ext->config) {
4249f464c52Smaya      unsigned char adaptive_sync = 0;
4259f464c52Smaya
42601e04c3fSmrg      draw->ext->config->configQueryi(draw->dri_screen,
42701e04c3fSmrg                                      "vblank_mode", &vblank_mode);
42801e04c3fSmrg
4299f464c52Smaya      draw->ext->config->configQueryb(draw->dri_screen,
4309f464c52Smaya                                      "adaptive_sync",
4319f464c52Smaya                                      &adaptive_sync);
4329f464c52Smaya
4339f464c52Smaya      draw->adaptive_sync = adaptive_sync;
4349f464c52Smaya   }
4359f464c52Smaya
4369f464c52Smaya   if (!draw->adaptive_sync)
4379f464c52Smaya      set_adaptive_sync_property(conn, draw->drawable, false);
4389f464c52Smaya
43901e04c3fSmrg   switch (vblank_mode) {
44001e04c3fSmrg   case DRI_CONF_VBLANK_NEVER:
44101e04c3fSmrg   case DRI_CONF_VBLANK_DEF_INTERVAL_0:
44201e04c3fSmrg      swap_interval = 0;
44301e04c3fSmrg      break;
44401e04c3fSmrg   case DRI_CONF_VBLANK_DEF_INTERVAL_1:
44501e04c3fSmrg   case DRI_CONF_VBLANK_ALWAYS_SYNC:
44601e04c3fSmrg   default:
44701e04c3fSmrg      swap_interval = 1;
44801e04c3fSmrg      break;
44901e04c3fSmrg   }
45001e04c3fSmrg   draw->swap_interval = swap_interval;
45101e04c3fSmrg
4527ec681f3Smrg   dri3_update_max_num_back(draw);
45301e04c3fSmrg
45401e04c3fSmrg   /* Create a new drawable */
45501e04c3fSmrg   draw->dri_drawable =
45601e04c3fSmrg      draw->ext->image_driver->createNewDrawable(dri_screen,
45701e04c3fSmrg                                                 dri_config,
45801e04c3fSmrg                                                 draw);
45901e04c3fSmrg
46001e04c3fSmrg   if (!draw->dri_drawable)
46101e04c3fSmrg      return 1;
46201e04c3fSmrg
46301e04c3fSmrg   cookie = xcb_get_geometry(draw->conn, draw->drawable);
46401e04c3fSmrg   reply = xcb_get_geometry_reply(draw->conn, cookie, &error);
46501e04c3fSmrg   if (reply == NULL || error != NULL) {
46601e04c3fSmrg      draw->ext->core->destroyDrawable(draw->dri_drawable);
46701e04c3fSmrg      return 1;
46801e04c3fSmrg   }
46901e04c3fSmrg
47001e04c3fSmrg   draw->screen = get_screen_for_root(draw->conn, reply->root);
47101e04c3fSmrg   draw->width = reply->width;
47201e04c3fSmrg   draw->height = reply->height;
47301e04c3fSmrg   draw->depth = reply->depth;
47401e04c3fSmrg   draw->vtable->set_drawable_size(draw, draw->width, draw->height);
47501e04c3fSmrg   free(reply);
47601e04c3fSmrg
47701e04c3fSmrg   draw->swap_method = __DRI_ATTRIB_SWAP_UNDEFINED;
47801e04c3fSmrg   if (draw->ext->core->base.version >= 2) {
47901e04c3fSmrg      (void )draw->ext->core->getConfigAttrib(dri_config,
48001e04c3fSmrg                                              __DRI_ATTRIB_SWAP_METHOD,
48101e04c3fSmrg                                              &draw->swap_method);
48201e04c3fSmrg   }
48301e04c3fSmrg
48401e04c3fSmrg   /*
48501e04c3fSmrg    * Make sure server has the same swap interval we do for the new
48601e04c3fSmrg    * drawable.
48701e04c3fSmrg    */
48801e04c3fSmrg   loader_dri3_set_swap_interval(draw, swap_interval);
48901e04c3fSmrg
49001e04c3fSmrg   return 0;
49101e04c3fSmrg}
49201e04c3fSmrg
49301e04c3fSmrg/*
49401e04c3fSmrg * Process one Present event
49501e04c3fSmrg */
49601e04c3fSmrgstatic void
49701e04c3fSmrgdri3_handle_present_event(struct loader_dri3_drawable *draw,
49801e04c3fSmrg                          xcb_present_generic_event_t *ge)
49901e04c3fSmrg{
50001e04c3fSmrg   switch (ge->evtype) {
50101e04c3fSmrg   case XCB_PRESENT_CONFIGURE_NOTIFY: {
50201e04c3fSmrg      xcb_present_configure_notify_event_t *ce = (void *) ge;
50301e04c3fSmrg
50401e04c3fSmrg      draw->width = ce->width;
50501e04c3fSmrg      draw->height = ce->height;
50601e04c3fSmrg      draw->vtable->set_drawable_size(draw, draw->width, draw->height);
50701e04c3fSmrg      draw->ext->flush->invalidate(draw->dri_drawable);
50801e04c3fSmrg      break;
50901e04c3fSmrg   }
51001e04c3fSmrg   case XCB_PRESENT_COMPLETE_NOTIFY: {
51101e04c3fSmrg      xcb_present_complete_notify_event_t *ce = (void *) ge;
51201e04c3fSmrg
51301e04c3fSmrg      /* Compute the processed SBC number from the received 32-bit serial number
51401e04c3fSmrg       * merged with the upper 32-bits of the sent 64-bit serial number while
51501e04c3fSmrg       * checking for wrap.
51601e04c3fSmrg       */
51701e04c3fSmrg      if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) {
51801e04c3fSmrg         uint64_t recv_sbc = (draw->send_sbc & 0xffffffff00000000LL) | ce->serial;
51901e04c3fSmrg
52001e04c3fSmrg         /* Only assume wraparound if that results in exactly the previous
52101e04c3fSmrg          * SBC + 1, otherwise ignore received SBC > sent SBC (those are
52201e04c3fSmrg          * probably from a previous loader_dri3_drawable instance) to avoid
52301e04c3fSmrg          * calculating bogus target MSC values in loader_dri3_swap_buffers_msc
52401e04c3fSmrg          */
52501e04c3fSmrg         if (recv_sbc <= draw->send_sbc)
52601e04c3fSmrg            draw->recv_sbc = recv_sbc;
52701e04c3fSmrg         else if (recv_sbc == (draw->recv_sbc + 0x100000001ULL))
52801e04c3fSmrg            draw->recv_sbc = recv_sbc - 0x100000000ULL;
52901e04c3fSmrg
53001e04c3fSmrg         /* When moving from flip to copy, we assume that we can allocate in
53101e04c3fSmrg          * a more optimal way if we don't need to cater for the display
53201e04c3fSmrg          * controller.
53301e04c3fSmrg          */
53401e04c3fSmrg         if (ce->mode == XCB_PRESENT_COMPLETE_MODE_COPY &&
53501e04c3fSmrg             draw->last_present_mode == XCB_PRESENT_COMPLETE_MODE_FLIP) {
53601e04c3fSmrg            for (int b = 0; b < ARRAY_SIZE(draw->buffers); b++) {
53701e04c3fSmrg               if (draw->buffers[b])
53801e04c3fSmrg                  draw->buffers[b]->reallocate = true;
53901e04c3fSmrg            }
54001e04c3fSmrg         }
54101e04c3fSmrg
54201e04c3fSmrg         /* If the server tells us that our allocation is suboptimal, we
54301e04c3fSmrg          * reallocate once.
54401e04c3fSmrg          */
54501e04c3fSmrg#ifdef HAVE_DRI3_MODIFIERS
54601e04c3fSmrg         if (ce->mode == XCB_PRESENT_COMPLETE_MODE_SUBOPTIMAL_COPY &&
54701e04c3fSmrg             draw->last_present_mode != ce->mode) {
54801e04c3fSmrg            for (int b = 0; b < ARRAY_SIZE(draw->buffers); b++) {
54901e04c3fSmrg               if (draw->buffers[b])
55001e04c3fSmrg                  draw->buffers[b]->reallocate = true;
55101e04c3fSmrg            }
55201e04c3fSmrg         }
55301e04c3fSmrg#endif
55401e04c3fSmrg         draw->last_present_mode = ce->mode;
55501e04c3fSmrg
55601e04c3fSmrg         if (draw->vtable->show_fps)
55701e04c3fSmrg            draw->vtable->show_fps(draw, ce->ust);
55801e04c3fSmrg
55901e04c3fSmrg         draw->ust = ce->ust;
56001e04c3fSmrg         draw->msc = ce->msc;
56101e04c3fSmrg      } else if (ce->serial == draw->eid) {
56201e04c3fSmrg         draw->notify_ust = ce->ust;
56301e04c3fSmrg         draw->notify_msc = ce->msc;
56401e04c3fSmrg      }
56501e04c3fSmrg      break;
56601e04c3fSmrg   }
56701e04c3fSmrg   case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
56801e04c3fSmrg      xcb_present_idle_notify_event_t *ie = (void *) ge;
56901e04c3fSmrg      int b;
57001e04c3fSmrg
57101e04c3fSmrg      for (b = 0; b < ARRAY_SIZE(draw->buffers); b++) {
57201e04c3fSmrg         struct loader_dri3_buffer *buf = draw->buffers[b];
57301e04c3fSmrg
57401e04c3fSmrg         if (buf && buf->pixmap == ie->pixmap)
57501e04c3fSmrg            buf->busy = 0;
57601e04c3fSmrg      }
57701e04c3fSmrg      break;
57801e04c3fSmrg   }
57901e04c3fSmrg   }
58001e04c3fSmrg   free(ge);
58101e04c3fSmrg}
58201e04c3fSmrg
58301e04c3fSmrgstatic bool
5847ec681f3Smrgdri3_wait_for_event_locked(struct loader_dri3_drawable *draw,
5857ec681f3Smrg                           unsigned *full_sequence)
58601e04c3fSmrg{
58701e04c3fSmrg   xcb_generic_event_t *ev;
58801e04c3fSmrg   xcb_present_generic_event_t *ge;
58901e04c3fSmrg
59001e04c3fSmrg   xcb_flush(draw->conn);
59101e04c3fSmrg
59201e04c3fSmrg   /* Only have one thread waiting for events at a time */
59301e04c3fSmrg   if (draw->has_event_waiter) {
59401e04c3fSmrg      cnd_wait(&draw->event_cnd, &draw->mtx);
5957ec681f3Smrg      if (full_sequence)
5967ec681f3Smrg         *full_sequence = draw->last_special_event_sequence;
59701e04c3fSmrg      /* Another thread has updated the protected info, so retest. */
59801e04c3fSmrg      return true;
59901e04c3fSmrg   } else {
60001e04c3fSmrg      draw->has_event_waiter = true;
60101e04c3fSmrg      /* Allow other threads access to the drawable while we're waiting. */
60201e04c3fSmrg      mtx_unlock(&draw->mtx);
60301e04c3fSmrg      ev = xcb_wait_for_special_event(draw->conn, draw->special_event);
60401e04c3fSmrg      mtx_lock(&draw->mtx);
60501e04c3fSmrg      draw->has_event_waiter = false;
60601e04c3fSmrg      cnd_broadcast(&draw->event_cnd);
60701e04c3fSmrg   }
60801e04c3fSmrg   if (!ev)
60901e04c3fSmrg      return false;
6107ec681f3Smrg   draw->last_special_event_sequence = ev->full_sequence;
6117ec681f3Smrg   if (full_sequence)
6127ec681f3Smrg      *full_sequence = ev->full_sequence;
61301e04c3fSmrg   ge = (void *) ev;
61401e04c3fSmrg   dri3_handle_present_event(draw, ge);
61501e04c3fSmrg   return true;
61601e04c3fSmrg}
61701e04c3fSmrg
61801e04c3fSmrg/** loader_dri3_wait_for_msc
61901e04c3fSmrg *
62001e04c3fSmrg * Get the X server to send an event when the target msc/divisor/remainder is
62101e04c3fSmrg * reached.
62201e04c3fSmrg */
62301e04c3fSmrgbool
62401e04c3fSmrgloader_dri3_wait_for_msc(struct loader_dri3_drawable *draw,
62501e04c3fSmrg                         int64_t target_msc,
62601e04c3fSmrg                         int64_t divisor, int64_t remainder,
62701e04c3fSmrg                         int64_t *ust, int64_t *msc, int64_t *sbc)
62801e04c3fSmrg{
62901e04c3fSmrg   xcb_void_cookie_t cookie = xcb_present_notify_msc(draw->conn,
63001e04c3fSmrg                                                     draw->drawable,
63101e04c3fSmrg                                                     draw->eid,
63201e04c3fSmrg                                                     target_msc,
63301e04c3fSmrg                                                     divisor,
63401e04c3fSmrg                                                     remainder);
63501e04c3fSmrg   unsigned full_sequence;
63601e04c3fSmrg
63701e04c3fSmrg   mtx_lock(&draw->mtx);
63801e04c3fSmrg
63901e04c3fSmrg   /* Wait for the event */
64001e04c3fSmrg   do {
6417ec681f3Smrg      if (!dri3_wait_for_event_locked(draw, &full_sequence)) {
64201e04c3fSmrg         mtx_unlock(&draw->mtx);
64301e04c3fSmrg         return false;
64401e04c3fSmrg      }
64501e04c3fSmrg   } while (full_sequence != cookie.sequence || draw->notify_msc < target_msc);
64601e04c3fSmrg
64701e04c3fSmrg   *ust = draw->notify_ust;
64801e04c3fSmrg   *msc = draw->notify_msc;
64901e04c3fSmrg   *sbc = draw->recv_sbc;
65001e04c3fSmrg   mtx_unlock(&draw->mtx);
65101e04c3fSmrg
65201e04c3fSmrg   return true;
65301e04c3fSmrg}
65401e04c3fSmrg
65501e04c3fSmrg/** loader_dri3_wait_for_sbc
65601e04c3fSmrg *
65701e04c3fSmrg * Wait for the completed swap buffer count to reach the specified
65801e04c3fSmrg * target. Presumably the application knows that this will be reached with
65901e04c3fSmrg * outstanding complete events, or we're going to be here awhile.
66001e04c3fSmrg */
66101e04c3fSmrgint
66201e04c3fSmrgloader_dri3_wait_for_sbc(struct loader_dri3_drawable *draw,
66301e04c3fSmrg                         int64_t target_sbc, int64_t *ust,
66401e04c3fSmrg                         int64_t *msc, int64_t *sbc)
66501e04c3fSmrg{
66601e04c3fSmrg   /* From the GLX_OML_sync_control spec:
66701e04c3fSmrg    *
66801e04c3fSmrg    *     "If <target_sbc> = 0, the function will block until all previous
66901e04c3fSmrg    *      swaps requested with glXSwapBuffersMscOML for that window have
67001e04c3fSmrg    *      completed."
67101e04c3fSmrg    */
67201e04c3fSmrg   mtx_lock(&draw->mtx);
67301e04c3fSmrg   if (!target_sbc)
67401e04c3fSmrg      target_sbc = draw->send_sbc;
67501e04c3fSmrg
67601e04c3fSmrg   while (draw->recv_sbc < target_sbc) {
6777ec681f3Smrg      if (!dri3_wait_for_event_locked(draw, NULL)) {
67801e04c3fSmrg         mtx_unlock(&draw->mtx);
67901e04c3fSmrg         return 0;
68001e04c3fSmrg      }
68101e04c3fSmrg   }
68201e04c3fSmrg
68301e04c3fSmrg   *ust = draw->ust;
68401e04c3fSmrg   *msc = draw->msc;
68501e04c3fSmrg   *sbc = draw->recv_sbc;
68601e04c3fSmrg   mtx_unlock(&draw->mtx);
68701e04c3fSmrg   return 1;
68801e04c3fSmrg}
68901e04c3fSmrg
69001e04c3fSmrg/** loader_dri3_find_back
69101e04c3fSmrg *
69201e04c3fSmrg * Find an idle back buffer. If there isn't one, then
69301e04c3fSmrg * wait for a present idle notify event from the X server
69401e04c3fSmrg */
69501e04c3fSmrgstatic int
6967ec681f3Smrgdri3_find_back(struct loader_dri3_drawable *draw, bool prefer_a_different)
69701e04c3fSmrg{
69801e04c3fSmrg   int b;
69901e04c3fSmrg   int num_to_consider;
7007ec681f3Smrg   int max_num;
70101e04c3fSmrg
70201e04c3fSmrg   mtx_lock(&draw->mtx);
70301e04c3fSmrg   /* Increase the likelyhood of reusing current buffer */
70401e04c3fSmrg   dri3_flush_present_events(draw);
70501e04c3fSmrg
70601e04c3fSmrg   /* Check whether we need to reuse the current back buffer as new back.
70701e04c3fSmrg    * In that case, wait until it's not busy anymore.
70801e04c3fSmrg    */
70901e04c3fSmrg   if (!loader_dri3_have_image_blit(draw) && draw->cur_blit_source != -1) {
71001e04c3fSmrg      num_to_consider = 1;
7117ec681f3Smrg      max_num = 1;
71201e04c3fSmrg      draw->cur_blit_source = -1;
7137ec681f3Smrg   } else {
7147ec681f3Smrg      num_to_consider = draw->cur_num_back;
7157ec681f3Smrg      max_num = draw->max_num_back;
71601e04c3fSmrg   }
71701e04c3fSmrg
7187ec681f3Smrg   /* In a DRI_PRIME situation, if prefer_a_different is true, we first try
7197ec681f3Smrg    * to find an idle buffer that is not the last used one.
7207ec681f3Smrg    * This is useful if we receive a XCB_PRESENT_EVENT_IDLE_NOTIFY event
7217ec681f3Smrg    * for a pixmap but it's not actually idle (eg: the DRI_PRIME blit is
7227ec681f3Smrg    * still in progress).
7237ec681f3Smrg    * Unigine Superposition hits this and this allows to use 2 back buffers
7247ec681f3Smrg    * instead of reusing the same one all the time, causing the next frame
7257ec681f3Smrg    * to wait for the copy to finish.
7267ec681f3Smrg    */
7277ec681f3Smrg   int current_back_id = draw->cur_back;
72801e04c3fSmrg   for (;;) {
72901e04c3fSmrg      for (b = 0; b < num_to_consider; b++) {
7307ec681f3Smrg         int id = LOADER_DRI3_BACK_ID((b + draw->cur_back) % draw->cur_num_back);
73101e04c3fSmrg         struct loader_dri3_buffer *buffer = draw->buffers[id];
73201e04c3fSmrg
7337ec681f3Smrg         if (!buffer || (!buffer->busy &&
7347ec681f3Smrg                         (!prefer_a_different || id != current_back_id))) {
73501e04c3fSmrg            draw->cur_back = id;
73601e04c3fSmrg            mtx_unlock(&draw->mtx);
73701e04c3fSmrg            return id;
73801e04c3fSmrg         }
73901e04c3fSmrg      }
7407ec681f3Smrg
7417ec681f3Smrg      if (num_to_consider < max_num) {
7427ec681f3Smrg         num_to_consider = ++draw->cur_num_back;
7437ec681f3Smrg      } else if (prefer_a_different) {
7447ec681f3Smrg         prefer_a_different = false;
7457ec681f3Smrg      } else if (!dri3_wait_for_event_locked(draw, NULL)) {
74601e04c3fSmrg         mtx_unlock(&draw->mtx);
74701e04c3fSmrg         return -1;
74801e04c3fSmrg      }
74901e04c3fSmrg   }
75001e04c3fSmrg}
75101e04c3fSmrg
75201e04c3fSmrgstatic xcb_gcontext_t
75301e04c3fSmrgdri3_drawable_gc(struct loader_dri3_drawable *draw)
75401e04c3fSmrg{
75501e04c3fSmrg   if (!draw->gc) {
75601e04c3fSmrg      uint32_t v = 0;
75701e04c3fSmrg      xcb_create_gc(draw->conn,
75801e04c3fSmrg                    (draw->gc = xcb_generate_id(draw->conn)),
75901e04c3fSmrg                    draw->drawable,
76001e04c3fSmrg                    XCB_GC_GRAPHICS_EXPOSURES,
76101e04c3fSmrg                    &v);
76201e04c3fSmrg   }
76301e04c3fSmrg   return draw->gc;
76401e04c3fSmrg}
76501e04c3fSmrg
76601e04c3fSmrg
76701e04c3fSmrgstatic struct loader_dri3_buffer *
76801e04c3fSmrgdri3_back_buffer(struct loader_dri3_drawable *draw)
76901e04c3fSmrg{
77001e04c3fSmrg   return draw->buffers[LOADER_DRI3_BACK_ID(draw->cur_back)];
77101e04c3fSmrg}
77201e04c3fSmrg
77301e04c3fSmrgstatic struct loader_dri3_buffer *
77401e04c3fSmrgdri3_fake_front_buffer(struct loader_dri3_drawable *draw)
77501e04c3fSmrg{
77601e04c3fSmrg   return draw->buffers[LOADER_DRI3_FRONT_ID];
77701e04c3fSmrg}
77801e04c3fSmrg
77901e04c3fSmrgstatic void
78001e04c3fSmrgdri3_copy_area(xcb_connection_t *c,
78101e04c3fSmrg               xcb_drawable_t    src_drawable,
78201e04c3fSmrg               xcb_drawable_t    dst_drawable,
78301e04c3fSmrg               xcb_gcontext_t    gc,
78401e04c3fSmrg               int16_t           src_x,
78501e04c3fSmrg               int16_t           src_y,
78601e04c3fSmrg               int16_t           dst_x,
78701e04c3fSmrg               int16_t           dst_y,
78801e04c3fSmrg               uint16_t          width,
78901e04c3fSmrg               uint16_t          height)
79001e04c3fSmrg{
79101e04c3fSmrg   xcb_void_cookie_t cookie;
79201e04c3fSmrg
79301e04c3fSmrg   cookie = xcb_copy_area_checked(c,
79401e04c3fSmrg                                  src_drawable,
79501e04c3fSmrg                                  dst_drawable,
79601e04c3fSmrg                                  gc,
79701e04c3fSmrg                                  src_x,
79801e04c3fSmrg                                  src_y,
79901e04c3fSmrg                                  dst_x,
80001e04c3fSmrg                                  dst_y,
80101e04c3fSmrg                                  width,
80201e04c3fSmrg                                  height);
80301e04c3fSmrg   xcb_discard_reply(c, cookie.sequence);
80401e04c3fSmrg}
80501e04c3fSmrg
80601e04c3fSmrg/**
80701e04c3fSmrg * Asks the driver to flush any queued work necessary for serializing with the
80801e04c3fSmrg * X command stream, and optionally the slightly more strict requirement of
80901e04c3fSmrg * glFlush() equivalence (which would require flushing even if nothing had
81001e04c3fSmrg * been drawn to a window system framebuffer, for example).
81101e04c3fSmrg */
81201e04c3fSmrgvoid
81301e04c3fSmrgloader_dri3_flush(struct loader_dri3_drawable *draw,
81401e04c3fSmrg                  unsigned flags,
81501e04c3fSmrg                  enum __DRI2throttleReason throttle_reason)
81601e04c3fSmrg{
81701e04c3fSmrg   /* NEED TO CHECK WHETHER CONTEXT IS NULL */
81801e04c3fSmrg   __DRIcontext *dri_context = draw->vtable->get_dri_context(draw);
81901e04c3fSmrg
82001e04c3fSmrg   if (dri_context) {
82101e04c3fSmrg      draw->ext->flush->flush_with_flags(dri_context, draw->dri_drawable,
82201e04c3fSmrg                                         flags, throttle_reason);
82301e04c3fSmrg   }
82401e04c3fSmrg}
82501e04c3fSmrg
82601e04c3fSmrgvoid
82701e04c3fSmrgloader_dri3_copy_sub_buffer(struct loader_dri3_drawable *draw,
82801e04c3fSmrg                            int x, int y,
82901e04c3fSmrg                            int width, int height,
83001e04c3fSmrg                            bool flush)
83101e04c3fSmrg{
83201e04c3fSmrg   struct loader_dri3_buffer *back;
83301e04c3fSmrg   unsigned flags = __DRI2_FLUSH_DRAWABLE;
83401e04c3fSmrg
83501e04c3fSmrg   /* Check we have the right attachments */
83601e04c3fSmrg   if (!draw->have_back || draw->is_pixmap)
83701e04c3fSmrg      return;
83801e04c3fSmrg
83901e04c3fSmrg   if (flush)
84001e04c3fSmrg      flags |= __DRI2_FLUSH_CONTEXT;
8417ec681f3Smrg   loader_dri3_flush(draw, flags, __DRI2_THROTTLE_COPYSUBBUFFER);
84201e04c3fSmrg
84301e04c3fSmrg   back = dri3_find_back_alloc(draw);
84401e04c3fSmrg   if (!back)
84501e04c3fSmrg      return;
84601e04c3fSmrg
84701e04c3fSmrg   y = draw->height - y - height;
84801e04c3fSmrg
84901e04c3fSmrg   if (draw->is_different_gpu) {
85001e04c3fSmrg      /* Update the linear buffer part of the back buffer
85101e04c3fSmrg       * for the dri3_copy_area operation
85201e04c3fSmrg       */
85301e04c3fSmrg      (void) loader_dri3_blit_image(draw,
85401e04c3fSmrg                                    back->linear_buffer,
85501e04c3fSmrg                                    back->image,
85601e04c3fSmrg                                    0, 0, back->width, back->height,
85701e04c3fSmrg                                    0, 0, __BLIT_FLAG_FLUSH);
85801e04c3fSmrg   }
85901e04c3fSmrg
86001e04c3fSmrg   loader_dri3_swapbuffer_barrier(draw);
86101e04c3fSmrg   dri3_fence_reset(draw->conn, back);
86201e04c3fSmrg   dri3_copy_area(draw->conn,
86301e04c3fSmrg                  back->pixmap,
86401e04c3fSmrg                  draw->drawable,
86501e04c3fSmrg                  dri3_drawable_gc(draw),
86601e04c3fSmrg                  x, y, x, y, width, height);
86701e04c3fSmrg   dri3_fence_trigger(draw->conn, back);
86801e04c3fSmrg   /* Refresh the fake front (if present) after we just damaged the real
86901e04c3fSmrg    * front.
87001e04c3fSmrg    */
87101e04c3fSmrg   if (draw->have_fake_front &&
87201e04c3fSmrg       !loader_dri3_blit_image(draw,
87301e04c3fSmrg                               dri3_fake_front_buffer(draw)->image,
87401e04c3fSmrg                               back->image,
87501e04c3fSmrg                               x, y, width, height,
87601e04c3fSmrg                               x, y, __BLIT_FLAG_FLUSH) &&
87701e04c3fSmrg       !draw->is_different_gpu) {
87801e04c3fSmrg      dri3_fence_reset(draw->conn, dri3_fake_front_buffer(draw));
87901e04c3fSmrg      dri3_copy_area(draw->conn,
88001e04c3fSmrg                     back->pixmap,
88101e04c3fSmrg                     dri3_fake_front_buffer(draw)->pixmap,
88201e04c3fSmrg                     dri3_drawable_gc(draw),
88301e04c3fSmrg                     x, y, x, y, width, height);
88401e04c3fSmrg      dri3_fence_trigger(draw->conn, dri3_fake_front_buffer(draw));
88501e04c3fSmrg      dri3_fence_await(draw->conn, NULL, dri3_fake_front_buffer(draw));
88601e04c3fSmrg   }
88701e04c3fSmrg   dri3_fence_await(draw->conn, draw, back);
88801e04c3fSmrg}
88901e04c3fSmrg
89001e04c3fSmrgvoid
89101e04c3fSmrgloader_dri3_copy_drawable(struct loader_dri3_drawable *draw,
89201e04c3fSmrg                          xcb_drawable_t dest,
89301e04c3fSmrg                          xcb_drawable_t src)
89401e04c3fSmrg{
8957ec681f3Smrg   loader_dri3_flush(draw, __DRI2_FLUSH_DRAWABLE, __DRI2_THROTTLE_COPYSUBBUFFER);
89601e04c3fSmrg
89701e04c3fSmrg   dri3_fence_reset(draw->conn, dri3_fake_front_buffer(draw));
89801e04c3fSmrg   dri3_copy_area(draw->conn,
89901e04c3fSmrg                  src, dest,
90001e04c3fSmrg                  dri3_drawable_gc(draw),
90101e04c3fSmrg                  0, 0, 0, 0, draw->width, draw->height);
90201e04c3fSmrg   dri3_fence_trigger(draw->conn, dri3_fake_front_buffer(draw));
90301e04c3fSmrg   dri3_fence_await(draw->conn, draw, dri3_fake_front_buffer(draw));
90401e04c3fSmrg}
90501e04c3fSmrg
90601e04c3fSmrgvoid
90701e04c3fSmrgloader_dri3_wait_x(struct loader_dri3_drawable *draw)
90801e04c3fSmrg{
90901e04c3fSmrg   struct loader_dri3_buffer *front;
91001e04c3fSmrg
91101e04c3fSmrg   if (draw == NULL || !draw->have_fake_front)
91201e04c3fSmrg      return;
91301e04c3fSmrg
91401e04c3fSmrg   front = dri3_fake_front_buffer(draw);
91501e04c3fSmrg
91601e04c3fSmrg   loader_dri3_copy_drawable(draw, front->pixmap, draw->drawable);
91701e04c3fSmrg
91801e04c3fSmrg   /* In the psc->is_different_gpu case, the linear buffer has been updated,
91901e04c3fSmrg    * but not yet the tiled buffer.
92001e04c3fSmrg    * Copy back to the tiled buffer we use for rendering.
92101e04c3fSmrg    * Note that we don't need flushing.
92201e04c3fSmrg    */
92301e04c3fSmrg   if (draw->is_different_gpu)
92401e04c3fSmrg      (void) loader_dri3_blit_image(draw,
92501e04c3fSmrg                                    front->image,
92601e04c3fSmrg                                    front->linear_buffer,
92701e04c3fSmrg                                    0, 0, front->width, front->height,
92801e04c3fSmrg                                    0, 0, 0);
92901e04c3fSmrg}
93001e04c3fSmrg
93101e04c3fSmrgvoid
93201e04c3fSmrgloader_dri3_wait_gl(struct loader_dri3_drawable *draw)
93301e04c3fSmrg{
93401e04c3fSmrg   struct loader_dri3_buffer *front;
93501e04c3fSmrg
93601e04c3fSmrg   if (draw == NULL || !draw->have_fake_front)
93701e04c3fSmrg      return;
93801e04c3fSmrg
93901e04c3fSmrg   front = dri3_fake_front_buffer(draw);
94001e04c3fSmrg
94101e04c3fSmrg   /* In the psc->is_different_gpu case, we update the linear_buffer
94201e04c3fSmrg    * before updating the real front.
94301e04c3fSmrg    */
94401e04c3fSmrg   if (draw->is_different_gpu)
94501e04c3fSmrg      (void) loader_dri3_blit_image(draw,
94601e04c3fSmrg                                    front->linear_buffer,
94701e04c3fSmrg                                    front->image,
94801e04c3fSmrg                                    0, 0, front->width, front->height,
94901e04c3fSmrg                                    0, 0, __BLIT_FLAG_FLUSH);
95001e04c3fSmrg   loader_dri3_swapbuffer_barrier(draw);
95101e04c3fSmrg   loader_dri3_copy_drawable(draw, draw->drawable, front->pixmap);
95201e04c3fSmrg}
95301e04c3fSmrg
95401e04c3fSmrg/** dri3_flush_present_events
95501e04c3fSmrg *
95601e04c3fSmrg * Process any present events that have been received from the X server
95701e04c3fSmrg */
95801e04c3fSmrgstatic void
95901e04c3fSmrgdri3_flush_present_events(struct loader_dri3_drawable *draw)
96001e04c3fSmrg{
96101e04c3fSmrg   /* Check to see if any configuration changes have occurred
96201e04c3fSmrg    * since we were last invoked
96301e04c3fSmrg    */
96401e04c3fSmrg   if (draw->has_event_waiter)
96501e04c3fSmrg      return;
96601e04c3fSmrg
96701e04c3fSmrg   if (draw->special_event) {
96801e04c3fSmrg      xcb_generic_event_t    *ev;
96901e04c3fSmrg
97001e04c3fSmrg      while ((ev = xcb_poll_for_special_event(draw->conn,
97101e04c3fSmrg                                              draw->special_event)) != NULL) {
97201e04c3fSmrg         xcb_present_generic_event_t *ge = (void *) ev;
97301e04c3fSmrg         dri3_handle_present_event(draw, ge);
97401e04c3fSmrg      }
97501e04c3fSmrg   }
97601e04c3fSmrg}
97701e04c3fSmrg
97801e04c3fSmrg/** loader_dri3_swap_buffers_msc
97901e04c3fSmrg *
98001e04c3fSmrg * Make the current back buffer visible using the present extension
98101e04c3fSmrg */
98201e04c3fSmrgint64_t
98301e04c3fSmrgloader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw,
98401e04c3fSmrg                             int64_t target_msc, int64_t divisor,
98501e04c3fSmrg                             int64_t remainder, unsigned flush_flags,
9867ec681f3Smrg                             const int *rects, int n_rects,
98701e04c3fSmrg                             bool force_copy)
98801e04c3fSmrg{
98901e04c3fSmrg   struct loader_dri3_buffer *back;
99001e04c3fSmrg   int64_t ret = 0;
99101e04c3fSmrg   uint32_t options = XCB_PRESENT_OPTION_NONE;
99201e04c3fSmrg
99301e04c3fSmrg   draw->vtable->flush_drawable(draw, flush_flags);
99401e04c3fSmrg
99501e04c3fSmrg   back = dri3_find_back_alloc(draw);
99601e04c3fSmrg
99701e04c3fSmrg   mtx_lock(&draw->mtx);
9989f464c52Smaya
9999f464c52Smaya   if (draw->adaptive_sync && !draw->adaptive_sync_active) {
10009f464c52Smaya      set_adaptive_sync_property(draw->conn, draw->drawable, true);
10019f464c52Smaya      draw->adaptive_sync_active = true;
10029f464c52Smaya   }
10039f464c52Smaya
100401e04c3fSmrg   if (draw->is_different_gpu && back) {
100501e04c3fSmrg      /* Update the linear buffer before presenting the pixmap */
100601e04c3fSmrg      (void) loader_dri3_blit_image(draw,
100701e04c3fSmrg                                    back->linear_buffer,
100801e04c3fSmrg                                    back->image,
100901e04c3fSmrg                                    0, 0, back->width, back->height,
101001e04c3fSmrg                                    0, 0, __BLIT_FLAG_FLUSH);
101101e04c3fSmrg   }
101201e04c3fSmrg
101301e04c3fSmrg   /* If we need to preload the new back buffer, remember the source.
101401e04c3fSmrg    * The force_copy parameter is used by EGL to attempt to preserve
101501e04c3fSmrg    * the back buffer across a call to this function.
101601e04c3fSmrg    */
101701e04c3fSmrg   if (draw->swap_method != __DRI_ATTRIB_SWAP_UNDEFINED || force_copy)
101801e04c3fSmrg      draw->cur_blit_source = LOADER_DRI3_BACK_ID(draw->cur_back);
101901e04c3fSmrg
102001e04c3fSmrg   /* Exchange the back and fake front. Even though the server knows about these
102101e04c3fSmrg    * buffers, it has no notion of back and fake front.
102201e04c3fSmrg    */
102301e04c3fSmrg   if (back && draw->have_fake_front) {
102401e04c3fSmrg      struct loader_dri3_buffer *tmp;
102501e04c3fSmrg
102601e04c3fSmrg      tmp = dri3_fake_front_buffer(draw);
102701e04c3fSmrg      draw->buffers[LOADER_DRI3_FRONT_ID] = back;
102801e04c3fSmrg      draw->buffers[LOADER_DRI3_BACK_ID(draw->cur_back)] = tmp;
102901e04c3fSmrg
103001e04c3fSmrg      if (draw->swap_method == __DRI_ATTRIB_SWAP_COPY  || force_copy)
103101e04c3fSmrg         draw->cur_blit_source = LOADER_DRI3_FRONT_ID;
103201e04c3fSmrg   }
103301e04c3fSmrg
103401e04c3fSmrg   dri3_flush_present_events(draw);
103501e04c3fSmrg
103601e04c3fSmrg   if (back && !draw->is_pixmap) {
103701e04c3fSmrg      dri3_fence_reset(draw->conn, back);
103801e04c3fSmrg
103901e04c3fSmrg      /* Compute when we want the frame shown by taking the last known
104001e04c3fSmrg       * successful MSC and adding in a swap interval for each outstanding swap
104101e04c3fSmrg       * request. target_msc=divisor=remainder=0 means "Use glXSwapBuffers()
104201e04c3fSmrg       * semantic"
104301e04c3fSmrg       */
104401e04c3fSmrg      ++draw->send_sbc;
104501e04c3fSmrg      if (target_msc == 0 && divisor == 0 && remainder == 0)
10467ec681f3Smrg         target_msc = draw->msc + abs(draw->swap_interval) *
104701e04c3fSmrg                      (draw->send_sbc - draw->recv_sbc);
104801e04c3fSmrg      else if (divisor == 0 && remainder > 0) {
104901e04c3fSmrg         /* From the GLX_OML_sync_control spec:
105001e04c3fSmrg          *     "If <divisor> = 0, the swap will occur when MSC becomes
105101e04c3fSmrg          *      greater than or equal to <target_msc>."
105201e04c3fSmrg          *
105301e04c3fSmrg          * Note that there's no mention of the remainder.  The Present
105401e04c3fSmrg          * extension throws BadValue for remainder != 0 with divisor == 0, so
105501e04c3fSmrg          * just drop the passed in value.
105601e04c3fSmrg          */
105701e04c3fSmrg         remainder = 0;
105801e04c3fSmrg      }
105901e04c3fSmrg
106001e04c3fSmrg      /* From the GLX_EXT_swap_control spec
106101e04c3fSmrg       * and the EGL 1.4 spec (page 53):
106201e04c3fSmrg       *
106301e04c3fSmrg       *     "If <interval> is set to a value of 0, buffer swaps are not
106401e04c3fSmrg       *      synchronized to a video frame."
106501e04c3fSmrg       *
10667ec681f3Smrg       * From GLX_EXT_swap_control_tear:
10677ec681f3Smrg       *
10687ec681f3Smrg       *     "If <interval> is negative, the minimum number of video frames
10697ec681f3Smrg       *      between buffer swaps is the absolute value of <interval>. In this
10707ec681f3Smrg       *      case, if abs(<interval>) video frames have already passed from
10717ec681f3Smrg       *      the previous swap when the swap is ready to be performed, the
10727ec681f3Smrg       *      swap will occur without synchronization to a video frame."
10737ec681f3Smrg       *
107401e04c3fSmrg       * Implementation note: It is possible to enable triple buffering
107501e04c3fSmrg       * behaviour by not using XCB_PRESENT_OPTION_ASYNC, but this should not be
107601e04c3fSmrg       * the default.
107701e04c3fSmrg       */
10787ec681f3Smrg      if (draw->swap_interval <= 0)
107901e04c3fSmrg          options |= XCB_PRESENT_OPTION_ASYNC;
108001e04c3fSmrg
108101e04c3fSmrg      /* If we need to populate the new back, but need to reuse the back
108201e04c3fSmrg       * buffer slot due to lack of local blit capabilities, make sure
108301e04c3fSmrg       * the server doesn't flip and we deadlock.
108401e04c3fSmrg       */
108501e04c3fSmrg      if (!loader_dri3_have_image_blit(draw) && draw->cur_blit_source != -1)
108601e04c3fSmrg         options |= XCB_PRESENT_OPTION_COPY;
108701e04c3fSmrg#ifdef HAVE_DRI3_MODIFIERS
108801e04c3fSmrg      if (draw->multiplanes_available)
108901e04c3fSmrg         options |= XCB_PRESENT_OPTION_SUBOPTIMAL;
109001e04c3fSmrg#endif
109101e04c3fSmrg      back->busy = 1;
109201e04c3fSmrg      back->last_swap = draw->send_sbc;
10937ec681f3Smrg
10947ec681f3Smrg      if (!draw->region) {
10957ec681f3Smrg         draw->region = xcb_generate_id(draw->conn);
10967ec681f3Smrg         xcb_xfixes_create_region(draw->conn, draw->region, 0, NULL);
10977ec681f3Smrg      }
10987ec681f3Smrg
10997ec681f3Smrg      xcb_xfixes_region_t region = 0;
11007ec681f3Smrg      xcb_rectangle_t xcb_rects[64];
11017ec681f3Smrg
11027ec681f3Smrg      if (n_rects > 0 && n_rects <= ARRAY_SIZE(xcb_rects)) {
11037ec681f3Smrg         for (int i = 0; i < n_rects; i++) {
11047ec681f3Smrg            const int *rect = &rects[i * 4];
11057ec681f3Smrg            xcb_rects[i].x = rect[0];
11067ec681f3Smrg            xcb_rects[i].y = draw->height - rect[1] - rect[3];
11077ec681f3Smrg            xcb_rects[i].width = rect[2];
11087ec681f3Smrg            xcb_rects[i].height = rect[3];
11097ec681f3Smrg         }
11107ec681f3Smrg
11117ec681f3Smrg         region = draw->region;
11127ec681f3Smrg         xcb_xfixes_set_region(draw->conn, region, n_rects, xcb_rects);
11137ec681f3Smrg      }
11147ec681f3Smrg
111501e04c3fSmrg      xcb_present_pixmap(draw->conn,
111601e04c3fSmrg                         draw->drawable,
111701e04c3fSmrg                         back->pixmap,
111801e04c3fSmrg                         (uint32_t) draw->send_sbc,
111901e04c3fSmrg                         0,                                    /* valid */
11207ec681f3Smrg                         region,                               /* update */
112101e04c3fSmrg                         0,                                    /* x_off */
112201e04c3fSmrg                         0,                                    /* y_off */
112301e04c3fSmrg                         None,                                 /* target_crtc */
112401e04c3fSmrg                         None,
112501e04c3fSmrg                         back->sync_fence,
112601e04c3fSmrg                         options,
112701e04c3fSmrg                         target_msc,
112801e04c3fSmrg                         divisor,
112901e04c3fSmrg                         remainder, 0, NULL);
113001e04c3fSmrg      ret = (int64_t) draw->send_sbc;
113101e04c3fSmrg
113201e04c3fSmrg      /* Schedule a server-side back-preserving blit if necessary.
113301e04c3fSmrg       * This happens iff all conditions below are satisfied:
113401e04c3fSmrg       * a) We have a fake front,
113501e04c3fSmrg       * b) We need to preserve the back buffer,
113601e04c3fSmrg       * c) We don't have local blit capabilities.
113701e04c3fSmrg       */
113801e04c3fSmrg      if (!loader_dri3_have_image_blit(draw) && draw->cur_blit_source != -1 &&
113901e04c3fSmrg          draw->cur_blit_source != LOADER_DRI3_BACK_ID(draw->cur_back)) {
114001e04c3fSmrg         struct loader_dri3_buffer *new_back = dri3_back_buffer(draw);
114101e04c3fSmrg         struct loader_dri3_buffer *src = draw->buffers[draw->cur_blit_source];
114201e04c3fSmrg
114301e04c3fSmrg         dri3_fence_reset(draw->conn, new_back);
114401e04c3fSmrg         dri3_copy_area(draw->conn, src->pixmap,
114501e04c3fSmrg                        new_back->pixmap,
114601e04c3fSmrg                        dri3_drawable_gc(draw),
114701e04c3fSmrg                        0, 0, 0, 0, draw->width, draw->height);
114801e04c3fSmrg         dri3_fence_trigger(draw->conn, new_back);
114901e04c3fSmrg         new_back->last_swap = src->last_swap;
115001e04c3fSmrg      }
115101e04c3fSmrg
115201e04c3fSmrg      xcb_flush(draw->conn);
115301e04c3fSmrg      if (draw->stamp)
115401e04c3fSmrg         ++(*draw->stamp);
115501e04c3fSmrg   }
115601e04c3fSmrg   mtx_unlock(&draw->mtx);
115701e04c3fSmrg
115801e04c3fSmrg   draw->ext->flush->invalidate(draw->dri_drawable);
115901e04c3fSmrg
116001e04c3fSmrg   return ret;
116101e04c3fSmrg}
116201e04c3fSmrg
116301e04c3fSmrgint
116401e04c3fSmrgloader_dri3_query_buffer_age(struct loader_dri3_drawable *draw)
116501e04c3fSmrg{
116601e04c3fSmrg   struct loader_dri3_buffer *back = dri3_find_back_alloc(draw);
116701e04c3fSmrg   int ret;
116801e04c3fSmrg
116901e04c3fSmrg   mtx_lock(&draw->mtx);
117001e04c3fSmrg   ret = (!back || back->last_swap == 0) ? 0 :
117101e04c3fSmrg      draw->send_sbc - back->last_swap + 1;
117201e04c3fSmrg   mtx_unlock(&draw->mtx);
117301e04c3fSmrg
117401e04c3fSmrg   return ret;
117501e04c3fSmrg}
117601e04c3fSmrg
117701e04c3fSmrg/** loader_dri3_open
117801e04c3fSmrg *
117901e04c3fSmrg * Wrapper around xcb_dri3_open
118001e04c3fSmrg */
118101e04c3fSmrgint
118201e04c3fSmrgloader_dri3_open(xcb_connection_t *conn,
118301e04c3fSmrg                 xcb_window_t root,
118401e04c3fSmrg                 uint32_t provider)
118501e04c3fSmrg{
118601e04c3fSmrg   xcb_dri3_open_cookie_t       cookie;
118701e04c3fSmrg   xcb_dri3_open_reply_t        *reply;
11887ec681f3Smrg   xcb_xfixes_query_version_cookie_t fixes_cookie;
11897ec681f3Smrg   xcb_xfixes_query_version_reply_t *fixes_reply;
119001e04c3fSmrg   int                          fd;
119101e04c3fSmrg
119201e04c3fSmrg   cookie = xcb_dri3_open(conn,
119301e04c3fSmrg                          root,
119401e04c3fSmrg                          provider);
119501e04c3fSmrg
119601e04c3fSmrg   reply = xcb_dri3_open_reply(conn, cookie, NULL);
119701e04c3fSmrg   if (!reply)
119801e04c3fSmrg      return -1;
119901e04c3fSmrg
120001e04c3fSmrg   if (reply->nfd != 1) {
120101e04c3fSmrg      free(reply);
120201e04c3fSmrg      return -1;
120301e04c3fSmrg   }
120401e04c3fSmrg
120501e04c3fSmrg   fd = xcb_dri3_open_reply_fds(conn, reply)[0];
120601e04c3fSmrg   free(reply);
120701e04c3fSmrg   fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
120801e04c3fSmrg
12097ec681f3Smrg   /* let the server know our xfixes level */
12107ec681f3Smrg   fixes_cookie = xcb_xfixes_query_version(conn,
12117ec681f3Smrg                                           XCB_XFIXES_MAJOR_VERSION,
12127ec681f3Smrg                                           XCB_XFIXES_MINOR_VERSION);
12137ec681f3Smrg   fixes_reply = xcb_xfixes_query_version_reply(conn, fixes_cookie, NULL);
12147ec681f3Smrg   free(fixes_reply);
12157ec681f3Smrg
121601e04c3fSmrg   return fd;
121701e04c3fSmrg}
121801e04c3fSmrg
121901e04c3fSmrgstatic uint32_t
122001e04c3fSmrgdri3_cpp_for_format(uint32_t format) {
122101e04c3fSmrg   switch (format) {
122201e04c3fSmrg   case  __DRI_IMAGE_FORMAT_R8:
122301e04c3fSmrg      return 1;
122401e04c3fSmrg   case  __DRI_IMAGE_FORMAT_RGB565:
122501e04c3fSmrg   case  __DRI_IMAGE_FORMAT_GR88:
122601e04c3fSmrg      return 2;
122701e04c3fSmrg   case  __DRI_IMAGE_FORMAT_XRGB8888:
122801e04c3fSmrg   case  __DRI_IMAGE_FORMAT_ARGB8888:
122901e04c3fSmrg   case  __DRI_IMAGE_FORMAT_ABGR8888:
123001e04c3fSmrg   case  __DRI_IMAGE_FORMAT_XBGR8888:
123101e04c3fSmrg   case  __DRI_IMAGE_FORMAT_XRGB2101010:
123201e04c3fSmrg   case  __DRI_IMAGE_FORMAT_ARGB2101010:
123301e04c3fSmrg   case  __DRI_IMAGE_FORMAT_XBGR2101010:
123401e04c3fSmrg   case  __DRI_IMAGE_FORMAT_ABGR2101010:
123501e04c3fSmrg   case  __DRI_IMAGE_FORMAT_SARGB8:
123601e04c3fSmrg   case  __DRI_IMAGE_FORMAT_SABGR8:
12377ec681f3Smrg   case  __DRI_IMAGE_FORMAT_SXRGB8:
123801e04c3fSmrg      return 4;
12397ec681f3Smrg   case __DRI_IMAGE_FORMAT_XBGR16161616F:
12407ec681f3Smrg   case __DRI_IMAGE_FORMAT_ABGR16161616F:
12417ec681f3Smrg      return 8;
124201e04c3fSmrg   case  __DRI_IMAGE_FORMAT_NONE:
124301e04c3fSmrg   default:
124401e04c3fSmrg      return 0;
124501e04c3fSmrg   }
124601e04c3fSmrg}
124701e04c3fSmrg
124801e04c3fSmrg/* Map format of render buffer to corresponding format for the linear_buffer
124901e04c3fSmrg * used for sharing with the display gpu of a Prime setup (== is_different_gpu).
125001e04c3fSmrg * Usually linear_format == format, except for depth >= 30 formats, where
125101e04c3fSmrg * different gpu vendors have different preferences wrt. color channel ordering.
125201e04c3fSmrg */
125301e04c3fSmrgstatic uint32_t
125401e04c3fSmrgdri3_linear_format_for_format(struct loader_dri3_drawable *draw, uint32_t format)
125501e04c3fSmrg{
125601e04c3fSmrg   switch (format) {
125701e04c3fSmrg      case  __DRI_IMAGE_FORMAT_XRGB2101010:
125801e04c3fSmrg      case  __DRI_IMAGE_FORMAT_XBGR2101010:
125901e04c3fSmrg         /* Different preferred formats for different hw */
126001e04c3fSmrg         if (dri3_get_red_mask_for_depth(draw, 30) == 0x3ff)
126101e04c3fSmrg            return __DRI_IMAGE_FORMAT_XBGR2101010;
126201e04c3fSmrg         else
126301e04c3fSmrg            return __DRI_IMAGE_FORMAT_XRGB2101010;
126401e04c3fSmrg
126501e04c3fSmrg      case  __DRI_IMAGE_FORMAT_ARGB2101010:
126601e04c3fSmrg      case  __DRI_IMAGE_FORMAT_ABGR2101010:
126701e04c3fSmrg         /* Different preferred formats for different hw */
126801e04c3fSmrg         if (dri3_get_red_mask_for_depth(draw, 30) == 0x3ff)
126901e04c3fSmrg            return __DRI_IMAGE_FORMAT_ABGR2101010;
127001e04c3fSmrg         else
127101e04c3fSmrg            return __DRI_IMAGE_FORMAT_ARGB2101010;
127201e04c3fSmrg
127301e04c3fSmrg      default:
127401e04c3fSmrg         return format;
127501e04c3fSmrg   }
127601e04c3fSmrg}
127701e04c3fSmrg
127801e04c3fSmrg/* the DRIimage createImage function takes __DRI_IMAGE_FORMAT codes, while
12797ec681f3Smrg * the createImageFromFds call takes DRM_FORMAT codes. To avoid
128001e04c3fSmrg * complete confusion, just deal in __DRI_IMAGE_FORMAT codes for now and
12817ec681f3Smrg * translate to DRM_FORMAT codes in the call to createImageFromFds
128201e04c3fSmrg */
128301e04c3fSmrgstatic int
128401e04c3fSmrgimage_format_to_fourcc(int format)
128501e04c3fSmrg{
128601e04c3fSmrg
12877ec681f3Smrg   /* Convert from __DRI_IMAGE_FORMAT to DRM_FORMAT (sigh) */
128801e04c3fSmrg   switch (format) {
128901e04c3fSmrg   case __DRI_IMAGE_FORMAT_SARGB8: return __DRI_IMAGE_FOURCC_SARGB8888;
129001e04c3fSmrg   case __DRI_IMAGE_FORMAT_SABGR8: return __DRI_IMAGE_FOURCC_SABGR8888;
12917ec681f3Smrg   case __DRI_IMAGE_FORMAT_SXRGB8: return __DRI_IMAGE_FOURCC_SXRGB8888;
12927ec681f3Smrg   case __DRI_IMAGE_FORMAT_RGB565: return DRM_FORMAT_RGB565;
12937ec681f3Smrg   case __DRI_IMAGE_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888;
12947ec681f3Smrg   case __DRI_IMAGE_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888;
12957ec681f3Smrg   case __DRI_IMAGE_FORMAT_ABGR8888: return DRM_FORMAT_ABGR8888;
12967ec681f3Smrg   case __DRI_IMAGE_FORMAT_XBGR8888: return DRM_FORMAT_XBGR8888;
12977ec681f3Smrg   case __DRI_IMAGE_FORMAT_XRGB2101010: return DRM_FORMAT_XRGB2101010;
12987ec681f3Smrg   case __DRI_IMAGE_FORMAT_ARGB2101010: return DRM_FORMAT_ARGB2101010;
12997ec681f3Smrg   case __DRI_IMAGE_FORMAT_XBGR2101010: return DRM_FORMAT_XBGR2101010;
13007ec681f3Smrg   case __DRI_IMAGE_FORMAT_ABGR2101010: return DRM_FORMAT_ABGR2101010;
13017ec681f3Smrg   case __DRI_IMAGE_FORMAT_XBGR16161616F: return DRM_FORMAT_XBGR16161616F;
13027ec681f3Smrg   case __DRI_IMAGE_FORMAT_ABGR16161616F: return DRM_FORMAT_ABGR16161616F;
130301e04c3fSmrg   }
130401e04c3fSmrg   return 0;
130501e04c3fSmrg}
130601e04c3fSmrg
130701e04c3fSmrg#ifdef HAVE_DRI3_MODIFIERS
130801e04c3fSmrgstatic bool
130901e04c3fSmrghas_supported_modifier(struct loader_dri3_drawable *draw, unsigned int format,
131001e04c3fSmrg                       uint64_t *modifiers, uint32_t count)
131101e04c3fSmrg{
131201e04c3fSmrg   uint64_t *supported_modifiers;
131301e04c3fSmrg   int32_t supported_modifiers_count;
131401e04c3fSmrg   bool found = false;
131501e04c3fSmrg   int i, j;
131601e04c3fSmrg
131701e04c3fSmrg   if (!draw->ext->image->queryDmaBufModifiers(draw->dri_screen,
131801e04c3fSmrg                                               format, 0, NULL, NULL,
131901e04c3fSmrg                                               &supported_modifiers_count) ||
132001e04c3fSmrg       supported_modifiers_count == 0)
132101e04c3fSmrg      return false;
132201e04c3fSmrg
132301e04c3fSmrg   supported_modifiers = malloc(supported_modifiers_count * sizeof(uint64_t));
132401e04c3fSmrg   if (!supported_modifiers)
132501e04c3fSmrg      return false;
132601e04c3fSmrg
132701e04c3fSmrg   draw->ext->image->queryDmaBufModifiers(draw->dri_screen, format,
132801e04c3fSmrg                                          supported_modifiers_count,
132901e04c3fSmrg                                          supported_modifiers, NULL,
133001e04c3fSmrg                                          &supported_modifiers_count);
133101e04c3fSmrg
133201e04c3fSmrg   for (i = 0; !found && i < supported_modifiers_count; i++) {
133301e04c3fSmrg      for (j = 0; !found && j < count; j++) {
133401e04c3fSmrg         if (supported_modifiers[i] == modifiers[j])
133501e04c3fSmrg            found = true;
133601e04c3fSmrg      }
133701e04c3fSmrg   }
133801e04c3fSmrg
133901e04c3fSmrg   free(supported_modifiers);
134001e04c3fSmrg   return found;
134101e04c3fSmrg}
134201e04c3fSmrg#endif
134301e04c3fSmrg
134401e04c3fSmrg/** loader_dri3_alloc_render_buffer
134501e04c3fSmrg *
134601e04c3fSmrg * Use the driver createImage function to construct a __DRIimage, then
134701e04c3fSmrg * get a file descriptor for that and create an X pixmap from that
134801e04c3fSmrg *
134901e04c3fSmrg * Allocate an xshmfence for synchronization
135001e04c3fSmrg */
135101e04c3fSmrgstatic struct loader_dri3_buffer *
135201e04c3fSmrgdri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
135301e04c3fSmrg                         int width, int height, int depth)
135401e04c3fSmrg{
135501e04c3fSmrg   struct loader_dri3_buffer *buffer;
13567ec681f3Smrg   __DRIimage *pixmap_buffer = NULL, *linear_buffer_display_gpu = NULL;
135701e04c3fSmrg   xcb_pixmap_t pixmap;
135801e04c3fSmrg   xcb_sync_fence_t sync_fence;
135901e04c3fSmrg   struct xshmfence *shm_fence;
136001e04c3fSmrg   int buffer_fds[4], fence_fd;
136101e04c3fSmrg   int num_planes = 0;
13627ec681f3Smrg   uint64_t *modifiers = NULL;
13637ec681f3Smrg   uint32_t count = 0;
136401e04c3fSmrg   int i, mod;
136501e04c3fSmrg   int ret;
136601e04c3fSmrg
136701e04c3fSmrg   /* Create an xshmfence object and
136801e04c3fSmrg    * prepare to send that to the X server
136901e04c3fSmrg    */
137001e04c3fSmrg
137101e04c3fSmrg   fence_fd = xshmfence_alloc_shm();
137201e04c3fSmrg   if (fence_fd < 0)
137301e04c3fSmrg      return NULL;
137401e04c3fSmrg
137501e04c3fSmrg   shm_fence = xshmfence_map_shm(fence_fd);
137601e04c3fSmrg   if (shm_fence == NULL)
137701e04c3fSmrg      goto no_shm_fence;
137801e04c3fSmrg
137901e04c3fSmrg   /* Allocate the image from the driver
138001e04c3fSmrg    */
138101e04c3fSmrg   buffer = calloc(1, sizeof *buffer);
138201e04c3fSmrg   if (!buffer)
138301e04c3fSmrg      goto no_buffer;
138401e04c3fSmrg
138501e04c3fSmrg   buffer->cpp = dri3_cpp_for_format(format);
138601e04c3fSmrg   if (!buffer->cpp)
138701e04c3fSmrg      goto no_image;
138801e04c3fSmrg
138901e04c3fSmrg   if (!draw->is_different_gpu) {
139001e04c3fSmrg#ifdef HAVE_DRI3_MODIFIERS
139101e04c3fSmrg      if (draw->multiplanes_available &&
139201e04c3fSmrg          draw->ext->image->base.version >= 15 &&
139301e04c3fSmrg          draw->ext->image->queryDmaBufModifiers &&
139401e04c3fSmrg          draw->ext->image->createImageWithModifiers) {
139501e04c3fSmrg         xcb_dri3_get_supported_modifiers_cookie_t mod_cookie;
139601e04c3fSmrg         xcb_dri3_get_supported_modifiers_reply_t *mod_reply;
139701e04c3fSmrg         xcb_generic_error_t *error = NULL;
139801e04c3fSmrg
139901e04c3fSmrg         mod_cookie = xcb_dri3_get_supported_modifiers(draw->conn,
140001e04c3fSmrg                                                       draw->window,
140101e04c3fSmrg                                                       depth, buffer->cpp * 8);
140201e04c3fSmrg         mod_reply = xcb_dri3_get_supported_modifiers_reply(draw->conn,
140301e04c3fSmrg                                                            mod_cookie,
140401e04c3fSmrg                                                            &error);
140501e04c3fSmrg         if (!mod_reply)
140601e04c3fSmrg            goto no_image;
140701e04c3fSmrg
140801e04c3fSmrg         if (mod_reply->num_window_modifiers) {
140901e04c3fSmrg            count = mod_reply->num_window_modifiers;
141001e04c3fSmrg            modifiers = malloc(count * sizeof(uint64_t));
141101e04c3fSmrg            if (!modifiers) {
141201e04c3fSmrg               free(mod_reply);
141301e04c3fSmrg               goto no_image;
141401e04c3fSmrg            }
141501e04c3fSmrg
141601e04c3fSmrg            memcpy(modifiers,
141701e04c3fSmrg                   xcb_dri3_get_supported_modifiers_window_modifiers(mod_reply),
141801e04c3fSmrg                   count * sizeof(uint64_t));
141901e04c3fSmrg
142001e04c3fSmrg            if (!has_supported_modifier(draw, image_format_to_fourcc(format),
142101e04c3fSmrg                                        modifiers, count)) {
142201e04c3fSmrg               free(modifiers);
142301e04c3fSmrg               count = 0;
142401e04c3fSmrg               modifiers = NULL;
142501e04c3fSmrg            }
142601e04c3fSmrg         }
142701e04c3fSmrg
142801e04c3fSmrg         if (mod_reply->num_screen_modifiers && modifiers == NULL) {
142901e04c3fSmrg            count = mod_reply->num_screen_modifiers;
143001e04c3fSmrg            modifiers = malloc(count * sizeof(uint64_t));
143101e04c3fSmrg            if (!modifiers) {
143201e04c3fSmrg               free(modifiers);
143301e04c3fSmrg               free(mod_reply);
143401e04c3fSmrg               goto no_image;
143501e04c3fSmrg            }
143601e04c3fSmrg
143701e04c3fSmrg            memcpy(modifiers,
143801e04c3fSmrg                   xcb_dri3_get_supported_modifiers_screen_modifiers(mod_reply),
143901e04c3fSmrg                   count * sizeof(uint64_t));
144001e04c3fSmrg         }
144101e04c3fSmrg
144201e04c3fSmrg         free(mod_reply);
144301e04c3fSmrg      }
144401e04c3fSmrg#endif
14457ec681f3Smrg      buffer->image = loader_dri_create_image(draw->dri_screen, draw->ext->image,
14467ec681f3Smrg                                              width, height, format,
14477ec681f3Smrg                                              __DRI_IMAGE_USE_SHARE |
14487ec681f3Smrg                                              __DRI_IMAGE_USE_SCANOUT |
14497ec681f3Smrg                                              __DRI_IMAGE_USE_BACKBUFFER |
14507ec681f3Smrg                                              (draw->is_protected_content ?
14517ec681f3Smrg                                               __DRI_IMAGE_USE_PROTECTED : 0),
14527ec681f3Smrg                                              modifiers, count, buffer);
14537ec681f3Smrg      free(modifiers);
145401e04c3fSmrg
145501e04c3fSmrg      pixmap_buffer = buffer->image;
145601e04c3fSmrg
145701e04c3fSmrg      if (!buffer->image)
145801e04c3fSmrg         goto no_image;
145901e04c3fSmrg   } else {
146001e04c3fSmrg      buffer->image = draw->ext->image->createImage(draw->dri_screen,
146101e04c3fSmrg                                                    width, height,
146201e04c3fSmrg                                                    format,
146301e04c3fSmrg                                                    0,
146401e04c3fSmrg                                                    buffer);
146501e04c3fSmrg
146601e04c3fSmrg      if (!buffer->image)
146701e04c3fSmrg         goto no_image;
146801e04c3fSmrg
14697ec681f3Smrg      /* if driver name is same only then dri_screen_display_gpu is set.
14707ec681f3Smrg       * This check is needed because for simplicity render gpu image extension
14717ec681f3Smrg       * is also used for display gpu.
14727ec681f3Smrg       */
14737ec681f3Smrg      if (draw->dri_screen_display_gpu) {
14747ec681f3Smrg         linear_buffer_display_gpu =
14757ec681f3Smrg           draw->ext->image->createImage(draw->dri_screen_display_gpu,
14767ec681f3Smrg                                         width, height,
14777ec681f3Smrg                                         dri3_linear_format_for_format(draw, format),
14787ec681f3Smrg                                         __DRI_IMAGE_USE_SHARE |
14797ec681f3Smrg                                         __DRI_IMAGE_USE_LINEAR |
14807ec681f3Smrg                                         __DRI_IMAGE_USE_BACKBUFFER |
14817ec681f3Smrg                                         __DRI_IMAGE_USE_SCANOUT,
14827ec681f3Smrg                                         buffer);
14837ec681f3Smrg         pixmap_buffer = linear_buffer_display_gpu;
14847ec681f3Smrg      }
148501e04c3fSmrg
14867ec681f3Smrg      if (!pixmap_buffer) {
14877ec681f3Smrg         buffer->linear_buffer =
14887ec681f3Smrg           draw->ext->image->createImage(draw->dri_screen,
14897ec681f3Smrg                                         width, height,
14907ec681f3Smrg                                         dri3_linear_format_for_format(draw, format),
14917ec681f3Smrg                                         __DRI_IMAGE_USE_SHARE |
14927ec681f3Smrg                                         __DRI_IMAGE_USE_LINEAR |
14937ec681f3Smrg                                         __DRI_IMAGE_USE_BACKBUFFER |
14947ec681f3Smrg                                         __DRI_IMAGE_USE_SCANOUT,
14957ec681f3Smrg                                         buffer);
14967ec681f3Smrg
14977ec681f3Smrg         pixmap_buffer = buffer->linear_buffer;
14987ec681f3Smrg         if (!buffer->linear_buffer) {
14997ec681f3Smrg            goto no_linear_buffer;
15007ec681f3Smrg         }
15017ec681f3Smrg      }
150201e04c3fSmrg   }
150301e04c3fSmrg
150401e04c3fSmrg   /* X want some information about the planes, so ask the image for it
150501e04c3fSmrg    */
150601e04c3fSmrg   if (!draw->ext->image->queryImage(pixmap_buffer, __DRI_IMAGE_ATTRIB_NUM_PLANES,
150701e04c3fSmrg                                     &num_planes))
150801e04c3fSmrg      num_planes = 1;
150901e04c3fSmrg
151001e04c3fSmrg   for (i = 0; i < num_planes; i++) {
151101e04c3fSmrg      __DRIimage *image = draw->ext->image->fromPlanar(pixmap_buffer, i, NULL);
151201e04c3fSmrg
151301e04c3fSmrg      if (!image) {
151401e04c3fSmrg         assert(i == 0);
151501e04c3fSmrg         image = pixmap_buffer;
151601e04c3fSmrg      }
151701e04c3fSmrg
15187ec681f3Smrg      buffer_fds[i] = -1;
15197ec681f3Smrg
152001e04c3fSmrg      ret = draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_FD,
152101e04c3fSmrg                                         &buffer_fds[i]);
152201e04c3fSmrg      ret &= draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE,
152301e04c3fSmrg                                          &buffer->strides[i]);
152401e04c3fSmrg      ret &= draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_OFFSET,
152501e04c3fSmrg                                          &buffer->offsets[i]);
152601e04c3fSmrg      if (image != pixmap_buffer)
152701e04c3fSmrg         draw->ext->image->destroyImage(image);
152801e04c3fSmrg
152901e04c3fSmrg      if (!ret)
153001e04c3fSmrg         goto no_buffer_attrib;
153101e04c3fSmrg   }
153201e04c3fSmrg
153301e04c3fSmrg   ret = draw->ext->image->queryImage(pixmap_buffer,
153401e04c3fSmrg                                     __DRI_IMAGE_ATTRIB_MODIFIER_UPPER, &mod);
153501e04c3fSmrg   buffer->modifier = (uint64_t) mod << 32;
153601e04c3fSmrg   ret &= draw->ext->image->queryImage(pixmap_buffer,
153701e04c3fSmrg                                       __DRI_IMAGE_ATTRIB_MODIFIER_LOWER, &mod);
153801e04c3fSmrg   buffer->modifier |= (uint64_t)(mod & 0xffffffff);
153901e04c3fSmrg
154001e04c3fSmrg   if (!ret)
154101e04c3fSmrg      buffer->modifier = DRM_FORMAT_MOD_INVALID;
154201e04c3fSmrg
15437ec681f3Smrg   if (draw->is_different_gpu && draw->dri_screen_display_gpu &&
15447ec681f3Smrg       linear_buffer_display_gpu) {
15457ec681f3Smrg      /* The linear buffer was created in the display GPU's vram, so we
15467ec681f3Smrg       * need to make it visible to render GPU
15477ec681f3Smrg       */
15487ec681f3Smrg      buffer->linear_buffer =
15497ec681f3Smrg         draw->ext->image->createImageFromFds(draw->dri_screen,
15507ec681f3Smrg                                              width,
15517ec681f3Smrg                                              height,
15527ec681f3Smrg                                              image_format_to_fourcc(format),
15537ec681f3Smrg                                              &buffer_fds[0], num_planes,
15547ec681f3Smrg                                              &buffer->strides[0],
15557ec681f3Smrg                                              &buffer->offsets[0],
15567ec681f3Smrg                                              buffer);
15577ec681f3Smrg      if (!buffer->linear_buffer)
15587ec681f3Smrg         goto no_buffer_attrib;
15597ec681f3Smrg
15607ec681f3Smrg      draw->ext->image->destroyImage(linear_buffer_display_gpu);
15617ec681f3Smrg   }
15627ec681f3Smrg
156301e04c3fSmrg   pixmap = xcb_generate_id(draw->conn);
156401e04c3fSmrg#ifdef HAVE_DRI3_MODIFIERS
156501e04c3fSmrg   if (draw->multiplanes_available &&
156601e04c3fSmrg       buffer->modifier != DRM_FORMAT_MOD_INVALID) {
156701e04c3fSmrg      xcb_dri3_pixmap_from_buffers(draw->conn,
156801e04c3fSmrg                                   pixmap,
156901e04c3fSmrg                                   draw->window,
157001e04c3fSmrg                                   num_planes,
157101e04c3fSmrg                                   width, height,
157201e04c3fSmrg                                   buffer->strides[0], buffer->offsets[0],
157301e04c3fSmrg                                   buffer->strides[1], buffer->offsets[1],
157401e04c3fSmrg                                   buffer->strides[2], buffer->offsets[2],
157501e04c3fSmrg                                   buffer->strides[3], buffer->offsets[3],
157601e04c3fSmrg                                   depth, buffer->cpp * 8,
157701e04c3fSmrg                                   buffer->modifier,
157801e04c3fSmrg                                   buffer_fds);
157901e04c3fSmrg   } else
158001e04c3fSmrg#endif
158101e04c3fSmrg   {
158201e04c3fSmrg      xcb_dri3_pixmap_from_buffer(draw->conn,
158301e04c3fSmrg                                  pixmap,
158401e04c3fSmrg                                  draw->drawable,
158501e04c3fSmrg                                  buffer->size,
158601e04c3fSmrg                                  width, height, buffer->strides[0],
158701e04c3fSmrg                                  depth, buffer->cpp * 8,
158801e04c3fSmrg                                  buffer_fds[0]);
158901e04c3fSmrg   }
159001e04c3fSmrg
159101e04c3fSmrg   xcb_dri3_fence_from_fd(draw->conn,
159201e04c3fSmrg                          pixmap,
159301e04c3fSmrg                          (sync_fence = xcb_generate_id(draw->conn)),
159401e04c3fSmrg                          false,
159501e04c3fSmrg                          fence_fd);
159601e04c3fSmrg
159701e04c3fSmrg   buffer->pixmap = pixmap;
159801e04c3fSmrg   buffer->own_pixmap = true;
159901e04c3fSmrg   buffer->sync_fence = sync_fence;
160001e04c3fSmrg   buffer->shm_fence = shm_fence;
160101e04c3fSmrg   buffer->width = width;
160201e04c3fSmrg   buffer->height = height;
160301e04c3fSmrg
160401e04c3fSmrg   /* Mark the buffer as idle
160501e04c3fSmrg    */
160601e04c3fSmrg   dri3_fence_set(buffer);
160701e04c3fSmrg
160801e04c3fSmrg   return buffer;
160901e04c3fSmrg
161001e04c3fSmrgno_buffer_attrib:
161101e04c3fSmrg   do {
16127ec681f3Smrg      if (buffer_fds[i] != -1)
16137ec681f3Smrg         close(buffer_fds[i]);
161401e04c3fSmrg   } while (--i >= 0);
161501e04c3fSmrg   draw->ext->image->destroyImage(pixmap_buffer);
161601e04c3fSmrgno_linear_buffer:
161701e04c3fSmrg   if (draw->is_different_gpu)
161801e04c3fSmrg      draw->ext->image->destroyImage(buffer->image);
161901e04c3fSmrgno_image:
162001e04c3fSmrg   free(buffer);
162101e04c3fSmrgno_buffer:
162201e04c3fSmrg   xshmfence_unmap_shm(shm_fence);
162301e04c3fSmrgno_shm_fence:
162401e04c3fSmrg   close(fence_fd);
162501e04c3fSmrg   return NULL;
162601e04c3fSmrg}
162701e04c3fSmrg
162801e04c3fSmrg/** loader_dri3_update_drawable
162901e04c3fSmrg *
163001e04c3fSmrg * Called the first time we use the drawable and then
163101e04c3fSmrg * after we receive present configure notify events to
163201e04c3fSmrg * track the geometry of the drawable
163301e04c3fSmrg */
163401e04c3fSmrgstatic int
163501e04c3fSmrgdri3_update_drawable(struct loader_dri3_drawable *draw)
163601e04c3fSmrg{
163701e04c3fSmrg   mtx_lock(&draw->mtx);
163801e04c3fSmrg   if (draw->first_init) {
163901e04c3fSmrg      xcb_get_geometry_cookie_t                 geom_cookie;
164001e04c3fSmrg      xcb_get_geometry_reply_t                  *geom_reply;
164101e04c3fSmrg      xcb_void_cookie_t                         cookie;
164201e04c3fSmrg      xcb_generic_error_t                       *error;
164301e04c3fSmrg      xcb_present_query_capabilities_cookie_t   present_capabilities_cookie;
164401e04c3fSmrg      xcb_present_query_capabilities_reply_t    *present_capabilities_reply;
164501e04c3fSmrg      xcb_window_t                               root_win;
164601e04c3fSmrg
164701e04c3fSmrg      draw->first_init = false;
164801e04c3fSmrg
164901e04c3fSmrg      /* Try to select for input on the window.
165001e04c3fSmrg       *
165101e04c3fSmrg       * If the drawable is a window, this will get our events
165201e04c3fSmrg       * delivered.
165301e04c3fSmrg       *
165401e04c3fSmrg       * Otherwise, we'll get a BadWindow error back from this request which
165501e04c3fSmrg       * will let us know that the drawable is a pixmap instead.
165601e04c3fSmrg       */
165701e04c3fSmrg
165801e04c3fSmrg      draw->eid = xcb_generate_id(draw->conn);
165901e04c3fSmrg      cookie =
166001e04c3fSmrg         xcb_present_select_input_checked(draw->conn, draw->eid, draw->drawable,
166101e04c3fSmrg                                          XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY |
166201e04c3fSmrg                                          XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY |
166301e04c3fSmrg                                          XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
166401e04c3fSmrg
166501e04c3fSmrg      present_capabilities_cookie =
166601e04c3fSmrg         xcb_present_query_capabilities(draw->conn, draw->drawable);
166701e04c3fSmrg
166801e04c3fSmrg      /* Create an XCB event queue to hold present events outside of the usual
166901e04c3fSmrg       * application event queue
167001e04c3fSmrg       */
167101e04c3fSmrg      draw->special_event = xcb_register_for_special_xge(draw->conn,
167201e04c3fSmrg                                                         &xcb_present_id,
167301e04c3fSmrg                                                         draw->eid,
167401e04c3fSmrg                                                         draw->stamp);
167501e04c3fSmrg      geom_cookie = xcb_get_geometry(draw->conn, draw->drawable);
167601e04c3fSmrg
167701e04c3fSmrg      geom_reply = xcb_get_geometry_reply(draw->conn, geom_cookie, NULL);
167801e04c3fSmrg
167901e04c3fSmrg      if (!geom_reply) {
168001e04c3fSmrg         mtx_unlock(&draw->mtx);
168101e04c3fSmrg         return false;
168201e04c3fSmrg      }
168301e04c3fSmrg      draw->width = geom_reply->width;
168401e04c3fSmrg      draw->height = geom_reply->height;
168501e04c3fSmrg      draw->depth = geom_reply->depth;
168601e04c3fSmrg      draw->vtable->set_drawable_size(draw, draw->width, draw->height);
168701e04c3fSmrg      root_win = geom_reply->root;
168801e04c3fSmrg
168901e04c3fSmrg      free(geom_reply);
169001e04c3fSmrg
169101e04c3fSmrg      draw->is_pixmap = false;
169201e04c3fSmrg
169301e04c3fSmrg      /* Check to see if our select input call failed. If it failed with a
169401e04c3fSmrg       * BadWindow error, then assume the drawable is a pixmap. Destroy the
169501e04c3fSmrg       * special event queue created above and mark the drawable as a pixmap
169601e04c3fSmrg       */
169701e04c3fSmrg
169801e04c3fSmrg      error = xcb_request_check(draw->conn, cookie);
169901e04c3fSmrg
170001e04c3fSmrg      present_capabilities_reply =
170101e04c3fSmrg          xcb_present_query_capabilities_reply(draw->conn,
170201e04c3fSmrg                                               present_capabilities_cookie,
170301e04c3fSmrg                                               NULL);
170401e04c3fSmrg
170501e04c3fSmrg      if (present_capabilities_reply) {
170601e04c3fSmrg         draw->present_capabilities = present_capabilities_reply->capabilities;
170701e04c3fSmrg         free(present_capabilities_reply);
170801e04c3fSmrg      } else
170901e04c3fSmrg         draw->present_capabilities = 0;
171001e04c3fSmrg
171101e04c3fSmrg      if (error) {
171201e04c3fSmrg         if (error->error_code != BadWindow) {
171301e04c3fSmrg            free(error);
171401e04c3fSmrg            mtx_unlock(&draw->mtx);
171501e04c3fSmrg            return false;
171601e04c3fSmrg         }
171701e04c3fSmrg         free(error);
171801e04c3fSmrg         draw->is_pixmap = true;
171901e04c3fSmrg         xcb_unregister_for_special_event(draw->conn, draw->special_event);
172001e04c3fSmrg         draw->special_event = NULL;
172101e04c3fSmrg      }
172201e04c3fSmrg
172301e04c3fSmrg      if (draw->is_pixmap)
172401e04c3fSmrg         draw->window = root_win;
172501e04c3fSmrg      else
172601e04c3fSmrg         draw->window = draw->drawable;
172701e04c3fSmrg   }
172801e04c3fSmrg   dri3_flush_present_events(draw);
172901e04c3fSmrg   mtx_unlock(&draw->mtx);
173001e04c3fSmrg   return true;
173101e04c3fSmrg}
173201e04c3fSmrg
173301e04c3fSmrg__DRIimage *
173401e04c3fSmrgloader_dri3_create_image(xcb_connection_t *c,
173501e04c3fSmrg                         xcb_dri3_buffer_from_pixmap_reply_t *bp_reply,
173601e04c3fSmrg                         unsigned int format,
173701e04c3fSmrg                         __DRIscreen *dri_screen,
173801e04c3fSmrg                         const __DRIimageExtension *image,
173901e04c3fSmrg                         void *loaderPrivate)
174001e04c3fSmrg{
174101e04c3fSmrg   int                                  *fds;
174201e04c3fSmrg   __DRIimage                           *image_planar, *ret;
174301e04c3fSmrg   int                                  stride, offset;
174401e04c3fSmrg
174501e04c3fSmrg   /* Get an FD for the pixmap object
174601e04c3fSmrg    */
174701e04c3fSmrg   fds = xcb_dri3_buffer_from_pixmap_reply_fds(c, bp_reply);
174801e04c3fSmrg
174901e04c3fSmrg   stride = bp_reply->stride;
175001e04c3fSmrg   offset = 0;
175101e04c3fSmrg
175201e04c3fSmrg   /* createImageFromFds creates a wrapper __DRIimage structure which
175301e04c3fSmrg    * can deal with multiple planes for things like Yuv images. So, once
175401e04c3fSmrg    * we've gotten the planar wrapper, pull the single plane out of it and
175501e04c3fSmrg    * discard the wrapper.
175601e04c3fSmrg    */
175701e04c3fSmrg   image_planar = image->createImageFromFds(dri_screen,
175801e04c3fSmrg                                            bp_reply->width,
175901e04c3fSmrg                                            bp_reply->height,
176001e04c3fSmrg                                            image_format_to_fourcc(format),
176101e04c3fSmrg                                            fds, 1,
176201e04c3fSmrg                                            &stride, &offset, loaderPrivate);
176301e04c3fSmrg   close(fds[0]);
176401e04c3fSmrg   if (!image_planar)
176501e04c3fSmrg      return NULL;
176601e04c3fSmrg
176701e04c3fSmrg   ret = image->fromPlanar(image_planar, 0, loaderPrivate);
176801e04c3fSmrg
176901e04c3fSmrg   if (!ret)
177001e04c3fSmrg      ret = image_planar;
177101e04c3fSmrg   else
177201e04c3fSmrg      image->destroyImage(image_planar);
177301e04c3fSmrg
177401e04c3fSmrg   return ret;
177501e04c3fSmrg}
177601e04c3fSmrg
177701e04c3fSmrg#ifdef HAVE_DRI3_MODIFIERS
177801e04c3fSmrg__DRIimage *
177901e04c3fSmrgloader_dri3_create_image_from_buffers(xcb_connection_t *c,
178001e04c3fSmrg                                      xcb_dri3_buffers_from_pixmap_reply_t *bp_reply,
178101e04c3fSmrg                                      unsigned int format,
178201e04c3fSmrg                                      __DRIscreen *dri_screen,
178301e04c3fSmrg                                      const __DRIimageExtension *image,
178401e04c3fSmrg                                      void *loaderPrivate)
178501e04c3fSmrg{
178601e04c3fSmrg   __DRIimage                           *ret;
178701e04c3fSmrg   int                                  *fds;
178801e04c3fSmrg   uint32_t                             *strides_in, *offsets_in;
178901e04c3fSmrg   int                                   strides[4], offsets[4];
179001e04c3fSmrg   unsigned                              error;
179101e04c3fSmrg   int                                   i;
179201e04c3fSmrg
179301e04c3fSmrg   if (bp_reply->nfd > 4)
179401e04c3fSmrg      return NULL;
179501e04c3fSmrg
179601e04c3fSmrg   fds = xcb_dri3_buffers_from_pixmap_reply_fds(c, bp_reply);
179701e04c3fSmrg   strides_in = xcb_dri3_buffers_from_pixmap_strides(bp_reply);
179801e04c3fSmrg   offsets_in = xcb_dri3_buffers_from_pixmap_offsets(bp_reply);
179901e04c3fSmrg   for (i = 0; i < bp_reply->nfd; i++) {
180001e04c3fSmrg      strides[i] = strides_in[i];
180101e04c3fSmrg      offsets[i] = offsets_in[i];
180201e04c3fSmrg   }
180301e04c3fSmrg
180401e04c3fSmrg   ret = image->createImageFromDmaBufs2(dri_screen,
180501e04c3fSmrg                                        bp_reply->width,
180601e04c3fSmrg                                        bp_reply->height,
180701e04c3fSmrg                                        image_format_to_fourcc(format),
180801e04c3fSmrg                                        bp_reply->modifier,
180901e04c3fSmrg                                        fds, bp_reply->nfd,
181001e04c3fSmrg                                        strides, offsets,
181101e04c3fSmrg                                        0, 0, 0, 0, /* UNDEFINED */
181201e04c3fSmrg                                        &error, loaderPrivate);
181301e04c3fSmrg
181401e04c3fSmrg   for (i = 0; i < bp_reply->nfd; i++)
181501e04c3fSmrg      close(fds[i]);
181601e04c3fSmrg
181701e04c3fSmrg   return ret;
181801e04c3fSmrg}
181901e04c3fSmrg#endif
182001e04c3fSmrg
182101e04c3fSmrg/** dri3_get_pixmap_buffer
182201e04c3fSmrg *
182301e04c3fSmrg * Get the DRM object for a pixmap from the X server and
182401e04c3fSmrg * wrap that with a __DRIimage structure using createImageFromFds
182501e04c3fSmrg */
182601e04c3fSmrgstatic struct loader_dri3_buffer *
182701e04c3fSmrgdri3_get_pixmap_buffer(__DRIdrawable *driDrawable, unsigned int format,
182801e04c3fSmrg                       enum loader_dri3_buffer_type buffer_type,
182901e04c3fSmrg                       struct loader_dri3_drawable *draw)
183001e04c3fSmrg{
183101e04c3fSmrg   int                                  buf_id = loader_dri3_pixmap_buf_id(buffer_type);
183201e04c3fSmrg   struct loader_dri3_buffer            *buffer = draw->buffers[buf_id];
183301e04c3fSmrg   xcb_drawable_t                       pixmap;
183401e04c3fSmrg   xcb_sync_fence_t                     sync_fence;
183501e04c3fSmrg   struct xshmfence                     *shm_fence;
183601e04c3fSmrg   int                                  width;
183701e04c3fSmrg   int                                  height;
183801e04c3fSmrg   int                                  fence_fd;
183901e04c3fSmrg   __DRIscreen                          *cur_screen;
184001e04c3fSmrg
184101e04c3fSmrg   if (buffer)
184201e04c3fSmrg      return buffer;
184301e04c3fSmrg
184401e04c3fSmrg   pixmap = draw->drawable;
184501e04c3fSmrg
184601e04c3fSmrg   buffer = calloc(1, sizeof *buffer);
184701e04c3fSmrg   if (!buffer)
184801e04c3fSmrg      goto no_buffer;
184901e04c3fSmrg
185001e04c3fSmrg   fence_fd = xshmfence_alloc_shm();
185101e04c3fSmrg   if (fence_fd < 0)
185201e04c3fSmrg      goto no_fence;
185301e04c3fSmrg   shm_fence = xshmfence_map_shm(fence_fd);
185401e04c3fSmrg   if (shm_fence == NULL) {
185501e04c3fSmrg      close (fence_fd);
185601e04c3fSmrg      goto no_fence;
185701e04c3fSmrg   }
185801e04c3fSmrg
185901e04c3fSmrg   /* Get the currently-bound screen or revert to using the drawable's screen if
186001e04c3fSmrg    * no contexts are currently bound. The latter case is at least necessary for
186101e04c3fSmrg    * obs-studio, when using Window Capture (Xcomposite) as a Source.
186201e04c3fSmrg    */
186301e04c3fSmrg   cur_screen = draw->vtable->get_dri_screen();
186401e04c3fSmrg   if (!cur_screen) {
186501e04c3fSmrg       cur_screen = draw->dri_screen;
186601e04c3fSmrg   }
186701e04c3fSmrg
186801e04c3fSmrg   xcb_dri3_fence_from_fd(draw->conn,
186901e04c3fSmrg                          pixmap,
187001e04c3fSmrg                          (sync_fence = xcb_generate_id(draw->conn)),
187101e04c3fSmrg                          false,
187201e04c3fSmrg                          fence_fd);
187301e04c3fSmrg#ifdef HAVE_DRI3_MODIFIERS
187401e04c3fSmrg   if (draw->multiplanes_available &&
187501e04c3fSmrg       draw->ext->image->base.version >= 15 &&
187601e04c3fSmrg       draw->ext->image->createImageFromDmaBufs2) {
187701e04c3fSmrg      xcb_dri3_buffers_from_pixmap_cookie_t bps_cookie;
187801e04c3fSmrg      xcb_dri3_buffers_from_pixmap_reply_t *bps_reply;
187901e04c3fSmrg
188001e04c3fSmrg      bps_cookie = xcb_dri3_buffers_from_pixmap(draw->conn, pixmap);
188101e04c3fSmrg      bps_reply = xcb_dri3_buffers_from_pixmap_reply(draw->conn, bps_cookie,
188201e04c3fSmrg                                                     NULL);
188301e04c3fSmrg      if (!bps_reply)
188401e04c3fSmrg         goto no_image;
188501e04c3fSmrg      buffer->image =
188601e04c3fSmrg         loader_dri3_create_image_from_buffers(draw->conn, bps_reply, format,
188701e04c3fSmrg                                               cur_screen, draw->ext->image,
188801e04c3fSmrg                                               buffer);
188901e04c3fSmrg      width = bps_reply->width;
189001e04c3fSmrg      height = bps_reply->height;
189101e04c3fSmrg      free(bps_reply);
189201e04c3fSmrg   } else
189301e04c3fSmrg#endif
189401e04c3fSmrg   {
189501e04c3fSmrg      xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
189601e04c3fSmrg      xcb_dri3_buffer_from_pixmap_reply_t *bp_reply;
189701e04c3fSmrg
189801e04c3fSmrg      bp_cookie = xcb_dri3_buffer_from_pixmap(draw->conn, pixmap);
189901e04c3fSmrg      bp_reply = xcb_dri3_buffer_from_pixmap_reply(draw->conn, bp_cookie, NULL);
190001e04c3fSmrg      if (!bp_reply)
190101e04c3fSmrg         goto no_image;
190201e04c3fSmrg
190301e04c3fSmrg      buffer->image = loader_dri3_create_image(draw->conn, bp_reply, format,
190401e04c3fSmrg                                               cur_screen, draw->ext->image,
190501e04c3fSmrg                                               buffer);
190601e04c3fSmrg      width = bp_reply->width;
190701e04c3fSmrg      height = bp_reply->height;
190801e04c3fSmrg      free(bp_reply);
190901e04c3fSmrg   }
191001e04c3fSmrg
191101e04c3fSmrg   if (!buffer->image)
191201e04c3fSmrg      goto no_image;
191301e04c3fSmrg
191401e04c3fSmrg   buffer->pixmap = pixmap;
191501e04c3fSmrg   buffer->own_pixmap = false;
191601e04c3fSmrg   buffer->width = width;
191701e04c3fSmrg   buffer->height = height;
191801e04c3fSmrg   buffer->shm_fence = shm_fence;
191901e04c3fSmrg   buffer->sync_fence = sync_fence;
192001e04c3fSmrg
192101e04c3fSmrg   draw->buffers[buf_id] = buffer;
192201e04c3fSmrg
192301e04c3fSmrg   return buffer;
192401e04c3fSmrg
192501e04c3fSmrgno_image:
192601e04c3fSmrg   xcb_sync_destroy_fence(draw->conn, sync_fence);
192701e04c3fSmrg   xshmfence_unmap_shm(shm_fence);
192801e04c3fSmrgno_fence:
192901e04c3fSmrg   free(buffer);
193001e04c3fSmrgno_buffer:
193101e04c3fSmrg   return NULL;
193201e04c3fSmrg}
193301e04c3fSmrg
193401e04c3fSmrg/** dri3_get_buffer
193501e04c3fSmrg *
193601e04c3fSmrg * Find a front or back buffer, allocating new ones as necessary
193701e04c3fSmrg */
193801e04c3fSmrgstatic struct loader_dri3_buffer *
193901e04c3fSmrgdri3_get_buffer(__DRIdrawable *driDrawable,
194001e04c3fSmrg                unsigned int format,
194101e04c3fSmrg                enum loader_dri3_buffer_type buffer_type,
194201e04c3fSmrg                struct loader_dri3_drawable *draw)
194301e04c3fSmrg{
194401e04c3fSmrg   struct loader_dri3_buffer *buffer;
194501e04c3fSmrg   bool fence_await = buffer_type == loader_dri3_buffer_back;
194601e04c3fSmrg   int buf_id;
194701e04c3fSmrg
194801e04c3fSmrg   if (buffer_type == loader_dri3_buffer_back) {
194901e04c3fSmrg      draw->back_format = format;
195001e04c3fSmrg
19517ec681f3Smrg      buf_id = dri3_find_back(draw, !draw->prefer_back_buffer_reuse);
195201e04c3fSmrg
195301e04c3fSmrg      if (buf_id < 0)
195401e04c3fSmrg         return NULL;
195501e04c3fSmrg   } else {
195601e04c3fSmrg      buf_id = LOADER_DRI3_FRONT_ID;
195701e04c3fSmrg   }
195801e04c3fSmrg
195901e04c3fSmrg   buffer = draw->buffers[buf_id];
196001e04c3fSmrg
196101e04c3fSmrg   /* Allocate a new buffer if there isn't an old one, if that
196201e04c3fSmrg    * old one is the wrong size, or if it's suboptimal
196301e04c3fSmrg    */
196401e04c3fSmrg   if (!buffer || buffer->width != draw->width ||
196501e04c3fSmrg       buffer->height != draw->height ||
196601e04c3fSmrg       buffer->reallocate) {
196701e04c3fSmrg      struct loader_dri3_buffer *new_buffer;
196801e04c3fSmrg
196901e04c3fSmrg      /* Allocate the new buffers
197001e04c3fSmrg       */
197101e04c3fSmrg      new_buffer = dri3_alloc_render_buffer(draw,
197201e04c3fSmrg                                                   format,
197301e04c3fSmrg                                                   draw->width,
197401e04c3fSmrg                                                   draw->height,
197501e04c3fSmrg                                                   draw->depth);
197601e04c3fSmrg      if (!new_buffer)
197701e04c3fSmrg         return NULL;
197801e04c3fSmrg
197901e04c3fSmrg      /* When resizing, copy the contents of the old buffer, waiting for that
198001e04c3fSmrg       * copy to complete using our fences before proceeding
198101e04c3fSmrg       */
198201e04c3fSmrg      if ((buffer_type == loader_dri3_buffer_back ||
198301e04c3fSmrg           (buffer_type == loader_dri3_buffer_front && draw->have_fake_front))
198401e04c3fSmrg          && buffer) {
198501e04c3fSmrg
198601e04c3fSmrg         /* Fill the new buffer with data from an old buffer */
198701e04c3fSmrg         if (!loader_dri3_blit_image(draw,
198801e04c3fSmrg                                     new_buffer->image,
198901e04c3fSmrg                                     buffer->image,
19907ec681f3Smrg                                     0, 0,
19917ec681f3Smrg                                     MIN2(buffer->width, new_buffer->width),
19927ec681f3Smrg                                     MIN2(buffer->height, new_buffer->height),
199301e04c3fSmrg                                     0, 0, 0) &&
199401e04c3fSmrg             !buffer->linear_buffer) {
199501e04c3fSmrg            dri3_fence_reset(draw->conn, new_buffer);
199601e04c3fSmrg            dri3_copy_area(draw->conn,
199701e04c3fSmrg                           buffer->pixmap,
199801e04c3fSmrg                           new_buffer->pixmap,
199901e04c3fSmrg                           dri3_drawable_gc(draw),
200001e04c3fSmrg                           0, 0, 0, 0,
200101e04c3fSmrg                           draw->width, draw->height);
200201e04c3fSmrg            dri3_fence_trigger(draw->conn, new_buffer);
200301e04c3fSmrg            fence_await = true;
200401e04c3fSmrg         }
200501e04c3fSmrg         dri3_free_render_buffer(draw, buffer);
200601e04c3fSmrg      } else if (buffer_type == loader_dri3_buffer_front) {
200701e04c3fSmrg         /* Fill the new fake front with data from a real front */
200801e04c3fSmrg         loader_dri3_swapbuffer_barrier(draw);
200901e04c3fSmrg         dri3_fence_reset(draw->conn, new_buffer);
201001e04c3fSmrg         dri3_copy_area(draw->conn,
201101e04c3fSmrg                        draw->drawable,
201201e04c3fSmrg                        new_buffer->pixmap,
201301e04c3fSmrg                        dri3_drawable_gc(draw),
201401e04c3fSmrg                        0, 0, 0, 0,
201501e04c3fSmrg                        draw->width, draw->height);
201601e04c3fSmrg         dri3_fence_trigger(draw->conn, new_buffer);
201701e04c3fSmrg
201801e04c3fSmrg         if (new_buffer->linear_buffer) {
201901e04c3fSmrg            dri3_fence_await(draw->conn, draw, new_buffer);
202001e04c3fSmrg            (void) loader_dri3_blit_image(draw,
202101e04c3fSmrg                                          new_buffer->image,
202201e04c3fSmrg                                          new_buffer->linear_buffer,
202301e04c3fSmrg                                          0, 0, draw->width, draw->height,
202401e04c3fSmrg                                          0, 0, 0);
202501e04c3fSmrg         } else
202601e04c3fSmrg            fence_await = true;
202701e04c3fSmrg      }
202801e04c3fSmrg      buffer = new_buffer;
202901e04c3fSmrg      draw->buffers[buf_id] = buffer;
203001e04c3fSmrg   }
203101e04c3fSmrg
203201e04c3fSmrg   if (fence_await)
203301e04c3fSmrg      dri3_fence_await(draw->conn, draw, buffer);
203401e04c3fSmrg
203501e04c3fSmrg   /*
203601e04c3fSmrg    * Do we need to preserve the content of a previous buffer?
203701e04c3fSmrg    *
203801e04c3fSmrg    * Note that this blit is needed only to avoid a wait for a buffer that
203901e04c3fSmrg    * is currently in the flip chain or being scanned out from. That's really
204001e04c3fSmrg    * a tradeoff. If we're ok with the wait we can reduce the number of back
204101e04c3fSmrg    * buffers to 1 for SWAP_EXCHANGE, and 1 for SWAP_COPY,
204201e04c3fSmrg    * but in the latter case we must disallow page-flipping.
204301e04c3fSmrg    */
204401e04c3fSmrg   if (buffer_type == loader_dri3_buffer_back &&
204501e04c3fSmrg       draw->cur_blit_source != -1 &&
204601e04c3fSmrg       draw->buffers[draw->cur_blit_source] &&
204701e04c3fSmrg       buffer != draw->buffers[draw->cur_blit_source]) {
204801e04c3fSmrg
204901e04c3fSmrg      struct loader_dri3_buffer *source = draw->buffers[draw->cur_blit_source];
205001e04c3fSmrg
205101e04c3fSmrg      /* Avoid flushing here. Will propably do good for tiling hardware. */
205201e04c3fSmrg      (void) loader_dri3_blit_image(draw,
205301e04c3fSmrg                                    buffer->image,
205401e04c3fSmrg                                    source->image,
205501e04c3fSmrg                                    0, 0, draw->width, draw->height,
205601e04c3fSmrg                                    0, 0, 0);
205701e04c3fSmrg      buffer->last_swap = source->last_swap;
205801e04c3fSmrg      draw->cur_blit_source = -1;
205901e04c3fSmrg   }
206001e04c3fSmrg   /* Return the requested buffer */
206101e04c3fSmrg   return buffer;
206201e04c3fSmrg}
206301e04c3fSmrg
206401e04c3fSmrg/** dri3_free_buffers
206501e04c3fSmrg *
206601e04c3fSmrg * Free the front bufffer or all of the back buffers. Used
206701e04c3fSmrg * when the application changes which buffers it needs
206801e04c3fSmrg */
206901e04c3fSmrgstatic void
207001e04c3fSmrgdri3_free_buffers(__DRIdrawable *driDrawable,
207101e04c3fSmrg                  enum loader_dri3_buffer_type buffer_type,
207201e04c3fSmrg                  struct loader_dri3_drawable *draw)
207301e04c3fSmrg{
207401e04c3fSmrg   struct loader_dri3_buffer *buffer;
207501e04c3fSmrg   int first_id;
207601e04c3fSmrg   int n_id;
207701e04c3fSmrg   int buf_id;
207801e04c3fSmrg
207901e04c3fSmrg   switch (buffer_type) {
208001e04c3fSmrg   case loader_dri3_buffer_back:
208101e04c3fSmrg      first_id = LOADER_DRI3_BACK_ID(0);
208201e04c3fSmrg      n_id = LOADER_DRI3_MAX_BACK;
208301e04c3fSmrg      draw->cur_blit_source = -1;
208401e04c3fSmrg      break;
208501e04c3fSmrg   case loader_dri3_buffer_front:
208601e04c3fSmrg      first_id = LOADER_DRI3_FRONT_ID;
208701e04c3fSmrg      /* Don't free a fake front holding new backbuffer content. */
208801e04c3fSmrg      n_id = (draw->cur_blit_source == LOADER_DRI3_FRONT_ID) ? 0 : 1;
20897ec681f3Smrg      break;
20907ec681f3Smrg   default:
20917ec681f3Smrg      unreachable("unhandled buffer_type");
209201e04c3fSmrg   }
209301e04c3fSmrg
209401e04c3fSmrg   for (buf_id = first_id; buf_id < first_id + n_id; buf_id++) {
209501e04c3fSmrg      buffer = draw->buffers[buf_id];
209601e04c3fSmrg      if (buffer) {
209701e04c3fSmrg         dri3_free_render_buffer(draw, buffer);
209801e04c3fSmrg         draw->buffers[buf_id] = NULL;
209901e04c3fSmrg      }
210001e04c3fSmrg   }
210101e04c3fSmrg}
210201e04c3fSmrg
210301e04c3fSmrg/** loader_dri3_get_buffers
210401e04c3fSmrg *
210501e04c3fSmrg * The published buffer allocation API.
210601e04c3fSmrg * Returns all of the necessary buffers, allocating
210701e04c3fSmrg * as needed.
210801e04c3fSmrg */
210901e04c3fSmrgint
211001e04c3fSmrgloader_dri3_get_buffers(__DRIdrawable *driDrawable,
211101e04c3fSmrg                        unsigned int format,
211201e04c3fSmrg                        uint32_t *stamp,
211301e04c3fSmrg                        void *loaderPrivate,
211401e04c3fSmrg                        uint32_t buffer_mask,
211501e04c3fSmrg                        struct __DRIimageList *buffers)
211601e04c3fSmrg{
211701e04c3fSmrg   struct loader_dri3_drawable *draw = loaderPrivate;
211801e04c3fSmrg   struct loader_dri3_buffer   *front, *back;
211901e04c3fSmrg   int buf_id;
212001e04c3fSmrg
212101e04c3fSmrg   buffers->image_mask = 0;
212201e04c3fSmrg   buffers->front = NULL;
212301e04c3fSmrg   buffers->back = NULL;
212401e04c3fSmrg
212501e04c3fSmrg   front = NULL;
212601e04c3fSmrg   back = NULL;
212701e04c3fSmrg
212801e04c3fSmrg   if (!dri3_update_drawable(draw))
212901e04c3fSmrg      return false;
213001e04c3fSmrg
21317ec681f3Smrg   dri3_update_max_num_back(draw);
213201e04c3fSmrg
213301e04c3fSmrg   /* Free no longer needed back buffers */
21347ec681f3Smrg   for (buf_id = draw->cur_num_back; buf_id < LOADER_DRI3_MAX_BACK; buf_id++) {
213501e04c3fSmrg      if (draw->cur_blit_source != buf_id && draw->buffers[buf_id]) {
213601e04c3fSmrg         dri3_free_render_buffer(draw, draw->buffers[buf_id]);
213701e04c3fSmrg         draw->buffers[buf_id] = NULL;
213801e04c3fSmrg      }
213901e04c3fSmrg   }
214001e04c3fSmrg
214101e04c3fSmrg   /* pixmaps always have front buffers.
214201e04c3fSmrg    * Exchange swaps also mandate fake front buffers.
214301e04c3fSmrg    */
214401e04c3fSmrg   if (draw->is_pixmap || draw->swap_method == __DRI_ATTRIB_SWAP_EXCHANGE)
214501e04c3fSmrg      buffer_mask |= __DRI_IMAGE_BUFFER_FRONT;
214601e04c3fSmrg
214701e04c3fSmrg   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
214801e04c3fSmrg      /* All pixmaps are owned by the server gpu.
214901e04c3fSmrg       * When we use a different gpu, we can't use the pixmap
215001e04c3fSmrg       * as buffer since it is potentially tiled a way
215101e04c3fSmrg       * our device can't understand. In this case, use
215201e04c3fSmrg       * a fake front buffer. Hopefully the pixmap
215301e04c3fSmrg       * content will get synced with the fake front
215401e04c3fSmrg       * buffer.
215501e04c3fSmrg       */
215601e04c3fSmrg      if (draw->is_pixmap && !draw->is_different_gpu)
215701e04c3fSmrg         front = dri3_get_pixmap_buffer(driDrawable,
215801e04c3fSmrg                                               format,
215901e04c3fSmrg                                               loader_dri3_buffer_front,
216001e04c3fSmrg                                               draw);
216101e04c3fSmrg      else
216201e04c3fSmrg         front = dri3_get_buffer(driDrawable,
216301e04c3fSmrg                                        format,
216401e04c3fSmrg                                        loader_dri3_buffer_front,
216501e04c3fSmrg                                        draw);
216601e04c3fSmrg
216701e04c3fSmrg      if (!front)
216801e04c3fSmrg         return false;
216901e04c3fSmrg   } else {
217001e04c3fSmrg      dri3_free_buffers(driDrawable, loader_dri3_buffer_front, draw);
217101e04c3fSmrg      draw->have_fake_front = 0;
217201e04c3fSmrg   }
217301e04c3fSmrg
217401e04c3fSmrg   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
217501e04c3fSmrg      back = dri3_get_buffer(driDrawable,
217601e04c3fSmrg                                    format,
217701e04c3fSmrg                                    loader_dri3_buffer_back,
217801e04c3fSmrg                                    draw);
217901e04c3fSmrg      if (!back)
218001e04c3fSmrg         return false;
218101e04c3fSmrg      draw->have_back = 1;
218201e04c3fSmrg   } else {
218301e04c3fSmrg      dri3_free_buffers(driDrawable, loader_dri3_buffer_back, draw);
218401e04c3fSmrg      draw->have_back = 0;
218501e04c3fSmrg   }
218601e04c3fSmrg
218701e04c3fSmrg   if (front) {
218801e04c3fSmrg      buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
218901e04c3fSmrg      buffers->front = front->image;
219001e04c3fSmrg      draw->have_fake_front = draw->is_different_gpu || !draw->is_pixmap;
219101e04c3fSmrg   }
219201e04c3fSmrg
219301e04c3fSmrg   if (back) {
219401e04c3fSmrg      buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
219501e04c3fSmrg      buffers->back = back->image;
219601e04c3fSmrg   }
219701e04c3fSmrg
219801e04c3fSmrg   draw->stamp = stamp;
219901e04c3fSmrg
220001e04c3fSmrg   return true;
220101e04c3fSmrg}
220201e04c3fSmrg
220301e04c3fSmrg/** loader_dri3_update_drawable_geometry
220401e04c3fSmrg *
220501e04c3fSmrg * Get the current drawable geometry.
220601e04c3fSmrg */
220701e04c3fSmrgvoid
220801e04c3fSmrgloader_dri3_update_drawable_geometry(struct loader_dri3_drawable *draw)
220901e04c3fSmrg{
221001e04c3fSmrg   xcb_get_geometry_cookie_t geom_cookie;
221101e04c3fSmrg   xcb_get_geometry_reply_t *geom_reply;
221201e04c3fSmrg
221301e04c3fSmrg   geom_cookie = xcb_get_geometry(draw->conn, draw->drawable);
221401e04c3fSmrg
221501e04c3fSmrg   geom_reply = xcb_get_geometry_reply(draw->conn, geom_cookie, NULL);
221601e04c3fSmrg
221701e04c3fSmrg   if (geom_reply) {
221801e04c3fSmrg      draw->width = geom_reply->width;
221901e04c3fSmrg      draw->height = geom_reply->height;
222001e04c3fSmrg      draw->vtable->set_drawable_size(draw, draw->width, draw->height);
222101e04c3fSmrg      draw->ext->flush->invalidate(draw->dri_drawable);
222201e04c3fSmrg
222301e04c3fSmrg      free(geom_reply);
222401e04c3fSmrg   }
222501e04c3fSmrg}
222601e04c3fSmrg
222701e04c3fSmrg
222801e04c3fSmrg/**
222901e04c3fSmrg * Make sure the server has flushed all pending swap buffers to hardware
223001e04c3fSmrg * for this drawable. Ideally we'd want to send an X protocol request to
223101e04c3fSmrg * have the server block our connection until the swaps are complete. That
223201e04c3fSmrg * would avoid the potential round-trip here.
223301e04c3fSmrg */
223401e04c3fSmrgvoid
223501e04c3fSmrgloader_dri3_swapbuffer_barrier(struct loader_dri3_drawable *draw)
223601e04c3fSmrg{
223701e04c3fSmrg   int64_t ust, msc, sbc;
223801e04c3fSmrg
223901e04c3fSmrg   (void) loader_dri3_wait_for_sbc(draw, 0, &ust, &msc, &sbc);
224001e04c3fSmrg}
224101e04c3fSmrg
224201e04c3fSmrg/**
224301e04c3fSmrg * Perform any cleanup associated with a close screen operation.
224401e04c3fSmrg * \param dri_screen[in,out] Pointer to __DRIscreen about to be closed.
224501e04c3fSmrg *
224601e04c3fSmrg * This function destroys the screen's cached swap context if any.
224701e04c3fSmrg */
224801e04c3fSmrgvoid
224901e04c3fSmrgloader_dri3_close_screen(__DRIscreen *dri_screen)
225001e04c3fSmrg{
225101e04c3fSmrg   mtx_lock(&blit_context.mtx);
225201e04c3fSmrg   if (blit_context.ctx && blit_context.cur_screen == dri_screen) {
225301e04c3fSmrg      blit_context.core->destroyContext(blit_context.ctx);
225401e04c3fSmrg      blit_context.ctx = NULL;
225501e04c3fSmrg   }
225601e04c3fSmrg   mtx_unlock(&blit_context.mtx);
225701e04c3fSmrg}
225801e04c3fSmrg
225901e04c3fSmrg/**
226001e04c3fSmrg * Find a backbuffer slot - potentially allocating a back buffer
226101e04c3fSmrg *
226201e04c3fSmrg * \param draw[in,out]  Pointer to the drawable for which to find back.
226301e04c3fSmrg * \return Pointer to a new back buffer or NULL if allocation failed or was
226401e04c3fSmrg * not mandated.
226501e04c3fSmrg *
226601e04c3fSmrg * Find a potentially new back buffer, and if it's not been allocated yet and
226701e04c3fSmrg * in addition needs initializing, then try to allocate and initialize it.
226801e04c3fSmrg */
226901e04c3fSmrg#include <stdio.h>
227001e04c3fSmrgstatic struct loader_dri3_buffer *
227101e04c3fSmrgdri3_find_back_alloc(struct loader_dri3_drawable *draw)
227201e04c3fSmrg{
227301e04c3fSmrg   struct loader_dri3_buffer *back;
227401e04c3fSmrg   int id;
227501e04c3fSmrg
22767ec681f3Smrg   id = dri3_find_back(draw, false);
227701e04c3fSmrg   if (id < 0)
227801e04c3fSmrg      return NULL;
227901e04c3fSmrg
228001e04c3fSmrg   back = draw->buffers[id];
228101e04c3fSmrg   /* Allocate a new back if we haven't got one */
228201e04c3fSmrg   if (!back && draw->back_format != __DRI_IMAGE_FORMAT_NONE &&
228301e04c3fSmrg       dri3_update_drawable(draw))
228401e04c3fSmrg      back = dri3_alloc_render_buffer(draw, draw->back_format,
228501e04c3fSmrg                                      draw->width, draw->height, draw->depth);
228601e04c3fSmrg
228701e04c3fSmrg   if (!back)
228801e04c3fSmrg      return NULL;
228901e04c3fSmrg
229001e04c3fSmrg   draw->buffers[id] = back;
229101e04c3fSmrg
229201e04c3fSmrg   /* If necessary, prefill the back with data according to swap_method mode. */
229301e04c3fSmrg   if (draw->cur_blit_source != -1 &&
229401e04c3fSmrg       draw->buffers[draw->cur_blit_source] &&
229501e04c3fSmrg       back != draw->buffers[draw->cur_blit_source]) {
229601e04c3fSmrg      struct loader_dri3_buffer *source = draw->buffers[draw->cur_blit_source];
229701e04c3fSmrg
229801e04c3fSmrg      dri3_fence_await(draw->conn, draw, source);
229901e04c3fSmrg      dri3_fence_await(draw->conn, draw, back);
230001e04c3fSmrg      (void) loader_dri3_blit_image(draw,
230101e04c3fSmrg                                    back->image,
230201e04c3fSmrg                                    source->image,
230301e04c3fSmrg                                    0, 0, draw->width, draw->height,
230401e04c3fSmrg                                    0, 0, 0);
230501e04c3fSmrg      back->last_swap = source->last_swap;
230601e04c3fSmrg      draw->cur_blit_source = -1;
230701e04c3fSmrg   }
230801e04c3fSmrg
230901e04c3fSmrg   return back;
231001e04c3fSmrg}
2311