diff options
Diffstat (limited to 'xorg-server/exa/exa.c')
-rw-r--r-- | xorg-server/exa/exa.c | 978 |
1 files changed, 567 insertions, 411 deletions
diff --git a/xorg-server/exa/exa.c b/xorg-server/exa/exa.c index 270810766..483e3b4eb 100644 --- a/xorg-server/exa/exa.c +++ b/xorg-server/exa/exa.c @@ -41,22 +41,13 @@ 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 -static _X_INLINE void* -ExaGetPixmapAddress(PixmapPtr p) -{ - ExaPixmapPriv(p); - - if (pExaPixmap->offscreen && pExaPixmap->fb_ptr) - return pExaPixmap->fb_ptr; - else - return pExaPixmap->sys_ptr; -} - /** * exaGetPixmapOffset() returns the offset (in bytes) within the framebuffer of * the beginning of the given pixmap. @@ -71,9 +62,9 @@ unsigned long exaGetPixmapOffset(PixmapPtr pPix) { ExaScreenPriv (pPix->drawable.pScreen); + ExaPixmapPriv (pPix); - return ((unsigned long)ExaGetPixmapAddress(pPix) - - (unsigned long)pExaScr->info->memoryBase); + return (CARD8 *)pExaPixmap->fb_ptr - pExaScr->info->memoryBase; } void * @@ -129,7 +120,7 @@ exaGetDrawablePixmap(DrawablePtr pDrawable) 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 @@ -159,14 +150,9 @@ exaGetDrawableDeltas (DrawablePtr pDrawable, PixmapPtr pPixmap, void exaPixmapDirty (PixmapPtr pPix, int x1, int y1, int x2, int y2) { - ExaPixmapPriv(pPix); BoxRec box; - RegionPtr pDamageReg; RegionRec region; - if (!pExaPixmap || !pExaPixmap->pDamage) - return; - box.x1 = max(x1, 0); box.y1 = max(y1, 0); box.x2 = min(x2, pPix->drawable.width); @@ -175,46 +161,12 @@ exaPixmapDirty (PixmapPtr pPix, int x1, int y1, int x2, int y2) if (box.x1 >= box.x2 || box.y1 >= box.y2) return; - pDamageReg = DamageRegion(pExaPixmap->pDamage); - REGION_INIT(pScreen, ®ion, &box, 1); - REGION_UNION(pScreen, pDamageReg, pDamageReg, ®ion); + DamageRegionAppend(&pPix->drawable, ®ion); + DamageRegionProcessPending(&pPix->drawable); REGION_UNINIT(pScreen, ®ion); } -static Bool -exaDestroyPixmap (PixmapPtr pPixmap) -{ - ScreenPtr pScreen = pPixmap->drawable.pScreen; - ExaScreenPriv(pScreen); - - if (pPixmap->refcnt == 1) - { - ExaPixmapPriv (pPixmap); - - if (pExaPixmap->driverPriv) { - pExaScr->info->DestroyPixmap(pScreen, pExaPixmap->driverPriv); - pExaPixmap->driverPriv = NULL; - } - - if (pExaPixmap->area) - { - DBG_PIXMAP(("-- 0x%p (0x%x) (%dx%d)\n", - (void*)pPixmap->drawable.id, - ExaGetPixmapPriv(pPixmap)->area->offset, - pPixmap->drawable.width, - pPixmap->drawable.height)); - /* Free the offscreen area */ - exaOffscreenFree (pPixmap->drawable.pScreen, pExaPixmap->area); - pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr; - pPixmap->devKind = pExaPixmap->sys_pitch; - } - REGION_UNINIT(pPixmap->drawable.pScreen, &pExaPixmap->validSys); - REGION_UNINIT(pPixmap->drawable.pScreen, &pExaPixmap->validFB); - } - return fbDestroyPixmap (pPixmap); -} - static int exaLog2(int val) { @@ -227,14 +179,14 @@ exaLog2(int val) return bits - 1; } -static void +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 * (bpp + 7) / 8; + int max_pitch = pExaScr->info->maxPitchPixels * bits_to_bytes(bpp); if (pExaPixmap->fb_pitch > max_pitch) pExaPixmap->accel_blocked |= EXA_RANGE_PITCH; @@ -251,179 +203,33 @@ exaSetAccelBlock(ExaScreenPrivPtr pExaScr, ExaPixmapPrivPtr pExaPixmap, pExaPixmap->accel_blocked |= EXA_RANGE_HEIGHT; } -static void +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 = (1 << (exaLog2(w - 1) + 1)) * bpp / 8; + pExaPixmap->fb_pitch = bits_to_bytes((1 << (exaLog2(w - 1) + 1)) * bpp); else - pExaPixmap->fb_pitch = w * bpp / 8; + pExaPixmap->fb_pitch = bits_to_bytes(w * bpp); pExaPixmap->fb_pitch = EXA_ALIGN(pExaPixmap->fb_pitch, pExaScr->info->pixmapPitchAlign); } /** - * exaCreatePixmap() creates a new pixmap. - * - * If width and height are 0, this won't be a full-fledged pixmap and it will - * get ModifyPixmapHeader() called on it later. So, we mark it as pinned, because - * ModifyPixmapHeader() would break migration. These types of pixmaps are used - * for scratch pixmaps, or to represent the visible screen. + * 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). */ -static PixmapPtr -exaCreatePixmap(ScreenPtr pScreen, int w, int h, int depth, - unsigned usage_hint) -{ - PixmapPtr pPixmap; - ExaPixmapPrivPtr pExaPixmap; - int driver_alloc = 0; - int bpp; - ExaScreenPriv(pScreen); - - if (w > 32767 || h > 32767) - return NullPixmap; - - if (!pExaScr->info->CreatePixmap) { - pPixmap = fbCreatePixmap (pScreen, w, h, depth, usage_hint); - } else { - driver_alloc = 1; - pPixmap = fbCreatePixmap(pScreen, 0, 0, depth, usage_hint); - } - - if (!pPixmap) - return NULL; - - pExaPixmap = ExaGetPixmapPriv(pPixmap); - pExaPixmap->driverPriv = NULL; - - bpp = pPixmap->drawable.bitsPerPixel; - - if (driver_alloc) { - size_t paddedWidth, datasize; - - paddedWidth = ((w * bpp + FB_MASK) >> FB_SHIFT) * sizeof(FbBits); - if (paddedWidth / 4 > 32767 || h > 32767) - return NullPixmap; - - exaSetFbPitch(pExaScr, pExaPixmap, w, h, bpp); - - if (paddedWidth < pExaPixmap->fb_pitch) - paddedWidth = pExaPixmap->fb_pitch; - - datasize = h * paddedWidth; - - /* Set this before driver hooks, to allow for !offscreen pixmaps. - * !offscreen pixmaps have a valid pointer at all times. - */ - pPixmap->devPrivate.ptr = NULL; - - pExaPixmap->driverPriv = pExaScr->info->CreatePixmap(pScreen, datasize, 0); - if (!pExaPixmap->driverPriv) { - fbDestroyPixmap(pPixmap); - return NULL; - } - - (*pScreen->ModifyPixmapHeader)(pPixmap, w, h, 0, 0, - paddedWidth, NULL); - pExaPixmap->score = EXA_PIXMAP_SCORE_PINNED; - pExaPixmap->fb_ptr = NULL; - pExaPixmap->pDamage = NULL; - pExaPixmap->sys_ptr = pPixmap->devPrivate.ptr; - - } else { - pExaPixmap->driverPriv = NULL; - /* Scratch pixmaps may have w/h equal to zero, and may not be - * migrated. - */ - if (!w || !h) - pExaPixmap->score = EXA_PIXMAP_SCORE_PINNED; - else - pExaPixmap->score = EXA_PIXMAP_SCORE_INIT; - - pExaPixmap->sys_ptr = pPixmap->devPrivate.ptr; - pExaPixmap->sys_pitch = pPixmap->devKind; - - pPixmap->devPrivate.ptr = NULL; - pExaPixmap->offscreen = FALSE; - - pExaPixmap->fb_ptr = NULL; - exaSetFbPitch(pExaScr, pExaPixmap, w, h, bpp); - pExaPixmap->fb_size = pExaPixmap->fb_pitch * h; - - if (pExaPixmap->fb_pitch > 131071) { - fbDestroyPixmap(pPixmap); - return NULL; - } - - /* Set up damage tracking */ - pExaPixmap->pDamage = DamageCreate (NULL, NULL, - DamageReportNone, TRUE, - pScreen, pPixmap); - - if (pExaPixmap->pDamage == NULL) { - fbDestroyPixmap (pPixmap); - return NULL; - } - - DamageRegister (&pPixmap->drawable, pExaPixmap->pDamage); - /* This ensures that pending damage reflects the current operation. */ - /* This is used by exa to optimize migration. */ - DamageSetReportAfterOp (pExaPixmap->pDamage, TRUE); - } - - pExaPixmap->area = NULL; - - /* None of the pixmap bits are valid initially */ - REGION_NULL(pScreen, &pExaPixmap->validSys); - REGION_NULL(pScreen, &pExaPixmap->validFB); - - exaSetAccelBlock(pExaScr, pExaPixmap, - w, h, bpp); - - return pPixmap; -} - -static Bool -exaModifyPixmapHeader(PixmapPtr pPixmap, int width, int height, int depth, - int bitsPerPixel, int devKind, pointer pPixData) +Bool +exaPixmapIsPinned (PixmapPtr pPix) { - ExaScreenPrivPtr pExaScr; - ExaPixmapPrivPtr pExaPixmap; - Bool ret; - - if (!pPixmap) - return FALSE; - - pExaScr = ExaGetScreenPriv(pPixmap->drawable.pScreen); - pExaPixmap = ExaGetPixmapPriv(pPixmap); - - if (pExaPixmap) { - if (pPixData) - pExaPixmap->sys_ptr = pPixData; + ExaPixmapPriv (pPix); - if (devKind > 0) - pExaPixmap->sys_pitch = devKind; + if (pExaPixmap == NULL) + EXA_FatalErrorDebugWithRet(("EXA bug: exaPixmapIsPinned was called on a non-exa pixmap.\n"), TRUE); - if (width > 0 && height > 0 && bitsPerPixel > 0) { - exaSetFbPitch(pExaScr, pExaPixmap, - width, height, bitsPerPixel); - - exaSetAccelBlock(pExaScr, pExaPixmap, - width, height, bitsPerPixel); - } - } - - - if (pExaScr->info->ModifyPixmapHeader) { - ret = pExaScr->info->ModifyPixmapHeader(pPixmap, width, height, depth, - bitsPerPixel, devKind, pPixData); - if (ret == TRUE) - return ret; - } - return pExaScr->SavedModifyPixmapHeader(pPixmap, width, height, depth, - bitsPerPixel, devKind, pPixData); + return pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED; } /** @@ -439,29 +245,15 @@ exaModifyPixmapHeader(PixmapPtr pPixmap, int width, int height, int depth, * @return TRUE if the given drawable is in framebuffer memory. */ Bool -exaPixmapIsOffscreen(PixmapPtr p) +exaPixmapIsOffscreen(PixmapPtr pPixmap) { - ScreenPtr pScreen = p->drawable.pScreen; + ScreenPtr pScreen = pPixmap->drawable.pScreen; ExaScreenPriv(pScreen); - ExaPixmapPriv(p); - void *save_ptr; - Bool ret; - - save_ptr = p->devPrivate.ptr; - - if (!save_ptr && pExaPixmap && !(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) - p->devPrivate.ptr = ExaGetPixmapAddress(p); - if (pExaScr->info->PixmapIsOffscreen) - ret = pExaScr->info->PixmapIsOffscreen(p); - else - ret = ((unsigned long) ((CARD8 *) p->devPrivate.ptr - - (CARD8 *) pExaScr->info->memoryBase) < - pExaScr->info->memorySize); - - p->devPrivate.ptr = save_ptr; + if (!(pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS)) + return FALSE; - return ret; + return (*pExaScr->pixmap_is_offscreen)(pPixmap); } /** @@ -490,52 +282,115 @@ exaGetOffscreenPixmap (DrawablePtr pDrawable, int *xp, int *yp) return NULL; } -void +/** + * Returns TRUE if pixmap can be accessed offscreen. + */ +Bool ExaDoPrepareAccess(DrawablePtr pDrawable, int index) { - ScreenPtr pScreen = pDrawable->pScreen; - ExaScreenPriv (pScreen); - PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable); - Bool offscreen = exaPixmapIsOffscreen(pPixmap); + ScreenPtr pScreen = pDrawable->pScreen; + ExaScreenPriv (pScreen); + PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable); + ExaPixmapPriv(pPixmap); + Bool offscreen; + 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 TRUE; + } + } - /* Unhide pixmap pointer */ - if (pPixmap->devPrivate.ptr == NULL && !(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) { - pPixmap->devPrivate.ptr = ExaGetPixmapAddress(pPixmap); + /* 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; } - if (!offscreen) - return; + /* 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)); + } + + offscreen = exaPixmapIsOffscreen(pPixmap); + + if (offscreen) + pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr; + else + pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr; + + /* Store so we can handle repeated / nested calls. */ + pExaScr->access[index].pixmap = pPixmap; + pExaScr->access[index].count = 1; + + if (!offscreen) { + /* Do we need to allocate our system buffer? */ + if ((pExaScr->info->flags & EXA_HANDLES_PIXMAPS) && (pExaScr->info->flags & EXA_MIXED_PIXMAPS)) { + if (!pExaPixmap->sys_ptr && !exaPixmapIsPinned(pPixmap)) { + pExaPixmap->sys_ptr = malloc(pExaPixmap->sys_pitch * pDrawable->height); + if (!pExaPixmap->sys_ptr) + FatalError("EXA: malloc failed for size %d bytes\n", pExaPixmap->sys_pitch * pDrawable->height); + pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr; + } + } + return FALSE; + } exaWaitSync (pDrawable->pScreen); if (pExaScr->info->PrepareAccess == NULL) - return; + return TRUE; - if (index >= EXA_PREPARE_AUX0 && + 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); - return; + return FALSE; } if (!(*pExaScr->info->PrepareAccess) (pPixmap, index)) { - ExaPixmapPriv (pPixmap); if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED) - FatalError("Driver failed PrepareAccess on a pinned pixmap\n"); + FatalError("Driver failed PrepareAccess on a pinned pixmap.\n"); exaMoveOutPixmap (pPixmap); + + return FALSE; } + + return TRUE; } void exaPrepareAccessReg(DrawablePtr pDrawable, int index, RegionPtr pReg) { - ExaMigrationRec pixmaps[1]; + PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable); + ExaScreenPriv(pPixmap->drawable.pScreen); - pixmaps[0].as_dst = index == EXA_PREPARE_DEST; - pixmaps[0].as_src = index != EXA_PREPARE_DEST; - pixmaps[0].pPix = exaGetDrawablePixmap (pDrawable); - pixmaps[0].pReg = pReg; + if (pExaScr->do_migration) { + ExaMigrationRec pixmaps[1]; - exaDoMigration(pixmaps, 1, FALSE); + if (index == EXA_PREPARE_DEST || index == EXA_PREPARE_AUX_DEST) { + pixmaps[0].as_dst = TRUE; + pixmaps[0].as_src = FALSE; + } else { + pixmaps[0].as_dst = FALSE; + pixmaps[0].as_src = TRUE; + } + pixmaps[0].pPix = pPixmap; + pixmaps[0].pReg = pReg; + + exaDoMigration(pixmaps, 1, FALSE); + } ExaDoPrepareAccess(pDrawable, index); } @@ -564,19 +419,41 @@ exaFinishAccess(DrawablePtr pDrawable, int index) ExaScreenPriv (pScreen); PixmapPtr pPixmap = exaGetDrawablePixmap (pDrawable); ExaPixmapPriv (pPixmap); + int i; - /* Rehide pixmap pointer if we're doing that. */ - if (pExaPixmap && !(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) { - pPixmap->devPrivate.ptr = NULL; + 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; + index = i; + break; + } } + /* Catch unbalanced Prepare/FinishAccess calls. */ + if (i == EXA_NUM_PREPARE_INDICES) + EXA_FatalErrorDebug(("EXA bug: FinishAccess called without PrepareAccess for pixmap 0x%p.\n", + pPixmap)); + + pExaScr->access[index].pixmap = NULL; + + /* We always hide the devPrivate.ptr. */ + pPixmap->devPrivate.ptr = NULL; + if (pExaScr->info->FinishAccess == NULL) return; if (!exaPixmapIsOffscreen (pPixmap)) return; - if (index >= EXA_PREPARE_AUX0 && + if (index >= 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"); @@ -587,86 +464,225 @@ exaFinishAccess(DrawablePtr pDrawable, int index) } /** - * exaValidateGC() sets the ops to EXA's implementations, which may be - * accelerated or may sync the card and fall back to fb. + * 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 -exaValidateGC (GCPtr pGC, unsigned long changes, DrawablePtr pDrawable) +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 +}; + +/* + * This wrapper exists to allow fbValidateGC to work. + * Note that we no longer assume newly created pixmaps to be in normal ram. + * This assumption is certainly not garuanteed with driver allocated pixmaps. + */ +static PixmapPtr +exaCreatePixmapWithPrepare(ScreenPtr pScreen, int w, int h, int depth, + unsigned usage_hint) +{ + PixmapPtr pPixmap; + ExaScreenPriv(pScreen); + + /* This swaps between this function and the real upper layer function. + * Normally this would swap to the fb layer pointer, this is a very special case. + */ + swap(pExaScr, pScreen, CreatePixmap); + pPixmap = pScreen->CreatePixmap(pScreen, w, h, depth, usage_hint); + swap(pExaScr, pScreen, CreatePixmap); + + if (!pPixmap) + return NULL; + + /* Note the usage of ExaDoPrepareAccess, this allowed because: + * The pixmap is new, so not offscreen in the classic exa case. + * For EXA_HANDLES_PIXMAPS the driver will handle whatever is needed. + * We want to signal that the pixmaps will be used as destination. + */ + ExaDoPrepareAccess(&pPixmap->drawable, EXA_PREPARE_AUX_DEST); + + return pPixmap; +} + +static Bool +exaDestroyPixmapWithFinish(PixmapPtr pPixmap) +{ + ScreenPtr pScreen = pPixmap->drawable.pScreen; + ExaScreenPriv(pScreen); + Bool ret; + + exaFinishAccess(&pPixmap->drawable, EXA_PREPARE_AUX_DEST); + + /* This swaps between this function and the real upper layer function. + * Normally this would swap to the fb layer pointer, this is a very special case. + */ + swap(pExaScr, pScreen, DestroyPixmap); + ret = pScreen->DestroyPixmap(pPixmap); + swap(pExaScr, pScreen, DestroyPixmap); + + return ret; +} + +static void +exaValidateGC(GCPtr pGC, + unsigned long changes, + DrawablePtr pDrawable) { /* fbValidateGC will do direct access to pixmaps if the tiling has changed. - * Preempt fbValidateGC by doing its work and masking the change out, so - * that we can do the Prepare/FinishAccess. + * Do a few smart things so fbValidateGC can do it's work. */ -#ifdef FB_24_32BIT - if ((changes & GCTile) && fbGetRotatedPixmap(pGC)) { - (*pGC->pScreen->DestroyPixmap) (fbGetRotatedPixmap(pGC)); - fbGetRotatedPixmap(pGC) = 0; - } - - if (pGC->fillStyle == FillTiled) { - PixmapPtr pOldTile, pNewTile; - - pOldTile = pGC->tile.pixmap; - if (pOldTile->drawable.bitsPerPixel != pDrawable->bitsPerPixel) - { - pNewTile = fbGetRotatedPixmap(pGC); - if (!pNewTile || - pNewTile ->drawable.bitsPerPixel != pDrawable->bitsPerPixel) - { - if (pNewTile) - (*pGC->pScreen->DestroyPixmap) (pNewTile); - /* fb24_32ReformatTile will do direct access of a newly- - * allocated pixmap. This isn't a problem yet, since we don't - * put pixmaps in FB until at least one accelerated EXA op. - */ - exaPrepareAccess(&pOldTile->drawable, EXA_PREPARE_SRC); - pNewTile = fb24_32ReformatTile (pOldTile, - pDrawable->bitsPerPixel); - exaPixmapDirty(pNewTile, 0, 0, pNewTile->drawable.width, pNewTile->drawable.height); - exaFinishAccess(&pOldTile->drawable, EXA_PREPARE_SRC); - } - if (pNewTile) - { - fbGetRotatedPixmap(pGC) = pOldTile; - pGC->tile.pixmap = pNewTile; - changes |= GCTile; - } - } - } -#endif - if (changes & GCTile) { - if (!pGC->tileIsPixel && FbEvenTile (pGC->tile.pixmap->drawable.width * - pDrawable->bitsPerPixel)) - { - exaPrepareAccess(&pGC->tile.pixmap->drawable, EXA_PREPARE_SRC); - fbPadPixmap (pGC->tile.pixmap); - exaFinishAccess(&pGC->tile.pixmap->drawable, EXA_PREPARE_SRC); - exaPixmapDirty(pGC->tile.pixmap, 0, 0, - pGC->tile.pixmap->drawable.width, - pGC->tile.pixmap->drawable.height); - } - /* Mask out the GCTile change notification, now that we've done FB's - * job for it. + + ScreenPtr pScreen = pDrawable->pScreen; + ExaScreenPriv(pScreen); + CreatePixmapProcPtr old_ptr = NULL; + DestroyPixmapProcPtr old_ptr2 = NULL; + PixmapPtr pTile = NULL; + EXA_GC_PROLOGUE(pGC); + + /* save the "fb" pointer. */ + old_ptr = pExaScr->SavedCreatePixmap; + /* create a new upper layer pointer. */ + wrap(pExaScr, pScreen, CreatePixmap, exaCreatePixmapWithPrepare); + + /* save the "fb" pointer. */ + old_ptr2 = pExaScr->SavedDestroyPixmap; + /* create a new upper layer pointer. */ + wrap(pExaScr, pScreen, DestroyPixmap, exaDestroyPixmapWithFinish); + + /* 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. */ - changes &= ~GCTile; + if (pTile && pTile->drawable.depth != pDrawable->depth && !(changes & GCTile)) { + PixmapPtr pRotatedTile = fbGetRotatedPixmap(pGC); + if (pRotatedTile->drawable.depth == pDrawable->depth) + pTile = pRotatedTile; + } } - exaPrepareAccessGC(pGC); - fbValidateGC (pGC, changes, pDrawable); - exaFinishAccessGC(pGC); + if (pGC->stipple) + exaPrepareAccess(&pGC->stipple->drawable, EXA_PREPARE_MASK); + if (pTile) + exaPrepareAccess(&pTile->drawable, EXA_PREPARE_SRC); + + (*pGC->funcs->ValidateGC)(pGC, changes, pDrawable); - pGC->ops = (GCOps *) &exaOps; + if (pTile) + exaFinishAccess(&pTile->drawable, EXA_PREPARE_SRC); + if (pGC->stipple) + exaFinishAccess(&pGC->stipple->drawable, EXA_PREPARE_MASK); + + /* switch back to the normal upper layer. */ + unwrap(pExaScr, pScreen, CreatePixmap); + /* restore copy of fb layer pointer. */ + pExaScr->SavedCreatePixmap = old_ptr; + + /* switch back to the normal upper layer. */ + unwrap(pExaScr, pScreen, DestroyPixmap); + /* restore copy of fb layer pointer. */ + pExaScr->SavedDestroyPixmap = old_ptr2; + + EXA_GC_EPILOGUE(pGC); } -static GCFuncs exaGCFuncs = { - exaValidateGC, - miChangeGC, - miCopyGC, - miDestroyGC, - miChangeClip, - miDestroyClip, - miCopyClip -}; +/* Is exaPrepareAccessGC() needed? */ +static void +exaDestroyGC(GCPtr pGC) +{ + EXA_GC_PROLOGUE (pGC); + (*pGC->funcs->DestroyGC)(pGC); + EXA_GC_EPILOGUE (pGC); +} + +static void +exaChangeGC (GCPtr pGC, + unsigned long mask) +{ + EXA_GC_PROLOGUE (pGC); + (*pGC->funcs->ChangeGC) (pGC, mask); + EXA_GC_EPILOGUE (pGC); +} + +static void +exaCopyGC (GCPtr pGCSrc, + unsigned long mask, + GCPtr pGCDst) +{ + EXA_GC_PROLOGUE (pGCDst); + (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst); + EXA_GC_EPILOGUE (pGCDst); +} + +static void +exaChangeClip (GCPtr pGC, + int type, + pointer pvalue, + int nrects) +{ + EXA_GC_PROLOGUE (pGC); + (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects); + EXA_GC_EPILOGUE (pGC); +} + +static void +exaCopyClip(GCPtr pGCDst, GCPtr pGCSrc) +{ + EXA_GC_PROLOGUE (pGCDst); + (*pGCDst->funcs->CopyClip)(pGCDst, pGCSrc); + EXA_GC_EPILOGUE (pGCDst); +} + +static void +exaDestroyClip(GCPtr pGC) +{ + EXA_GC_PROLOGUE (pGC); + (*pGC->funcs->DestroyClip)(pGC); + EXA_GC_EPILOGUE (pGC); +} /** * exaCreateGC makes a new GC and hooks up its funcs handler, so that @@ -675,32 +691,64 @@ static GCFuncs exaGCFuncs = { static int exaCreateGC (GCPtr pGC) { - if (!fbCreateGC (pGC)) - return FALSE; + ScreenPtr pScreen = pGC->pScreen; + ExaScreenPriv(pScreen); + ExaGCPriv(pGC); + Bool ret; - pGC->funcs = &exaGCFuncs; + 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 TRUE; + return ret; } static Bool exaChangeWindowAttributes(WindowPtr pWin, unsigned long mask) { Bool ret; + ScreenPtr pScreen = pWin->drawable.pScreen; + ExaScreenPriv(pScreen); + CreatePixmapProcPtr old_ptr = NULL; + DestroyPixmapProcPtr old_ptr2 = NULL; + + /* save the "fb" pointer. */ + old_ptr = pExaScr->SavedCreatePixmap; + /* create a new upper layer pointer. */ + wrap(pExaScr, pScreen, CreatePixmap, exaCreatePixmapWithPrepare); + + /* save the "fb" pointer. */ + old_ptr2 = pExaScr->SavedDestroyPixmap; + /* create a new upper layer pointer. */ + wrap(pExaScr, pScreen, DestroyPixmap, exaDestroyPixmapWithFinish); if ((mask & CWBackPixmap) && pWin->backgroundState == BackgroundPixmap) - exaPrepareAccess(&pWin->background.pixmap->drawable, EXA_PREPARE_SRC); + exaPrepareAccess(&pWin->background.pixmap->drawable, EXA_PREPARE_SRC); if ((mask & CWBorderPixmap) && pWin->borderIsPixel == FALSE) - exaPrepareAccess(&pWin->border.pixmap->drawable, EXA_PREPARE_MASK); + exaPrepareAccess(&pWin->border.pixmap->drawable, EXA_PREPARE_MASK); - ret = fbChangeWindowAttributes(pWin, mask); + swap(pExaScr, pScreen, ChangeWindowAttributes); + ret = pScreen->ChangeWindowAttributes(pWin, mask); + swap(pExaScr, pScreen, ChangeWindowAttributes); + 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); + exaFinishAccess(&pWin->border.pixmap->drawable, EXA_PREPARE_MASK); - if ((mask & CWBackPixmap) && pWin->backgroundState == BackgroundPixmap) - exaFinishAccess(&pWin->background.pixmap->drawable, EXA_PREPARE_SRC); + /* switch back to the normal upper layer. */ + unwrap(pExaScr, pScreen, CreatePixmap); + /* restore copy of fb layer pointer. */ + pExaScr->SavedCreatePixmap = old_ptr; + + /* switch back to the normal upper layer. */ + unwrap(pExaScr, pScreen, DestroyPixmap); + /* restore copy of fb layer pointer. */ + pExaScr->SavedDestroyPixmap = old_ptr2; return ret; } @@ -708,11 +756,17 @@ exaChangeWindowAttributes(WindowPtr pWin, unsigned long mask) static RegionPtr exaBitmapToRegion(PixmapPtr pPix) { - RegionPtr ret; - exaPrepareAccess(&pPix->drawable, EXA_PREPARE_SRC); - ret = fbPixmapToRegion(pPix); - exaFinishAccess(&pPix->drawable, EXA_PREPARE_SRC); - return ret; + 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 @@ -722,9 +776,9 @@ exaCreateScreenResources(ScreenPtr pScreen) PixmapPtr pScreenPixmap; Bool b; - pScreen->CreateScreenResources = pExaScr->SavedCreateScreenResources; + swap(pExaScr, pScreen, CreateScreenResources); b = pScreen->CreateScreenResources(pScreen); - pScreen->CreateScreenResources = exaCreateScreenResources; + swap(pExaScr, pScreen, CreateScreenResources); if (!b) return FALSE; @@ -743,6 +797,50 @@ exaCreateScreenResources(ScreenPtr pScreen) return TRUE; } +static void +ExaBlockHandler(int screenNum, pointer blockData, pointer pTimeout, + pointer pReadmask) +{ + ScreenPtr pScreen = screenInfo.screens[screenNum]; + ExaScreenPriv(pScreen); + + unwrap(pExaScr, pScreen, BlockHandler); + (*pScreen->BlockHandler) (screenNum, blockData, pTimeout, pReadmask); + wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler); + + /* 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. @@ -758,23 +856,32 @@ exaCloseScreen(int i, ScreenPtr pScreen) if (ps->Glyphs == exaGlyphs) exaGlyphsFini(pScreen); - pScreen->CreateGC = pExaScr->SavedCreateGC; - pScreen->CloseScreen = pExaScr->SavedCloseScreen; - pScreen->GetImage = pExaScr->SavedGetImage; - pScreen->GetSpans = pExaScr->SavedGetSpans; - pScreen->CreatePixmap = pExaScr->SavedCreatePixmap; - pScreen->DestroyPixmap = pExaScr->SavedDestroyPixmap; - pScreen->CopyWindow = pExaScr->SavedCopyWindow; - pScreen->ChangeWindowAttributes = pExaScr->SavedChangeWindowAttributes; - pScreen->BitmapToRegion = pExaScr->SavedBitmapToRegion; - pScreen->CreateScreenResources = pExaScr->SavedCreateScreenResources; + 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); #ifdef RENDER if (ps) { - ps->Composite = pExaScr->SavedComposite; - ps->Glyphs = pExaScr->SavedGlyphs; - ps->Trapezoids = pExaScr->SavedTrapezoids; - ps->Triangles = pExaScr->SavedTriangles; - ps->AddTraps = pExaScr->SavedAddTraps; + unwrap(pExaScr, ps, Composite); + if (pExaScr->SavedGlyphs) + unwrap(pExaScr, ps, Glyphs); + unwrap(pExaScr, ps, Trapezoids); + unwrap(pExaScr, ps, Triangles); + unwrap(pExaScr, ps, AddTraps); } #endif @@ -832,7 +939,7 @@ exaDriverInit (ScreenPtr pScreen, return FALSE; } - if (!pScreenInfo->CreatePixmap) { + if (!pScreenInfo->CreatePixmap && !pScreenInfo->CreatePixmap2) { if (!pScreenInfo->memoryBase) { LogMessage(X_ERROR, "EXA(%d): ExaDriverRec::memoryBase " "must be non-zero\n", pScreen->myNum); @@ -889,7 +996,6 @@ exaDriverInit (ScreenPtr pScreen, #endif pExaScr = xcalloc (sizeof (ExaScreenPrivRec), 1); - if (!pExaScr) { LogMessage(X_WARNING, "EXA(%d): Failed to allocate screen private\n", pScreen->myNum); @@ -904,51 +1010,38 @@ exaDriverInit (ScreenPtr pScreen, 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 */ - pExaScr->SavedCloseScreen = pScreen->CloseScreen; - pScreen->CloseScreen = exaCloseScreen; - - pExaScr->SavedCreateGC = pScreen->CreateGC; - pScreen->CreateGC = exaCreateGC; - - pExaScr->SavedGetImage = pScreen->GetImage; - pScreen->GetImage = exaGetImage; - - pExaScr->SavedGetSpans = pScreen->GetSpans; - pScreen->GetSpans = ExaCheckGetSpans; - - pExaScr->SavedCopyWindow = pScreen->CopyWindow; - pScreen->CopyWindow = exaCopyWindow; - - pExaScr->SavedChangeWindowAttributes = pScreen->ChangeWindowAttributes; - pScreen->ChangeWindowAttributes = exaChangeWindowAttributes; - - pExaScr->SavedBitmapToRegion = pScreen->BitmapToRegion; - pScreen->BitmapToRegion = exaBitmapToRegion; - - pExaScr->SavedCreateScreenResources = pScreen->CreateScreenResources; - pScreen->CreateScreenResources = exaCreateScreenResources; + if ((pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS) && + !(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) { + wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler); + 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); #ifdef RENDER if (ps) { - pExaScr->SavedComposite = ps->Composite; - ps->Composite = exaComposite; - - if (pScreenInfo->PrepareComposite) { - pExaScr->SavedGlyphs = ps->Glyphs; - ps->Glyphs = exaGlyphs; - } - - pExaScr->SavedTriangles = ps->Triangles; - ps->Triangles = exaTriangles; - - pExaScr->SavedTrapezoids = ps->Trapezoids; - ps->Trapezoids = exaTrapezoids; - - pExaScr->SavedAddTraps = ps->AddTraps; - ps->AddTraps = ExaCheckAddTraps; + 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); } #endif @@ -969,15 +1062,34 @@ exaDriverInit (ScreenPtr pScreen, pScreen->myNum); return FALSE; } - pExaScr->SavedCreatePixmap = pScreen->CreatePixmap; - pScreen->CreatePixmap = exaCreatePixmap; - - pExaScr->SavedDestroyPixmap = pScreen->DestroyPixmap; - pScreen->DestroyPixmap = exaDestroyPixmap; - - pExaScr->SavedModifyPixmapHeader = pScreen->ModifyPixmapHeader; - pScreen->ModifyPixmapHeader = exaModifyPixmapHeader; - if (!pExaScr->info->CreatePixmap) { + 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_is_offscreen = exaPixmapIsOffscreen_mixed; + pExaScr->do_move_in_pixmap = exaMoveInPixmap_mixed; + pExaScr->do_move_out_pixmap = NULL; + } 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_is_offscreen = exaPixmapIsOffscreen_driver; + pExaScr->do_move_in_pixmap = NULL; + pExaScr->do_move_out_pixmap = 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_is_offscreen = exaPixmapIsOffscreen_classic; + pExaScr->do_move_in_pixmap = exaMoveInPixmap_classic; + pExaScr->do_move_out_pixmap = exaMoveOutPixmap_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); @@ -990,7 +1102,7 @@ exaDriverInit (ScreenPtr pScreen, else LogMessage(X_INFO, "EXA(%d): No offscreen pixmaps\n", pScreen->myNum); - if (!pExaScr->info->CreatePixmap) { + 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) { @@ -1074,3 +1186,47 @@ void exaWaitSync(ScreenPtr pScreen) 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); +} |