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