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