/** * 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); }
/** * 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); exaMoveOutPixmap(pPixmap); pExaPixmap->fb_ptr = NULL; pExaPixmap->area = NULL; /* Mark all FB bits as invalid, so all valid system bits get copied to FB * next time */ RegionEmpty(&pExaPixmap->validFB); }
/** * exaValidateGC() sets the ops to EXA's implementations, which may be * accelerated or may sync the card and fall back to fb. */ 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. */ #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)) { /* XXX This fixes corruption with tiled pixmaps, but may just be a * workaround for broken drivers */ exaMoveOutPixmap(pGC->tile.pixmap); fbPadPixmap (pGC->tile.pixmap); 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. */ changes &= ~GCTile; } fbValidateGC (pGC, changes, pDrawable); pGC->ops = (GCOps *) &exaOps; }
/** * 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; } } } }