sna_dri2.c revision 42542f5f
142542f5fSchristos/************************************************************************** 242542f5fSchristos 342542f5fSchristosCopyright 2001 VA Linux Systems Inc., Fremont, California. 442542f5fSchristosCopyright © 2002 by David Dawes 542542f5fSchristos 642542f5fSchristosAll Rights Reserved. 742542f5fSchristos 842542f5fSchristosPermission is hereby granted, free of charge, to any person obtaining a 942542f5fSchristoscopy of this software and associated documentation files (the "Software"), 1042542f5fSchristosto deal in the Software without restriction, including without limitation 1142542f5fSchristoson the rights to use, copy, modify, merge, publish, distribute, sub 1242542f5fSchristoslicense, and/or sell copies of the Software, and to permit persons to whom 1342542f5fSchristosthe Software is furnished to do so, subject to the following conditions: 1442542f5fSchristos 1542542f5fSchristosThe above copyright notice and this permission notice (including the next 1642542f5fSchristosparagraph) shall be included in all copies or substantial portions of the 1742542f5fSchristosSoftware. 1842542f5fSchristos 1942542f5fSchristosTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2042542f5fSchristosIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2142542f5fSchristosFITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 2242542f5fSchristosATI, VA LINUX SYSTEMS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 2342542f5fSchristosDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2442542f5fSchristosOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 2542542f5fSchristosUSE OR OTHER DEALINGS IN THE SOFTWARE. 2642542f5fSchristos 2742542f5fSchristos**************************************************************************/ 2842542f5fSchristos 2942542f5fSchristos/* 3042542f5fSchristos * Authors: Jeff Hartmann <jhartmann@valinux.com> 3142542f5fSchristos * David Dawes <dawes@xfree86.org> 3242542f5fSchristos * Keith Whitwell <keith@tungstengraphics.com> 3342542f5fSchristos */ 3442542f5fSchristos 3542542f5fSchristos#ifdef HAVE_CONFIG_H 3642542f5fSchristos#include "config.h" 3742542f5fSchristos#endif 3842542f5fSchristos 3942542f5fSchristos#include <errno.h> 4042542f5fSchristos#include <time.h> 4142542f5fSchristos#include <string.h> 4242542f5fSchristos#include <unistd.h> 4342542f5fSchristos#include <poll.h> 4442542f5fSchristos 4542542f5fSchristos#include "sna.h" 4642542f5fSchristos#include "intel_options.h" 4742542f5fSchristos 4842542f5fSchristos#include <xf86drm.h> 4942542f5fSchristos#include <i915_drm.h> 5042542f5fSchristos#include <dri2.h> 5142542f5fSchristos#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,12,99,901,0) && defined(COMPOSITE) 5242542f5fSchristos#include <compositeext.h> 5342542f5fSchristos#define CHECK_FOR_COMPOSITOR 5442542f5fSchristos#endif 5542542f5fSchristos 5642542f5fSchristos#define DBG_CAN_FLIP 1 5742542f5fSchristos#define DBG_CAN_XCHG 1 5842542f5fSchristos 5942542f5fSchristos#define DBG_FORCE_COPY -1 /* KGEM_BLT or KGEM_3D */ 6042542f5fSchristos 6142542f5fSchristos#if DRI2INFOREC_VERSION < 2 6242542f5fSchristos#error DRI2 version supported by the Xserver is too old 6342542f5fSchristos#endif 6442542f5fSchristos 6542542f5fSchristosstatic inline struct kgem_bo *ref(struct kgem_bo *bo) 6642542f5fSchristos{ 6742542f5fSchristos assert(bo->refcnt); 6842542f5fSchristos bo->refcnt++; 6942542f5fSchristos return bo; 7042542f5fSchristos} 7142542f5fSchristos 7242542f5fSchristosstatic inline void unref(struct kgem_bo *bo) 7342542f5fSchristos{ 7442542f5fSchristos assert(bo->refcnt > 1); 7542542f5fSchristos bo->refcnt--; 7642542f5fSchristos} 7742542f5fSchristos 7842542f5fSchristosstruct sna_dri2_private { 7942542f5fSchristos PixmapPtr pixmap; 8042542f5fSchristos struct kgem_bo *bo; 8142542f5fSchristos DRI2Buffer2Ptr proxy; 8242542f5fSchristos bool stale; 8342542f5fSchristos uint32_t size; 8442542f5fSchristos int refcnt; 8542542f5fSchristos}; 8642542f5fSchristos 8742542f5fSchristosstatic inline struct sna_dri2_private * 8842542f5fSchristosget_private(void *buffer) 8942542f5fSchristos{ 9042542f5fSchristos return (struct sna_dri2_private *)((DRI2Buffer2Ptr)buffer+1); 9142542f5fSchristos} 9242542f5fSchristos 9342542f5fSchristos#if DRI2INFOREC_VERSION >= 4 9442542f5fSchristosenum event_type { 9542542f5fSchristos WAITMSC = 0, 9642542f5fSchristos SWAP, 9742542f5fSchristos SWAP_WAIT, 9842542f5fSchristos SWAP_THROTTLE, 9942542f5fSchristos FLIP, 10042542f5fSchristos FLIP_THROTTLE, 10142542f5fSchristos FLIP_COMPLETE, 10242542f5fSchristos FLIP_ASYNC, 10342542f5fSchristos}; 10442542f5fSchristos 10542542f5fSchristosstruct dri_bo { 10642542f5fSchristos struct list link; 10742542f5fSchristos struct kgem_bo *bo; 10842542f5fSchristos uint32_t name; 10942542f5fSchristos}; 11042542f5fSchristos 11142542f5fSchristosstruct sna_dri2_event { 11242542f5fSchristos DrawablePtr draw; 11342542f5fSchristos ClientPtr client; 11442542f5fSchristos enum event_type type; 11542542f5fSchristos xf86CrtcPtr crtc; 11642542f5fSchristos int pipe; 11742542f5fSchristos bool queued; 11842542f5fSchristos 11942542f5fSchristos /* for swaps & flips only */ 12042542f5fSchristos DRI2SwapEventPtr event_complete; 12142542f5fSchristos void *event_data; 12242542f5fSchristos DRI2BufferPtr front; 12342542f5fSchristos DRI2BufferPtr back; 12442542f5fSchristos struct kgem_bo *bo; 12542542f5fSchristos 12642542f5fSchristos struct sna_dri2_event *chain; 12742542f5fSchristos 12842542f5fSchristos struct list cache; 12942542f5fSchristos struct list link; 13042542f5fSchristos 13142542f5fSchristos int mode; 13242542f5fSchristos}; 13342542f5fSchristos 13442542f5fSchristosstatic void sna_dri2_flip_event(struct sna *sna, 13542542f5fSchristos struct sna_dri2_event *flip); 13642542f5fSchristos 13742542f5fSchristosstatic void 13842542f5fSchristossna_dri2_get_back(struct sna *sna, 13942542f5fSchristos DrawablePtr draw, 14042542f5fSchristos DRI2BufferPtr back, 14142542f5fSchristos struct sna_dri2_event *info) 14242542f5fSchristos{ 14342542f5fSchristos struct kgem_bo *bo; 14442542f5fSchristos uint32_t name; 14542542f5fSchristos bool reuse; 14642542f5fSchristos 14742542f5fSchristos DBG(("%s: draw size=%dx%d, buffer size=%dx%d\n", 14842542f5fSchristos __FUNCTION__, draw->width, draw->height, 14942542f5fSchristos get_private(back)->size & 0xffff, get_private(back)->size >> 16)); 15042542f5fSchristos reuse = (draw->height << 16 | draw->width) == get_private(back)->size; 15142542f5fSchristos if (reuse) { 15242542f5fSchristos bo = get_private(back)->bo; 15342542f5fSchristos assert(bo->refcnt); 15442542f5fSchristos DBG(("%s: back buffer handle=%d, scanout?=%d, refcnt=%d\n", 15542542f5fSchristos __FUNCTION__, bo->handle, bo->active_scanout, get_private(back)->refcnt)); 15642542f5fSchristos if (bo->active_scanout == 0) { 15742542f5fSchristos DBG(("%s: reuse unattached back\n", __FUNCTION__)); 15842542f5fSchristos get_private(back)->stale = false; 15942542f5fSchristos return; 16042542f5fSchristos } 16142542f5fSchristos } 16242542f5fSchristos 16342542f5fSchristos bo = NULL; 16442542f5fSchristos if (info) { 16542542f5fSchristos struct dri_bo *c; 16642542f5fSchristos list_for_each_entry(c, &info->cache, link) { 16742542f5fSchristos if (c->bo && c->bo->scanout == 0) { 16842542f5fSchristos bo = c->bo; 16942542f5fSchristos name = c->name; 17042542f5fSchristos DBG(("%s: reuse cache handle=%d\n", __FUNCTION__, bo->handle)); 17142542f5fSchristos list_move_tail(&c->link, &info->cache); 17242542f5fSchristos c->bo = NULL; 17342542f5fSchristos } 17442542f5fSchristos } 17542542f5fSchristos } 17642542f5fSchristos if (bo == NULL) { 17742542f5fSchristos DBG(("%s: allocating new backbuffer\n", __FUNCTION__)); 17842542f5fSchristos bo = kgem_create_2d(&sna->kgem, 17942542f5fSchristos draw->width, draw->height, draw->bitsPerPixel, 18042542f5fSchristos get_private(back)->bo->tiling, 18142542f5fSchristos get_private(back)->bo->scanout ? CREATE_SCANOUT : 0); 18242542f5fSchristos if (bo == NULL) 18342542f5fSchristos return; 18442542f5fSchristos 18542542f5fSchristos name = kgem_bo_flink(&sna->kgem, bo); 18642542f5fSchristos if (name == 0) { 18742542f5fSchristos kgem_bo_destroy(&sna->kgem, bo); 18842542f5fSchristos return; 18942542f5fSchristos } 19042542f5fSchristos } 19142542f5fSchristos assert(bo->active_scanout == 0); 19242542f5fSchristos 19342542f5fSchristos if (info && reuse) { 19442542f5fSchristos bool found = false; 19542542f5fSchristos struct dri_bo *c; 19642542f5fSchristos 19742542f5fSchristos list_for_each_entry_reverse(c, &info->cache, link) { 19842542f5fSchristos if (c->bo == NULL) { 19942542f5fSchristos found = true; 20042542f5fSchristos _list_del(&c->link); 20142542f5fSchristos break; 20242542f5fSchristos } 20342542f5fSchristos } 20442542f5fSchristos if (!found) 20542542f5fSchristos c = malloc(sizeof(*c)); 20642542f5fSchristos if (c != NULL) { 20742542f5fSchristos c->bo = ref(get_private(back)->bo); 20842542f5fSchristos c->name = back->name; 20942542f5fSchristos list_add(&c->link, &info->cache); 21042542f5fSchristos DBG(("%s: cacheing handle=%d (name=%d)\n", __FUNCTION__, c->bo->handle, c->name)); 21142542f5fSchristos } 21242542f5fSchristos } 21342542f5fSchristos 21442542f5fSchristos assert(bo != get_private(back)->bo); 21542542f5fSchristos kgem_bo_destroy(&sna->kgem, get_private(back)->bo); 21642542f5fSchristos 21742542f5fSchristos get_private(back)->bo = bo; 21842542f5fSchristos get_private(back)->size = draw->height << 16 | draw->width; 21942542f5fSchristos back->pitch = bo->pitch; 22042542f5fSchristos back->name = name; 22142542f5fSchristos 22242542f5fSchristos get_private(back)->stale = false; 22342542f5fSchristos} 22442542f5fSchristos 22542542f5fSchristosstruct dri2_window { 22642542f5fSchristos DRI2BufferPtr front; 22742542f5fSchristos struct sna_dri2_event *chain; 22842542f5fSchristos xf86CrtcPtr crtc; 22942542f5fSchristos int64_t msc_delta; 23042542f5fSchristos}; 23142542f5fSchristos 23242542f5fSchristosstatic struct dri2_window *dri2_window(WindowPtr win) 23342542f5fSchristos{ 23442542f5fSchristos assert(win->drawable.type != DRAWABLE_PIXMAP); 23542542f5fSchristos return ((void **)__get_private(win, sna_window_key))[1]; 23642542f5fSchristos} 23742542f5fSchristos 23842542f5fSchristosstatic struct sna_dri2_event * 23942542f5fSchristosdri2_chain(DrawablePtr d) 24042542f5fSchristos{ 24142542f5fSchristos struct dri2_window *priv = dri2_window((WindowPtr)d); 24242542f5fSchristos assert(priv != NULL); 24342542f5fSchristos return priv->chain; 24442542f5fSchristos} 24542542f5fSchristosinline static DRI2BufferPtr dri2_window_get_front(WindowPtr win) { return dri2_window(win)->front; } 24642542f5fSchristos#else 24742542f5fSchristosinline static void *dri2_window_get_front(WindowPtr win) { return NULL; } 24842542f5fSchristos#endif 24942542f5fSchristos 25042542f5fSchristos#if DRI2INFOREC_VERSION < 6 25142542f5fSchristos 25242542f5fSchristos#define xorg_can_triple_buffer(ptr) 0 25342542f5fSchristos#define swap_limit(d, l) false 25442542f5fSchristos 25542542f5fSchristos#else 25642542f5fSchristos 25742542f5fSchristos#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,904,0) 25842542f5fSchristos/* Prime fixed for triple buffer support */ 25942542f5fSchristos#define xorg_can_triple_buffer(ptr) 1 26042542f5fSchristos#elif XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,12,99,901,0) 26142542f5fSchristos/* Before numGPUScreens was introduced */ 26242542f5fSchristos#define xorg_can_triple_buffer(ptr) 1 26342542f5fSchristos#else 26442542f5fSchristos/* Subject to crashers when combining triple buffering and Prime */ 26542542f5fSchristosinline static bool xorg_can_triple_buffer(struct sna *sna) 26642542f5fSchristos{ 26742542f5fSchristos return screenInfo.numGPUScreens == 0; 26842542f5fSchristos} 26942542f5fSchristos#endif 27042542f5fSchristos 27142542f5fSchristosstatic Bool 27242542f5fSchristossna_dri2_swap_limit_validate(DrawablePtr draw, int swap_limit) 27342542f5fSchristos{ 27442542f5fSchristos DBG(("%s: swap limit set to %d\n", __FUNCTION__, swap_limit)); 27542542f5fSchristos return swap_limit >= 1; 27642542f5fSchristos} 27742542f5fSchristos 27842542f5fSchristosstatic void 27942542f5fSchristossna_dri2_reuse_buffer(DrawablePtr draw, DRI2BufferPtr buffer) 28042542f5fSchristos{ 28142542f5fSchristos DBG(("%s: reusing buffer pixmap=%ld, attachment=%d, handle=%d, name=%d\n", 28242542f5fSchristos __FUNCTION__, get_drawable_pixmap(draw)->drawable.serialNumber, 28342542f5fSchristos buffer->attachment, get_private(buffer)->bo->handle, buffer->name)); 28442542f5fSchristos assert(get_private(buffer)->refcnt); 28542542f5fSchristos assert(get_private(buffer)->bo->refcnt > get_private(buffer)->bo->active_scanout); 28642542f5fSchristos 28742542f5fSchristos if (buffer->attachment == DRI2BufferBackLeft && 28842542f5fSchristos draw->type != DRAWABLE_PIXMAP) { 28942542f5fSchristos DBG(("%s: replacing back buffer\n", __FUNCTION__)); 29042542f5fSchristos sna_dri2_get_back(to_sna_from_drawable(draw), draw, buffer, dri2_chain(draw)); 29142542f5fSchristos 29242542f5fSchristos assert(kgem_bo_flink(&to_sna_from_drawable(draw)->kgem, get_private(buffer)->bo) == buffer->name); 29342542f5fSchristos assert(get_private(buffer)->bo->refcnt); 29442542f5fSchristos assert(get_private(buffer)->bo->active_scanout == 0); 29542542f5fSchristos } 29642542f5fSchristos} 29742542f5fSchristos 29842542f5fSchristosstatic bool swap_limit(DrawablePtr draw, int limit) 29942542f5fSchristos{ 30042542f5fSchristos DBG(("%s: draw=%ld setting swap limit to %d\n", __FUNCTION__, (long)draw->id, limit)); 30142542f5fSchristos DRI2SwapLimit(draw, limit); 30242542f5fSchristos return true; 30342542f5fSchristos} 30442542f5fSchristos#endif 30542542f5fSchristos 30642542f5fSchristos#if DRI2INFOREC_VERSION < 10 30742542f5fSchristos#undef USE_ASYNC_SWAP 30842542f5fSchristos#define USE_ASYNC_SWAP 0 30942542f5fSchristos#endif 31042542f5fSchristos 31142542f5fSchristos#define COLOR_PREFER_TILING_Y 0 31242542f5fSchristos 31342542f5fSchristos/* Prefer to enable TILING_Y if this buffer will never be a 31442542f5fSchristos * candidate for pageflipping 31542542f5fSchristos */ 31642542f5fSchristosstatic uint32_t color_tiling(struct sna *sna, DrawablePtr draw) 31742542f5fSchristos{ 31842542f5fSchristos uint32_t tiling; 31942542f5fSchristos 32042542f5fSchristos if (COLOR_PREFER_TILING_Y && 32142542f5fSchristos (draw->width != sna->front->drawable.width || 32242542f5fSchristos draw->height != sna->front->drawable.height)) 32342542f5fSchristos tiling = I915_TILING_Y; 32442542f5fSchristos else 32542542f5fSchristos tiling = I915_TILING_X; 32642542f5fSchristos 32742542f5fSchristos return kgem_choose_tiling(&sna->kgem, -tiling, 32842542f5fSchristos draw->width, 32942542f5fSchristos draw->height, 33042542f5fSchristos draw->bitsPerPixel); 33142542f5fSchristos} 33242542f5fSchristos 33342542f5fSchristosstatic uint32_t other_tiling(struct sna *sna, DrawablePtr draw) 33442542f5fSchristos{ 33542542f5fSchristos /* XXX Can mix color X / depth Y? */ 33642542f5fSchristos return kgem_choose_tiling(&sna->kgem, 33742542f5fSchristos sna->kgem.gen >= 040 ? -I915_TILING_Y : -I915_TILING_X, 33842542f5fSchristos draw->width, 33942542f5fSchristos draw->height, 34042542f5fSchristos draw->bitsPerPixel); 34142542f5fSchristos} 34242542f5fSchristos 34342542f5fSchristosstatic struct kgem_bo *sna_pixmap_set_dri(struct sna *sna, 34442542f5fSchristos PixmapPtr pixmap) 34542542f5fSchristos{ 34642542f5fSchristos struct sna_pixmap *priv; 34742542f5fSchristos int tiling; 34842542f5fSchristos 34942542f5fSchristos DBG(("%s: attaching DRI client to pixmap=%ld\n", 35042542f5fSchristos __FUNCTION__, pixmap->drawable.serialNumber)); 35142542f5fSchristos 35242542f5fSchristos priv = sna_pixmap(pixmap); 35342542f5fSchristos if (priv != NULL && IS_STATIC_PTR(priv->ptr) && priv->cpu_bo) { 35442542f5fSchristos DBG(("%s: SHM or unattached Pixmap, BadAlloc\n", __FUNCTION__)); 35542542f5fSchristos return NULL; 35642542f5fSchristos } 35742542f5fSchristos 35842542f5fSchristos priv = sna_pixmap_move_to_gpu(pixmap, 35942542f5fSchristos MOVE_READ | __MOVE_FORCE | __MOVE_DRI); 36042542f5fSchristos if (priv == NULL) { 36142542f5fSchristos DBG(("%s: failed to move to GPU, BadAlloc\n", __FUNCTION__)); 36242542f5fSchristos return NULL; 36342542f5fSchristos } 36442542f5fSchristos 36542542f5fSchristos assert(priv->flush == false); 36642542f5fSchristos assert(priv->cpu_damage == NULL); 36742542f5fSchristos assert(priv->gpu_bo); 36842542f5fSchristos assert(priv->gpu_bo->proxy == NULL); 36942542f5fSchristos assert(priv->gpu_bo->flush == false); 37042542f5fSchristos 37142542f5fSchristos tiling = color_tiling(sna, &pixmap->drawable); 37242542f5fSchristos if (tiling < 0) 37342542f5fSchristos tiling = -tiling; 37442542f5fSchristos if (priv->gpu_bo->tiling != tiling) 37542542f5fSchristos sna_pixmap_change_tiling(pixmap, tiling); 37642542f5fSchristos 37742542f5fSchristos return priv->gpu_bo; 37842542f5fSchristos} 37942542f5fSchristos 38042542f5fSchristospure static inline void *sna_pixmap_get_buffer(PixmapPtr pixmap) 38142542f5fSchristos{ 38242542f5fSchristos assert(pixmap->refcnt); 38342542f5fSchristos return ((void **)__get_private(pixmap, sna_pixmap_key))[2]; 38442542f5fSchristos} 38542542f5fSchristos 38642542f5fSchristosstatic inline void sna_pixmap_set_buffer(PixmapPtr pixmap, void *ptr) 38742542f5fSchristos{ 38842542f5fSchristos assert(pixmap->refcnt); 38942542f5fSchristos ((void **)__get_private(pixmap, sna_pixmap_key))[2] = ptr; 39042542f5fSchristos} 39142542f5fSchristos 39242542f5fSchristosvoid 39342542f5fSchristossna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap, struct kgem_bo *bo) 39442542f5fSchristos{ 39542542f5fSchristos DRI2BufferPtr buffer; 39642542f5fSchristos struct sna_dri2_private *private; 39742542f5fSchristos 39842542f5fSchristos buffer = sna_pixmap_get_buffer(pixmap); 39942542f5fSchristos if (buffer == NULL) 40042542f5fSchristos return; 40142542f5fSchristos 40242542f5fSchristos DBG(("%s: pixmap=%ld, old handle=%d, new handle=%d\n", __FUNCTION__, 40342542f5fSchristos pixmap->drawable.serialNumber, 40442542f5fSchristos get_private(buffer)->bo->handle, 40542542f5fSchristos sna_pixmap(pixmap)->gpu_bo->handle)); 40642542f5fSchristos 40742542f5fSchristos private = get_private(buffer); 40842542f5fSchristos assert(private->pixmap == pixmap); 40942542f5fSchristos 41042542f5fSchristos assert(bo != private->bo); 41142542f5fSchristos if (private->bo == bo) 41242542f5fSchristos return; 41342542f5fSchristos 41442542f5fSchristos DBG(("%s: dropping flush hint from handle=%d\n", __FUNCTION__, private->bo->handle)); 41542542f5fSchristos private->bo->flush = false; 41642542f5fSchristos kgem_bo_destroy(&sna->kgem, private->bo); 41742542f5fSchristos 41842542f5fSchristos buffer->name = kgem_bo_flink(&sna->kgem, bo); 41942542f5fSchristos private->bo = ref(bo); 42042542f5fSchristos 42142542f5fSchristos DBG(("%s: adding flush hint to handle=%d\n", __FUNCTION__, bo->handle)); 42242542f5fSchristos bo->flush = true; 42342542f5fSchristos assert(sna_pixmap(pixmap)->flush); 42442542f5fSchristos 42542542f5fSchristos /* XXX DRI2InvalidateDrawable(&pixmap->drawable); */ 42642542f5fSchristos} 42742542f5fSchristos 42842542f5fSchristosstatic DRI2Buffer2Ptr 42942542f5fSchristossna_dri2_create_buffer(DrawablePtr draw, 43042542f5fSchristos unsigned int attachment, 43142542f5fSchristos unsigned int format) 43242542f5fSchristos{ 43342542f5fSchristos struct sna *sna = to_sna_from_drawable(draw); 43442542f5fSchristos DRI2Buffer2Ptr buffer; 43542542f5fSchristos struct sna_dri2_private *private; 43642542f5fSchristos PixmapPtr pixmap; 43742542f5fSchristos struct kgem_bo *bo; 43842542f5fSchristos unsigned flags = 0; 43942542f5fSchristos uint32_t size; 44042542f5fSchristos int bpp; 44142542f5fSchristos 44242542f5fSchristos DBG(("%s pixmap=%ld, (attachment=%d, format=%d, drawable=%dx%d)\n", 44342542f5fSchristos __FUNCTION__, 44442542f5fSchristos get_drawable_pixmap(draw)->drawable.serialNumber, 44542542f5fSchristos attachment, format, draw->width, draw->height)); 44642542f5fSchristos 44742542f5fSchristos pixmap = NULL; 44842542f5fSchristos size = (uint32_t)draw->height << 16 | draw->width; 44942542f5fSchristos switch (attachment) { 45042542f5fSchristos case DRI2BufferFrontLeft: 45142542f5fSchristos pixmap = get_drawable_pixmap(draw); 45242542f5fSchristos buffer = NULL; 45342542f5fSchristos if (draw->type != DRAWABLE_PIXMAP) 45442542f5fSchristos buffer = dri2_window_get_front((WindowPtr)draw); 45542542f5fSchristos if (buffer == NULL) 45642542f5fSchristos buffer = sna_pixmap_get_buffer(pixmap); 45742542f5fSchristos if (buffer) { 45842542f5fSchristos private = get_private(buffer); 45942542f5fSchristos 46042542f5fSchristos DBG(("%s: reusing front buffer attachment, win=%lu %dx%d, pixmap=%ld %dx%d, handle=%d, name=%d\n", 46142542f5fSchristos __FUNCTION__, 46242542f5fSchristos draw->type != DRAWABLE_PIXMAP ? (long)draw->id : (long)0, 46342542f5fSchristos draw->width, draw->height, 46442542f5fSchristos pixmap->drawable.serialNumber, 46542542f5fSchristos pixmap->drawable.width, 46642542f5fSchristos pixmap->drawable.height, 46742542f5fSchristos private->bo->handle, buffer->name)); 46842542f5fSchristos 46942542f5fSchristos assert(private->pixmap == pixmap); 47042542f5fSchristos assert(sna_pixmap(pixmap)->flush); 47142542f5fSchristos assert(sna_pixmap(pixmap)->pinned & PIN_DRI2); 47242542f5fSchristos assert(kgem_bo_flink(&sna->kgem, private->bo) == buffer->name); 47342542f5fSchristos 47442542f5fSchristos private->refcnt++; 47542542f5fSchristos return buffer; 47642542f5fSchristos } 47742542f5fSchristos 47842542f5fSchristos bo = sna_pixmap_set_dri(sna, pixmap); 47942542f5fSchristos if (bo == NULL) 48042542f5fSchristos return NULL; 48142542f5fSchristos 48242542f5fSchristos assert(sna_pixmap(pixmap) != NULL); 48342542f5fSchristos 48442542f5fSchristos bo = ref(bo); 48542542f5fSchristos bpp = pixmap->drawable.bitsPerPixel; 48642542f5fSchristos if (pixmap == sna->front && !(sna->flags & SNA_LINEAR_FB)) 48742542f5fSchristos flags |= CREATE_SCANOUT; 48842542f5fSchristos DBG(("%s: attaching to front buffer %dx%d [%p:%d], scanout? %d\n", 48942542f5fSchristos __FUNCTION__, 49042542f5fSchristos pixmap->drawable.width, pixmap->drawable.height, 49142542f5fSchristos pixmap, pixmap->refcnt, flags & CREATE_SCANOUT)); 49242542f5fSchristos size = (uint32_t)pixmap->drawable.height << 16 | pixmap->drawable.width; 49342542f5fSchristos break; 49442542f5fSchristos 49542542f5fSchristos case DRI2BufferBackLeft: 49642542f5fSchristos if (draw->type != DRAWABLE_PIXMAP) { 49742542f5fSchristos if (dri2_window_get_front((WindowPtr)draw)) 49842542f5fSchristos flags |= CREATE_SCANOUT; 49942542f5fSchristos if (draw->width == sna->front->drawable.width && 50042542f5fSchristos draw->height == sna->front->drawable.height && 50142542f5fSchristos (sna->flags & (SNA_LINEAR_FB | SNA_NO_WAIT | SNA_NO_FLIP)) == 0) 50242542f5fSchristos flags |= CREATE_SCANOUT; 50342542f5fSchristos } 50442542f5fSchristos case DRI2BufferBackRight: 50542542f5fSchristos case DRI2BufferFrontRight: 50642542f5fSchristos case DRI2BufferFakeFrontLeft: 50742542f5fSchristos case DRI2BufferFakeFrontRight: 50842542f5fSchristos bpp = draw->bitsPerPixel; 50942542f5fSchristos DBG(("%s: creating back buffer %dx%d, suitable for scanout? %d\n", 51042542f5fSchristos __FUNCTION__, 51142542f5fSchristos draw->width, draw->height, 51242542f5fSchristos flags & CREATE_SCANOUT)); 51342542f5fSchristos 51442542f5fSchristos bo = kgem_create_2d(&sna->kgem, 51542542f5fSchristos draw->width, 51642542f5fSchristos draw->height, 51742542f5fSchristos draw->bitsPerPixel, 51842542f5fSchristos color_tiling(sna, draw), 51942542f5fSchristos flags); 52042542f5fSchristos break; 52142542f5fSchristos 52242542f5fSchristos case DRI2BufferStencil: 52342542f5fSchristos /* 52442542f5fSchristos * The stencil buffer has quirky pitch requirements. From Vol 52542542f5fSchristos * 2a, 11.5.6.2.1 3DSTATE_STENCIL_BUFFER, field "Surface 52642542f5fSchristos * Pitch": 52742542f5fSchristos * The pitch must be set to 2x the value computed based on 52842542f5fSchristos * width, as the stencil buffer is stored with two rows 52942542f5fSchristos * interleaved. 53042542f5fSchristos * To accomplish this, we resort to the nasty hack of doubling 53142542f5fSchristos * the drm region's cpp and halving its height. 53242542f5fSchristos * 53342542f5fSchristos * If we neglect to double the pitch, then 53442542f5fSchristos * drm_intel_gem_bo_map_gtt() maps the memory incorrectly. 53542542f5fSchristos * 53642542f5fSchristos * The alignment for W-tiling is quite different to the 53742542f5fSchristos * nominal no-tiling case, so we have to account for 53842542f5fSchristos * the tiled access pattern explicitly. 53942542f5fSchristos * 54042542f5fSchristos * The stencil buffer is W tiled. However, we request from 54142542f5fSchristos * the kernel a non-tiled buffer because the kernel does 54242542f5fSchristos * not understand W tiling and the GTT is incapable of 54342542f5fSchristos * W fencing. 54442542f5fSchristos */ 54542542f5fSchristos bpp = format ? format : draw->bitsPerPixel; 54642542f5fSchristos bpp *= 2; 54742542f5fSchristos bo = kgem_create_2d(&sna->kgem, 54842542f5fSchristos ALIGN(draw->width, 64), 54942542f5fSchristos ALIGN((draw->height + 1) / 2, 64), 55042542f5fSchristos bpp, I915_TILING_NONE, flags); 55142542f5fSchristos break; 55242542f5fSchristos 55342542f5fSchristos case DRI2BufferDepth: 55442542f5fSchristos case DRI2BufferDepthStencil: 55542542f5fSchristos case DRI2BufferHiz: 55642542f5fSchristos case DRI2BufferAccum: 55742542f5fSchristos bpp = format ? format : draw->bitsPerPixel, 55842542f5fSchristos bo = kgem_create_2d(&sna->kgem, 55942542f5fSchristos draw->width, draw->height, bpp, 56042542f5fSchristos other_tiling(sna, draw), 56142542f5fSchristos flags); 56242542f5fSchristos break; 56342542f5fSchristos 56442542f5fSchristos default: 56542542f5fSchristos return NULL; 56642542f5fSchristos } 56742542f5fSchristos if (bo == NULL) 56842542f5fSchristos return NULL; 56942542f5fSchristos 57042542f5fSchristos buffer = calloc(1, sizeof *buffer + sizeof *private); 57142542f5fSchristos if (buffer == NULL) 57242542f5fSchristos goto err; 57342542f5fSchristos 57442542f5fSchristos private = get_private(buffer); 57542542f5fSchristos buffer->attachment = attachment; 57642542f5fSchristos buffer->pitch = bo->pitch; 57742542f5fSchristos buffer->cpp = bpp / 8; 57842542f5fSchristos buffer->driverPrivate = private; 57942542f5fSchristos buffer->format = format; 58042542f5fSchristos buffer->flags = 0; 58142542f5fSchristos buffer->name = kgem_bo_flink(&sna->kgem, bo); 58242542f5fSchristos private->refcnt = 1; 58342542f5fSchristos private->bo = bo; 58442542f5fSchristos private->pixmap = pixmap; 58542542f5fSchristos private->size = size; 58642542f5fSchristos 58742542f5fSchristos if (buffer->name == 0) 58842542f5fSchristos goto err; 58942542f5fSchristos 59042542f5fSchristos if (pixmap) { 59142542f5fSchristos struct sna_pixmap *priv; 59242542f5fSchristos 59342542f5fSchristos assert(attachment == DRI2BufferFrontLeft); 59442542f5fSchristos assert(sna_pixmap_get_buffer(pixmap) == NULL); 59542542f5fSchristos 59642542f5fSchristos sna_pixmap_set_buffer(pixmap, buffer); 59742542f5fSchristos assert(sna_pixmap_get_buffer(pixmap) == buffer); 59842542f5fSchristos pixmap->refcnt++; 59942542f5fSchristos 60042542f5fSchristos priv = sna_pixmap(pixmap); 60142542f5fSchristos assert(priv->flush == false); 60242542f5fSchristos assert((priv->pinned & PIN_DRI2) == 0); 60342542f5fSchristos 60442542f5fSchristos /* Don't allow this named buffer to be replaced */ 60542542f5fSchristos priv->pinned |= PIN_DRI2; 60642542f5fSchristos 60742542f5fSchristos /* We need to submit any modifications to and reads from this 60842542f5fSchristos * buffer before we send any reply to the Client. 60942542f5fSchristos * 61042542f5fSchristos * As we don't track which Client, we flush for all. 61142542f5fSchristos */ 61242542f5fSchristos DBG(("%s: adding flush hint to handle=%d\n", __FUNCTION__, priv->gpu_bo->handle)); 61342542f5fSchristos priv->gpu_bo->flush = true; 61442542f5fSchristos if (priv->gpu_bo->exec) 61542542f5fSchristos sna->kgem.flush = 1; 61642542f5fSchristos 61742542f5fSchristos priv->flush |= 1; 61842542f5fSchristos if (draw->type == DRAWABLE_PIXMAP) { 61942542f5fSchristos /* DRI2 renders directly into GLXPixmaps, treat as hostile */ 62042542f5fSchristos kgem_bo_unclean(&sna->kgem, priv->gpu_bo); 62142542f5fSchristos sna_damage_all(&priv->gpu_damage, pixmap); 62242542f5fSchristos priv->clear = false; 62342542f5fSchristos priv->cpu = false; 62442542f5fSchristos priv->flush |= 2; 62542542f5fSchristos } 62642542f5fSchristos 62742542f5fSchristos sna_accel_watch_flush(sna, 1); 62842542f5fSchristos } 62942542f5fSchristos 63042542f5fSchristos return buffer; 63142542f5fSchristos 63242542f5fSchristoserr: 63342542f5fSchristos kgem_bo_destroy(&sna->kgem, bo); 63442542f5fSchristos free(buffer); 63542542f5fSchristos return NULL; 63642542f5fSchristos} 63742542f5fSchristos 63842542f5fSchristosstatic void _sna_dri2_destroy_buffer(struct sna *sna, DRI2Buffer2Ptr buffer) 63942542f5fSchristos{ 64042542f5fSchristos struct sna_dri2_private *private = get_private(buffer); 64142542f5fSchristos 64242542f5fSchristos if (buffer == NULL) 64342542f5fSchristos return; 64442542f5fSchristos 64542542f5fSchristos DBG(("%s: %p [handle=%d] -- refcnt=%d, pixmap=%ld\n", 64642542f5fSchristos __FUNCTION__, buffer, private->bo->handle, private->refcnt, 64742542f5fSchristos private->pixmap ? private->pixmap->drawable.serialNumber : 0)); 64842542f5fSchristos assert(private->refcnt > 0); 64942542f5fSchristos if (--private->refcnt) 65042542f5fSchristos return; 65142542f5fSchristos 65242542f5fSchristos assert(private->bo); 65342542f5fSchristos 65442542f5fSchristos if (private->proxy) { 65542542f5fSchristos DBG(("%s: destroying proxy\n", __FUNCTION__)); 65642542f5fSchristos _sna_dri2_destroy_buffer(sna, private->proxy); 65742542f5fSchristos private->pixmap = NULL; 65842542f5fSchristos } 65942542f5fSchristos 66042542f5fSchristos if (private->pixmap) { 66142542f5fSchristos PixmapPtr pixmap = private->pixmap; 66242542f5fSchristos struct sna_pixmap *priv = sna_pixmap(pixmap); 66342542f5fSchristos 66442542f5fSchristos assert(sna_pixmap_get_buffer(pixmap) == buffer); 66542542f5fSchristos assert(priv->gpu_bo == private->bo); 66642542f5fSchristos assert(priv->gpu_bo->flush); 66742542f5fSchristos assert(priv->pinned & PIN_DRI2); 66842542f5fSchristos assert(priv->flush); 66942542f5fSchristos 67042542f5fSchristos /* Undo the DRI markings on this pixmap */ 67142542f5fSchristos DBG(("%s: releasing last DRI pixmap=%ld, scanout?=%d\n", 67242542f5fSchristos __FUNCTION__, 67342542f5fSchristos pixmap->drawable.serialNumber, 67442542f5fSchristos pixmap == sna->front)); 67542542f5fSchristos 67642542f5fSchristos list_del(&priv->flush_list); 67742542f5fSchristos 67842542f5fSchristos DBG(("%s: dropping flush hint from handle=%d\n", __FUNCTION__, private->bo->handle)); 67942542f5fSchristos priv->gpu_bo->flush = false; 68042542f5fSchristos priv->pinned &= ~PIN_DRI2; 68142542f5fSchristos 68242542f5fSchristos priv->flush = false; 68342542f5fSchristos sna_accel_watch_flush(sna, -1); 68442542f5fSchristos 68542542f5fSchristos sna_pixmap_set_buffer(pixmap, NULL); 68642542f5fSchristos pixmap->drawable.pScreen->DestroyPixmap(pixmap); 68742542f5fSchristos } 68842542f5fSchristos assert(private->bo->flush == false); 68942542f5fSchristos 69042542f5fSchristos kgem_bo_destroy(&sna->kgem, private->bo); 69142542f5fSchristos free(buffer); 69242542f5fSchristos} 69342542f5fSchristos 69442542f5fSchristosstatic void sna_dri2_destroy_buffer(DrawablePtr draw, DRI2Buffer2Ptr buffer) 69542542f5fSchristos{ 69642542f5fSchristos _sna_dri2_destroy_buffer(to_sna_from_drawable(draw), buffer); 69742542f5fSchristos} 69842542f5fSchristos 69942542f5fSchristosstatic DRI2BufferPtr sna_dri2_reference_buffer(DRI2BufferPtr buffer) 70042542f5fSchristos{ 70142542f5fSchristos get_private(buffer)->refcnt++; 70242542f5fSchristos return buffer; 70342542f5fSchristos} 70442542f5fSchristos 70542542f5fSchristosstatic inline void damage(PixmapPtr pixmap, struct sna_pixmap *priv, RegionPtr region) 70642542f5fSchristos{ 70742542f5fSchristos assert(priv->gpu_bo); 70842542f5fSchristos if (DAMAGE_IS_ALL(priv->gpu_damage)) 70942542f5fSchristos goto done; 71042542f5fSchristos 71142542f5fSchristos if (region == NULL) { 71242542f5fSchristosdamage_all: 71342542f5fSchristos priv->gpu_damage = _sna_damage_all(priv->gpu_damage, 71442542f5fSchristos pixmap->drawable.width, 71542542f5fSchristos pixmap->drawable.height); 71642542f5fSchristos sna_damage_destroy(&priv->cpu_damage); 71742542f5fSchristos list_del(&priv->flush_list); 71842542f5fSchristos } else { 71942542f5fSchristos sna_damage_subtract(&priv->cpu_damage, region); 72042542f5fSchristos if (priv->cpu_damage == NULL) 72142542f5fSchristos goto damage_all; 72242542f5fSchristos sna_damage_add(&priv->gpu_damage, region); 72342542f5fSchristos } 72442542f5fSchristosdone: 72542542f5fSchristos priv->cpu = false; 72642542f5fSchristos priv->clear = false; 72742542f5fSchristos} 72842542f5fSchristos 72942542f5fSchristosstatic void set_bo(PixmapPtr pixmap, struct kgem_bo *bo) 73042542f5fSchristos{ 73142542f5fSchristos struct sna *sna = to_sna_from_pixmap(pixmap); 73242542f5fSchristos struct sna_pixmap *priv = sna_pixmap(pixmap); 73342542f5fSchristos RegionRec region; 73442542f5fSchristos 73542542f5fSchristos DBG(("%s: pixmap=%ld, handle=%d\n", 73642542f5fSchristos __FUNCTION__, pixmap->drawable.serialNumber, bo->handle)); 73742542f5fSchristos 73842542f5fSchristos assert(pixmap->drawable.width * pixmap->drawable.bitsPerPixel <= 8*bo->pitch); 73942542f5fSchristos assert(pixmap->drawable.height * bo->pitch <= kgem_bo_size(bo)); 74042542f5fSchristos assert(bo->proxy == NULL); 74142542f5fSchristos assert(priv->pinned & PIN_DRI2); 74242542f5fSchristos assert((priv->pinned & (PIN_PRIME | PIN_DRI3)) == 0); 74342542f5fSchristos assert(priv->flush); 74442542f5fSchristos 74542542f5fSchristos /* Post damage on the new front buffer so that listeners, such 74642542f5fSchristos * as DisplayLink know take a copy and shove it over the USB, 74742542f5fSchristos * also for software cursors and the like. 74842542f5fSchristos */ 74942542f5fSchristos region.extents.x1 = region.extents.y1 = 0; 75042542f5fSchristos region.extents.x2 = pixmap->drawable.width; 75142542f5fSchristos region.extents.y2 = pixmap->drawable.height; 75242542f5fSchristos region.data = NULL; 75342542f5fSchristos DamageRegionAppend(&pixmap->drawable, ®ion); 75442542f5fSchristos 75542542f5fSchristos damage(pixmap, priv, NULL); 75642542f5fSchristos 75742542f5fSchristos assert(bo->refcnt); 75842542f5fSchristos if (priv->move_to_gpu) 75942542f5fSchristos priv->move_to_gpu(sna, priv, 0); 76042542f5fSchristos if (priv->gpu_bo != bo) { 76142542f5fSchristos DBG(("%s: dropping flush hint from handle=%d\n", __FUNCTION__, priv->gpu_bo->handle)); 76242542f5fSchristos priv->gpu_bo->flush = false; 76342542f5fSchristos if (priv->cow) 76442542f5fSchristos sna_pixmap_undo_cow(sna, priv, 0); 76542542f5fSchristos if (priv->gpu_bo) { 76642542f5fSchristos sna_pixmap_unmap(pixmap, priv); 76742542f5fSchristos kgem_bo_destroy(&sna->kgem, priv->gpu_bo); 76842542f5fSchristos } 76942542f5fSchristos DBG(("%s: adding flush hint to handle=%d\n", __FUNCTION__, bo->handle)); 77042542f5fSchristos bo->flush = true; 77142542f5fSchristos if (bo->exec) 77242542f5fSchristos sna->kgem.flush = 1; 77342542f5fSchristos priv->gpu_bo = ref(bo); 77442542f5fSchristos } 77542542f5fSchristos if (bo->domain != DOMAIN_GPU) 77642542f5fSchristos bo->domain = DOMAIN_NONE; 77742542f5fSchristos assert(bo->flush); 77842542f5fSchristos 77942542f5fSchristos DamageRegionProcessPending(&pixmap->drawable); 78042542f5fSchristos} 78142542f5fSchristos 78242542f5fSchristosstatic void sna_dri2_select_mode(struct sna *sna, struct kgem_bo *dst, struct kgem_bo *src, bool sync) 78342542f5fSchristos{ 78442542f5fSchristos struct drm_i915_gem_busy busy; 78542542f5fSchristos int mode; 78642542f5fSchristos 78742542f5fSchristos if (sna->kgem.gen < 060) 78842542f5fSchristos return; 78942542f5fSchristos 79042542f5fSchristos if (sync) { 79142542f5fSchristos DBG(("%s: sync, force %s ring\n", __FUNCTION__, 79242542f5fSchristos sna->kgem.gen >= 070 ? "BLT" : "RENDER")); 79342542f5fSchristos kgem_set_mode(&sna->kgem, 79442542f5fSchristos sna->kgem.gen >= 070 ? KGEM_BLT : KGEM_RENDER, 79542542f5fSchristos dst); 79642542f5fSchristos return; 79742542f5fSchristos } 79842542f5fSchristos 79942542f5fSchristos if (DBG_FORCE_COPY != -1) { 80042542f5fSchristos DBG(("%s: forcing %d\n", __FUNCTION__, DBG_FORCE_COPY)); 80142542f5fSchristos kgem_set_mode(&sna->kgem, DBG_FORCE_COPY, dst); 80242542f5fSchristos return; 80342542f5fSchristos } 80442542f5fSchristos 80542542f5fSchristos if (sna->kgem.mode != KGEM_NONE) { 80642542f5fSchristos DBG(("%s: busy, not switching\n", __FUNCTION__)); 80742542f5fSchristos return; 80842542f5fSchristos } 80942542f5fSchristos 81042542f5fSchristos VG_CLEAR(busy); 81142542f5fSchristos busy.handle = src->handle; 81242542f5fSchristos if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_BUSY, &busy)) 81342542f5fSchristos return; 81442542f5fSchristos 81542542f5fSchristos DBG(("%s: src handle=%d busy?=%x\n", __FUNCTION__, busy.handle, busy.busy)); 81642542f5fSchristos if (busy.busy == 0) { 81742542f5fSchristos __kgem_bo_clear_busy(src); 81842542f5fSchristos 81942542f5fSchristos busy.handle = dst->handle; 82042542f5fSchristos if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_BUSY, &busy)) 82142542f5fSchristos return; 82242542f5fSchristos 82342542f5fSchristos DBG(("%s: dst handle=%d busy?=%x\n", __FUNCTION__, busy.handle, busy.busy)); 82442542f5fSchristos if (busy.busy == 0) { 82542542f5fSchristos __kgem_bo_clear_busy(dst); 82642542f5fSchristos DBG(("%s: src/dst is idle, using defaults\n", __FUNCTION__)); 82742542f5fSchristos return; 82842542f5fSchristos } 82942542f5fSchristos } 83042542f5fSchristos 83142542f5fSchristos /* Sandybridge introduced a separate ring which it uses to 83242542f5fSchristos * perform blits. Switching rendering between rings incurs 83342542f5fSchristos * a stall as we wait upon the old ring to finish and 83442542f5fSchristos * flush its render cache before we can proceed on with 83542542f5fSchristos * the operation on the new ring. 83642542f5fSchristos * 83742542f5fSchristos * As this buffer, we presume, has just been written to by 83842542f5fSchristos * the DRI client using the RENDER ring, we want to perform 83942542f5fSchristos * our operation on the same ring, and ideally on the same 84042542f5fSchristos * ring as we will flip from (which should be the RENDER ring 84142542f5fSchristos * as well). 84242542f5fSchristos * 84342542f5fSchristos * The ultimate question is whether preserving the ring outweighs 84442542f5fSchristos * the cost of the query. 84542542f5fSchristos */ 84642542f5fSchristos mode = KGEM_RENDER; 84742542f5fSchristos if (busy.busy & (0xfffe << 16)) 84842542f5fSchristos mode = KGEM_BLT; 84942542f5fSchristos kgem_bo_mark_busy(&sna->kgem, busy.handle == src->handle ? src : dst, mode); 85042542f5fSchristos _kgem_set_mode(&sna->kgem, mode); 85142542f5fSchristos} 85242542f5fSchristos 85342542f5fSchristosstatic bool can_copy_cpu(struct sna *sna, 85442542f5fSchristos struct kgem_bo *src, 85542542f5fSchristos struct kgem_bo *dst) 85642542f5fSchristos{ 85742542f5fSchristos if (src->tiling != dst->tiling) 85842542f5fSchristos return false; 85942542f5fSchristos 86042542f5fSchristos if (src->pitch != dst->pitch) 86142542f5fSchristos return false; 86242542f5fSchristos 86342542f5fSchristos if (!kgem_bo_can_map__cpu(&sna->kgem, src, false)) 86442542f5fSchristos return false; 86542542f5fSchristos 86642542f5fSchristos if (!kgem_bo_can_map__cpu(&sna->kgem, dst, true)) 86742542f5fSchristos return false; 86842542f5fSchristos 86942542f5fSchristos DBG(("%s -- yes, src handle=%d, dst handle=%d\n", __FUNCTION__, src->handle, dst->handle)); 87042542f5fSchristos return true; 87142542f5fSchristos} 87242542f5fSchristos 87342542f5fSchristosstatic void 87442542f5fSchristossna_dri2_copy_fallback(struct sna *sna, 87542542f5fSchristos const DrawableRec *draw, 87642542f5fSchristos struct kgem_bo *src_bo, int sx, int sy, 87742542f5fSchristos struct kgem_bo *dst_bo, int dx, int dy, 87842542f5fSchristos const BoxRec *box, int n) 87942542f5fSchristos{ 88042542f5fSchristos void *dst, *src; 88142542f5fSchristos bool clipped; 88242542f5fSchristos 88342542f5fSchristos clipped = (n > 1 || 88442542f5fSchristos box->x1 + sx > 0 || 88542542f5fSchristos box->y1 + sy > 0 || 88642542f5fSchristos box->x2 + sx < draw->width || 88742542f5fSchristos box->y2 + sy < draw->height); 88842542f5fSchristos 88942542f5fSchristos dst = src = NULL; 89042542f5fSchristos if (!clipped && can_copy_cpu(sna, src_bo, dst_bo)) { 89142542f5fSchristos dst = kgem_bo_map__cpu(&sna->kgem, dst_bo); 89242542f5fSchristos src = kgem_bo_map__cpu(&sna->kgem, src_bo); 89342542f5fSchristos } 89442542f5fSchristos 89542542f5fSchristos if (dst == NULL || src == NULL) { 89642542f5fSchristos dst = kgem_bo_map__gtt(&sna->kgem, dst_bo); 89742542f5fSchristos src = kgem_bo_map__gtt(&sna->kgem, src_bo); 89842542f5fSchristos if (dst == NULL || src == NULL) 89942542f5fSchristos return; 90042542f5fSchristos } else { 90142542f5fSchristos kgem_bo_sync__cpu_full(&sna->kgem, dst_bo, true); 90242542f5fSchristos kgem_bo_sync__cpu_full(&sna->kgem, src_bo, false); 90342542f5fSchristos } 90442542f5fSchristos 90542542f5fSchristos DBG(("%s: src(%d, %d), dst(%d, %d) x %d\n", 90642542f5fSchristos __FUNCTION__, sx, sy, dx, dy, n)); 90742542f5fSchristos 90842542f5fSchristos if (sigtrap_get() == 0) { 90942542f5fSchristos do { 91042542f5fSchristos memcpy_blt(src, dst, draw->bitsPerPixel, 91142542f5fSchristos src_bo->pitch, dst_bo->pitch, 91242542f5fSchristos box->x1 + sx, box->y1 + sy, 91342542f5fSchristos box->x1 + dx, box->y1 + dy, 91442542f5fSchristos box->x2 - box->x1, box->y2 - box->y1); 91542542f5fSchristos box++; 91642542f5fSchristos } while (--n); 91742542f5fSchristos sigtrap_put(); 91842542f5fSchristos } 91942542f5fSchristos} 92042542f5fSchristos 92142542f5fSchristosstatic bool is_front(int attachment) 92242542f5fSchristos{ 92342542f5fSchristos return attachment == DRI2BufferFrontLeft; 92442542f5fSchristos} 92542542f5fSchristos 92642542f5fSchristosstatic struct kgem_bo * 92742542f5fSchristos__sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, 92842542f5fSchristos DRI2BufferPtr src, DRI2BufferPtr dst, 92942542f5fSchristos bool sync) 93042542f5fSchristos{ 93142542f5fSchristos PixmapPtr pixmap = get_drawable_pixmap(draw); 93242542f5fSchristos DrawableRec scratch, *src_draw = &pixmap->drawable, *dst_draw = &pixmap->drawable; 93342542f5fSchristos struct sna_dri2_private *src_priv = get_private(src); 93442542f5fSchristos struct sna_dri2_private *dst_priv = get_private(dst); 93542542f5fSchristos pixman_region16_t clip; 93642542f5fSchristos struct kgem_bo *bo = NULL; 93742542f5fSchristos struct kgem_bo *src_bo; 93842542f5fSchristos struct kgem_bo *dst_bo; 93942542f5fSchristos const BoxRec *boxes; 94042542f5fSchristos int16_t dx, dy, sx, sy; 94142542f5fSchristos int n; 94242542f5fSchristos 94342542f5fSchristos /* To hide a stale DRI2Buffer, one may choose to substitute 94442542f5fSchristos * pixmap->gpu_bo instead of dst/src->bo, however you then run 94542542f5fSchristos * the risk of copying around invalid data. So either you may not 94642542f5fSchristos * see the results of the copy, or you may see the wrong pixels. 94742542f5fSchristos * Either way you eventually lose. 94842542f5fSchristos * 94942542f5fSchristos * We also have to be careful in case that the stale buffers are 95042542f5fSchristos * now attached to invalid (non-DRI) pixmaps. 95142542f5fSchristos */ 95242542f5fSchristos 95342542f5fSchristos assert(is_front(dst->attachment) || is_front(src->attachment)); 95442542f5fSchristos assert(dst->attachment != src->attachment); 95542542f5fSchristos 95642542f5fSchristos clip.extents.x1 = draw->x; 95742542f5fSchristos clip.extents.y1 = draw->y; 95842542f5fSchristos clip.extents.x2 = draw->x + draw->width; 95942542f5fSchristos clip.extents.y2 = draw->y + draw->height; 96042542f5fSchristos clip.data = NULL; 96142542f5fSchristos 96242542f5fSchristos if (region) { 96342542f5fSchristos pixman_region_translate(region, draw->x, draw->y); 96442542f5fSchristos pixman_region_intersect(&clip, &clip, region); 96542542f5fSchristos region = &clip; 96642542f5fSchristos } 96742542f5fSchristos 96842542f5fSchristos if (clip.extents.x1 >= clip.extents.x2 || 96942542f5fSchristos clip.extents.y1 >= clip.extents.y2) { 97042542f5fSchristos DBG(("%s: all clipped\n", __FUNCTION__)); 97142542f5fSchristos return NULL; 97242542f5fSchristos } 97342542f5fSchristos 97442542f5fSchristos sx = sy = dx = dy = 0; 97542542f5fSchristos if (is_front(dst->attachment)) { 97642542f5fSchristos sx = -draw->x; 97742542f5fSchristos sy = -draw->y; 97842542f5fSchristos } else { 97942542f5fSchristos dx = -draw->x; 98042542f5fSchristos dy = -draw->y; 98142542f5fSchristos } 98242542f5fSchristos if (draw->type == DRAWABLE_WINDOW) { 98342542f5fSchristos WindowPtr win = (WindowPtr)draw; 98442542f5fSchristos int16_t tx, ty; 98542542f5fSchristos 98642542f5fSchristos if (is_clipped(&win->clipList, draw)) { 98742542f5fSchristos DBG(("%s: draw=(%d, %d), delta=(%d, %d), draw=(%d, %d),(%d, %d), clip.extents=(%d, %d), (%d, %d)\n", 98842542f5fSchristos __FUNCTION__, draw->x, draw->y, 98942542f5fSchristos get_drawable_dx(draw), get_drawable_dy(draw), 99042542f5fSchristos clip.extents.x1, clip.extents.y1, 99142542f5fSchristos clip.extents.x2, clip.extents.y2, 99242542f5fSchristos win->clipList.extents.x1, win->clipList.extents.y1, 99342542f5fSchristos win->clipList.extents.x2, win->clipList.extents.y2)); 99442542f5fSchristos 99542542f5fSchristos assert(region == NULL || region == &clip); 99642542f5fSchristos pixman_region_intersect(&clip, &win->clipList, &clip); 99742542f5fSchristos if (!pixman_region_not_empty(&clip)) { 99842542f5fSchristos DBG(("%s: all clipped\n", __FUNCTION__)); 99942542f5fSchristos return NULL; 100042542f5fSchristos } 100142542f5fSchristos 100242542f5fSchristos region = &clip; 100342542f5fSchristos } 100442542f5fSchristos 100542542f5fSchristos if (get_drawable_deltas(draw, pixmap, &tx, &ty)) { 100642542f5fSchristos if (is_front(dst->attachment)) { 100742542f5fSchristos pixman_region_translate(region ?: &clip, tx, ty); 100842542f5fSchristos sx -= tx; 100942542f5fSchristos sy -= ty; 101042542f5fSchristos } else { 101142542f5fSchristos sx += tx; 101242542f5fSchristos sy += ty; 101342542f5fSchristos } 101442542f5fSchristos } 101542542f5fSchristos } else 101642542f5fSchristos sync = false; 101742542f5fSchristos 101842542f5fSchristos scratch.x = scratch.y = 0; 101942542f5fSchristos scratch.width = scratch.height = 0; 102042542f5fSchristos scratch.depth = draw->depth; 102142542f5fSchristos scratch.bitsPerPixel = draw->bitsPerPixel; 102242542f5fSchristos 102342542f5fSchristos src_bo = src_priv->bo; 102442542f5fSchristos assert(src_bo->refcnt); 102542542f5fSchristos if (is_front(src->attachment)) { 102642542f5fSchristos struct sna_pixmap *priv; 102742542f5fSchristos 102842542f5fSchristos priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ); 102942542f5fSchristos if (priv) 103042542f5fSchristos src_bo = priv->gpu_bo; 103142542f5fSchristos DBG(("%s: updated FrontLeft src_bo from handle=%d to handle=%d\n", 103242542f5fSchristos __FUNCTION__, src_priv->bo->handle, src_bo->handle)); 103342542f5fSchristos assert(src_bo->refcnt); 103442542f5fSchristos } else { 103542542f5fSchristos RegionRec source; 103642542f5fSchristos 103742542f5fSchristos scratch.width = src_priv->size & 0xffff; 103842542f5fSchristos scratch.height = src_priv->size >> 16; 103942542f5fSchristos src_draw = &scratch; 104042542f5fSchristos 104142542f5fSchristos DBG(("%s: source size %dx%d, region size %dx%d\n", 104242542f5fSchristos __FUNCTION__, 104342542f5fSchristos scratch.width, scratch.height, 104442542f5fSchristos clip.extents.x2 - clip.extents.x1, 104542542f5fSchristos clip.extents.y2 - clip.extents.y1)); 104642542f5fSchristos 104742542f5fSchristos source.extents.x1 = -sx; 104842542f5fSchristos source.extents.y1 = -sy; 104942542f5fSchristos source.extents.x2 = source.extents.x1 + scratch.width; 105042542f5fSchristos source.extents.y2 = source.extents.y1 + scratch.height; 105142542f5fSchristos source.data = NULL; 105242542f5fSchristos 105342542f5fSchristos assert(region == NULL || region == &clip); 105442542f5fSchristos pixman_region_intersect(&clip, &clip, &source); 105542542f5fSchristos 105642542f5fSchristos } 105742542f5fSchristos 105842542f5fSchristos dst_bo = dst_priv->bo; 105942542f5fSchristos assert(dst_bo->refcnt); 106042542f5fSchristos if (is_front(dst->attachment)) { 106142542f5fSchristos struct sna_pixmap *priv; 106242542f5fSchristos unsigned int flags; 106342542f5fSchristos 106442542f5fSchristos flags = MOVE_WRITE | __MOVE_FORCE; 106542542f5fSchristos if (clip.data) 106642542f5fSchristos flags |= MOVE_READ; 106742542f5fSchristos 106842542f5fSchristos assert(region == NULL || region == &clip); 106942542f5fSchristos priv = sna_pixmap_move_area_to_gpu(pixmap, &clip.extents, flags); 107042542f5fSchristos if (priv) { 107142542f5fSchristos damage(pixmap, priv, region); 107242542f5fSchristos dst_bo = priv->gpu_bo; 107342542f5fSchristos } 107442542f5fSchristos DBG(("%s: updated FrontLeft dst_bo from handle=%d to handle=%d\n", 107542542f5fSchristos __FUNCTION__, dst_priv->bo->handle, dst_bo->handle)); 107642542f5fSchristos assert(dst_bo->refcnt); 107742542f5fSchristos } else { 107842542f5fSchristos RegionRec target; 107942542f5fSchristos 108042542f5fSchristos scratch.width = dst_priv->size & 0xffff; 108142542f5fSchristos scratch.height = dst_priv->size >> 16; 108242542f5fSchristos dst_draw = &scratch; 108342542f5fSchristos 108442542f5fSchristos DBG(("%s: target size %dx%d, region size %dx%d\n", 108542542f5fSchristos __FUNCTION__, 108642542f5fSchristos scratch.width, scratch.height, 108742542f5fSchristos clip.extents.x2 - clip.extents.x1, 108842542f5fSchristos clip.extents.y2 - clip.extents.y1)); 108942542f5fSchristos 109042542f5fSchristos target.extents.x1 = -dx; 109142542f5fSchristos target.extents.y1 = -dy; 109242542f5fSchristos target.extents.x2 = target.extents.x1 + scratch.width; 109342542f5fSchristos target.extents.y2 = target.extents.y1 + scratch.height; 109442542f5fSchristos target.data = NULL; 109542542f5fSchristos 109642542f5fSchristos assert(region == NULL || region == &clip); 109742542f5fSchristos pixman_region_intersect(&clip, &clip, &target); 109842542f5fSchristos 109942542f5fSchristos sync = false; 110042542f5fSchristos } 110142542f5fSchristos 110242542f5fSchristos if (!wedged(sna)) { 110342542f5fSchristos xf86CrtcPtr crtc; 110442542f5fSchristos 110542542f5fSchristos crtc = NULL; 110642542f5fSchristos if (sync && sna_pixmap_is_scanout(sna, pixmap)) 110742542f5fSchristos crtc = sna_covering_crtc(sna, &clip.extents, NULL); 110842542f5fSchristos sna_dri2_select_mode(sna, dst_bo, src_bo, crtc != NULL); 110942542f5fSchristos 111042542f5fSchristos sync = (crtc != NULL&& 111142542f5fSchristos sna_wait_for_scanline(sna, pixmap, crtc, 111242542f5fSchristos &clip.extents)); 111342542f5fSchristos } 111442542f5fSchristos 111542542f5fSchristos if (region) { 111642542f5fSchristos boxes = region_rects(region); 111742542f5fSchristos n = region_num_rects(region); 111842542f5fSchristos assert(n); 111942542f5fSchristos } else { 112042542f5fSchristos region = &clip; 112142542f5fSchristos boxes = &clip.extents; 112242542f5fSchristos n = 1; 112342542f5fSchristos } 112442542f5fSchristos DamageRegionAppend(&pixmap->drawable, region); 112542542f5fSchristos 112642542f5fSchristos if (wedged(sna)) { 112742542f5fSchristosfallback: 112842542f5fSchristos sna_dri2_copy_fallback(sna, src_draw, 112942542f5fSchristos src_bo, sx, sy, 113042542f5fSchristos dst_bo, dx, dy, 113142542f5fSchristos boxes, n); 113242542f5fSchristos } else { 113342542f5fSchristos unsigned flags; 113442542f5fSchristos 113542542f5fSchristos DBG(("%s: copying [(%d, %d), (%d, %d)]x%d src=(%d, %d), dst=(%d, %d)\n", 113642542f5fSchristos __FUNCTION__, 113742542f5fSchristos boxes[0].x1, boxes[0].y1, 113842542f5fSchristos boxes[0].x2, boxes[0].y2, 113942542f5fSchristos n, sx, sy, dx, dy)); 114042542f5fSchristos 114142542f5fSchristos flags = COPY_LAST; 114242542f5fSchristos if (sync) 114342542f5fSchristos flags |= COPY_SYNC; 114442542f5fSchristos if (!sna->render.copy_boxes(sna, GXcopy, 114542542f5fSchristos src_draw, src_bo, sx, sy, 114642542f5fSchristos dst_draw, dst_bo, dx, dy, 114742542f5fSchristos boxes, n, flags)) 114842542f5fSchristos goto fallback; 114942542f5fSchristos 115042542f5fSchristos DBG(("%s: flushing? %d\n", __FUNCTION__, sync)); 115142542f5fSchristos if (sync) { /* STAT! */ 115242542f5fSchristos struct kgem_request *rq = sna->kgem.next_request; 115342542f5fSchristos kgem_submit(&sna->kgem); 115442542f5fSchristos if (rq->bo) { 115542542f5fSchristos bo = ref(rq->bo); 115642542f5fSchristos DBG(("%s: recording sync fence handle=%d\n", __FUNCTION__, bo->handle)); 115742542f5fSchristos } 115842542f5fSchristos } 115942542f5fSchristos } 116042542f5fSchristos 116142542f5fSchristos DamageRegionProcessPending(&pixmap->drawable); 116242542f5fSchristos 116342542f5fSchristos if (clip.data) 116442542f5fSchristos pixman_region_fini(&clip); 116542542f5fSchristos 116642542f5fSchristos return bo; 116742542f5fSchristos} 116842542f5fSchristos 116942542f5fSchristosstatic void 117042542f5fSchristossna_dri2_copy_region(DrawablePtr draw, 117142542f5fSchristos RegionPtr region, 117242542f5fSchristos DRI2BufferPtr dst, 117342542f5fSchristos DRI2BufferPtr src) 117442542f5fSchristos{ 117542542f5fSchristos PixmapPtr pixmap = get_drawable_pixmap(draw); 117642542f5fSchristos struct sna *sna = to_sna_from_pixmap(pixmap); 117742542f5fSchristos 117842542f5fSchristos DBG(("%s: pixmap=%ld, src=%u (refs=%d/%d, flush=%d, attach=%d) , dst=%u (refs=%d/%d, flush=%d, attach=%d)\n", 117942542f5fSchristos __FUNCTION__, 118042542f5fSchristos pixmap->drawable.serialNumber, 118142542f5fSchristos get_private(src)->bo->handle, 118242542f5fSchristos get_private(src)->refcnt, 118342542f5fSchristos get_private(src)->bo->refcnt, 118442542f5fSchristos get_private(src)->bo->flush, 118542542f5fSchristos src->attachment, 118642542f5fSchristos get_private(dst)->bo->handle, 118742542f5fSchristos get_private(dst)->refcnt, 118842542f5fSchristos get_private(dst)->bo->refcnt, 118942542f5fSchristos get_private(dst)->bo->flush, 119042542f5fSchristos dst->attachment)); 119142542f5fSchristos 119242542f5fSchristos assert(src != dst); 119342542f5fSchristos 119442542f5fSchristos assert(get_private(src)->refcnt); 119542542f5fSchristos assert(get_private(dst)->refcnt); 119642542f5fSchristos 119742542f5fSchristos assert(get_private(src)->bo->refcnt); 119842542f5fSchristos assert(get_private(dst)->bo->refcnt); 119942542f5fSchristos 120042542f5fSchristos DBG(("%s: region (%d, %d), (%d, %d) x %d\n", 120142542f5fSchristos __FUNCTION__, 120242542f5fSchristos region->extents.x1, region->extents.y1, 120342542f5fSchristos region->extents.x2, region->extents.y2, 120442542f5fSchristos region_num_rects(region))); 120542542f5fSchristos 120642542f5fSchristos __sna_dri2_copy_region(sna, draw, region, src, dst, false); 120742542f5fSchristos} 120842542f5fSchristos 120942542f5fSchristosinline static uint32_t pipe_select(int pipe) 121042542f5fSchristos{ 121142542f5fSchristos /* The third pipe was introduced with IvyBridge long after 121242542f5fSchristos * multiple pipe support was added to the kernel, hence 121342542f5fSchristos * we can safely ignore the capability check - if we have more 121442542f5fSchristos * than two pipes, we can assume that they are fully supported. 121542542f5fSchristos */ 121642542f5fSchristos if (pipe > 1) 121742542f5fSchristos return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT; 121842542f5fSchristos else if (pipe > 0) 121942542f5fSchristos return DRM_VBLANK_SECONDARY; 122042542f5fSchristos else 122142542f5fSchristos return 0; 122242542f5fSchristos} 122342542f5fSchristos 122442542f5fSchristosstatic inline int sna_wait_vblank(struct sna *sna, union drm_wait_vblank *vbl, int pipe) 122542542f5fSchristos{ 122642542f5fSchristos DBG(("%s(pipe=%d, waiting until seq=%u%s)\n", 122742542f5fSchristos __FUNCTION__, pipe, vbl->request.sequence, 122842542f5fSchristos vbl->request.type & DRM_VBLANK_RELATIVE ? " [relative]" : "")); 122942542f5fSchristos assert(pipe != -1); 123042542f5fSchristos 123142542f5fSchristos vbl->request.type |= pipe_select(pipe); 123242542f5fSchristos return drmIoctl(sna->kgem.fd, DRM_IOCTL_WAIT_VBLANK, vbl); 123342542f5fSchristos} 123442542f5fSchristos 123542542f5fSchristos#if DRI2INFOREC_VERSION >= 4 123642542f5fSchristos 123742542f5fSchristosstatic void dri2_window_attach(WindowPtr win, struct dri2_window *priv) 123842542f5fSchristos{ 123942542f5fSchristos assert(win->drawable.type == DRAWABLE_WINDOW); 124042542f5fSchristos assert(dri2_window(win) == NULL); 124142542f5fSchristos ((void **)__get_private(win, sna_window_key))[1] = priv; 124242542f5fSchristos assert(dri2_window(win) == priv); 124342542f5fSchristos} 124442542f5fSchristos 124542542f5fSchristosstatic uint64_t 124642542f5fSchristosdraw_current_msc(DrawablePtr draw, xf86CrtcPtr crtc, uint64_t msc) 124742542f5fSchristos{ 124842542f5fSchristos struct dri2_window *priv; 124942542f5fSchristos 125042542f5fSchristos if (draw->type != DRAWABLE_WINDOW) 125142542f5fSchristos return msc; 125242542f5fSchristos 125342542f5fSchristos priv = dri2_window((WindowPtr)draw); 125442542f5fSchristos if (priv == NULL) { 125542542f5fSchristos priv = malloc(sizeof(*priv)); 125642542f5fSchristos if (priv != NULL) { 125742542f5fSchristos priv->front = NULL; 125842542f5fSchristos priv->crtc = crtc; 125942542f5fSchristos priv->msc_delta = 0; 126042542f5fSchristos priv->chain = NULL; 126142542f5fSchristos dri2_window_attach((WindowPtr)draw, priv); 126242542f5fSchristos } 126342542f5fSchristos } else { 126442542f5fSchristos if (priv->crtc != crtc) { 126542542f5fSchristos const struct ust_msc *last = sna_crtc_last_swap(priv->crtc); 126642542f5fSchristos const struct ust_msc *this = sna_crtc_last_swap(crtc); 126742542f5fSchristos DBG(("%s: Window transferring from pipe=%d [msc=%llu] to pipe=%d [msc=%llu], delta now %lld\n", 126842542f5fSchristos __FUNCTION__, 126942542f5fSchristos sna_crtc_to_pipe(priv->crtc), (long long)last->msc, 127042542f5fSchristos sna_crtc_to_pipe(crtc), (long long)this->msc, 127142542f5fSchristos (long long)(priv->msc_delta + this->msc - last->msc))); 127242542f5fSchristos priv->msc_delta += this->msc - last->msc; 127342542f5fSchristos priv->crtc = crtc; 127442542f5fSchristos } 127542542f5fSchristos msc -= priv->msc_delta; 127642542f5fSchristos } 127742542f5fSchristos return msc; 127842542f5fSchristos} 127942542f5fSchristos 128042542f5fSchristosstatic uint32_t 128142542f5fSchristosdraw_target_seq(DrawablePtr draw, uint64_t msc) 128242542f5fSchristos{ 128342542f5fSchristos struct dri2_window *priv = dri2_window((WindowPtr)draw); 128442542f5fSchristos if (priv == NULL) 128542542f5fSchristos return msc; 128642542f5fSchristos DBG(("%s: converting target_msc=%llu to seq %u\n", 128742542f5fSchristos __FUNCTION__, (long long)msc, (unsigned)(msc + priv->msc_delta))); 128842542f5fSchristos return msc + priv->msc_delta; 128942542f5fSchristos} 129042542f5fSchristos 129142542f5fSchristosstatic xf86CrtcPtr 129242542f5fSchristossna_dri2_get_crtc(DrawablePtr draw) 129342542f5fSchristos{ 129442542f5fSchristos struct sna *sna = to_sna_from_drawable(draw); 129542542f5fSchristos BoxRec box; 129642542f5fSchristos 129742542f5fSchristos if (draw->type == DRAWABLE_PIXMAP) 129842542f5fSchristos return NULL; 129942542f5fSchristos 130042542f5fSchristos box.x1 = draw->x; 130142542f5fSchristos box.y1 = draw->y; 130242542f5fSchristos box.x2 = box.x1 + draw->width; 130342542f5fSchristos box.y2 = box.y1 + draw->height; 130442542f5fSchristos 130542542f5fSchristos /* Make sure the CRTC is valid and this is the real front buffer */ 130642542f5fSchristos return sna_covering_crtc(sna, &box, NULL); 130742542f5fSchristos} 130842542f5fSchristos 130942542f5fSchristosstatic void 131042542f5fSchristossna_dri2_remove_event(WindowPtr win, struct sna_dri2_event *info) 131142542f5fSchristos{ 131242542f5fSchristos struct dri2_window *priv; 131342542f5fSchristos struct sna_dri2_event *chain; 131442542f5fSchristos 131542542f5fSchristos assert(win->drawable.type == DRAWABLE_WINDOW); 131642542f5fSchristos DBG(("%s: remove[%p] from window %ld, active? %d\n", 131742542f5fSchristos __FUNCTION__, info, (long)win->drawable.id, info->draw != NULL)); 131842542f5fSchristos 131942542f5fSchristos priv = dri2_window(win); 132042542f5fSchristos assert(priv); 132142542f5fSchristos assert(priv->chain != NULL); 132242542f5fSchristos 132342542f5fSchristos if (priv->chain == info) { 132442542f5fSchristos priv->chain = info->chain; 132542542f5fSchristos return; 132642542f5fSchristos } 132742542f5fSchristos 132842542f5fSchristos chain = priv->chain; 132942542f5fSchristos while (chain->chain != info) 133042542f5fSchristos chain = chain->chain; 133142542f5fSchristos assert(chain != info); 133242542f5fSchristos assert(info->chain != chain); 133342542f5fSchristos chain->chain = info->chain; 133442542f5fSchristos} 133542542f5fSchristos 133642542f5fSchristosstatic void 133742542f5fSchristossna_dri2_event_free(struct sna *sna, 133842542f5fSchristos struct sna_dri2_event *info) 133942542f5fSchristos{ 134042542f5fSchristos DrawablePtr draw = info->draw; 134142542f5fSchristos 134242542f5fSchristos DBG(("%s(draw?=%d)\n", __FUNCTION__, draw != NULL)); 134342542f5fSchristos if (draw && draw->type == DRAWABLE_WINDOW) 134442542f5fSchristos sna_dri2_remove_event((WindowPtr)draw, info); 134542542f5fSchristos 134642542f5fSchristos _sna_dri2_destroy_buffer(sna, info->front); 134742542f5fSchristos _sna_dri2_destroy_buffer(sna, info->back); 134842542f5fSchristos 134942542f5fSchristos while (!list_is_empty(&info->cache)) { 135042542f5fSchristos struct dri_bo *c; 135142542f5fSchristos 135242542f5fSchristos c = list_first_entry(&info->cache, struct dri_bo, link); 135342542f5fSchristos list_del(&c->link); 135442542f5fSchristos 135542542f5fSchristos DBG(("%s: releasing cached handle=%d\n", __FUNCTION__, c->bo ? c->bo->handle : 0)); 135642542f5fSchristos if (c->bo) 135742542f5fSchristos kgem_bo_destroy(&sna->kgem, c->bo); 135842542f5fSchristos 135942542f5fSchristos free(c); 136042542f5fSchristos } 136142542f5fSchristos 136242542f5fSchristos if (info->bo) { 136342542f5fSchristos DBG(("%s: releasing batch handle=%d\n", __FUNCTION__, info->bo->handle)); 136442542f5fSchristos kgem_bo_destroy(&sna->kgem, info->bo); 136542542f5fSchristos } 136642542f5fSchristos 136742542f5fSchristos _list_del(&info->link); 136842542f5fSchristos free(info); 136942542f5fSchristos} 137042542f5fSchristos 137142542f5fSchristosstatic void 137242542f5fSchristossna_dri2_client_gone(CallbackListPtr *list, void *closure, void *data) 137342542f5fSchristos{ 137442542f5fSchristos NewClientInfoRec *clientinfo = data; 137542542f5fSchristos ClientPtr client = clientinfo->client; 137642542f5fSchristos struct sna_client *priv = sna_client(client); 137742542f5fSchristos struct sna *sna = closure; 137842542f5fSchristos 137942542f5fSchristos if (priv->events.next == NULL) 138042542f5fSchristos return; 138142542f5fSchristos 138242542f5fSchristos if (client->clientState != ClientStateGone) 138342542f5fSchristos return; 138442542f5fSchristos 138542542f5fSchristos DBG(("%s(active?=%d)\n", __FUNCTION__, 138642542f5fSchristos !list_is_empty(&priv->events))); 138742542f5fSchristos 138842542f5fSchristos while (!list_is_empty(&priv->events)) { 138942542f5fSchristos struct sna_dri2_event *event; 139042542f5fSchristos 139142542f5fSchristos event = list_first_entry(&priv->events, struct sna_dri2_event, link); 139242542f5fSchristos assert(event->client == client); 139342542f5fSchristos 139442542f5fSchristos if (event->queued) { 139542542f5fSchristos if (event->draw) 139642542f5fSchristos sna_dri2_remove_event((WindowPtr)event->draw, 139742542f5fSchristos event); 139842542f5fSchristos event->client = NULL; 139942542f5fSchristos event->draw = NULL; 140042542f5fSchristos list_del(&event->link); 140142542f5fSchristos } else 140242542f5fSchristos sna_dri2_event_free(sna, event); 140342542f5fSchristos } 140442542f5fSchristos 140542542f5fSchristos if (--sna->dri2.client_count == 0) 140642542f5fSchristos DeleteCallback(&ClientStateCallback, sna_dri2_client_gone, sna); 140742542f5fSchristos} 140842542f5fSchristos 140942542f5fSchristosstatic bool add_event_to_client(struct sna_dri2_event *info, struct sna *sna, ClientPtr client) 141042542f5fSchristos{ 141142542f5fSchristos struct sna_client *priv = sna_client(client); 141242542f5fSchristos 141342542f5fSchristos if (priv->events.next == NULL) { 141442542f5fSchristos if (sna->dri2.client_count++ == 0 && 141542542f5fSchristos !AddCallback(&ClientStateCallback, sna_dri2_client_gone, sna)) 141642542f5fSchristos return false; 141742542f5fSchristos 141842542f5fSchristos list_init(&priv->events); 141942542f5fSchristos } 142042542f5fSchristos 142142542f5fSchristos list_add(&info->link, &priv->events); 142242542f5fSchristos info->client = client; 142342542f5fSchristos return true; 142442542f5fSchristos} 142542542f5fSchristos 142642542f5fSchristosstatic struct sna_dri2_event * 142742542f5fSchristossna_dri2_add_event(struct sna *sna, DrawablePtr draw, ClientPtr client) 142842542f5fSchristos{ 142942542f5fSchristos struct dri2_window *priv; 143042542f5fSchristos struct sna_dri2_event *info, *chain; 143142542f5fSchristos 143242542f5fSchristos assert(draw->type == DRAWABLE_WINDOW); 143342542f5fSchristos DBG(("%s: adding event to window %ld)\n", 143442542f5fSchristos __FUNCTION__, (long)draw->id)); 143542542f5fSchristos 143642542f5fSchristos priv = dri2_window((WindowPtr)draw); 143742542f5fSchristos if (priv == NULL) 143842542f5fSchristos return NULL; 143942542f5fSchristos 144042542f5fSchristos info = calloc(1, sizeof(struct sna_dri2_event)); 144142542f5fSchristos if (info == NULL) 144242542f5fSchristos return NULL; 144342542f5fSchristos 144442542f5fSchristos list_init(&info->cache); 144542542f5fSchristos info->draw = draw; 144642542f5fSchristos info->crtc = priv->crtc; 144742542f5fSchristos info->pipe = sna_crtc_to_pipe(priv->crtc); 144842542f5fSchristos 144942542f5fSchristos if (!add_event_to_client(info, sna, client)) { 145042542f5fSchristos free(info); 145142542f5fSchristos return NULL; 145242542f5fSchristos } 145342542f5fSchristos 145442542f5fSchristos assert(priv->chain != info); 145542542f5fSchristos 145642542f5fSchristos if (priv->chain == NULL) { 145742542f5fSchristos priv->chain = info; 145842542f5fSchristos return info; 145942542f5fSchristos } 146042542f5fSchristos 146142542f5fSchristos chain = priv->chain; 146242542f5fSchristos while (chain->chain != NULL) 146342542f5fSchristos chain = chain->chain; 146442542f5fSchristos 146542542f5fSchristos assert(chain != info); 146642542f5fSchristos chain->chain = info; 146742542f5fSchristos return info; 146842542f5fSchristos} 146942542f5fSchristos 147042542f5fSchristosvoid sna_dri2_destroy_window(WindowPtr win) 147142542f5fSchristos{ 147242542f5fSchristos struct sna *sna; 147342542f5fSchristos struct dri2_window *priv; 147442542f5fSchristos 147542542f5fSchristos priv = dri2_window(win); 147642542f5fSchristos if (priv == NULL) 147742542f5fSchristos return; 147842542f5fSchristos 147942542f5fSchristos DBG(("%s: window=%ld\n", __FUNCTION__, win->drawable.serialNumber)); 148042542f5fSchristos sna = to_sna_from_drawable(&win->drawable); 148142542f5fSchristos 148242542f5fSchristos if (priv->front) { 148342542f5fSchristos assert(priv->crtc); 148442542f5fSchristos sna_shadow_unset_crtc(sna, priv->crtc); 148542542f5fSchristos _sna_dri2_destroy_buffer(sna, priv->front); 148642542f5fSchristos } 148742542f5fSchristos 148842542f5fSchristos if (priv->chain) { 148942542f5fSchristos struct sna_dri2_event *info, *chain; 149042542f5fSchristos 149142542f5fSchristos DBG(("%s: freeing chain\n", __FUNCTION__)); 149242542f5fSchristos 149342542f5fSchristos chain = priv->chain; 149442542f5fSchristos while ((info = chain)) { 149542542f5fSchristos info->draw = NULL; 149642542f5fSchristos info->client = NULL; 149742542f5fSchristos 149842542f5fSchristos chain = info->chain; 149942542f5fSchristos info->chain = NULL; 150042542f5fSchristos 150142542f5fSchristos if (!info->queued) 150242542f5fSchristos sna_dri2_event_free(sna, info); 150342542f5fSchristos } 150442542f5fSchristos } 150542542f5fSchristos 150642542f5fSchristos free(priv); 150742542f5fSchristos} 150842542f5fSchristos 150942542f5fSchristosstatic void 151042542f5fSchristossna_dri2_flip_handler(struct sna *sna, 151142542f5fSchristos struct drm_event_vblank *event, 151242542f5fSchristos void *data) 151342542f5fSchristos{ 151442542f5fSchristos DBG(("%s: sequence=%d\n", __FUNCTION__, event->sequence)); 151542542f5fSchristos sna_dri2_flip_event(sna, data); 151642542f5fSchristos} 151742542f5fSchristos 151842542f5fSchristosstatic bool 151942542f5fSchristossna_dri2_flip(struct sna *sna, struct sna_dri2_event *info) 152042542f5fSchristos{ 152142542f5fSchristos struct kgem_bo *bo = get_private(info->back)->bo; 152242542f5fSchristos struct kgem_bo *tmp_bo; 152342542f5fSchristos uint32_t tmp_name; 152442542f5fSchristos 152542542f5fSchristos DBG(("%s(type=%d)\n", __FUNCTION__, info->type)); 152642542f5fSchristos 152742542f5fSchristos assert(sna_pixmap_get_buffer(sna->front) == info->front); 152842542f5fSchristos assert(get_drawable_pixmap(info->draw)->drawable.height * bo->pitch <= kgem_bo_size(bo)); 152942542f5fSchristos assert(bo->refcnt); 153042542f5fSchristos 153142542f5fSchristos if (!sna_page_flip(sna, bo, sna_dri2_flip_handler, 153242542f5fSchristos info->type == FLIP_ASYNC ? NULL : info)) 153342542f5fSchristos return false; 153442542f5fSchristos 153542542f5fSchristos assert(sna->dri2.flip_pending == NULL || sna->dri2.flip_pending == info); 153642542f5fSchristos if (info->type != FLIP_ASYNC) 153742542f5fSchristos sna->dri2.flip_pending = info; 153842542f5fSchristos 153942542f5fSchristos DBG(("%s: marked handle=%d as scanout, swap front (handle=%d, name=%d) and back (handle=%d, name=%d)\n", 154042542f5fSchristos __FUNCTION__, bo->handle, 154142542f5fSchristos get_private(info->front)->bo->handle, info->front->name, 154242542f5fSchristos get_private(info->back)->bo->handle, info->back->name)); 154342542f5fSchristos 154442542f5fSchristos tmp_bo = get_private(info->front)->bo; 154542542f5fSchristos tmp_name = info->front->name; 154642542f5fSchristos 154742542f5fSchristos set_bo(sna->front, bo); 154842542f5fSchristos 154942542f5fSchristos info->front->name = info->back->name; 155042542f5fSchristos get_private(info->front)->bo = bo; 155142542f5fSchristos 155242542f5fSchristos info->back->name = tmp_name; 155342542f5fSchristos get_private(info->back)->bo = tmp_bo; 155442542f5fSchristos get_private(info->back)->stale = true; 155542542f5fSchristos 155642542f5fSchristos assert(get_private(info->front)->bo->refcnt); 155742542f5fSchristos assert(get_private(info->back)->bo->refcnt); 155842542f5fSchristos assert(get_private(info->front)->bo != get_private(info->back)->bo); 155942542f5fSchristos 156042542f5fSchristos info->queued = true; 156142542f5fSchristos return true; 156242542f5fSchristos} 156342542f5fSchristos 156442542f5fSchristosstatic bool 156542542f5fSchristoscan_flip(struct sna * sna, 156642542f5fSchristos DrawablePtr draw, 156742542f5fSchristos DRI2BufferPtr front, 156842542f5fSchristos DRI2BufferPtr back, 156942542f5fSchristos xf86CrtcPtr crtc) 157042542f5fSchristos{ 157142542f5fSchristos WindowPtr win = (WindowPtr)draw; 157242542f5fSchristos PixmapPtr pixmap; 157342542f5fSchristos 157442542f5fSchristos assert((sna->flags & SNA_NO_WAIT) == 0); 157542542f5fSchristos 157642542f5fSchristos if (!DBG_CAN_FLIP) 157742542f5fSchristos return false; 157842542f5fSchristos 157942542f5fSchristos if (draw->type == DRAWABLE_PIXMAP) 158042542f5fSchristos return false; 158142542f5fSchristos 158242542f5fSchristos if (!sna->mode.front_active) { 158342542f5fSchristos DBG(("%s: no, active CRTC\n", __FUNCTION__)); 158442542f5fSchristos return false; 158542542f5fSchristos } 158642542f5fSchristos 158742542f5fSchristos assert(sna->scrn->vtSema); 158842542f5fSchristos 158942542f5fSchristos if ((sna->flags & (SNA_HAS_FLIP | SNA_HAS_ASYNC_FLIP)) == 0) { 159042542f5fSchristos DBG(("%s: no, pageflips disabled\n", __FUNCTION__)); 159142542f5fSchristos return false; 159242542f5fSchristos } 159342542f5fSchristos 159442542f5fSchristos if (front->format != back->format) { 159542542f5fSchristos DBG(("%s: no, format mismatch, front = %d, back = %d\n", 159642542f5fSchristos __FUNCTION__, front->format, back->format)); 159742542f5fSchristos return false; 159842542f5fSchristos } 159942542f5fSchristos 160042542f5fSchristos if (sna->mode.shadow_active) { 160142542f5fSchristos DBG(("%s: no, shadow enabled\n", __FUNCTION__)); 160242542f5fSchristos return false; 160342542f5fSchristos } 160442542f5fSchristos 160542542f5fSchristos if (!sna_crtc_is_on(crtc)) { 160642542f5fSchristos DBG(("%s: ref-pipe=%d is disabled\n", __FUNCTION__, sna_crtc_to_pipe(crtc))); 160742542f5fSchristos return false; 160842542f5fSchristos } 160942542f5fSchristos 161042542f5fSchristos pixmap = get_window_pixmap(win); 161142542f5fSchristos if (pixmap != sna->front) { 161242542f5fSchristos DBG(("%s: no, window (pixmap=%ld) is not attached to the front buffer (pixmap=%ld)\n", 161342542f5fSchristos __FUNCTION__, pixmap->drawable.serialNumber, sna->front->drawable.serialNumber)); 161442542f5fSchristos return false; 161542542f5fSchristos } 161642542f5fSchristos 161742542f5fSchristos if (sna_pixmap_get_buffer(pixmap) != front) { 161842542f5fSchristos DBG(("%s: no, DRI2 drawable is no longer attached (old name=%d, new name=%d) to pixmap=%ld\n", 161942542f5fSchristos __FUNCTION__, front->name, 162042542f5fSchristos sna_pixmap_get_buffer(pixmap) ? ((DRI2BufferPtr)sna_pixmap_get_buffer(pixmap))->name : 0, 162142542f5fSchristos pixmap->drawable.serialNumber)); 162242542f5fSchristos return false; 162342542f5fSchristos } 162442542f5fSchristos 162542542f5fSchristos assert(get_private(front)->pixmap == sna->front); 162642542f5fSchristos assert(sna_pixmap(sna->front)->gpu_bo == get_private(front)->bo); 162742542f5fSchristos 162842542f5fSchristos if (!get_private(back)->bo->scanout) { 162942542f5fSchristos DBG(("%s: no, DRI2 drawable was too small at time of creation)\n", 163042542f5fSchristos __FUNCTION__)); 163142542f5fSchristos return false; 163242542f5fSchristos } 163342542f5fSchristos 163442542f5fSchristos if (get_private(back)->size != get_private(front)->size) { 163542542f5fSchristos DBG(("%s: no, DRI2 drawable does not fit into scanout\n", 163642542f5fSchristos __FUNCTION__)); 163742542f5fSchristos return false; 163842542f5fSchristos } 163942542f5fSchristos 164042542f5fSchristos DBG(("%s: window size: %dx%d, clip=(%d, %d), (%d, %d) x %d\n", 164142542f5fSchristos __FUNCTION__, 164242542f5fSchristos win->drawable.width, win->drawable.height, 164342542f5fSchristos win->clipList.extents.x1, win->clipList.extents.y1, 164442542f5fSchristos win->clipList.extents.x2, win->clipList.extents.y2, 164542542f5fSchristos region_num_rects(&win->clipList))); 164642542f5fSchristos if (!RegionEqual(&win->clipList, &draw->pScreen->root->winSize)) { 164742542f5fSchristos DBG(("%s: no, window is clipped: clip region=(%d, %d), (%d, %d), root size=(%d, %d), (%d, %d)\n", 164842542f5fSchristos __FUNCTION__, 164942542f5fSchristos win->clipList.extents.x1, 165042542f5fSchristos win->clipList.extents.y1, 165142542f5fSchristos win->clipList.extents.x2, 165242542f5fSchristos win->clipList.extents.y2, 165342542f5fSchristos draw->pScreen->root->winSize.extents.x1, 165442542f5fSchristos draw->pScreen->root->winSize.extents.y1, 165542542f5fSchristos draw->pScreen->root->winSize.extents.x2, 165642542f5fSchristos draw->pScreen->root->winSize.extents.y2)); 165742542f5fSchristos return false; 165842542f5fSchristos } 165942542f5fSchristos 166042542f5fSchristos if (draw->x != 0 || draw->y != 0 || 166142542f5fSchristos#ifdef COMPOSITE 166242542f5fSchristos draw->x != pixmap->screen_x || 166342542f5fSchristos draw->y != pixmap->screen_y || 166442542f5fSchristos#endif 166542542f5fSchristos draw->width != pixmap->drawable.width || 166642542f5fSchristos draw->height != pixmap->drawable.height) { 166742542f5fSchristos DBG(("%s: no, window is not full size (%dx%d)!=(%dx%d)\n", 166842542f5fSchristos __FUNCTION__, 166942542f5fSchristos draw->width, draw->height, 167042542f5fSchristos pixmap->drawable.width, 167142542f5fSchristos pixmap->drawable.height)); 167242542f5fSchristos return false; 167342542f5fSchristos } 167442542f5fSchristos 167542542f5fSchristos /* prevent an implicit tiling mode change */ 167642542f5fSchristos if (get_private(back)->bo->tiling > I915_TILING_X) { 167742542f5fSchristos DBG(("%s -- no, tiling mismatch: front %d, back=%d, want-tiled?=%d\n", 167842542f5fSchristos __FUNCTION__, 167942542f5fSchristos get_private(front)->bo->tiling, 168042542f5fSchristos get_private(back)->bo->tiling, 168142542f5fSchristos !!(sna->flags & SNA_LINEAR_FB))); 168242542f5fSchristos return false; 168342542f5fSchristos } 168442542f5fSchristos 168542542f5fSchristos if (get_private(front)->bo->pitch != get_private(back)->bo->pitch) { 168642542f5fSchristos DBG(("%s -- no, pitch mismatch: front %d, back=%d\n", 168742542f5fSchristos __FUNCTION__, 168842542f5fSchristos get_private(front)->bo->pitch, 168942542f5fSchristos get_private(back)->bo->pitch)); 169042542f5fSchristos return false; 169142542f5fSchristos } 169242542f5fSchristos 169342542f5fSchristos if (sna_pixmap(pixmap)->pinned & ~(PIN_DRI2 | PIN_SCANOUT)) { 169442542f5fSchristos DBG(("%s -- no, pinned: front %x\n", 169542542f5fSchristos __FUNCTION__, sna_pixmap(pixmap)->pinned)); 169642542f5fSchristos return false; 169742542f5fSchristos } 169842542f5fSchristos 169942542f5fSchristos DBG(("%s: yes, pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber)); 170042542f5fSchristos assert(dri2_window(win)->front == NULL); 170142542f5fSchristos return true; 170242542f5fSchristos} 170342542f5fSchristos 170442542f5fSchristosstatic bool 170542542f5fSchristoscan_xchg(struct sna * sna, 170642542f5fSchristos DrawablePtr draw, 170742542f5fSchristos DRI2BufferPtr front, 170842542f5fSchristos DRI2BufferPtr back) 170942542f5fSchristos{ 171042542f5fSchristos WindowPtr win = (WindowPtr)draw; 171142542f5fSchristos PixmapPtr pixmap; 171242542f5fSchristos 171342542f5fSchristos if (!DBG_CAN_XCHG) 171442542f5fSchristos return false; 171542542f5fSchristos 171642542f5fSchristos if (draw->type == DRAWABLE_PIXMAP) 171742542f5fSchristos return false; 171842542f5fSchristos 171942542f5fSchristos if (front->format != back->format) { 172042542f5fSchristos DBG(("%s: no, format mismatch, front = %d, back = %d\n", 172142542f5fSchristos __FUNCTION__, front->format, back->format)); 172242542f5fSchristos return false; 172342542f5fSchristos } 172442542f5fSchristos 172542542f5fSchristos pixmap = get_window_pixmap(win); 172642542f5fSchristos if (get_private(front)->pixmap != pixmap) { 172742542f5fSchristos DBG(("%s: no, DRI2 drawable is no longer attached, old pixmap=%ld, now pixmap=%ld\n", 172842542f5fSchristos __FUNCTION__, 172942542f5fSchristos get_private(front)->pixmap->drawable.serialNumber, 173042542f5fSchristos pixmap->drawable.serialNumber)); 173142542f5fSchristos return false; 173242542f5fSchristos } 173342542f5fSchristos 173442542f5fSchristos DBG(("%s: window size: %dx%d, clip=(%d, %d), (%d, %d) x %d, pixmap size=%dx%d\n", 173542542f5fSchristos __FUNCTION__, 173642542f5fSchristos win->drawable.width, win->drawable.height, 173742542f5fSchristos win->clipList.extents.x1, win->clipList.extents.y1, 173842542f5fSchristos win->clipList.extents.x2, win->clipList.extents.y2, 173942542f5fSchristos region_num_rects(&win->clipList), 174042542f5fSchristos pixmap->drawable.width, 174142542f5fSchristos pixmap->drawable.height)); 174242542f5fSchristos if (is_clipped(&win->clipList, &pixmap->drawable)) { 174342542f5fSchristos DBG(("%s: no, %dx%d window is clipped: clip region=(%d, %d), (%d, %d)\n", 174442542f5fSchristos __FUNCTION__, 174542542f5fSchristos draw->width, draw->height, 174642542f5fSchristos win->clipList.extents.x1, 174742542f5fSchristos win->clipList.extents.y1, 174842542f5fSchristos win->clipList.extents.x2, 174942542f5fSchristos win->clipList.extents.y2)); 175042542f5fSchristos return false; 175142542f5fSchristos } 175242542f5fSchristos 175342542f5fSchristos if (get_private(back)->size != get_private(front)->size) { 175442542f5fSchristos DBG(("%s: no, back buffer %dx%d does not match front buffer %dx%d\n", 175542542f5fSchristos __FUNCTION__, 175642542f5fSchristos get_private(back)->size & 0x7fff, (get_private(back)->size >> 16) & 0x7fff, 175742542f5fSchristos get_private(front)->size & 0x7fff, (get_private(front)->size >> 16) & 0x7fff)); 175842542f5fSchristos return false; 175942542f5fSchristos } 176042542f5fSchristos 176142542f5fSchristos if (pixmap == sna->front && !(sna->flags & SNA_TEAR_FREE) && sna->mode.front_active) { 176242542f5fSchristos DBG(("%s: no, front buffer, requires flipping\n", 176342542f5fSchristos __FUNCTION__)); 176442542f5fSchristos return false; 176542542f5fSchristos } 176642542f5fSchristos 176742542f5fSchristos if (sna_pixmap(pixmap)->pinned & ~(PIN_DRI2 | PIN_SCANOUT)) { 176842542f5fSchristos DBG(("%s: no, pinned: %x\n", 176942542f5fSchristos __FUNCTION__, sna_pixmap(pixmap)->pinned)); 177042542f5fSchristos return false; 177142542f5fSchristos } 177242542f5fSchristos 177342542f5fSchristos DBG(("%s: yes, pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber)); 177442542f5fSchristos return true; 177542542f5fSchristos} 177642542f5fSchristos 177742542f5fSchristosstatic bool 177842542f5fSchristosoverlaps_other_crtc(struct sna *sna, xf86CrtcPtr desired) 177942542f5fSchristos{ 178042542f5fSchristos xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); 178142542f5fSchristos int c; 178242542f5fSchristos 178342542f5fSchristos for (c = 0; c < sna->mode.num_real_crtc; c++) { 178442542f5fSchristos xf86CrtcPtr crtc = config->crtc[c]; 178542542f5fSchristos 178642542f5fSchristos if (crtc == desired) 178742542f5fSchristos continue; 178842542f5fSchristos 178942542f5fSchristos if (!crtc->enabled) 179042542f5fSchristos continue; 179142542f5fSchristos 179242542f5fSchristos if (desired->bounds.x1 < crtc->bounds.x2 && 179342542f5fSchristos desired->bounds.x2 > crtc->bounds.x1 && 179442542f5fSchristos desired->bounds.y1 < crtc->bounds.y2 && 179542542f5fSchristos desired->bounds.y2 > crtc->bounds.y1) 179642542f5fSchristos return true; 179742542f5fSchristos } 179842542f5fSchristos 179942542f5fSchristos return false; 180042542f5fSchristos} 180142542f5fSchristos 180242542f5fSchristosstatic bool 180342542f5fSchristoscan_xchg_crtc(struct sna *sna, 180442542f5fSchristos DrawablePtr draw, 180542542f5fSchristos DRI2BufferPtr front, 180642542f5fSchristos DRI2BufferPtr back, 180742542f5fSchristos xf86CrtcPtr crtc) 180842542f5fSchristos{ 180942542f5fSchristos WindowPtr win = (WindowPtr)draw; 181042542f5fSchristos PixmapPtr pixmap; 181142542f5fSchristos 181242542f5fSchristos if (!DBG_CAN_XCHG) 181342542f5fSchristos return false; 181442542f5fSchristos 181542542f5fSchristos if ((sna->flags & SNA_TEAR_FREE) == 0) { 181642542f5fSchristos DBG(("%s: no, requires TearFree\n", 181742542f5fSchristos __FUNCTION__)); 181842542f5fSchristos return false; 181942542f5fSchristos } 182042542f5fSchristos 182142542f5fSchristos if (draw->type == DRAWABLE_PIXMAP) 182242542f5fSchristos return false; 182342542f5fSchristos 182442542f5fSchristos if (front->format != back->format) { 182542542f5fSchristos DBG(("%s: no, format mismatch, front = %d, back = %d\n", 182642542f5fSchristos __FUNCTION__, front->format, back->format)); 182742542f5fSchristos return false; 182842542f5fSchristos } 182942542f5fSchristos 183042542f5fSchristos if (memcmp(&win->clipList.extents, &crtc->bounds, sizeof(crtc->bounds))) { 183142542f5fSchristos DBG(("%s: no, window [(%d, %d), (%d, %d)] does not cover CRTC [(%d, %d), (%d, %d)]\n", 183242542f5fSchristos __FUNCTION__, 183342542f5fSchristos win->clipList.extents.x1, win->clipList.extents.y1, 183442542f5fSchristos win->clipList.extents.x2, win->clipList.extents.y2, 183542542f5fSchristos crtc->bounds.x1, crtc->bounds.y1, 183642542f5fSchristos crtc->bounds.x2, crtc->bounds.y2)); 183742542f5fSchristos return false; 183842542f5fSchristos } 183942542f5fSchristos 184042542f5fSchristos if (sna_crtc_is_transformed(crtc)) { 184142542f5fSchristos DBG(("%s: no, CRTC is rotated\n", __FUNCTION__)); 184242542f5fSchristos return false; 184342542f5fSchristos } 184442542f5fSchristos 184542542f5fSchristos pixmap = get_window_pixmap(win); 184642542f5fSchristos if (pixmap != sna->front) { 184742542f5fSchristos DBG(("%s: no, not attached to front buffer\n", __FUNCTION__)); 184842542f5fSchristos return false; 184942542f5fSchristos } 185042542f5fSchristos 185142542f5fSchristos if (get_private(front)->pixmap != pixmap) { 185242542f5fSchristos DBG(("%s: no, DRI2 drawable is no longer attached, old pixmap=%ld, now pixmap=%ld\n", 185342542f5fSchristos __FUNCTION__, 185442542f5fSchristos get_private(front)->pixmap->drawable.serialNumber, 185542542f5fSchristos pixmap->drawable.serialNumber)); 185642542f5fSchristos return false; 185742542f5fSchristos } 185842542f5fSchristos 185942542f5fSchristos DBG(("%s: window size: %dx%d, clip=(%d, %d), (%d, %d) x %d\n", 186042542f5fSchristos __FUNCTION__, 186142542f5fSchristos win->drawable.width, win->drawable.height, 186242542f5fSchristos win->clipList.extents.x1, win->clipList.extents.y1, 186342542f5fSchristos win->clipList.extents.x2, win->clipList.extents.y2, 186442542f5fSchristos region_num_rects(&win->clipList))); 186542542f5fSchristos if (is_clipped(&win->clipList, &win->drawable)) { 186642542f5fSchristos DBG(("%s: no, %dx%d window is clipped: clip region=(%d, %d), (%d, %d)\n", 186742542f5fSchristos __FUNCTION__, 186842542f5fSchristos draw->width, draw->height, 186942542f5fSchristos win->clipList.extents.x1, 187042542f5fSchristos win->clipList.extents.y1, 187142542f5fSchristos win->clipList.extents.x2, 187242542f5fSchristos win->clipList.extents.y2)); 187342542f5fSchristos return false; 187442542f5fSchristos } 187542542f5fSchristos 187642542f5fSchristos if (overlaps_other_crtc(sna, crtc)) { 187742542f5fSchristos DBG(("%s: no, overlaps other CRTC\n", __FUNCTION__)); 187842542f5fSchristos return false; 187942542f5fSchristos } 188042542f5fSchristos 188142542f5fSchristos if (get_private(back)->size != (draw->height << 16 | draw->width)) { 188242542f5fSchristos DBG(("%s: no, DRI2 buffers does not fit window\n", 188342542f5fSchristos __FUNCTION__)); 188442542f5fSchristos return false; 188542542f5fSchristos } 188642542f5fSchristos 188742542f5fSchristos assert(win != win->drawable.pScreen->root); 188842542f5fSchristos DBG(("%s: yes, pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber)); 188942542f5fSchristos return true; 189042542f5fSchristos} 189142542f5fSchristos 189242542f5fSchristosstatic void 189342542f5fSchristossna_dri2_xchg(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back) 189442542f5fSchristos{ 189542542f5fSchristos WindowPtr win = (WindowPtr)draw; 189642542f5fSchristos struct kgem_bo *back_bo, *front_bo; 189742542f5fSchristos PixmapPtr pixmap; 189842542f5fSchristos int tmp; 189942542f5fSchristos 190042542f5fSchristos assert(draw->type != DRAWABLE_PIXMAP); 190142542f5fSchristos pixmap = get_window_pixmap(win); 190242542f5fSchristos 190342542f5fSchristos back_bo = get_private(back)->bo; 190442542f5fSchristos front_bo = get_private(front)->bo; 190542542f5fSchristos assert(front_bo != back_bo); 190642542f5fSchristos 190742542f5fSchristos DBG(("%s: win=%ld, exchange front=%d/%d and back=%d/%d, pixmap=%ld %dx%d\n", 190842542f5fSchristos __FUNCTION__, win->drawable.id, 190942542f5fSchristos front_bo->handle, front->name, 191042542f5fSchristos back_bo->handle, back->name, 191142542f5fSchristos pixmap->drawable.serialNumber, 191242542f5fSchristos pixmap->drawable.width, 191342542f5fSchristos pixmap->drawable.height)); 191442542f5fSchristos 191542542f5fSchristos DBG(("%s: back_bo pitch=%d, size=%d, ref=%d, active_scanout?=%d\n", 191642542f5fSchristos __FUNCTION__, back_bo->pitch, kgem_bo_size(back_bo), back_bo->refcnt, back_bo->active_scanout)); 191742542f5fSchristos DBG(("%s: front_bo pitch=%d, size=%d, ref=%d, active_scanout?=%d\n", 191842542f5fSchristos __FUNCTION__, front_bo->pitch, kgem_bo_size(front_bo), front_bo->refcnt, front_bo->active_scanout)); 191942542f5fSchristos assert(front_bo->refcnt); 192042542f5fSchristos assert(back_bo->refcnt); 192142542f5fSchristos 192242542f5fSchristos assert(sna_pixmap_get_buffer(pixmap) == front); 192342542f5fSchristos 192442542f5fSchristos assert(pixmap->drawable.height * back_bo->pitch <= kgem_bo_size(back_bo)); 192542542f5fSchristos assert(pixmap->drawable.height * front_bo->pitch <= kgem_bo_size(front_bo)); 192642542f5fSchristos 192742542f5fSchristos set_bo(pixmap, back_bo); 192842542f5fSchristos 192942542f5fSchristos get_private(front)->bo = back_bo; 193042542f5fSchristos get_private(back)->bo = front_bo; 193142542f5fSchristos get_private(back)->stale = true; 193242542f5fSchristos 193342542f5fSchristos tmp = front->name; 193442542f5fSchristos front->name = back->name; 193542542f5fSchristos back->name = tmp; 193642542f5fSchristos 193742542f5fSchristos assert(front_bo->refcnt); 193842542f5fSchristos assert(back_bo->refcnt); 193942542f5fSchristos 194042542f5fSchristos assert(get_private(front)->bo == sna_pixmap(pixmap)->gpu_bo); 194142542f5fSchristos} 194242542f5fSchristos 194342542f5fSchristosstatic void sna_dri2_xchg_crtc(struct sna *sna, DrawablePtr draw, xf86CrtcPtr crtc, DRI2BufferPtr front, DRI2BufferPtr back) 194442542f5fSchristos{ 194542542f5fSchristos WindowPtr win = (WindowPtr)draw; 194642542f5fSchristos DRI2Buffer2Ptr tmp; 194742542f5fSchristos struct kgem_bo *bo; 194842542f5fSchristos 194942542f5fSchristos DBG(("%s: exchange front=%d/%d and back=%d/%d, win id=%lu, pixmap=%ld %dx%d\n", 195042542f5fSchristos __FUNCTION__, 195142542f5fSchristos get_private(front)->bo->handle, front->name, 195242542f5fSchristos get_private(back)->bo->handle, back->name, 195342542f5fSchristos win->drawable.id, 195442542f5fSchristos get_window_pixmap(win)->drawable.serialNumber, 195542542f5fSchristos get_window_pixmap(win)->drawable.width, 195642542f5fSchristos get_window_pixmap(win)->drawable.height)); 195742542f5fSchristos 195842542f5fSchristos DamageRegionAppend(&win->drawable, &win->clipList); 195942542f5fSchristos sna_shadow_set_crtc(sna, crtc, get_private(back)->bo); 196042542f5fSchristos DamageRegionProcessPending(&win->drawable); 196142542f5fSchristos 196242542f5fSchristos assert(dri2_window(win)->front == NULL); 196342542f5fSchristos 196442542f5fSchristos tmp = calloc(1, sizeof(*tmp) + sizeof(struct sna_dri2_private)); 196542542f5fSchristos if (tmp == NULL) { 196642542f5fSchristos back->attachment = -1; 196742542f5fSchristos if (get_private(back)->proxy == NULL) { 196842542f5fSchristos get_private(back)->pixmap = get_window_pixmap(win); 196942542f5fSchristos get_private(back)->proxy = sna_dri2_reference_buffer(sna_pixmap_get_buffer(get_private(back)->pixmap)); 197042542f5fSchristos } 197142542f5fSchristos dri2_window(win)->front = sna_dri2_reference_buffer(back); 197242542f5fSchristos return; 197342542f5fSchristos } 197442542f5fSchristos 197542542f5fSchristos *tmp = *back; 197642542f5fSchristos tmp->attachment = DRI2BufferFrontLeft; 197742542f5fSchristos tmp->driverPrivate = tmp + 1; 197842542f5fSchristos get_private(tmp)->refcnt = 1; 197942542f5fSchristos get_private(tmp)->bo = get_private(back)->bo; 198042542f5fSchristos get_private(tmp)->size = get_private(back)->size; 198142542f5fSchristos get_private(tmp)->pixmap = get_window_pixmap(win); 198242542f5fSchristos get_private(tmp)->proxy = sna_dri2_reference_buffer(sna_pixmap_get_buffer(get_private(tmp)->pixmap)); 198342542f5fSchristos dri2_window(win)->front = tmp; 198442542f5fSchristos 198542542f5fSchristos DBG(("%s: allocating new backbuffer\n", __FUNCTION__)); 198642542f5fSchristos back->name = 0; 198742542f5fSchristos bo = kgem_create_2d(&sna->kgem, 198842542f5fSchristos draw->width, draw->height, draw->bitsPerPixel, 198942542f5fSchristos get_private(back)->bo->tiling, 199042542f5fSchristos CREATE_SCANOUT); 199142542f5fSchristos if (bo != NULL) { 199242542f5fSchristos get_private(back)->bo = bo; 199342542f5fSchristos back->pitch = bo->pitch; 199442542f5fSchristos back->name = kgem_bo_flink(&sna->kgem, bo); 199542542f5fSchristos } 199642542f5fSchristos if (back->name == 0) { 199742542f5fSchristos if (bo != NULL) 199842542f5fSchristos kgem_bo_destroy(&sna->kgem, bo); 199942542f5fSchristos get_private(back)->bo = NULL; 200042542f5fSchristos back->attachment = -1; 200142542f5fSchristos } 200242542f5fSchristos} 200342542f5fSchristos 200442542f5fSchristosstatic void frame_swap_complete(struct sna *sna, 200542542f5fSchristos struct sna_dri2_event *frame, 200642542f5fSchristos int type) 200742542f5fSchristos{ 200842542f5fSchristos const struct ust_msc *swap; 200942542f5fSchristos 201042542f5fSchristos if (frame->draw == NULL) 201142542f5fSchristos return; 201242542f5fSchristos 201342542f5fSchristos assert(frame->client); 201442542f5fSchristos 201542542f5fSchristos swap = sna_crtc_last_swap(frame->crtc); 201642542f5fSchristos DBG(("%s: draw=%ld, pipe=%d, frame=%lld [msc=%lld], tv=%d.%06d\n", 201742542f5fSchristos __FUNCTION__, (long)frame->draw, frame->pipe, 201842542f5fSchristos (long long)swap->msc, 201942542f5fSchristos (long long)draw_current_msc(frame->draw, frame->crtc, swap->msc), 202042542f5fSchristos swap->tv_sec, swap->tv_usec)); 202142542f5fSchristos 202242542f5fSchristos DRI2SwapComplete(frame->client, frame->draw, 202342542f5fSchristos draw_current_msc(frame->draw, frame->crtc, swap->msc), 202442542f5fSchristos swap->tv_sec, swap->tv_usec, 202542542f5fSchristos type, frame->event_complete, frame->event_data); 202642542f5fSchristos} 202742542f5fSchristos 202842542f5fSchristosstatic void fake_swap_complete(struct sna *sna, ClientPtr client, 202942542f5fSchristos DrawablePtr draw, xf86CrtcPtr crtc, 203042542f5fSchristos int type, DRI2SwapEventPtr func, void *data) 203142542f5fSchristos{ 203242542f5fSchristos const struct ust_msc *swap; 203342542f5fSchristos 203442542f5fSchristos swap = sna_crtc_last_swap(crtc); 203542542f5fSchristos DBG(("%s: draw=%ld, pipe=%d, frame=%lld [msc %lld], tv=%d.%06d\n", 203642542f5fSchristos __FUNCTION__, (long)draw->id, crtc ? sna_crtc_to_pipe(crtc) : -1, 203742542f5fSchristos (long long)swap->msc, 203842542f5fSchristos (long long)draw_current_msc(draw, crtc, swap->msc), 203942542f5fSchristos swap->tv_sec, swap->tv_usec)); 204042542f5fSchristos 204142542f5fSchristos DRI2SwapComplete(client, draw, 204242542f5fSchristos draw_current_msc(draw, crtc, swap->msc), 204342542f5fSchristos swap->tv_sec, swap->tv_usec, 204442542f5fSchristos type, func, data); 204542542f5fSchristos} 204642542f5fSchristos 204742542f5fSchristosstatic void chain_swap(struct sna *sna, struct sna_dri2_event *chain) 204842542f5fSchristos{ 204942542f5fSchristos union drm_wait_vblank vbl; 205042542f5fSchristos 205142542f5fSchristos if (chain->draw == NULL) { 205242542f5fSchristos sna_dri2_event_free(sna, chain); 205342542f5fSchristos return; 205442542f5fSchristos } 205542542f5fSchristos 205642542f5fSchristos if (chain->queued) /* too early! */ 205742542f5fSchristos return; 205842542f5fSchristos 205942542f5fSchristos assert(chain == dri2_chain(chain->draw)); 206042542f5fSchristos DBG(("%s: chaining draw=%ld, type=%d\n", 206142542f5fSchristos __FUNCTION__, (long)chain->draw->id, chain->type)); 206242542f5fSchristos chain->queued = true; 206342542f5fSchristos 206442542f5fSchristos switch (chain->type) { 206542542f5fSchristos case SWAP_THROTTLE: 206642542f5fSchristos DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__)); 206742542f5fSchristos if (sna->mode.shadow && !sna->mode.shadow_damage) { 206842542f5fSchristos /* recursed from wait_for_shadow(), simply requeue */ 206942542f5fSchristos DBG(("%s -- recursed from wait_for_shadow(), requeuing\n", __FUNCTION__)); 207042542f5fSchristos VG_CLEAR(vbl); 207142542f5fSchristos vbl.request.type = 207242542f5fSchristos DRM_VBLANK_RELATIVE | 207342542f5fSchristos DRM_VBLANK_EVENT; 207442542f5fSchristos vbl.request.sequence = 1; 207542542f5fSchristos vbl.request.signal = (uintptr_t)chain; 207642542f5fSchristos 207742542f5fSchristos if (!sna_wait_vblank(sna, &vbl, chain->pipe)) 207842542f5fSchristos return; 207942542f5fSchristos 208042542f5fSchristos DBG(("%s -- requeue failed, errno=%d\n", __FUNCTION__, errno)); 208142542f5fSchristos } 208242542f5fSchristos 208342542f5fSchristos if (can_xchg(sna, chain->draw, chain->front, chain->back)) { 208442542f5fSchristos sna_dri2_xchg(chain->draw, chain->front, chain->back); 208542542f5fSchristos } else if (can_xchg_crtc(sna, chain->draw, chain->front, chain->back, chain->crtc)) { 208642542f5fSchristos sna_dri2_xchg_crtc(sna, chain->draw, chain->crtc, chain->front, chain->back); 208742542f5fSchristos } else { 208842542f5fSchristos assert(chain->queued); 208942542f5fSchristos chain->bo = __sna_dri2_copy_region(sna, chain->draw, NULL, 209042542f5fSchristos chain->back, chain->front, 209142542f5fSchristos true); 209242542f5fSchristos } 209342542f5fSchristos case SWAP: 209442542f5fSchristos break; 209542542f5fSchristos default: 209642542f5fSchristos return; 209742542f5fSchristos } 209842542f5fSchristos 209942542f5fSchristos VG_CLEAR(vbl); 210042542f5fSchristos vbl.request.type = 210142542f5fSchristos DRM_VBLANK_RELATIVE | 210242542f5fSchristos DRM_VBLANK_EVENT; 210342542f5fSchristos vbl.request.sequence = 1; 210442542f5fSchristos vbl.request.signal = (uintptr_t)chain; 210542542f5fSchristos if (sna_wait_vblank(sna, &vbl, chain->pipe)) { 210642542f5fSchristos DBG(("%s: vblank wait failed, unblocking client\n", __FUNCTION__)); 210742542f5fSchristos frame_swap_complete(sna, chain, DRI2_BLIT_COMPLETE); 210842542f5fSchristos sna_dri2_event_free(sna, chain); 210942542f5fSchristos } else { 211042542f5fSchristos if (chain->type == SWAP_THROTTLE && !swap_limit(chain->draw, 2)) { 211142542f5fSchristos DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); 211242542f5fSchristos frame_swap_complete(sna, chain, DRI2_BLIT_COMPLETE); 211342542f5fSchristos } 211442542f5fSchristos } 211542542f5fSchristos} 211642542f5fSchristos 211742542f5fSchristosstatic inline bool rq_is_busy(struct kgem *kgem, struct kgem_bo *bo) 211842542f5fSchristos{ 211942542f5fSchristos if (bo == NULL) 212042542f5fSchristos return false; 212142542f5fSchristos 212242542f5fSchristos DBG(("%s: handle=%d, domain: %d exec? %d, rq? %d\n", __FUNCTION__, 212342542f5fSchristos bo->handle, bo->domain, bo->exec != NULL, bo->rq != NULL)); 212442542f5fSchristos assert(bo->refcnt); 212542542f5fSchristos 212642542f5fSchristos if (bo->exec) 212742542f5fSchristos return true; 212842542f5fSchristos 212942542f5fSchristos if (bo->rq == NULL) 213042542f5fSchristos return false; 213142542f5fSchristos 213242542f5fSchristos return __kgem_busy(kgem, bo->handle); 213342542f5fSchristos} 213442542f5fSchristos 213542542f5fSchristosstatic bool sna_dri2_blit_complete(struct sna *sna, 213642542f5fSchristos struct sna_dri2_event *info) 213742542f5fSchristos{ 213842542f5fSchristos if (rq_is_busy(&sna->kgem, info->bo)) { 213942542f5fSchristos union drm_wait_vblank vbl; 214042542f5fSchristos 214142542f5fSchristos DBG(("%s: vsync'ed blit is still busy, postponing\n", 214242542f5fSchristos __FUNCTION__)); 214342542f5fSchristos 214442542f5fSchristos VG_CLEAR(vbl); 214542542f5fSchristos vbl.request.type = 214642542f5fSchristos DRM_VBLANK_RELATIVE | 214742542f5fSchristos DRM_VBLANK_EVENT; 214842542f5fSchristos vbl.request.sequence = 1; 214942542f5fSchristos vbl.request.signal = (uintptr_t)info; 215042542f5fSchristos assert(info->queued); 215142542f5fSchristos if (!sna_wait_vblank(sna, &vbl, info->pipe)) 215242542f5fSchristos return false; 215342542f5fSchristos } 215442542f5fSchristos 215542542f5fSchristos DBG(("%s: blit finished\n", __FUNCTION__)); 215642542f5fSchristos return true; 215742542f5fSchristos} 215842542f5fSchristos 215942542f5fSchristosvoid sna_dri2_vblank_handler(struct sna *sna, struct drm_event_vblank *event) 216042542f5fSchristos{ 216142542f5fSchristos struct sna_dri2_event *info = (void *)(uintptr_t)event->user_data; 216242542f5fSchristos DrawablePtr draw; 216342542f5fSchristos union drm_wait_vblank vbl; 216442542f5fSchristos uint64_t msc; 216542542f5fSchristos 216642542f5fSchristos DBG(("%s(type=%d, sequence=%d)\n", __FUNCTION__, info->type, event->sequence)); 216742542f5fSchristos assert(info->queued); 216842542f5fSchristos msc = sna_crtc_record_event(info->crtc, event); 216942542f5fSchristos 217042542f5fSchristos draw = info->draw; 217142542f5fSchristos if (draw == NULL) { 217242542f5fSchristos DBG(("%s -- drawable gone\n", __FUNCTION__)); 217342542f5fSchristos goto done; 217442542f5fSchristos } 217542542f5fSchristos 217642542f5fSchristos switch (info->type) { 217742542f5fSchristos case FLIP: 217842542f5fSchristos /* If we can still flip... */ 217942542f5fSchristos if (can_flip(sna, draw, info->front, info->back, info->crtc) && 218042542f5fSchristos sna_dri2_flip(sna, info)) 218142542f5fSchristos return; 218242542f5fSchristos 218342542f5fSchristos /* else fall through to blit */ 218442542f5fSchristos case SWAP: 218542542f5fSchristos assert(info->queued); 218642542f5fSchristos if (sna->mode.shadow && !sna->mode.shadow_damage) { 218742542f5fSchristos /* recursed from wait_for_shadow(), simply requeue */ 218842542f5fSchristos DBG(("%s -- recursed from wait_for_shadow(), requeuing\n", __FUNCTION__)); 218942542f5fSchristos 219042542f5fSchristos } else if (can_xchg(sna, draw, info->front, info->back)) { 219142542f5fSchristos sna_dri2_xchg(draw, info->front, info->back); 219242542f5fSchristos info->type = SWAP_WAIT; 219342542f5fSchristos } else if (can_xchg_crtc(sna, draw, info->front, info->back, info->crtc)) { 219442542f5fSchristos sna_dri2_xchg_crtc(sna, draw, info->crtc, info->front, info->back); 219542542f5fSchristos info->type = SWAP_WAIT; 219642542f5fSchristos } else { 219742542f5fSchristos assert(info->queued); 219842542f5fSchristos info->bo = __sna_dri2_copy_region(sna, draw, NULL, 219942542f5fSchristos info->back, info->front, true); 220042542f5fSchristos info->type = SWAP_WAIT; 220142542f5fSchristos } 220242542f5fSchristos 220342542f5fSchristos VG_CLEAR(vbl); 220442542f5fSchristos vbl.request.type = 220542542f5fSchristos DRM_VBLANK_RELATIVE | 220642542f5fSchristos DRM_VBLANK_EVENT; 220742542f5fSchristos vbl.request.sequence = 1; 220842542f5fSchristos vbl.request.signal = (uintptr_t)info; 220942542f5fSchristos 221042542f5fSchristos assert(info->queued); 221142542f5fSchristos if (!sna_wait_vblank(sna, &vbl, info->pipe)) 221242542f5fSchristos return; 221342542f5fSchristos 221442542f5fSchristos DBG(("%s -- requeue failed, errno=%d\n", __FUNCTION__, errno)); 221542542f5fSchristos /* fall through to SwapComplete */ 221642542f5fSchristos case SWAP_WAIT: 221742542f5fSchristos if (!sna_dri2_blit_complete(sna, info)) 221842542f5fSchristos return; 221942542f5fSchristos 222042542f5fSchristos DBG(("%s: swap complete, unblocking client (frame=%d, tv=%d.%06d)\n", __FUNCTION__, 222142542f5fSchristos event->sequence, event->tv_sec, event->tv_usec)); 222242542f5fSchristos frame_swap_complete(sna, info, DRI2_BLIT_COMPLETE); 222342542f5fSchristos break; 222442542f5fSchristos 222542542f5fSchristos case SWAP_THROTTLE: 222642542f5fSchristos DBG(("%s: %d complete, frame=%d tv=%d.%06d\n", 222742542f5fSchristos __FUNCTION__, info->type, 222842542f5fSchristos event->sequence, event->tv_sec, event->tv_usec)); 222942542f5fSchristos 223042542f5fSchristos if (xorg_can_triple_buffer(sna)) { 223142542f5fSchristos if (!sna_dri2_blit_complete(sna, info)) 223242542f5fSchristos return; 223342542f5fSchristos 223442542f5fSchristos DBG(("%s: triple buffer swap complete, unblocking client (frame=%d, tv=%d.%06d)\n", __FUNCTION__, 223542542f5fSchristos event->sequence, event->tv_sec, event->tv_usec)); 223642542f5fSchristos frame_swap_complete(sna, info, DRI2_BLIT_COMPLETE); 223742542f5fSchristos } 223842542f5fSchristos break; 223942542f5fSchristos 224042542f5fSchristos case WAITMSC: 224142542f5fSchristos assert(info->client); 224242542f5fSchristos DRI2WaitMSCComplete(info->client, draw, msc, 224342542f5fSchristos event->tv_sec, event->tv_usec); 224442542f5fSchristos break; 224542542f5fSchristos default: 224642542f5fSchristos xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING, 224742542f5fSchristos "%s: unknown vblank event received\n", __func__); 224842542f5fSchristos /* Unknown type */ 224942542f5fSchristos break; 225042542f5fSchristos } 225142542f5fSchristos 225242542f5fSchristos if (info->chain) { 225342542f5fSchristos assert(info->chain != info); 225442542f5fSchristos assert(info->draw == draw); 225542542f5fSchristos sna_dri2_remove_event((WindowPtr)draw, info); 225642542f5fSchristos chain_swap(sna, info->chain); 225742542f5fSchristos info->draw = NULL; 225842542f5fSchristos } 225942542f5fSchristos 226042542f5fSchristosdone: 226142542f5fSchristos sna_dri2_event_free(sna, info); 226242542f5fSchristos DBG(("%s complete\n", __FUNCTION__)); 226342542f5fSchristos} 226442542f5fSchristos 226542542f5fSchristosstatic bool 226642542f5fSchristossna_dri2_immediate_blit(struct sna *sna, 226742542f5fSchristos struct sna_dri2_event *info, 226842542f5fSchristos bool sync, bool event) 226942542f5fSchristos{ 227042542f5fSchristos DrawablePtr draw = info->draw; 227142542f5fSchristos bool ret = false; 227242542f5fSchristos 227342542f5fSchristos if (sna->flags & SNA_NO_WAIT) 227442542f5fSchristos sync = false; 227542542f5fSchristos 227642542f5fSchristos DBG(("%s: emitting immediate blit, throttling client, synced? %d, chained? %d, send-event? %d\n", 227742542f5fSchristos __FUNCTION__, sync, dri2_chain(draw) != info, 227842542f5fSchristos event)); 227942542f5fSchristos 228042542f5fSchristos info->type = SWAP_THROTTLE; 228142542f5fSchristos if (!sync || dri2_chain(draw) == info) { 228242542f5fSchristos DBG(("%s: no pending blit, starting chain\n", 228342542f5fSchristos __FUNCTION__)); 228442542f5fSchristos 228542542f5fSchristos info->queued = true; 228642542f5fSchristos info->bo = __sna_dri2_copy_region(sna, draw, NULL, 228742542f5fSchristos info->back, 228842542f5fSchristos info->front, 228942542f5fSchristos sync); 229042542f5fSchristos if (event) { 229142542f5fSchristos if (sync) { 229242542f5fSchristos union drm_wait_vblank vbl; 229342542f5fSchristos 229442542f5fSchristos VG_CLEAR(vbl); 229542542f5fSchristos vbl.request.type = 229642542f5fSchristos DRM_VBLANK_RELATIVE | 229742542f5fSchristos DRM_VBLANK_EVENT; 229842542f5fSchristos vbl.request.sequence = 1; 229942542f5fSchristos vbl.request.signal = (uintptr_t)info; 230042542f5fSchristos ret = !sna_wait_vblank(sna, &vbl, info->pipe); 230142542f5fSchristos if (ret) 230242542f5fSchristos event = !swap_limit(draw, 2); 230342542f5fSchristos } 230442542f5fSchristos if (event) { 230542542f5fSchristos DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); 230642542f5fSchristos frame_swap_complete(sna, info, DRI2_BLIT_COMPLETE); 230742542f5fSchristos } 230842542f5fSchristos } 230942542f5fSchristos } else { 231042542f5fSchristos DBG(("%s: pending blit, chained\n", __FUNCTION__)); 231142542f5fSchristos ret = true; 231242542f5fSchristos } 231342542f5fSchristos 231442542f5fSchristos DBG(("%s: continue? %d\n", __FUNCTION__, ret)); 231542542f5fSchristos return ret; 231642542f5fSchristos} 231742542f5fSchristos 231842542f5fSchristosstatic bool 231942542f5fSchristossna_dri2_flip_continue(struct sna *sna, struct sna_dri2_event *info) 232042542f5fSchristos{ 232142542f5fSchristos DBG(("%s(mode=%d)\n", __FUNCTION__, info->mode)); 232242542f5fSchristos 232342542f5fSchristos if (info->mode > 0){ 232442542f5fSchristos struct kgem_bo *bo = get_private(info->front)->bo; 232542542f5fSchristos 232642542f5fSchristos info->type = info->mode; 232742542f5fSchristos 232842542f5fSchristos if (bo != sna_pixmap(sna->front)->gpu_bo) 232942542f5fSchristos return false; 233042542f5fSchristos 233142542f5fSchristos if (!sna_page_flip(sna, bo, sna_dri2_flip_handler, info)) 233242542f5fSchristos return false; 233342542f5fSchristos 233442542f5fSchristos assert(sna->dri2.flip_pending == NULL || sna->dri2.flip_pending == info); 233542542f5fSchristos sna->dri2.flip_pending = info; 233642542f5fSchristos assert(info->queued); 233742542f5fSchristos } else { 233842542f5fSchristos info->type = -info->mode; 233942542f5fSchristos 234042542f5fSchristos if (!info->draw) 234142542f5fSchristos return false; 234242542f5fSchristos 234342542f5fSchristos if (!can_flip(sna, info->draw, info->front, info->back, info->crtc)) 234442542f5fSchristos return false; 234542542f5fSchristos 234642542f5fSchristos assert(sna_pixmap_get_buffer(get_drawable_pixmap(info->draw)) == info->front); 234742542f5fSchristos if (!sna_dri2_flip(sna, info)) 234842542f5fSchristos return false; 234942542f5fSchristos 235042542f5fSchristos if (!xorg_can_triple_buffer(sna)) { 235142542f5fSchristos sna_dri2_get_back(sna, info->draw, info->back, info); 235242542f5fSchristos DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); 235342542f5fSchristos frame_swap_complete(sna, info, DRI2_FLIP_COMPLETE); 235442542f5fSchristos } 235542542f5fSchristos } 235642542f5fSchristos 235742542f5fSchristos info->mode = 0; 235842542f5fSchristos return true; 235942542f5fSchristos} 236042542f5fSchristos 236142542f5fSchristosstatic void chain_flip(struct sna *sna) 236242542f5fSchristos{ 236342542f5fSchristos struct sna_dri2_event *chain = sna->dri2.flip_pending; 236442542f5fSchristos 236542542f5fSchristos assert(chain->type == FLIP); 236642542f5fSchristos DBG(("%s: chaining type=%d, cancelled?=%d\n", 236742542f5fSchristos __FUNCTION__, chain->type, chain->draw == NULL)); 236842542f5fSchristos 236942542f5fSchristos sna->dri2.flip_pending = NULL; 237042542f5fSchristos if (chain->draw == NULL) { 237142542f5fSchristos sna_dri2_event_free(sna, chain); 237242542f5fSchristos return; 237342542f5fSchristos } 237442542f5fSchristos 237542542f5fSchristos assert(chain == dri2_chain(chain->draw)); 237642542f5fSchristos assert(!chain->queued); 237742542f5fSchristos chain->queued = true; 237842542f5fSchristos 237942542f5fSchristos if (can_flip(sna, chain->draw, chain->front, chain->back, chain->crtc) && 238042542f5fSchristos sna_dri2_flip(sna, chain)) { 238142542f5fSchristos DBG(("%s: performing chained flip\n", __FUNCTION__)); 238242542f5fSchristos } else { 238342542f5fSchristos DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__)); 238442542f5fSchristos chain->bo = __sna_dri2_copy_region(sna, chain->draw, NULL, 238542542f5fSchristos chain->back, chain->front, 238642542f5fSchristos true); 238742542f5fSchristos 238842542f5fSchristos if (xorg_can_triple_buffer(sna)) { 238942542f5fSchristos union drm_wait_vblank vbl; 239042542f5fSchristos 239142542f5fSchristos VG_CLEAR(vbl); 239242542f5fSchristos 239342542f5fSchristos chain->type = SWAP_WAIT; 239442542f5fSchristos vbl.request.type = 239542542f5fSchristos DRM_VBLANK_RELATIVE | 239642542f5fSchristos DRM_VBLANK_EVENT; 239742542f5fSchristos vbl.request.sequence = 1; 239842542f5fSchristos vbl.request.signal = (uintptr_t)chain; 239942542f5fSchristos 240042542f5fSchristos assert(chain->queued); 240142542f5fSchristos if (!sna_wait_vblank(sna, &vbl, chain->pipe)) 240242542f5fSchristos return; 240342542f5fSchristos } 240442542f5fSchristos 240542542f5fSchristos DBG(("%s: fake triple buffering (or vblank wait failed), unblocking client\n", __FUNCTION__)); 240642542f5fSchristos frame_swap_complete(sna, chain, DRI2_BLIT_COMPLETE); 240742542f5fSchristos sna_dri2_event_free(sna, chain); 240842542f5fSchristos } 240942542f5fSchristos} 241042542f5fSchristos 241142542f5fSchristosstatic void sna_dri2_flip_event(struct sna *sna, 241242542f5fSchristos struct sna_dri2_event *flip) 241342542f5fSchristos{ 241442542f5fSchristos DBG(("%s(pipe=%d, event=%d)\n", __FUNCTION__, flip->pipe, flip->type)); 241542542f5fSchristos assert(flip->queued); 241642542f5fSchristos 241742542f5fSchristos if (sna->dri2.flip_pending == flip) 241842542f5fSchristos sna->dri2.flip_pending = NULL; 241942542f5fSchristos 242042542f5fSchristos /* We assume our flips arrive in order, so we don't check the frame */ 242142542f5fSchristos switch (flip->type) { 242242542f5fSchristos case FLIP: 242342542f5fSchristos DBG(("%s: swap complete, unblocking client\n", __FUNCTION__)); 242442542f5fSchristos frame_swap_complete(sna, flip, DRI2_FLIP_COMPLETE); 242542542f5fSchristos sna_dri2_event_free(sna, flip); 242642542f5fSchristos 242742542f5fSchristos if (sna->dri2.flip_pending) 242842542f5fSchristos chain_flip(sna); 242942542f5fSchristos break; 243042542f5fSchristos 243142542f5fSchristos case FLIP_THROTTLE: 243242542f5fSchristos DBG(("%s: triple buffer swap complete, unblocking client\n", __FUNCTION__)); 243342542f5fSchristos frame_swap_complete(sna, flip, DRI2_FLIP_COMPLETE); 243442542f5fSchristos case FLIP_COMPLETE: 243542542f5fSchristos if (sna->dri2.flip_pending) { 243642542f5fSchristos sna_dri2_event_free(sna, flip); 243742542f5fSchristos chain_flip(sna); 243842542f5fSchristos } else if (!flip->mode) { 243942542f5fSchristos DBG(("%s: flip chain complete\n", __FUNCTION__)); 244042542f5fSchristos 244142542f5fSchristos if (flip->chain) { 244242542f5fSchristos sna_dri2_remove_event((WindowPtr)flip->draw, 244342542f5fSchristos flip); 244442542f5fSchristos chain_swap(sna, flip->chain); 244542542f5fSchristos flip->draw = NULL; 244642542f5fSchristos } 244742542f5fSchristos 244842542f5fSchristos sna_dri2_event_free(sna, flip); 244942542f5fSchristos } else if (!sna_dri2_flip_continue(sna, flip)) { 245042542f5fSchristos DBG(("%s: no longer able to flip\n", __FUNCTION__)); 245142542f5fSchristos if (flip->draw == NULL || !sna_dri2_immediate_blit(sna, flip, false, flip->mode < 0)) 245242542f5fSchristos sna_dri2_event_free(sna, flip); 245342542f5fSchristos } 245442542f5fSchristos break; 245542542f5fSchristos 245642542f5fSchristos default: /* Unknown type */ 245742542f5fSchristos xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING, 245842542f5fSchristos "%s: unknown vblank event received\n", __func__); 245942542f5fSchristos sna_dri2_event_free(sna, flip); 246042542f5fSchristos if (sna->dri2.flip_pending) 246142542f5fSchristos chain_flip(sna); 246242542f5fSchristos break; 246342542f5fSchristos } 246442542f5fSchristos} 246542542f5fSchristos 246642542f5fSchristosstatic uint64_t 246742542f5fSchristosget_current_msc(struct sna *sna, DrawablePtr draw, xf86CrtcPtr crtc) 246842542f5fSchristos{ 246942542f5fSchristos union drm_wait_vblank vbl; 247042542f5fSchristos uint64_t ret = -1; 247142542f5fSchristos 247242542f5fSchristos VG_CLEAR(vbl); 247342542f5fSchristos vbl.request.type = _DRM_VBLANK_RELATIVE; 247442542f5fSchristos vbl.request.sequence = 0; 247542542f5fSchristos if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(crtc)) == 0) 247642542f5fSchristos ret = sna_crtc_record_vblank(crtc, &vbl); 247742542f5fSchristos 247842542f5fSchristos return draw_current_msc(draw, crtc, ret); 247942542f5fSchristos} 248042542f5fSchristos 248142542f5fSchristos#if defined(CHECK_FOR_COMPOSITOR) 248242542f5fSchristosstatic Bool find(pointer value, XID id, pointer cdata) 248342542f5fSchristos{ 248442542f5fSchristos return TRUE; 248542542f5fSchristos} 248642542f5fSchristos#endif 248742542f5fSchristos 248842542f5fSchristosstatic int use_triple_buffer(struct sna *sna, ClientPtr client, bool async) 248942542f5fSchristos{ 249042542f5fSchristos if ((sna->flags & SNA_TRIPLE_BUFFER) == 0) { 249142542f5fSchristos DBG(("%s: triple buffer disabled, using FLIP\n", __FUNCTION__)); 249242542f5fSchristos return FLIP; 249342542f5fSchristos } 249442542f5fSchristos 249542542f5fSchristos if (async) { 249642542f5fSchristos DBG(("%s: running async, using %s\n", __FUNCTION__, 249742542f5fSchristos sna->flags & SNA_HAS_ASYNC_FLIP ? "FLIP_ASYNC" : "FLIP_COMPLETE")); 249842542f5fSchristos return sna->flags & SNA_HAS_ASYNC_FLIP ? FLIP_ASYNC : FLIP_COMPLETE; 249942542f5fSchristos } 250042542f5fSchristos 250142542f5fSchristos if (xorg_can_triple_buffer(sna)) { 250242542f5fSchristos DBG(("%s: triple buffer enabled, using FLIP_THROTTLE\n", __FUNCTION__)); 250342542f5fSchristos return FLIP_THROTTLE; 250442542f5fSchristos } 250542542f5fSchristos 250642542f5fSchristos#if defined(CHECK_FOR_COMPOSITOR) 250742542f5fSchristos /* Hack: Disable triple buffering for compositors */ 250842542f5fSchristos { 250942542f5fSchristos struct sna_client *priv = sna_client(client); 251042542f5fSchristos if (priv->is_compositor == 0) 251142542f5fSchristos priv->is_compositor = 251242542f5fSchristos LookupClientResourceComplex(client, 251342542f5fSchristos CompositeClientWindowType+1, 251442542f5fSchristos find, NULL) ? FLIP : FLIP_COMPLETE; 251542542f5fSchristos 251642542f5fSchristos DBG(("%s: fake triple buffer enabled?=%d using %s\n", __FUNCTION__, 251742542f5fSchristos priv->is_compositor != FLIP, priv->is_compositor == FLIP ? "FLIP" : "FLIP_COMPLETE")); 251842542f5fSchristos return priv->is_compositor; 251942542f5fSchristos } 252042542f5fSchristos#else 252142542f5fSchristos DBG(("%s: fake triple buffer enabled, using FLIP_COMPLETE\n", __FUNCTION__)); 252242542f5fSchristos return FLIP_COMPLETE; 252342542f5fSchristos#endif 252442542f5fSchristos} 252542542f5fSchristos 252642542f5fSchristosstatic bool immediate_swap(struct sna *sna, 252742542f5fSchristos uint64_t target_msc, 252842542f5fSchristos uint64_t divisor, 252942542f5fSchristos DrawablePtr draw, 253042542f5fSchristos xf86CrtcPtr crtc, 253142542f5fSchristos uint64_t *current_msc) 253242542f5fSchristos{ 253342542f5fSchristos if (divisor == 0) { 253442542f5fSchristos *current_msc = -1; 253542542f5fSchristos 253642542f5fSchristos if (sna->flags & SNA_NO_WAIT) { 253742542f5fSchristos DBG(("%s: yes, waits are disabled\n", __FUNCTION__)); 253842542f5fSchristos return true; 253942542f5fSchristos } 254042542f5fSchristos 254142542f5fSchristos if (target_msc) 254242542f5fSchristos *current_msc = get_current_msc(sna, draw, crtc); 254342542f5fSchristos 254442542f5fSchristos DBG(("%s: current_msc=%ld, target_msc=%ld -- %s\n", 254542542f5fSchristos __FUNCTION__, (long)*current_msc, (long)target_msc, 254642542f5fSchristos (*current_msc >= target_msc - 1) ? "yes" : "no")); 254742542f5fSchristos return *current_msc >= target_msc - 1; 254842542f5fSchristos } 254942542f5fSchristos 255042542f5fSchristos DBG(("%s: explicit waits requests, divisor=%ld\n", 255142542f5fSchristos __FUNCTION__, (long)divisor)); 255242542f5fSchristos *current_msc = get_current_msc(sna, draw, crtc); 255342542f5fSchristos return false; 255442542f5fSchristos} 255542542f5fSchristos 255642542f5fSchristosstatic bool 255742542f5fSchristossna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, 255842542f5fSchristos DRI2BufferPtr front, DRI2BufferPtr back, 255942542f5fSchristos CARD64 *target_msc, CARD64 divisor, CARD64 remainder, 256042542f5fSchristos DRI2SwapEventPtr func, void *data) 256142542f5fSchristos{ 256242542f5fSchristos struct sna *sna = to_sna_from_drawable(draw); 256342542f5fSchristos struct sna_dri2_event *info; 256442542f5fSchristos uint64_t current_msc; 256542542f5fSchristos 256642542f5fSchristos if (immediate_swap(sna, *target_msc, divisor, draw, crtc, ¤t_msc)) { 256742542f5fSchristos int type; 256842542f5fSchristos 256942542f5fSchristos info = sna->dri2.flip_pending; 257042542f5fSchristos DBG(("%s: performing immediate swap on pipe %d, pending? %d, mode: %d, continuation? %d\n", 257142542f5fSchristos __FUNCTION__, sna_crtc_to_pipe(crtc), 257242542f5fSchristos info != NULL, info ? info->mode : 0, 257342542f5fSchristos info && info->draw == draw)); 257442542f5fSchristos 257542542f5fSchristos if (info && info->draw == draw) { 257642542f5fSchristos assert(info->type != FLIP); 257742542f5fSchristos assert(info->front == front); 257842542f5fSchristos if (info->back != back) { 257942542f5fSchristos _sna_dri2_destroy_buffer(sna, info->back); 258042542f5fSchristos info->back = sna_dri2_reference_buffer(back); 258142542f5fSchristos } 258242542f5fSchristos if (info->mode || current_msc >= *target_msc) { 258342542f5fSchristos DBG(("%s: executing xchg of pending flip\n", 258442542f5fSchristos __FUNCTION__)); 258542542f5fSchristos sna_dri2_xchg(draw, front, back); 258642542f5fSchristos info->mode = type = FLIP_COMPLETE; 258742542f5fSchristos goto new_back; 258842542f5fSchristos } else { 258942542f5fSchristos DBG(("%s: chaining flip\n", __FUNCTION__)); 259042542f5fSchristos type = FLIP_THROTTLE; 259142542f5fSchristos if (xorg_can_triple_buffer(sna)) 259242542f5fSchristos info->mode = -type; 259342542f5fSchristos else 259442542f5fSchristos info->mode = -FLIP_COMPLETE; 259542542f5fSchristos goto out; 259642542f5fSchristos } 259742542f5fSchristos } 259842542f5fSchristos 259942542f5fSchristos info = sna_dri2_add_event(sna, draw, client); 260042542f5fSchristos if (info == NULL) 260142542f5fSchristos return false; 260242542f5fSchristos 260342542f5fSchristos assert(info->crtc == crtc); 260442542f5fSchristos info->event_complete = func; 260542542f5fSchristos info->event_data = data; 260642542f5fSchristos 260742542f5fSchristos info->front = sna_dri2_reference_buffer(front); 260842542f5fSchristos info->back = sna_dri2_reference_buffer(back); 260942542f5fSchristos 261042542f5fSchristos if (sna->dri2.flip_pending) { 261142542f5fSchristos /* We need to first wait (one vblank) for the 261242542f5fSchristos * async flips to complete before this client 261342542f5fSchristos * can take over. 261442542f5fSchristos */ 261542542f5fSchristos DBG(("%s: queueing flip after pending completion\n", 261642542f5fSchristos __FUNCTION__)); 261742542f5fSchristos info->type = type = FLIP; 261842542f5fSchristos sna->dri2.flip_pending = info; 261942542f5fSchristos assert(info->queued); 262042542f5fSchristos current_msc++; 262142542f5fSchristos } else { 262242542f5fSchristos info->type = type = use_triple_buffer(sna, client, *target_msc == 0); 262342542f5fSchristos if (!sna_dri2_flip(sna, info)) { 262442542f5fSchristos DBG(("%s: flip failed, falling back\n", __FUNCTION__)); 262542542f5fSchristos sna_dri2_event_free(sna, info); 262642542f5fSchristos return false; 262742542f5fSchristos } 262842542f5fSchristos } 262942542f5fSchristos 263042542f5fSchristos swap_limit(draw, 1 + (type == FLIP_THROTTLE)); 263142542f5fSchristos if (type >= FLIP_COMPLETE) { 263242542f5fSchristosnew_back: 263342542f5fSchristos if (!xorg_can_triple_buffer(sna)) 263442542f5fSchristos sna_dri2_get_back(sna, draw, back, info); 263542542f5fSchristos DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); 263642542f5fSchristos frame_swap_complete(sna, info, DRI2_EXCHANGE_COMPLETE); 263742542f5fSchristos if (info->type == FLIP_ASYNC) 263842542f5fSchristos sna_dri2_event_free(sna, info); 263942542f5fSchristos } 264042542f5fSchristosout: 264142542f5fSchristos DBG(("%s: target_msc=%llu\n", __FUNCTION__, current_msc + 1)); 264242542f5fSchristos *target_msc = current_msc + 1; 264342542f5fSchristos return true; 264442542f5fSchristos } 264542542f5fSchristos 264642542f5fSchristos info = sna_dri2_add_event(sna, draw, client); 264742542f5fSchristos if (info == NULL) 264842542f5fSchristos return false; 264942542f5fSchristos 265042542f5fSchristos assert(info->crtc == crtc); 265142542f5fSchristos info->event_complete = func; 265242542f5fSchristos info->event_data = data; 265342542f5fSchristos info->type = FLIP; 265442542f5fSchristos 265542542f5fSchristos info->front = sna_dri2_reference_buffer(front); 265642542f5fSchristos info->back = sna_dri2_reference_buffer(back); 265742542f5fSchristos 265842542f5fSchristos /* 265942542f5fSchristos * If divisor is zero, or current_msc is smaller than target_msc 266042542f5fSchristos * we just need to make sure target_msc passes before initiating 266142542f5fSchristos * the swap. 266242542f5fSchristos */ 266342542f5fSchristos if (divisor && current_msc >= *target_msc) { 266442542f5fSchristos DBG(("%s: missed target, queueing event for next: current=%lld, target=%lld, divisor=%lld, remainder=%lld\n", 266542542f5fSchristos __FUNCTION__, 266642542f5fSchristos (long long)current_msc, 266742542f5fSchristos (long long)*target_msc, 266842542f5fSchristos (long long)divisor, 266942542f5fSchristos (long long)remainder)); 267042542f5fSchristos 267142542f5fSchristos *target_msc = current_msc + remainder - current_msc % divisor; 267242542f5fSchristos if (*target_msc <= current_msc) 267342542f5fSchristos *target_msc += divisor; 267442542f5fSchristos } 267542542f5fSchristos 267642542f5fSchristos if (*target_msc <= current_msc + 1) { 267742542f5fSchristos if (!sna_dri2_flip(sna, info)) { 267842542f5fSchristos sna_dri2_event_free(sna, info); 267942542f5fSchristos return false; 268042542f5fSchristos } 268142542f5fSchristos *target_msc = current_msc + 1; 268242542f5fSchristos } else { 268342542f5fSchristos union drm_wait_vblank vbl; 268442542f5fSchristos 268542542f5fSchristos VG_CLEAR(vbl); 268642542f5fSchristos 268742542f5fSchristos vbl.request.type = 268842542f5fSchristos DRM_VBLANK_ABSOLUTE | 268942542f5fSchristos DRM_VBLANK_EVENT; 269042542f5fSchristos 269142542f5fSchristos /* Account for 1 frame extra pageflip delay */ 269242542f5fSchristos vbl.reply.sequence = draw_target_seq(draw, *target_msc - 1); 269342542f5fSchristos vbl.request.signal = (uintptr_t)info; 269442542f5fSchristos 269542542f5fSchristos info->queued = true; 269642542f5fSchristos if (sna_wait_vblank(sna, &vbl, info->pipe)) { 269742542f5fSchristos sna_dri2_event_free(sna, info); 269842542f5fSchristos return false; 269942542f5fSchristos } 270042542f5fSchristos } 270142542f5fSchristos 270242542f5fSchristos DBG(("%s: reported target_msc=%llu\n", __FUNCTION__, *target_msc)); 270342542f5fSchristos swap_limit(draw, 1); 270442542f5fSchristos return true; 270542542f5fSchristos} 270642542f5fSchristos 270742542f5fSchristosstatic bool 270842542f5fSchristossna_dri2_schedule_xchg(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, 270942542f5fSchristos DRI2BufferPtr front, DRI2BufferPtr back, 271042542f5fSchristos CARD64 *target_msc, CARD64 divisor, CARD64 remainder, 271142542f5fSchristos DRI2SwapEventPtr func, void *data) 271242542f5fSchristos{ 271342542f5fSchristos struct sna *sna = to_sna_from_drawable(draw); 271442542f5fSchristos uint64_t current_msc; 271542542f5fSchristos bool sync, event; 271642542f5fSchristos 271742542f5fSchristos if (!immediate_swap(sna, *target_msc, divisor, draw, crtc, ¤t_msc)) 271842542f5fSchristos return false; 271942542f5fSchristos 272042542f5fSchristos sync = current_msc < *target_msc; 272142542f5fSchristos event = dri2_chain(draw) == NULL; 272242542f5fSchristos if (!sync || event) { 272342542f5fSchristos DBG(("%s: performing immediate xchg on pipe %d\n", 272442542f5fSchristos __FUNCTION__, sna_crtc_to_pipe(crtc))); 272542542f5fSchristos sna_dri2_xchg(draw, front, back); 272642542f5fSchristos } 272742542f5fSchristos if (sync) { 272842542f5fSchristos struct sna_dri2_event *info; 272942542f5fSchristos 273042542f5fSchristos info = sna_dri2_add_event(sna, draw, client); 273142542f5fSchristos if (!info) 273242542f5fSchristos goto complete; 273342542f5fSchristos 273442542f5fSchristos info->event_complete = func; 273542542f5fSchristos info->event_data = data; 273642542f5fSchristos 273742542f5fSchristos info->front = sna_dri2_reference_buffer(front); 273842542f5fSchristos info->back = sna_dri2_reference_buffer(back); 273942542f5fSchristos info->type = SWAP_THROTTLE; 274042542f5fSchristos 274142542f5fSchristos if (event) { 274242542f5fSchristos union drm_wait_vblank vbl; 274342542f5fSchristos 274442542f5fSchristos VG_CLEAR(vbl); 274542542f5fSchristos vbl.request.type = 274642542f5fSchristos DRM_VBLANK_RELATIVE | 274742542f5fSchristos DRM_VBLANK_EVENT; 274842542f5fSchristos vbl.request.sequence = 1; 274942542f5fSchristos vbl.request.signal = (uintptr_t)info; 275042542f5fSchristos 275142542f5fSchristos info->queued = true; 275242542f5fSchristos if (sna_wait_vblank(sna, &vbl, info->pipe)) { 275342542f5fSchristos sna_dri2_event_free(sna, info); 275442542f5fSchristos goto complete; 275542542f5fSchristos } 275642542f5fSchristos 275742542f5fSchristos swap_limit(draw, 2); 275842542f5fSchristos } 275942542f5fSchristos } else { 276042542f5fSchristoscomplete: 276142542f5fSchristos fake_swap_complete(sna, client, draw, crtc, DRI2_EXCHANGE_COMPLETE, func, data); 276242542f5fSchristos } 276342542f5fSchristos 276442542f5fSchristos *target_msc = current_msc + 1; 276542542f5fSchristos return true; 276642542f5fSchristos} 276742542f5fSchristos 276842542f5fSchristosstatic bool 276942542f5fSchristossna_dri2_schedule_xchg_crtc(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, 277042542f5fSchristos DRI2BufferPtr front, DRI2BufferPtr back, 277142542f5fSchristos CARD64 *target_msc, CARD64 divisor, CARD64 remainder, 277242542f5fSchristos DRI2SwapEventPtr func, void *data) 277342542f5fSchristos{ 277442542f5fSchristos struct sna *sna = to_sna_from_drawable(draw); 277542542f5fSchristos uint64_t current_msc; 277642542f5fSchristos bool sync, event; 277742542f5fSchristos 277842542f5fSchristos if (!immediate_swap(sna, *target_msc, divisor, draw, crtc, ¤t_msc)) 277942542f5fSchristos return false; 278042542f5fSchristos 278142542f5fSchristos sync = current_msc < *target_msc; 278242542f5fSchristos event = dri2_chain(draw) == NULL; 278342542f5fSchristos if (!sync || event) { 278442542f5fSchristos DBG(("%s: performing immediate xchg only on pipe %d\n", 278542542f5fSchristos __FUNCTION__, sna_crtc_to_pipe(crtc))); 278642542f5fSchristos sna_dri2_xchg_crtc(sna, draw, crtc, front, back); 278742542f5fSchristos } 278842542f5fSchristos if (sync) { 278942542f5fSchristos struct sna_dri2_event *info; 279042542f5fSchristos 279142542f5fSchristos info = sna_dri2_add_event(sna, draw, client); 279242542f5fSchristos if (!info) 279342542f5fSchristos goto complete; 279442542f5fSchristos 279542542f5fSchristos info->event_complete = func; 279642542f5fSchristos info->event_data = data; 279742542f5fSchristos 279842542f5fSchristos info->front = sna_dri2_reference_buffer(front); 279942542f5fSchristos info->back = sna_dri2_reference_buffer(back); 280042542f5fSchristos info->type = SWAP_THROTTLE; 280142542f5fSchristos 280242542f5fSchristos if (event) { 280342542f5fSchristos union drm_wait_vblank vbl; 280442542f5fSchristos 280542542f5fSchristos VG_CLEAR(vbl); 280642542f5fSchristos vbl.request.type = 280742542f5fSchristos DRM_VBLANK_RELATIVE | 280842542f5fSchristos DRM_VBLANK_EVENT; 280942542f5fSchristos vbl.request.sequence = 1; 281042542f5fSchristos vbl.request.signal = (uintptr_t)info; 281142542f5fSchristos 281242542f5fSchristos info->queued = true; 281342542f5fSchristos if (sna_wait_vblank(sna, &vbl, info->pipe)) { 281442542f5fSchristos sna_dri2_event_free(sna, info); 281542542f5fSchristos goto complete; 281642542f5fSchristos } 281742542f5fSchristos 281842542f5fSchristos swap_limit(draw, 2); 281942542f5fSchristos } 282042542f5fSchristos } else { 282142542f5fSchristoscomplete: 282242542f5fSchristos fake_swap_complete(sna, client, draw, crtc, DRI2_EXCHANGE_COMPLETE, func, data); 282342542f5fSchristos } 282442542f5fSchristos 282542542f5fSchristos *target_msc = current_msc + 1; 282642542f5fSchristos return true; 282742542f5fSchristos} 282842542f5fSchristos 282942542f5fSchristosstatic bool has_pending_events(struct sna *sna) 283042542f5fSchristos{ 283142542f5fSchristos struct pollfd pfd; 283242542f5fSchristos pfd.fd = sna->kgem.fd; 283342542f5fSchristos pfd.events = POLLIN; 283442542f5fSchristos return poll(&pfd, 1, 0) == 1; 283542542f5fSchristos} 283642542f5fSchristos 283742542f5fSchristos/* 283842542f5fSchristos * ScheduleSwap is responsible for requesting a DRM vblank event for the 283942542f5fSchristos * appropriate frame. 284042542f5fSchristos * 284142542f5fSchristos * In the case of a blit (e.g. for a windowed swap) or buffer exchange, 284242542f5fSchristos * the vblank requested can simply be the last queued swap frame + the swap 284342542f5fSchristos * interval for the drawable. 284442542f5fSchristos * 284542542f5fSchristos * In the case of a page flip, we request an event for the last queued swap 284642542f5fSchristos * frame + swap interval - 1, since we'll need to queue the flip for the frame 284742542f5fSchristos * immediately following the received event. 284842542f5fSchristos * 284942542f5fSchristos * The client will be blocked if it tries to perform further GL commands 285042542f5fSchristos * after queueing a swap, though in the Intel case after queueing a flip, the 285142542f5fSchristos * client is free to queue more commands; they'll block in the kernel if 285242542f5fSchristos * they access buffers busy with the flip. 285342542f5fSchristos * 285442542f5fSchristos * When the swap is complete, the driver should call into the server so it 285542542f5fSchristos * can send any swap complete events that have been requested. 285642542f5fSchristos */ 285742542f5fSchristosstatic int 285842542f5fSchristossna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, 285942542f5fSchristos DRI2BufferPtr back, CARD64 *target_msc, CARD64 divisor, 286042542f5fSchristos CARD64 remainder, DRI2SwapEventPtr func, void *data) 286142542f5fSchristos{ 286242542f5fSchristos struct sna *sna = to_sna_from_drawable(draw); 286342542f5fSchristos union drm_wait_vblank vbl; 286442542f5fSchristos xf86CrtcPtr crtc = NULL; 286542542f5fSchristos struct sna_dri2_event *info = NULL; 286642542f5fSchristos CARD64 current_msc; 286742542f5fSchristos 286842542f5fSchristos DBG(("%s: draw=%lu %dx%d, pixmap=%ld %dx%d, back=%u (refs=%d/%d, flush=%d) , front=%u (refs=%d/%d, flush=%d)\n", 286942542f5fSchristos __FUNCTION__, 287042542f5fSchristos (long)draw->id, draw->width, draw->height, 287142542f5fSchristos get_drawable_pixmap(draw)->drawable.serialNumber, 287242542f5fSchristos get_drawable_pixmap(draw)->drawable.width, 287342542f5fSchristos get_drawable_pixmap(draw)->drawable.height, 287442542f5fSchristos get_private(back)->bo->handle, 287542542f5fSchristos get_private(back)->refcnt, 287642542f5fSchristos get_private(back)->bo->refcnt, 287742542f5fSchristos get_private(back)->bo->flush, 287842542f5fSchristos get_private(front)->bo->handle, 287942542f5fSchristos get_private(front)->refcnt, 288042542f5fSchristos get_private(front)->bo->refcnt, 288142542f5fSchristos get_private(front)->bo->flush)); 288242542f5fSchristos 288342542f5fSchristos DBG(("%s(target_msc=%llu, divisor=%llu, remainder=%llu)\n", 288442542f5fSchristos __FUNCTION__, 288542542f5fSchristos (long long)*target_msc, 288642542f5fSchristos (long long)divisor, 288742542f5fSchristos (long long)remainder)); 288842542f5fSchristos 288942542f5fSchristos assert(get_private(front)->refcnt); 289042542f5fSchristos assert(get_private(back)->refcnt); 289142542f5fSchristos 289242542f5fSchristos assert(get_private(front)->bo->refcnt); 289342542f5fSchristos assert(get_private(back)->bo->refcnt); 289442542f5fSchristos 289542542f5fSchristos if (get_private(front)->pixmap != get_drawable_pixmap(draw)) 289642542f5fSchristos goto skip; 289742542f5fSchristos 289842542f5fSchristos if (get_private(back)->stale) 289942542f5fSchristos goto skip; 290042542f5fSchristos 290142542f5fSchristos assert(sna_pixmap_from_drawable(draw)->flush); 290242542f5fSchristos 290342542f5fSchristos if (draw->type != DRAWABLE_PIXMAP) { 290442542f5fSchristos WindowPtr win = (WindowPtr)draw; 290542542f5fSchristos struct dri2_window *priv = dri2_window(win); 290642542f5fSchristos if (priv->front) { 290742542f5fSchristos assert(front == priv->front); 290842542f5fSchristos assert(get_private(priv->front)->refcnt > 1); 290942542f5fSchristos get_private(priv->front)->refcnt--; 291042542f5fSchristos priv->front = NULL; 291142542f5fSchristos } 291242542f5fSchristos if (win->clipList.extents.x2 <= win->clipList.extents.x1 || 291342542f5fSchristos win->clipList.extents.y2 <= win->clipList.extents.y1) 291442542f5fSchristos goto skip; 291542542f5fSchristos } 291642542f5fSchristos 291742542f5fSchristos /* Drawable not displayed... just complete the swap */ 291842542f5fSchristos if ((sna->flags & SNA_NO_WAIT) == 0) 291942542f5fSchristos crtc = sna_dri2_get_crtc(draw); 292042542f5fSchristos if (crtc == NULL) { 292142542f5fSchristos DBG(("%s: off-screen, immediate update\n", __FUNCTION__)); 292242542f5fSchristos goto blit; 292342542f5fSchristos } 292442542f5fSchristos 292542542f5fSchristos assert(draw->type != DRAWABLE_PIXMAP); 292642542f5fSchristos 292742542f5fSchristos while (dri2_chain(draw) && has_pending_events(sna)) { 292842542f5fSchristos DBG(("%s: flushing pending events\n", __FUNCTION__)); 292942542f5fSchristos sna_mode_wakeup(sna); 293042542f5fSchristos } 293142542f5fSchristos 293242542f5fSchristos if (can_xchg(sna, draw, front, back) && 293342542f5fSchristos sna_dri2_schedule_xchg(client, draw, crtc, front, back, 293442542f5fSchristos target_msc, divisor, remainder, 293542542f5fSchristos func, data)) 293642542f5fSchristos return TRUE; 293742542f5fSchristos 293842542f5fSchristos if (can_xchg_crtc(sna, draw, front, back, crtc) && 293942542f5fSchristos sna_dri2_schedule_xchg_crtc(client, draw, crtc, front, back, 294042542f5fSchristos target_msc, divisor, remainder, 294142542f5fSchristos func, data)) 294242542f5fSchristos return TRUE; 294342542f5fSchristos 294442542f5fSchristos if (can_flip(sna, draw, front, back, crtc) && 294542542f5fSchristos sna_dri2_schedule_flip(client, draw, crtc, front, back, 294642542f5fSchristos target_msc, divisor, remainder, 294742542f5fSchristos func, data)) 294842542f5fSchristos return TRUE; 294942542f5fSchristos 295042542f5fSchristos VG_CLEAR(vbl); 295142542f5fSchristos 295242542f5fSchristos info = sna_dri2_add_event(sna, draw, client); 295342542f5fSchristos if (!info) 295442542f5fSchristos goto blit; 295542542f5fSchristos 295642542f5fSchristos assert(info->crtc == crtc); 295742542f5fSchristos info->event_complete = func; 295842542f5fSchristos info->event_data = data; 295942542f5fSchristos 296042542f5fSchristos info->front = sna_dri2_reference_buffer(front); 296142542f5fSchristos info->back = sna_dri2_reference_buffer(back); 296242542f5fSchristos 296342542f5fSchristos if (immediate_swap(sna, *target_msc, divisor, draw, crtc, ¤t_msc)) { 296442542f5fSchristos bool sync = current_msc < *target_msc; 296542542f5fSchristos if (!sna_dri2_immediate_blit(sna, info, sync, true)) 296642542f5fSchristos sna_dri2_event_free(sna, info); 296742542f5fSchristos *target_msc = current_msc + sync; 296842542f5fSchristos return TRUE; 296942542f5fSchristos } 297042542f5fSchristos 297142542f5fSchristos vbl.request.type = 297242542f5fSchristos DRM_VBLANK_ABSOLUTE | 297342542f5fSchristos DRM_VBLANK_EVENT; 297442542f5fSchristos vbl.request.signal = (uintptr_t)info; 297542542f5fSchristos 297642542f5fSchristos /* 297742542f5fSchristos * If divisor is zero, or current_msc is smaller than target_msc 297842542f5fSchristos * we just need to make sure target_msc passes before initiating 297942542f5fSchristos * the swap. 298042542f5fSchristos */ 298142542f5fSchristos info->type = SWAP; 298242542f5fSchristos info->queued = true; 298342542f5fSchristos if (divisor && current_msc >= *target_msc) { 298442542f5fSchristos DBG(("%s: missed target, queueing event for next: current=%lld, target=%lld, divisor=%lld, remainder=%lld\n", 298542542f5fSchristos __FUNCTION__, 298642542f5fSchristos (long long)current_msc, 298742542f5fSchristos (long long)*target_msc, 298842542f5fSchristos (long long)divisor, 298942542f5fSchristos (long long)remainder)); 299042542f5fSchristos 299142542f5fSchristos *target_msc = current_msc + remainder - current_msc % divisor; 299242542f5fSchristos if (*target_msc <= current_msc) 299342542f5fSchristos *target_msc += divisor; 299442542f5fSchristos } 299542542f5fSchristos vbl.request.sequence = draw_target_seq(draw, *target_msc - 1); 299642542f5fSchristos if (*target_msc <= current_msc + 1) { 299742542f5fSchristos DBG(("%s: performing blit before queueing\n", __FUNCTION__)); 299842542f5fSchristos assert(info->queued); 299942542f5fSchristos info->bo = __sna_dri2_copy_region(sna, draw, NULL, 300042542f5fSchristos back, front, 300142542f5fSchristos true); 300242542f5fSchristos info->type = SWAP_WAIT; 300342542f5fSchristos 300442542f5fSchristos vbl.request.type = 300542542f5fSchristos DRM_VBLANK_RELATIVE | 300642542f5fSchristos DRM_VBLANK_EVENT; 300742542f5fSchristos vbl.request.sequence = 1; 300842542f5fSchristos *target_msc = current_msc + 1; 300942542f5fSchristos } 301042542f5fSchristos 301142542f5fSchristos assert(info->queued); 301242542f5fSchristos if (sna_wait_vblank(sna, &vbl, info->pipe)) 301342542f5fSchristos goto blit; 301442542f5fSchristos 301542542f5fSchristos DBG(("%s: reported target_msc=%llu\n", __FUNCTION__, *target_msc)); 301642542f5fSchristos swap_limit(draw, 1 + (info->type == SWAP_WAIT)); 301742542f5fSchristos return TRUE; 301842542f5fSchristos 301942542f5fSchristosblit: 302042542f5fSchristos DBG(("%s -- blit\n", __FUNCTION__)); 302142542f5fSchristos if (info) 302242542f5fSchristos sna_dri2_event_free(sna, info); 302342542f5fSchristos if (can_xchg(sna, draw, front, back)) 302442542f5fSchristos sna_dri2_xchg(draw, front, back); 302542542f5fSchristos else 302642542f5fSchristos __sna_dri2_copy_region(sna, draw, NULL, back, front, false); 302742542f5fSchristosskip: 302842542f5fSchristos DBG(("%s: unable to show frame, unblocking client\n", __FUNCTION__)); 302942542f5fSchristos if (crtc == NULL) 303042542f5fSchristos crtc = sna_mode_first_crtc(sna); 303142542f5fSchristos fake_swap_complete(sna, client, draw, crtc, DRI2_BLIT_COMPLETE, func, data); 303242542f5fSchristos *target_msc = 0; /* offscreen, so zero out target vblank count */ 303342542f5fSchristos return TRUE; 303442542f5fSchristos} 303542542f5fSchristos 303642542f5fSchristos/* 303742542f5fSchristos * Get current frame count and frame count timestamp, based on drawable's 303842542f5fSchristos * crtc. 303942542f5fSchristos */ 304042542f5fSchristosstatic int 304142542f5fSchristossna_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc) 304242542f5fSchristos{ 304342542f5fSchristos struct sna *sna = to_sna_from_drawable(draw); 304442542f5fSchristos xf86CrtcPtr crtc = sna_dri2_get_crtc(draw); 304542542f5fSchristos const struct ust_msc *swap; 304642542f5fSchristos 304742542f5fSchristos DBG(("%s(draw=%ld, pipe=%d)\n", __FUNCTION__, draw->id, 304842542f5fSchristos crtc ? sna_crtc_to_pipe(crtc) : -1)); 304942542f5fSchristos 305042542f5fSchristos if (crtc != NULL) { 305142542f5fSchristos union drm_wait_vblank vbl; 305242542f5fSchristos 305342542f5fSchristos VG_CLEAR(vbl); 305442542f5fSchristos vbl.request.type = _DRM_VBLANK_RELATIVE; 305542542f5fSchristos vbl.request.sequence = 0; 305642542f5fSchristos if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(crtc)) == 0) 305742542f5fSchristos sna_crtc_record_vblank(crtc, &vbl); 305842542f5fSchristos } else 305942542f5fSchristos /* Drawable not displayed, make up a *monotonic* value */ 306042542f5fSchristos crtc = sna_mode_first_crtc(sna); 306142542f5fSchristos 306242542f5fSchristos swap = sna_crtc_last_swap(crtc); 306342542f5fSchristos *msc = draw_current_msc(draw, crtc, swap->msc); 306442542f5fSchristos *ust = ust64(swap->tv_sec, swap->tv_usec); 306542542f5fSchristos DBG(("%s: msc=%llu, ust=%llu\n", __FUNCTION__, 306642542f5fSchristos (long long)*msc, (long long)*ust)); 306742542f5fSchristos return TRUE; 306842542f5fSchristos} 306942542f5fSchristos 307042542f5fSchristos/* 307142542f5fSchristos * Request a DRM event when the requested conditions will be satisfied. 307242542f5fSchristos * 307342542f5fSchristos * We need to handle the event and ask the server to wake up the client when 307442542f5fSchristos * we receive it. 307542542f5fSchristos */ 307642542f5fSchristosstatic int 307742542f5fSchristossna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc, 307842542f5fSchristos CARD64 divisor, CARD64 remainder) 307942542f5fSchristos{ 308042542f5fSchristos struct sna *sna = to_sna_from_drawable(draw); 308142542f5fSchristos struct sna_dri2_event *info = NULL; 308242542f5fSchristos xf86CrtcPtr crtc; 308342542f5fSchristos CARD64 current_msc; 308442542f5fSchristos union drm_wait_vblank vbl; 308542542f5fSchristos const struct ust_msc *swap; 308642542f5fSchristos int pipe; 308742542f5fSchristos 308842542f5fSchristos crtc = sna_dri2_get_crtc(draw); 308942542f5fSchristos DBG(("%s(pipe=%d, target_msc=%llu, divisor=%llu, rem=%llu)\n", 309042542f5fSchristos __FUNCTION__, crtc ? sna_crtc_to_pipe(crtc) : -1, 309142542f5fSchristos (long long)target_msc, 309242542f5fSchristos (long long)divisor, 309342542f5fSchristos (long long)remainder)); 309442542f5fSchristos 309542542f5fSchristos /* Drawable not visible, return immediately */ 309642542f5fSchristos if (crtc == NULL) 309742542f5fSchristos goto out_complete; 309842542f5fSchristos 309942542f5fSchristos pipe = sna_crtc_to_pipe(crtc); 310042542f5fSchristos 310142542f5fSchristos VG_CLEAR(vbl); 310242542f5fSchristos 310342542f5fSchristos /* Get current count */ 310442542f5fSchristos vbl.request.type = _DRM_VBLANK_RELATIVE; 310542542f5fSchristos vbl.request.sequence = 0; 310642542f5fSchristos if (sna_wait_vblank(sna, &vbl, pipe)) 310742542f5fSchristos goto out_complete; 310842542f5fSchristos 310942542f5fSchristos current_msc = draw_current_msc(draw, crtc, sna_crtc_record_vblank(crtc, &vbl)); 311042542f5fSchristos 311142542f5fSchristos /* If target_msc already reached or passed, set it to 311242542f5fSchristos * current_msc to ensure we return a reasonable value back 311342542f5fSchristos * to the caller. This keeps the client from continually 311442542f5fSchristos * sending us MSC targets from the past by forcibly updating 311542542f5fSchristos * their count on this call. 311642542f5fSchristos */ 311742542f5fSchristos if (divisor == 0 && current_msc >= target_msc) 311842542f5fSchristos goto out_complete; 311942542f5fSchristos 312042542f5fSchristos info = sna_dri2_add_event(sna, draw, client); 312142542f5fSchristos if (!info) 312242542f5fSchristos goto out_complete; 312342542f5fSchristos 312442542f5fSchristos assert(info->crtc == crtc); 312542542f5fSchristos info->type = WAITMSC; 312642542f5fSchristos 312742542f5fSchristos vbl.request.signal = (uintptr_t)info; 312842542f5fSchristos vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; 312942542f5fSchristos /* 313042542f5fSchristos * If divisor is zero, or current_msc is smaller than target_msc, 313142542f5fSchristos * we just need to make sure target_msc passes before waking up the 313242542f5fSchristos * client. Otherwise, compute the next msc to match divisor/remainder. 313342542f5fSchristos */ 313442542f5fSchristos if (divisor && current_msc >= target_msc) { 313542542f5fSchristos DBG(("%s: missed target, queueing event for next: current=%lld, target=%lld, divisor=%lld, remainder=%lld\n", 313642542f5fSchristos __FUNCTION__, 313742542f5fSchristos (long long)current_msc, 313842542f5fSchristos (long long)target_msc, 313942542f5fSchristos (long long)divisor, 314042542f5fSchristos (long long)remainder)); 314142542f5fSchristos target_msc = current_msc + remainder - current_msc % divisor; 314242542f5fSchristos if (target_msc <= current_msc) 314342542f5fSchristos target_msc += divisor; 314442542f5fSchristos } 314542542f5fSchristos vbl.request.sequence = draw_target_seq(draw, target_msc); 314642542f5fSchristos 314742542f5fSchristos info->queued = true; 314842542f5fSchristos if (sna_wait_vblank(sna, &vbl, pipe)) 314942542f5fSchristos goto out_free_info; 315042542f5fSchristos 315142542f5fSchristos DRI2BlockClient(client, draw); 315242542f5fSchristos return TRUE; 315342542f5fSchristos 315442542f5fSchristosout_free_info: 315542542f5fSchristos sna_dri2_event_free(sna, info); 315642542f5fSchristosout_complete: 315742542f5fSchristos if (crtc == NULL) 315842542f5fSchristos crtc = sna_mode_first_crtc(sna); 315942542f5fSchristos swap = sna_crtc_last_swap(crtc); 316042542f5fSchristos DRI2WaitMSCComplete(client, draw, 316142542f5fSchristos draw_current_msc(draw, crtc, swap->msc), 316242542f5fSchristos swap->tv_sec, swap->tv_usec); 316342542f5fSchristos return TRUE; 316442542f5fSchristos} 316542542f5fSchristos#else 316642542f5fSchristosvoid sna_dri2_destroy_window(WindowPtr win) { } 316742542f5fSchristos#endif 316842542f5fSchristos 316942542f5fSchristosstatic bool has_i830_dri(void) 317042542f5fSchristos{ 317142542f5fSchristos return access(DRI_DRIVER_PATH "/i830_dri.so", R_OK) == 0; 317242542f5fSchristos} 317342542f5fSchristos 317442542f5fSchristosstatic int 317542542f5fSchristosnamecmp(const char *s1, const char *s2) 317642542f5fSchristos{ 317742542f5fSchristos char c1, c2; 317842542f5fSchristos 317942542f5fSchristos if (!s1 || *s1 == 0) { 318042542f5fSchristos if (!s2 || *s2 == 0) 318142542f5fSchristos return 0; 318242542f5fSchristos else 318342542f5fSchristos return 1; 318442542f5fSchristos } 318542542f5fSchristos 318642542f5fSchristos while (*s1 == '_' || *s1 == ' ' || *s1 == '\t') 318742542f5fSchristos s1++; 318842542f5fSchristos 318942542f5fSchristos while (*s2 == '_' || *s2 == ' ' || *s2 == '\t') 319042542f5fSchristos s2++; 319142542f5fSchristos 319242542f5fSchristos c1 = isupper(*s1) ? tolower(*s1) : *s1; 319342542f5fSchristos c2 = isupper(*s2) ? tolower(*s2) : *s2; 319442542f5fSchristos while (c1 == c2) { 319542542f5fSchristos if (c1 == '\0') 319642542f5fSchristos return 0; 319742542f5fSchristos 319842542f5fSchristos s1++; 319942542f5fSchristos while (*s1 == '_' || *s1 == ' ' || *s1 == '\t') 320042542f5fSchristos s1++; 320142542f5fSchristos 320242542f5fSchristos s2++; 320342542f5fSchristos while (*s2 == '_' || *s2 == ' ' || *s2 == '\t') 320442542f5fSchristos s2++; 320542542f5fSchristos 320642542f5fSchristos c1 = isupper(*s1) ? tolower(*s1) : *s1; 320742542f5fSchristos c2 = isupper(*s2) ? tolower(*s2) : *s2; 320842542f5fSchristos } 320942542f5fSchristos 321042542f5fSchristos return c1 - c2; 321142542f5fSchristos} 321242542f5fSchristos 321342542f5fSchristosstatic bool is_level(const char **str) 321442542f5fSchristos{ 321542542f5fSchristos const char *s = *str; 321642542f5fSchristos char *end; 321742542f5fSchristos unsigned val; 321842542f5fSchristos 321942542f5fSchristos if (s == NULL || *s == '\0') 322042542f5fSchristos return true; 322142542f5fSchristos 322242542f5fSchristos if (namecmp(s, "on") == 0) 322342542f5fSchristos return true; 322442542f5fSchristos if (namecmp(s, "true") == 0) 322542542f5fSchristos return true; 322642542f5fSchristos if (namecmp(s, "yes") == 0) 322742542f5fSchristos return true; 322842542f5fSchristos 322942542f5fSchristos if (namecmp(s, "0") == 0) 323042542f5fSchristos return true; 323142542f5fSchristos if (namecmp(s, "off") == 0) 323242542f5fSchristos return true; 323342542f5fSchristos if (namecmp(s, "false") == 0) 323442542f5fSchristos return true; 323542542f5fSchristos if (namecmp(s, "no") == 0) 323642542f5fSchristos return true; 323742542f5fSchristos 323842542f5fSchristos val = strtoul(s, &end, 0); 323942542f5fSchristos if (val && *end == '\0') 324042542f5fSchristos return true; 324142542f5fSchristos if (val && *end == ':') 324242542f5fSchristos *str = end + 1; 324342542f5fSchristos return false; 324442542f5fSchristos} 324542542f5fSchristos 324642542f5fSchristosstatic const char *dri_driver_name(struct sna *sna) 324742542f5fSchristos{ 324842542f5fSchristos const char *s = xf86GetOptValString(sna->Options, OPTION_DRI); 324942542f5fSchristos 325042542f5fSchristos if (is_level(&s)) { 325142542f5fSchristos if (sna->kgem.gen < 030) 325242542f5fSchristos return has_i830_dri() ? "i830" : "i915"; 325342542f5fSchristos else if (sna->kgem.gen < 040) 325442542f5fSchristos return "i915"; 325542542f5fSchristos else 325642542f5fSchristos return "i965"; 325742542f5fSchristos } 325842542f5fSchristos 325942542f5fSchristos return s; 326042542f5fSchristos} 326142542f5fSchristos 326242542f5fSchristosbool sna_dri2_open(struct sna *sna, ScreenPtr screen) 326342542f5fSchristos{ 326442542f5fSchristos DRI2InfoRec info; 326542542f5fSchristos int major = 1, minor = 0; 326642542f5fSchristos#if DRI2INFOREC_VERSION >= 4 326742542f5fSchristos const char *driverNames[2]; 326842542f5fSchristos#endif 326942542f5fSchristos 327042542f5fSchristos DBG(("%s()\n", __FUNCTION__)); 327142542f5fSchristos 327242542f5fSchristos if (wedged(sna)) { 327342542f5fSchristos xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING, 327442542f5fSchristos "loading DRI2 whilst the GPU is wedged.\n"); 327542542f5fSchristos } 327642542f5fSchristos 327742542f5fSchristos if (xf86LoaderCheckSymbol("DRI2Version")) 327842542f5fSchristos DRI2Version(&major, &minor); 327942542f5fSchristos 328042542f5fSchristos if (minor < 1) { 328142542f5fSchristos xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING, 328242542f5fSchristos "DRI2 requires DRI2 module version 1.1.0 or later\n"); 328342542f5fSchristos return false; 328442542f5fSchristos } 328542542f5fSchristos 328642542f5fSchristos memset(&info, '\0', sizeof(info)); 328742542f5fSchristos info.fd = sna->kgem.fd; 328842542f5fSchristos info.driverName = dri_driver_name(sna); 328942542f5fSchristos info.deviceName = intel_get_client_name(sna->scrn); 329042542f5fSchristos 329142542f5fSchristos DBG(("%s: loading dri driver '%s' [gen=%d] for device '%s'\n", 329242542f5fSchristos __FUNCTION__, info.driverName, sna->kgem.gen, info.deviceName)); 329342542f5fSchristos 329442542f5fSchristos#if DRI2INFOREC_VERSION == 2 329542542f5fSchristos /* The ABI between 2 and 3 was broken so we could get rid of 329642542f5fSchristos * the multi-buffer alloc functions. Make sure we indicate the 329742542f5fSchristos * right version so DRI2 can reject us if it's version 3 or above. */ 329842542f5fSchristos info.version = 2; 329942542f5fSchristos#else 330042542f5fSchristos info.version = 3; 330142542f5fSchristos#endif 330242542f5fSchristos info.CreateBuffer = sna_dri2_create_buffer; 330342542f5fSchristos info.DestroyBuffer = sna_dri2_destroy_buffer; 330442542f5fSchristos 330542542f5fSchristos info.CopyRegion = sna_dri2_copy_region; 330642542f5fSchristos#if DRI2INFOREC_VERSION >= 4 330742542f5fSchristos info.version = 4; 330842542f5fSchristos info.ScheduleSwap = sna_dri2_schedule_swap; 330942542f5fSchristos info.GetMSC = sna_dri2_get_msc; 331042542f5fSchristos info.ScheduleWaitMSC = sna_dri2_schedule_wait_msc; 331142542f5fSchristos info.numDrivers = 2; 331242542f5fSchristos info.driverNames = driverNames; 331342542f5fSchristos driverNames[0] = info.driverName; 331442542f5fSchristos driverNames[1] = info.driverName; 331542542f5fSchristos#endif 331642542f5fSchristos 331742542f5fSchristos#if DRI2INFOREC_VERSION >= 6 331842542f5fSchristos if (xorg_can_triple_buffer(sna)) { 331942542f5fSchristos info.version = 6; 332042542f5fSchristos info.SwapLimitValidate = sna_dri2_swap_limit_validate; 332142542f5fSchristos info.ReuseBufferNotify = sna_dri2_reuse_buffer; 332242542f5fSchristos } 332342542f5fSchristos#endif 332442542f5fSchristos 332542542f5fSchristos#if USE_ASYNC_SWAP 332642542f5fSchristos info.version = 10; 332742542f5fSchristos info.scheduleSwap0 = 1; 332842542f5fSchristos#endif 332942542f5fSchristos 333042542f5fSchristos return DRI2ScreenInit(screen, &info); 333142542f5fSchristos} 333242542f5fSchristos 333342542f5fSchristosvoid sna_dri2_close(struct sna *sna, ScreenPtr screen) 333442542f5fSchristos{ 333542542f5fSchristos DBG(("%s()\n", __FUNCTION__)); 333642542f5fSchristos DRI2CloseScreen(screen); 333742542f5fSchristos} 3338