+/**
+ * A cached blit context.
+ */
+struct loader_dri3_blit_context {
+ mtx_t mtx;
+ __DRIcontext *ctx;
+ __DRIscreen *cur_screen;
+ const __DRIcoreExtension *core;
+};
+
+/* For simplicity we maintain the cache only for a single screen at a time */
+static struct loader_dri3_blit_context blit_context = {
+ _MTX_INITIALIZER_NP, NULL
+};
+
+static void
+dri3_flush_present_events(struct loader_dri3_drawable *draw);
+
+static struct loader_dri3_buffer *
+dri3_find_back_alloc(struct loader_dri3_drawable *draw);
+
+static xcb_screen_t *
+get_screen_for_root(xcb_connection_t *conn, xcb_window_t root)
+{
+ xcb_screen_iterator_t screen_iter =
+ xcb_setup_roots_iterator(xcb_get_setup(conn));
+
+ for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {
+ if (screen_iter.data->root == root)
+ return screen_iter.data;
+ }
+
+ return NULL;
+}
+
+static xcb_visualtype_t *
+get_xcb_visualtype_for_depth(struct loader_dri3_drawable *draw, int depth)
+{
+ xcb_visualtype_iterator_t visual_iter;
+ xcb_screen_t *screen = draw->screen;
+ xcb_depth_iterator_t depth_iter;
+
+ if (!screen)
+ return NULL;
+
+ depth_iter = xcb_screen_allowed_depths_iterator(screen);
+ for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
+ if (depth_iter.data->depth != depth)
+ continue;
+
+ visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
+ if (visual_iter.rem)
+ return visual_iter.data;
+ }
+
+ return NULL;
+}
+
+/* Sets the adaptive sync window property state. */
+static void
+set_adaptive_sync_property(xcb_connection_t *conn, xcb_drawable_t drawable,
+ uint32_t state)
+{
+ static char const name[] = "_VARIABLE_REFRESH";
+ xcb_intern_atom_cookie_t cookie;
+ xcb_intern_atom_reply_t* reply;
+ xcb_void_cookie_t check;
+
+ cookie = xcb_intern_atom(conn, 0, strlen(name), name);
+ reply = xcb_intern_atom_reply(conn, cookie, NULL);
+ if (reply == NULL)
+ return;
+
+ if (state)
+ check = xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE,
+ drawable, reply->atom,
+ XCB_ATOM_CARDINAL, 32, 1, &state);
+ else
+ check = xcb_delete_property_checked(conn, drawable, reply->atom);
+
+ xcb_discard_reply(conn, check.sequence);
+ free(reply);
+}
+
+/* Get red channel mask for given drawable at given depth. */
+static unsigned int
+dri3_get_red_mask_for_depth(struct loader_dri3_drawable *draw, int depth)
+{
+ xcb_visualtype_t *visual = get_xcb_visualtype_for_depth(draw, depth);
+
+ if (visual)
+ return visual->red_mask;
+
+ return 0;
+}
+
+/**
+ * Do we have blit functionality in the image blit extension?
+ *
+ * \param draw[in] The drawable intended to blit from / to.
+ * \return true if we have blit functionality. false otherwise.
+ */
+static bool loader_dri3_have_image_blit(const struct loader_dri3_drawable *draw)
+{
+ return draw->ext->image->base.version >= 9 &&
+ draw->ext->image->blitImage != NULL;
+}
+
+/**
+ * Get and lock (for use with the current thread) a dri context associated
+ * with the drawable's dri screen. The context is intended to be used with
+ * the dri image extension's blitImage method.
+ *
+ * \param draw[in] Pointer to the drawable whose dri screen we want a
+ * dri context for.
+ * \return A dri context or NULL if context creation failed.
+ *
+ * When the caller is done with the context (even if the context returned was
+ * NULL), the caller must call loader_dri3_blit_context_put.
+ */
+static __DRIcontext *
+loader_dri3_blit_context_get(struct loader_dri3_drawable *draw)
+{
+ mtx_lock(&blit_context.mtx);
+
+ if (blit_context.ctx && blit_context.cur_screen != draw->dri_screen) {
+ blit_context.core->destroyContext(blit_context.ctx);
+ blit_context.ctx = NULL;
+ }
+
+ if (!blit_context.ctx) {
+ blit_context.ctx = draw->ext->core->createNewContext(draw->dri_screen,
+ NULL, NULL, NULL);
+ blit_context.cur_screen = draw->dri_screen;
+ blit_context.core = draw->ext->core;
+ }
+
+ return blit_context.ctx;
+}
+
+/**
+ * Release (for use with other threads) a dri context previously obtained using
+ * loader_dri3_blit_context_get.
+ */
+static void
+loader_dri3_blit_context_put(void)
+{
+ mtx_unlock(&blit_context.mtx);
+}
+
+/**
+ * Blit (parts of) the contents of a DRI image to another dri image
+ *
+ * \param draw[in] The drawable which owns the images.
+ * \param dst[in] The destination image.
+ * \param src[in] The source image.
+ * \param dstx0[in] Start destination coordinate.
+ * \param dsty0[in] Start destination coordinate.
+ * \param width[in] Blit width.
+ * \param height[in] Blit height.
+ * \param srcx0[in] Start source coordinate.
+ * \param srcy0[in] Start source coordinate.
+ * \param flush_flag[in] Image blit flush flag.
+ * \return true iff successful.
+ */
+static bool
+loader_dri3_blit_image(struct loader_dri3_drawable *draw,
+ __DRIimage *dst, __DRIimage *src,
+ int dstx0, int dsty0, int width, int height,
+ int srcx0, int srcy0, int flush_flag)
+{
+ __DRIcontext *dri_context;
+ bool use_blit_context = false;
+
+ if (!loader_dri3_have_image_blit(draw))
+ return false;
+
+ dri_context = draw->vtable->get_dri_context(draw);
+
+ if (!dri_context || !draw->vtable->in_current_context(draw)) {
+ dri_context = loader_dri3_blit_context_get(draw);
+ use_blit_context = true;
+ flush_flag |= __BLIT_FLAG_FLUSH;
+ }
+
+ if (dri_context)
+ draw->ext->image->blitImage(dri_context, dst, src, dstx0, dsty0,
+ width, height, srcx0, srcy0,
+ width, height, flush_flag);
+
+ if (use_blit_context)
+ loader_dri3_blit_context_put();
+
+ return dri_context != NULL;
+}
+