diff options
Diffstat (limited to 'xorg-server/exa')
-rw-r--r-- | xorg-server/exa/exa.c | 2258 | ||||
-rw-r--r-- | xorg-server/exa/exa_accel.c | 2608 | ||||
-rw-r--r-- | xorg-server/exa/exa_glyphs.c | 1734 | ||||
-rw-r--r-- | xorg-server/exa/exa_offscreen.c | 1392 |
4 files changed, 3996 insertions, 3996 deletions
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 <dix-config.h> -#endif - -#include <stdlib.h> - -#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 <dix-config.h>
+#endif
+
+#include <stdlib.h>
+
+#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 57029fdc5..e30ddce74 100644 --- a/xorg-server/exa/exa_accel.c +++ b/xorg-server/exa/exa_accel.c @@ -1,1304 +1,1304 @@ -/* - * 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. - * - * Authors: - * Eric Anholt <eric@anholt.net> - * Michel Dänzer <michel@tungstengraphics.com> - * - */ - -#ifdef HAVE_DIX_CONFIG_H -#include <dix-config.h> -#endif -#include "exa_priv.h" -#include <X11/fonts/fontstruct.h> -#include "dixfontstr.h" -#include "exa.h" - -static void -exaFillSpans(DrawablePtr pDrawable, GCPtr pGC, int n, - DDXPointPtr ppt, int *pwidth, int fSorted) -{ - ScreenPtr pScreen = pDrawable->pScreen; - ExaScreenPriv (pScreen); - RegionPtr pClip = fbGetCompositeClip(pGC); - PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable); - ExaPixmapPriv (pPixmap); - BoxPtr pextent, pbox; - int nbox; - int extentX1, extentX2, extentY1, extentY2; - int fullX1, fullX2, fullY1; - int partX1, partX2; - int off_x, off_y; - - if (pExaScr->fallback_counter || - pExaScr->swappedOut || - pGC->fillStyle != FillSolid || - pExaPixmap->accel_blocked) - { - ExaCheckFillSpans (pDrawable, pGC, n, ppt, pwidth, fSorted); - return; - } - - if (pExaScr->do_migration) { - ExaMigrationRec pixmaps[1]; - - pixmaps[0].as_dst = TRUE; - pixmaps[0].as_src = FALSE; - pixmaps[0].pPix = pPixmap; - pixmaps[0].pReg = NULL; - - exaDoMigration (pixmaps, 1, TRUE); - } - - if (!(pPixmap = exaGetOffscreenPixmap (pDrawable, &off_x, &off_y)) || - !(*pExaScr->info->PrepareSolid) (pPixmap, - pGC->alu, - pGC->planemask, - pGC->fgPixel)) - { - ExaCheckFillSpans (pDrawable, pGC, n, ppt, pwidth, fSorted); - return; - } - - pextent = REGION_EXTENTS(pGC->pScreen, pClip); - extentX1 = pextent->x1; - extentY1 = pextent->y1; - extentX2 = pextent->x2; - extentY2 = pextent->y2; - while (n--) - { - fullX1 = ppt->x; - fullY1 = ppt->y; - fullX2 = fullX1 + (int) *pwidth; - ppt++; - pwidth++; - - if (fullY1 < extentY1 || extentY2 <= fullY1) - continue; - - if (fullX1 < extentX1) - fullX1 = extentX1; - - if (fullX2 > extentX2) - fullX2 = extentX2; - - if (fullX1 >= fullX2) - continue; - - nbox = REGION_NUM_RECTS (pClip); - if (nbox == 1) - { - (*pExaScr->info->Solid) (pPixmap, - fullX1 + off_x, fullY1 + off_y, - fullX2 + off_x, fullY1 + 1 + off_y); - } - else - { - pbox = REGION_RECTS(pClip); - while(nbox--) - { - if (pbox->y1 <= fullY1 && fullY1 < pbox->y2) - { - partX1 = pbox->x1; - if (partX1 < fullX1) - partX1 = fullX1; - partX2 = pbox->x2; - if (partX2 > fullX2) - partX2 = fullX2; - if (partX2 > partX1) { - (*pExaScr->info->Solid) (pPixmap, - partX1 + off_x, fullY1 + off_y, - partX2 + off_x, fullY1 + 1 + off_y); - } - } - pbox++; - } - } - } - (*pExaScr->info->DoneSolid) (pPixmap); - exaMarkSync(pScreen); -} - -static Bool -exaDoPutImage (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, - int w, int h, int format, char *bits, int src_stride) -{ - ExaScreenPriv (pDrawable->pScreen); - PixmapPtr pPix = exaGetDrawablePixmap (pDrawable); - ExaPixmapPriv(pPix); - RegionPtr pClip; - BoxPtr pbox; - int nbox; - int xoff, yoff; - int bpp = pDrawable->bitsPerPixel; - Bool ret = TRUE; - - if (pExaScr->fallback_counter || pExaPixmap->accel_blocked || !pExaScr->info->UploadToScreen) - return FALSE; - - /* If there's a system copy, we want to save the result there */ - if (pExaPixmap->pDamage) - return FALSE; - - /* Don't bother with under 8bpp, XYPixmaps. */ - if (format != ZPixmap || bpp < 8) - return FALSE; - - /* Only accelerate copies: no rop or planemask. */ - if (!EXA_PM_IS_SOLID(pDrawable, pGC->planemask) || pGC->alu != GXcopy) - return FALSE; - - if (pExaScr->swappedOut) - return FALSE; - - if (pExaScr->do_migration) { - ExaMigrationRec pixmaps[1]; - - pixmaps[0].as_dst = TRUE; - pixmaps[0].as_src = FALSE; - pixmaps[0].pPix = pPix; - pixmaps[0].pReg = DamagePendingRegion(pExaPixmap->pDamage); - - exaDoMigration (pixmaps, 1, TRUE); - } - - pPix = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff); - - if (!pPix) - return FALSE; - - x += pDrawable->x; - y += pDrawable->y; - - pClip = fbGetCompositeClip(pGC); - for (nbox = REGION_NUM_RECTS(pClip), - pbox = REGION_RECTS(pClip); - nbox--; - pbox++) - { - int x1 = x; - int y1 = y; - int x2 = x + w; - int y2 = y + h; - char *src; - Bool ok; - - if (x1 < pbox->x1) - x1 = pbox->x1; - if (y1 < pbox->y1) - y1 = pbox->y1; - if (x2 > pbox->x2) - x2 = pbox->x2; - if (y2 > pbox->y2) - y2 = pbox->y2; - if (x1 >= x2 || y1 >= y2) - continue; - - src = bits + (y1 - y) * src_stride + (x1 - x) * (bpp / 8); - ok = pExaScr->info->UploadToScreen(pPix, x1 + xoff, y1 + yoff, - x2 - x1, y2 - y1, src, src_stride); - /* We have to fall back completely, and ignore what has already been completed. - * Messing with the fb layer directly like we used to is completely unacceptable. - */ - if (!ok) { - ret = FALSE; - break; - } - } - - if (ret) - exaMarkSync(pDrawable->pScreen); - - return ret; -} - -static void -exaPutImage (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, - int w, int h, int leftPad, int format, char *bits) -{ - if (!exaDoPutImage(pDrawable, pGC, depth, x, y, w, h, format, bits, - PixmapBytePad(w, pDrawable->depth))) - ExaCheckPutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format, - bits); -} - -static Bool inline -exaCopyNtoNTwoDir (DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, - GCPtr pGC, BoxPtr pbox, int nbox, int dx, int dy) -{ - ExaScreenPriv (pDstDrawable->pScreen); - PixmapPtr pSrcPixmap, pDstPixmap; - int src_off_x, src_off_y, dst_off_x, dst_off_y; - int dirsetup; - - /* Need to get both pixmaps to call the driver routines */ - pSrcPixmap = exaGetOffscreenPixmap (pSrcDrawable, &src_off_x, &src_off_y); - pDstPixmap = exaGetOffscreenPixmap (pDstDrawable, &dst_off_x, &dst_off_y); - if (!pSrcPixmap || !pDstPixmap) - return FALSE; - - /* - * Now the case of a chip that only supports xdir = ydir = 1 or - * xdir = ydir = -1, but we have xdir != ydir. - */ - dirsetup = 0; /* No direction set up yet. */ - for (; nbox; pbox++, nbox--) { - if (dx >= 0 && (src_off_y + pbox->y1 + dy) != pbox->y1) { - /* Do a xdir = ydir = -1 blit instead. */ - if (dirsetup != -1) { - if (dirsetup != 0) - pExaScr->info->DoneCopy(pDstPixmap); - dirsetup = -1; - if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap, - pDstPixmap, - -1, -1, - pGC ? pGC->alu : GXcopy, - pGC ? pGC->planemask : - FB_ALLONES)) - return FALSE; - } - (*pExaScr->info->Copy)(pDstPixmap, - src_off_x + pbox->x1 + dx, - src_off_y + pbox->y1 + dy, - dst_off_x + pbox->x1, - dst_off_y + pbox->y1, - pbox->x2 - pbox->x1, - pbox->y2 - pbox->y1); - } else if (dx < 0 && (src_off_y + pbox->y1 + dy) != pbox->y1) { - /* Do a xdir = ydir = 1 blit instead. */ - if (dirsetup != 1) { - if (dirsetup != 0) - pExaScr->info->DoneCopy(pDstPixmap); - dirsetup = 1; - if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap, - pDstPixmap, - 1, 1, - pGC ? pGC->alu : GXcopy, - pGC ? pGC->planemask : - FB_ALLONES)) - return FALSE; - } - (*pExaScr->info->Copy)(pDstPixmap, - src_off_x + pbox->x1 + dx, - src_off_y + pbox->y1 + dy, - dst_off_x + pbox->x1, - dst_off_y + pbox->y1, - pbox->x2 - pbox->x1, - pbox->y2 - pbox->y1); - } else if (dx >= 0) { - /* - * xdir = 1, ydir = -1. - * Perform line-by-line xdir = ydir = 1 blits, going up. - */ - int i; - if (dirsetup != 1) { - if (dirsetup != 0) - pExaScr->info->DoneCopy(pDstPixmap); - dirsetup = 1; - if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap, - pDstPixmap, - 1, 1, - pGC ? pGC->alu : GXcopy, - pGC ? pGC->planemask : - FB_ALLONES)) - return FALSE; - } - for (i = pbox->y2 - pbox->y1 - 1; i >= 0; i--) - (*pExaScr->info->Copy)(pDstPixmap, - src_off_x + pbox->x1 + dx, - src_off_y + pbox->y1 + dy + i, - dst_off_x + pbox->x1, - dst_off_y + pbox->y1 + i, - pbox->x2 - pbox->x1, 1); - } else { - /* - * xdir = -1, ydir = 1. - * Perform line-by-line xdir = ydir = -1 blits, going down. - */ - int i; - if (dirsetup != -1) { - if (dirsetup != 0) - pExaScr->info->DoneCopy(pDstPixmap); - dirsetup = -1; - if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap, - pDstPixmap, - -1, -1, - pGC ? pGC->alu : GXcopy, - pGC ? pGC->planemask : - FB_ALLONES)) - return FALSE; - } - for (i = 0; i < pbox->y2 - pbox->y1; i++) - (*pExaScr->info->Copy)(pDstPixmap, - src_off_x + pbox->x1 + dx, - src_off_y + pbox->y1 + dy + i, - dst_off_x + pbox->x1, - dst_off_y + pbox->y1 + i, - pbox->x2 - pbox->x1, 1); - } - } - if (dirsetup != 0) - pExaScr->info->DoneCopy(pDstPixmap); - exaMarkSync(pDstDrawable->pScreen); - return TRUE; -} - -Bool -exaHWCopyNtoN (DrawablePtr pSrcDrawable, - DrawablePtr pDstDrawable, - GCPtr pGC, - BoxPtr pbox, - int nbox, - int dx, - int dy, - Bool reverse, - Bool upsidedown) -{ - ExaScreenPriv (pDstDrawable->pScreen); - PixmapPtr pSrcPixmap, pDstPixmap; - ExaPixmapPrivPtr pSrcExaPixmap, pDstExaPixmap; - int src_off_x, src_off_y; - int dst_off_x, dst_off_y; - RegionPtr srcregion = NULL, dstregion = NULL; - xRectangle *rects; - Bool ret = TRUE; - - /* avoid doing copy operations if no boxes */ - if (nbox == 0) - return TRUE; - - pSrcPixmap = exaGetDrawablePixmap (pSrcDrawable); - pDstPixmap = exaGetDrawablePixmap (pDstDrawable); - - exaGetDrawableDeltas (pSrcDrawable, pSrcPixmap, &src_off_x, &src_off_y); - exaGetDrawableDeltas (pDstDrawable, pDstPixmap, &dst_off_x, &dst_off_y); - - rects = xalloc(nbox * sizeof(xRectangle)); - - if (rects) { - int i; - int ordering; - - for (i = 0; i < nbox; i++) { - rects[i].x = pbox[i].x1 + dx + src_off_x; - rects[i].y = pbox[i].y1 + dy + src_off_y; - rects[i].width = pbox[i].x2 - pbox[i].x1; - rects[i].height = pbox[i].y2 - pbox[i].y1; - } - - /* This must match the miRegionCopy() logic for reversing rect order */ - if (nbox == 1 || (dx > 0 && dy > 0) || - (pDstDrawable != pSrcDrawable && - (pDstDrawable->type != DRAWABLE_WINDOW || - pSrcDrawable->type != DRAWABLE_WINDOW))) - ordering = CT_YXBANDED; - else - ordering = CT_UNSORTED; - - srcregion = RECTS_TO_REGION(pScreen, nbox, rects, ordering); - xfree(rects); - - if (!pGC || !exaGCReadsDestination(pDstDrawable, pGC->planemask, - pGC->fillStyle, pGC->alu, - pGC->clientClipType)) { - dstregion = REGION_CREATE(pScreen, NullBox, 0); - REGION_COPY(pScreen, dstregion, srcregion); - REGION_TRANSLATE(pScreen, dstregion, dst_off_x - dx - src_off_x, - dst_off_y - dy - src_off_y); - } - } - - - pSrcExaPixmap = ExaGetPixmapPriv (pSrcPixmap); - pDstExaPixmap = ExaGetPixmapPriv (pDstPixmap); - - /* Check whether the accelerator can use this pixmap. - * If the pitch of the pixmaps is out of range, there's nothing - * we can do but fall back to software rendering. - */ - if (pSrcExaPixmap->accel_blocked & EXA_RANGE_PITCH || - pDstExaPixmap->accel_blocked & EXA_RANGE_PITCH) - goto fallback; - - /* If the width or the height of either of the pixmaps - * is out of range, check whether the boxes are actually out of the - * addressable range as well. If they aren't, we can still do - * the copying in hardware. - */ - if (pSrcExaPixmap->accel_blocked || pDstExaPixmap->accel_blocked) { - int i; - - for (i = 0; i < nbox; i++) { - /* src */ - if ((pbox[i].x2 + dx + src_off_x) >= pExaScr->info->maxX || - (pbox[i].y2 + dy + src_off_y) >= pExaScr->info->maxY) - goto fallback; - - /* dst */ - if ((pbox[i].x2 + dst_off_x) >= pExaScr->info->maxX || - (pbox[i].y2 + dst_off_y) >= pExaScr->info->maxY) - goto fallback; - } - } - - if (pExaScr->do_migration) { - ExaMigrationRec pixmaps[2]; - - pixmaps[0].as_dst = TRUE; - pixmaps[0].as_src = FALSE; - pixmaps[0].pPix = pDstPixmap; - pixmaps[0].pReg = dstregion; - pixmaps[1].as_dst = FALSE; - pixmaps[1].as_src = TRUE; - pixmaps[1].pPix = pSrcPixmap; - pixmaps[1].pReg = srcregion; - - exaDoMigration (pixmaps, 2, TRUE); - } - - /* Mixed directions must be handled specially if the card is lame */ - if ((pExaScr->info->flags & EXA_TWO_BITBLT_DIRECTIONS) && - reverse != upsidedown) { - if (exaCopyNtoNTwoDir(pSrcDrawable, pDstDrawable, pGC, pbox, nbox, - dx, dy)) - goto out; - goto fallback; - } - - if (exaPixmapHasGpuCopy(pDstPixmap)) { - /* Normal blitting. */ - if (exaPixmapHasGpuCopy(pSrcPixmap)) { - if (!(*pExaScr->info->PrepareCopy) (pSrcPixmap, pDstPixmap, reverse ? -1 : 1, - upsidedown ? -1 : 1, - pGC ? pGC->alu : GXcopy, - pGC ? pGC->planemask : FB_ALLONES)) { - goto fallback; - } - - while (nbox--) - { - (*pExaScr->info->Copy) (pDstPixmap, - pbox->x1 + dx + src_off_x, - pbox->y1 + dy + src_off_y, - pbox->x1 + dst_off_x, pbox->y1 + dst_off_y, - pbox->x2 - pbox->x1, pbox->y2 - pbox->y1); - pbox++; - } - - (*pExaScr->info->DoneCopy) (pDstPixmap); - exaMarkSync (pDstDrawable->pScreen); - /* UTS: mainly for SHM PutImage's secondary path. - * - * Only taking this path for directly accessible pixmaps. - */ - } else if (!pDstExaPixmap->pDamage && pSrcExaPixmap->sys_ptr) { - int bpp = pSrcDrawable->bitsPerPixel; - int src_stride = exaGetPixmapPitch(pSrcPixmap); - CARD8 *src = NULL; - - if (!pExaScr->info->UploadToScreen) - goto fallback; - - if (pSrcDrawable->bitsPerPixel != pDstDrawable->bitsPerPixel) - goto fallback; - - if (pSrcDrawable->bitsPerPixel < 8) - goto fallback; - - if (pGC && !(pGC->alu == GXcopy && EXA_PM_IS_SOLID(pSrcDrawable, pGC->planemask))) - goto fallback; - - while (nbox--) - { - src = pSrcExaPixmap->sys_ptr + (pbox->y1 + dy + src_off_y) * src_stride + (pbox->x1 + dx + src_off_x) * (bpp / 8); - if (!pExaScr->info->UploadToScreen(pDstPixmap, pbox->x1 + dst_off_x, - pbox->y1 + dst_off_y, pbox->x2 - pbox->x1, pbox->y2 - pbox->y1, - (char *) src, src_stride)) - goto fallback; - - pbox++; - } - } else - goto fallback; - } else - goto fallback; - - goto out; - -fallback: - ret = FALSE; - -out: - if (dstregion) { - REGION_UNINIT(pScreen, dstregion); - REGION_DESTROY(pScreen, dstregion); - } - if (srcregion) { - REGION_UNINIT(pScreen, srcregion); - REGION_DESTROY(pScreen, srcregion); - } - - return ret; -} - -void -exaCopyNtoN (DrawablePtr pSrcDrawable, - DrawablePtr pDstDrawable, - GCPtr pGC, - BoxPtr pbox, - int nbox, - int dx, - int dy, - Bool reverse, - Bool upsidedown, - Pixel bitplane, - void *closure) -{ - ExaScreenPriv(pDstDrawable->pScreen); - - if (pExaScr->fallback_counter || - (pExaScr->fallback_flags & EXA_FALLBACK_COPYWINDOW)) - return; - - if (exaHWCopyNtoN(pSrcDrawable, pDstDrawable, pGC, pbox, nbox, dx, dy, reverse, upsidedown)) - return; - - /* This is a CopyWindow, it's cleaner to fallback at the original call. */ - if (pExaScr->fallback_flags & EXA_ACCEL_COPYWINDOW) { - pExaScr->fallback_flags |= EXA_FALLBACK_COPYWINDOW; - return; - } - - /* fallback */ - ExaCheckCopyNtoN(pSrcDrawable, pDstDrawable, pGC, pbox, nbox, dx, dy, reverse, upsidedown, bitplane, closure); -} - -RegionPtr -exaCopyArea(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC, - int srcx, int srcy, int width, int height, int dstx, int dsty) -{ - ExaScreenPriv (pDstDrawable->pScreen); - - if (pExaScr->fallback_counter || pExaScr->swappedOut) { - return ExaCheckCopyArea(pSrcDrawable, pDstDrawable, pGC, - srcx, srcy, width, height, dstx, dsty); - } - - return miDoCopy (pSrcDrawable, pDstDrawable, pGC, - srcx, srcy, width, height, - dstx, dsty, exaCopyNtoN, 0, NULL); -} - -static void -exaPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, - DDXPointPtr ppt) -{ - ExaScreenPriv (pDrawable->pScreen); - int i; - xRectangle *prect; - - /* If we can't reuse the current GC as is, don't bother accelerating the - * points. - */ - if (pExaScr->fallback_counter || pGC->fillStyle != FillSolid) { - ExaCheckPolyPoint(pDrawable, pGC, mode, npt, ppt); - return; - } - - prect = xalloc(sizeof(xRectangle) * npt); - for (i = 0; i < npt; i++) { - prect[i].x = ppt[i].x; - prect[i].y = ppt[i].y; - if (i > 0 && mode == CoordModePrevious) { - prect[i].x += prect[i - 1].x; - prect[i].y += prect[i - 1].y; - } - prect[i].width = 1; - prect[i].height = 1; - } - pGC->ops->PolyFillRect(pDrawable, pGC, npt, prect); - xfree(prect); -} - -/** - * exaPolylines() checks if it can accelerate the lines as a group of - * horizontal or vertical lines (rectangles), and uses existing rectangle fill - * acceleration if so. - */ -static void -exaPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, - DDXPointPtr ppt) -{ - ExaScreenPriv (pDrawable->pScreen); - xRectangle *prect; - int x1, x2, y1, y2; - int i; - - if (pExaScr->fallback_counter) { - ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt); - return; - } - - /* Don't try to do wide lines or non-solid fill style. */ - if (pGC->lineWidth != 0 || pGC->lineStyle != LineSolid || - pGC->fillStyle != FillSolid) { - ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt); - return; - } - - prect = xalloc(sizeof(xRectangle) * (npt - 1)); - x1 = ppt[0].x; - y1 = ppt[0].y; - /* If we have any non-horizontal/vertical, fall back. */ - for (i = 0; i < npt - 1; i++) { - if (mode == CoordModePrevious) { - x2 = x1 + ppt[i + 1].x; - y2 = y1 + ppt[i + 1].y; - } else { - x2 = ppt[i + 1].x; - y2 = ppt[i + 1].y; - } - - if (x1 != x2 && y1 != y2) { - xfree(prect); - ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt); - return; - } - - if (x1 < x2) { - prect[i].x = x1; - prect[i].width = x2 - x1 + 1; - } else { - prect[i].x = x2; - prect[i].width = x1 - x2 + 1; - } - if (y1 < y2) { - prect[i].y = y1; - prect[i].height = y2 - y1 + 1; - } else { - prect[i].y = y2; - prect[i].height = y1 - y2 + 1; - } - - x1 = x2; - y1 = y2; - } - pGC->ops->PolyFillRect(pDrawable, pGC, npt - 1, prect); - xfree(prect); -} - -/** - * exaPolySegment() checks if it can accelerate the lines as a group of - * horizontal or vertical lines (rectangles), and uses existing rectangle fill - * acceleration if so. - */ -static void -exaPolySegment (DrawablePtr pDrawable, GCPtr pGC, int nseg, - xSegment *pSeg) -{ - ExaScreenPriv (pDrawable->pScreen); - xRectangle *prect; - int i; - - /* Don't try to do wide lines or non-solid fill style. */ - if (pExaScr->fallback_counter || pGC->lineWidth != 0 || - pGC->lineStyle != LineSolid || pGC->fillStyle != FillSolid) - { - ExaCheckPolySegment(pDrawable, pGC, nseg, pSeg); - return; - } - - /* If we have any non-horizontal/vertical, fall back. */ - for (i = 0; i < nseg; i++) { - if (pSeg[i].x1 != pSeg[i].x2 && pSeg[i].y1 != pSeg[i].y2) { - ExaCheckPolySegment(pDrawable, pGC, nseg, pSeg); - return; - } - } - - prect = xalloc(sizeof(xRectangle) * nseg); - for (i = 0; i < nseg; i++) { - if (pSeg[i].x1 < pSeg[i].x2) { - prect[i].x = pSeg[i].x1; - prect[i].width = pSeg[i].x2 - pSeg[i].x1 + 1; - } else { - prect[i].x = pSeg[i].x2; - prect[i].width = pSeg[i].x1 - pSeg[i].x2 + 1; - } - if (pSeg[i].y1 < pSeg[i].y2) { - prect[i].y = pSeg[i].y1; - prect[i].height = pSeg[i].y2 - pSeg[i].y1 + 1; - } else { - prect[i].y = pSeg[i].y2; - prect[i].height = pSeg[i].y1 - pSeg[i].y2 + 1; - } - - /* don't paint last pixel */ - if (pGC->capStyle == CapNotLast) { - if (prect[i].width == 1) - prect[i].height--; - else - prect[i].width--; - } - } - pGC->ops->PolyFillRect(pDrawable, pGC, nseg, prect); - xfree(prect); -} - -static Bool exaFillRegionSolid (DrawablePtr pDrawable, RegionPtr pRegion, - Pixel pixel, CARD32 planemask, CARD32 alu, - unsigned int clientClipType); - -static void -exaPolyFillRect(DrawablePtr pDrawable, - GCPtr pGC, - int nrect, - xRectangle *prect) -{ - ExaScreenPriv (pDrawable->pScreen); - RegionPtr pClip = fbGetCompositeClip(pGC); - PixmapPtr pPixmap = exaGetDrawablePixmap(pDrawable); - ExaPixmapPriv (pPixmap); - register BoxPtr pbox; - BoxPtr pextent; - int extentX1, extentX2, extentY1, extentY2; - int fullX1, fullX2, fullY1, fullY2; - int partX1, partX2, partY1, partY2; - int xoff, yoff; - int xorg, yorg; - int n; - RegionPtr pReg = RECTS_TO_REGION(pScreen, nrect, prect, CT_UNSORTED); - - /* Compute intersection of rects and clip region */ - REGION_TRANSLATE(pScreen, pReg, pDrawable->x, pDrawable->y); - REGION_INTERSECT(pScreen, pReg, pClip, pReg); - - if (!REGION_NUM_RECTS(pReg)) { - goto out; - } - - exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff); - - if (pExaScr->fallback_counter || pExaScr->swappedOut || - pExaPixmap->accel_blocked) - { - goto fallback; - } - - /* For ROPs where overlaps don't matter, convert rectangles to region and - * call exaFillRegion{Solid,Tiled}. - */ - if ((pGC->fillStyle == FillSolid || pGC->fillStyle == FillTiled) && - (nrect == 1 || pGC->alu == GXcopy || pGC->alu == GXclear || - pGC->alu == GXnoop || pGC->alu == GXcopyInverted || - pGC->alu == GXset)) { - if (((pGC->fillStyle == FillSolid || pGC->tileIsPixel) && - exaFillRegionSolid(pDrawable, pReg, pGC->fillStyle == FillSolid ? - pGC->fgPixel : pGC->tile.pixel, pGC->planemask, - pGC->alu, pGC->clientClipType)) || - (pGC->fillStyle == FillTiled && !pGC->tileIsPixel && - exaFillRegionTiled(pDrawable, pReg, pGC->tile.pixmap, &pGC->patOrg, - pGC->planemask, pGC->alu, - pGC->clientClipType))) { - goto out; - } - } - - if (pGC->fillStyle != FillSolid && - !(pGC->tileIsPixel && pGC->fillStyle == FillTiled)) - { - goto fallback; - } - - if (pExaScr->do_migration) { - ExaMigrationRec pixmaps[1]; - - pixmaps[0].as_dst = TRUE; - pixmaps[0].as_src = FALSE; - pixmaps[0].pPix = pPixmap; - pixmaps[0].pReg = NULL; - - exaDoMigration (pixmaps, 1, TRUE); - } - - if (!exaPixmapHasGpuCopy (pPixmap) || - !(*pExaScr->info->PrepareSolid) (pPixmap, - pGC->alu, - pGC->planemask, - pGC->fgPixel)) - { -fallback: - ExaCheckPolyFillRect (pDrawable, pGC, nrect, prect); - goto out; - } - - xorg = pDrawable->x; - yorg = pDrawable->y; - - pextent = REGION_EXTENTS(pGC->pScreen, pClip); - extentX1 = pextent->x1; - extentY1 = pextent->y1; - extentX2 = pextent->x2; - extentY2 = pextent->y2; - while (nrect--) - { - fullX1 = prect->x + xorg; - fullY1 = prect->y + yorg; - fullX2 = fullX1 + (int) prect->width; - fullY2 = fullY1 + (int) prect->height; - prect++; - - if (fullX1 < extentX1) - fullX1 = extentX1; - - if (fullY1 < extentY1) - fullY1 = extentY1; - - if (fullX2 > extentX2) - fullX2 = extentX2; - - if (fullY2 > extentY2) - fullY2 = extentY2; - - if ((fullX1 >= fullX2) || (fullY1 >= fullY2)) - continue; - n = REGION_NUM_RECTS (pClip); - if (n == 1) - { - (*pExaScr->info->Solid) (pPixmap, - fullX1 + xoff, fullY1 + yoff, - fullX2 + xoff, fullY2 + yoff); - } - else - { - pbox = REGION_RECTS(pClip); - /* - * clip the rectangle to each box in the clip region - * this is logically equivalent to calling Intersect(), - * but rectangles may overlap each other here. - */ - while(n--) - { - partX1 = pbox->x1; - if (partX1 < fullX1) - partX1 = fullX1; - partY1 = pbox->y1; - if (partY1 < fullY1) - partY1 = fullY1; - partX2 = pbox->x2; - if (partX2 > fullX2) - partX2 = fullX2; - partY2 = pbox->y2; - if (partY2 > fullY2) - partY2 = fullY2; - - pbox++; - - if (partX1 < partX2 && partY1 < partY2) { - (*pExaScr->info->Solid) (pPixmap, - partX1 + xoff, partY1 + yoff, - partX2 + xoff, partY2 + yoff); - } - } - } - } - (*pExaScr->info->DoneSolid) (pPixmap); - exaMarkSync(pDrawable->pScreen); - -out: - REGION_UNINIT(pScreen, pReg); - REGION_DESTROY(pScreen, pReg); -} - -const GCOps exaOps = { - exaFillSpans, - ExaCheckSetSpans, - exaPutImage, - exaCopyArea, - ExaCheckCopyPlane, - exaPolyPoint, - exaPolylines, - exaPolySegment, - miPolyRectangle, - ExaCheckPolyArc, - miFillPolygon, - exaPolyFillRect, - miPolyFillArc, - miPolyText8, - miPolyText16, - miImageText8, - miImageText16, - ExaCheckImageGlyphBlt, - ExaCheckPolyGlyphBlt, - ExaCheckPushPixels, -}; - -void -exaCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc) -{ - RegionRec rgnDst; - int dx, dy; - PixmapPtr pPixmap = (*pWin->drawable.pScreen->GetWindowPixmap) (pWin); - ExaScreenPriv(pWin->drawable.pScreen); - - dx = ptOldOrg.x - pWin->drawable.x; - dy = ptOldOrg.y - pWin->drawable.y; - REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, -dx, -dy); - - REGION_INIT (pWin->drawable.pScreen, &rgnDst, NullBox, 0); - - REGION_INTERSECT(pWin->drawable.pScreen, &rgnDst, &pWin->borderClip, prgnSrc); -#ifdef COMPOSITE - if (pPixmap->screen_x || pPixmap->screen_y) - REGION_TRANSLATE (pWin->drawable.pScreen, &rgnDst, - -pPixmap->screen_x, -pPixmap->screen_y); -#endif - - if (pExaScr->fallback_counter) { - pExaScr->fallback_flags |= EXA_FALLBACK_COPYWINDOW; - goto fallback; - } - - pExaScr->fallback_flags |= EXA_ACCEL_COPYWINDOW; - miCopyRegion (&pPixmap->drawable, &pPixmap->drawable, - NULL, - &rgnDst, dx, dy, exaCopyNtoN, 0, NULL); - pExaScr->fallback_flags &= ~EXA_ACCEL_COPYWINDOW; - -fallback: - REGION_UNINIT(pWin->drawable.pScreen, &rgnDst); - - if (pExaScr->fallback_flags & EXA_FALLBACK_COPYWINDOW) { - pExaScr->fallback_flags &= ~EXA_FALLBACK_COPYWINDOW; - REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, dx, dy); - ExaCheckCopyWindow(pWin, ptOldOrg, prgnSrc); - } -} - -static Bool -exaFillRegionSolid (DrawablePtr pDrawable, RegionPtr pRegion, Pixel pixel, - CARD32 planemask, CARD32 alu, unsigned int clientClipType) -{ - ExaScreenPriv(pDrawable->pScreen); - PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable); - ExaPixmapPriv (pPixmap); - int xoff, yoff; - Bool ret = FALSE; - - exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff); - REGION_TRANSLATE(pScreen, pRegion, xoff, yoff); - - if (pExaScr->fallback_counter || pExaPixmap->accel_blocked) - goto out; - - if (pExaScr->do_migration) { - ExaMigrationRec pixmaps[1]; - - pixmaps[0].as_dst = TRUE; - pixmaps[0].as_src = FALSE; - pixmaps[0].pPix = pPixmap; - pixmaps[0].pReg = exaGCReadsDestination(pDrawable, planemask, FillSolid, - alu, clientClipType) ? NULL : pRegion; - - exaDoMigration (pixmaps, 1, TRUE); - } - - if (exaPixmapHasGpuCopy (pPixmap) && - (*pExaScr->info->PrepareSolid) (pPixmap, alu, planemask, pixel)) - { - int nbox; - BoxPtr pBox; - - nbox = REGION_NUM_RECTS (pRegion); - pBox = REGION_RECTS (pRegion); - - while (nbox--) - { - (*pExaScr->info->Solid) (pPixmap, pBox->x1, pBox->y1, pBox->x2, - pBox->y2); - pBox++; - } - (*pExaScr->info->DoneSolid) (pPixmap); - exaMarkSync(pDrawable->pScreen); - - if (pExaPixmap->pDamage && - pExaPixmap->sys_ptr && pDrawable->type == DRAWABLE_PIXMAP && - pDrawable->width == 1 && pDrawable->height == 1 && - pDrawable->bitsPerPixel != 24) { - ExaPixmapPriv(pPixmap); - RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage); - - switch (pDrawable->bitsPerPixel) { - case 32: - *(CARD32*)pExaPixmap->sys_ptr = pixel; - break; - case 16: - *(CARD16*)pExaPixmap->sys_ptr = pixel; - break; - case 8: - *(CARD8*)pExaPixmap->sys_ptr = pixel; - } - - REGION_UNION(pScreen, &pExaPixmap->validSys, &pExaPixmap->validSys, - pRegion); - REGION_UNION(pScreen, &pExaPixmap->validFB, &pExaPixmap->validFB, - pRegion); - REGION_SUBTRACT(pScreen, pending_damage, pending_damage, pRegion); - } - - ret = TRUE; - } - -out: - REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff); - - return ret; -} - -/* Try to do an accelerated tile of the pTile into pRegion of pDrawable. - * Based on fbFillRegionTiled(), fbTile(). - */ -Bool -exaFillRegionTiled (DrawablePtr pDrawable, RegionPtr pRegion, PixmapPtr pTile, - DDXPointPtr pPatOrg, CARD32 planemask, CARD32 alu, - unsigned int clientClipType) -{ - ExaScreenPriv(pDrawable->pScreen); - PixmapPtr pPixmap; - ExaPixmapPrivPtr pExaPixmap; - ExaPixmapPrivPtr pTileExaPixmap = ExaGetPixmapPriv(pTile); - int xoff, yoff; - int tileWidth, tileHeight; - int nbox = REGION_NUM_RECTS (pRegion); - BoxPtr pBox = REGION_RECTS (pRegion); - Bool ret = FALSE; - int i; - - tileWidth = pTile->drawable.width; - tileHeight = pTile->drawable.height; - - /* If we're filling with a solid color, grab it out and go to - * FillRegionSolid, saving numerous copies. - */ - if (tileWidth == 1 && tileHeight == 1) - return exaFillRegionSolid(pDrawable, pRegion, - exaGetPixmapFirstPixel (pTile), planemask, - alu, clientClipType); - - pPixmap = exaGetDrawablePixmap (pDrawable); - pExaPixmap = ExaGetPixmapPriv (pPixmap); - - if (pExaScr->fallback_counter || pExaPixmap->accel_blocked || - pTileExaPixmap->accel_blocked) - return FALSE; - - if (pExaScr->do_migration) { - ExaMigrationRec pixmaps[2]; - - pixmaps[0].as_dst = TRUE; - pixmaps[0].as_src = FALSE; - pixmaps[0].pPix = pPixmap; - pixmaps[0].pReg = exaGCReadsDestination(pDrawable, planemask, FillTiled, - alu, clientClipType) ? NULL : pRegion; - pixmaps[1].as_dst = FALSE; - pixmaps[1].as_src = TRUE; - pixmaps[1].pPix = pTile; - pixmaps[1].pReg = NULL; - - exaDoMigration (pixmaps, 2, TRUE); - } - - pPixmap = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff); - - if (!pPixmap || !exaPixmapHasGpuCopy(pTile)) - return FALSE; - - if ((*pExaScr->info->PrepareCopy) (pTile, pPixmap, 1, 1, alu, planemask)) - { - if (xoff || yoff) - REGION_TRANSLATE(pScreen, pRegion, xoff, yoff); - - for (i = 0; i < nbox; i++) - { - int height = pBox[i].y2 - pBox[i].y1; - int dstY = pBox[i].y1; - int tileY; - - if (alu == GXcopy) - height = min(height, tileHeight); - - modulus(dstY - yoff - pDrawable->y - pPatOrg->y, tileHeight, tileY); - - while (height > 0) { - int width = pBox[i].x2 - pBox[i].x1; - int dstX = pBox[i].x1; - int tileX; - int h = tileHeight - tileY; - - if (alu == GXcopy) - width = min(width, tileWidth); - - if (h > height) - h = height; - height -= h; - - modulus(dstX - xoff - pDrawable->x - pPatOrg->x, tileWidth, - tileX); - - while (width > 0) { - int w = tileWidth - tileX; - if (w > width) - w = width; - width -= w; - - (*pExaScr->info->Copy) (pPixmap, tileX, tileY, dstX, dstY, - w, h); - dstX += w; - tileX = 0; - } - dstY += h; - tileY = 0; - } - } - (*pExaScr->info->DoneCopy) (pPixmap); - - /* With GXcopy, we only need to do the basic algorithm up to the tile - * size; then, we can just keep doubling the destination in each - * direction until it fills the box. This way, the number of copy - * operations is O(log(rx)) + O(log(ry)) instead of O(rx * ry), where - * rx/ry is the ratio between box and tile width/height. This can make - * a big difference if each driver copy incurs a significant constant - * overhead. - */ - if (alu != GXcopy) - ret = TRUE; - else { - Bool more_copy = FALSE; - - for (i = 0; i < nbox; i++) { - int dstX = pBox[i].x1 + tileWidth; - int dstY = pBox[i].y1 + tileHeight; - - if ((dstX < pBox[i].x2) || (dstY < pBox[i].y2)) { - more_copy = TRUE; - break; - } - } - - if (more_copy == FALSE) - ret = TRUE; - - if (more_copy && (*pExaScr->info->PrepareCopy) (pPixmap, pPixmap, - 1, 1, alu, planemask)) { - for (i = 0; i < nbox; i++) - { - int dstX = pBox[i].x1 + tileWidth; - int dstY = pBox[i].y1 + tileHeight; - int width = min(pBox[i].x2 - dstX, tileWidth); - int height = min(pBox[i].y2 - pBox[i].y1, tileHeight); - - while (dstX < pBox[i].x2) { - (*pExaScr->info->Copy) (pPixmap, pBox[i].x1, pBox[i].y1, - dstX, pBox[i].y1, width, height); - dstX += width; - width = min(pBox[i].x2 - dstX, width * 2); - } - - width = pBox[i].x2 - pBox[i].x1; - height = min(pBox[i].y2 - dstY, tileHeight); - - while (dstY < pBox[i].y2) { - (*pExaScr->info->Copy) (pPixmap, pBox[i].x1, pBox[i].y1, - pBox[i].x1, dstY, width, height); - dstY += height; - height = min(pBox[i].y2 - dstY, height * 2); - } - } - - (*pExaScr->info->DoneCopy) (pPixmap); - - ret = TRUE; - } - } - - exaMarkSync(pDrawable->pScreen); - - if (xoff || yoff) - REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff); - } - - return ret; -} - - -/** - * Accelerates GetImage for solid ZPixmap downloads from framebuffer memory. - * - * This is probably the only case we actually care about. The rest fall through - * to migration and fbGetImage, which hopefully will result in migration pushing - * the pixmap out of framebuffer. - */ -void -exaGetImage (DrawablePtr pDrawable, int x, int y, int w, int h, - unsigned int format, unsigned long planeMask, char *d) -{ - ExaScreenPriv (pDrawable->pScreen); - PixmapPtr pPix = exaGetDrawablePixmap (pDrawable); - ExaPixmapPriv(pPix); - int xoff, yoff; - Bool ok; - - if (pExaScr->fallback_counter || pExaScr->swappedOut) - goto fallback; - - /* If there's a system copy, we want to save the result there */ - if (pExaPixmap->pDamage) - goto fallback; - - pPix = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff); - - if (pPix == NULL || pExaScr->info->DownloadFromScreen == NULL) - goto fallback; - - /* Only cover the ZPixmap, solid copy case. */ - if (format != ZPixmap || !EXA_PM_IS_SOLID(pDrawable, planeMask)) - goto fallback; - - /* Only try to handle the 8bpp and up cases, since we don't want to think - * about <8bpp. - */ - if (pDrawable->bitsPerPixel < 8) - goto fallback; - - ok = pExaScr->info->DownloadFromScreen(pPix, pDrawable->x + x + xoff, - pDrawable->y + y + yoff, w, h, d, - PixmapBytePad(w, pDrawable->depth)); - if (ok) { - exaWaitSync(pDrawable->pScreen); - return; - } - -fallback: - ExaCheckGetImage(pDrawable, x, y, w, h, format, planeMask, d); -} +/*
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ * Michel Dänzer <michel@tungstengraphics.com>
+ *
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+#include "exa_priv.h"
+#include <X11/fonts/fontstruct.h>
+#include "dixfontstr.h"
+#include "exa.h"
+
+static void
+exaFillSpans(DrawablePtr pDrawable, GCPtr pGC, int n,
+ DDXPointPtr ppt, int *pwidth, int fSorted)
+{
+ ScreenPtr pScreen = pDrawable->pScreen;
+ ExaScreenPriv (pScreen);
+ RegionPtr pClip = fbGetCompositeClip(pGC);
+ PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable);
+ ExaPixmapPriv (pPixmap);
+ BoxPtr pextent, pbox;
+ int nbox;
+ int extentX1, extentX2, extentY1, extentY2;
+ int fullX1, fullX2, fullY1;
+ int partX1, partX2;
+ int off_x, off_y;
+
+ if (pExaScr->fallback_counter ||
+ pExaScr->swappedOut ||
+ pGC->fillStyle != FillSolid ||
+ pExaPixmap->accel_blocked)
+ {
+ ExaCheckFillSpans (pDrawable, pGC, n, ppt, pwidth, fSorted);
+ return;
+ }
+
+ if (pExaScr->do_migration) {
+ ExaMigrationRec pixmaps[1];
+
+ pixmaps[0].as_dst = TRUE;
+ pixmaps[0].as_src = FALSE;
+ pixmaps[0].pPix = pPixmap;
+ pixmaps[0].pReg = NULL;
+
+ exaDoMigration (pixmaps, 1, TRUE);
+ }
+
+ if (!(pPixmap = exaGetOffscreenPixmap (pDrawable, &off_x, &off_y)) ||
+ !(*pExaScr->info->PrepareSolid) (pPixmap,
+ pGC->alu,
+ pGC->planemask,
+ pGC->fgPixel))
+ {
+ ExaCheckFillSpans (pDrawable, pGC, n, ppt, pwidth, fSorted);
+ return;
+ }
+
+ pextent = REGION_EXTENTS(pGC->pScreen, pClip);
+ extentX1 = pextent->x1;
+ extentY1 = pextent->y1;
+ extentX2 = pextent->x2;
+ extentY2 = pextent->y2;
+ while (n--)
+ {
+ fullX1 = ppt->x;
+ fullY1 = ppt->y;
+ fullX2 = fullX1 + (int) *pwidth;
+ ppt++;
+ pwidth++;
+
+ if (fullY1 < extentY1 || extentY2 <= fullY1)
+ continue;
+
+ if (fullX1 < extentX1)
+ fullX1 = extentX1;
+
+ if (fullX2 > extentX2)
+ fullX2 = extentX2;
+
+ if (fullX1 >= fullX2)
+ continue;
+
+ nbox = REGION_NUM_RECTS (pClip);
+ if (nbox == 1)
+ {
+ (*pExaScr->info->Solid) (pPixmap,
+ fullX1 + off_x, fullY1 + off_y,
+ fullX2 + off_x, fullY1 + 1 + off_y);
+ }
+ else
+ {
+ pbox = REGION_RECTS(pClip);
+ while(nbox--)
+ {
+ if (pbox->y1 <= fullY1 && fullY1 < pbox->y2)
+ {
+ partX1 = pbox->x1;
+ if (partX1 < fullX1)
+ partX1 = fullX1;
+ partX2 = pbox->x2;
+ if (partX2 > fullX2)
+ partX2 = fullX2;
+ if (partX2 > partX1) {
+ (*pExaScr->info->Solid) (pPixmap,
+ partX1 + off_x, fullY1 + off_y,
+ partX2 + off_x, fullY1 + 1 + off_y);
+ }
+ }
+ pbox++;
+ }
+ }
+ }
+ (*pExaScr->info->DoneSolid) (pPixmap);
+ exaMarkSync(pScreen);
+}
+
+static Bool
+exaDoPutImage (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y,
+ int w, int h, int format, char *bits, int src_stride)
+{
+ ExaScreenPriv (pDrawable->pScreen);
+ PixmapPtr pPix = exaGetDrawablePixmap (pDrawable);
+ ExaPixmapPriv(pPix);
+ RegionPtr pClip;
+ BoxPtr pbox;
+ int nbox;
+ int xoff, yoff;
+ int bpp = pDrawable->bitsPerPixel;
+ Bool ret = TRUE;
+
+ if (pExaScr->fallback_counter || pExaPixmap->accel_blocked || !pExaScr->info->UploadToScreen)
+ return FALSE;
+
+ /* If there's a system copy, we want to save the result there */
+ if (pExaPixmap->pDamage)
+ return FALSE;
+
+ /* Don't bother with under 8bpp, XYPixmaps. */
+ if (format != ZPixmap || bpp < 8)
+ return FALSE;
+
+ /* Only accelerate copies: no rop or planemask. */
+ if (!EXA_PM_IS_SOLID(pDrawable, pGC->planemask) || pGC->alu != GXcopy)
+ return FALSE;
+
+ if (pExaScr->swappedOut)
+ return FALSE;
+
+ if (pExaScr->do_migration) {
+ ExaMigrationRec pixmaps[1];
+
+ pixmaps[0].as_dst = TRUE;
+ pixmaps[0].as_src = FALSE;
+ pixmaps[0].pPix = pPix;
+ pixmaps[0].pReg = DamagePendingRegion(pExaPixmap->pDamage);
+
+ exaDoMigration (pixmaps, 1, TRUE);
+ }
+
+ pPix = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff);
+
+ if (!pPix)
+ return FALSE;
+
+ x += pDrawable->x;
+ y += pDrawable->y;
+
+ pClip = fbGetCompositeClip(pGC);
+ for (nbox = REGION_NUM_RECTS(pClip),
+ pbox = REGION_RECTS(pClip);
+ nbox--;
+ pbox++)
+ {
+ int x1 = x;
+ int y1 = y;
+ int x2 = x + w;
+ int y2 = y + h;
+ char *src;
+ Bool ok;
+
+ if (x1 < pbox->x1)
+ x1 = pbox->x1;
+ if (y1 < pbox->y1)
+ y1 = pbox->y1;
+ if (x2 > pbox->x2)
+ x2 = pbox->x2;
+ if (y2 > pbox->y2)
+ y2 = pbox->y2;
+ if (x1 >= x2 || y1 >= y2)
+ continue;
+
+ src = bits + (y1 - y) * src_stride + (x1 - x) * (bpp / 8);
+ ok = pExaScr->info->UploadToScreen(pPix, x1 + xoff, y1 + yoff,
+ x2 - x1, y2 - y1, src, src_stride);
+ /* We have to fall back completely, and ignore what has already been completed.
+ * Messing with the fb layer directly like we used to is completely unacceptable.
+ */
+ if (!ok) {
+ ret = FALSE;
+ break;
+ }
+ }
+
+ if (ret)
+ exaMarkSync(pDrawable->pScreen);
+
+ return ret;
+}
+
+static void
+exaPutImage (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y,
+ int w, int h, int leftPad, int format, char *bits)
+{
+ if (!exaDoPutImage(pDrawable, pGC, depth, x, y, w, h, format, bits,
+ PixmapBytePad(w, pDrawable->depth)))
+ ExaCheckPutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format,
+ bits);
+}
+
+static Bool inline
+exaCopyNtoNTwoDir (DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable,
+ GCPtr pGC, BoxPtr pbox, int nbox, int dx, int dy)
+{
+ ExaScreenPriv (pDstDrawable->pScreen);
+ PixmapPtr pSrcPixmap, pDstPixmap;
+ int src_off_x, src_off_y, dst_off_x, dst_off_y;
+ int dirsetup;
+
+ /* Need to get both pixmaps to call the driver routines */
+ pSrcPixmap = exaGetOffscreenPixmap (pSrcDrawable, &src_off_x, &src_off_y);
+ pDstPixmap = exaGetOffscreenPixmap (pDstDrawable, &dst_off_x, &dst_off_y);
+ if (!pSrcPixmap || !pDstPixmap)
+ return FALSE;
+
+ /*
+ * Now the case of a chip that only supports xdir = ydir = 1 or
+ * xdir = ydir = -1, but we have xdir != ydir.
+ */
+ dirsetup = 0; /* No direction set up yet. */
+ for (; nbox; pbox++, nbox--) {
+ if (dx >= 0 && (src_off_y + pbox->y1 + dy) != pbox->y1) {
+ /* Do a xdir = ydir = -1 blit instead. */
+ if (dirsetup != -1) {
+ if (dirsetup != 0)
+ pExaScr->info->DoneCopy(pDstPixmap);
+ dirsetup = -1;
+ if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap,
+ pDstPixmap,
+ -1, -1,
+ pGC ? pGC->alu : GXcopy,
+ pGC ? pGC->planemask :
+ FB_ALLONES))
+ return FALSE;
+ }
+ (*pExaScr->info->Copy)(pDstPixmap,
+ src_off_x + pbox->x1 + dx,
+ src_off_y + pbox->y1 + dy,
+ dst_off_x + pbox->x1,
+ dst_off_y + pbox->y1,
+ pbox->x2 - pbox->x1,
+ pbox->y2 - pbox->y1);
+ } else if (dx < 0 && (src_off_y + pbox->y1 + dy) != pbox->y1) {
+ /* Do a xdir = ydir = 1 blit instead. */
+ if (dirsetup != 1) {
+ if (dirsetup != 0)
+ pExaScr->info->DoneCopy(pDstPixmap);
+ dirsetup = 1;
+ if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap,
+ pDstPixmap,
+ 1, 1,
+ pGC ? pGC->alu : GXcopy,
+ pGC ? pGC->planemask :
+ FB_ALLONES))
+ return FALSE;
+ }
+ (*pExaScr->info->Copy)(pDstPixmap,
+ src_off_x + pbox->x1 + dx,
+ src_off_y + pbox->y1 + dy,
+ dst_off_x + pbox->x1,
+ dst_off_y + pbox->y1,
+ pbox->x2 - pbox->x1,
+ pbox->y2 - pbox->y1);
+ } else if (dx >= 0) {
+ /*
+ * xdir = 1, ydir = -1.
+ * Perform line-by-line xdir = ydir = 1 blits, going up.
+ */
+ int i;
+ if (dirsetup != 1) {
+ if (dirsetup != 0)
+ pExaScr->info->DoneCopy(pDstPixmap);
+ dirsetup = 1;
+ if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap,
+ pDstPixmap,
+ 1, 1,
+ pGC ? pGC->alu : GXcopy,
+ pGC ? pGC->planemask :
+ FB_ALLONES))
+ return FALSE;
+ }
+ for (i = pbox->y2 - pbox->y1 - 1; i >= 0; i--)
+ (*pExaScr->info->Copy)(pDstPixmap,
+ src_off_x + pbox->x1 + dx,
+ src_off_y + pbox->y1 + dy + i,
+ dst_off_x + pbox->x1,
+ dst_off_y + pbox->y1 + i,
+ pbox->x2 - pbox->x1, 1);
+ } else {
+ /*
+ * xdir = -1, ydir = 1.
+ * Perform line-by-line xdir = ydir = -1 blits, going down.
+ */
+ int i;
+ if (dirsetup != -1) {
+ if (dirsetup != 0)
+ pExaScr->info->DoneCopy(pDstPixmap);
+ dirsetup = -1;
+ if (!(*pExaScr->info->PrepareCopy)(pSrcPixmap,
+ pDstPixmap,
+ -1, -1,
+ pGC ? pGC->alu : GXcopy,
+ pGC ? pGC->planemask :
+ FB_ALLONES))
+ return FALSE;
+ }
+ for (i = 0; i < pbox->y2 - pbox->y1; i++)
+ (*pExaScr->info->Copy)(pDstPixmap,
+ src_off_x + pbox->x1 + dx,
+ src_off_y + pbox->y1 + dy + i,
+ dst_off_x + pbox->x1,
+ dst_off_y + pbox->y1 + i,
+ pbox->x2 - pbox->x1, 1);
+ }
+ }
+ if (dirsetup != 0)
+ pExaScr->info->DoneCopy(pDstPixmap);
+ exaMarkSync(pDstDrawable->pScreen);
+ return TRUE;
+}
+
+Bool
+exaHWCopyNtoN (DrawablePtr pSrcDrawable,
+ DrawablePtr pDstDrawable,
+ GCPtr pGC,
+ BoxPtr pbox,
+ int nbox,
+ int dx,
+ int dy,
+ Bool reverse,
+ Bool upsidedown)
+{
+ ExaScreenPriv (pDstDrawable->pScreen);
+ PixmapPtr pSrcPixmap, pDstPixmap;
+ ExaPixmapPrivPtr pSrcExaPixmap, pDstExaPixmap;
+ int src_off_x, src_off_y;
+ int dst_off_x, dst_off_y;
+ RegionPtr srcregion = NULL, dstregion = NULL;
+ xRectangle *rects;
+ Bool ret = TRUE;
+
+ /* avoid doing copy operations if no boxes */
+ if (nbox == 0)
+ return TRUE;
+
+ pSrcPixmap = exaGetDrawablePixmap (pSrcDrawable);
+ pDstPixmap = exaGetDrawablePixmap (pDstDrawable);
+
+ exaGetDrawableDeltas (pSrcDrawable, pSrcPixmap, &src_off_x, &src_off_y);
+ exaGetDrawableDeltas (pDstDrawable, pDstPixmap, &dst_off_x, &dst_off_y);
+
+ rects = malloc(nbox * sizeof(xRectangle));
+
+ if (rects) {
+ int i;
+ int ordering;
+
+ for (i = 0; i < nbox; i++) {
+ rects[i].x = pbox[i].x1 + dx + src_off_x;
+ rects[i].y = pbox[i].y1 + dy + src_off_y;
+ rects[i].width = pbox[i].x2 - pbox[i].x1;
+ rects[i].height = pbox[i].y2 - pbox[i].y1;
+ }
+
+ /* This must match the miRegionCopy() logic for reversing rect order */
+ if (nbox == 1 || (dx > 0 && dy > 0) ||
+ (pDstDrawable != pSrcDrawable &&
+ (pDstDrawable->type != DRAWABLE_WINDOW ||
+ pSrcDrawable->type != DRAWABLE_WINDOW)))
+ ordering = CT_YXBANDED;
+ else
+ ordering = CT_UNSORTED;
+
+ srcregion = RECTS_TO_REGION(pScreen, nbox, rects, ordering);
+ free(rects);
+
+ if (!pGC || !exaGCReadsDestination(pDstDrawable, pGC->planemask,
+ pGC->fillStyle, pGC->alu,
+ pGC->clientClipType)) {
+ dstregion = REGION_CREATE(pScreen, NullBox, 0);
+ REGION_COPY(pScreen, dstregion, srcregion);
+ REGION_TRANSLATE(pScreen, dstregion, dst_off_x - dx - src_off_x,
+ dst_off_y - dy - src_off_y);
+ }
+ }
+
+
+ pSrcExaPixmap = ExaGetPixmapPriv (pSrcPixmap);
+ pDstExaPixmap = ExaGetPixmapPriv (pDstPixmap);
+
+ /* Check whether the accelerator can use this pixmap.
+ * If the pitch of the pixmaps is out of range, there's nothing
+ * we can do but fall back to software rendering.
+ */
+ if (pSrcExaPixmap->accel_blocked & EXA_RANGE_PITCH ||
+ pDstExaPixmap->accel_blocked & EXA_RANGE_PITCH)
+ goto fallback;
+
+ /* If the width or the height of either of the pixmaps
+ * is out of range, check whether the boxes are actually out of the
+ * addressable range as well. If they aren't, we can still do
+ * the copying in hardware.
+ */
+ if (pSrcExaPixmap->accel_blocked || pDstExaPixmap->accel_blocked) {
+ int i;
+
+ for (i = 0; i < nbox; i++) {
+ /* src */
+ if ((pbox[i].x2 + dx + src_off_x) >= pExaScr->info->maxX ||
+ (pbox[i].y2 + dy + src_off_y) >= pExaScr->info->maxY)
+ goto fallback;
+
+ /* dst */
+ if ((pbox[i].x2 + dst_off_x) >= pExaScr->info->maxX ||
+ (pbox[i].y2 + dst_off_y) >= pExaScr->info->maxY)
+ goto fallback;
+ }
+ }
+
+ if (pExaScr->do_migration) {
+ ExaMigrationRec pixmaps[2];
+
+ pixmaps[0].as_dst = TRUE;
+ pixmaps[0].as_src = FALSE;
+ pixmaps[0].pPix = pDstPixmap;
+ pixmaps[0].pReg = dstregion;
+ pixmaps[1].as_dst = FALSE;
+ pixmaps[1].as_src = TRUE;
+ pixmaps[1].pPix = pSrcPixmap;
+ pixmaps[1].pReg = srcregion;
+
+ exaDoMigration (pixmaps, 2, TRUE);
+ }
+
+ /* Mixed directions must be handled specially if the card is lame */
+ if ((pExaScr->info->flags & EXA_TWO_BITBLT_DIRECTIONS) &&
+ reverse != upsidedown) {
+ if (exaCopyNtoNTwoDir(pSrcDrawable, pDstDrawable, pGC, pbox, nbox,
+ dx, dy))
+ goto out;
+ goto fallback;
+ }
+
+ if (exaPixmapHasGpuCopy(pDstPixmap)) {
+ /* Normal blitting. */
+ if (exaPixmapHasGpuCopy(pSrcPixmap)) {
+ if (!(*pExaScr->info->PrepareCopy) (pSrcPixmap, pDstPixmap, reverse ? -1 : 1,
+ upsidedown ? -1 : 1,
+ pGC ? pGC->alu : GXcopy,
+ pGC ? pGC->planemask : FB_ALLONES)) {
+ goto fallback;
+ }
+
+ while (nbox--)
+ {
+ (*pExaScr->info->Copy) (pDstPixmap,
+ pbox->x1 + dx + src_off_x,
+ pbox->y1 + dy + src_off_y,
+ pbox->x1 + dst_off_x, pbox->y1 + dst_off_y,
+ pbox->x2 - pbox->x1, pbox->y2 - pbox->y1);
+ pbox++;
+ }
+
+ (*pExaScr->info->DoneCopy) (pDstPixmap);
+ exaMarkSync (pDstDrawable->pScreen);
+ /* UTS: mainly for SHM PutImage's secondary path.
+ *
+ * Only taking this path for directly accessible pixmaps.
+ */
+ } else if (!pDstExaPixmap->pDamage && pSrcExaPixmap->sys_ptr) {
+ int bpp = pSrcDrawable->bitsPerPixel;
+ int src_stride = exaGetPixmapPitch(pSrcPixmap);
+ CARD8 *src = NULL;
+
+ if (!pExaScr->info->UploadToScreen)
+ goto fallback;
+
+ if (pSrcDrawable->bitsPerPixel != pDstDrawable->bitsPerPixel)
+ goto fallback;
+
+ if (pSrcDrawable->bitsPerPixel < 8)
+ goto fallback;
+
+ if (pGC && !(pGC->alu == GXcopy && EXA_PM_IS_SOLID(pSrcDrawable, pGC->planemask)))
+ goto fallback;
+
+ while (nbox--)
+ {
+ src = pSrcExaPixmap->sys_ptr + (pbox->y1 + dy + src_off_y) * src_stride + (pbox->x1 + dx + src_off_x) * (bpp / 8);
+ if (!pExaScr->info->UploadToScreen(pDstPixmap, pbox->x1 + dst_off_x,
+ pbox->y1 + dst_off_y, pbox->x2 - pbox->x1, pbox->y2 - pbox->y1,
+ (char *) src, src_stride))
+ goto fallback;
+
+ pbox++;
+ }
+ } else
+ goto fallback;
+ } else
+ goto fallback;
+
+ goto out;
+
+fallback:
+ ret = FALSE;
+
+out:
+ if (dstregion) {
+ REGION_UNINIT(pScreen, dstregion);
+ REGION_DESTROY(pScreen, dstregion);
+ }
+ if (srcregion) {
+ REGION_UNINIT(pScreen, srcregion);
+ REGION_DESTROY(pScreen, srcregion);
+ }
+
+ return ret;
+}
+
+void
+exaCopyNtoN (DrawablePtr pSrcDrawable,
+ DrawablePtr pDstDrawable,
+ GCPtr pGC,
+ BoxPtr pbox,
+ int nbox,
+ int dx,
+ int dy,
+ Bool reverse,
+ Bool upsidedown,
+ Pixel bitplane,
+ void *closure)
+{
+ ExaScreenPriv(pDstDrawable->pScreen);
+
+ if (pExaScr->fallback_counter ||
+ (pExaScr->fallback_flags & EXA_FALLBACK_COPYWINDOW))
+ return;
+
+ if (exaHWCopyNtoN(pSrcDrawable, pDstDrawable, pGC, pbox, nbox, dx, dy, reverse, upsidedown))
+ return;
+
+ /* This is a CopyWindow, it's cleaner to fallback at the original call. */
+ if (pExaScr->fallback_flags & EXA_ACCEL_COPYWINDOW) {
+ pExaScr->fallback_flags |= EXA_FALLBACK_COPYWINDOW;
+ return;
+ }
+
+ /* fallback */
+ ExaCheckCopyNtoN(pSrcDrawable, pDstDrawable, pGC, pbox, nbox, dx, dy, reverse, upsidedown, bitplane, closure);
+}
+
+RegionPtr
+exaCopyArea(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC,
+ int srcx, int srcy, int width, int height, int dstx, int dsty)
+{
+ ExaScreenPriv (pDstDrawable->pScreen);
+
+ if (pExaScr->fallback_counter || pExaScr->swappedOut) {
+ return ExaCheckCopyArea(pSrcDrawable, pDstDrawable, pGC,
+ srcx, srcy, width, height, dstx, dsty);
+ }
+
+ return miDoCopy (pSrcDrawable, pDstDrawable, pGC,
+ srcx, srcy, width, height,
+ dstx, dsty, exaCopyNtoN, 0, NULL);
+}
+
+static void
+exaPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt,
+ DDXPointPtr ppt)
+{
+ ExaScreenPriv (pDrawable->pScreen);
+ int i;
+ xRectangle *prect;
+
+ /* If we can't reuse the current GC as is, don't bother accelerating the
+ * points.
+ */
+ if (pExaScr->fallback_counter || pGC->fillStyle != FillSolid) {
+ ExaCheckPolyPoint(pDrawable, pGC, mode, npt, ppt);
+ return;
+ }
+
+ prect = malloc(sizeof(xRectangle) * npt);
+ for (i = 0; i < npt; i++) {
+ prect[i].x = ppt[i].x;
+ prect[i].y = ppt[i].y;
+ if (i > 0 && mode == CoordModePrevious) {
+ prect[i].x += prect[i - 1].x;
+ prect[i].y += prect[i - 1].y;
+ }
+ prect[i].width = 1;
+ prect[i].height = 1;
+ }
+ pGC->ops->PolyFillRect(pDrawable, pGC, npt, prect);
+ free(prect);
+}
+
+/**
+ * exaPolylines() checks if it can accelerate the lines as a group of
+ * horizontal or vertical lines (rectangles), and uses existing rectangle fill
+ * acceleration if so.
+ */
+static void
+exaPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt,
+ DDXPointPtr ppt)
+{
+ ExaScreenPriv (pDrawable->pScreen);
+ xRectangle *prect;
+ int x1, x2, y1, y2;
+ int i;
+
+ if (pExaScr->fallback_counter) {
+ ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt);
+ return;
+ }
+
+ /* Don't try to do wide lines or non-solid fill style. */
+ if (pGC->lineWidth != 0 || pGC->lineStyle != LineSolid ||
+ pGC->fillStyle != FillSolid) {
+ ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt);
+ return;
+ }
+
+ prect = malloc(sizeof(xRectangle) * (npt - 1));
+ x1 = ppt[0].x;
+ y1 = ppt[0].y;
+ /* If we have any non-horizontal/vertical, fall back. */
+ for (i = 0; i < npt - 1; i++) {
+ if (mode == CoordModePrevious) {
+ x2 = x1 + ppt[i + 1].x;
+ y2 = y1 + ppt[i + 1].y;
+ } else {
+ x2 = ppt[i + 1].x;
+ y2 = ppt[i + 1].y;
+ }
+
+ if (x1 != x2 && y1 != y2) {
+ free(prect);
+ ExaCheckPolylines(pDrawable, pGC, mode, npt, ppt);
+ return;
+ }
+
+ if (x1 < x2) {
+ prect[i].x = x1;
+ prect[i].width = x2 - x1 + 1;
+ } else {
+ prect[i].x = x2;
+ prect[i].width = x1 - x2 + 1;
+ }
+ if (y1 < y2) {
+ prect[i].y = y1;
+ prect[i].height = y2 - y1 + 1;
+ } else {
+ prect[i].y = y2;
+ prect[i].height = y1 - y2 + 1;
+ }
+
+ x1 = x2;
+ y1 = y2;
+ }
+ pGC->ops->PolyFillRect(pDrawable, pGC, npt - 1, prect);
+ free(prect);
+}
+
+/**
+ * exaPolySegment() checks if it can accelerate the lines as a group of
+ * horizontal or vertical lines (rectangles), and uses existing rectangle fill
+ * acceleration if so.
+ */
+static void
+exaPolySegment (DrawablePtr pDrawable, GCPtr pGC, int nseg,
+ xSegment *pSeg)
+{
+ ExaScreenPriv (pDrawable->pScreen);
+ xRectangle *prect;
+ int i;
+
+ /* Don't try to do wide lines or non-solid fill style. */
+ if (pExaScr->fallback_counter || pGC->lineWidth != 0 ||
+ pGC->lineStyle != LineSolid || pGC->fillStyle != FillSolid)
+ {
+ ExaCheckPolySegment(pDrawable, pGC, nseg, pSeg);
+ return;
+ }
+
+ /* If we have any non-horizontal/vertical, fall back. */
+ for (i = 0; i < nseg; i++) {
+ if (pSeg[i].x1 != pSeg[i].x2 && pSeg[i].y1 != pSeg[i].y2) {
+ ExaCheckPolySegment(pDrawable, pGC, nseg, pSeg);
+ return;
+ }
+ }
+
+ prect = malloc(sizeof(xRectangle) * nseg);
+ for (i = 0; i < nseg; i++) {
+ if (pSeg[i].x1 < pSeg[i].x2) {
+ prect[i].x = pSeg[i].x1;
+ prect[i].width = pSeg[i].x2 - pSeg[i].x1 + 1;
+ } else {
+ prect[i].x = pSeg[i].x2;
+ prect[i].width = pSeg[i].x1 - pSeg[i].x2 + 1;
+ }
+ if (pSeg[i].y1 < pSeg[i].y2) {
+ prect[i].y = pSeg[i].y1;
+ prect[i].height = pSeg[i].y2 - pSeg[i].y1 + 1;
+ } else {
+ prect[i].y = pSeg[i].y2;
+ prect[i].height = pSeg[i].y1 - pSeg[i].y2 + 1;
+ }
+
+ /* don't paint last pixel */
+ if (pGC->capStyle == CapNotLast) {
+ if (prect[i].width == 1)
+ prect[i].height--;
+ else
+ prect[i].width--;
+ }
+ }
+ pGC->ops->PolyFillRect(pDrawable, pGC, nseg, prect);
+ free(prect);
+}
+
+static Bool exaFillRegionSolid (DrawablePtr pDrawable, RegionPtr pRegion,
+ Pixel pixel, CARD32 planemask, CARD32 alu,
+ unsigned int clientClipType);
+
+static void
+exaPolyFillRect(DrawablePtr pDrawable,
+ GCPtr pGC,
+ int nrect,
+ xRectangle *prect)
+{
+ ExaScreenPriv (pDrawable->pScreen);
+ RegionPtr pClip = fbGetCompositeClip(pGC);
+ PixmapPtr pPixmap = exaGetDrawablePixmap(pDrawable);
+ ExaPixmapPriv (pPixmap);
+ register BoxPtr pbox;
+ BoxPtr pextent;
+ int extentX1, extentX2, extentY1, extentY2;
+ int fullX1, fullX2, fullY1, fullY2;
+ int partX1, partX2, partY1, partY2;
+ int xoff, yoff;
+ int xorg, yorg;
+ int n;
+ RegionPtr pReg = RECTS_TO_REGION(pScreen, nrect, prect, CT_UNSORTED);
+
+ /* Compute intersection of rects and clip region */
+ REGION_TRANSLATE(pScreen, pReg, pDrawable->x, pDrawable->y);
+ REGION_INTERSECT(pScreen, pReg, pClip, pReg);
+
+ if (!REGION_NUM_RECTS(pReg)) {
+ goto out;
+ }
+
+ exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff);
+
+ if (pExaScr->fallback_counter || pExaScr->swappedOut ||
+ pExaPixmap->accel_blocked)
+ {
+ goto fallback;
+ }
+
+ /* For ROPs where overlaps don't matter, convert rectangles to region and
+ * call exaFillRegion{Solid,Tiled}.
+ */
+ if ((pGC->fillStyle == FillSolid || pGC->fillStyle == FillTiled) &&
+ (nrect == 1 || pGC->alu == GXcopy || pGC->alu == GXclear ||
+ pGC->alu == GXnoop || pGC->alu == GXcopyInverted ||
+ pGC->alu == GXset)) {
+ if (((pGC->fillStyle == FillSolid || pGC->tileIsPixel) &&
+ exaFillRegionSolid(pDrawable, pReg, pGC->fillStyle == FillSolid ?
+ pGC->fgPixel : pGC->tile.pixel, pGC->planemask,
+ pGC->alu, pGC->clientClipType)) ||
+ (pGC->fillStyle == FillTiled && !pGC->tileIsPixel &&
+ exaFillRegionTiled(pDrawable, pReg, pGC->tile.pixmap, &pGC->patOrg,
+ pGC->planemask, pGC->alu,
+ pGC->clientClipType))) {
+ goto out;
+ }
+ }
+
+ if (pGC->fillStyle != FillSolid &&
+ !(pGC->tileIsPixel && pGC->fillStyle == FillTiled))
+ {
+ goto fallback;
+ }
+
+ if (pExaScr->do_migration) {
+ ExaMigrationRec pixmaps[1];
+
+ pixmaps[0].as_dst = TRUE;
+ pixmaps[0].as_src = FALSE;
+ pixmaps[0].pPix = pPixmap;
+ pixmaps[0].pReg = NULL;
+
+ exaDoMigration (pixmaps, 1, TRUE);
+ }
+
+ if (!exaPixmapHasGpuCopy (pPixmap) ||
+ !(*pExaScr->info->PrepareSolid) (pPixmap,
+ pGC->alu,
+ pGC->planemask,
+ pGC->fgPixel))
+ {
+fallback:
+ ExaCheckPolyFillRect (pDrawable, pGC, nrect, prect);
+ goto out;
+ }
+
+ xorg = pDrawable->x;
+ yorg = pDrawable->y;
+
+ pextent = REGION_EXTENTS(pGC->pScreen, pClip);
+ extentX1 = pextent->x1;
+ extentY1 = pextent->y1;
+ extentX2 = pextent->x2;
+ extentY2 = pextent->y2;
+ while (nrect--)
+ {
+ fullX1 = prect->x + xorg;
+ fullY1 = prect->y + yorg;
+ fullX2 = fullX1 + (int) prect->width;
+ fullY2 = fullY1 + (int) prect->height;
+ prect++;
+
+ if (fullX1 < extentX1)
+ fullX1 = extentX1;
+
+ if (fullY1 < extentY1)
+ fullY1 = extentY1;
+
+ if (fullX2 > extentX2)
+ fullX2 = extentX2;
+
+ if (fullY2 > extentY2)
+ fullY2 = extentY2;
+
+ if ((fullX1 >= fullX2) || (fullY1 >= fullY2))
+ continue;
+ n = REGION_NUM_RECTS (pClip);
+ if (n == 1)
+ {
+ (*pExaScr->info->Solid) (pPixmap,
+ fullX1 + xoff, fullY1 + yoff,
+ fullX2 + xoff, fullY2 + yoff);
+ }
+ else
+ {
+ pbox = REGION_RECTS(pClip);
+ /*
+ * clip the rectangle to each box in the clip region
+ * this is logically equivalent to calling Intersect(),
+ * but rectangles may overlap each other here.
+ */
+ while(n--)
+ {
+ partX1 = pbox->x1;
+ if (partX1 < fullX1)
+ partX1 = fullX1;
+ partY1 = pbox->y1;
+ if (partY1 < fullY1)
+ partY1 = fullY1;
+ partX2 = pbox->x2;
+ if (partX2 > fullX2)
+ partX2 = fullX2;
+ partY2 = pbox->y2;
+ if (partY2 > fullY2)
+ partY2 = fullY2;
+
+ pbox++;
+
+ if (partX1 < partX2 && partY1 < partY2) {
+ (*pExaScr->info->Solid) (pPixmap,
+ partX1 + xoff, partY1 + yoff,
+ partX2 + xoff, partY2 + yoff);
+ }
+ }
+ }
+ }
+ (*pExaScr->info->DoneSolid) (pPixmap);
+ exaMarkSync(pDrawable->pScreen);
+
+out:
+ REGION_UNINIT(pScreen, pReg);
+ REGION_DESTROY(pScreen, pReg);
+}
+
+const GCOps exaOps = {
+ exaFillSpans,
+ ExaCheckSetSpans,
+ exaPutImage,
+ exaCopyArea,
+ ExaCheckCopyPlane,
+ exaPolyPoint,
+ exaPolylines,
+ exaPolySegment,
+ miPolyRectangle,
+ ExaCheckPolyArc,
+ miFillPolygon,
+ exaPolyFillRect,
+ miPolyFillArc,
+ miPolyText8,
+ miPolyText16,
+ miImageText8,
+ miImageText16,
+ ExaCheckImageGlyphBlt,
+ ExaCheckPolyGlyphBlt,
+ ExaCheckPushPixels,
+};
+
+void
+exaCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc)
+{
+ RegionRec rgnDst;
+ int dx, dy;
+ PixmapPtr pPixmap = (*pWin->drawable.pScreen->GetWindowPixmap) (pWin);
+ ExaScreenPriv(pWin->drawable.pScreen);
+
+ dx = ptOldOrg.x - pWin->drawable.x;
+ dy = ptOldOrg.y - pWin->drawable.y;
+ REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, -dx, -dy);
+
+ REGION_INIT (pWin->drawable.pScreen, &rgnDst, NullBox, 0);
+
+ REGION_INTERSECT(pWin->drawable.pScreen, &rgnDst, &pWin->borderClip, prgnSrc);
+#ifdef COMPOSITE
+ if (pPixmap->screen_x || pPixmap->screen_y)
+ REGION_TRANSLATE (pWin->drawable.pScreen, &rgnDst,
+ -pPixmap->screen_x, -pPixmap->screen_y);
+#endif
+
+ if (pExaScr->fallback_counter) {
+ pExaScr->fallback_flags |= EXA_FALLBACK_COPYWINDOW;
+ goto fallback;
+ }
+
+ pExaScr->fallback_flags |= EXA_ACCEL_COPYWINDOW;
+ miCopyRegion (&pPixmap->drawable, &pPixmap->drawable,
+ NULL,
+ &rgnDst, dx, dy, exaCopyNtoN, 0, NULL);
+ pExaScr->fallback_flags &= ~EXA_ACCEL_COPYWINDOW;
+
+fallback:
+ REGION_UNINIT(pWin->drawable.pScreen, &rgnDst);
+
+ if (pExaScr->fallback_flags & EXA_FALLBACK_COPYWINDOW) {
+ pExaScr->fallback_flags &= ~EXA_FALLBACK_COPYWINDOW;
+ REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, dx, dy);
+ ExaCheckCopyWindow(pWin, ptOldOrg, prgnSrc);
+ }
+}
+
+static Bool
+exaFillRegionSolid (DrawablePtr pDrawable, RegionPtr pRegion, Pixel pixel,
+ CARD32 planemask, CARD32 alu, unsigned int clientClipType)
+{
+ ExaScreenPriv(pDrawable->pScreen);
+ PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable);
+ ExaPixmapPriv (pPixmap);
+ int xoff, yoff;
+ Bool ret = FALSE;
+
+ exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff);
+ REGION_TRANSLATE(pScreen, pRegion, xoff, yoff);
+
+ if (pExaScr->fallback_counter || pExaPixmap->accel_blocked)
+ goto out;
+
+ if (pExaScr->do_migration) {
+ ExaMigrationRec pixmaps[1];
+
+ pixmaps[0].as_dst = TRUE;
+ pixmaps[0].as_src = FALSE;
+ pixmaps[0].pPix = pPixmap;
+ pixmaps[0].pReg = exaGCReadsDestination(pDrawable, planemask, FillSolid,
+ alu, clientClipType) ? NULL : pRegion;
+
+ exaDoMigration (pixmaps, 1, TRUE);
+ }
+
+ if (exaPixmapHasGpuCopy (pPixmap) &&
+ (*pExaScr->info->PrepareSolid) (pPixmap, alu, planemask, pixel))
+ {
+ int nbox;
+ BoxPtr pBox;
+
+ nbox = REGION_NUM_RECTS (pRegion);
+ pBox = REGION_RECTS (pRegion);
+
+ while (nbox--)
+ {
+ (*pExaScr->info->Solid) (pPixmap, pBox->x1, pBox->y1, pBox->x2,
+ pBox->y2);
+ pBox++;
+ }
+ (*pExaScr->info->DoneSolid) (pPixmap);
+ exaMarkSync(pDrawable->pScreen);
+
+ if (pExaPixmap->pDamage &&
+ pExaPixmap->sys_ptr && pDrawable->type == DRAWABLE_PIXMAP &&
+ pDrawable->width == 1 && pDrawable->height == 1 &&
+ pDrawable->bitsPerPixel != 24) {
+ ExaPixmapPriv(pPixmap);
+ RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage);
+
+ switch (pDrawable->bitsPerPixel) {
+ case 32:
+ *(CARD32*)pExaPixmap->sys_ptr = pixel;
+ break;
+ case 16:
+ *(CARD16*)pExaPixmap->sys_ptr = pixel;
+ break;
+ case 8:
+ *(CARD8*)pExaPixmap->sys_ptr = pixel;
+ }
+
+ REGION_UNION(pScreen, &pExaPixmap->validSys, &pExaPixmap->validSys,
+ pRegion);
+ REGION_UNION(pScreen, &pExaPixmap->validFB, &pExaPixmap->validFB,
+ pRegion);
+ REGION_SUBTRACT(pScreen, pending_damage, pending_damage, pRegion);
+ }
+
+ ret = TRUE;
+ }
+
+out:
+ REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff);
+
+ return ret;
+}
+
+/* Try to do an accelerated tile of the pTile into pRegion of pDrawable.
+ * Based on fbFillRegionTiled(), fbTile().
+ */
+Bool
+exaFillRegionTiled (DrawablePtr pDrawable, RegionPtr pRegion, PixmapPtr pTile,
+ DDXPointPtr pPatOrg, CARD32 planemask, CARD32 alu,
+ unsigned int clientClipType)
+{
+ ExaScreenPriv(pDrawable->pScreen);
+ PixmapPtr pPixmap;
+ ExaPixmapPrivPtr pExaPixmap;
+ ExaPixmapPrivPtr pTileExaPixmap = ExaGetPixmapPriv(pTile);
+ int xoff, yoff;
+ int tileWidth, tileHeight;
+ int nbox = REGION_NUM_RECTS (pRegion);
+ BoxPtr pBox = REGION_RECTS (pRegion);
+ Bool ret = FALSE;
+ int i;
+
+ tileWidth = pTile->drawable.width;
+ tileHeight = pTile->drawable.height;
+
+ /* If we're filling with a solid color, grab it out and go to
+ * FillRegionSolid, saving numerous copies.
+ */
+ if (tileWidth == 1 && tileHeight == 1)
+ return exaFillRegionSolid(pDrawable, pRegion,
+ exaGetPixmapFirstPixel (pTile), planemask,
+ alu, clientClipType);
+
+ pPixmap = exaGetDrawablePixmap (pDrawable);
+ pExaPixmap = ExaGetPixmapPriv (pPixmap);
+
+ if (pExaScr->fallback_counter || pExaPixmap->accel_blocked ||
+ pTileExaPixmap->accel_blocked)
+ return FALSE;
+
+ if (pExaScr->do_migration) {
+ ExaMigrationRec pixmaps[2];
+
+ pixmaps[0].as_dst = TRUE;
+ pixmaps[0].as_src = FALSE;
+ pixmaps[0].pPix = pPixmap;
+ pixmaps[0].pReg = exaGCReadsDestination(pDrawable, planemask, FillTiled,
+ alu, clientClipType) ? NULL : pRegion;
+ pixmaps[1].as_dst = FALSE;
+ pixmaps[1].as_src = TRUE;
+ pixmaps[1].pPix = pTile;
+ pixmaps[1].pReg = NULL;
+
+ exaDoMigration (pixmaps, 2, TRUE);
+ }
+
+ pPixmap = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff);
+
+ if (!pPixmap || !exaPixmapHasGpuCopy(pTile))
+ return FALSE;
+
+ if ((*pExaScr->info->PrepareCopy) (pTile, pPixmap, 1, 1, alu, planemask))
+ {
+ if (xoff || yoff)
+ REGION_TRANSLATE(pScreen, pRegion, xoff, yoff);
+
+ for (i = 0; i < nbox; i++)
+ {
+ int height = pBox[i].y2 - pBox[i].y1;
+ int dstY = pBox[i].y1;
+ int tileY;
+
+ if (alu == GXcopy)
+ height = min(height, tileHeight);
+
+ modulus(dstY - yoff - pDrawable->y - pPatOrg->y, tileHeight, tileY);
+
+ while (height > 0) {
+ int width = pBox[i].x2 - pBox[i].x1;
+ int dstX = pBox[i].x1;
+ int tileX;
+ int h = tileHeight - tileY;
+
+ if (alu == GXcopy)
+ width = min(width, tileWidth);
+
+ if (h > height)
+ h = height;
+ height -= h;
+
+ modulus(dstX - xoff - pDrawable->x - pPatOrg->x, tileWidth,
+ tileX);
+
+ while (width > 0) {
+ int w = tileWidth - tileX;
+ if (w > width)
+ w = width;
+ width -= w;
+
+ (*pExaScr->info->Copy) (pPixmap, tileX, tileY, dstX, dstY,
+ w, h);
+ dstX += w;
+ tileX = 0;
+ }
+ dstY += h;
+ tileY = 0;
+ }
+ }
+ (*pExaScr->info->DoneCopy) (pPixmap);
+
+ /* With GXcopy, we only need to do the basic algorithm up to the tile
+ * size; then, we can just keep doubling the destination in each
+ * direction until it fills the box. This way, the number of copy
+ * operations is O(log(rx)) + O(log(ry)) instead of O(rx * ry), where
+ * rx/ry is the ratio between box and tile width/height. This can make
+ * a big difference if each driver copy incurs a significant constant
+ * overhead.
+ */
+ if (alu != GXcopy)
+ ret = TRUE;
+ else {
+ Bool more_copy = FALSE;
+
+ for (i = 0; i < nbox; i++) {
+ int dstX = pBox[i].x1 + tileWidth;
+ int dstY = pBox[i].y1 + tileHeight;
+
+ if ((dstX < pBox[i].x2) || (dstY < pBox[i].y2)) {
+ more_copy = TRUE;
+ break;
+ }
+ }
+
+ if (more_copy == FALSE)
+ ret = TRUE;
+
+ if (more_copy && (*pExaScr->info->PrepareCopy) (pPixmap, pPixmap,
+ 1, 1, alu, planemask)) {
+ for (i = 0; i < nbox; i++)
+ {
+ int dstX = pBox[i].x1 + tileWidth;
+ int dstY = pBox[i].y1 + tileHeight;
+ int width = min(pBox[i].x2 - dstX, tileWidth);
+ int height = min(pBox[i].y2 - pBox[i].y1, tileHeight);
+
+ while (dstX < pBox[i].x2) {
+ (*pExaScr->info->Copy) (pPixmap, pBox[i].x1, pBox[i].y1,
+ dstX, pBox[i].y1, width, height);
+ dstX += width;
+ width = min(pBox[i].x2 - dstX, width * 2);
+ }
+
+ width = pBox[i].x2 - pBox[i].x1;
+ height = min(pBox[i].y2 - dstY, tileHeight);
+
+ while (dstY < pBox[i].y2) {
+ (*pExaScr->info->Copy) (pPixmap, pBox[i].x1, pBox[i].y1,
+ pBox[i].x1, dstY, width, height);
+ dstY += height;
+ height = min(pBox[i].y2 - dstY, height * 2);
+ }
+ }
+
+ (*pExaScr->info->DoneCopy) (pPixmap);
+
+ ret = TRUE;
+ }
+ }
+
+ exaMarkSync(pDrawable->pScreen);
+
+ if (xoff || yoff)
+ REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff);
+ }
+
+ return ret;
+}
+
+
+/**
+ * Accelerates GetImage for solid ZPixmap downloads from framebuffer memory.
+ *
+ * This is probably the only case we actually care about. The rest fall through
+ * to migration and fbGetImage, which hopefully will result in migration pushing
+ * the pixmap out of framebuffer.
+ */
+void
+exaGetImage (DrawablePtr pDrawable, int x, int y, int w, int h,
+ unsigned int format, unsigned long planeMask, char *d)
+{
+ ExaScreenPriv (pDrawable->pScreen);
+ PixmapPtr pPix = exaGetDrawablePixmap (pDrawable);
+ ExaPixmapPriv(pPix);
+ int xoff, yoff;
+ Bool ok;
+
+ if (pExaScr->fallback_counter || pExaScr->swappedOut)
+ goto fallback;
+
+ /* If there's a system copy, we want to save the result there */
+ if (pExaPixmap->pDamage)
+ goto fallback;
+
+ pPix = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff);
+
+ if (pPix == NULL || pExaScr->info->DownloadFromScreen == NULL)
+ goto fallback;
+
+ /* Only cover the ZPixmap, solid copy case. */
+ if (format != ZPixmap || !EXA_PM_IS_SOLID(pDrawable, planeMask))
+ goto fallback;
+
+ /* Only try to handle the 8bpp and up cases, since we don't want to think
+ * about <8bpp.
+ */
+ if (pDrawable->bitsPerPixel < 8)
+ goto fallback;
+
+ ok = pExaScr->info->DownloadFromScreen(pPix, pDrawable->x + x + xoff,
+ pDrawable->y + y + yoff, w, h, d,
+ PixmapBytePad(w, pDrawable->depth));
+ if (ok) {
+ exaWaitSync(pDrawable->pScreen);
+ return;
+ }
+
+fallback:
+ ExaCheckGetImage(pDrawable, x, y, w, h, format, planeMask, d);
+}
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 <otaylor@fishsoup.net> - * Based on code by: Keith Packard - */ - -#ifdef HAVE_DIX_CONFIG_H -#include <dix-config.h> -#endif - -#include <stdlib.h> - -#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 <otaylor@fishsoup.net>
+ * Based on code by: Keith Packard
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include <stdlib.h>
+
+#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 <limits.h> -#include <assert.h> -#include <stdlib.h> - -#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 <limits.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#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);
+ }
+}
|