/** * 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; ExaPixmapPrivPtr pExaPixmap; pPixmap = exaGetDrawablePixmap (pDrawable); pExaPixmap = ExaGetPixmapPriv(pPixmap); /* Rehide pixmap pointer if we're doing that. */ if (pExaPixmap != NULL && pExaScr->hideOffscreenPixmapData && pExaPixmap->fb_ptr == pPixmap->devPrivate.ptr) { pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr; } if (pExaScr->info->FinishAccess == NULL) return; if (!exaPixmapIsOffscreen (pPixmap)) return; (*pExaScr->info->FinishAccess) (pPixmap, index); }
/** * Copies out important pixmap data and removes references to framebuffer area. * Called when the memory manager decides it's time to kick the pixmap out of * framebuffer entirely. */ void exaPixmapSave (ScreenPtr pScreen, ExaOffscreenArea *area) { PixmapPtr pPixmap = area->privData; ExaPixmapPriv(pPixmap); RegionPtr pDamageReg = DamageRegion(pExaPixmap->pDamage); DBG_MIGRATE (("Save %p (%p) (%dx%d) (%c)\n", pPixmap, (void*)(ExaGetPixmapPriv(pPixmap)->area ? ExaGetPixmapPriv(pPixmap)->area->offset : 0), pPixmap->drawable.width, pPixmap->drawable.height, exaPixmapIsDirty(pPixmap) ? 'd' : 'c')); if (exaPixmapIsOffscreen(pPixmap)) { exaCopyDirtyToSys (pPixmap); pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr; pPixmap->devKind = pExaPixmap->sys_pitch; pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; } pExaPixmap->fb_ptr = NULL; pExaPixmap->area = NULL; /* Mark all valid bits as damaged, so they'll get copied to FB next time */ REGION_UNION(pPixmap->drawable.pScreen, pDamageReg, pDamageReg, &pExaPixmap->validReg); }
/** * 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) { ScreenPtr pScreen = pDrawable->pScreen; ExaScreenPriv (pScreen); PixmapPtr pPixmap; pPixmap = exaGetDrawablePixmap (pDrawable); if (exaPixmapIsOffscreen (pPixmap)) exaWaitSync (pDrawable->pScreen); else return; /* Unhide pixmap pointer */ if (pPixmap->devPrivate.ptr == NULL) { ExaPixmapPriv (pPixmap); pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr; } if (pExaScr->info->PrepareAccess == NULL) return; if (!(*pExaScr->info->PrepareAccess) (pPixmap, index)) { ExaPixmapPriv (pPixmap); if (pExaPixmap->score != EXA_PIXMAP_SCORE_PINNED) FatalError("Driver failed PrepareAccess on a pinned pixmap\n"); exaMoveOutPixmap (pPixmap); } }
/** * For the "greedy" migration scheme, pushes the pixmap toward being located in * system memory. */ static void exaMigrateTowardSys (PixmapPtr pPixmap) { ExaPixmapPriv (pPixmap); if (pExaPixmap == NULL) { DBG_MIGRATE(("UseMem: ignoring exa-uncontrolled pixmap %p (%s)\n", (pointer)pPixmap, exaPixmapIsOffscreen(pPixmap) ? "s" : "m")); return; } DBG_MIGRATE(("UseMem: %p score %d\n", (pointer)pPixmap, pExaPixmap->score)); if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED) return; if (pExaPixmap->score == EXA_PIXMAP_SCORE_INIT) pExaPixmap->score = 0; if (pExaPixmap->score > EXA_PIXMAP_SCORE_MIN) pExaPixmap->score--; if (pExaPixmap->score <= EXA_PIXMAP_SCORE_MOVE_OUT && pExaPixmap->area) exaMoveOutPixmap (pPixmap); }
/** * Allocates a framebuffer copy of the pixmap if necessary, and then copies * any necessary pixmap data into the framebuffer copy and points the pixmap at * it. * * Note that when first allocated, a pixmap will have FALSE dirty flag. * This is intentional because pixmap data starts out undefined. So if we move * it in due to the first operation against it being accelerated, it will have * undefined framebuffer contents that we didn't have to upload. If we do * moveouts (and moveins) after the first movein, then we will only have to copy * back and forth if the pixmap was written to after the last synchronization of * the two copies. Then, at exaPixmapSave (when the framebuffer copy goes away) * we mark the pixmap dirty, so that the next exaMoveInPixmap will actually move * all the data, since it's almost surely all valid now. */ void exaMoveInPixmap (PixmapPtr pPixmap) { ScreenPtr pScreen = pPixmap->drawable.pScreen; ExaScreenPriv (pScreen); ExaPixmapPriv (pPixmap); /* If we're VT-switched away, no touching card memory allowed. */ if (pExaScr->swappedOut) return; /* If we're already in FB, our work is done. */ if (exaPixmapIsOffscreen(pPixmap)) return; /* If we're not allowed to move, then fail. */ if (exaPixmapIsPinned(pPixmap)) return; /* Don't migrate in pixmaps which are less than 8bpp. This avoids a lot of * fragility in EXA, and <8bpp is probably not used enough any more to care * (at least, not in acceleratd paths). */ if (pPixmap->drawable.bitsPerPixel < 8) return; if (pExaPixmap->area == NULL) { pExaPixmap->area = exaOffscreenAlloc (pScreen, pExaPixmap->fb_size, pExaScr->info->pixmapOffsetAlign, FALSE, exaPixmapSave, (pointer) pPixmap); if (pExaPixmap->area == NULL) return; pExaPixmap->fb_ptr = (CARD8 *) pExaScr->info->memoryBase + pExaPixmap->area->offset; } DBG_MIGRATE (("-> %p (0x%x) (%dx%d) (%c)\n", pPixmap, (ExaGetPixmapPriv(pPixmap)->area ? ExaGetPixmapPriv(pPixmap)->area->offset : 0), pPixmap->drawable.width, pPixmap->drawable.height, exaPixmapIsDirty(pPixmap) ? 'd' : 'c')); exaCopyDirtyToFb (pPixmap); if (pExaScr->hideOffscreenPixmapData) pPixmap->devPrivate.ptr = NULL; else pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr; pPixmap->devKind = pExaPixmap->fb_pitch; pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; }
/** * 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 (exaPixmapIsOffscreen (pPixmap)) return pPixmap; else return NULL; }
/** * For the "greedy" migration scheme, pushes the pixmap toward being located in * framebuffer memory. */ static void exaMigrateTowardFb (PixmapPtr pPixmap) { ExaPixmapPriv (pPixmap); if (pExaPixmap == NULL) { DBG_MIGRATE(("UseScreen: ignoring exa-uncontrolled pixmap %p (%s)\n", (pointer)pPixmap, exaPixmapIsOffscreen(pPixmap) ? "s" : "m")); return; } if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED) { DBG_MIGRATE(("UseScreen: not migrating pinned pixmap %p\n", (pointer)pPixmap)); return; } DBG_MIGRATE(("UseScreen %p score %d\n", (pointer)pPixmap, pExaPixmap->score)); if (pExaPixmap->score == EXA_PIXMAP_SCORE_INIT) { exaMoveInPixmap(pPixmap); pExaPixmap->score = 0; } if (pExaPixmap->score < EXA_PIXMAP_SCORE_MAX) pExaPixmap->score++; if (pExaPixmap->score >= EXA_PIXMAP_SCORE_MOVE_IN && !exaPixmapIsOffscreen(pPixmap)) { exaMoveInPixmap (pPixmap); } ExaOffscreenMarkUsed (pPixmap); }
/** * Switches the current active location of the pixmap to system memory, copying * updated data out if necessary. */ void exaMoveOutPixmap (PixmapPtr pPixmap) { ExaPixmapPriv (pPixmap); if (exaPixmapIsPinned(pPixmap)) return; if (exaPixmapIsOffscreen(pPixmap)) { DBG_MIGRATE (("<- %p (%p) (%dx%d) (%c)\n", pPixmap, (void*)(ExaGetPixmapPriv(pPixmap)->area ? ExaGetPixmapPriv(pPixmap)->area->offset : 0), pPixmap->drawable.width, pPixmap->drawable.height, exaPixmapIsDirty(pPixmap) ? 'd' : 'c')); exaCopyDirtyToSys (pPixmap); pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr; pPixmap->devKind = pExaPixmap->sys_pitch; pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; } }
/** * exaDrawableIsOffscreen() is a convenience wrapper for exaPixmapIsOffscreen(). */ Bool exaDrawableIsOffscreen (DrawablePtr pDrawable) { return exaPixmapIsOffscreen (exaGetDrawablePixmap (pDrawable)); }
/** * 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); int i, j; /* If this debugging flag is set, check each pixmap for whether it is marked * as clean, and if so, actually check if that's the case. This should help * catch issues with failing to mark a drawable as dirty. While it will * catch them late (after the operation happened), it at least explains what * went wrong, and instrumenting the code to find what operation happened * to the pixmap last shouldn't be hard. */ if (pExaScr->checkDirtyCorrectness) { for (i = 0; i < npixmaps; i++) { if (!exaPixmapIsDirty (pixmaps[i].pPix) && !exaAssertNotDirty (pixmaps[i].pPix)) ErrorF("%s: Pixmap %d dirty but not marked as such!\n", __func__, i); } } /* If anything is pinned in system memory, we won't be able to * accelerate. */ for (i = 0; i < npixmaps; i++) { if (exaPixmapIsPinned (pixmaps[i].pPix) && !exaPixmapIsOffscreen (pixmaps[i].pPix)) { EXA_FALLBACK(("Pixmap %p (%dx%d) pinned in sys\n", pixmaps[i].pPix, pixmaps[i].pPix->drawable.width, pixmaps[i].pPix->drawable.height)); can_accel = FALSE; break; } } if (pExaScr->migration == ExaMigrationSmart) { /* If we've got something as a destination that we shouldn't cause to * become newly dirtied, take the unaccelerated route. */ for (i = 0; i < npixmaps; i++) { if (pixmaps[i].as_dst && !exaPixmapShouldBeInFB (pixmaps[i].pPix) && !exaPixmapIsDirty (pixmaps[i].pPix)) { for (i = 0; i < npixmaps; i++) { if (!exaPixmapIsDirty (pixmaps[i].pPix)) exaMoveOutPixmap (pixmaps[i].pPix); } return; } } /* If we aren't going to accelerate, then we migrate everybody toward * system memory, and kick out if it's free. */ if (!can_accel) { for (i = 0; i < npixmaps; i++) { exaMigrateTowardSys (pixmaps[i].pPix); if (!exaPixmapIsDirty (pixmaps[i].pPix)) exaMoveOutPixmap (pixmaps[i].pPix); } return; } /* Finally, the acceleration path. Move them all in. */ for (i = 0; i < npixmaps; i++) { exaMigrateTowardFb(pixmaps[i].pPix); exaMoveInPixmap(pixmaps[i].pPix); } } else if (pExaScr->migration == ExaMigrationGreedy) { /* If we can't accelerate, either because the driver can't or because one of * the pixmaps is pinned in system memory, then we migrate everybody toward * system memory. * * We also migrate toward system if all pixmaps involved are currently in * system memory -- this can mitigate thrashing when there are significantly * more pixmaps active than would fit in memory. * * If not, then we migrate toward FB so that hopefully acceleration can * happen. */ if (!can_accel) { for (i = 0; i < npixmaps; i++) exaMigrateTowardSys (pixmaps[i].pPix); return; } for (i = 0; i < npixmaps; i++) { if (exaPixmapIsOffscreen(pixmaps[i].pPix)) { /* Found one in FB, so move all to FB. */ for (j = 0; j < npixmaps; j++) exaMigrateTowardFb(pixmaps[j].pPix); return; } } /* Nobody's in FB, so move all away from FB. */ for (i = 0; i < npixmaps; i++) exaMigrateTowardSys(pixmaps[i].pPix); } else if (pExaScr->migration == ExaMigrationAlways) { /* Always move the pixmaps out if we can't accelerate. If we can * accelerate, try to move them all in. If that fails, then move them * back out. */ if (!can_accel) { for (i = 0; i < npixmaps; i++) exaMoveOutPixmap(pixmaps[i].pPix); return; } /* Now, try to move them all into FB */ for (i = 0; i < npixmaps; i++) { exaMoveInPixmap(pixmaps[i].pPix); ExaOffscreenMarkUsed (pixmaps[i].pPix); } /* If we couldn't fit everything in, then kick back out */ for (i = 0; i < npixmaps; i++) { if (!exaPixmapIsOffscreen(pixmaps[i].pPix)) { EXA_FALLBACK(("Pixmap %p (%dx%d) not in fb\n", pixmaps[i].pPix, pixmaps[i].pPix->drawable.width, pixmaps[i].pPix->drawable.height)); for (j = 0; j < npixmaps; j++) exaMoveOutPixmap(pixmaps[j].pPix); break; } } } }
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 (exaPixmapIsOffscreen (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); 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); } ret = TRUE; } out: REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff); return ret; }
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 (!exaPixmapIsOffscreen (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); }
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 (exaPixmapIsOffscreen(pDstPixmap)) { /* Normal blitting. */ if (exaPixmapIsOffscreen(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. */ } else if (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; }
/* 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 || !exaPixmapIsOffscreen(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; }