From 1c94119ae26b94a60bb2c2b33494ed43c3b8a52f Mon Sep 17 00:00:00 2001 From: marha Date: Sun, 16 May 2010 20:50:58 +0000 Subject: svn merge -r588:HEAD ^/branches/released . --- xorg-server/exa/exa.c | 2258 +++++++++++++++++++-------------------- xorg-server/exa/exa_accel.c | 18 +- xorg-server/exa/exa_glyphs.c | 1734 +++++++++++++++--------------- xorg-server/exa/exa_offscreen.c | 1392 ++++++++++++------------ 4 files changed, 2701 insertions(+), 2701 deletions(-) (limited to 'xorg-server/exa') diff --git a/xorg-server/exa/exa.c b/xorg-server/exa/exa.c index da3797237..c16870e5d 100644 --- a/xorg-server/exa/exa.c +++ b/xorg-server/exa/exa.c @@ -1,1129 +1,1129 @@ -/* - * Copyright © 2001 Keith Packard - * - * Partly based on code that is Copyright © The XFree86 Project Inc. - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Keith Packard not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission. Keith Packard makes no - * representations about the suitability of this software for any purpose. It - * is provided "as is" without express or implied warranty. - * - * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -/** @file - * This file covers the initialization and teardown of EXA, and has various - * functions not responsible for performing rendering, pixmap migration, or - * memory management. - */ - -#ifdef HAVE_DIX_CONFIG_H -#include -#endif - -#include - -#include "exa_priv.h" -#include "exa.h" - -static int exaScreenPrivateKeyIndex; -DevPrivateKey exaScreenPrivateKey = &exaScreenPrivateKeyIndex; -static int exaPixmapPrivateKeyIndex; -DevPrivateKey exaPixmapPrivateKey = &exaPixmapPrivateKeyIndex; -static int exaGCPrivateKeyIndex; -DevPrivateKey exaGCPrivateKey = &exaGCPrivateKeyIndex; - -#ifdef MITSHM -static ShmFuncs exaShmFuncs = { NULL, NULL }; -#endif - -/** - * exaGetPixmapOffset() returns the offset (in bytes) within the framebuffer of - * the beginning of the given pixmap. - * - * Note that drivers are free to, and often do, munge this offset as necessary - * for handing to the hardware -- for example, translating it into a different - * aperture. This function may need to be extended in the future if we grow - * support for having multiple card-accessible offscreen, such as an AGP memory - * pool alongside the framebuffer pool. - */ -unsigned long -exaGetPixmapOffset(PixmapPtr pPix) -{ - ExaScreenPriv (pPix->drawable.pScreen); - ExaPixmapPriv (pPix); - - return (CARD8 *)pExaPixmap->fb_ptr - pExaScr->info->memoryBase; -} - -void * -exaGetPixmapDriverPrivate(PixmapPtr pPix) -{ - ExaPixmapPriv(pPix); - - return pExaPixmap->driverPriv; -} - -/** - * exaGetPixmapPitch() returns the pitch (in bytes) of the given pixmap. - * - * This is a helper to make driver code more obvious, due to the rather obscure - * naming of the pitch field in the pixmap. - */ -unsigned long -exaGetPixmapPitch(PixmapPtr pPix) -{ - return pPix->devKind; -} - -/** - * exaGetPixmapSize() returns the size in bytes of the given pixmap in video - * memory. Only valid when the pixmap is currently in framebuffer. - */ -unsigned long -exaGetPixmapSize(PixmapPtr pPix) -{ - ExaPixmapPrivPtr pExaPixmap; - - pExaPixmap = ExaGetPixmapPriv(pPix); - if (pExaPixmap != NULL) - return pExaPixmap->fb_size; - return 0; -} - -/** - * exaGetDrawablePixmap() returns a backing pixmap for a given drawable. - * - * @param pDrawable the drawable being requested. - * - * This function returns the backing pixmap for a drawable, whether it is a - * redirected window, unredirected window, or already a pixmap. Note that - * coordinate translation is needed when drawing to the backing pixmap of a - * redirected window, and the translation coordinates are provided by calling - * exaGetOffscreenPixmap() on the drawable. - */ -PixmapPtr -exaGetDrawablePixmap(DrawablePtr pDrawable) -{ - if (pDrawable->type == DRAWABLE_WINDOW) - return pDrawable->pScreen->GetWindowPixmap ((WindowPtr) pDrawable); - else - return (PixmapPtr) pDrawable; -} - -/** - * Sets the offsets to add to coordinates to make them address the same bits in - * the backing drawable. These coordinates are nonzero only for redirected - * windows. - */ -void -exaGetDrawableDeltas (DrawablePtr pDrawable, PixmapPtr pPixmap, - int *xp, int *yp) -{ -#ifdef COMPOSITE - if (pDrawable->type == DRAWABLE_WINDOW) { - *xp = -pPixmap->screen_x; - *yp = -pPixmap->screen_y; - return; - } -#endif - - *xp = 0; - *yp = 0; -} - -/** - * exaPixmapDirty() marks a pixmap as dirty, allowing for - * optimizations in pixmap migration when no changes have occurred. - */ -void -exaPixmapDirty (PixmapPtr pPix, int x1, int y1, int x2, int y2) -{ - BoxRec box; - RegionRec region; - - box.x1 = max(x1, 0); - box.y1 = max(y1, 0); - box.x2 = min(x2, pPix->drawable.width); - box.y2 = min(y2, pPix->drawable.height); - - if (box.x1 >= box.x2 || box.y1 >= box.y2) - return; - - REGION_INIT(pScreen, ®ion, &box, 1); - DamageRegionAppend(&pPix->drawable, ®ion); - DamageRegionProcessPending(&pPix->drawable); - REGION_UNINIT(pScreen, ®ion); -} - -static int -exaLog2(int val) -{ - int bits; - - if (val <= 0) - return 0; - for (bits = 0; val != 0; bits++) - val >>= 1; - return bits - 1; -} - -void -exaSetAccelBlock(ExaScreenPrivPtr pExaScr, ExaPixmapPrivPtr pExaPixmap, - int w, int h, int bpp) -{ - pExaPixmap->accel_blocked = 0; - - if (pExaScr->info->maxPitchPixels) { - int max_pitch = pExaScr->info->maxPitchPixels * bits_to_bytes(bpp); - - if (pExaPixmap->fb_pitch > max_pitch) - pExaPixmap->accel_blocked |= EXA_RANGE_PITCH; - } - - if (pExaScr->info->maxPitchBytes && - pExaPixmap->fb_pitch > pExaScr->info->maxPitchBytes) - pExaPixmap->accel_blocked |= EXA_RANGE_PITCH; - - if (w > pExaScr->info->maxX) - pExaPixmap->accel_blocked |= EXA_RANGE_WIDTH; - - if (h > pExaScr->info->maxY) - pExaPixmap->accel_blocked |= EXA_RANGE_HEIGHT; -} - -void -exaSetFbPitch(ExaScreenPrivPtr pExaScr, ExaPixmapPrivPtr pExaPixmap, - int w, int h, int bpp) -{ - if (pExaScr->info->flags & EXA_OFFSCREEN_ALIGN_POT && w != 1) - pExaPixmap->fb_pitch = bits_to_bytes((1 << (exaLog2(w - 1) + 1)) * bpp); - else - pExaPixmap->fb_pitch = bits_to_bytes(w * bpp); - - pExaPixmap->fb_pitch = EXA_ALIGN(pExaPixmap->fb_pitch, - pExaScr->info->pixmapPitchAlign); -} - -/** - * Returns TRUE if the pixmap is not movable. This is the case where it's a - * pixmap which has no private (almost always bad) or it's a scratch pixmap created by - * some X Server internal component (the score says it's pinned). - */ -Bool -exaPixmapIsPinned (PixmapPtr pPix) -{ - ExaPixmapPriv (pPix); - - if (pExaPixmap == NULL) - EXA_FatalErrorDebugWithRet(("EXA bug: exaPixmapIsPinned was called on a non-exa pixmap.\n"), TRUE); - - return pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED; -} - -/** - * exaPixmapHasGpuCopy() is used to determine if a pixmap is in offscreen - * memory, meaning that acceleration could probably be done to it, and that it - * will need to be wrapped by PrepareAccess()/FinishAccess() when accessing it - * with the CPU. - * - * Note that except for UploadToScreen()/DownloadFromScreen() (which explicitly - * deal with moving pixmaps in and out of system memory), EXA will give drivers - * pixmaps as arguments for which exaPixmapHasGpuCopy() is TRUE. - * - * @return TRUE if the given drawable is in framebuffer memory. - */ -Bool -exaPixmapHasGpuCopy(PixmapPtr pPixmap) -{ - ScreenPtr pScreen = pPixmap->drawable.pScreen; - ExaScreenPriv(pScreen); - - if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) - return FALSE; - - return (*pExaScr->pixmap_has_gpu_copy)(pPixmap); -} - -/** - * exaDrawableIsOffscreen() is a convenience wrapper for exaPixmapHasGpuCopy(). - */ -Bool -exaDrawableIsOffscreen (DrawablePtr pDrawable) -{ - return exaPixmapHasGpuCopy (exaGetDrawablePixmap (pDrawable)); -} - -/** - * Returns the pixmap which backs a drawable, and the offsets to add to - * coordinates to make them address the same bits in the backing drawable. - */ -PixmapPtr -exaGetOffscreenPixmap (DrawablePtr pDrawable, int *xp, int *yp) -{ - PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable); - - exaGetDrawableDeltas (pDrawable, pPixmap, xp, yp); - - if (exaPixmapHasGpuCopy (pPixmap)) - return pPixmap; - else - return NULL; -} - -/** - * Returns TRUE if the pixmap GPU copy is being accessed. - */ -Bool -ExaDoPrepareAccess(PixmapPtr pPixmap, int index) -{ - ScreenPtr pScreen = pPixmap->drawable.pScreen; - ExaScreenPriv (pScreen); - ExaPixmapPriv(pPixmap); - Bool has_gpu_copy, ret; - int i; - - if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) - return FALSE; - - if (pExaPixmap == NULL) - EXA_FatalErrorDebugWithRet(("EXA bug: ExaDoPrepareAccess was called on a non-exa pixmap.\n"), FALSE); - - /* Handle repeated / nested calls. */ - for (i = 0; i < EXA_NUM_PREPARE_INDICES; i++) { - if (pExaScr->access[i].pixmap == pPixmap) { - pExaScr->access[i].count++; - return pExaScr->access[i].retval; - } - } - - /* If slot for this index is taken, find an empty slot */ - if (pExaScr->access[index].pixmap) { - for (index = EXA_NUM_PREPARE_INDICES - 1; index >= 0; index--) - if (!pExaScr->access[index].pixmap) - break; - } - - /* Access to this pixmap hasn't been prepared yet, so data pointer should be NULL. */ - if (pPixmap->devPrivate.ptr != NULL) { - EXA_FatalErrorDebug(("EXA bug: pPixmap->devPrivate.ptr was %p, but should have been NULL.\n", - pPixmap->devPrivate.ptr)); - } - - has_gpu_copy = exaPixmapHasGpuCopy(pPixmap); - - if (has_gpu_copy && pExaPixmap->fb_ptr) { - pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr; - ret = TRUE; - } else { - pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr; - ret = FALSE; - } - - /* Store so we can handle repeated / nested calls. */ - pExaScr->access[index].pixmap = pPixmap; - pExaScr->access[index].count = 1; - - if (!has_gpu_copy) - goto out; - - exaWaitSync (pScreen); - - if (pExaScr->info->PrepareAccess == NULL) - goto out; - - if (index >= EXA_PREPARE_AUX_DEST && - !(pExaScr->info->flags & EXA_SUPPORTS_PREPARE_AUX)) { - if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED) - FatalError("Unsupported AUX indices used on a pinned pixmap.\n"); - exaMoveOutPixmap (pPixmap); - ret = FALSE; - goto out; - } - - if (!(*pExaScr->info->PrepareAccess) (pPixmap, index)) { - if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED && - !(pExaScr->info->flags & EXA_MIXED_PIXMAPS)) - FatalError("Driver failed PrepareAccess on a pinned pixmap.\n"); - exaMoveOutPixmap (pPixmap); - ret = FALSE; - goto out; - } - - ret = TRUE; - -out: - pExaScr->access[index].retval = ret; - return ret; -} - -/** - * exaPrepareAccess() is EXA's wrapper for the driver's PrepareAccess() handler. - * - * It deals with waiting for synchronization with the card, determining if - * PrepareAccess() is necessary, and working around PrepareAccess() failure. - */ -void -exaPrepareAccess(DrawablePtr pDrawable, int index) -{ - PixmapPtr pPixmap = exaGetDrawablePixmap(pDrawable); - ExaScreenPriv(pDrawable->pScreen); - - if (pExaScr->prepare_access_reg) - pExaScr->prepare_access_reg(pPixmap, index, NULL); - else - (void)ExaDoPrepareAccess(pPixmap, index); -} - -/** - * exaFinishAccess() is EXA's wrapper for the driver's FinishAccess() handler. - * - * It deals with calling the driver's FinishAccess() only if necessary. - */ -void -exaFinishAccess(DrawablePtr pDrawable, int index) -{ - ScreenPtr pScreen = pDrawable->pScreen; - ExaScreenPriv (pScreen); - PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable); - ExaPixmapPriv (pPixmap); - int i; - - if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) - return; - - if (pExaPixmap == NULL) - EXA_FatalErrorDebugWithRet(("EXA bug: exaFinishAccesss was called on a non-exa pixmap.\n"),); - - /* Handle repeated / nested calls. */ - for (i = 0; i < EXA_NUM_PREPARE_INDICES; i++) { - if (pExaScr->access[i].pixmap == pPixmap) { - if (--pExaScr->access[i].count > 0) - return; - break; - } - } - - /* Catch unbalanced Prepare/FinishAccess calls. */ - if (i == EXA_NUM_PREPARE_INDICES) - EXA_FatalErrorDebugWithRet(("EXA bug: FinishAccess called without PrepareAccess for pixmap 0x%p.\n", - pPixmap),); - - pExaScr->access[i].pixmap = NULL; - - /* We always hide the devPrivate.ptr. */ - pPixmap->devPrivate.ptr = NULL; - - if (!pExaScr->info->FinishAccess || !exaPixmapHasGpuCopy(pPixmap)) - return; - - if (i >= EXA_PREPARE_AUX_DEST && - !(pExaScr->info->flags & EXA_SUPPORTS_PREPARE_AUX)) { - ErrorF("EXA bug: Trying to call driver FinishAccess hook with " - "unsupported index EXA_PREPARE_AUX*\n"); - return; - } - - (*pExaScr->info->FinishAccess) (pPixmap, i); -} - -/** - * Here begins EXA's GC code. - * Do not ever access the fb/mi layer directly. - */ - -static void -exaValidateGC(GCPtr pGC, - unsigned long changes, - DrawablePtr pDrawable); - -static void -exaDestroyGC(GCPtr pGC); - -static void -exaChangeGC (GCPtr pGC, - unsigned long mask); - -static void -exaCopyGC (GCPtr pGCSrc, - unsigned long mask, - GCPtr pGCDst); - -static void -exaChangeClip (GCPtr pGC, - int type, - pointer pvalue, - int nrects); - -static void -exaCopyClip(GCPtr pGCDst, GCPtr pGCSrc); - -static void -exaCopyClip(GCPtr pGCDst, GCPtr pGCSrc); - -static void -exaDestroyClip(GCPtr pGC); - -const GCFuncs exaGCFuncs = { - exaValidateGC, - exaChangeGC, - exaCopyGC, - exaDestroyGC, - exaChangeClip, - exaDestroyClip, - exaCopyClip -}; - -static void -exaValidateGC(GCPtr pGC, - unsigned long changes, - DrawablePtr pDrawable) -{ - /* fbValidateGC will do direct access to pixmaps if the tiling has changed. - * Do a few smart things so fbValidateGC can do it's work. - */ - - ScreenPtr pScreen = pDrawable->pScreen; - ExaScreenPriv(pScreen); - ExaGCPriv(pGC); - PixmapPtr pTile = NULL; - Bool finish_current_tile = FALSE; - - /* Either of these conditions is enough to trigger access to a tile pixmap. */ - /* With pGC->tileIsPixel == 1, you run the risk of dereferencing an invalid tile pixmap pointer. */ - if (pGC->fillStyle == FillTiled || ((changes & GCTile) && !pGC->tileIsPixel)) { - pTile = pGC->tile.pixmap; - - /* Sometimes tile pixmaps are swapped, you need access to: - * - The current tile if it depth matches. - * - Or the rotated tile if that one matches depth and !(changes & GCTile). - * - Or the current tile pixmap and a newly created one. - */ - if (pTile && pTile->drawable.depth != pDrawable->depth && !(changes & GCTile)) { - PixmapPtr pRotatedTile = fbGetRotatedPixmap(pGC); - if (pRotatedTile && pRotatedTile->drawable.depth == pDrawable->depth) - pTile = pRotatedTile; - else - finish_current_tile = TRUE; /* CreatePixmap will be called. */ - } - } - - if (pGC->stipple) - exaPrepareAccess(&pGC->stipple->drawable, EXA_PREPARE_MASK); - if (pTile) - exaPrepareAccess(&pTile->drawable, EXA_PREPARE_SRC); - - /* Calls to Create/DestroyPixmap have to be identified as special. */ - pExaScr->fallback_counter++; - swap(pExaGC, pGC, funcs); - (*pGC->funcs->ValidateGC)(pGC, changes, pDrawable); - swap(pExaGC, pGC, funcs); - pExaScr->fallback_counter--; - - if (pTile) - exaFinishAccess(&pTile->drawable, EXA_PREPARE_SRC); - if (finish_current_tile && pGC->tile.pixmap) - exaFinishAccess(&pGC->tile.pixmap->drawable, EXA_PREPARE_AUX_DEST); - if (pGC->stipple) - exaFinishAccess(&pGC->stipple->drawable, EXA_PREPARE_MASK); -} - -/* Is exaPrepareAccessGC() needed? */ -static void -exaDestroyGC(GCPtr pGC) -{ - ExaGCPriv(pGC); - swap(pExaGC, pGC, funcs); - (*pGC->funcs->DestroyGC)(pGC); - swap(pExaGC, pGC, funcs); -} - -static void -exaChangeGC (GCPtr pGC, - unsigned long mask) -{ - ExaGCPriv(pGC); - swap(pExaGC, pGC, funcs); - (*pGC->funcs->ChangeGC) (pGC, mask); - swap(pExaGC, pGC, funcs); -} - -static void -exaCopyGC (GCPtr pGCSrc, - unsigned long mask, - GCPtr pGCDst) -{ - ExaGCPriv(pGCDst); - swap(pExaGC, pGCDst, funcs); - (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst); - swap(pExaGC, pGCDst, funcs); -} - -static void -exaChangeClip (GCPtr pGC, - int type, - pointer pvalue, - int nrects) -{ - ExaGCPriv(pGC); - swap(pExaGC, pGC, funcs); - (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects); - swap(pExaGC, pGC, funcs); -} - -static void -exaCopyClip(GCPtr pGCDst, GCPtr pGCSrc) -{ - ExaGCPriv(pGCDst); - swap(pExaGC, pGCDst, funcs); - (*pGCDst->funcs->CopyClip)(pGCDst, pGCSrc); - swap(pExaGC, pGCDst, funcs); -} - -static void -exaDestroyClip(GCPtr pGC) -{ - ExaGCPriv(pGC); - swap(pExaGC, pGC, funcs); - (*pGC->funcs->DestroyClip)(pGC); - swap(pExaGC, pGC, funcs); -} - -/** - * exaCreateGC makes a new GC and hooks up its funcs handler, so that - * exaValidateGC() will get called. - */ -static int -exaCreateGC (GCPtr pGC) -{ - ScreenPtr pScreen = pGC->pScreen; - ExaScreenPriv(pScreen); - ExaGCPriv(pGC); - Bool ret; - - swap(pExaScr, pScreen, CreateGC); - if ((ret = (*pScreen->CreateGC) (pGC))) { - wrap(pExaGC, pGC, funcs, (GCFuncs *) &exaGCFuncs); - wrap(pExaGC, pGC, ops, (GCOps *) &exaOps); - } - swap(pExaScr, pScreen, CreateGC); - - return ret; -} - -static Bool -exaChangeWindowAttributes(WindowPtr pWin, unsigned long mask) -{ - Bool ret; - ScreenPtr pScreen = pWin->drawable.pScreen; - ExaScreenPriv(pScreen); - - if ((mask & CWBackPixmap) && pWin->backgroundState == BackgroundPixmap) - exaPrepareAccess(&pWin->background.pixmap->drawable, EXA_PREPARE_SRC); - - if ((mask & CWBorderPixmap) && pWin->borderIsPixel == FALSE) - exaPrepareAccess(&pWin->border.pixmap->drawable, EXA_PREPARE_MASK); - - pExaScr->fallback_counter++; - swap(pExaScr, pScreen, ChangeWindowAttributes); - ret = pScreen->ChangeWindowAttributes(pWin, mask); - swap(pExaScr, pScreen, ChangeWindowAttributes); - pExaScr->fallback_counter--; - - if ((mask & CWBackPixmap) && pWin->backgroundState == BackgroundPixmap) - exaFinishAccess(&pWin->background.pixmap->drawable, EXA_PREPARE_SRC); - if ((mask & CWBorderPixmap) && pWin->borderIsPixel == FALSE) - exaFinishAccess(&pWin->border.pixmap->drawable, EXA_PREPARE_MASK); - - return ret; -} - -static RegionPtr -exaBitmapToRegion(PixmapPtr pPix) -{ - RegionPtr ret; - ScreenPtr pScreen = pPix->drawable.pScreen; - ExaScreenPriv(pScreen); - - exaPrepareAccess(&pPix->drawable, EXA_PREPARE_SRC); - swap(pExaScr, pScreen, BitmapToRegion); - ret = pScreen->BitmapToRegion(pPix); - swap(pExaScr, pScreen, BitmapToRegion); - exaFinishAccess(&pPix->drawable, EXA_PREPARE_SRC); - - return ret; -} - -static Bool -exaCreateScreenResources(ScreenPtr pScreen) -{ - ExaScreenPriv(pScreen); - PixmapPtr pScreenPixmap; - Bool b; - - swap(pExaScr, pScreen, CreateScreenResources); - b = pScreen->CreateScreenResources(pScreen); - swap(pExaScr, pScreen, CreateScreenResources); - - if (!b) - return FALSE; - - pScreenPixmap = pScreen->GetScreenPixmap(pScreen); - - if (pScreenPixmap) { - ExaPixmapPriv(pScreenPixmap); - - exaSetAccelBlock(pExaScr, pExaPixmap, - pScreenPixmap->drawable.width, - pScreenPixmap->drawable.height, - pScreenPixmap->drawable.bitsPerPixel); - } - - return TRUE; -} - -static void -ExaBlockHandler(int screenNum, pointer blockData, pointer pTimeout, - pointer pReadmask) -{ - ScreenPtr pScreen = screenInfo.screens[screenNum]; - ExaScreenPriv(pScreen); - - /* Move any deferred results from a software fallback to the driver pixmap */ - if (pExaScr->deferred_mixed_pixmap) - exaMoveInPixmap_mixed(pExaScr->deferred_mixed_pixmap); - - unwrap(pExaScr, pScreen, BlockHandler); - (*pScreen->BlockHandler) (screenNum, blockData, pTimeout, pReadmask); - wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler); - - /* The rest only applies to classic EXA */ - if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) - return; - - /* Try and keep the offscreen memory area tidy every now and then (at most - * once per second) when the server has been idle for at least 100ms. - */ - if (pExaScr->numOffscreenAvailable > 1) { - CARD32 now = GetTimeInMillis(); - - pExaScr->nextDefragment = now + - max(100, (INT32)(pExaScr->lastDefragment + 1000 - now)); - AdjustWaitForDelay(pTimeout, pExaScr->nextDefragment - now); - } -} - -static void -ExaWakeupHandler(int screenNum, pointer wakeupData, unsigned long result, - pointer pReadmask) -{ - ScreenPtr pScreen = screenInfo.screens[screenNum]; - ExaScreenPriv(pScreen); - - unwrap(pExaScr, pScreen, WakeupHandler); - (*pScreen->WakeupHandler) (screenNum, wakeupData, result, pReadmask); - wrap(pExaScr, pScreen, WakeupHandler, ExaWakeupHandler); - - if (result == 0 && pExaScr->numOffscreenAvailable > 1) { - CARD32 now = GetTimeInMillis(); - - if ((int)(now - pExaScr->nextDefragment) > 0) { - ExaOffscreenDefragment(pScreen); - pExaScr->lastDefragment = now; - } - } -} - -/** - * exaCloseScreen() unwraps its wrapped screen functions and tears down EXA's - * screen private, before calling down to the next CloseSccreen. - */ -static Bool -exaCloseScreen(int i, ScreenPtr pScreen) -{ - ExaScreenPriv(pScreen); - PictureScreenPtr ps = GetPictureScreenIfSet(pScreen); - - if (ps->Glyphs == exaGlyphs) - exaGlyphsFini(pScreen); - - if (pScreen->BlockHandler == ExaBlockHandler) - unwrap(pExaScr, pScreen, BlockHandler); - if (pScreen->WakeupHandler == ExaWakeupHandler) - unwrap(pExaScr, pScreen, WakeupHandler); - unwrap(pExaScr, pScreen, CreateGC); - unwrap(pExaScr, pScreen, CloseScreen); - unwrap(pExaScr, pScreen, GetImage); - unwrap(pExaScr, pScreen, GetSpans); - if (pExaScr->SavedCreatePixmap) - unwrap(pExaScr, pScreen, CreatePixmap); - if (pExaScr->SavedDestroyPixmap) - unwrap(pExaScr, pScreen, DestroyPixmap); - if (pExaScr->SavedModifyPixmapHeader) - unwrap(pExaScr, pScreen, ModifyPixmapHeader); - unwrap(pExaScr, pScreen, CopyWindow); - unwrap(pExaScr, pScreen, ChangeWindowAttributes); - unwrap(pExaScr, pScreen, BitmapToRegion); - unwrap(pExaScr, pScreen, CreateScreenResources); - unwrap(pExaScr, ps, Composite); - if (pExaScr->SavedGlyphs) - unwrap(pExaScr, ps, Glyphs); - unwrap(pExaScr, ps, Trapezoids); - unwrap(pExaScr, ps, Triangles); - unwrap(pExaScr, ps, AddTraps); - - xfree (pExaScr); - - return (*pScreen->CloseScreen) (i, pScreen); -} - -/** - * This function allocates a driver structure for EXA drivers to fill in. By - * having EXA allocate the structure, the driver structure can be extended - * without breaking ABI between EXA and the drivers. The driver's - * responsibility is to check beforehand that the EXA module has a matching - * major number and sufficient minor. Drivers are responsible for freeing the - * driver structure using xfree(). - * - * @return a newly allocated, zero-filled driver structure - */ -ExaDriverPtr -exaDriverAlloc(void) -{ - return xcalloc(1, sizeof(ExaDriverRec)); -} - -/** - * @param pScreen screen being initialized - * @param pScreenInfo EXA driver record - * - * exaDriverInit sets up EXA given a driver record filled in by the driver. - * pScreenInfo should have been allocated by exaDriverAlloc(). See the - * comments in _ExaDriver for what must be filled in and what is optional. - * - * @return TRUE if EXA was successfully initialized. - */ -Bool -exaDriverInit (ScreenPtr pScreen, - ExaDriverPtr pScreenInfo) -{ - ExaScreenPrivPtr pExaScr; - PictureScreenPtr ps; - - if (!pScreenInfo) - return FALSE; - - if (pScreenInfo->exa_major != EXA_VERSION_MAJOR || - pScreenInfo->exa_minor > EXA_VERSION_MINOR) - { - LogMessage(X_ERROR, "EXA(%d): driver's EXA version requirements " - "(%d.%d) are incompatible with EXA version (%d.%d)\n", - pScreen->myNum, - pScreenInfo->exa_major, pScreenInfo->exa_minor, - EXA_VERSION_MAJOR, EXA_VERSION_MINOR); - return FALSE; - } - - if (!pScreenInfo->CreatePixmap && !pScreenInfo->CreatePixmap2) { - if (!pScreenInfo->memoryBase) { - LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::memoryBase " - "must be non-zero\n", pScreen->myNum); - return FALSE; - } - - if (!pScreenInfo->memorySize) { - LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::memorySize must be " - "non-zero\n", pScreen->myNum); - return FALSE; - } - - if (pScreenInfo->offScreenBase > pScreenInfo->memorySize) { - LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::offScreenBase must " - "be <= ExaDriverRec::memorySize\n", pScreen->myNum); - return FALSE; - } - } - - if (!pScreenInfo->PrepareSolid) { - LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::PrepareSolid must be " - "non-NULL\n", pScreen->myNum); - return FALSE; - } - - if (!pScreenInfo->PrepareCopy) { - LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::PrepareCopy must be " - "non-NULL\n", pScreen->myNum); - return FALSE; - } - - if (!pScreenInfo->WaitMarker) { - LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::WaitMarker must be " - "non-NULL\n", pScreen->myNum); - return FALSE; - } - - /* If the driver doesn't set any max pitch values, we'll just assume - * that there's a limitation by pixels, and that it's the same as - * maxX. - * - * We want maxPitchPixels or maxPitchBytes to be set so we can check - * pixmaps against the max pitch in exaCreatePixmap() -- it matters - * whether a pixmap is rejected because of its pitch or - * because of its width. - */ - if (!pScreenInfo->maxPitchPixels && !pScreenInfo->maxPitchBytes) - { - pScreenInfo->maxPitchPixels = pScreenInfo->maxX; - } - - ps = GetPictureScreenIfSet(pScreen); - - pExaScr = xcalloc (sizeof (ExaScreenPrivRec), 1); - if (!pExaScr) { - LogMessage(X_WARNING, "EXA(%d): Failed to allocate screen private\n", - pScreen->myNum); - return FALSE; - } - - pExaScr->info = pScreenInfo; - - dixSetPrivate(&pScreen->devPrivates, exaScreenPrivateKey, pExaScr); - - pExaScr->migration = ExaMigrationAlways; - - exaDDXDriverInit(pScreen); - - if (!dixRequestPrivate(exaGCPrivateKey, sizeof(ExaGCPrivRec))) { - LogMessage(X_WARNING, - "EXA(%d): Failed to allocate GC private\n", - pScreen->myNum); - return FALSE; - } - - /* - * Replace various fb screen functions - */ - if ((pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS) && - (!(pExaScr->info->flags & EXA_HANDLES_PIXMAPS) || - (pExaScr->info->flags & EXA_MIXED_PIXMAPS))) - wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler); - if ((pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS) && - !(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) - wrap(pExaScr, pScreen, WakeupHandler, ExaWakeupHandler); - wrap(pExaScr, pScreen, CreateGC, exaCreateGC); - wrap(pExaScr, pScreen, CloseScreen, exaCloseScreen); - wrap(pExaScr, pScreen, GetImage, exaGetImage); - wrap(pExaScr, pScreen, GetSpans, ExaCheckGetSpans); - wrap(pExaScr, pScreen, CopyWindow, exaCopyWindow); - wrap(pExaScr, pScreen, ChangeWindowAttributes, exaChangeWindowAttributes); - wrap(pExaScr, pScreen, BitmapToRegion, exaBitmapToRegion); - wrap(pExaScr, pScreen, CreateScreenResources, exaCreateScreenResources); - - if (ps) { - wrap(pExaScr, ps, Composite, exaComposite); - if (pScreenInfo->PrepareComposite) - wrap(pExaScr, ps, Glyphs, exaGlyphs); - wrap(pExaScr, ps, Trapezoids, exaTrapezoids); - wrap(pExaScr, ps, Triangles, exaTriangles); - wrap(pExaScr, ps, AddTraps, ExaCheckAddTraps); - } - -#ifdef MITSHM - /* - * Don't allow shared pixmaps. - */ - ShmRegisterFuncs(pScreen, &exaShmFuncs); -#endif - /* - * Hookup offscreen pixmaps - */ - if (pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS) - { - if (!dixRequestPrivate(exaPixmapPrivateKey, sizeof(ExaPixmapPrivRec))) { - LogMessage(X_WARNING, - "EXA(%d): Failed to allocate pixmap private\n", - pScreen->myNum); - return FALSE; - } - if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) { - if (pExaScr->info->flags & EXA_MIXED_PIXMAPS) { - wrap(pExaScr, pScreen, CreatePixmap, exaCreatePixmap_mixed); - wrap(pExaScr, pScreen, DestroyPixmap, exaDestroyPixmap_mixed); - wrap(pExaScr, pScreen, ModifyPixmapHeader, exaModifyPixmapHeader_mixed); - pExaScr->do_migration = exaDoMigration_mixed; - pExaScr->pixmap_has_gpu_copy = exaPixmapHasGpuCopy_mixed; - pExaScr->do_move_in_pixmap = exaMoveInPixmap_mixed; - pExaScr->do_move_out_pixmap = NULL; - pExaScr->prepare_access_reg = exaPrepareAccessReg_mixed; - } else { - wrap(pExaScr, pScreen, CreatePixmap, exaCreatePixmap_driver); - wrap(pExaScr, pScreen, DestroyPixmap, exaDestroyPixmap_driver); - wrap(pExaScr, pScreen, ModifyPixmapHeader, exaModifyPixmapHeader_driver); - pExaScr->do_migration = NULL; - pExaScr->pixmap_has_gpu_copy = exaPixmapHasGpuCopy_driver; - pExaScr->do_move_in_pixmap = NULL; - pExaScr->do_move_out_pixmap = NULL; - pExaScr->prepare_access_reg = NULL; - } - } else { - wrap(pExaScr, pScreen, CreatePixmap, exaCreatePixmap_classic); - wrap(pExaScr, pScreen, DestroyPixmap, exaDestroyPixmap_classic); - wrap(pExaScr, pScreen, ModifyPixmapHeader, exaModifyPixmapHeader_classic); - pExaScr->do_migration = exaDoMigration_classic; - pExaScr->pixmap_has_gpu_copy = exaPixmapHasGpuCopy_classic; - pExaScr->do_move_in_pixmap = exaMoveInPixmap_classic; - pExaScr->do_move_out_pixmap = exaMoveOutPixmap_classic; - pExaScr->prepare_access_reg = exaPrepareAccessReg_classic; - } - if (!(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) { - LogMessage(X_INFO, "EXA(%d): Offscreen pixmap area of %lu bytes\n", - pScreen->myNum, - pExaScr->info->memorySize - pExaScr->info->offScreenBase); - } else { - LogMessage(X_INFO, "EXA(%d): Driver allocated offscreen pixmaps\n", - pScreen->myNum); - - } - } - else - LogMessage(X_INFO, "EXA(%d): No offscreen pixmaps\n", pScreen->myNum); - - if (!(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) { - DBG_PIXMAP(("============== %ld < %ld\n", pExaScr->info->offScreenBase, - pExaScr->info->memorySize)); - if (pExaScr->info->offScreenBase < pExaScr->info->memorySize) { - if (!exaOffscreenInit (pScreen)) { - LogMessage(X_WARNING, "EXA(%d): Offscreen pixmap setup failed\n", - pScreen->myNum); - return FALSE; - } - } - } - - if (ps->Glyphs == exaGlyphs) - exaGlyphsInit(pScreen); - - LogMessage(X_INFO, "EXA(%d): Driver registered support for the following" - " operations:\n", pScreen->myNum); - assert(pScreenInfo->PrepareSolid != NULL); - LogMessage(X_INFO, " Solid\n"); - assert(pScreenInfo->PrepareCopy != NULL); - LogMessage(X_INFO, " Copy\n"); - if (pScreenInfo->PrepareComposite != NULL) { - LogMessage(X_INFO, " Composite (RENDER acceleration)\n"); - } - if (pScreenInfo->UploadToScreen != NULL) { - LogMessage(X_INFO, " UploadToScreen\n"); - } - if (pScreenInfo->DownloadFromScreen != NULL) { - LogMessage(X_INFO, " DownloadFromScreen\n"); - } - - return TRUE; -} - -/** - * exaDriverFini tears down EXA on a given screen. - * - * @param pScreen screen being torn down. - */ -void -exaDriverFini (ScreenPtr pScreen) -{ - /*right now does nothing*/ -} - -/** - * exaMarkSync() should be called after any asynchronous drawing by the hardware. - * - * @param pScreen screen which drawing occurred on - * - * exaMarkSync() sets a flag to indicate that some asynchronous drawing has - * happened and a WaitSync() will be necessary before relying on the contents of - * offscreen memory from the CPU's perspective. It also calls an optional - * driver MarkSync() callback, the return value of which may be used to do partial - * synchronization with the hardware in the future. - */ -void exaMarkSync(ScreenPtr pScreen) -{ - ExaScreenPriv(pScreen); - - pExaScr->info->needsSync = TRUE; - if (pExaScr->info->MarkSync != NULL) { - pExaScr->info->lastMarker = (*pExaScr->info->MarkSync)(pScreen); - } -} - -/** - * exaWaitSync() ensures that all drawing has been completed. - * - * @param pScreen screen being synchronized. - * - * Calls down into the driver to ensure that all previous drawing has completed. - * It should always be called before relying on the framebuffer contents - * reflecting previous drawing, from a CPU perspective. - */ -void exaWaitSync(ScreenPtr pScreen) -{ - ExaScreenPriv(pScreen); - - if (pExaScr->info->needsSync && !pExaScr->swappedOut) { - (*pExaScr->info->WaitMarker)(pScreen, pExaScr->info->lastMarker); - pExaScr->info->needsSync = FALSE; - } -} - -/** - * Performs migration of the pixmaps according to the operation information - * provided in pixmaps and can_accel and the migration scheme chosen in the - * config file. - */ -void -exaDoMigration (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel) -{ - ScreenPtr pScreen = pixmaps[0].pPix->drawable.pScreen; - ExaScreenPriv(pScreen); - - if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) - return; - - if (pExaScr->do_migration) - (*pExaScr->do_migration)(pixmaps, npixmaps, can_accel); -} - -void -exaMoveInPixmap (PixmapPtr pPixmap) -{ - ScreenPtr pScreen = pPixmap->drawable.pScreen; - ExaScreenPriv(pScreen); - - if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) - return; - - if (pExaScr->do_move_in_pixmap) - (*pExaScr->do_move_in_pixmap)(pPixmap); -} - -void -exaMoveOutPixmap (PixmapPtr pPixmap) -{ - ScreenPtr pScreen = pPixmap->drawable.pScreen; - ExaScreenPriv(pScreen); - - if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) - return; - - if (pExaScr->do_move_out_pixmap) - (*pExaScr->do_move_out_pixmap)(pPixmap); -} +/* + * Copyright © 2001 Keith Packard + * + * Partly based on code that is Copyright © The XFree86 Project Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** @file + * This file covers the initialization and teardown of EXA, and has various + * functions not responsible for performing rendering, pixmap migration, or + * memory management. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include + +#include "exa_priv.h" +#include "exa.h" + +static int exaScreenPrivateKeyIndex; +DevPrivateKey exaScreenPrivateKey = &exaScreenPrivateKeyIndex; +static int exaPixmapPrivateKeyIndex; +DevPrivateKey exaPixmapPrivateKey = &exaPixmapPrivateKeyIndex; +static int exaGCPrivateKeyIndex; +DevPrivateKey exaGCPrivateKey = &exaGCPrivateKeyIndex; + +#ifdef MITSHM +static ShmFuncs exaShmFuncs = { NULL, NULL }; +#endif + +/** + * exaGetPixmapOffset() returns the offset (in bytes) within the framebuffer of + * the beginning of the given pixmap. + * + * Note that drivers are free to, and often do, munge this offset as necessary + * for handing to the hardware -- for example, translating it into a different + * aperture. This function may need to be extended in the future if we grow + * support for having multiple card-accessible offscreen, such as an AGP memory + * pool alongside the framebuffer pool. + */ +unsigned long +exaGetPixmapOffset(PixmapPtr pPix) +{ + ExaScreenPriv (pPix->drawable.pScreen); + ExaPixmapPriv (pPix); + + return (CARD8 *)pExaPixmap->fb_ptr - pExaScr->info->memoryBase; +} + +void * +exaGetPixmapDriverPrivate(PixmapPtr pPix) +{ + ExaPixmapPriv(pPix); + + return pExaPixmap->driverPriv; +} + +/** + * exaGetPixmapPitch() returns the pitch (in bytes) of the given pixmap. + * + * This is a helper to make driver code more obvious, due to the rather obscure + * naming of the pitch field in the pixmap. + */ +unsigned long +exaGetPixmapPitch(PixmapPtr pPix) +{ + return pPix->devKind; +} + +/** + * exaGetPixmapSize() returns the size in bytes of the given pixmap in video + * memory. Only valid when the pixmap is currently in framebuffer. + */ +unsigned long +exaGetPixmapSize(PixmapPtr pPix) +{ + ExaPixmapPrivPtr pExaPixmap; + + pExaPixmap = ExaGetPixmapPriv(pPix); + if (pExaPixmap != NULL) + return pExaPixmap->fb_size; + return 0; +} + +/** + * exaGetDrawablePixmap() returns a backing pixmap for a given drawable. + * + * @param pDrawable the drawable being requested. + * + * This function returns the backing pixmap for a drawable, whether it is a + * redirected window, unredirected window, or already a pixmap. Note that + * coordinate translation is needed when drawing to the backing pixmap of a + * redirected window, and the translation coordinates are provided by calling + * exaGetOffscreenPixmap() on the drawable. + */ +PixmapPtr +exaGetDrawablePixmap(DrawablePtr pDrawable) +{ + if (pDrawable->type == DRAWABLE_WINDOW) + return pDrawable->pScreen->GetWindowPixmap ((WindowPtr) pDrawable); + else + return (PixmapPtr) pDrawable; +} + +/** + * Sets the offsets to add to coordinates to make them address the same bits in + * the backing drawable. These coordinates are nonzero only for redirected + * windows. + */ +void +exaGetDrawableDeltas (DrawablePtr pDrawable, PixmapPtr pPixmap, + int *xp, int *yp) +{ +#ifdef COMPOSITE + if (pDrawable->type == DRAWABLE_WINDOW) { + *xp = -pPixmap->screen_x; + *yp = -pPixmap->screen_y; + return; + } +#endif + + *xp = 0; + *yp = 0; +} + +/** + * exaPixmapDirty() marks a pixmap as dirty, allowing for + * optimizations in pixmap migration when no changes have occurred. + */ +void +exaPixmapDirty (PixmapPtr pPix, int x1, int y1, int x2, int y2) +{ + BoxRec box; + RegionRec region; + + box.x1 = max(x1, 0); + box.y1 = max(y1, 0); + box.x2 = min(x2, pPix->drawable.width); + box.y2 = min(y2, pPix->drawable.height); + + if (box.x1 >= box.x2 || box.y1 >= box.y2) + return; + + REGION_INIT(pScreen, ®ion, &box, 1); + DamageRegionAppend(&pPix->drawable, ®ion); + DamageRegionProcessPending(&pPix->drawable); + REGION_UNINIT(pScreen, ®ion); +} + +static int +exaLog2(int val) +{ + int bits; + + if (val <= 0) + return 0; + for (bits = 0; val != 0; bits++) + val >>= 1; + return bits - 1; +} + +void +exaSetAccelBlock(ExaScreenPrivPtr pExaScr, ExaPixmapPrivPtr pExaPixmap, + int w, int h, int bpp) +{ + pExaPixmap->accel_blocked = 0; + + if (pExaScr->info->maxPitchPixels) { + int max_pitch = pExaScr->info->maxPitchPixels * bits_to_bytes(bpp); + + if (pExaPixmap->fb_pitch > max_pitch) + pExaPixmap->accel_blocked |= EXA_RANGE_PITCH; + } + + if (pExaScr->info->maxPitchBytes && + pExaPixmap->fb_pitch > pExaScr->info->maxPitchBytes) + pExaPixmap->accel_blocked |= EXA_RANGE_PITCH; + + if (w > pExaScr->info->maxX) + pExaPixmap->accel_blocked |= EXA_RANGE_WIDTH; + + if (h > pExaScr->info->maxY) + pExaPixmap->accel_blocked |= EXA_RANGE_HEIGHT; +} + +void +exaSetFbPitch(ExaScreenPrivPtr pExaScr, ExaPixmapPrivPtr pExaPixmap, + int w, int h, int bpp) +{ + if (pExaScr->info->flags & EXA_OFFSCREEN_ALIGN_POT && w != 1) + pExaPixmap->fb_pitch = bits_to_bytes((1 << (exaLog2(w - 1) + 1)) * bpp); + else + pExaPixmap->fb_pitch = bits_to_bytes(w * bpp); + + pExaPixmap->fb_pitch = EXA_ALIGN(pExaPixmap->fb_pitch, + pExaScr->info->pixmapPitchAlign); +} + +/** + * Returns TRUE if the pixmap is not movable. This is the case where it's a + * pixmap which has no private (almost always bad) or it's a scratch pixmap created by + * some X Server internal component (the score says it's pinned). + */ +Bool +exaPixmapIsPinned (PixmapPtr pPix) +{ + ExaPixmapPriv (pPix); + + if (pExaPixmap == NULL) + EXA_FatalErrorDebugWithRet(("EXA bug: exaPixmapIsPinned was called on a non-exa pixmap.\n"), TRUE); + + return pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED; +} + +/** + * exaPixmapHasGpuCopy() is used to determine if a pixmap is in offscreen + * memory, meaning that acceleration could probably be done to it, and that it + * will need to be wrapped by PrepareAccess()/FinishAccess() when accessing it + * with the CPU. + * + * Note that except for UploadToScreen()/DownloadFromScreen() (which explicitly + * deal with moving pixmaps in and out of system memory), EXA will give drivers + * pixmaps as arguments for which exaPixmapHasGpuCopy() is TRUE. + * + * @return TRUE if the given drawable is in framebuffer memory. + */ +Bool +exaPixmapHasGpuCopy(PixmapPtr pPixmap) +{ + ScreenPtr pScreen = pPixmap->drawable.pScreen; + ExaScreenPriv(pScreen); + + if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) + return FALSE; + + return (*pExaScr->pixmap_has_gpu_copy)(pPixmap); +} + +/** + * exaDrawableIsOffscreen() is a convenience wrapper for exaPixmapHasGpuCopy(). + */ +Bool +exaDrawableIsOffscreen (DrawablePtr pDrawable) +{ + return exaPixmapHasGpuCopy (exaGetDrawablePixmap (pDrawable)); +} + +/** + * Returns the pixmap which backs a drawable, and the offsets to add to + * coordinates to make them address the same bits in the backing drawable. + */ +PixmapPtr +exaGetOffscreenPixmap (DrawablePtr pDrawable, int *xp, int *yp) +{ + PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable); + + exaGetDrawableDeltas (pDrawable, pPixmap, xp, yp); + + if (exaPixmapHasGpuCopy (pPixmap)) + return pPixmap; + else + return NULL; +} + +/** + * Returns TRUE if the pixmap GPU copy is being accessed. + */ +Bool +ExaDoPrepareAccess(PixmapPtr pPixmap, int index) +{ + ScreenPtr pScreen = pPixmap->drawable.pScreen; + ExaScreenPriv (pScreen); + ExaPixmapPriv(pPixmap); + Bool has_gpu_copy, ret; + int i; + + if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) + return FALSE; + + if (pExaPixmap == NULL) + EXA_FatalErrorDebugWithRet(("EXA bug: ExaDoPrepareAccess was called on a non-exa pixmap.\n"), FALSE); + + /* Handle repeated / nested calls. */ + for (i = 0; i < EXA_NUM_PREPARE_INDICES; i++) { + if (pExaScr->access[i].pixmap == pPixmap) { + pExaScr->access[i].count++; + return pExaScr->access[i].retval; + } + } + + /* If slot for this index is taken, find an empty slot */ + if (pExaScr->access[index].pixmap) { + for (index = EXA_NUM_PREPARE_INDICES - 1; index >= 0; index--) + if (!pExaScr->access[index].pixmap) + break; + } + + /* Access to this pixmap hasn't been prepared yet, so data pointer should be NULL. */ + if (pPixmap->devPrivate.ptr != NULL) { + EXA_FatalErrorDebug(("EXA bug: pPixmap->devPrivate.ptr was %p, but should have been NULL.\n", + pPixmap->devPrivate.ptr)); + } + + has_gpu_copy = exaPixmapHasGpuCopy(pPixmap); + + if (has_gpu_copy && pExaPixmap->fb_ptr) { + pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr; + ret = TRUE; + } else { + pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr; + ret = FALSE; + } + + /* Store so we can handle repeated / nested calls. */ + pExaScr->access[index].pixmap = pPixmap; + pExaScr->access[index].count = 1; + + if (!has_gpu_copy) + goto out; + + exaWaitSync (pScreen); + + if (pExaScr->info->PrepareAccess == NULL) + goto out; + + if (index >= EXA_PREPARE_AUX_DEST && + !(pExaScr->info->flags & EXA_SUPPORTS_PREPARE_AUX)) { + if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED) + FatalError("Unsupported AUX indices used on a pinned pixmap.\n"); + exaMoveOutPixmap (pPixmap); + ret = FALSE; + goto out; + } + + if (!(*pExaScr->info->PrepareAccess) (pPixmap, index)) { + if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED && + !(pExaScr->info->flags & EXA_MIXED_PIXMAPS)) + FatalError("Driver failed PrepareAccess on a pinned pixmap.\n"); + exaMoveOutPixmap (pPixmap); + ret = FALSE; + goto out; + } + + ret = TRUE; + +out: + pExaScr->access[index].retval = ret; + return ret; +} + +/** + * exaPrepareAccess() is EXA's wrapper for the driver's PrepareAccess() handler. + * + * It deals with waiting for synchronization with the card, determining if + * PrepareAccess() is necessary, and working around PrepareAccess() failure. + */ +void +exaPrepareAccess(DrawablePtr pDrawable, int index) +{ + PixmapPtr pPixmap = exaGetDrawablePixmap(pDrawable); + ExaScreenPriv(pDrawable->pScreen); + + if (pExaScr->prepare_access_reg) + pExaScr->prepare_access_reg(pPixmap, index, NULL); + else + (void)ExaDoPrepareAccess(pPixmap, index); +} + +/** + * exaFinishAccess() is EXA's wrapper for the driver's FinishAccess() handler. + * + * It deals with calling the driver's FinishAccess() only if necessary. + */ +void +exaFinishAccess(DrawablePtr pDrawable, int index) +{ + ScreenPtr pScreen = pDrawable->pScreen; + ExaScreenPriv (pScreen); + PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable); + ExaPixmapPriv (pPixmap); + int i; + + if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) + return; + + if (pExaPixmap == NULL) + EXA_FatalErrorDebugWithRet(("EXA bug: exaFinishAccesss was called on a non-exa pixmap.\n"),); + + /* Handle repeated / nested calls. */ + for (i = 0; i < EXA_NUM_PREPARE_INDICES; i++) { + if (pExaScr->access[i].pixmap == pPixmap) { + if (--pExaScr->access[i].count > 0) + return; + break; + } + } + + /* Catch unbalanced Prepare/FinishAccess calls. */ + if (i == EXA_NUM_PREPARE_INDICES) + EXA_FatalErrorDebugWithRet(("EXA bug: FinishAccess called without PrepareAccess for pixmap 0x%p.\n", + pPixmap),); + + pExaScr->access[i].pixmap = NULL; + + /* We always hide the devPrivate.ptr. */ + pPixmap->devPrivate.ptr = NULL; + + if (!pExaScr->info->FinishAccess || !exaPixmapHasGpuCopy(pPixmap)) + return; + + if (i >= EXA_PREPARE_AUX_DEST && + !(pExaScr->info->flags & EXA_SUPPORTS_PREPARE_AUX)) { + ErrorF("EXA bug: Trying to call driver FinishAccess hook with " + "unsupported index EXA_PREPARE_AUX*\n"); + return; + } + + (*pExaScr->info->FinishAccess) (pPixmap, i); +} + +/** + * Here begins EXA's GC code. + * Do not ever access the fb/mi layer directly. + */ + +static void +exaValidateGC(GCPtr pGC, + unsigned long changes, + DrawablePtr pDrawable); + +static void +exaDestroyGC(GCPtr pGC); + +static void +exaChangeGC (GCPtr pGC, + unsigned long mask); + +static void +exaCopyGC (GCPtr pGCSrc, + unsigned long mask, + GCPtr pGCDst); + +static void +exaChangeClip (GCPtr pGC, + int type, + pointer pvalue, + int nrects); + +static void +exaCopyClip(GCPtr pGCDst, GCPtr pGCSrc); + +static void +exaCopyClip(GCPtr pGCDst, GCPtr pGCSrc); + +static void +exaDestroyClip(GCPtr pGC); + +const GCFuncs exaGCFuncs = { + exaValidateGC, + exaChangeGC, + exaCopyGC, + exaDestroyGC, + exaChangeClip, + exaDestroyClip, + exaCopyClip +}; + +static void +exaValidateGC(GCPtr pGC, + unsigned long changes, + DrawablePtr pDrawable) +{ + /* fbValidateGC will do direct access to pixmaps if the tiling has changed. + * Do a few smart things so fbValidateGC can do it's work. + */ + + ScreenPtr pScreen = pDrawable->pScreen; + ExaScreenPriv(pScreen); + ExaGCPriv(pGC); + PixmapPtr pTile = NULL; + Bool finish_current_tile = FALSE; + + /* Either of these conditions is enough to trigger access to a tile pixmap. */ + /* With pGC->tileIsPixel == 1, you run the risk of dereferencing an invalid tile pixmap pointer. */ + if (pGC->fillStyle == FillTiled || ((changes & GCTile) && !pGC->tileIsPixel)) { + pTile = pGC->tile.pixmap; + + /* Sometimes tile pixmaps are swapped, you need access to: + * - The current tile if it depth matches. + * - Or the rotated tile if that one matches depth and !(changes & GCTile). + * - Or the current tile pixmap and a newly created one. + */ + if (pTile && pTile->drawable.depth != pDrawable->depth && !(changes & GCTile)) { + PixmapPtr pRotatedTile = fbGetRotatedPixmap(pGC); + if (pRotatedTile && pRotatedTile->drawable.depth == pDrawable->depth) + pTile = pRotatedTile; + else + finish_current_tile = TRUE; /* CreatePixmap will be called. */ + } + } + + if (pGC->stipple) + exaPrepareAccess(&pGC->stipple->drawable, EXA_PREPARE_MASK); + if (pTile) + exaPrepareAccess(&pTile->drawable, EXA_PREPARE_SRC); + + /* Calls to Create/DestroyPixmap have to be identified as special. */ + pExaScr->fallback_counter++; + swap(pExaGC, pGC, funcs); + (*pGC->funcs->ValidateGC)(pGC, changes, pDrawable); + swap(pExaGC, pGC, funcs); + pExaScr->fallback_counter--; + + if (pTile) + exaFinishAccess(&pTile->drawable, EXA_PREPARE_SRC); + if (finish_current_tile && pGC->tile.pixmap) + exaFinishAccess(&pGC->tile.pixmap->drawable, EXA_PREPARE_AUX_DEST); + if (pGC->stipple) + exaFinishAccess(&pGC->stipple->drawable, EXA_PREPARE_MASK); +} + +/* Is exaPrepareAccessGC() needed? */ +static void +exaDestroyGC(GCPtr pGC) +{ + ExaGCPriv(pGC); + swap(pExaGC, pGC, funcs); + (*pGC->funcs->DestroyGC)(pGC); + swap(pExaGC, pGC, funcs); +} + +static void +exaChangeGC (GCPtr pGC, + unsigned long mask) +{ + ExaGCPriv(pGC); + swap(pExaGC, pGC, funcs); + (*pGC->funcs->ChangeGC) (pGC, mask); + swap(pExaGC, pGC, funcs); +} + +static void +exaCopyGC (GCPtr pGCSrc, + unsigned long mask, + GCPtr pGCDst) +{ + ExaGCPriv(pGCDst); + swap(pExaGC, pGCDst, funcs); + (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst); + swap(pExaGC, pGCDst, funcs); +} + +static void +exaChangeClip (GCPtr pGC, + int type, + pointer pvalue, + int nrects) +{ + ExaGCPriv(pGC); + swap(pExaGC, pGC, funcs); + (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects); + swap(pExaGC, pGC, funcs); +} + +static void +exaCopyClip(GCPtr pGCDst, GCPtr pGCSrc) +{ + ExaGCPriv(pGCDst); + swap(pExaGC, pGCDst, funcs); + (*pGCDst->funcs->CopyClip)(pGCDst, pGCSrc); + swap(pExaGC, pGCDst, funcs); +} + +static void +exaDestroyClip(GCPtr pGC) +{ + ExaGCPriv(pGC); + swap(pExaGC, pGC, funcs); + (*pGC->funcs->DestroyClip)(pGC); + swap(pExaGC, pGC, funcs); +} + +/** + * exaCreateGC makes a new GC and hooks up its funcs handler, so that + * exaValidateGC() will get called. + */ +static int +exaCreateGC (GCPtr pGC) +{ + ScreenPtr pScreen = pGC->pScreen; + ExaScreenPriv(pScreen); + ExaGCPriv(pGC); + Bool ret; + + swap(pExaScr, pScreen, CreateGC); + if ((ret = (*pScreen->CreateGC) (pGC))) { + wrap(pExaGC, pGC, funcs, (GCFuncs *) &exaGCFuncs); + wrap(pExaGC, pGC, ops, (GCOps *) &exaOps); + } + swap(pExaScr, pScreen, CreateGC); + + return ret; +} + +static Bool +exaChangeWindowAttributes(WindowPtr pWin, unsigned long mask) +{ + Bool ret; + ScreenPtr pScreen = pWin->drawable.pScreen; + ExaScreenPriv(pScreen); + + if ((mask & CWBackPixmap) && pWin->backgroundState == BackgroundPixmap) + exaPrepareAccess(&pWin->background.pixmap->drawable, EXA_PREPARE_SRC); + + if ((mask & CWBorderPixmap) && pWin->borderIsPixel == FALSE) + exaPrepareAccess(&pWin->border.pixmap->drawable, EXA_PREPARE_MASK); + + pExaScr->fallback_counter++; + swap(pExaScr, pScreen, ChangeWindowAttributes); + ret = pScreen->ChangeWindowAttributes(pWin, mask); + swap(pExaScr, pScreen, ChangeWindowAttributes); + pExaScr->fallback_counter--; + + if ((mask & CWBackPixmap) && pWin->backgroundState == BackgroundPixmap) + exaFinishAccess(&pWin->background.pixmap->drawable, EXA_PREPARE_SRC); + if ((mask & CWBorderPixmap) && pWin->borderIsPixel == FALSE) + exaFinishAccess(&pWin->border.pixmap->drawable, EXA_PREPARE_MASK); + + return ret; +} + +static RegionPtr +exaBitmapToRegion(PixmapPtr pPix) +{ + RegionPtr ret; + ScreenPtr pScreen = pPix->drawable.pScreen; + ExaScreenPriv(pScreen); + + exaPrepareAccess(&pPix->drawable, EXA_PREPARE_SRC); + swap(pExaScr, pScreen, BitmapToRegion); + ret = pScreen->BitmapToRegion(pPix); + swap(pExaScr, pScreen, BitmapToRegion); + exaFinishAccess(&pPix->drawable, EXA_PREPARE_SRC); + + return ret; +} + +static Bool +exaCreateScreenResources(ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + PixmapPtr pScreenPixmap; + Bool b; + + swap(pExaScr, pScreen, CreateScreenResources); + b = pScreen->CreateScreenResources(pScreen); + swap(pExaScr, pScreen, CreateScreenResources); + + if (!b) + return FALSE; + + pScreenPixmap = pScreen->GetScreenPixmap(pScreen); + + if (pScreenPixmap) { + ExaPixmapPriv(pScreenPixmap); + + exaSetAccelBlock(pExaScr, pExaPixmap, + pScreenPixmap->drawable.width, + pScreenPixmap->drawable.height, + pScreenPixmap->drawable.bitsPerPixel); + } + + return TRUE; +} + +static void +ExaBlockHandler(int screenNum, pointer blockData, pointer pTimeout, + pointer pReadmask) +{ + ScreenPtr pScreen = screenInfo.screens[screenNum]; + ExaScreenPriv(pScreen); + + /* Move any deferred results from a software fallback to the driver pixmap */ + if (pExaScr->deferred_mixed_pixmap) + exaMoveInPixmap_mixed(pExaScr->deferred_mixed_pixmap); + + unwrap(pExaScr, pScreen, BlockHandler); + (*pScreen->BlockHandler) (screenNum, blockData, pTimeout, pReadmask); + wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler); + + /* The rest only applies to classic EXA */ + if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) + return; + + /* Try and keep the offscreen memory area tidy every now and then (at most + * once per second) when the server has been idle for at least 100ms. + */ + if (pExaScr->numOffscreenAvailable > 1) { + CARD32 now = GetTimeInMillis(); + + pExaScr->nextDefragment = now + + max(100, (INT32)(pExaScr->lastDefragment + 1000 - now)); + AdjustWaitForDelay(pTimeout, pExaScr->nextDefragment - now); + } +} + +static void +ExaWakeupHandler(int screenNum, pointer wakeupData, unsigned long result, + pointer pReadmask) +{ + ScreenPtr pScreen = screenInfo.screens[screenNum]; + ExaScreenPriv(pScreen); + + unwrap(pExaScr, pScreen, WakeupHandler); + (*pScreen->WakeupHandler) (screenNum, wakeupData, result, pReadmask); + wrap(pExaScr, pScreen, WakeupHandler, ExaWakeupHandler); + + if (result == 0 && pExaScr->numOffscreenAvailable > 1) { + CARD32 now = GetTimeInMillis(); + + if ((int)(now - pExaScr->nextDefragment) > 0) { + ExaOffscreenDefragment(pScreen); + pExaScr->lastDefragment = now; + } + } +} + +/** + * exaCloseScreen() unwraps its wrapped screen functions and tears down EXA's + * screen private, before calling down to the next CloseSccreen. + */ +static Bool +exaCloseScreen(int i, ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + PictureScreenPtr ps = GetPictureScreenIfSet(pScreen); + + if (ps->Glyphs == exaGlyphs) + exaGlyphsFini(pScreen); + + if (pScreen->BlockHandler == ExaBlockHandler) + unwrap(pExaScr, pScreen, BlockHandler); + if (pScreen->WakeupHandler == ExaWakeupHandler) + unwrap(pExaScr, pScreen, WakeupHandler); + unwrap(pExaScr, pScreen, CreateGC); + unwrap(pExaScr, pScreen, CloseScreen); + unwrap(pExaScr, pScreen, GetImage); + unwrap(pExaScr, pScreen, GetSpans); + if (pExaScr->SavedCreatePixmap) + unwrap(pExaScr, pScreen, CreatePixmap); + if (pExaScr->SavedDestroyPixmap) + unwrap(pExaScr, pScreen, DestroyPixmap); + if (pExaScr->SavedModifyPixmapHeader) + unwrap(pExaScr, pScreen, ModifyPixmapHeader); + unwrap(pExaScr, pScreen, CopyWindow); + unwrap(pExaScr, pScreen, ChangeWindowAttributes); + unwrap(pExaScr, pScreen, BitmapToRegion); + unwrap(pExaScr, pScreen, CreateScreenResources); + unwrap(pExaScr, ps, Composite); + if (pExaScr->SavedGlyphs) + unwrap(pExaScr, ps, Glyphs); + unwrap(pExaScr, ps, Trapezoids); + unwrap(pExaScr, ps, Triangles); + unwrap(pExaScr, ps, AddTraps); + + free(pExaScr); + + return (*pScreen->CloseScreen) (i, pScreen); +} + +/** + * This function allocates a driver structure for EXA drivers to fill in. By + * having EXA allocate the structure, the driver structure can be extended + * without breaking ABI between EXA and the drivers. The driver's + * responsibility is to check beforehand that the EXA module has a matching + * major number and sufficient minor. Drivers are responsible for freeing the + * driver structure using free(). + * + * @return a newly allocated, zero-filled driver structure + */ +ExaDriverPtr +exaDriverAlloc(void) +{ + return calloc(1, sizeof(ExaDriverRec)); +} + +/** + * @param pScreen screen being initialized + * @param pScreenInfo EXA driver record + * + * exaDriverInit sets up EXA given a driver record filled in by the driver. + * pScreenInfo should have been allocated by exaDriverAlloc(). See the + * comments in _ExaDriver for what must be filled in and what is optional. + * + * @return TRUE if EXA was successfully initialized. + */ +Bool +exaDriverInit (ScreenPtr pScreen, + ExaDriverPtr pScreenInfo) +{ + ExaScreenPrivPtr pExaScr; + PictureScreenPtr ps; + + if (!pScreenInfo) + return FALSE; + + if (pScreenInfo->exa_major != EXA_VERSION_MAJOR || + pScreenInfo->exa_minor > EXA_VERSION_MINOR) + { + LogMessage(X_ERROR, "EXA(%d): driver's EXA version requirements " + "(%d.%d) are incompatible with EXA version (%d.%d)\n", + pScreen->myNum, + pScreenInfo->exa_major, pScreenInfo->exa_minor, + EXA_VERSION_MAJOR, EXA_VERSION_MINOR); + return FALSE; + } + + if (!pScreenInfo->CreatePixmap && !pScreenInfo->CreatePixmap2) { + if (!pScreenInfo->memoryBase) { + LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::memoryBase " + "must be non-zero\n", pScreen->myNum); + return FALSE; + } + + if (!pScreenInfo->memorySize) { + LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::memorySize must be " + "non-zero\n", pScreen->myNum); + return FALSE; + } + + if (pScreenInfo->offScreenBase > pScreenInfo->memorySize) { + LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::offScreenBase must " + "be <= ExaDriverRec::memorySize\n", pScreen->myNum); + return FALSE; + } + } + + if (!pScreenInfo->PrepareSolid) { + LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::PrepareSolid must be " + "non-NULL\n", pScreen->myNum); + return FALSE; + } + + if (!pScreenInfo->PrepareCopy) { + LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::PrepareCopy must be " + "non-NULL\n", pScreen->myNum); + return FALSE; + } + + if (!pScreenInfo->WaitMarker) { + LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::WaitMarker must be " + "non-NULL\n", pScreen->myNum); + return FALSE; + } + + /* If the driver doesn't set any max pitch values, we'll just assume + * that there's a limitation by pixels, and that it's the same as + * maxX. + * + * We want maxPitchPixels or maxPitchBytes to be set so we can check + * pixmaps against the max pitch in exaCreatePixmap() -- it matters + * whether a pixmap is rejected because of its pitch or + * because of its width. + */ + if (!pScreenInfo->maxPitchPixels && !pScreenInfo->maxPitchBytes) + { + pScreenInfo->maxPitchPixels = pScreenInfo->maxX; + } + + ps = GetPictureScreenIfSet(pScreen); + + pExaScr = calloc(sizeof (ExaScreenPrivRec), 1); + if (!pExaScr) { + LogMessage(X_WARNING, "EXA(%d): Failed to allocate screen private\n", + pScreen->myNum); + return FALSE; + } + + pExaScr->info = pScreenInfo; + + dixSetPrivate(&pScreen->devPrivates, exaScreenPrivateKey, pExaScr); + + pExaScr->migration = ExaMigrationAlways; + + exaDDXDriverInit(pScreen); + + if (!dixRequestPrivate(exaGCPrivateKey, sizeof(ExaGCPrivRec))) { + LogMessage(X_WARNING, + "EXA(%d): Failed to allocate GC private\n", + pScreen->myNum); + return FALSE; + } + + /* + * Replace various fb screen functions + */ + if ((pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS) && + (!(pExaScr->info->flags & EXA_HANDLES_PIXMAPS) || + (pExaScr->info->flags & EXA_MIXED_PIXMAPS))) + wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler); + if ((pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS) && + !(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) + wrap(pExaScr, pScreen, WakeupHandler, ExaWakeupHandler); + wrap(pExaScr, pScreen, CreateGC, exaCreateGC); + wrap(pExaScr, pScreen, CloseScreen, exaCloseScreen); + wrap(pExaScr, pScreen, GetImage, exaGetImage); + wrap(pExaScr, pScreen, GetSpans, ExaCheckGetSpans); + wrap(pExaScr, pScreen, CopyWindow, exaCopyWindow); + wrap(pExaScr, pScreen, ChangeWindowAttributes, exaChangeWindowAttributes); + wrap(pExaScr, pScreen, BitmapToRegion, exaBitmapToRegion); + wrap(pExaScr, pScreen, CreateScreenResources, exaCreateScreenResources); + + if (ps) { + wrap(pExaScr, ps, Composite, exaComposite); + if (pScreenInfo->PrepareComposite) + wrap(pExaScr, ps, Glyphs, exaGlyphs); + wrap(pExaScr, ps, Trapezoids, exaTrapezoids); + wrap(pExaScr, ps, Triangles, exaTriangles); + wrap(pExaScr, ps, AddTraps, ExaCheckAddTraps); + } + +#ifdef MITSHM + /* + * Don't allow shared pixmaps. + */ + ShmRegisterFuncs(pScreen, &exaShmFuncs); +#endif + /* + * Hookup offscreen pixmaps + */ + if (pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS) + { + if (!dixRequestPrivate(exaPixmapPrivateKey, sizeof(ExaPixmapPrivRec))) { + LogMessage(X_WARNING, + "EXA(%d): Failed to allocate pixmap private\n", + pScreen->myNum); + return FALSE; + } + if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) { + if (pExaScr->info->flags & EXA_MIXED_PIXMAPS) { + wrap(pExaScr, pScreen, CreatePixmap, exaCreatePixmap_mixed); + wrap(pExaScr, pScreen, DestroyPixmap, exaDestroyPixmap_mixed); + wrap(pExaScr, pScreen, ModifyPixmapHeader, exaModifyPixmapHeader_mixed); + pExaScr->do_migration = exaDoMigration_mixed; + pExaScr->pixmap_has_gpu_copy = exaPixmapHasGpuCopy_mixed; + pExaScr->do_move_in_pixmap = exaMoveInPixmap_mixed; + pExaScr->do_move_out_pixmap = NULL; + pExaScr->prepare_access_reg = exaPrepareAccessReg_mixed; + } else { + wrap(pExaScr, pScreen, CreatePixmap, exaCreatePixmap_driver); + wrap(pExaScr, pScreen, DestroyPixmap, exaDestroyPixmap_driver); + wrap(pExaScr, pScreen, ModifyPixmapHeader, exaModifyPixmapHeader_driver); + pExaScr->do_migration = NULL; + pExaScr->pixmap_has_gpu_copy = exaPixmapHasGpuCopy_driver; + pExaScr->do_move_in_pixmap = NULL; + pExaScr->do_move_out_pixmap = NULL; + pExaScr->prepare_access_reg = NULL; + } + } else { + wrap(pExaScr, pScreen, CreatePixmap, exaCreatePixmap_classic); + wrap(pExaScr, pScreen, DestroyPixmap, exaDestroyPixmap_classic); + wrap(pExaScr, pScreen, ModifyPixmapHeader, exaModifyPixmapHeader_classic); + pExaScr->do_migration = exaDoMigration_classic; + pExaScr->pixmap_has_gpu_copy = exaPixmapHasGpuCopy_classic; + pExaScr->do_move_in_pixmap = exaMoveInPixmap_classic; + pExaScr->do_move_out_pixmap = exaMoveOutPixmap_classic; + pExaScr->prepare_access_reg = exaPrepareAccessReg_classic; + } + if (!(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) { + LogMessage(X_INFO, "EXA(%d): Offscreen pixmap area of %lu bytes\n", + pScreen->myNum, + pExaScr->info->memorySize - pExaScr->info->offScreenBase); + } else { + LogMessage(X_INFO, "EXA(%d): Driver allocated offscreen pixmaps\n", + pScreen->myNum); + + } + } + else + LogMessage(X_INFO, "EXA(%d): No offscreen pixmaps\n", pScreen->myNum); + + if (!(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) { + DBG_PIXMAP(("============== %ld < %ld\n", pExaScr->info->offScreenBase, + pExaScr->info->memorySize)); + if (pExaScr->info->offScreenBase < pExaScr->info->memorySize) { + if (!exaOffscreenInit (pScreen)) { + LogMessage(X_WARNING, "EXA(%d): Offscreen pixmap setup failed\n", + pScreen->myNum); + return FALSE; + } + } + } + + if (ps->Glyphs == exaGlyphs) + exaGlyphsInit(pScreen); + + LogMessage(X_INFO, "EXA(%d): Driver registered support for the following" + " operations:\n", pScreen->myNum); + assert(pScreenInfo->PrepareSolid != NULL); + LogMessage(X_INFO, " Solid\n"); + assert(pScreenInfo->PrepareCopy != NULL); + LogMessage(X_INFO, " Copy\n"); + if (pScreenInfo->PrepareComposite != NULL) { + LogMessage(X_INFO, " Composite (RENDER acceleration)\n"); + } + if (pScreenInfo->UploadToScreen != NULL) { + LogMessage(X_INFO, " UploadToScreen\n"); + } + if (pScreenInfo->DownloadFromScreen != NULL) { + LogMessage(X_INFO, " DownloadFromScreen\n"); + } + + return TRUE; +} + +/** + * exaDriverFini tears down EXA on a given screen. + * + * @param pScreen screen being torn down. + */ +void +exaDriverFini (ScreenPtr pScreen) +{ + /*right now does nothing*/ +} + +/** + * exaMarkSync() should be called after any asynchronous drawing by the hardware. + * + * @param pScreen screen which drawing occurred on + * + * exaMarkSync() sets a flag to indicate that some asynchronous drawing has + * happened and a WaitSync() will be necessary before relying on the contents of + * offscreen memory from the CPU's perspective. It also calls an optional + * driver MarkSync() callback, the return value of which may be used to do partial + * synchronization with the hardware in the future. + */ +void exaMarkSync(ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + + pExaScr->info->needsSync = TRUE; + if (pExaScr->info->MarkSync != NULL) { + pExaScr->info->lastMarker = (*pExaScr->info->MarkSync)(pScreen); + } +} + +/** + * exaWaitSync() ensures that all drawing has been completed. + * + * @param pScreen screen being synchronized. + * + * Calls down into the driver to ensure that all previous drawing has completed. + * It should always be called before relying on the framebuffer contents + * reflecting previous drawing, from a CPU perspective. + */ +void exaWaitSync(ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + + if (pExaScr->info->needsSync && !pExaScr->swappedOut) { + (*pExaScr->info->WaitMarker)(pScreen, pExaScr->info->lastMarker); + pExaScr->info->needsSync = FALSE; + } +} + +/** + * Performs migration of the pixmaps according to the operation information + * provided in pixmaps and can_accel and the migration scheme chosen in the + * config file. + */ +void +exaDoMigration (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel) +{ + ScreenPtr pScreen = pixmaps[0].pPix->drawable.pScreen; + ExaScreenPriv(pScreen); + + if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) + return; + + if (pExaScr->do_migration) + (*pExaScr->do_migration)(pixmaps, npixmaps, can_accel); +} + +void +exaMoveInPixmap (PixmapPtr pPixmap) +{ + ScreenPtr pScreen = pPixmap->drawable.pScreen; + ExaScreenPriv(pScreen); + + if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) + return; + + if (pExaScr->do_move_in_pixmap) + (*pExaScr->do_move_in_pixmap)(pPixmap); +} + +void +exaMoveOutPixmap (PixmapPtr pPixmap) +{ + ScreenPtr pScreen = pPixmap->drawable.pScreen; + ExaScreenPriv(pScreen); + + if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) + return; + + if (pExaScr->do_move_out_pixmap) + (*pExaScr->do_move_out_pixmap)(pPixmap); +} diff --git a/xorg-server/exa/exa_accel.c b/xorg-server/exa/exa_accel.c index 4823398b5..47f955da5 100644 --- a/xorg-server/exa/exa_accel.c +++ b/xorg-server/exa/exa_accel.c @@ -394,7 +394,7 @@ exaHWCopyNtoN (DrawablePtr pSrcDrawable, exaGetDrawableDeltas (pSrcDrawable, pSrcPixmap, &src_off_x, &src_off_y); exaGetDrawableDeltas (pDstDrawable, pDstPixmap, &dst_off_x, &dst_off_y); - rects = xalloc(nbox * sizeof(xRectangle)); + rects = malloc(nbox * sizeof(xRectangle)); if (rects) { int i; @@ -417,7 +417,7 @@ exaHWCopyNtoN (DrawablePtr pSrcDrawable, ordering = CT_UNSORTED; srcregion = RECTS_TO_REGION(pScreen, nbox, rects, ordering); - xfree(rects); + free(rects); if (!pGC || !exaGCReadsDestination(pDstDrawable, pGC->planemask, pGC->fillStyle, pGC->alu, @@ -626,7 +626,7 @@ exaPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, return; } - prect = xalloc(sizeof(xRectangle) * npt); + prect = malloc(sizeof(xRectangle) * npt); for (i = 0; i < npt; i++) { prect[i].x = ppt[i].x; prect[i].y = ppt[i].y; @@ -638,7 +638,7 @@ exaPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, prect[i].height = 1; } pGC->ops->PolyFillRect(pDrawable, pGC, npt, prect); - xfree(prect); + free(prect); } /** @@ -667,7 +667,7 @@ exaPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, return; } - prect = xalloc(sizeof(xRectangle) * (npt - 1)); + prect = malloc(sizeof(xRectangle) * (npt - 1)); x1 = ppt[0].x; y1 = ppt[0].y; /* If we have any non-horizontal/vertical, fall back. */ @@ -681,7 +681,7 @@ exaPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, } if (x1 != x2 && y1 != y2) { - xfree(prect); + free(prect); ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt); return; } @@ -705,7 +705,7 @@ exaPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, y1 = y2; } pGC->ops->PolyFillRect(pDrawable, pGC, npt - 1, prect); - xfree(prect); + free(prect); } /** @@ -737,7 +737,7 @@ exaPolySegment (DrawablePtr pDrawable, GCPtr pGC, int nseg, } } - prect = xalloc(sizeof(xRectangle) * nseg); + prect = malloc(sizeof(xRectangle) * nseg); for (i = 0; i < nseg; i++) { if (pSeg[i].x1 < pSeg[i].x2) { prect[i].x = pSeg[i].x1; @@ -763,7 +763,7 @@ exaPolySegment (DrawablePtr pDrawable, GCPtr pGC, int nseg, } } pGC->ops->PolyFillRect(pDrawable, pGC, nseg, prect); - xfree(prect); + free(prect); } static Bool exaFillRegionSolid (DrawablePtr pDrawable, RegionPtr pRegion, diff --git a/xorg-server/exa/exa_glyphs.c b/xorg-server/exa/exa_glyphs.c index fd14e9b87..36217dc59 100644 --- a/xorg-server/exa/exa_glyphs.c +++ b/xorg-server/exa/exa_glyphs.c @@ -1,867 +1,867 @@ -/* - * Copyright © 2008 Red Hat, Inc. - * Partly based on code Copyright © 2000 SuSE, Inc. - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Red Hat not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. Red Hat makes no representations about the - * suitability of this software for any purpose. It is provided "as is" - * without express or implied warranty. - * - * Red Hat DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Red Hat - * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of SuSE not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. SuSE makes no representations about the - * suitability of this software for any purpose. It is provided "as is" - * without express or implied warranty. - * - * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE - * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Author: Owen Taylor - * Based on code by: Keith Packard - */ - -#ifdef HAVE_DIX_CONFIG_H -#include -#endif - -#include - -#include "exa_priv.h" - -#include "mipict.h" - -#if DEBUG_GLYPH_CACHE -#define DBG_GLYPH_CACHE(a) ErrorF a -#else -#define DBG_GLYPH_CACHE(a) -#endif - -/* Width of the pixmaps we use for the caches; this should be less than - * max texture size of the driver; this may need to actually come from - * the driver. - */ -#define CACHE_PICTURE_WIDTH 1024 - -/* Maximum number of glyphs we buffer on the stack before flushing - * rendering to the mask or destination surface. - */ -#define GLYPH_BUFFER_SIZE 256 - -typedef struct { - PicturePtr mask; - ExaCompositeRectRec rects[GLYPH_BUFFER_SIZE]; - int count; -} ExaGlyphBuffer, *ExaGlyphBufferPtr; - -typedef enum { - ExaGlyphSuccess, /* Glyph added to render buffer */ - ExaGlyphFail, /* out of memory, etc */ - ExaGlyphNeedFlush, /* would evict a glyph already in the buffer */ -} ExaGlyphCacheResult; - -void -exaGlyphsInit(ScreenPtr pScreen) -{ - ExaScreenPriv(pScreen); - int i = 0; - - memset(pExaScr->glyphCaches, 0, sizeof(pExaScr->glyphCaches)); - - pExaScr->glyphCaches[i].format = PICT_a8; - pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 16; - i++; - pExaScr->glyphCaches[i].format = PICT_a8; - pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 32; - i++; - pExaScr->glyphCaches[i].format = PICT_a8r8g8b8; - pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 16; - i++; - pExaScr->glyphCaches[i].format = PICT_a8r8g8b8; - pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 32; - i++; - - assert(i == EXA_NUM_GLYPH_CACHES); - - for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { - pExaScr->glyphCaches[i].columns = CACHE_PICTURE_WIDTH / pExaScr->glyphCaches[i].glyphWidth; - pExaScr->glyphCaches[i].size = 256; - pExaScr->glyphCaches[i].hashSize = 557; - } -} - -static void -exaUnrealizeGlyphCaches(ScreenPtr pScreen, - unsigned int format) -{ - ExaScreenPriv(pScreen); - int i; - - for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { - ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; - - if (cache->format != format) - continue; - - if (cache->picture) { - FreePicture ((pointer) cache->picture, (XID) 0); - cache->picture = NULL; - } - - if (cache->hashEntries) { - xfree(cache->hashEntries); - cache->hashEntries = NULL; - } - - if (cache->glyphs) { - xfree(cache->glyphs); - cache->glyphs = NULL; - } - cache->glyphCount = 0; - } -} - -#define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0) - -/* All caches for a single format share a single pixmap for glyph storage, - * allowing mixing glyphs of different sizes without paying a penalty - * for switching between mask pixmaps. (Note that for a size of font - * right at the border between two sizes, we might be switching for almost - * every glyph.) - * - * This function allocates the storage pixmap, and then fills in the - * rest of the allocated structures for all caches with the given format. - */ -static Bool -exaRealizeGlyphCaches(ScreenPtr pScreen, - unsigned int format) -{ - ExaScreenPriv(pScreen); - - int depth = PIXMAN_FORMAT_DEPTH(format); - PictFormatPtr pPictFormat; - PixmapPtr pPixmap; - PicturePtr pPicture; - CARD32 component_alpha; - int height; - int i; - int error; - - pPictFormat = PictureMatchFormat(pScreen, depth, format); - if (!pPictFormat) - return FALSE; - - /* Compute the total vertical size needed for the format */ - - height = 0; - for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { - ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; - int rows; - - if (cache->format != format) - continue; - - cache->yOffset = height; - - rows = (cache->size + cache->columns - 1) / cache->columns; - height += rows * cache->glyphHeight; - } - - /* Now allocate the pixmap and picture */ - pPixmap = (*pScreen->CreatePixmap) (pScreen, - CACHE_PICTURE_WIDTH, - height, depth, 0); - if (!pPixmap) - return FALSE; - - component_alpha = NeedsComponent(pPictFormat->format); - pPicture = CreatePicture(0, &pPixmap->drawable, pPictFormat, - CPComponentAlpha, &component_alpha, serverClient, - &error); - - (*pScreen->DestroyPixmap) (pPixmap); /* picture holds a refcount */ - - if (!pPicture) - return FALSE; - - /* And store the picture in all the caches for the format */ - for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { - ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; - int j; - - if (cache->format != format) - continue; - - cache->picture = pPicture; - cache->picture->refcnt++; - cache->hashEntries = xalloc(sizeof(int) * cache->hashSize); - cache->glyphs = xalloc(sizeof(ExaCachedGlyphRec) * cache->size); - cache->glyphCount = 0; - - if (!cache->hashEntries || !cache->glyphs) - goto bail; - - for (j = 0; j < cache->hashSize; j++) - cache->hashEntries[j] = -1; - - cache->evictionPosition = rand() % cache->size; - } - - /* Each cache references the picture individually */ - FreePicture ((pointer) pPicture, (XID) 0); - return TRUE; - -bail: - exaUnrealizeGlyphCaches(pScreen, format); - return FALSE; -} - -void -exaGlyphsFini (ScreenPtr pScreen) -{ - ExaScreenPriv(pScreen); - int i; - - for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { - ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; - - if (cache->picture) - exaUnrealizeGlyphCaches(pScreen, cache->format); - } -} - -static int -exaGlyphCacheHashLookup(ExaGlyphCachePtr cache, - GlyphPtr pGlyph) -{ - int slot; - - slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize; - - while (TRUE) { /* hash table can never be full */ - int entryPos = cache->hashEntries[slot]; - if (entryPos == -1) - return -1; - - if (memcmp(pGlyph->sha1, cache->glyphs[entryPos].sha1, sizeof(pGlyph->sha1)) == 0){ - return entryPos; - } - - slot--; - if (slot < 0) - slot = cache->hashSize - 1; - } -} - -static void -exaGlyphCacheHashInsert(ExaGlyphCachePtr cache, - GlyphPtr pGlyph, - int pos) -{ - int slot; - - memcpy(cache->glyphs[pos].sha1, pGlyph->sha1, sizeof(pGlyph->sha1)); - - slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize; - - while (TRUE) { /* hash table can never be full */ - if (cache->hashEntries[slot] == -1) { - cache->hashEntries[slot] = pos; - return; - } - - slot--; - if (slot < 0) - slot = cache->hashSize - 1; - } -} - -static void -exaGlyphCacheHashRemove(ExaGlyphCachePtr cache, - int pos) -{ - int slot; - int emptiedSlot = -1; - - slot = (*(CARD32 *) cache->glyphs[pos].sha1) % cache->hashSize; - - while (TRUE) { /* hash table can never be full */ - int entryPos = cache->hashEntries[slot]; - - if (entryPos == -1) - return; - - if (entryPos == pos) { - cache->hashEntries[slot] = -1; - emptiedSlot = slot; - } else if (emptiedSlot != -1) { - /* See if we can move this entry into the emptied slot, we can't - * do that if if entry would have hashed between the current position - * and the emptied slot. (taking wrapping into account). Bad positions - * are: - * - * | XXXXXXXXXX | - * i j - * - * |XXX XXXX| - * j i - * - * i - slot, j - emptiedSlot - * - * (Knuth 6.4R) - */ - - int entrySlot = (*(CARD32 *) cache->glyphs[entryPos].sha1) % cache->hashSize; - - if (!((entrySlot >= slot && entrySlot < emptiedSlot) || - (emptiedSlot < slot && (entrySlot < emptiedSlot || entrySlot >= slot)))) - { - cache->hashEntries[emptiedSlot] = entryPos; - cache->hashEntries[slot] = -1; - emptiedSlot = slot; - } - } - - slot--; - if (slot < 0) - slot = cache->hashSize - 1; - } -} - -#define CACHE_X(pos) (((pos) % cache->columns) * cache->glyphWidth) -#define CACHE_Y(pos) (cache->yOffset + ((pos) / cache->columns) * cache->glyphHeight) - -/* The most efficient thing to way to upload the glyph to the screen - * is to use the UploadToScreen() driver hook; this allows us to - * pipeline glyph uploads and to avoid creating gpu backed pixmaps for - * glyphs that we'll never use again. - * - * If we can't do it with UploadToScreen (because the glyph has a gpu copy, - * etc), we fall back to CompositePicture. - * - * We need to damage the cache pixmap manually in either case because the damage - * layer unwrapped the picture screen before calling exaGlyphs. - */ -static void -exaGlyphCacheUploadGlyph(ScreenPtr pScreen, - ExaGlyphCachePtr cache, - int x, - int y, - GlyphPtr pGlyph) -{ - ExaScreenPriv(pScreen); - PicturePtr pGlyphPicture = GlyphPicture(pGlyph)[pScreen->myNum]; - PixmapPtr pGlyphPixmap = (PixmapPtr)pGlyphPicture->pDrawable; - ExaPixmapPriv(pGlyphPixmap); - PixmapPtr pCachePixmap = (PixmapPtr)cache->picture->pDrawable; - - if (!pExaScr->info->UploadToScreen || pExaScr->swappedOut || pExaPixmap->accel_blocked) - goto composite; - - /* If the glyph pixmap is already uploaded, no point in doing - * things this way */ - if (exaPixmapHasGpuCopy(pGlyphPixmap)) - goto composite; - - /* UploadToScreen only works if bpp match */ - if (pGlyphPixmap->drawable.bitsPerPixel != pCachePixmap->drawable.bitsPerPixel) - goto composite; - - if (pExaScr->do_migration) { - ExaMigrationRec pixmaps[1]; - - /* cache pixmap must have a gpu copy. */ - pixmaps[0].as_dst = TRUE; - pixmaps[0].as_src = FALSE; - pixmaps[0].pPix = pCachePixmap; - pixmaps[0].pReg = NULL; - exaDoMigration (pixmaps, 1, TRUE); - } - - if (!exaPixmapHasGpuCopy(pCachePixmap)) - goto composite; - - /* x,y are in pixmap coordinates, no need for cache{X,Y}off */ - if (pExaScr->info->UploadToScreen(pCachePixmap, - x, - y, - pGlyph->info.width, - pGlyph->info.height, - (char *)pExaPixmap->sys_ptr, - pExaPixmap->sys_pitch)) - goto damage; - -composite: - CompositePicture (PictOpSrc, - pGlyphPicture, - None, - cache->picture, - 0, 0, - 0, 0, - x, - y, - pGlyph->info.width, - pGlyph->info.height); - -damage: - /* The cache pixmap isn't a window, so no need to offset coordinates. */ - exaPixmapDirty (pCachePixmap, - x, - y, - x + cache->glyphWidth, - y + cache->glyphHeight); -} - -static ExaGlyphCacheResult -exaGlyphCacheBufferGlyph(ScreenPtr pScreen, - ExaGlyphCachePtr cache, - ExaGlyphBufferPtr buffer, - GlyphPtr pGlyph, - PicturePtr pSrc, - PicturePtr pDst, - INT16 xSrc, - INT16 ySrc, - INT16 xMask, - INT16 yMask, - INT16 xDst, - INT16 yDst) -{ - ExaCompositeRectPtr rect; - int pos; - int x, y; - - if (buffer->mask && buffer->mask != cache->picture) - return ExaGlyphNeedFlush; - - if (!cache->picture) { - if (!exaRealizeGlyphCaches(pScreen, cache->format)) - return ExaGlyphFail; - } - - DBG_GLYPH_CACHE(("(%d,%d,%s): buffering glyph %lx\n", - cache->glyphWidth, cache->glyphHeight, cache->format == PICT_a8 ? "A" : "ARGB", - (long)*(CARD32 *) pGlyph->sha1)); - - pos = exaGlyphCacheHashLookup(cache, pGlyph); - if (pos != -1) { - DBG_GLYPH_CACHE((" found existing glyph at %d\n", pos)); - x = CACHE_X(pos); - y = CACHE_Y(pos); - } else { - if (cache->glyphCount < cache->size) { - /* Space remaining; we fill from the start */ - pos = cache->glyphCount; - x = CACHE_X(pos); - y = CACHE_Y(pos); - cache->glyphCount++; - DBG_GLYPH_CACHE((" storing glyph in free space at %d\n", pos)); - - exaGlyphCacheHashInsert(cache, pGlyph, pos); - - } else { - /* Need to evict an entry. We have to see if any glyphs - * already in the output buffer were at this position in - * the cache - */ - pos = cache->evictionPosition; - x = CACHE_X(pos); - y = CACHE_Y(pos); - DBG_GLYPH_CACHE((" evicting glyph at %d\n", pos)); - if (buffer->count) { - int i; - - for (i = 0; i < buffer->count; i++) { - if (pSrc ? - (buffer->rects[i].xMask == x && buffer->rects[i].yMask == y) : - (buffer->rects[i].xSrc == x && buffer->rects[i].ySrc == y)) { - DBG_GLYPH_CACHE((" must flush buffer\n")); - return ExaGlyphNeedFlush; - } - } - } - - /* OK, we're all set, swap in the new glyph */ - exaGlyphCacheHashRemove(cache, pos); - exaGlyphCacheHashInsert(cache, pGlyph, pos); - - /* And pick a new eviction position */ - cache->evictionPosition = rand() % cache->size; - } - - exaGlyphCacheUploadGlyph(pScreen, cache, x, y, pGlyph); - } - - buffer->mask = cache->picture; - - rect = &buffer->rects[buffer->count]; - - if (pSrc) - { - rect->xSrc = xSrc; - rect->ySrc = ySrc; - rect->xMask = x; - rect->yMask = y; - } - else - { - rect->xSrc = x; - rect->ySrc = y; - rect->xMask = 0; - rect->yMask = 0; - } - - rect->pDst = pDst; - rect->xDst = xDst; - rect->yDst = yDst; - rect->width = pGlyph->info.width; - rect->height = pGlyph->info.height; - - buffer->count++; - - return ExaGlyphSuccess; -} - -#undef CACHE_X -#undef CACHE_Y - -static ExaGlyphCacheResult -exaBufferGlyph(ScreenPtr pScreen, - ExaGlyphBufferPtr buffer, - GlyphPtr pGlyph, - PicturePtr pSrc, - PicturePtr pDst, - INT16 xSrc, - INT16 ySrc, - INT16 xMask, - INT16 yMask, - INT16 xDst, - INT16 yDst) -{ - ExaScreenPriv(pScreen); - unsigned int format = (GlyphPicture(pGlyph)[pScreen->myNum])->format; - int width = pGlyph->info.width; - int height = pGlyph->info.height; - ExaCompositeRectPtr rect; - PicturePtr mask; - int i; - - if (buffer->count == GLYPH_BUFFER_SIZE) - return ExaGlyphNeedFlush; - - if (PICT_FORMAT_BPP(format) == 1) - format = PICT_a8; - - for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { - ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; - - if (format == cache->format && - width <= cache->glyphWidth && - height <= cache->glyphHeight) { - ExaGlyphCacheResult result = exaGlyphCacheBufferGlyph(pScreen, - &pExaScr->glyphCaches[i], - buffer, - pGlyph, - pSrc, - pDst, - xSrc, ySrc, - xMask, yMask, - xDst, yDst); - switch (result) { - case ExaGlyphFail: - break; - case ExaGlyphSuccess: - case ExaGlyphNeedFlush: - return result; - } - } - } - - /* Couldn't find the glyph in the cache, use the glyph picture directly */ - - mask = GlyphPicture(pGlyph)[pScreen->myNum]; - if (buffer->mask && buffer->mask != mask) - return ExaGlyphNeedFlush; - - buffer->mask = mask; - - rect = &buffer->rects[buffer->count]; - rect->xSrc = xSrc; - rect->ySrc = ySrc; - rect->xMask = xMask; - rect->yMask = yMask; - rect->xDst = xDst; - rect->yDst = yDst; - rect->width = width; - rect->height = height; - - buffer->count++; - - return ExaGlyphSuccess; -} - -static void -exaGlyphsToMask(PicturePtr pMask, - ExaGlyphBufferPtr buffer) -{ - exaCompositeRects(PictOpAdd, buffer->mask, NULL, pMask, - buffer->count, buffer->rects); - - buffer->count = 0; - buffer->mask = NULL; -} - -static void -exaGlyphsToDst(PicturePtr pSrc, - PicturePtr pDst, - ExaGlyphBufferPtr buffer) -{ - exaCompositeRects(PictOpOver, pSrc, buffer->mask, pDst, buffer->count, - buffer->rects); - - buffer->count = 0; - buffer->mask = NULL; -} - -/* Cut and paste from render/glyph.c - probably should export it instead */ -static void -GlyphExtents (int nlist, - GlyphListPtr list, - GlyphPtr *glyphs, - BoxPtr extents) -{ - int x1, x2, y1, y2; - int n; - GlyphPtr glyph; - int x, y; - - x = 0; - y = 0; - extents->x1 = MAXSHORT; - extents->x2 = MINSHORT; - extents->y1 = MAXSHORT; - extents->y2 = MINSHORT; - while (nlist--) - { - x += list->xOff; - y += list->yOff; - n = list->len; - list++; - while (n--) - { - glyph = *glyphs++; - x1 = x - glyph->info.x; - if (x1 < MINSHORT) - x1 = MINSHORT; - y1 = y - glyph->info.y; - if (y1 < MINSHORT) - y1 = MINSHORT; - x2 = x1 + glyph->info.width; - if (x2 > MAXSHORT) - x2 = MAXSHORT; - y2 = y1 + glyph->info.height; - if (y2 > MAXSHORT) - y2 = MAXSHORT; - if (x1 < extents->x1) - extents->x1 = x1; - if (x2 > extents->x2) - extents->x2 = x2; - if (y1 < extents->y1) - extents->y1 = y1; - if (y2 > extents->y2) - extents->y2 = y2; - x += glyph->info.xOff; - y += glyph->info.yOff; - } - } -} - -void -exaGlyphs (CARD8 op, - PicturePtr pSrc, - PicturePtr pDst, - PictFormatPtr maskFormat, - INT16 xSrc, - INT16 ySrc, - int nlist, - GlyphListPtr list, - GlyphPtr *glyphs) -{ - PixmapPtr pMaskPixmap = 0; - PicturePtr pMask = NULL; - ScreenPtr pScreen = pDst->pDrawable->pScreen; - int width = 0, height = 0; - int x, y; - int first_xOff = list->xOff, first_yOff = list->yOff; - int n; - GlyphPtr glyph; - int error; - BoxRec extents = {0, 0, 0, 0}; - CARD32 component_alpha; - ExaGlyphBuffer buffer; - - if (maskFormat) - { - ExaScreenPriv(pScreen); - GCPtr pGC; - xRectangle rect; - - GlyphExtents (nlist, list, glyphs, &extents); - - if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) - return; - width = extents.x2 - extents.x1; - height = extents.y2 - extents.y1; - - if (maskFormat->depth == 1) { - PictFormatPtr a8Format = PictureMatchFormat (pScreen, 8, PICT_a8); - - if (a8Format) - maskFormat = a8Format; - } - - pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, - maskFormat->depth, - CREATE_PIXMAP_USAGE_SCRATCH); - if (!pMaskPixmap) - return; - component_alpha = NeedsComponent(maskFormat->format); - pMask = CreatePicture (0, &pMaskPixmap->drawable, - maskFormat, CPComponentAlpha, &component_alpha, - serverClient, &error); - if (!pMask || - (!component_alpha && pExaScr->info->CheckComposite && - !(*pExaScr->info->CheckComposite) (PictOpAdd, pSrc, NULL, pMask))) - { - PictFormatPtr argbFormat; - - (*pScreen->DestroyPixmap) (pMaskPixmap); - - if (!pMask) - return; - - /* The driver can't seem to composite to a8, let's try argb (but - * without component-alpha) */ - FreePicture ((pointer) pMask, (XID) 0); - - argbFormat = PictureMatchFormat (pScreen, 32, PICT_a8r8g8b8); - - if (argbFormat) - maskFormat = argbFormat; - - pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, - maskFormat->depth, - CREATE_PIXMAP_USAGE_SCRATCH); - if (!pMaskPixmap) - return; - - pMask = CreatePicture (0, &pMaskPixmap->drawable, maskFormat, 0, 0, - serverClient, &error); - if (!pMask) { - (*pScreen->DestroyPixmap) (pMaskPixmap); - return; - } - } - pGC = GetScratchGC (pMaskPixmap->drawable.depth, pScreen); - ValidateGC (&pMaskPixmap->drawable, pGC); - rect.x = 0; - rect.y = 0; - rect.width = width; - rect.height = height; - (*pGC->ops->PolyFillRect) (&pMaskPixmap->drawable, pGC, 1, &rect); - FreeScratchGC (pGC); - x = -extents.x1; - y = -extents.y1; - } - else - { - x = 0; - y = 0; - } - buffer.count = 0; - buffer.mask = NULL; - while (nlist--) - { - x += list->xOff; - y += list->yOff; - n = list->len; - while (n--) - { - glyph = *glyphs++; - - if (glyph->info.width > 0 && glyph->info.height > 0) - { - /* pGlyph->info.{x,y} compensate for empty space in the glyph. */ - if (maskFormat) - { - if (exaBufferGlyph(pScreen, &buffer, glyph, NULL, pMask, - 0, 0, 0, 0, x - glyph->info.x, y - glyph->info.y) == ExaGlyphNeedFlush) - { - exaGlyphsToMask(pMask, &buffer); - exaBufferGlyph(pScreen, &buffer, glyph, NULL, pMask, - 0, 0, 0, 0, x - glyph->info.x, y - glyph->info.y); - } - } - else - { - if (exaBufferGlyph(pScreen, &buffer, glyph, pSrc, pDst, - xSrc + (x - glyph->info.x) - first_xOff, ySrc + (y - glyph->info.y) - first_yOff, - 0, 0, x - glyph->info.x, y - glyph->info.y) - == ExaGlyphNeedFlush) - { - exaGlyphsToDst(pSrc, pDst, &buffer); - exaBufferGlyph(pScreen, &buffer, glyph, pSrc, pDst, - xSrc + (x - glyph->info.x) - first_xOff, ySrc + (y - glyph->info.y) - first_yOff, - 0, 0, x - glyph->info.x, y - glyph->info.y); - } - } - } - - x += glyph->info.xOff; - y += glyph->info.yOff; - } - list++; - } - - if (buffer.count) { - if (maskFormat) - exaGlyphsToMask(pMask, &buffer); - else - exaGlyphsToDst(pSrc, pDst, &buffer); - } - - if (maskFormat) - { - x = extents.x1; - y = extents.y1; - CompositePicture (op, - pSrc, - pMask, - pDst, - xSrc + x - first_xOff, - ySrc + y - first_yOff, - 0, 0, - x, y, - width, height); - FreePicture ((pointer) pMask, (XID) 0); - (*pScreen->DestroyPixmap) (pMaskPixmap); - } -} +/* + * Copyright © 2008 Red Hat, Inc. + * Partly based on code Copyright © 2000 SuSE, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * Red Hat DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Red Hat + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Owen Taylor + * Based on code by: Keith Packard + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include + +#include "exa_priv.h" + +#include "mipict.h" + +#if DEBUG_GLYPH_CACHE +#define DBG_GLYPH_CACHE(a) ErrorF a +#else +#define DBG_GLYPH_CACHE(a) +#endif + +/* Width of the pixmaps we use for the caches; this should be less than + * max texture size of the driver; this may need to actually come from + * the driver. + */ +#define CACHE_PICTURE_WIDTH 1024 + +/* Maximum number of glyphs we buffer on the stack before flushing + * rendering to the mask or destination surface. + */ +#define GLYPH_BUFFER_SIZE 256 + +typedef struct { + PicturePtr mask; + ExaCompositeRectRec rects[GLYPH_BUFFER_SIZE]; + int count; +} ExaGlyphBuffer, *ExaGlyphBufferPtr; + +typedef enum { + ExaGlyphSuccess, /* Glyph added to render buffer */ + ExaGlyphFail, /* out of memory, etc */ + ExaGlyphNeedFlush, /* would evict a glyph already in the buffer */ +} ExaGlyphCacheResult; + +void +exaGlyphsInit(ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + int i = 0; + + memset(pExaScr->glyphCaches, 0, sizeof(pExaScr->glyphCaches)); + + pExaScr->glyphCaches[i].format = PICT_a8; + pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 16; + i++; + pExaScr->glyphCaches[i].format = PICT_a8; + pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 32; + i++; + pExaScr->glyphCaches[i].format = PICT_a8r8g8b8; + pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 16; + i++; + pExaScr->glyphCaches[i].format = PICT_a8r8g8b8; + pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 32; + i++; + + assert(i == EXA_NUM_GLYPH_CACHES); + + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + pExaScr->glyphCaches[i].columns = CACHE_PICTURE_WIDTH / pExaScr->glyphCaches[i].glyphWidth; + pExaScr->glyphCaches[i].size = 256; + pExaScr->glyphCaches[i].hashSize = 557; + } +} + +static void +exaUnrealizeGlyphCaches(ScreenPtr pScreen, + unsigned int format) +{ + ExaScreenPriv(pScreen); + int i; + + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; + + if (cache->format != format) + continue; + + if (cache->picture) { + FreePicture ((pointer) cache->picture, (XID) 0); + cache->picture = NULL; + } + + if (cache->hashEntries) { + free(cache->hashEntries); + cache->hashEntries = NULL; + } + + if (cache->glyphs) { + free(cache->glyphs); + cache->glyphs = NULL; + } + cache->glyphCount = 0; + } +} + +#define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0) + +/* All caches for a single format share a single pixmap for glyph storage, + * allowing mixing glyphs of different sizes without paying a penalty + * for switching between mask pixmaps. (Note that for a size of font + * right at the border between two sizes, we might be switching for almost + * every glyph.) + * + * This function allocates the storage pixmap, and then fills in the + * rest of the allocated structures for all caches with the given format. + */ +static Bool +exaRealizeGlyphCaches(ScreenPtr pScreen, + unsigned int format) +{ + ExaScreenPriv(pScreen); + + int depth = PIXMAN_FORMAT_DEPTH(format); + PictFormatPtr pPictFormat; + PixmapPtr pPixmap; + PicturePtr pPicture; + CARD32 component_alpha; + int height; + int i; + int error; + + pPictFormat = PictureMatchFormat(pScreen, depth, format); + if (!pPictFormat) + return FALSE; + + /* Compute the total vertical size needed for the format */ + + height = 0; + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; + int rows; + + if (cache->format != format) + continue; + + cache->yOffset = height; + + rows = (cache->size + cache->columns - 1) / cache->columns; + height += rows * cache->glyphHeight; + } + + /* Now allocate the pixmap and picture */ + pPixmap = (*pScreen->CreatePixmap) (pScreen, + CACHE_PICTURE_WIDTH, + height, depth, 0); + if (!pPixmap) + return FALSE; + + component_alpha = NeedsComponent(pPictFormat->format); + pPicture = CreatePicture(0, &pPixmap->drawable, pPictFormat, + CPComponentAlpha, &component_alpha, serverClient, + &error); + + (*pScreen->DestroyPixmap) (pPixmap); /* picture holds a refcount */ + + if (!pPicture) + return FALSE; + + /* And store the picture in all the caches for the format */ + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; + int j; + + if (cache->format != format) + continue; + + cache->picture = pPicture; + cache->picture->refcnt++; + cache->hashEntries = malloc(sizeof(int) * cache->hashSize); + cache->glyphs = malloc(sizeof(ExaCachedGlyphRec) * cache->size); + cache->glyphCount = 0; + + if (!cache->hashEntries || !cache->glyphs) + goto bail; + + for (j = 0; j < cache->hashSize; j++) + cache->hashEntries[j] = -1; + + cache->evictionPosition = rand() % cache->size; + } + + /* Each cache references the picture individually */ + FreePicture ((pointer) pPicture, (XID) 0); + return TRUE; + +bail: + exaUnrealizeGlyphCaches(pScreen, format); + return FALSE; +} + +void +exaGlyphsFini (ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + int i; + + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; + + if (cache->picture) + exaUnrealizeGlyphCaches(pScreen, cache->format); + } +} + +static int +exaGlyphCacheHashLookup(ExaGlyphCachePtr cache, + GlyphPtr pGlyph) +{ + int slot; + + slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize; + + while (TRUE) { /* hash table can never be full */ + int entryPos = cache->hashEntries[slot]; + if (entryPos == -1) + return -1; + + if (memcmp(pGlyph->sha1, cache->glyphs[entryPos].sha1, sizeof(pGlyph->sha1)) == 0){ + return entryPos; + } + + slot--; + if (slot < 0) + slot = cache->hashSize - 1; + } +} + +static void +exaGlyphCacheHashInsert(ExaGlyphCachePtr cache, + GlyphPtr pGlyph, + int pos) +{ + int slot; + + memcpy(cache->glyphs[pos].sha1, pGlyph->sha1, sizeof(pGlyph->sha1)); + + slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize; + + while (TRUE) { /* hash table can never be full */ + if (cache->hashEntries[slot] == -1) { + cache->hashEntries[slot] = pos; + return; + } + + slot--; + if (slot < 0) + slot = cache->hashSize - 1; + } +} + +static void +exaGlyphCacheHashRemove(ExaGlyphCachePtr cache, + int pos) +{ + int slot; + int emptiedSlot = -1; + + slot = (*(CARD32 *) cache->glyphs[pos].sha1) % cache->hashSize; + + while (TRUE) { /* hash table can never be full */ + int entryPos = cache->hashEntries[slot]; + + if (entryPos == -1) + return; + + if (entryPos == pos) { + cache->hashEntries[slot] = -1; + emptiedSlot = slot; + } else if (emptiedSlot != -1) { + /* See if we can move this entry into the emptied slot, we can't + * do that if if entry would have hashed between the current position + * and the emptied slot. (taking wrapping into account). Bad positions + * are: + * + * | XXXXXXXXXX | + * i j + * + * |XXX XXXX| + * j i + * + * i - slot, j - emptiedSlot + * + * (Knuth 6.4R) + */ + + int entrySlot = (*(CARD32 *) cache->glyphs[entryPos].sha1) % cache->hashSize; + + if (!((entrySlot >= slot && entrySlot < emptiedSlot) || + (emptiedSlot < slot && (entrySlot < emptiedSlot || entrySlot >= slot)))) + { + cache->hashEntries[emptiedSlot] = entryPos; + cache->hashEntries[slot] = -1; + emptiedSlot = slot; + } + } + + slot--; + if (slot < 0) + slot = cache->hashSize - 1; + } +} + +#define CACHE_X(pos) (((pos) % cache->columns) * cache->glyphWidth) +#define CACHE_Y(pos) (cache->yOffset + ((pos) / cache->columns) * cache->glyphHeight) + +/* The most efficient thing to way to upload the glyph to the screen + * is to use the UploadToScreen() driver hook; this allows us to + * pipeline glyph uploads and to avoid creating gpu backed pixmaps for + * glyphs that we'll never use again. + * + * If we can't do it with UploadToScreen (because the glyph has a gpu copy, + * etc), we fall back to CompositePicture. + * + * We need to damage the cache pixmap manually in either case because the damage + * layer unwrapped the picture screen before calling exaGlyphs. + */ +static void +exaGlyphCacheUploadGlyph(ScreenPtr pScreen, + ExaGlyphCachePtr cache, + int x, + int y, + GlyphPtr pGlyph) +{ + ExaScreenPriv(pScreen); + PicturePtr pGlyphPicture = GlyphPicture(pGlyph)[pScreen->myNum]; + PixmapPtr pGlyphPixmap = (PixmapPtr)pGlyphPicture->pDrawable; + ExaPixmapPriv(pGlyphPixmap); + PixmapPtr pCachePixmap = (PixmapPtr)cache->picture->pDrawable; + + if (!pExaScr->info->UploadToScreen || pExaScr->swappedOut || pExaPixmap->accel_blocked) + goto composite; + + /* If the glyph pixmap is already uploaded, no point in doing + * things this way */ + if (exaPixmapHasGpuCopy(pGlyphPixmap)) + goto composite; + + /* UploadToScreen only works if bpp match */ + if (pGlyphPixmap->drawable.bitsPerPixel != pCachePixmap->drawable.bitsPerPixel) + goto composite; + + if (pExaScr->do_migration) { + ExaMigrationRec pixmaps[1]; + + /* cache pixmap must have a gpu copy. */ + pixmaps[0].as_dst = TRUE; + pixmaps[0].as_src = FALSE; + pixmaps[0].pPix = pCachePixmap; + pixmaps[0].pReg = NULL; + exaDoMigration (pixmaps, 1, TRUE); + } + + if (!exaPixmapHasGpuCopy(pCachePixmap)) + goto composite; + + /* x,y are in pixmap coordinates, no need for cache{X,Y}off */ + if (pExaScr->info->UploadToScreen(pCachePixmap, + x, + y, + pGlyph->info.width, + pGlyph->info.height, + (char *)pExaPixmap->sys_ptr, + pExaPixmap->sys_pitch)) + goto damage; + +composite: + CompositePicture (PictOpSrc, + pGlyphPicture, + None, + cache->picture, + 0, 0, + 0, 0, + x, + y, + pGlyph->info.width, + pGlyph->info.height); + +damage: + /* The cache pixmap isn't a window, so no need to offset coordinates. */ + exaPixmapDirty (pCachePixmap, + x, + y, + x + cache->glyphWidth, + y + cache->glyphHeight); +} + +static ExaGlyphCacheResult +exaGlyphCacheBufferGlyph(ScreenPtr pScreen, + ExaGlyphCachePtr cache, + ExaGlyphBufferPtr buffer, + GlyphPtr pGlyph, + PicturePtr pSrc, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst) +{ + ExaCompositeRectPtr rect; + int pos; + int x, y; + + if (buffer->mask && buffer->mask != cache->picture) + return ExaGlyphNeedFlush; + + if (!cache->picture) { + if (!exaRealizeGlyphCaches(pScreen, cache->format)) + return ExaGlyphFail; + } + + DBG_GLYPH_CACHE(("(%d,%d,%s): buffering glyph %lx\n", + cache->glyphWidth, cache->glyphHeight, cache->format == PICT_a8 ? "A" : "ARGB", + (long)*(CARD32 *) pGlyph->sha1)); + + pos = exaGlyphCacheHashLookup(cache, pGlyph); + if (pos != -1) { + DBG_GLYPH_CACHE((" found existing glyph at %d\n", pos)); + x = CACHE_X(pos); + y = CACHE_Y(pos); + } else { + if (cache->glyphCount < cache->size) { + /* Space remaining; we fill from the start */ + pos = cache->glyphCount; + x = CACHE_X(pos); + y = CACHE_Y(pos); + cache->glyphCount++; + DBG_GLYPH_CACHE((" storing glyph in free space at %d\n", pos)); + + exaGlyphCacheHashInsert(cache, pGlyph, pos); + + } else { + /* Need to evict an entry. We have to see if any glyphs + * already in the output buffer were at this position in + * the cache + */ + pos = cache->evictionPosition; + x = CACHE_X(pos); + y = CACHE_Y(pos); + DBG_GLYPH_CACHE((" evicting glyph at %d\n", pos)); + if (buffer->count) { + int i; + + for (i = 0; i < buffer->count; i++) { + if (pSrc ? + (buffer->rects[i].xMask == x && buffer->rects[i].yMask == y) : + (buffer->rects[i].xSrc == x && buffer->rects[i].ySrc == y)) { + DBG_GLYPH_CACHE((" must flush buffer\n")); + return ExaGlyphNeedFlush; + } + } + } + + /* OK, we're all set, swap in the new glyph */ + exaGlyphCacheHashRemove(cache, pos); + exaGlyphCacheHashInsert(cache, pGlyph, pos); + + /* And pick a new eviction position */ + cache->evictionPosition = rand() % cache->size; + } + + exaGlyphCacheUploadGlyph(pScreen, cache, x, y, pGlyph); + } + + buffer->mask = cache->picture; + + rect = &buffer->rects[buffer->count]; + + if (pSrc) + { + rect->xSrc = xSrc; + rect->ySrc = ySrc; + rect->xMask = x; + rect->yMask = y; + } + else + { + rect->xSrc = x; + rect->ySrc = y; + rect->xMask = 0; + rect->yMask = 0; + } + + rect->pDst = pDst; + rect->xDst = xDst; + rect->yDst = yDst; + rect->width = pGlyph->info.width; + rect->height = pGlyph->info.height; + + buffer->count++; + + return ExaGlyphSuccess; +} + +#undef CACHE_X +#undef CACHE_Y + +static ExaGlyphCacheResult +exaBufferGlyph(ScreenPtr pScreen, + ExaGlyphBufferPtr buffer, + GlyphPtr pGlyph, + PicturePtr pSrc, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst) +{ + ExaScreenPriv(pScreen); + unsigned int format = (GlyphPicture(pGlyph)[pScreen->myNum])->format; + int width = pGlyph->info.width; + int height = pGlyph->info.height; + ExaCompositeRectPtr rect; + PicturePtr mask; + int i; + + if (buffer->count == GLYPH_BUFFER_SIZE) + return ExaGlyphNeedFlush; + + if (PICT_FORMAT_BPP(format) == 1) + format = PICT_a8; + + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; + + if (format == cache->format && + width <= cache->glyphWidth && + height <= cache->glyphHeight) { + ExaGlyphCacheResult result = exaGlyphCacheBufferGlyph(pScreen, + &pExaScr->glyphCaches[i], + buffer, + pGlyph, + pSrc, + pDst, + xSrc, ySrc, + xMask, yMask, + xDst, yDst); + switch (result) { + case ExaGlyphFail: + break; + case ExaGlyphSuccess: + case ExaGlyphNeedFlush: + return result; + } + } + } + + /* Couldn't find the glyph in the cache, use the glyph picture directly */ + + mask = GlyphPicture(pGlyph)[pScreen->myNum]; + if (buffer->mask && buffer->mask != mask) + return ExaGlyphNeedFlush; + + buffer->mask = mask; + + rect = &buffer->rects[buffer->count]; + rect->xSrc = xSrc; + rect->ySrc = ySrc; + rect->xMask = xMask; + rect->yMask = yMask; + rect->xDst = xDst; + rect->yDst = yDst; + rect->width = width; + rect->height = height; + + buffer->count++; + + return ExaGlyphSuccess; +} + +static void +exaGlyphsToMask(PicturePtr pMask, + ExaGlyphBufferPtr buffer) +{ + exaCompositeRects(PictOpAdd, buffer->mask, NULL, pMask, + buffer->count, buffer->rects); + + buffer->count = 0; + buffer->mask = NULL; +} + +static void +exaGlyphsToDst(PicturePtr pSrc, + PicturePtr pDst, + ExaGlyphBufferPtr buffer) +{ + exaCompositeRects(PictOpOver, pSrc, buffer->mask, pDst, buffer->count, + buffer->rects); + + buffer->count = 0; + buffer->mask = NULL; +} + +/* Cut and paste from render/glyph.c - probably should export it instead */ +static void +GlyphExtents (int nlist, + GlyphListPtr list, + GlyphPtr *glyphs, + BoxPtr extents) +{ + int x1, x2, y1, y2; + int n; + GlyphPtr glyph; + int x, y; + + x = 0; + y = 0; + extents->x1 = MAXSHORT; + extents->x2 = MINSHORT; + extents->y1 = MAXSHORT; + extents->y2 = MINSHORT; + while (nlist--) + { + x += list->xOff; + y += list->yOff; + n = list->len; + list++; + while (n--) + { + glyph = *glyphs++; + x1 = x - glyph->info.x; + if (x1 < MINSHORT) + x1 = MINSHORT; + y1 = y - glyph->info.y; + if (y1 < MINSHORT) + y1 = MINSHORT; + x2 = x1 + glyph->info.width; + if (x2 > MAXSHORT) + x2 = MAXSHORT; + y2 = y1 + glyph->info.height; + if (y2 > MAXSHORT) + y2 = MAXSHORT; + if (x1 < extents->x1) + extents->x1 = x1; + if (x2 > extents->x2) + extents->x2 = x2; + if (y1 < extents->y1) + extents->y1 = y1; + if (y2 > extents->y2) + extents->y2 = y2; + x += glyph->info.xOff; + y += glyph->info.yOff; + } + } +} + +void +exaGlyphs (CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, + INT16 ySrc, + int nlist, + GlyphListPtr list, + GlyphPtr *glyphs) +{ + PixmapPtr pMaskPixmap = 0; + PicturePtr pMask = NULL; + ScreenPtr pScreen = pDst->pDrawable->pScreen; + int width = 0, height = 0; + int x, y; + int first_xOff = list->xOff, first_yOff = list->yOff; + int n; + GlyphPtr glyph; + int error; + BoxRec extents = {0, 0, 0, 0}; + CARD32 component_alpha; + ExaGlyphBuffer buffer; + + if (maskFormat) + { + ExaScreenPriv(pScreen); + GCPtr pGC; + xRectangle rect; + + GlyphExtents (nlist, list, glyphs, &extents); + + if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) + return; + width = extents.x2 - extents.x1; + height = extents.y2 - extents.y1; + + if (maskFormat->depth == 1) { + PictFormatPtr a8Format = PictureMatchFormat (pScreen, 8, PICT_a8); + + if (a8Format) + maskFormat = a8Format; + } + + pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, + maskFormat->depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pMaskPixmap) + return; + component_alpha = NeedsComponent(maskFormat->format); + pMask = CreatePicture (0, &pMaskPixmap->drawable, + maskFormat, CPComponentAlpha, &component_alpha, + serverClient, &error); + if (!pMask || + (!component_alpha && pExaScr->info->CheckComposite && + !(*pExaScr->info->CheckComposite) (PictOpAdd, pSrc, NULL, pMask))) + { + PictFormatPtr argbFormat; + + (*pScreen->DestroyPixmap) (pMaskPixmap); + + if (!pMask) + return; + + /* The driver can't seem to composite to a8, let's try argb (but + * without component-alpha) */ + FreePicture ((pointer) pMask, (XID) 0); + + argbFormat = PictureMatchFormat (pScreen, 32, PICT_a8r8g8b8); + + if (argbFormat) + maskFormat = argbFormat; + + pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, + maskFormat->depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pMaskPixmap) + return; + + pMask = CreatePicture (0, &pMaskPixmap->drawable, maskFormat, 0, 0, + serverClient, &error); + if (!pMask) { + (*pScreen->DestroyPixmap) (pMaskPixmap); + return; + } + } + pGC = GetScratchGC (pMaskPixmap->drawable.depth, pScreen); + ValidateGC (&pMaskPixmap->drawable, pGC); + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; + (*pGC->ops->PolyFillRect) (&pMaskPixmap->drawable, pGC, 1, &rect); + FreeScratchGC (pGC); + x = -extents.x1; + y = -extents.y1; + } + else + { + x = 0; + y = 0; + } + buffer.count = 0; + buffer.mask = NULL; + while (nlist--) + { + x += list->xOff; + y += list->yOff; + n = list->len; + while (n--) + { + glyph = *glyphs++; + + if (glyph->info.width > 0 && glyph->info.height > 0) + { + /* pGlyph->info.{x,y} compensate for empty space in the glyph. */ + if (maskFormat) + { + if (exaBufferGlyph(pScreen, &buffer, glyph, NULL, pMask, + 0, 0, 0, 0, x - glyph->info.x, y - glyph->info.y) == ExaGlyphNeedFlush) + { + exaGlyphsToMask(pMask, &buffer); + exaBufferGlyph(pScreen, &buffer, glyph, NULL, pMask, + 0, 0, 0, 0, x - glyph->info.x, y - glyph->info.y); + } + } + else + { + if (exaBufferGlyph(pScreen, &buffer, glyph, pSrc, pDst, + xSrc + (x - glyph->info.x) - first_xOff, ySrc + (y - glyph->info.y) - first_yOff, + 0, 0, x - glyph->info.x, y - glyph->info.y) + == ExaGlyphNeedFlush) + { + exaGlyphsToDst(pSrc, pDst, &buffer); + exaBufferGlyph(pScreen, &buffer, glyph, pSrc, pDst, + xSrc + (x - glyph->info.x) - first_xOff, ySrc + (y - glyph->info.y) - first_yOff, + 0, 0, x - glyph->info.x, y - glyph->info.y); + } + } + } + + x += glyph->info.xOff; + y += glyph->info.yOff; + } + list++; + } + + if (buffer.count) { + if (maskFormat) + exaGlyphsToMask(pMask, &buffer); + else + exaGlyphsToDst(pSrc, pDst, &buffer); + } + + if (maskFormat) + { + x = extents.x1; + y = extents.y1; + CompositePicture (op, + pSrc, + pMask, + pDst, + xSrc + x - first_xOff, + ySrc + y - first_yOff, + 0, 0, + x, y, + width, height); + FreePicture ((pointer) pMask, (XID) 0); + (*pScreen->DestroyPixmap) (pMaskPixmap); + } +} diff --git a/xorg-server/exa/exa_offscreen.c b/xorg-server/exa/exa_offscreen.c index e3a9ab2f6..506da616d 100644 --- a/xorg-server/exa/exa_offscreen.c +++ b/xorg-server/exa/exa_offscreen.c @@ -1,696 +1,696 @@ -/* - * Copyright © 2003 Anders Carlsson - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Anders Carlsson not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission. Anders Carlsson makes no - * representations about the suitability of this software for any purpose. It - * is provided "as is" without express or implied warranty. - * - * ANDERS CARLSSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL ANDERS CARLSSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -/** @file - * This allocator allocates blocks of memory by maintaining a list of areas. - * When allocating, the contiguous block of areas with the minimum eviction - * cost is found and evicted in order to make room for the new allocation. - */ - -#include "exa_priv.h" - -#include -#include -#include - -#if DEBUG_OFFSCREEN -#define DBG_OFFSCREEN(a) ErrorF a -#else -#define DBG_OFFSCREEN(a) -#endif - -#if DEBUG_OFFSCREEN -static void -ExaOffscreenValidate (ScreenPtr pScreen) -{ - ExaScreenPriv (pScreen); - ExaOffscreenArea *prev = 0, *area; - - assert (pExaScr->info->offScreenAreas->base_offset == - pExaScr->info->offScreenBase); - for (area = pExaScr->info->offScreenAreas; area; area = area->next) - { - assert (area->offset >= area->base_offset && - area->offset < (area->base_offset + area->size)); - if (prev) - assert (prev->base_offset + prev->size == area->base_offset); - prev = area; - } - assert (prev->base_offset + prev->size == pExaScr->info->memorySize); -} -#else -#define ExaOffscreenValidate(s) -#endif - -static ExaOffscreenArea * -ExaOffscreenKickOut (ScreenPtr pScreen, ExaOffscreenArea *area) -{ - if (area->save) - (*area->save) (pScreen, area); - return exaOffscreenFree (pScreen, area); -} - -static void -exaUpdateEvictionCost(ExaOffscreenArea *area, unsigned offScreenCounter) -{ - unsigned age; - - if (area->state == ExaOffscreenAvail) - return; - - age = offScreenCounter - area->last_use; - - /* This is unlikely to happen, but could result in a division by zero... */ - if (age > (UINT_MAX / 2)) { - age = UINT_MAX / 2; - area->last_use = offScreenCounter - age; - } - - area->eviction_cost = area->size / age; -} - -static ExaOffscreenArea * -exaFindAreaToEvict(ExaScreenPrivPtr pExaScr, int size, int align) -{ - ExaOffscreenArea *begin, *end, *best; - unsigned cost, best_cost; - int avail, real_size; - - best_cost = UINT_MAX; - begin = end = pExaScr->info->offScreenAreas; - avail = 0; - cost = 0; - best = 0; - - while (end != NULL) - { - restart: - while (begin != NULL && begin->state == ExaOffscreenLocked) - begin = end = begin->next; - - if (begin == NULL) - break; - - /* adjust size needed to account for alignment loss for this area */ - real_size = size + (begin->base_offset + begin->size - size) % align; - - while (avail < real_size && end != NULL) - { - if (end->state == ExaOffscreenLocked) { - /* Can't more room here, restart after this locked area */ - avail = 0; - cost = 0; - begin = end; - goto restart; - } - avail += end->size; - exaUpdateEvictionCost(end, pExaScr->offScreenCounter); - cost += end->eviction_cost; - end = end->next; - } - - /* Check the cost, update best */ - if (avail >= real_size && cost < best_cost) { - best = begin; - best_cost = cost; - } - - avail -= begin->size; - cost -= begin->eviction_cost; - begin = begin->next; - } - - return best; -} - -/** - * exaOffscreenAlloc allocates offscreen memory - * - * @param pScreen current screen - * @param size size in bytes of the allocation - * @param align byte alignment requirement for the offset of the allocated area - * @param locked whether the allocated area is locked and can't be kicked out - * @param save callback for when the area is evicted from memory - * @param privdata private data for the save callback. - * - * Allocates offscreen memory from the device associated with pScreen. size - * and align deteremine where and how large the allocated area is, and locked - * will mark whether it should be held in card memory. privdata may be any - * pointer for the save callback when the area is removed. - * - * Note that locked areas do get evicted on VT switch unless the driver - * requested version 2.1 or newer behavior. In that case, the save callback is - * still called. - */ -ExaOffscreenArea * -exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, - Bool locked, - ExaOffscreenSaveProc save, - pointer privData) -{ - ExaOffscreenArea *area; - ExaScreenPriv (pScreen); - int real_size = 0, largest_avail = 0; -#if DEBUG_OFFSCREEN - static int number = 0; - ErrorF("================= ============ allocating a new pixmap %d\n", ++number); -#endif - - ExaOffscreenValidate (pScreen); - if (!align) - align = 1; - - if (!size) - { - DBG_OFFSCREEN (("Alloc 0x%x -> EMPTY\n", size)); - return NULL; - } - - /* throw out requests that cannot fit */ - if (size > (pExaScr->info->memorySize - pExaScr->info->offScreenBase)) - { - DBG_OFFSCREEN (("Alloc 0x%x vs (0x%lx) -> TOBIG\n", size, - pExaScr->info->memorySize - - pExaScr->info->offScreenBase)); - return NULL; - } - - /* Try to find a free space that'll fit. */ - for (area = pExaScr->info->offScreenAreas; area; area = area->next) - { - /* skip allocated areas */ - if (area->state != ExaOffscreenAvail) - continue; - - /* adjust size to match alignment requirement */ - real_size = size + (area->base_offset + area->size - size) % align; - - /* does it fit? */ - if (real_size <= area->size) - break; - - if (area->size > largest_avail) - largest_avail = area->size; - } - - if (!area) - { - area = exaFindAreaToEvict(pExaScr, size, align); - - if (!area) - { - DBG_OFFSCREEN (("Alloc 0x%x -> NOSPACE\n", size)); - /* Could not allocate memory */ - ExaOffscreenValidate (pScreen); - return NULL; - } - - /* adjust size needed to account for alignment loss for this area */ - real_size = size + (area->base_offset + area->size - size) % align; - - /* - * Kick out first area if in use - */ - if (area->state != ExaOffscreenAvail) - area = ExaOffscreenKickOut (pScreen, area); - /* - * Now get the system to merge the other needed areas together - */ - while (area->size < real_size) - { - assert (area->next && area->next->state == ExaOffscreenRemovable); - (void) ExaOffscreenKickOut (pScreen, area->next); - } - } - - /* save extra space in new area */ - if (real_size < area->size) - { - ExaOffscreenArea *new_area = xalloc (sizeof (ExaOffscreenArea)); - if (!new_area) - return NULL; - new_area->base_offset = area->base_offset; - - new_area->offset = new_area->base_offset; - new_area->align = 0; - new_area->size = area->size - real_size; - new_area->state = ExaOffscreenAvail; - new_area->save = NULL; - new_area->last_use = 0; - new_area->eviction_cost = 0; - new_area->next = area; - new_area->prev = area->prev; - if (area->prev->next) - area->prev->next = new_area; - else - pExaScr->info->offScreenAreas = new_area; - area->prev = new_area; - area->base_offset = new_area->base_offset + new_area->size; - area->size = real_size; - } else - pExaScr->numOffscreenAvailable--; - - /* - * Mark this area as in use - */ - if (locked) - area->state = ExaOffscreenLocked; - else - area->state = ExaOffscreenRemovable; - area->privData = privData; - area->save = save; - area->last_use = pExaScr->offScreenCounter++; - area->offset = (area->base_offset + align - 1); - area->offset -= area->offset % align; - area->align = align; - - ExaOffscreenValidate (pScreen); - - DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x (0x%x)\n", size, - area->base_offset, area->offset)); - return area; -} - -/** - * Ejects all offscreen areas, and uninitializes the offscreen memory manager. - */ -void -ExaOffscreenSwapOut (ScreenPtr pScreen) -{ - ExaScreenPriv (pScreen); - - ExaOffscreenValidate (pScreen); - /* loop until a single free area spans the space */ - for (;;) - { - ExaOffscreenArea *area = pExaScr->info->offScreenAreas; - - if (!area) - break; - if (area->state == ExaOffscreenAvail) - { - area = area->next; - if (!area) - break; - } - assert (area->state != ExaOffscreenAvail); - (void) ExaOffscreenKickOut (pScreen, area); - ExaOffscreenValidate (pScreen); - } - ExaOffscreenValidate (pScreen); - ExaOffscreenFini (pScreen); -} - -/** Ejects all pixmaps managed by EXA. */ -static void -ExaOffscreenEjectPixmaps (ScreenPtr pScreen) -{ - ExaScreenPriv (pScreen); - - ExaOffscreenValidate (pScreen); - /* loop until a single free area spans the space */ - for (;;) - { - ExaOffscreenArea *area; - - for (area = pExaScr->info->offScreenAreas; area != NULL; - area = area->next) - { - if (area->state == ExaOffscreenRemovable && - area->save == exaPixmapSave) - { - (void) ExaOffscreenKickOut (pScreen, area); - ExaOffscreenValidate (pScreen); - break; - } - } - if (area == NULL) - break; - } - ExaOffscreenValidate (pScreen); -} - -void -ExaOffscreenSwapIn (ScreenPtr pScreen) -{ - exaOffscreenInit (pScreen); -} - -/** - * Prepares EXA for disabling of FB access, or restoring it. - * - * In version 2.1, the disabling results in pixmaps being ejected, while other - * allocations remain. With this plus the prevention of migration while - * swappedOut is set, EXA by itself should not cause any access of the - * framebuffer to occur while swapped out. Any remaining issues are the - * responsibility of the driver. - * - * Prior to version 2.1, all allocations, including locked ones, are ejected - * when access is disabled, and the allocator is torn down while swappedOut - * is set. This is more drastic, and caused implementation difficulties for - * many drivers that could otherwise handle the lack of FB access while - * swapped out. - */ -void -exaEnableDisableFBAccess (int index, Bool enable) -{ - ScreenPtr pScreen = screenInfo.screens[index]; - ExaScreenPriv (pScreen); - - if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) - return; - - if (!enable && pExaScr->disableFbCount++ == 0) { - if (pExaScr->info->exa_minor < 1) - ExaOffscreenSwapOut (pScreen); - else - ExaOffscreenEjectPixmaps (pScreen); - pExaScr->swappedOut = TRUE; - } - - if (enable && --pExaScr->disableFbCount == 0) { - if (pExaScr->info->exa_minor < 1) - ExaOffscreenSwapIn (pScreen); - pExaScr->swappedOut = FALSE; - } -} - -/* merge the next free area into this one */ -static void -ExaOffscreenMerge (ExaScreenPrivPtr pExaScr, ExaOffscreenArea *area) -{ - ExaOffscreenArea *next = area->next; - - /* account for space */ - area->size += next->size; - /* frob pointer */ - area->next = next->next; - if (area->next) - area->next->prev = area; - else - pExaScr->info->offScreenAreas->prev = area; - xfree (next); - - pExaScr->numOffscreenAvailable--; -} - -/** - * exaOffscreenFree frees an allocation. - * - * @param pScreen current screen - * @param area offscreen area to free - * - * exaOffscreenFree frees an allocation created by exaOffscreenAlloc. Note that - * the save callback of the area is not called, and it is up to the driver to - * do any cleanup necessary as a result. - * - * @return pointer to the newly freed area. This behavior should not be relied - * on. - */ -ExaOffscreenArea * -exaOffscreenFree (ScreenPtr pScreen, ExaOffscreenArea *area) -{ - ExaScreenPriv(pScreen); - ExaOffscreenArea *next = area->next; - ExaOffscreenArea *prev; - - DBG_OFFSCREEN (("Free 0x%x -> 0x%x (0x%x)\n", area->size, - area->base_offset, area->offset)); - ExaOffscreenValidate (pScreen); - - area->state = ExaOffscreenAvail; - area->save = NULL; - area->last_use = 0; - area->eviction_cost = 0; - /* - * Find previous area - */ - if (area == pExaScr->info->offScreenAreas) - prev = NULL; - else - prev = area->prev; - - pExaScr->numOffscreenAvailable++; - - /* link with next area if free */ - if (next && next->state == ExaOffscreenAvail) - ExaOffscreenMerge (pExaScr, area); - - /* link with prev area if free */ - if (prev && prev->state == ExaOffscreenAvail) - { - area = prev; - ExaOffscreenMerge (pExaScr, area); - } - - ExaOffscreenValidate (pScreen); - DBG_OFFSCREEN(("\tdone freeing\n")); - return area; -} - -void -ExaOffscreenMarkUsed (PixmapPtr pPixmap) -{ - ExaPixmapPriv (pPixmap); - ExaScreenPriv (pPixmap->drawable.pScreen); - - if (!pExaPixmap || !pExaPixmap->area) - return; - - pExaPixmap->area->last_use = pExaScr->offScreenCounter++; -} - -/** - * Defragment offscreen memory by compacting allocated areas at the end of it, - * leaving the total amount of memory available as a single area at the - * beginning (when there are no pinned allocations). - */ -_X_HIDDEN ExaOffscreenArea* -ExaOffscreenDefragment (ScreenPtr pScreen) -{ - ExaScreenPriv (pScreen); - ExaOffscreenArea *area, *largest_available = NULL; - int largest_size = 0; - PixmapPtr pDstPix; - ExaPixmapPrivPtr pExaDstPix; - - pDstPix = (*pScreen->CreatePixmap) (pScreen, 0, 0, 0, 0); - - if (!pDstPix) - return NULL; - - pExaDstPix = ExaGetPixmapPriv (pDstPix); - pExaDstPix->use_gpu_copy = TRUE; - - for (area = pExaScr->info->offScreenAreas->prev; - area != pExaScr->info->offScreenAreas; - ) - { - ExaOffscreenArea *prev = area->prev; - PixmapPtr pSrcPix; - ExaPixmapPrivPtr pExaSrcPix; - Bool save_use_gpu_copy; - int save_pitch; - - if (area->state != ExaOffscreenAvail || - prev->state == ExaOffscreenLocked || - (prev->state == ExaOffscreenRemovable && - prev->save != exaPixmapSave)) { - area = prev; - continue; - } - - if (prev->state == ExaOffscreenAvail) { - if (area == largest_available) { - largest_available = prev; - largest_size += prev->size; - } - area = prev; - ExaOffscreenMerge (pExaScr, area); - continue; - } - - if (area->size > largest_size) { - largest_available = area; - largest_size = area->size; - } - - pSrcPix = prev->privData; - pExaSrcPix = ExaGetPixmapPriv (pSrcPix); - - pExaDstPix->fb_ptr = pExaScr->info->memoryBase + - area->base_offset + area->size - prev->size + prev->base_offset - - prev->offset; - pExaDstPix->fb_ptr -= (unsigned long)pExaDstPix->fb_ptr % prev->align; - - if (pExaDstPix->fb_ptr <= pExaSrcPix->fb_ptr) { - area = prev; - continue; - } - - if (!(pExaScr->info->flags & EXA_SUPPORTS_OFFSCREEN_OVERLAPS) && - (pExaSrcPix->fb_ptr + prev->size) > pExaDstPix->fb_ptr) { - area = prev; - continue; - } - - save_use_gpu_copy = pExaSrcPix->use_gpu_copy; - save_pitch = pSrcPix->devKind; - - pExaSrcPix->use_gpu_copy = TRUE; - pSrcPix->devKind = pExaSrcPix->fb_pitch; - - pDstPix->drawable.width = pSrcPix->drawable.width; - pDstPix->devKind = pSrcPix->devKind; - pDstPix->drawable.height = pSrcPix->drawable.height; - pDstPix->drawable.depth = pSrcPix->drawable.depth; - pDstPix->drawable.bitsPerPixel = pSrcPix->drawable.bitsPerPixel; - - if (!pExaScr->info->PrepareCopy (pSrcPix, pDstPix, -1, -1, GXcopy, ~0)) { - pExaSrcPix->use_gpu_copy = save_use_gpu_copy; - pSrcPix->devKind = save_pitch; - area = prev; - continue; - } - - pExaScr->info->Copy (pDstPix, 0, 0, 0, 0, pDstPix->drawable.width, - pDstPix->drawable.height); - pExaScr->info->DoneCopy (pDstPix); - exaMarkSync (pScreen); - - DBG_OFFSCREEN(("Before swap: prev=0x%08x-0x%08x-0x%08x area=0x%08x-0x%08x-0x%08x\n", - prev->base_offset, prev->offset, prev->base_offset + prev->size, - area->base_offset, area->offset, area->base_offset + area->size)); - - /* Calculate swapped area offsets and sizes */ - area->base_offset = prev->base_offset; - area->offset = area->base_offset; - prev->offset += pExaDstPix->fb_ptr - pExaSrcPix->fb_ptr; - assert(prev->offset >= pExaScr->info->offScreenBase && - prev->offset < pExaScr->info->memorySize); - prev->base_offset = prev->offset; - if (area->next) - prev->size = area->next->base_offset - prev->base_offset; - else - prev->size = pExaScr->info->memorySize - prev->base_offset; - area->size = prev->base_offset - area->base_offset; - - DBG_OFFSCREEN(("After swap: area=0x%08x-0x%08x-0x%08x prev=0x%08x-0x%08x-0x%08x\n", - area->base_offset, area->offset, area->base_offset + area->size, - prev->base_offset, prev->offset, prev->base_offset + prev->size)); - - /* Swap areas in list */ - if (area->next) - area->next->prev = prev; - else - pExaScr->info->offScreenAreas->prev = prev; - if (prev->prev->next) - prev->prev->next = area; - else - pExaScr->info->offScreenAreas = area; - prev->next = area->next; - area->next = prev; - area->prev = prev->prev; - prev->prev = area; - if (!area->prev->next) - pExaScr->info->offScreenAreas = area; - -#if DEBUG_OFFSCREEN - if (prev->prev == prev || prev->next == prev) - ErrorF("Whoops, prev points to itself!\n"); - - if (area->prev == area || area->next == area) - ErrorF("Whoops, area points to itself!\n"); -#endif - - pExaSrcPix->fb_ptr = pExaDstPix->fb_ptr; - pExaSrcPix->use_gpu_copy = save_use_gpu_copy; - pSrcPix->devKind = save_pitch; - } - - pDstPix->drawable.width = 0; - pDstPix->drawable.height = 0; - pDstPix->drawable.depth = 0; - pDstPix->drawable.bitsPerPixel = 0; - - (*pScreen->DestroyPixmap) (pDstPix); - - if (area->state == ExaOffscreenAvail && area->size > largest_size) - return area; - - return largest_available; -} - -/** - * exaOffscreenInit initializes the offscreen memory manager. - * - * @param pScreen current screen - * - * exaOffscreenInit is called by exaDriverInit to set up the memory manager for - * the screen, if any offscreen memory is available. - */ -Bool -exaOffscreenInit (ScreenPtr pScreen) -{ - ExaScreenPriv (pScreen); - ExaOffscreenArea *area; - - /* Allocate a big free area */ - area = xalloc (sizeof (ExaOffscreenArea)); - - if (!area) - return FALSE; - - area->state = ExaOffscreenAvail; - area->base_offset = pExaScr->info->offScreenBase; - area->offset = area->base_offset; - area->align = 0; - area->size = pExaScr->info->memorySize - area->base_offset; - area->save = NULL; - area->next = NULL; - area->prev = area; - area->last_use = 0; - area->eviction_cost = 0; - - /* Add it to the free areas */ - pExaScr->info->offScreenAreas = area; - pExaScr->offScreenCounter = 1; - pExaScr->numOffscreenAvailable = 1; - - ExaOffscreenValidate (pScreen); - - return TRUE; -} - -void -ExaOffscreenFini (ScreenPtr pScreen) -{ - ExaScreenPriv (pScreen); - ExaOffscreenArea *area; - - /* just free all of the area records */ - while ((area = pExaScr->info->offScreenAreas)) - { - pExaScr->info->offScreenAreas = area->next; - xfree (area); - } -} +/* + * Copyright © 2003 Anders Carlsson + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Anders Carlsson not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Anders Carlsson makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * ANDERS CARLSSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL ANDERS CARLSSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** @file + * This allocator allocates blocks of memory by maintaining a list of areas. + * When allocating, the contiguous block of areas with the minimum eviction + * cost is found and evicted in order to make room for the new allocation. + */ + +#include "exa_priv.h" + +#include +#include +#include + +#if DEBUG_OFFSCREEN +#define DBG_OFFSCREEN(a) ErrorF a +#else +#define DBG_OFFSCREEN(a) +#endif + +#if DEBUG_OFFSCREEN +static void +ExaOffscreenValidate (ScreenPtr pScreen) +{ + ExaScreenPriv (pScreen); + ExaOffscreenArea *prev = 0, *area; + + assert (pExaScr->info->offScreenAreas->base_offset == + pExaScr->info->offScreenBase); + for (area = pExaScr->info->offScreenAreas; area; area = area->next) + { + assert (area->offset >= area->base_offset && + area->offset < (area->base_offset + area->size)); + if (prev) + assert (prev->base_offset + prev->size == area->base_offset); + prev = area; + } + assert (prev->base_offset + prev->size == pExaScr->info->memorySize); +} +#else +#define ExaOffscreenValidate(s) +#endif + +static ExaOffscreenArea * +ExaOffscreenKickOut (ScreenPtr pScreen, ExaOffscreenArea *area) +{ + if (area->save) + (*area->save) (pScreen, area); + return exaOffscreenFree (pScreen, area); +} + +static void +exaUpdateEvictionCost(ExaOffscreenArea *area, unsigned offScreenCounter) +{ + unsigned age; + + if (area->state == ExaOffscreenAvail) + return; + + age = offScreenCounter - area->last_use; + + /* This is unlikely to happen, but could result in a division by zero... */ + if (age > (UINT_MAX / 2)) { + age = UINT_MAX / 2; + area->last_use = offScreenCounter - age; + } + + area->eviction_cost = area->size / age; +} + +static ExaOffscreenArea * +exaFindAreaToEvict(ExaScreenPrivPtr pExaScr, int size, int align) +{ + ExaOffscreenArea *begin, *end, *best; + unsigned cost, best_cost; + int avail, real_size; + + best_cost = UINT_MAX; + begin = end = pExaScr->info->offScreenAreas; + avail = 0; + cost = 0; + best = 0; + + while (end != NULL) + { + restart: + while (begin != NULL && begin->state == ExaOffscreenLocked) + begin = end = begin->next; + + if (begin == NULL) + break; + + /* adjust size needed to account for alignment loss for this area */ + real_size = size + (begin->base_offset + begin->size - size) % align; + + while (avail < real_size && end != NULL) + { + if (end->state == ExaOffscreenLocked) { + /* Can't more room here, restart after this locked area */ + avail = 0; + cost = 0; + begin = end; + goto restart; + } + avail += end->size; + exaUpdateEvictionCost(end, pExaScr->offScreenCounter); + cost += end->eviction_cost; + end = end->next; + } + + /* Check the cost, update best */ + if (avail >= real_size && cost < best_cost) { + best = begin; + best_cost = cost; + } + + avail -= begin->size; + cost -= begin->eviction_cost; + begin = begin->next; + } + + return best; +} + +/** + * exaOffscreenAlloc allocates offscreen memory + * + * @param pScreen current screen + * @param size size in bytes of the allocation + * @param align byte alignment requirement for the offset of the allocated area + * @param locked whether the allocated area is locked and can't be kicked out + * @param save callback for when the area is evicted from memory + * @param privdata private data for the save callback. + * + * Allocates offscreen memory from the device associated with pScreen. size + * and align deteremine where and how large the allocated area is, and locked + * will mark whether it should be held in card memory. privdata may be any + * pointer for the save callback when the area is removed. + * + * Note that locked areas do get evicted on VT switch unless the driver + * requested version 2.1 or newer behavior. In that case, the save callback is + * still called. + */ +ExaOffscreenArea * +exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, + Bool locked, + ExaOffscreenSaveProc save, + pointer privData) +{ + ExaOffscreenArea *area; + ExaScreenPriv (pScreen); + int real_size = 0, largest_avail = 0; +#if DEBUG_OFFSCREEN + static int number = 0; + ErrorF("================= ============ allocating a new pixmap %d\n", ++number); +#endif + + ExaOffscreenValidate (pScreen); + if (!align) + align = 1; + + if (!size) + { + DBG_OFFSCREEN (("Alloc 0x%x -> EMPTY\n", size)); + return NULL; + } + + /* throw out requests that cannot fit */ + if (size > (pExaScr->info->memorySize - pExaScr->info->offScreenBase)) + { + DBG_OFFSCREEN (("Alloc 0x%x vs (0x%lx) -> TOBIG\n", size, + pExaScr->info->memorySize - + pExaScr->info->offScreenBase)); + return NULL; + } + + /* Try to find a free space that'll fit. */ + for (area = pExaScr->info->offScreenAreas; area; area = area->next) + { + /* skip allocated areas */ + if (area->state != ExaOffscreenAvail) + continue; + + /* adjust size to match alignment requirement */ + real_size = size + (area->base_offset + area->size - size) % align; + + /* does it fit? */ + if (real_size <= area->size) + break; + + if (area->size > largest_avail) + largest_avail = area->size; + } + + if (!area) + { + area = exaFindAreaToEvict(pExaScr, size, align); + + if (!area) + { + DBG_OFFSCREEN (("Alloc 0x%x -> NOSPACE\n", size)); + /* Could not allocate memory */ + ExaOffscreenValidate (pScreen); + return NULL; + } + + /* adjust size needed to account for alignment loss for this area */ + real_size = size + (area->base_offset + area->size - size) % align; + + /* + * Kick out first area if in use + */ + if (area->state != ExaOffscreenAvail) + area = ExaOffscreenKickOut (pScreen, area); + /* + * Now get the system to merge the other needed areas together + */ + while (area->size < real_size) + { + assert (area->next && area->next->state == ExaOffscreenRemovable); + (void) ExaOffscreenKickOut (pScreen, area->next); + } + } + + /* save extra space in new area */ + if (real_size < area->size) + { + ExaOffscreenArea *new_area = malloc(sizeof (ExaOffscreenArea)); + if (!new_area) + return NULL; + new_area->base_offset = area->base_offset; + + new_area->offset = new_area->base_offset; + new_area->align = 0; + new_area->size = area->size - real_size; + new_area->state = ExaOffscreenAvail; + new_area->save = NULL; + new_area->last_use = 0; + new_area->eviction_cost = 0; + new_area->next = area; + new_area->prev = area->prev; + if (area->prev->next) + area->prev->next = new_area; + else + pExaScr->info->offScreenAreas = new_area; + area->prev = new_area; + area->base_offset = new_area->base_offset + new_area->size; + area->size = real_size; + } else + pExaScr->numOffscreenAvailable--; + + /* + * Mark this area as in use + */ + if (locked) + area->state = ExaOffscreenLocked; + else + area->state = ExaOffscreenRemovable; + area->privData = privData; + area->save = save; + area->last_use = pExaScr->offScreenCounter++; + area->offset = (area->base_offset + align - 1); + area->offset -= area->offset % align; + area->align = align; + + ExaOffscreenValidate (pScreen); + + DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x (0x%x)\n", size, + area->base_offset, area->offset)); + return area; +} + +/** + * Ejects all offscreen areas, and uninitializes the offscreen memory manager. + */ +void +ExaOffscreenSwapOut (ScreenPtr pScreen) +{ + ExaScreenPriv (pScreen); + + ExaOffscreenValidate (pScreen); + /* loop until a single free area spans the space */ + for (;;) + { + ExaOffscreenArea *area = pExaScr->info->offScreenAreas; + + if (!area) + break; + if (area->state == ExaOffscreenAvail) + { + area = area->next; + if (!area) + break; + } + assert (area->state != ExaOffscreenAvail); + (void) ExaOffscreenKickOut (pScreen, area); + ExaOffscreenValidate (pScreen); + } + ExaOffscreenValidate (pScreen); + ExaOffscreenFini (pScreen); +} + +/** Ejects all pixmaps managed by EXA. */ +static void +ExaOffscreenEjectPixmaps (ScreenPtr pScreen) +{ + ExaScreenPriv (pScreen); + + ExaOffscreenValidate (pScreen); + /* loop until a single free area spans the space */ + for (;;) + { + ExaOffscreenArea *area; + + for (area = pExaScr->info->offScreenAreas; area != NULL; + area = area->next) + { + if (area->state == ExaOffscreenRemovable && + area->save == exaPixmapSave) + { + (void) ExaOffscreenKickOut (pScreen, area); + ExaOffscreenValidate (pScreen); + break; + } + } + if (area == NULL) + break; + } + ExaOffscreenValidate (pScreen); +} + +void +ExaOffscreenSwapIn (ScreenPtr pScreen) +{ + exaOffscreenInit (pScreen); +} + +/** + * Prepares EXA for disabling of FB access, or restoring it. + * + * In version 2.1, the disabling results in pixmaps being ejected, while other + * allocations remain. With this plus the prevention of migration while + * swappedOut is set, EXA by itself should not cause any access of the + * framebuffer to occur while swapped out. Any remaining issues are the + * responsibility of the driver. + * + * Prior to version 2.1, all allocations, including locked ones, are ejected + * when access is disabled, and the allocator is torn down while swappedOut + * is set. This is more drastic, and caused implementation difficulties for + * many drivers that could otherwise handle the lack of FB access while + * swapped out. + */ +void +exaEnableDisableFBAccess (int index, Bool enable) +{ + ScreenPtr pScreen = screenInfo.screens[index]; + ExaScreenPriv (pScreen); + + if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) + return; + + if (!enable && pExaScr->disableFbCount++ == 0) { + if (pExaScr->info->exa_minor < 1) + ExaOffscreenSwapOut (pScreen); + else + ExaOffscreenEjectPixmaps (pScreen); + pExaScr->swappedOut = TRUE; + } + + if (enable && --pExaScr->disableFbCount == 0) { + if (pExaScr->info->exa_minor < 1) + ExaOffscreenSwapIn (pScreen); + pExaScr->swappedOut = FALSE; + } +} + +/* merge the next free area into this one */ +static void +ExaOffscreenMerge (ExaScreenPrivPtr pExaScr, ExaOffscreenArea *area) +{ + ExaOffscreenArea *next = area->next; + + /* account for space */ + area->size += next->size; + /* frob pointer */ + area->next = next->next; + if (area->next) + area->next->prev = area; + else + pExaScr->info->offScreenAreas->prev = area; + free(next); + + pExaScr->numOffscreenAvailable--; +} + +/** + * exaOffscreenFree frees an allocation. + * + * @param pScreen current screen + * @param area offscreen area to free + * + * exaOffscreenFree frees an allocation created by exaOffscreenAlloc. Note that + * the save callback of the area is not called, and it is up to the driver to + * do any cleanup necessary as a result. + * + * @return pointer to the newly freed area. This behavior should not be relied + * on. + */ +ExaOffscreenArea * +exaOffscreenFree (ScreenPtr pScreen, ExaOffscreenArea *area) +{ + ExaScreenPriv(pScreen); + ExaOffscreenArea *next = area->next; + ExaOffscreenArea *prev; + + DBG_OFFSCREEN (("Free 0x%x -> 0x%x (0x%x)\n", area->size, + area->base_offset, area->offset)); + ExaOffscreenValidate (pScreen); + + area->state = ExaOffscreenAvail; + area->save = NULL; + area->last_use = 0; + area->eviction_cost = 0; + /* + * Find previous area + */ + if (area == pExaScr->info->offScreenAreas) + prev = NULL; + else + prev = area->prev; + + pExaScr->numOffscreenAvailable++; + + /* link with next area if free */ + if (next && next->state == ExaOffscreenAvail) + ExaOffscreenMerge (pExaScr, area); + + /* link with prev area if free */ + if (prev && prev->state == ExaOffscreenAvail) + { + area = prev; + ExaOffscreenMerge (pExaScr, area); + } + + ExaOffscreenValidate (pScreen); + DBG_OFFSCREEN(("\tdone freeing\n")); + return area; +} + +void +ExaOffscreenMarkUsed (PixmapPtr pPixmap) +{ + ExaPixmapPriv (pPixmap); + ExaScreenPriv (pPixmap->drawable.pScreen); + + if (!pExaPixmap || !pExaPixmap->area) + return; + + pExaPixmap->area->last_use = pExaScr->offScreenCounter++; +} + +/** + * Defragment offscreen memory by compacting allocated areas at the end of it, + * leaving the total amount of memory available as a single area at the + * beginning (when there are no pinned allocations). + */ +_X_HIDDEN ExaOffscreenArea* +ExaOffscreenDefragment (ScreenPtr pScreen) +{ + ExaScreenPriv (pScreen); + ExaOffscreenArea *area, *largest_available = NULL; + int largest_size = 0; + PixmapPtr pDstPix; + ExaPixmapPrivPtr pExaDstPix; + + pDstPix = (*pScreen->CreatePixmap) (pScreen, 0, 0, 0, 0); + + if (!pDstPix) + return NULL; + + pExaDstPix = ExaGetPixmapPriv (pDstPix); + pExaDstPix->use_gpu_copy = TRUE; + + for (area = pExaScr->info->offScreenAreas->prev; + area != pExaScr->info->offScreenAreas; + ) + { + ExaOffscreenArea *prev = area->prev; + PixmapPtr pSrcPix; + ExaPixmapPrivPtr pExaSrcPix; + Bool save_use_gpu_copy; + int save_pitch; + + if (area->state != ExaOffscreenAvail || + prev->state == ExaOffscreenLocked || + (prev->state == ExaOffscreenRemovable && + prev->save != exaPixmapSave)) { + area = prev; + continue; + } + + if (prev->state == ExaOffscreenAvail) { + if (area == largest_available) { + largest_available = prev; + largest_size += prev->size; + } + area = prev; + ExaOffscreenMerge (pExaScr, area); + continue; + } + + if (area->size > largest_size) { + largest_available = area; + largest_size = area->size; + } + + pSrcPix = prev->privData; + pExaSrcPix = ExaGetPixmapPriv (pSrcPix); + + pExaDstPix->fb_ptr = pExaScr->info->memoryBase + + area->base_offset + area->size - prev->size + prev->base_offset - + prev->offset; + pExaDstPix->fb_ptr -= (unsigned long)pExaDstPix->fb_ptr % prev->align; + + if (pExaDstPix->fb_ptr <= pExaSrcPix->fb_ptr) { + area = prev; + continue; + } + + if (!(pExaScr->info->flags & EXA_SUPPORTS_OFFSCREEN_OVERLAPS) && + (pExaSrcPix->fb_ptr + prev->size) > pExaDstPix->fb_ptr) { + area = prev; + continue; + } + + save_use_gpu_copy = pExaSrcPix->use_gpu_copy; + save_pitch = pSrcPix->devKind; + + pExaSrcPix->use_gpu_copy = TRUE; + pSrcPix->devKind = pExaSrcPix->fb_pitch; + + pDstPix->drawable.width = pSrcPix->drawable.width; + pDstPix->devKind = pSrcPix->devKind; + pDstPix->drawable.height = pSrcPix->drawable.height; + pDstPix->drawable.depth = pSrcPix->drawable.depth; + pDstPix->drawable.bitsPerPixel = pSrcPix->drawable.bitsPerPixel; + + if (!pExaScr->info->PrepareCopy (pSrcPix, pDstPix, -1, -1, GXcopy, ~0)) { + pExaSrcPix->use_gpu_copy = save_use_gpu_copy; + pSrcPix->devKind = save_pitch; + area = prev; + continue; + } + + pExaScr->info->Copy (pDstPix, 0, 0, 0, 0, pDstPix->drawable.width, + pDstPix->drawable.height); + pExaScr->info->DoneCopy (pDstPix); + exaMarkSync (pScreen); + + DBG_OFFSCREEN(("Before swap: prev=0x%08x-0x%08x-0x%08x area=0x%08x-0x%08x-0x%08x\n", + prev->base_offset, prev->offset, prev->base_offset + prev->size, + area->base_offset, area->offset, area->base_offset + area->size)); + + /* Calculate swapped area offsets and sizes */ + area->base_offset = prev->base_offset; + area->offset = area->base_offset; + prev->offset += pExaDstPix->fb_ptr - pExaSrcPix->fb_ptr; + assert(prev->offset >= pExaScr->info->offScreenBase && + prev->offset < pExaScr->info->memorySize); + prev->base_offset = prev->offset; + if (area->next) + prev->size = area->next->base_offset - prev->base_offset; + else + prev->size = pExaScr->info->memorySize - prev->base_offset; + area->size = prev->base_offset - area->base_offset; + + DBG_OFFSCREEN(("After swap: area=0x%08x-0x%08x-0x%08x prev=0x%08x-0x%08x-0x%08x\n", + area->base_offset, area->offset, area->base_offset + area->size, + prev->base_offset, prev->offset, prev->base_offset + prev->size)); + + /* Swap areas in list */ + if (area->next) + area->next->prev = prev; + else + pExaScr->info->offScreenAreas->prev = prev; + if (prev->prev->next) + prev->prev->next = area; + else + pExaScr->info->offScreenAreas = area; + prev->next = area->next; + area->next = prev; + area->prev = prev->prev; + prev->prev = area; + if (!area->prev->next) + pExaScr->info->offScreenAreas = area; + +#if DEBUG_OFFSCREEN + if (prev->prev == prev || prev->next == prev) + ErrorF("Whoops, prev points to itself!\n"); + + if (area->prev == area || area->next == area) + ErrorF("Whoops, area points to itself!\n"); +#endif + + pExaSrcPix->fb_ptr = pExaDstPix->fb_ptr; + pExaSrcPix->use_gpu_copy = save_use_gpu_copy; + pSrcPix->devKind = save_pitch; + } + + pDstPix->drawable.width = 0; + pDstPix->drawable.height = 0; + pDstPix->drawable.depth = 0; + pDstPix->drawable.bitsPerPixel = 0; + + (*pScreen->DestroyPixmap) (pDstPix); + + if (area->state == ExaOffscreenAvail && area->size > largest_size) + return area; + + return largest_available; +} + +/** + * exaOffscreenInit initializes the offscreen memory manager. + * + * @param pScreen current screen + * + * exaOffscreenInit is called by exaDriverInit to set up the memory manager for + * the screen, if any offscreen memory is available. + */ +Bool +exaOffscreenInit (ScreenPtr pScreen) +{ + ExaScreenPriv (pScreen); + ExaOffscreenArea *area; + + /* Allocate a big free area */ + area = malloc(sizeof (ExaOffscreenArea)); + + if (!area) + return FALSE; + + area->state = ExaOffscreenAvail; + area->base_offset = pExaScr->info->offScreenBase; + area->offset = area->base_offset; + area->align = 0; + area->size = pExaScr->info->memorySize - area->base_offset; + area->save = NULL; + area->next = NULL; + area->prev = area; + area->last_use = 0; + area->eviction_cost = 0; + + /* Add it to the free areas */ + pExaScr->info->offScreenAreas = area; + pExaScr->offScreenCounter = 1; + pExaScr->numOffscreenAvailable = 1; + + ExaOffscreenValidate (pScreen); + + return TRUE; +} + +void +ExaOffscreenFini (ScreenPtr pScreen) +{ + ExaScreenPriv (pScreen); + ExaOffscreenArea *area; + + /* just free all of the area records */ + while ((area = pExaScr->info->offScreenAreas)) + { + pExaScr->info->offScreenAreas = area->next; + free(area); + } +} -- cgit v1.2.3