#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include <string.h>
#include "exa_priv.h"
#include "exa.h"
#if DEBUG_MIGRATE
#define DBG_MIGRATE(a) ErrorF a
#else
#define DBG_MIGRATE(a)
#endif
static Bool
exaPixmapIsPinned (PixmapPtr pPix)
{
ExaPixmapPriv (pPix);
return pExaPixmap == NULL || pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED;
}
static void
exaMemcpyBox (PixmapPtr pPixmap, BoxPtr pbox, CARD8 *src, int src_pitch,
CARD8 *dst, int dst_pitch)
{
int i, cpp = pPixmap->drawable.bitsPerPixel / 8;
int bytes = (pbox->x2 - pbox->x1) * cpp;
src += pbox->y1 * src_pitch + pbox->x1 * cpp;
dst += pbox->y1 * dst_pitch + pbox->x1 * cpp;
for (i = pbox->y2 - pbox->y1; i; i--) {
memcpy (dst, src, bytes);
src += src_pitch;
dst += dst_pitch;
}
}
static Bool
exaPixmapIsDirty (PixmapPtr pPix)
{
ExaPixmapPriv (pPix);
return pExaPixmap == NULL ||
REGION_NOTEMPTY (pScreen, DamageRegion(pExaPixmap->pDamage)) ||
!REGION_EQUAL(pScreen, &pExaPixmap->validSys, &pExaPixmap->validFB);
}
static Bool
exaPixmapShouldBeInFB (PixmapPtr pPix)
{
ExaPixmapPriv (pPix);
if (exaPixmapIsPinned (pPix))
return TRUE;
return pExaPixmap->score >= 0;
}
static void
exaCopyDirty(ExaMigrationPtr migrate, RegionPtr pValidDst, RegionPtr pValidSrc,
Bool (*transfer) (PixmapPtr pPix, int x, int y, int w, int h,
char *sys, int sys_pitch), CARD8 *fallback_src,
CARD8 *fallback_dst, int fallback_srcpitch, int fallback_dstpitch,
int fallback_index, void (*sync) (ScreenPtr pScreen))
{
PixmapPtr pPixmap = migrate->pPix;
ExaPixmapPriv (pPixmap);
RegionPtr damage = DamageRegion (pExaPixmap->pDamage);
RegionRec CopyReg;
Bool save_offscreen;
int save_pitch;
BoxPtr pBox;
int nbox;
Bool access_prepared = FALSE;
Bool need_sync = FALSE;
if (exaPixmapIsOffscreen(pPixmap)) {
REGION_UNION(pScreen, &pExaPixmap->validFB, &pExaPixmap->validFB,
damage);
REGION_SUBTRACT(pScreen, &pExaPixmap->validSys, &pExaPixmap->validSys,
damage);
} else {
REGION_UNION(pScreen, &pExaPixmap->validSys, &pExaPixmap->validSys,
damage);
REGION_SUBTRACT(pScreen, &pExaPixmap->validFB, &pExaPixmap->validFB,
damage);
}
REGION_EMPTY(pScreen, damage);
REGION_NULL(pScreen, &CopyReg);
REGION_SUBTRACT(pScreen, &CopyReg, pValidSrc, pValidDst);
if (migrate->as_dst) {
ExaScreenPriv (pPixmap->drawable.pScreen);
if (pExaScr->optimize_migration) {
RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage);
#if DEBUG_MIGRATE
if (REGION_NIL(pending_damage)) {
static Bool firsttime = TRUE;
if (firsttime) {
ErrorF("%s: Pending damage region empty!\n", __func__);
firsttime = FALSE;
}
}
#endif
REGION_INTERSECT(pScreen, &CopyReg, &CopyReg, pending_damage);
}
if (migrate->pReg)
REGION_SUBTRACT(pScreen, &CopyReg, &CopyReg, migrate->pReg);
} else {
if (migrate->pReg)
REGION_INTERSECT(pScreen, &CopyReg, &CopyReg, migrate->pReg);
}
pBox = REGION_RECTS(&CopyReg);
nbox = REGION_NUM_RECTS(&CopyReg);
save_offscreen = pExaPixmap->offscreen;
save_pitch = pPixmap->devKind;
pExaPixmap->offscreen = TRUE;
pPixmap->devKind = pExaPixmap->fb_pitch;
while (nbox--) {
pBox->x1 = max(pBox->x1, 0);
pBox->y1 = max(pBox->y1, 0);
pBox->x2 = min(pBox->x2, pPixmap->drawable.width);
pBox->y2 = min(pBox->y2, pPixmap->drawable.height);
if (pBox->x1 >= pBox->x2 || pBox->y1 >= pBox->y2)
continue;
if (!transfer || !transfer (pPixmap,
pBox->x1, pBox->y1,
pBox->x2 - pBox->x1,
pBox->y2 - pBox->y1,
(char *) (pExaPixmap->sys_ptr
+ pBox->y1 * pExaPixmap->sys_pitch
+ pBox->x1 * pPixmap->drawable.bitsPerPixel / 8),
pExaPixmap->sys_pitch))
{
if (!access_prepared) {
ExaDoPrepareAccess(&pPixmap->drawable, fallback_index);
access_prepared = TRUE;
}
exaMemcpyBox (pPixmap, pBox,
fallback_src, fallback_srcpitch,
fallback_dst, fallback_dstpitch);
} else
need_sync = TRUE;
pBox++;
}
if (access_prepared)
exaFinishAccess(&pPixmap->drawable, fallback_index);
else if (need_sync)
sync (pPixmap->drawable.pScreen);
pExaPixmap->offscreen = save_offscreen;
pPixmap->devKind = save_pitch;
REGION_UNION(pScreen, pValidDst, pValidDst, &CopyReg);
REGION_UNINIT(pScreen, &CopyReg);
}
static void
exaCopyDirtyToSys (ExaMigrationPtr migrate)
{
PixmapPtr pPixmap = migrate->pPix;
ExaScreenPriv (pPixmap->drawable.pScreen);
ExaPixmapPriv (pPixmap);
exaCopyDirty(migrate, &pExaPixmap->validSys, &pExaPixmap->validFB,
pExaScr->info->DownloadFromScreen, pExaPixmap->fb_ptr,
pExaPixmap->sys_ptr, pExaPixmap->fb_pitch,
pExaPixmap->sys_pitch, EXA_PREPARE_SRC, exaWaitSync);
}
static void
exaCopyDirtyToFb (ExaMigrationPtr migrate)
{
PixmapPtr pPixmap = migrate->pPix;
ExaScreenPriv (pPixmap->drawable.pScreen);
ExaPixmapPriv (pPixmap);
exaCopyDirty(migrate, &pExaPixmap->validFB, &pExaPixmap->validSys,
pExaScr->info->UploadToScreen, pExaPixmap->sys_ptr,
pExaPixmap->fb_ptr, pExaPixmap->sys_pitch,
pExaPixmap->fb_pitch, EXA_PREPARE_DEST, exaMarkSync);
}
static void
exaDoMoveInPixmap (ExaMigrationPtr migrate)
{
PixmapPtr pPixmap = migrate->pPix;
ScreenPtr pScreen = pPixmap->drawable.pScreen;
ExaScreenPriv (pScreen);
ExaPixmapPriv (pPixmap);
if (pExaScr->swappedOut)
return;
if (exaPixmapIsPinned(pPixmap))
return;
if (pPixmap->drawable.bitsPerPixel < 8)
return;
if (pExaPixmap->accel_blocked)
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;
}
exaCopyDirtyToFb (migrate);
if (exaPixmapIsOffscreen(pPixmap))
return;
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'));
pExaPixmap->offscreen = TRUE;
pPixmap->devKind = pExaPixmap->fb_pitch;
pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
}
void
exaMoveInPixmap (PixmapPtr pPixmap)
{
static ExaMigrationRec migrate = { .as_dst = FALSE, .as_src = TRUE,
.pReg = NULL };
migrate.pPix = pPixmap;
exaDoMoveInPixmap (&migrate);
}
static void
exaDoMoveOutPixmap (ExaMigrationPtr migrate)
{
PixmapPtr pPixmap = migrate->pPix;
ExaPixmapPriv (pPixmap);
if (!pExaPixmap->area || exaPixmapIsPinned(pPixmap))
return;
exaCopyDirtyToSys (migrate);
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'));
pExaPixmap->offscreen = FALSE;
pPixmap->devKind = pExaPixmap->sys_pitch;
pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
}
}
void
exaMoveOutPixmap (PixmapPtr pPixmap)
{
static ExaMigrationRec migrate = { .as_dst = FALSE, .as_src = TRUE,
.pReg = NULL };
migrate.pPix = pPixmap;
exaDoMoveOutPixmap (&migrate);
}
void
exaPixmapSave (ScreenPtr pScreen, ExaOffscreenArea *area)
{
PixmapPtr pPixmap = area->privData;
ExaPixmapPriv(pPixmap);
exaMoveOutPixmap(pPixmap);
pExaPixmap->fb_ptr = NULL;
pExaPixmap->area = NULL;
REGION_EMPTY(pPixmap->drawable.pScreen, &pExaPixmap->validFB);
}
static void
exaMigrateTowardFb (ExaMigrationPtr migrate)
{
PixmapPtr pPixmap = migrate->pPix;
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) {
exaDoMoveInPixmap(migrate);
pExaPixmap->score = 0;
}
if (pExaPixmap->score < EXA_PIXMAP_SCORE_MAX)
pExaPixmap->score++;
if (pExaPixmap->score >= EXA_PIXMAP_SCORE_MOVE_IN &&
!exaPixmapIsOffscreen(pPixmap))
{
exaDoMoveInPixmap(migrate);
}
ExaOffscreenMarkUsed (pPixmap);
}
static void
exaMigrateTowardSys (ExaMigrationPtr migrate)
{
PixmapPtr pPixmap = migrate->pPix;
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)
exaDoMoveOutPixmap(migrate);
}
static Bool
exaAssertNotDirty (PixmapPtr pPixmap)
{
ExaPixmapPriv (pPixmap);
CARD8 *dst, *src;
RegionRec ValidReg;
int dst_pitch, src_pitch, cpp, y, nbox;
BoxPtr pBox;
Bool ret = TRUE;
if (exaPixmapIsPinned(pPixmap) || pExaPixmap->area == NULL)
return ret;
REGION_NULL(pScreen, &ValidReg);
REGION_INTERSECT(pScreen, &ValidReg, &pExaPixmap->validFB,
&pExaPixmap->validSys);
nbox = REGION_NUM_RECTS(&ValidReg);
if (!nbox)
goto out;
pBox = REGION_RECTS(&ValidReg);
dst_pitch = pExaPixmap->sys_pitch;
src_pitch = pExaPixmap->fb_pitch;
cpp = pPixmap->drawable.bitsPerPixel / 8;
ExaDoPrepareAccess(&pPixmap->drawable, EXA_PREPARE_SRC);
while (nbox--) {
int rowbytes;
pBox->x1 = max(pBox->x1, 0);
pBox->y1 = max(pBox->y1, 0);
pBox->x2 = min(pBox->x2, pPixmap->drawable.width);
pBox->y2 = min(pBox->y2, pPixmap->drawable.height);
if (pBox->x1 >= pBox->x2 || pBox->y1 >= pBox->y2)
continue;
rowbytes = (pBox->x2 - pBox->x1) * cpp;
src = pExaPixmap->fb_ptr + pBox->y1 * src_pitch + pBox->x1 * cpp;
dst = pExaPixmap->sys_ptr + pBox->y1 * dst_pitch + pBox->x1 * cpp;
for (y = pBox->y1; y < pBox->y2;
y++, src += src_pitch, dst += dst_pitch) {
if (memcmp(dst, src, rowbytes) != 0) {
ret = FALSE;
exaPixmapDirty(pPixmap, pBox->x1, pBox->y1, pBox->x2,
pBox->y2);
break;
}
}
}
exaFinishAccess(&pPixmap->drawable, EXA_PREPARE_SRC);
out:
REGION_UNINIT(pScreen, &ValidReg);
return ret;
}
void
exaDoMigration (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel)
{
ScreenPtr pScreen = pixmaps[0].pPix->drawable.pScreen;
ExaScreenPriv(pScreen);
int i, j;
if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS)
return;
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);
}
}
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) {
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))
exaDoMoveOutPixmap (pixmaps + i);
}
return;
}
}
if (!can_accel) {
for (i = 0; i < npixmaps; i++) {
exaMigrateTowardSys (pixmaps + i);
if (!exaPixmapIsDirty (pixmaps[i].pPix))
exaDoMoveOutPixmap (pixmaps + i);
}
return;
}
for (i = 0; i < npixmaps; i++) {
exaMigrateTowardFb(pixmaps + i);
exaDoMoveInPixmap(pixmaps + i);
}
} else if (pExaScr->migration == ExaMigrationGreedy) {
if (!can_accel) {
for (i = 0; i < npixmaps; i++)
exaMigrateTowardSys (pixmaps + i);
return;
}
for (i = 0; i < npixmaps; i++) {
if (exaPixmapIsOffscreen(pixmaps[i].pPix)) {
for (j = 0; j < npixmaps; j++)
exaMigrateTowardFb(pixmaps + i);
return;
}
}
for (i = 0; i < npixmaps; i++)
exaMigrateTowardSys(pixmaps + i);
} else if (pExaScr->migration == ExaMigrationAlways) {
if (!can_accel) {
for (i = 0; i < npixmaps; i++)
exaDoMoveOutPixmap(pixmaps + i);
return;
}
for (i = 0; i < npixmaps; i++) {
exaDoMoveInPixmap(pixmaps + i);
}
for (i = 0; i < npixmaps; i++) {
if (!exaPixmapIsOffscreen(pixmaps[i].pPix)) {
return;
}
}
for (i = 0; i < npixmaps; i++) {
ExaOffscreenMarkUsed (pixmaps[i].pPix);
}
}
}