16747b715Smrg/*
235c4bbdfSmrg * Copyright © 2006 Intel Corporation
36747b715Smrg *
46747b715Smrg * Permission is hereby granted, free of charge, to any person obtaining a
56747b715Smrg * copy of this software and associated documentation files (the "Software"),
66747b715Smrg * to deal in the Software without restriction, including without limitation
76747b715Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
86747b715Smrg * and/or sell copies of the Software, and to permit persons to whom the
96747b715Smrg * Software is furnished to do so, subject to the following conditions:
106747b715Smrg *
116747b715Smrg * The above copyright notice and this permission notice (including the next
126747b715Smrg * paragraph) shall be included in all copies or substantial portions of the
136747b715Smrg * Software.
146747b715Smrg *
156747b715Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
166747b715Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
176747b715Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
186747b715Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
196747b715Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
206747b715Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
216747b715Smrg * SOFTWARE.
226747b715Smrg *
236747b715Smrg * Authors:
246747b715Smrg *    Eric Anholt <eric@anholt.net>
2535c4bbdfSmrg *    Michel Dänzer <michel@tungstengraphics.com>
266747b715Smrg *
276747b715Smrg */
286747b715Smrg
296747b715Smrg#ifdef HAVE_DIX_CONFIG_H
306747b715Smrg#include <dix-config.h>
316747b715Smrg#endif
326747b715Smrg
336747b715Smrg#include <string.h>
346747b715Smrg
356747b715Smrg#include "exa_priv.h"
366747b715Smrg#include "exa.h"
376747b715Smrg
386747b715Smrg#if DEBUG_MIGRATE
396747b715Smrg#define DBG_MIGRATE(a) ErrorF a
406747b715Smrg#else
416747b715Smrg#define DBG_MIGRATE(a)
426747b715Smrg#endif
436747b715Smrg
446747b715Smrg/**
456747b715Smrg * The fallback path for UTS/DFS failing is to just memcpy.  exaCopyDirtyToSys
466747b715Smrg * and exaCopyDirtyToFb both needed to do this loop.
476747b715Smrg */
486747b715Smrgstatic void
4935c4bbdfSmrgexaMemcpyBox(PixmapPtr pPixmap, BoxPtr pbox, CARD8 *src, int src_pitch,
5035c4bbdfSmrg             CARD8 *dst, int dst_pitch)
5135c4bbdfSmrg{
526747b715Smrg    int i, cpp = pPixmap->drawable.bitsPerPixel / 8;
536747b715Smrg    int bytes = (pbox->x2 - pbox->x1) * cpp;
546747b715Smrg
556747b715Smrg    src += pbox->y1 * src_pitch + pbox->x1 * cpp;
566747b715Smrg    dst += pbox->y1 * dst_pitch + pbox->x1 * cpp;
576747b715Smrg
586747b715Smrg    for (i = pbox->y2 - pbox->y1; i; i--) {
5935c4bbdfSmrg        memcpy(dst, src, bytes);
6035c4bbdfSmrg        src += src_pitch;
6135c4bbdfSmrg        dst += dst_pitch;
626747b715Smrg    }
636747b715Smrg}
646747b715Smrg
656747b715Smrg/**
666747b715Smrg * Returns TRUE if the pixmap is dirty (has been modified in its current
676747b715Smrg * location compared to the other), or lacks a private for tracking
686747b715Smrg * dirtiness.
696747b715Smrg */
706747b715Smrgstatic Bool
7135c4bbdfSmrgexaPixmapIsDirty(PixmapPtr pPix)
726747b715Smrg{
7335c4bbdfSmrg    ExaPixmapPriv(pPix);
746747b715Smrg
756747b715Smrg    if (pExaPixmap == NULL)
7635c4bbdfSmrg        EXA_FatalErrorDebugWithRet(("EXA bug: exaPixmapIsDirty was called on a non-exa pixmap.\n"), TRUE);
776747b715Smrg
786747b715Smrg    if (!pExaPixmap->pDamage)
7935c4bbdfSmrg        return FALSE;
806747b715Smrg
816747b715Smrg    return RegionNotEmpty(DamageRegion(pExaPixmap->pDamage)) ||
8235c4bbdfSmrg        !RegionEqual(&pExaPixmap->validSys, &pExaPixmap->validFB);
836747b715Smrg}
846747b715Smrg
856747b715Smrg/**
866747b715Smrg * Returns TRUE if the pixmap is either pinned in FB, or has a sufficient score
876747b715Smrg * to be considered "should be in framebuffer".  That's just anything that has
886747b715Smrg * had more acceleration than fallbacks, or has no score yet.
896747b715Smrg *
906747b715Smrg * Only valid if using a migration scheme that tracks score.
916747b715Smrg */
926747b715Smrgstatic Bool
9335c4bbdfSmrgexaPixmapShouldBeInFB(PixmapPtr pPix)
946747b715Smrg{
9535c4bbdfSmrg    ExaPixmapPriv(pPix);
966747b715Smrg
9735c4bbdfSmrg    if (exaPixmapIsPinned(pPix))
9835c4bbdfSmrg        return TRUE;
996747b715Smrg
1006747b715Smrg    return pExaPixmap->score >= 0;
1016747b715Smrg}
1026747b715Smrg
1036747b715Smrg/**
1046747b715Smrg * If the pixmap is currently dirty, this copies at least the dirty area from
1056747b715Smrg * FB to system or vice versa.  Both areas must be allocated.
1066747b715Smrg */
1076747b715Smrgstatic void
1086747b715SmrgexaCopyDirty(ExaMigrationPtr migrate, RegionPtr pValidDst, RegionPtr pValidSrc,
10935c4bbdfSmrg             Bool (*transfer) (PixmapPtr pPix, int x, int y, int w, int h,
11035c4bbdfSmrg                               char *sys, int sys_pitch), int fallback_index,
11135c4bbdfSmrg             void (*sync) (ScreenPtr pScreen))
1126747b715Smrg{
1136747b715Smrg    PixmapPtr pPixmap = migrate->pPix;
11435c4bbdfSmrg
11535c4bbdfSmrg    ExaPixmapPriv(pPixmap);
11635c4bbdfSmrg    RegionPtr damage = DamageRegion(pExaPixmap->pDamage);
1176747b715Smrg    RegionRec CopyReg;
1186747b715Smrg    Bool save_use_gpu_copy;
1196747b715Smrg    int save_pitch;
1206747b715Smrg    BoxPtr pBox;
1216747b715Smrg    int nbox;
1226747b715Smrg    Bool access_prepared = FALSE;
1236747b715Smrg    Bool need_sync = FALSE;
1246747b715Smrg
1256747b715Smrg    /* Damaged bits are valid in current copy but invalid in other one */
1266747b715Smrg    if (pExaPixmap->use_gpu_copy) {
12735c4bbdfSmrg        RegionUnion(&pExaPixmap->validFB, &pExaPixmap->validFB, damage);
12835c4bbdfSmrg        RegionSubtract(&pExaPixmap->validSys, &pExaPixmap->validSys, damage);
12935c4bbdfSmrg    }
13035c4bbdfSmrg    else {
13135c4bbdfSmrg        RegionUnion(&pExaPixmap->validSys, &pExaPixmap->validSys, damage);
13235c4bbdfSmrg        RegionSubtract(&pExaPixmap->validFB, &pExaPixmap->validFB, damage);
1336747b715Smrg    }
1346747b715Smrg
1356747b715Smrg    RegionEmpty(damage);
1366747b715Smrg
1376747b715Smrg    /* Copy bits valid in source but not in destination */
1386747b715Smrg    RegionNull(&CopyReg);
1396747b715Smrg    RegionSubtract(&CopyReg, pValidSrc, pValidDst);
1406747b715Smrg
1416747b715Smrg    if (migrate->as_dst) {
14235c4bbdfSmrg        ExaScreenPriv(pPixmap->drawable.pScreen);
1436747b715Smrg
14435c4bbdfSmrg        /* XXX: The pending damage region will be marked as damaged after the
14535c4bbdfSmrg         * operation, so it should serve as an upper bound for the region that
14635c4bbdfSmrg         * needs to be synchronized for the operation. Unfortunately, this
14735c4bbdfSmrg         * causes corruption in some cases, e.g. when starting compiz. See
14835c4bbdfSmrg         * https://bugs.freedesktop.org/show_bug.cgi?id=12916 .
14935c4bbdfSmrg         */
15035c4bbdfSmrg        if (pExaScr->optimize_migration) {
15135c4bbdfSmrg            RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage);
1526747b715Smrg
1536747b715Smrg#if DEBUG_MIGRATE
15435c4bbdfSmrg            if (RegionNil(pending_damage)) {
15535c4bbdfSmrg                static Bool firsttime = TRUE;
15635c4bbdfSmrg
15735c4bbdfSmrg                if (firsttime) {
15835c4bbdfSmrg                    ErrorF("%s: Pending damage region empty!\n", __func__);
15935c4bbdfSmrg                    firsttime = FALSE;
16035c4bbdfSmrg                }
16135c4bbdfSmrg            }
1626747b715Smrg#endif
1636747b715Smrg
16435c4bbdfSmrg            /* Try to prevent destination valid region from growing too many
16535c4bbdfSmrg             * rects by filling it up to the extents of the union of the
16635c4bbdfSmrg             * destination valid region and the pending damage region.
16735c4bbdfSmrg             */
16835c4bbdfSmrg            if (RegionNumRects(pValidDst) > 10) {
16935c4bbdfSmrg                BoxRec box;
17035c4bbdfSmrg                BoxPtr pValidExt, pDamageExt;
17135c4bbdfSmrg                RegionRec closure;
17235c4bbdfSmrg
17335c4bbdfSmrg                pValidExt = RegionExtents(pValidDst);
17435c4bbdfSmrg                pDamageExt = RegionExtents(pending_damage);
17535c4bbdfSmrg
17635c4bbdfSmrg                box.x1 = min(pValidExt->x1, pDamageExt->x1);
17735c4bbdfSmrg                box.y1 = min(pValidExt->y1, pDamageExt->y1);
17835c4bbdfSmrg                box.x2 = max(pValidExt->x2, pDamageExt->x2);
17935c4bbdfSmrg                box.y2 = max(pValidExt->y2, pDamageExt->y2);
18035c4bbdfSmrg
18135c4bbdfSmrg                RegionInit(&closure, &box, 0);
18235c4bbdfSmrg                RegionIntersect(&CopyReg, &CopyReg, &closure);
18335c4bbdfSmrg            }
18435c4bbdfSmrg            else
18535c4bbdfSmrg                RegionIntersect(&CopyReg, &CopyReg, pending_damage);
18635c4bbdfSmrg        }
18735c4bbdfSmrg
18835c4bbdfSmrg        /* The caller may provide a region to be subtracted from the calculated
18935c4bbdfSmrg         * dirty region. This is to avoid migration of bits that don't
19035c4bbdfSmrg         * contribute to the result of the operation.
19135c4bbdfSmrg         */
19235c4bbdfSmrg        if (migrate->pReg)
19335c4bbdfSmrg            RegionSubtract(&CopyReg, &CopyReg, migrate->pReg);
19435c4bbdfSmrg    }
19535c4bbdfSmrg    else {
19635c4bbdfSmrg        /* The caller may restrict the region to be migrated for source pixmaps
19735c4bbdfSmrg         * to what's relevant for the operation.
19835c4bbdfSmrg         */
19935c4bbdfSmrg        if (migrate->pReg)
20035c4bbdfSmrg            RegionIntersect(&CopyReg, &CopyReg, migrate->pReg);
2016747b715Smrg    }
2026747b715Smrg
2036747b715Smrg    pBox = RegionRects(&CopyReg);
2046747b715Smrg    nbox = RegionNumRects(&CopyReg);
2056747b715Smrg
2066747b715Smrg    save_use_gpu_copy = pExaPixmap->use_gpu_copy;
2076747b715Smrg    save_pitch = pPixmap->devKind;
2086747b715Smrg    pExaPixmap->use_gpu_copy = TRUE;
2096747b715Smrg    pPixmap->devKind = pExaPixmap->fb_pitch;
2106747b715Smrg
2116747b715Smrg    while (nbox--) {
21235c4bbdfSmrg        pBox->x1 = max(pBox->x1, 0);
21335c4bbdfSmrg        pBox->y1 = max(pBox->y1, 0);
21435c4bbdfSmrg        pBox->x2 = min(pBox->x2, pPixmap->drawable.width);
21535c4bbdfSmrg        pBox->y2 = min(pBox->y2, pPixmap->drawable.height);
21635c4bbdfSmrg
21735c4bbdfSmrg        if (pBox->x1 >= pBox->x2 || pBox->y1 >= pBox->y2)
21835c4bbdfSmrg            continue;
21935c4bbdfSmrg
22035c4bbdfSmrg        if (!transfer || !transfer(pPixmap,
22135c4bbdfSmrg                                   pBox->x1, pBox->y1,
22235c4bbdfSmrg                                   pBox->x2 - pBox->x1,
22335c4bbdfSmrg                                   pBox->y2 - pBox->y1,
22435c4bbdfSmrg                                   (char *) (pExaPixmap->sys_ptr
22535c4bbdfSmrg                                             + pBox->y1 * pExaPixmap->sys_pitch
22635c4bbdfSmrg                                             +
22735c4bbdfSmrg                                             pBox->x1 *
22835c4bbdfSmrg                                             pPixmap->drawable.bitsPerPixel /
22935c4bbdfSmrg                                             8), pExaPixmap->sys_pitch)) {
23035c4bbdfSmrg            if (!access_prepared) {
23135c4bbdfSmrg                ExaDoPrepareAccess(pPixmap, fallback_index);
23235c4bbdfSmrg                access_prepared = TRUE;
23335c4bbdfSmrg            }
23435c4bbdfSmrg            if (fallback_index == EXA_PREPARE_DEST) {
23535c4bbdfSmrg                exaMemcpyBox(pPixmap, pBox,
23635c4bbdfSmrg                             pExaPixmap->sys_ptr, pExaPixmap->sys_pitch,
23735c4bbdfSmrg                             pPixmap->devPrivate.ptr, pPixmap->devKind);
23835c4bbdfSmrg            }
23935c4bbdfSmrg            else {
24035c4bbdfSmrg                exaMemcpyBox(pPixmap, pBox,
24135c4bbdfSmrg                             pPixmap->devPrivate.ptr, pPixmap->devKind,
24235c4bbdfSmrg                             pExaPixmap->sys_ptr, pExaPixmap->sys_pitch);
24335c4bbdfSmrg            }
24435c4bbdfSmrg        }
24535c4bbdfSmrg        else
24635c4bbdfSmrg            need_sync = TRUE;
24735c4bbdfSmrg
24835c4bbdfSmrg        pBox++;
2496747b715Smrg    }
2506747b715Smrg
2516747b715Smrg    pExaPixmap->use_gpu_copy = save_use_gpu_copy;
2526747b715Smrg    pPixmap->devKind = save_pitch;
2536747b715Smrg
2546747b715Smrg    /* Try to prevent source valid region from growing too many rects by
2556747b715Smrg     * removing parts of it which are also in the destination valid region.
2566747b715Smrg     * Removing anything beyond that would lead to data loss.
2576747b715Smrg     */
2586747b715Smrg    if (RegionNumRects(pValidSrc) > 20)
25935c4bbdfSmrg        RegionSubtract(pValidSrc, pValidSrc, pValidDst);
2606747b715Smrg
2616747b715Smrg    /* The copied bits are now valid in destination */
2626747b715Smrg    RegionUnion(pValidDst, pValidDst, &CopyReg);
2636747b715Smrg
2646747b715Smrg    RegionUninit(&CopyReg);
2656747b715Smrg
2666747b715Smrg    if (access_prepared)
26735c4bbdfSmrg        exaFinishAccess(&pPixmap->drawable, fallback_index);
2686747b715Smrg    else if (need_sync && sync)
26935c4bbdfSmrg        sync(pPixmap->drawable.pScreen);
2706747b715Smrg}
2716747b715Smrg
2726747b715Smrg/**
2736747b715Smrg * If the pixmap is currently dirty, this copies at least the dirty area from
2746747b715Smrg * the framebuffer  memory copy to the system memory copy.  Both areas must be
2756747b715Smrg * allocated.
2766747b715Smrg */
2776747b715Smrgvoid
27835c4bbdfSmrgexaCopyDirtyToSys(ExaMigrationPtr migrate)
2796747b715Smrg{
2806747b715Smrg    PixmapPtr pPixmap = migrate->pPix;
28135c4bbdfSmrg
28235c4bbdfSmrg    ExaScreenPriv(pPixmap->drawable.pScreen);
28335c4bbdfSmrg    ExaPixmapPriv(pPixmap);
2846747b715Smrg
2856747b715Smrg    exaCopyDirty(migrate, &pExaPixmap->validSys, &pExaPixmap->validFB,
28635c4bbdfSmrg                 pExaScr->info->DownloadFromScreen, EXA_PREPARE_SRC,
28735c4bbdfSmrg                 exaWaitSync);
2886747b715Smrg}
2896747b715Smrg
2906747b715Smrg/**
2916747b715Smrg * If the pixmap is currently dirty, this copies at least the dirty area from
2926747b715Smrg * the system memory copy to the framebuffer memory copy.  Both areas must be
2936747b715Smrg * allocated.
2946747b715Smrg */
2956747b715Smrgvoid
29635c4bbdfSmrgexaCopyDirtyToFb(ExaMigrationPtr migrate)
2976747b715Smrg{
2986747b715Smrg    PixmapPtr pPixmap = migrate->pPix;
29935c4bbdfSmrg
30035c4bbdfSmrg    ExaScreenPriv(pPixmap->drawable.pScreen);
30135c4bbdfSmrg    ExaPixmapPriv(pPixmap);
3026747b715Smrg
3036747b715Smrg    exaCopyDirty(migrate, &pExaPixmap->validFB, &pExaPixmap->validSys,
30435c4bbdfSmrg                 pExaScr->info->UploadToScreen, EXA_PREPARE_DEST, NULL);
3056747b715Smrg}
3066747b715Smrg
3076747b715Smrg/**
3086747b715Smrg * Allocates a framebuffer copy of the pixmap if necessary, and then copies
3096747b715Smrg * any necessary pixmap data into the framebuffer copy and points the pixmap at
3106747b715Smrg * it.
3116747b715Smrg *
3126747b715Smrg * Note that when first allocated, a pixmap will have FALSE dirty flag.
3136747b715Smrg * This is intentional because pixmap data starts out undefined.  So if we move
3146747b715Smrg * it in due to the first operation against it being accelerated, it will have
3156747b715Smrg * undefined framebuffer contents that we didn't have to upload.  If we do
3166747b715Smrg * moveouts (and moveins) after the first movein, then we will only have to copy
3176747b715Smrg * back and forth if the pixmap was written to after the last synchronization of
3186747b715Smrg * the two copies.  Then, at exaPixmapSave (when the framebuffer copy goes away)
3196747b715Smrg * we mark the pixmap dirty, so that the next exaMoveInPixmap will actually move
3206747b715Smrg * all the data, since it's almost surely all valid now.
3216747b715Smrg */
3226747b715Smrgstatic void
32335c4bbdfSmrgexaDoMoveInPixmap(ExaMigrationPtr migrate)
3246747b715Smrg{
3256747b715Smrg    PixmapPtr pPixmap = migrate->pPix;
3266747b715Smrg    ScreenPtr pScreen = pPixmap->drawable.pScreen;
32735c4bbdfSmrg
32835c4bbdfSmrg    ExaScreenPriv(pScreen);
32935c4bbdfSmrg    ExaPixmapPriv(pPixmap);
3306747b715Smrg
3316747b715Smrg    /* If we're VT-switched away, no touching card memory allowed. */
3326747b715Smrg    if (pExaScr->swappedOut)
33335c4bbdfSmrg        return;
3346747b715Smrg
3356747b715Smrg    /* If we're not allowed to move, then fail. */
3366747b715Smrg    if (exaPixmapIsPinned(pPixmap))
33735c4bbdfSmrg        return;
3386747b715Smrg
3396747b715Smrg    /* Don't migrate in pixmaps which are less than 8bpp.  This avoids a lot of
3406747b715Smrg     * fragility in EXA, and <8bpp is probably not used enough any more to care
3416747b715Smrg     * (at least, not in acceleratd paths).
3426747b715Smrg     */
3436747b715Smrg    if (pPixmap->drawable.bitsPerPixel < 8)
34435c4bbdfSmrg        return;
3456747b715Smrg
3466747b715Smrg    if (pExaPixmap->accel_blocked)
34735c4bbdfSmrg        return;
3486747b715Smrg
3496747b715Smrg    if (pExaPixmap->area == NULL) {
35035c4bbdfSmrg        pExaPixmap->area =
35135c4bbdfSmrg            exaOffscreenAlloc(pScreen, pExaPixmap->fb_size,
35235c4bbdfSmrg                              pExaScr->info->pixmapOffsetAlign, FALSE,
35335c4bbdfSmrg                              exaPixmapSave, (void *) pPixmap);
35435c4bbdfSmrg        if (pExaPixmap->area == NULL)
35535c4bbdfSmrg            return;
35635c4bbdfSmrg
35735c4bbdfSmrg        pExaPixmap->fb_ptr = (CARD8 *) pExaScr->info->memoryBase +
35835c4bbdfSmrg            pExaPixmap->area->offset;
3596747b715Smrg    }
3606747b715Smrg
36135c4bbdfSmrg    exaCopyDirtyToFb(migrate);
3626747b715Smrg
3636747b715Smrg    if (exaPixmapHasGpuCopy(pPixmap))
36435c4bbdfSmrg        return;
3656747b715Smrg
36635c4bbdfSmrg    DBG_MIGRATE(("-> %p (0x%x) (%dx%d) (%c)\n", pPixmap,
36735c4bbdfSmrg                 (ExaGetPixmapPriv(pPixmap)->area ?
36835c4bbdfSmrg                  ExaGetPixmapPriv(pPixmap)->area->offset : 0),
36935c4bbdfSmrg                 pPixmap->drawable.width,
37035c4bbdfSmrg                 pPixmap->drawable.height,
37135c4bbdfSmrg                 exaPixmapIsDirty(pPixmap) ? 'd' : 'c'));
3726747b715Smrg
3736747b715Smrg    pExaPixmap->use_gpu_copy = TRUE;
3746747b715Smrg
3756747b715Smrg    pPixmap->devKind = pExaPixmap->fb_pitch;
3766747b715Smrg    pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
3776747b715Smrg}
3786747b715Smrg
3796747b715Smrgvoid
38035c4bbdfSmrgexaMoveInPixmap_classic(PixmapPtr pPixmap)
3816747b715Smrg{
38235c4bbdfSmrg    static ExaMigrationRec migrate = {.as_dst = FALSE,.as_src = TRUE,
38335c4bbdfSmrg        .pReg = NULL
38435c4bbdfSmrg    };
3856747b715Smrg
3866747b715Smrg    migrate.pPix = pPixmap;
38735c4bbdfSmrg    exaDoMoveInPixmap(&migrate);
3886747b715Smrg}
3896747b715Smrg
3906747b715Smrg/**
3916747b715Smrg * Switches the current active location of the pixmap to system memory, copying
3926747b715Smrg * updated data out if necessary.
3936747b715Smrg */
3946747b715Smrgstatic void
39535c4bbdfSmrgexaDoMoveOutPixmap(ExaMigrationPtr migrate)
3966747b715Smrg{
3976747b715Smrg    PixmapPtr pPixmap = migrate->pPix;
39835c4bbdfSmrg
39935c4bbdfSmrg    ExaPixmapPriv(pPixmap);
4006747b715Smrg
4016747b715Smrg    if (!pExaPixmap->area || exaPixmapIsPinned(pPixmap))
40235c4bbdfSmrg        return;
4036747b715Smrg
40435c4bbdfSmrg    exaCopyDirtyToSys(migrate);
4056747b715Smrg
4066747b715Smrg    if (exaPixmapHasGpuCopy(pPixmap)) {
4076747b715Smrg
40835c4bbdfSmrg        DBG_MIGRATE(("<- %p (%p) (%dx%d) (%c)\n", pPixmap,
40935c4bbdfSmrg                     (void *) (ExaGetPixmapPriv(pPixmap)->area ?
41035c4bbdfSmrg                               ExaGetPixmapPriv(pPixmap)->area->offset : 0),
41135c4bbdfSmrg                     pPixmap->drawable.width,
41235c4bbdfSmrg                     pPixmap->drawable.height,
41335c4bbdfSmrg                     exaPixmapIsDirty(pPixmap) ? 'd' : 'c'));
4146747b715Smrg
41535c4bbdfSmrg        pExaPixmap->use_gpu_copy = FALSE;
4166747b715Smrg
41735c4bbdfSmrg        pPixmap->devKind = pExaPixmap->sys_pitch;
41835c4bbdfSmrg        pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
4196747b715Smrg    }
4206747b715Smrg}
4216747b715Smrg
4226747b715Smrgvoid
42335c4bbdfSmrgexaMoveOutPixmap_classic(PixmapPtr pPixmap)
4246747b715Smrg{
42535c4bbdfSmrg    static ExaMigrationRec migrate = {.as_dst = FALSE,.as_src = TRUE,
42635c4bbdfSmrg        .pReg = NULL
42735c4bbdfSmrg    };
4286747b715Smrg
4296747b715Smrg    migrate.pPix = pPixmap;
43035c4bbdfSmrg    exaDoMoveOutPixmap(&migrate);
4316747b715Smrg}
4326747b715Smrg
4336747b715Smrg/**
4346747b715Smrg * Copies out important pixmap data and removes references to framebuffer area.
4356747b715Smrg * Called when the memory manager decides it's time to kick the pixmap out of
4366747b715Smrg * framebuffer entirely.
4376747b715Smrg */
4386747b715Smrgvoid
43935c4bbdfSmrgexaPixmapSave(ScreenPtr pScreen, ExaOffscreenArea * area)
4406747b715Smrg{
4416747b715Smrg    PixmapPtr pPixmap = area->privData;
44235c4bbdfSmrg
4436747b715Smrg    ExaPixmapPriv(pPixmap);
4446747b715Smrg
4456747b715Smrg    exaMoveOutPixmap(pPixmap);
4466747b715Smrg
4476747b715Smrg    pExaPixmap->fb_ptr = NULL;
4486747b715Smrg    pExaPixmap->area = NULL;
4496747b715Smrg
4506747b715Smrg    /* Mark all FB bits as invalid, so all valid system bits get copied to FB
4516747b715Smrg     * next time */
4526747b715Smrg    RegionEmpty(&pExaPixmap->validFB);
4536747b715Smrg}
4546747b715Smrg
4556747b715Smrg/**
4566747b715Smrg * For the "greedy" migration scheme, pushes the pixmap toward being located in
4576747b715Smrg * framebuffer memory.
4586747b715Smrg */
4596747b715Smrgstatic void
46035c4bbdfSmrgexaMigrateTowardFb(ExaMigrationPtr migrate)
4616747b715Smrg{
4626747b715Smrg    PixmapPtr pPixmap = migrate->pPix;
46335c4bbdfSmrg
46435c4bbdfSmrg    ExaPixmapPriv(pPixmap);
4656747b715Smrg
4666747b715Smrg    if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED) {
46735c4bbdfSmrg        DBG_MIGRATE(("UseScreen: not migrating pinned pixmap %p\n",
46835c4bbdfSmrg                     (void *) pPixmap));
46935c4bbdfSmrg        return;
4706747b715Smrg    }
4716747b715Smrg
4726747b715Smrg    DBG_MIGRATE(("UseScreen %p score %d\n",
47335c4bbdfSmrg                 (void *) pPixmap, pExaPixmap->score));
4746747b715Smrg
4756747b715Smrg    if (pExaPixmap->score == EXA_PIXMAP_SCORE_INIT) {
47635c4bbdfSmrg        exaDoMoveInPixmap(migrate);
47735c4bbdfSmrg        pExaPixmap->score = 0;
4786747b715Smrg    }
4796747b715Smrg
4806747b715Smrg    if (pExaPixmap->score < EXA_PIXMAP_SCORE_MAX)
48135c4bbdfSmrg        pExaPixmap->score++;
4826747b715Smrg
4836747b715Smrg    if (pExaPixmap->score >= EXA_PIXMAP_SCORE_MOVE_IN &&
48435c4bbdfSmrg        !exaPixmapHasGpuCopy(pPixmap)) {
48535c4bbdfSmrg        exaDoMoveInPixmap(migrate);
4866747b715Smrg    }
4876747b715Smrg
4886747b715Smrg    if (exaPixmapHasGpuCopy(pPixmap)) {
48935c4bbdfSmrg        exaCopyDirtyToFb(migrate);
49035c4bbdfSmrg        ExaOffscreenMarkUsed(pPixmap);
49135c4bbdfSmrg    }
49235c4bbdfSmrg    else
49335c4bbdfSmrg        exaCopyDirtyToSys(migrate);
4946747b715Smrg}
4956747b715Smrg
4966747b715Smrg/**
4976747b715Smrg * For the "greedy" migration scheme, pushes the pixmap toward being located in
4986747b715Smrg * system memory.
4996747b715Smrg */
5006747b715Smrgstatic void
50135c4bbdfSmrgexaMigrateTowardSys(ExaMigrationPtr migrate)
5026747b715Smrg{
5036747b715Smrg    PixmapPtr pPixmap = migrate->pPix;
5046747b715Smrg
50535c4bbdfSmrg    ExaPixmapPriv(pPixmap);
50635c4bbdfSmrg
50735c4bbdfSmrg    DBG_MIGRATE(("UseMem: %p score %d\n", (void *) pPixmap,
50835c4bbdfSmrg                 pExaPixmap->score));
5096747b715Smrg
5106747b715Smrg    if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED)
51135c4bbdfSmrg        return;
5126747b715Smrg
5136747b715Smrg    if (pExaPixmap->score == EXA_PIXMAP_SCORE_INIT)
51435c4bbdfSmrg        pExaPixmap->score = 0;
5156747b715Smrg
5166747b715Smrg    if (pExaPixmap->score > EXA_PIXMAP_SCORE_MIN)
51735c4bbdfSmrg        pExaPixmap->score--;
5186747b715Smrg
5196747b715Smrg    if (pExaPixmap->score <= EXA_PIXMAP_SCORE_MOVE_OUT && pExaPixmap->area)
52035c4bbdfSmrg        exaDoMoveOutPixmap(migrate);
5216747b715Smrg
5226747b715Smrg    if (exaPixmapHasGpuCopy(pPixmap)) {
52335c4bbdfSmrg        exaCopyDirtyToFb(migrate);
52435c4bbdfSmrg        ExaOffscreenMarkUsed(pPixmap);
52535c4bbdfSmrg    }
52635c4bbdfSmrg    else
52735c4bbdfSmrg        exaCopyDirtyToSys(migrate);
5286747b715Smrg}
5296747b715Smrg
5306747b715Smrg/**
5316747b715Smrg * If the pixmap has both a framebuffer and system memory copy, this function
5326747b715Smrg * asserts that both of them are the same.
5336747b715Smrg */
5346747b715Smrgstatic Bool
53535c4bbdfSmrgexaAssertNotDirty(PixmapPtr pPixmap)
5366747b715Smrg{
53735c4bbdfSmrg    ExaPixmapPriv(pPixmap);
5386747b715Smrg    CARD8 *dst, *src;
5396747b715Smrg    RegionRec ValidReg;
5406747b715Smrg    int dst_pitch, src_pitch, cpp, y, nbox, save_pitch;
5416747b715Smrg    BoxPtr pBox;
5426747b715Smrg    Bool ret = TRUE, save_use_gpu_copy;
5436747b715Smrg
5446747b715Smrg    if (exaPixmapIsPinned(pPixmap) || pExaPixmap->area == NULL)
54535c4bbdfSmrg        return ret;
5466747b715Smrg
5476747b715Smrg    RegionNull(&ValidReg);
54835c4bbdfSmrg    RegionIntersect(&ValidReg, &pExaPixmap->validFB, &pExaPixmap->validSys);
5496747b715Smrg    nbox = RegionNumRects(&ValidReg);
5506747b715Smrg
5516747b715Smrg    if (!nbox)
55235c4bbdfSmrg        goto out;
5536747b715Smrg
5546747b715Smrg    pBox = RegionRects(&ValidReg);
5556747b715Smrg
5566747b715Smrg    dst_pitch = pExaPixmap->sys_pitch;
5576747b715Smrg    src_pitch = pExaPixmap->fb_pitch;
5586747b715Smrg    cpp = pPixmap->drawable.bitsPerPixel / 8;
5596747b715Smrg
5606747b715Smrg    save_use_gpu_copy = pExaPixmap->use_gpu_copy;
5616747b715Smrg    save_pitch = pPixmap->devKind;
5626747b715Smrg    pExaPixmap->use_gpu_copy = TRUE;
5636747b715Smrg    pPixmap->devKind = pExaPixmap->fb_pitch;
5646747b715Smrg
5656747b715Smrg    if (!ExaDoPrepareAccess(pPixmap, EXA_PREPARE_SRC))
56635c4bbdfSmrg        goto skip;
5676747b715Smrg
5686747b715Smrg    while (nbox--) {
56935c4bbdfSmrg        int rowbytes;
57035c4bbdfSmrg
57135c4bbdfSmrg        pBox->x1 = max(pBox->x1, 0);
57235c4bbdfSmrg        pBox->y1 = max(pBox->y1, 0);
57335c4bbdfSmrg        pBox->x2 = min(pBox->x2, pPixmap->drawable.width);
57435c4bbdfSmrg        pBox->y2 = min(pBox->y2, pPixmap->drawable.height);
57535c4bbdfSmrg
57635c4bbdfSmrg        if (pBox->x1 >= pBox->x2 || pBox->y1 >= pBox->y2)
57735c4bbdfSmrg            continue;
57835c4bbdfSmrg
57935c4bbdfSmrg        rowbytes = (pBox->x2 - pBox->x1) * cpp;
58035c4bbdfSmrg        src =
58135c4bbdfSmrg            (CARD8 *) pPixmap->devPrivate.ptr + pBox->y1 * src_pitch +
58235c4bbdfSmrg            pBox->x1 * cpp;
58335c4bbdfSmrg        dst = pExaPixmap->sys_ptr + pBox->y1 * dst_pitch + pBox->x1 * cpp;
58435c4bbdfSmrg
58535c4bbdfSmrg        for (y = pBox->y1; y < pBox->y2;
58635c4bbdfSmrg             y++, src += src_pitch, dst += dst_pitch) {
58735c4bbdfSmrg            if (memcmp(dst, src, rowbytes) != 0) {
58835c4bbdfSmrg                ret = FALSE;
58935c4bbdfSmrg                exaPixmapDirty(pPixmap, pBox->x1, pBox->y1, pBox->x2, pBox->y2);
59035c4bbdfSmrg                break;
59135c4bbdfSmrg            }
59235c4bbdfSmrg        }
5936747b715Smrg    }
5946747b715Smrg
59535c4bbdfSmrg skip:
5966747b715Smrg    exaFinishAccess(&pPixmap->drawable, EXA_PREPARE_SRC);
5976747b715Smrg
5986747b715Smrg    pExaPixmap->use_gpu_copy = save_use_gpu_copy;
5996747b715Smrg    pPixmap->devKind = save_pitch;
6006747b715Smrg
60135c4bbdfSmrg out:
6026747b715Smrg    RegionUninit(&ValidReg);
6036747b715Smrg    return ret;
6046747b715Smrg}
6056747b715Smrg
6066747b715Smrg/**
6076747b715Smrg * Performs migration of the pixmaps according to the operation information
6086747b715Smrg * provided in pixmaps and can_accel and the migration scheme chosen in the
6096747b715Smrg * config file.
6106747b715Smrg */
6116747b715Smrgvoid
61235c4bbdfSmrgexaDoMigration_classic(ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel)
6136747b715Smrg{
6146747b715Smrg    ScreenPtr pScreen = pixmaps[0].pPix->drawable.pScreen;
61535c4bbdfSmrg
6166747b715Smrg    ExaScreenPriv(pScreen);
6176747b715Smrg    int i, j;
6186747b715Smrg
6196747b715Smrg    /* If this debugging flag is set, check each pixmap for whether it is marked
6206747b715Smrg     * as clean, and if so, actually check if that's the case.  This should help
6216747b715Smrg     * catch issues with failing to mark a drawable as dirty.  While it will
6226747b715Smrg     * catch them late (after the operation happened), it at least explains what
6236747b715Smrg     * went wrong, and instrumenting the code to find what operation happened
6246747b715Smrg     * to the pixmap last shouldn't be hard.
6256747b715Smrg     */
6266747b715Smrg    if (pExaScr->checkDirtyCorrectness) {
62735c4bbdfSmrg        for (i = 0; i < npixmaps; i++) {
62835c4bbdfSmrg            if (!exaPixmapIsDirty(pixmaps[i].pPix) &&
62935c4bbdfSmrg                !exaAssertNotDirty(pixmaps[i].pPix))
63035c4bbdfSmrg                ErrorF("%s: Pixmap %d dirty but not marked as such!\n",
63135c4bbdfSmrg                       __func__, i);
63235c4bbdfSmrg        }
6336747b715Smrg    }
6346747b715Smrg    /* If anything is pinned in system memory, we won't be able to
6356747b715Smrg     * accelerate.
6366747b715Smrg     */
6376747b715Smrg    for (i = 0; i < npixmaps; i++) {
63835c4bbdfSmrg        if (exaPixmapIsPinned(pixmaps[i].pPix) &&
63935c4bbdfSmrg            !exaPixmapHasGpuCopy(pixmaps[i].pPix)) {
64035c4bbdfSmrg            EXA_FALLBACK(("Pixmap %p (%dx%d) pinned in sys\n", pixmaps[i].pPix,
64135c4bbdfSmrg                          pixmaps[i].pPix->drawable.width,
64235c4bbdfSmrg                          pixmaps[i].pPix->drawable.height));
64335c4bbdfSmrg            can_accel = FALSE;
64435c4bbdfSmrg            break;
64535c4bbdfSmrg        }
6466747b715Smrg    }
6476747b715Smrg
6486747b715Smrg    if (pExaScr->migration == ExaMigrationSmart) {
64935c4bbdfSmrg        /* If we've got something as a destination that we shouldn't cause to
65035c4bbdfSmrg         * become newly dirtied, take the unaccelerated route.
65135c4bbdfSmrg         */
65235c4bbdfSmrg        for (i = 0; i < npixmaps; i++) {
65335c4bbdfSmrg            if (pixmaps[i].as_dst && !exaPixmapShouldBeInFB(pixmaps[i].pPix) &&
65435c4bbdfSmrg                !exaPixmapIsDirty(pixmaps[i].pPix)) {
65535c4bbdfSmrg                for (i = 0; i < npixmaps; i++) {
65635c4bbdfSmrg                    if (!exaPixmapIsDirty(pixmaps[i].pPix))
65735c4bbdfSmrg                        exaDoMoveOutPixmap(pixmaps + i);
65835c4bbdfSmrg                }
65935c4bbdfSmrg                return;
66035c4bbdfSmrg            }
66135c4bbdfSmrg        }
66235c4bbdfSmrg
66335c4bbdfSmrg        /* If we aren't going to accelerate, then we migrate everybody toward
66435c4bbdfSmrg         * system memory, and kick out if it's free.
66535c4bbdfSmrg         */
66635c4bbdfSmrg        if (!can_accel) {
66735c4bbdfSmrg            for (i = 0; i < npixmaps; i++) {
66835c4bbdfSmrg                exaMigrateTowardSys(pixmaps + i);
66935c4bbdfSmrg                if (!exaPixmapIsDirty(pixmaps[i].pPix))
67035c4bbdfSmrg                    exaDoMoveOutPixmap(pixmaps + i);
67135c4bbdfSmrg            }
67235c4bbdfSmrg            return;
67335c4bbdfSmrg        }
67435c4bbdfSmrg
67535c4bbdfSmrg        /* Finally, the acceleration path.  Move them all in. */
67635c4bbdfSmrg        for (i = 0; i < npixmaps; i++) {
67735c4bbdfSmrg            exaMigrateTowardFb(pixmaps + i);
67835c4bbdfSmrg            exaDoMoveInPixmap(pixmaps + i);
67935c4bbdfSmrg        }
68035c4bbdfSmrg    }
68135c4bbdfSmrg    else if (pExaScr->migration == ExaMigrationGreedy) {
68235c4bbdfSmrg        /* If we can't accelerate, either because the driver can't or because one of
68335c4bbdfSmrg         * the pixmaps is pinned in system memory, then we migrate everybody toward
68435c4bbdfSmrg         * system memory.
68535c4bbdfSmrg         *
68635c4bbdfSmrg         * We also migrate toward system if all pixmaps involved are currently in
68735c4bbdfSmrg         * system memory -- this can mitigate thrashing when there are significantly
68835c4bbdfSmrg         * more pixmaps active than would fit in memory.
68935c4bbdfSmrg         *
69035c4bbdfSmrg         * If not, then we migrate toward FB so that hopefully acceleration can
69135c4bbdfSmrg         * happen.
69235c4bbdfSmrg         */
69335c4bbdfSmrg        if (!can_accel) {
69435c4bbdfSmrg            for (i = 0; i < npixmaps; i++)
69535c4bbdfSmrg                exaMigrateTowardSys(pixmaps + i);
69635c4bbdfSmrg            return;
69735c4bbdfSmrg        }
69835c4bbdfSmrg
69935c4bbdfSmrg        for (i = 0; i < npixmaps; i++) {
70035c4bbdfSmrg            if (exaPixmapHasGpuCopy(pixmaps[i].pPix)) {
70135c4bbdfSmrg                /* Found one in FB, so move all to FB. */
70235c4bbdfSmrg                for (j = 0; j < npixmaps; j++)
70335c4bbdfSmrg                    exaMigrateTowardFb(pixmaps + i);
70435c4bbdfSmrg                return;
70535c4bbdfSmrg            }
70635c4bbdfSmrg        }
70735c4bbdfSmrg
70835c4bbdfSmrg        /* Nobody's in FB, so move all away from FB. */
70935c4bbdfSmrg        for (i = 0; i < npixmaps; i++)
71035c4bbdfSmrg            exaMigrateTowardSys(pixmaps + i);
71135c4bbdfSmrg    }
71235c4bbdfSmrg    else if (pExaScr->migration == ExaMigrationAlways) {
71335c4bbdfSmrg        /* Always move the pixmaps out if we can't accelerate.  If we can
71435c4bbdfSmrg         * accelerate, try to move them all in.  If that fails, then move them
71535c4bbdfSmrg         * back out.
71635c4bbdfSmrg         */
71735c4bbdfSmrg        if (!can_accel) {
71835c4bbdfSmrg            for (i = 0; i < npixmaps; i++)
71935c4bbdfSmrg                exaDoMoveOutPixmap(pixmaps + i);
72035c4bbdfSmrg            return;
72135c4bbdfSmrg        }
72235c4bbdfSmrg
72335c4bbdfSmrg        /* Now, try to move them all into FB */
72435c4bbdfSmrg        for (i = 0; i < npixmaps; i++) {
72535c4bbdfSmrg            exaDoMoveInPixmap(pixmaps + i);
72635c4bbdfSmrg        }
72735c4bbdfSmrg
72835c4bbdfSmrg        /* If we couldn't fit everything in, abort */
72935c4bbdfSmrg        for (i = 0; i < npixmaps; i++) {
73035c4bbdfSmrg            if (!exaPixmapHasGpuCopy(pixmaps[i].pPix)) {
73135c4bbdfSmrg                return;
73235c4bbdfSmrg            }
73335c4bbdfSmrg        }
73435c4bbdfSmrg
73535c4bbdfSmrg        /* Yay, everything has a gpu copy, mark memory as used */
73635c4bbdfSmrg        for (i = 0; i < npixmaps; i++) {
73735c4bbdfSmrg            ExaOffscreenMarkUsed(pixmaps[i].pPix);
73835c4bbdfSmrg        }
7396747b715Smrg    }
7406747b715Smrg}
7416747b715Smrg
7426747b715Smrgvoid
7436747b715SmrgexaPrepareAccessReg_classic(PixmapPtr pPixmap, int index, RegionPtr pReg)
7446747b715Smrg{
7456747b715Smrg    ExaMigrationRec pixmaps[1];
7466747b715Smrg
7476747b715Smrg    if (index == EXA_PREPARE_DEST || index == EXA_PREPARE_AUX_DEST) {
74835c4bbdfSmrg        pixmaps[0].as_dst = TRUE;
74935c4bbdfSmrg        pixmaps[0].as_src = FALSE;
75035c4bbdfSmrg    }
75135c4bbdfSmrg    else {
75235c4bbdfSmrg        pixmaps[0].as_dst = FALSE;
75335c4bbdfSmrg        pixmaps[0].as_src = TRUE;
7546747b715Smrg    }
7556747b715Smrg    pixmaps[0].pPix = pPixmap;
7566747b715Smrg    pixmaps[0].pReg = pReg;
7576747b715Smrg
7586747b715Smrg    exaDoMigration(pixmaps, 1, FALSE);
7596747b715Smrg
76035c4bbdfSmrg    (void) ExaDoPrepareAccess(pPixmap, index);
7616747b715Smrg}
762