1706f2543Smrg/*
2706f2543Smrg * Copyright � 2006 Intel Corporation
3706f2543Smrg *
4706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5706f2543Smrg * copy of this software and associated documentation files (the "Software"),
6706f2543Smrg * to deal in the Software without restriction, including without limitation
7706f2543Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8706f2543Smrg * and/or sell copies of the Software, and to permit persons to whom the
9706f2543Smrg * Software is furnished to do so, subject to the following conditions:
10706f2543Smrg *
11706f2543Smrg * The above copyright notice and this permission notice (including the next
12706f2543Smrg * paragraph) shall be included in all copies or substantial portions of the
13706f2543Smrg * Software.
14706f2543Smrg *
15706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16706f2543Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17706f2543Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18706f2543Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19706f2543Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20706f2543Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21706f2543Smrg * SOFTWARE.
22706f2543Smrg *
23706f2543Smrg * Authors:
24706f2543Smrg *    Eric Anholt <eric@anholt.net>
25706f2543Smrg *    Michel D�nzer <michel@tungstengraphics.com>
26706f2543Smrg *
27706f2543Smrg */
28706f2543Smrg
29706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
30706f2543Smrg#include <dix-config.h>
31706f2543Smrg#endif
32706f2543Smrg
33706f2543Smrg#include <string.h>
34706f2543Smrg
35706f2543Smrg#include "exa_priv.h"
36706f2543Smrg#include "exa.h"
37706f2543Smrg
38706f2543Smrg#if DEBUG_MIGRATE
39706f2543Smrg#define DBG_MIGRATE(a) ErrorF a
40706f2543Smrg#else
41706f2543Smrg#define DBG_MIGRATE(a)
42706f2543Smrg#endif
43706f2543Smrg
44706f2543Smrg/**
45706f2543Smrg * The fallback path for UTS/DFS failing is to just memcpy.  exaCopyDirtyToSys
46706f2543Smrg * and exaCopyDirtyToFb both needed to do this loop.
47706f2543Smrg */
48706f2543Smrgstatic void
49706f2543SmrgexaMemcpyBox (PixmapPtr pPixmap, BoxPtr pbox, CARD8 *src, int src_pitch,
50706f2543Smrg	      CARD8 *dst, int dst_pitch)
51706f2543Smrg {
52706f2543Smrg    int i, cpp = pPixmap->drawable.bitsPerPixel / 8;
53706f2543Smrg    int bytes = (pbox->x2 - pbox->x1) * cpp;
54706f2543Smrg
55706f2543Smrg    src += pbox->y1 * src_pitch + pbox->x1 * cpp;
56706f2543Smrg    dst += pbox->y1 * dst_pitch + pbox->x1 * cpp;
57706f2543Smrg
58706f2543Smrg    for (i = pbox->y2 - pbox->y1; i; i--) {
59706f2543Smrg	memcpy (dst, src, bytes);
60706f2543Smrg	src += src_pitch;
61706f2543Smrg	dst += dst_pitch;
62706f2543Smrg    }
63706f2543Smrg}
64706f2543Smrg
65706f2543Smrg/**
66706f2543Smrg * Returns TRUE if the pixmap is dirty (has been modified in its current
67706f2543Smrg * location compared to the other), or lacks a private for tracking
68706f2543Smrg * dirtiness.
69706f2543Smrg */
70706f2543Smrgstatic Bool
71706f2543SmrgexaPixmapIsDirty (PixmapPtr pPix)
72706f2543Smrg{
73706f2543Smrg    ExaPixmapPriv (pPix);
74706f2543Smrg
75706f2543Smrg    if (pExaPixmap == NULL)
76706f2543Smrg	EXA_FatalErrorDebugWithRet(("EXA bug: exaPixmapIsDirty was called on a non-exa pixmap.\n"), TRUE);
77706f2543Smrg
78706f2543Smrg    if (!pExaPixmap->pDamage)
79706f2543Smrg	return FALSE;
80706f2543Smrg
81706f2543Smrg    return RegionNotEmpty(DamageRegion(pExaPixmap->pDamage)) ||
82706f2543Smrg	!RegionEqual(&pExaPixmap->validSys, &pExaPixmap->validFB);
83706f2543Smrg}
84706f2543Smrg
85706f2543Smrg/**
86706f2543Smrg * Returns TRUE if the pixmap is either pinned in FB, or has a sufficient score
87706f2543Smrg * to be considered "should be in framebuffer".  That's just anything that has
88706f2543Smrg * had more acceleration than fallbacks, or has no score yet.
89706f2543Smrg *
90706f2543Smrg * Only valid if using a migration scheme that tracks score.
91706f2543Smrg */
92706f2543Smrgstatic Bool
93706f2543SmrgexaPixmapShouldBeInFB (PixmapPtr pPix)
94706f2543Smrg{
95706f2543Smrg    ExaPixmapPriv (pPix);
96706f2543Smrg
97706f2543Smrg    if (exaPixmapIsPinned (pPix))
98706f2543Smrg	return TRUE;
99706f2543Smrg
100706f2543Smrg    return pExaPixmap->score >= 0;
101706f2543Smrg}
102706f2543Smrg
103706f2543Smrg/**
104706f2543Smrg * If the pixmap is currently dirty, this copies at least the dirty area from
105706f2543Smrg * FB to system or vice versa.  Both areas must be allocated.
106706f2543Smrg */
107706f2543Smrgstatic void
108706f2543SmrgexaCopyDirty(ExaMigrationPtr migrate, RegionPtr pValidDst, RegionPtr pValidSrc,
109706f2543Smrg	     Bool (*transfer) (PixmapPtr pPix, int x, int y, int w, int h,
110706f2543Smrg			       char *sys, int sys_pitch), int fallback_index,
111706f2543Smrg	     void (*sync) (ScreenPtr pScreen))
112706f2543Smrg{
113706f2543Smrg    PixmapPtr pPixmap = migrate->pPix;
114706f2543Smrg    ExaPixmapPriv (pPixmap);
115706f2543Smrg    RegionPtr damage = DamageRegion (pExaPixmap->pDamage);
116706f2543Smrg    RegionRec CopyReg;
117706f2543Smrg    Bool save_use_gpu_copy;
118706f2543Smrg    int save_pitch;
119706f2543Smrg    BoxPtr pBox;
120706f2543Smrg    int nbox;
121706f2543Smrg    Bool access_prepared = FALSE;
122706f2543Smrg    Bool need_sync = FALSE;
123706f2543Smrg
124706f2543Smrg    /* Damaged bits are valid in current copy but invalid in other one */
125706f2543Smrg    if (pExaPixmap->use_gpu_copy) {
126706f2543Smrg	RegionUnion(&pExaPixmap->validFB, &pExaPixmap->validFB,
127706f2543Smrg		     damage);
128706f2543Smrg	RegionSubtract(&pExaPixmap->validSys, &pExaPixmap->validSys,
129706f2543Smrg			damage);
130706f2543Smrg    } else {
131706f2543Smrg	RegionUnion(&pExaPixmap->validSys, &pExaPixmap->validSys,
132706f2543Smrg		     damage);
133706f2543Smrg	RegionSubtract(&pExaPixmap->validFB, &pExaPixmap->validFB,
134706f2543Smrg			damage);
135706f2543Smrg    }
136706f2543Smrg
137706f2543Smrg    RegionEmpty(damage);
138706f2543Smrg
139706f2543Smrg    /* Copy bits valid in source but not in destination */
140706f2543Smrg    RegionNull(&CopyReg);
141706f2543Smrg    RegionSubtract(&CopyReg, pValidSrc, pValidDst);
142706f2543Smrg
143706f2543Smrg    if (migrate->as_dst) {
144706f2543Smrg	ExaScreenPriv (pPixmap->drawable.pScreen);
145706f2543Smrg
146706f2543Smrg	/* XXX: The pending damage region will be marked as damaged after the
147706f2543Smrg	 * operation, so it should serve as an upper bound for the region that
148706f2543Smrg	 * needs to be synchronized for the operation. Unfortunately, this
149706f2543Smrg	 * causes corruption in some cases, e.g. when starting compiz. See
150706f2543Smrg	 * https://bugs.freedesktop.org/show_bug.cgi?id=12916 .
151706f2543Smrg	 */
152706f2543Smrg	if (pExaScr->optimize_migration) {
153706f2543Smrg	    RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage);
154706f2543Smrg
155706f2543Smrg#if DEBUG_MIGRATE
156706f2543Smrg	    if (RegionNil(pending_damage)) {
157706f2543Smrg		static Bool firsttime = TRUE;
158706f2543Smrg
159706f2543Smrg		if (firsttime) {
160706f2543Smrg		    ErrorF("%s: Pending damage region empty!\n", __func__);
161706f2543Smrg		    firsttime = FALSE;
162706f2543Smrg		}
163706f2543Smrg	    }
164706f2543Smrg#endif
165706f2543Smrg
166706f2543Smrg	    /* Try to prevent destination valid region from growing too many
167706f2543Smrg	     * rects by filling it up to the extents of the union of the
168706f2543Smrg	     * destination valid region and the pending damage region.
169706f2543Smrg	     */
170706f2543Smrg	    if (RegionNumRects(pValidDst) > 10) {
171706f2543Smrg		BoxRec box;
172706f2543Smrg		BoxPtr pValidExt, pDamageExt;
173706f2543Smrg		RegionRec closure;
174706f2543Smrg
175706f2543Smrg		pValidExt = RegionExtents(pValidDst);
176706f2543Smrg		pDamageExt = RegionExtents(pending_damage);
177706f2543Smrg
178706f2543Smrg		box.x1 = min(pValidExt->x1, pDamageExt->x1);
179706f2543Smrg		box.y1 = min(pValidExt->y1, pDamageExt->y1);
180706f2543Smrg		box.x2 = max(pValidExt->x2, pDamageExt->x2);
181706f2543Smrg		box.y2 = max(pValidExt->y2, pDamageExt->y2);
182706f2543Smrg
183706f2543Smrg		RegionInit(&closure, &box, 0);
184706f2543Smrg		RegionIntersect(&CopyReg, &CopyReg, &closure);
185706f2543Smrg	    } else
186706f2543Smrg		RegionIntersect(&CopyReg, &CopyReg, pending_damage);
187706f2543Smrg	}
188706f2543Smrg
189706f2543Smrg	/* The caller may provide a region to be subtracted from the calculated
190706f2543Smrg	 * dirty region. This is to avoid migration of bits that don't
191706f2543Smrg	 * contribute to the result of the operation.
192706f2543Smrg	 */
193706f2543Smrg	if (migrate->pReg)
194706f2543Smrg	    RegionSubtract(&CopyReg, &CopyReg, migrate->pReg);
195706f2543Smrg    } else {
196706f2543Smrg	/* The caller may restrict the region to be migrated for source pixmaps
197706f2543Smrg	 * to what's relevant for the operation.
198706f2543Smrg	 */
199706f2543Smrg	if (migrate->pReg)
200706f2543Smrg	    RegionIntersect(&CopyReg, &CopyReg, migrate->pReg);
201706f2543Smrg    }
202706f2543Smrg
203706f2543Smrg    pBox = RegionRects(&CopyReg);
204706f2543Smrg    nbox = RegionNumRects(&CopyReg);
205706f2543Smrg
206706f2543Smrg    save_use_gpu_copy = pExaPixmap->use_gpu_copy;
207706f2543Smrg    save_pitch = pPixmap->devKind;
208706f2543Smrg    pExaPixmap->use_gpu_copy = TRUE;
209706f2543Smrg    pPixmap->devKind = pExaPixmap->fb_pitch;
210706f2543Smrg
211706f2543Smrg    while (nbox--) {
212706f2543Smrg	pBox->x1 = max(pBox->x1, 0);
213706f2543Smrg	pBox->y1 = max(pBox->y1, 0);
214706f2543Smrg	pBox->x2 = min(pBox->x2, pPixmap->drawable.width);
215706f2543Smrg	pBox->y2 = min(pBox->y2, pPixmap->drawable.height);
216706f2543Smrg
217706f2543Smrg	if (pBox->x1 >= pBox->x2 || pBox->y1 >= pBox->y2)
218706f2543Smrg	    continue;
219706f2543Smrg
220706f2543Smrg	if (!transfer || !transfer (pPixmap,
221706f2543Smrg				    pBox->x1, pBox->y1,
222706f2543Smrg				    pBox->x2 - pBox->x1,
223706f2543Smrg				    pBox->y2 - pBox->y1,
224706f2543Smrg				    (char *) (pExaPixmap->sys_ptr
225706f2543Smrg				    + pBox->y1 * pExaPixmap->sys_pitch
226706f2543Smrg				    + pBox->x1 * pPixmap->drawable.bitsPerPixel / 8),
227706f2543Smrg				    pExaPixmap->sys_pitch))
228706f2543Smrg	{
229706f2543Smrg	    if (!access_prepared) {
230706f2543Smrg		ExaDoPrepareAccess(pPixmap, fallback_index);
231706f2543Smrg		access_prepared = TRUE;
232706f2543Smrg	    }
233706f2543Smrg	    if (fallback_index == EXA_PREPARE_DEST) {
234706f2543Smrg		exaMemcpyBox (pPixmap, pBox,
235706f2543Smrg			      pExaPixmap->sys_ptr, pExaPixmap->sys_pitch,
236706f2543Smrg			      pPixmap->devPrivate.ptr, pPixmap->devKind);
237706f2543Smrg	    } else {
238706f2543Smrg		exaMemcpyBox (pPixmap, pBox,
239706f2543Smrg			      pPixmap->devPrivate.ptr, pPixmap->devKind,
240706f2543Smrg			      pExaPixmap->sys_ptr, pExaPixmap->sys_pitch);
241706f2543Smrg	    }
242706f2543Smrg	} else
243706f2543Smrg	    need_sync = TRUE;
244706f2543Smrg
245706f2543Smrg	pBox++;
246706f2543Smrg    }
247706f2543Smrg
248706f2543Smrg    pExaPixmap->use_gpu_copy = save_use_gpu_copy;
249706f2543Smrg    pPixmap->devKind = save_pitch;
250706f2543Smrg
251706f2543Smrg    /* Try to prevent source valid region from growing too many rects by
252706f2543Smrg     * removing parts of it which are also in the destination valid region.
253706f2543Smrg     * Removing anything beyond that would lead to data loss.
254706f2543Smrg     */
255706f2543Smrg    if (RegionNumRects(pValidSrc) > 20)
256706f2543Smrg	RegionSubtract(pValidSrc, pValidSrc, pValidDst);
257706f2543Smrg
258706f2543Smrg    /* The copied bits are now valid in destination */
259706f2543Smrg    RegionUnion(pValidDst, pValidDst, &CopyReg);
260706f2543Smrg
261706f2543Smrg    RegionUninit(&CopyReg);
262706f2543Smrg
263706f2543Smrg    if (access_prepared)
264706f2543Smrg	exaFinishAccess(&pPixmap->drawable, fallback_index);
265706f2543Smrg    else if (need_sync && sync)
266706f2543Smrg	sync (pPixmap->drawable.pScreen);
267706f2543Smrg}
268706f2543Smrg
269706f2543Smrg/**
270706f2543Smrg * If the pixmap is currently dirty, this copies at least the dirty area from
271706f2543Smrg * the framebuffer  memory copy to the system memory copy.  Both areas must be
272706f2543Smrg * allocated.
273706f2543Smrg */
274706f2543Smrgvoid
275706f2543SmrgexaCopyDirtyToSys (ExaMigrationPtr migrate)
276706f2543Smrg{
277706f2543Smrg    PixmapPtr pPixmap = migrate->pPix;
278706f2543Smrg    ExaScreenPriv (pPixmap->drawable.pScreen);
279706f2543Smrg    ExaPixmapPriv (pPixmap);
280706f2543Smrg
281706f2543Smrg    exaCopyDirty(migrate, &pExaPixmap->validSys, &pExaPixmap->validFB,
282706f2543Smrg		 pExaScr->info->DownloadFromScreen, EXA_PREPARE_SRC,
283706f2543Smrg		 exaWaitSync);
284706f2543Smrg}
285706f2543Smrg
286706f2543Smrg/**
287706f2543Smrg * If the pixmap is currently dirty, this copies at least the dirty area from
288706f2543Smrg * the system memory copy to the framebuffer memory copy.  Both areas must be
289706f2543Smrg * allocated.
290706f2543Smrg */
291706f2543Smrgvoid
292706f2543SmrgexaCopyDirtyToFb (ExaMigrationPtr migrate)
293706f2543Smrg{
294706f2543Smrg    PixmapPtr pPixmap = migrate->pPix;
295706f2543Smrg    ExaScreenPriv (pPixmap->drawable.pScreen);
296706f2543Smrg    ExaPixmapPriv (pPixmap);
297706f2543Smrg
298706f2543Smrg    exaCopyDirty(migrate, &pExaPixmap->validFB, &pExaPixmap->validSys,
299706f2543Smrg		 pExaScr->info->UploadToScreen, EXA_PREPARE_DEST, NULL);
300706f2543Smrg}
301706f2543Smrg
302706f2543Smrg/**
303706f2543Smrg * Allocates a framebuffer copy of the pixmap if necessary, and then copies
304706f2543Smrg * any necessary pixmap data into the framebuffer copy and points the pixmap at
305706f2543Smrg * it.
306706f2543Smrg *
307706f2543Smrg * Note that when first allocated, a pixmap will have FALSE dirty flag.
308706f2543Smrg * This is intentional because pixmap data starts out undefined.  So if we move
309706f2543Smrg * it in due to the first operation against it being accelerated, it will have
310706f2543Smrg * undefined framebuffer contents that we didn't have to upload.  If we do
311706f2543Smrg * moveouts (and moveins) after the first movein, then we will only have to copy
312706f2543Smrg * back and forth if the pixmap was written to after the last synchronization of
313706f2543Smrg * the two copies.  Then, at exaPixmapSave (when the framebuffer copy goes away)
314706f2543Smrg * we mark the pixmap dirty, so that the next exaMoveInPixmap will actually move
315706f2543Smrg * all the data, since it's almost surely all valid now.
316706f2543Smrg */
317706f2543Smrgstatic void
318706f2543SmrgexaDoMoveInPixmap (ExaMigrationPtr migrate)
319706f2543Smrg{
320706f2543Smrg    PixmapPtr pPixmap = migrate->pPix;
321706f2543Smrg    ScreenPtr pScreen = pPixmap->drawable.pScreen;
322706f2543Smrg    ExaScreenPriv (pScreen);
323706f2543Smrg    ExaPixmapPriv (pPixmap);
324706f2543Smrg
325706f2543Smrg    /* If we're VT-switched away, no touching card memory allowed. */
326706f2543Smrg    if (pExaScr->swappedOut)
327706f2543Smrg	return;
328706f2543Smrg
329706f2543Smrg    /* If we're not allowed to move, then fail. */
330706f2543Smrg    if (exaPixmapIsPinned(pPixmap))
331706f2543Smrg	return;
332706f2543Smrg
333706f2543Smrg    /* Don't migrate in pixmaps which are less than 8bpp.  This avoids a lot of
334706f2543Smrg     * fragility in EXA, and <8bpp is probably not used enough any more to care
335706f2543Smrg     * (at least, not in acceleratd paths).
336706f2543Smrg     */
337706f2543Smrg    if (pPixmap->drawable.bitsPerPixel < 8)
338706f2543Smrg	return;
339706f2543Smrg
340706f2543Smrg    if (pExaPixmap->accel_blocked)
341706f2543Smrg	return;
342706f2543Smrg
343706f2543Smrg    if (pExaPixmap->area == NULL) {
344706f2543Smrg	pExaPixmap->area =
345706f2543Smrg	    exaOffscreenAlloc (pScreen, pExaPixmap->fb_size,
346706f2543Smrg			       pExaScr->info->pixmapOffsetAlign, FALSE,
347706f2543Smrg                               exaPixmapSave, (pointer) pPixmap);
348706f2543Smrg	if (pExaPixmap->area == NULL)
349706f2543Smrg	    return;
350706f2543Smrg
351706f2543Smrg	pExaPixmap->fb_ptr = (CARD8 *) pExaScr->info->memoryBase +
352706f2543Smrg				       pExaPixmap->area->offset;
353706f2543Smrg    }
354706f2543Smrg
355706f2543Smrg    exaCopyDirtyToFb (migrate);
356706f2543Smrg
357706f2543Smrg    if (exaPixmapHasGpuCopy(pPixmap))
358706f2543Smrg	return;
359706f2543Smrg
360706f2543Smrg    DBG_MIGRATE (("-> %p (0x%x) (%dx%d) (%c)\n", pPixmap,
361706f2543Smrg		  (ExaGetPixmapPriv(pPixmap)->area ?
362706f2543Smrg                   ExaGetPixmapPriv(pPixmap)->area->offset : 0),
363706f2543Smrg		  pPixmap->drawable.width,
364706f2543Smrg		  pPixmap->drawable.height,
365706f2543Smrg		  exaPixmapIsDirty(pPixmap) ? 'd' : 'c'));
366706f2543Smrg
367706f2543Smrg    pExaPixmap->use_gpu_copy = TRUE;
368706f2543Smrg
369706f2543Smrg    pPixmap->devKind = pExaPixmap->fb_pitch;
370706f2543Smrg    pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
371706f2543Smrg}
372706f2543Smrg
373706f2543Smrgvoid
374706f2543SmrgexaMoveInPixmap_classic (PixmapPtr pPixmap)
375706f2543Smrg{
376706f2543Smrg    static ExaMigrationRec migrate = { .as_dst = FALSE, .as_src = TRUE,
377706f2543Smrg				       .pReg = NULL };
378706f2543Smrg
379706f2543Smrg    migrate.pPix = pPixmap;
380706f2543Smrg    exaDoMoveInPixmap (&migrate);
381706f2543Smrg}
382706f2543Smrg
383706f2543Smrg/**
384706f2543Smrg * Switches the current active location of the pixmap to system memory, copying
385706f2543Smrg * updated data out if necessary.
386706f2543Smrg */
387706f2543Smrgstatic void
388706f2543SmrgexaDoMoveOutPixmap (ExaMigrationPtr migrate)
389706f2543Smrg{
390706f2543Smrg    PixmapPtr pPixmap = migrate->pPix;
391706f2543Smrg    ExaPixmapPriv (pPixmap);
392706f2543Smrg
393706f2543Smrg    if (!pExaPixmap->area || exaPixmapIsPinned(pPixmap))
394706f2543Smrg	return;
395706f2543Smrg
396706f2543Smrg    exaCopyDirtyToSys (migrate);
397706f2543Smrg
398706f2543Smrg    if (exaPixmapHasGpuCopy(pPixmap)) {
399706f2543Smrg
400706f2543Smrg	DBG_MIGRATE (("<- %p (%p) (%dx%d) (%c)\n", pPixmap,
401706f2543Smrg		      (void*)(ExaGetPixmapPriv(pPixmap)->area ?
402706f2543Smrg			      ExaGetPixmapPriv(pPixmap)->area->offset : 0),
403706f2543Smrg		      pPixmap->drawable.width,
404706f2543Smrg		      pPixmap->drawable.height,
405706f2543Smrg		      exaPixmapIsDirty(pPixmap) ? 'd' : 'c'));
406706f2543Smrg
407706f2543Smrg	pExaPixmap->use_gpu_copy = FALSE;
408706f2543Smrg
409706f2543Smrg	pPixmap->devKind = pExaPixmap->sys_pitch;
410706f2543Smrg	pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
411706f2543Smrg    }
412706f2543Smrg}
413706f2543Smrg
414706f2543Smrgvoid
415706f2543SmrgexaMoveOutPixmap_classic (PixmapPtr pPixmap)
416706f2543Smrg{
417706f2543Smrg    static ExaMigrationRec migrate = { .as_dst = FALSE, .as_src = TRUE,
418706f2543Smrg				       .pReg = NULL };
419706f2543Smrg
420706f2543Smrg    migrate.pPix = pPixmap;
421706f2543Smrg    exaDoMoveOutPixmap (&migrate);
422706f2543Smrg}
423706f2543Smrg
424706f2543Smrg
425706f2543Smrg/**
426706f2543Smrg * Copies out important pixmap data and removes references to framebuffer area.
427706f2543Smrg * Called when the memory manager decides it's time to kick the pixmap out of
428706f2543Smrg * framebuffer entirely.
429706f2543Smrg */
430706f2543Smrgvoid
431706f2543SmrgexaPixmapSave (ScreenPtr pScreen, ExaOffscreenArea *area)
432706f2543Smrg{
433706f2543Smrg    PixmapPtr pPixmap = area->privData;
434706f2543Smrg    ExaPixmapPriv(pPixmap);
435706f2543Smrg
436706f2543Smrg    exaMoveOutPixmap(pPixmap);
437706f2543Smrg
438706f2543Smrg    pExaPixmap->fb_ptr = NULL;
439706f2543Smrg    pExaPixmap->area = NULL;
440706f2543Smrg
441706f2543Smrg    /* Mark all FB bits as invalid, so all valid system bits get copied to FB
442706f2543Smrg     * next time */
443706f2543Smrg    RegionEmpty(&pExaPixmap->validFB);
444706f2543Smrg}
445706f2543Smrg
446706f2543Smrg/**
447706f2543Smrg * For the "greedy" migration scheme, pushes the pixmap toward being located in
448706f2543Smrg * framebuffer memory.
449706f2543Smrg */
450706f2543Smrgstatic void
451706f2543SmrgexaMigrateTowardFb (ExaMigrationPtr migrate)
452706f2543Smrg{
453706f2543Smrg    PixmapPtr pPixmap = migrate->pPix;
454706f2543Smrg    ExaPixmapPriv (pPixmap);
455706f2543Smrg
456706f2543Smrg    if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED) {
457706f2543Smrg	DBG_MIGRATE(("UseScreen: not migrating pinned pixmap %p\n",
458706f2543Smrg		     (pointer)pPixmap));
459706f2543Smrg	return;
460706f2543Smrg    }
461706f2543Smrg
462706f2543Smrg    DBG_MIGRATE(("UseScreen %p score %d\n",
463706f2543Smrg		 (pointer)pPixmap, pExaPixmap->score));
464706f2543Smrg
465706f2543Smrg    if (pExaPixmap->score == EXA_PIXMAP_SCORE_INIT) {
466706f2543Smrg	exaDoMoveInPixmap(migrate);
467706f2543Smrg	pExaPixmap->score = 0;
468706f2543Smrg    }
469706f2543Smrg
470706f2543Smrg    if (pExaPixmap->score < EXA_PIXMAP_SCORE_MAX)
471706f2543Smrg	pExaPixmap->score++;
472706f2543Smrg
473706f2543Smrg    if (pExaPixmap->score >= EXA_PIXMAP_SCORE_MOVE_IN &&
474706f2543Smrg	!exaPixmapHasGpuCopy(pPixmap))
475706f2543Smrg    {
476706f2543Smrg	exaDoMoveInPixmap(migrate);
477706f2543Smrg    }
478706f2543Smrg
479706f2543Smrg    if (exaPixmapHasGpuCopy(pPixmap)) {
480706f2543Smrg	exaCopyDirtyToFb (migrate);
481706f2543Smrg	ExaOffscreenMarkUsed (pPixmap);
482706f2543Smrg    } else
483706f2543Smrg	exaCopyDirtyToSys (migrate);
484706f2543Smrg}
485706f2543Smrg
486706f2543Smrg/**
487706f2543Smrg * For the "greedy" migration scheme, pushes the pixmap toward being located in
488706f2543Smrg * system memory.
489706f2543Smrg */
490706f2543Smrgstatic void
491706f2543SmrgexaMigrateTowardSys (ExaMigrationPtr migrate)
492706f2543Smrg{
493706f2543Smrg    PixmapPtr pPixmap = migrate->pPix;
494706f2543Smrg    ExaPixmapPriv (pPixmap);
495706f2543Smrg
496706f2543Smrg    DBG_MIGRATE(("UseMem: %p score %d\n", (pointer)pPixmap, pExaPixmap->score));
497706f2543Smrg
498706f2543Smrg    if (pExaPixmap->score == EXA_PIXMAP_SCORE_PINNED)
499706f2543Smrg	return;
500706f2543Smrg
501706f2543Smrg    if (pExaPixmap->score == EXA_PIXMAP_SCORE_INIT)
502706f2543Smrg	pExaPixmap->score = 0;
503706f2543Smrg
504706f2543Smrg    if (pExaPixmap->score > EXA_PIXMAP_SCORE_MIN)
505706f2543Smrg	pExaPixmap->score--;
506706f2543Smrg
507706f2543Smrg    if (pExaPixmap->score <= EXA_PIXMAP_SCORE_MOVE_OUT && pExaPixmap->area)
508706f2543Smrg	exaDoMoveOutPixmap(migrate);
509706f2543Smrg
510706f2543Smrg    if (exaPixmapHasGpuCopy(pPixmap)) {
511706f2543Smrg	exaCopyDirtyToFb (migrate);
512706f2543Smrg	ExaOffscreenMarkUsed (pPixmap);
513706f2543Smrg    } else
514706f2543Smrg	exaCopyDirtyToSys (migrate);
515706f2543Smrg}
516706f2543Smrg
517706f2543Smrg/**
518706f2543Smrg * If the pixmap has both a framebuffer and system memory copy, this function
519706f2543Smrg * asserts that both of them are the same.
520706f2543Smrg */
521706f2543Smrgstatic Bool
522706f2543SmrgexaAssertNotDirty (PixmapPtr pPixmap)
523706f2543Smrg{
524706f2543Smrg    ExaPixmapPriv (pPixmap);
525706f2543Smrg    CARD8 *dst, *src;
526706f2543Smrg    RegionRec ValidReg;
527706f2543Smrg    int dst_pitch, src_pitch, cpp, y, nbox, save_pitch;
528706f2543Smrg    BoxPtr pBox;
529706f2543Smrg    Bool ret = TRUE, save_use_gpu_copy;
530706f2543Smrg
531706f2543Smrg    if (exaPixmapIsPinned(pPixmap) || pExaPixmap->area == NULL)
532706f2543Smrg	return ret;
533706f2543Smrg
534706f2543Smrg    RegionNull(&ValidReg);
535706f2543Smrg    RegionIntersect(&ValidReg, &pExaPixmap->validFB,
536706f2543Smrg		     &pExaPixmap->validSys);
537706f2543Smrg    nbox = RegionNumRects(&ValidReg);
538706f2543Smrg
539706f2543Smrg    if (!nbox)
540706f2543Smrg	goto out;
541706f2543Smrg
542706f2543Smrg    pBox = RegionRects(&ValidReg);
543706f2543Smrg
544706f2543Smrg    dst_pitch = pExaPixmap->sys_pitch;
545706f2543Smrg    src_pitch = pExaPixmap->fb_pitch;
546706f2543Smrg    cpp = pPixmap->drawable.bitsPerPixel / 8;
547706f2543Smrg
548706f2543Smrg    save_use_gpu_copy = pExaPixmap->use_gpu_copy;
549706f2543Smrg    save_pitch = pPixmap->devKind;
550706f2543Smrg    pExaPixmap->use_gpu_copy = TRUE;
551706f2543Smrg    pPixmap->devKind = pExaPixmap->fb_pitch;
552706f2543Smrg
553706f2543Smrg    if (!ExaDoPrepareAccess(pPixmap, EXA_PREPARE_SRC))
554706f2543Smrg	goto skip;
555706f2543Smrg
556706f2543Smrg    while (nbox--) {
557706f2543Smrg	    int rowbytes;
558706f2543Smrg
559706f2543Smrg	    pBox->x1 = max(pBox->x1, 0);
560706f2543Smrg	    pBox->y1 = max(pBox->y1, 0);
561706f2543Smrg	    pBox->x2 = min(pBox->x2, pPixmap->drawable.width);
562706f2543Smrg	    pBox->y2 = min(pBox->y2, pPixmap->drawable.height);
563706f2543Smrg
564706f2543Smrg	    if (pBox->x1 >= pBox->x2 || pBox->y1 >= pBox->y2)
565706f2543Smrg		continue;
566706f2543Smrg
567706f2543Smrg	    rowbytes = (pBox->x2 - pBox->x1) * cpp;
568706f2543Smrg	    src = (CARD8 *) pPixmap->devPrivate.ptr + pBox->y1 * src_pitch + pBox->x1 * cpp;
569706f2543Smrg	    dst = pExaPixmap->sys_ptr + pBox->y1 * dst_pitch + pBox->x1 * cpp;
570706f2543Smrg
571706f2543Smrg	    for (y = pBox->y1; y < pBox->y2;
572706f2543Smrg		 y++, src += src_pitch, dst += dst_pitch) {
573706f2543Smrg		if (memcmp(dst, src, rowbytes) != 0) {
574706f2543Smrg		    ret = FALSE;
575706f2543Smrg		    exaPixmapDirty(pPixmap, pBox->x1, pBox->y1, pBox->x2,
576706f2543Smrg				   pBox->y2);
577706f2543Smrg		    break;
578706f2543Smrg		}
579706f2543Smrg	    }
580706f2543Smrg    }
581706f2543Smrg
582706f2543Smrgskip:
583706f2543Smrg    exaFinishAccess(&pPixmap->drawable, EXA_PREPARE_SRC);
584706f2543Smrg
585706f2543Smrg    pExaPixmap->use_gpu_copy = save_use_gpu_copy;
586706f2543Smrg    pPixmap->devKind = save_pitch;
587706f2543Smrg
588706f2543Smrgout:
589706f2543Smrg    RegionUninit(&ValidReg);
590706f2543Smrg    return ret;
591706f2543Smrg}
592706f2543Smrg
593706f2543Smrg/**
594706f2543Smrg * Performs migration of the pixmaps according to the operation information
595706f2543Smrg * provided in pixmaps and can_accel and the migration scheme chosen in the
596706f2543Smrg * config file.
597706f2543Smrg */
598706f2543Smrgvoid
599706f2543SmrgexaDoMigration_classic (ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel)
600706f2543Smrg{
601706f2543Smrg    ScreenPtr pScreen = pixmaps[0].pPix->drawable.pScreen;
602706f2543Smrg    ExaScreenPriv(pScreen);
603706f2543Smrg    int i, j;
604706f2543Smrg
605706f2543Smrg    /* If this debugging flag is set, check each pixmap for whether it is marked
606706f2543Smrg     * as clean, and if so, actually check if that's the case.  This should help
607706f2543Smrg     * catch issues with failing to mark a drawable as dirty.  While it will
608706f2543Smrg     * catch them late (after the operation happened), it at least explains what
609706f2543Smrg     * went wrong, and instrumenting the code to find what operation happened
610706f2543Smrg     * to the pixmap last shouldn't be hard.
611706f2543Smrg     */
612706f2543Smrg    if (pExaScr->checkDirtyCorrectness) {
613706f2543Smrg	for (i = 0; i < npixmaps; i++) {
614706f2543Smrg	    if (!exaPixmapIsDirty (pixmaps[i].pPix) &&
615706f2543Smrg		!exaAssertNotDirty (pixmaps[i].pPix))
616706f2543Smrg		ErrorF("%s: Pixmap %d dirty but not marked as such!\n", __func__, i);
617706f2543Smrg	}
618706f2543Smrg    }
619706f2543Smrg    /* If anything is pinned in system memory, we won't be able to
620706f2543Smrg     * accelerate.
621706f2543Smrg     */
622706f2543Smrg    for (i = 0; i < npixmaps; i++) {
623706f2543Smrg	if (exaPixmapIsPinned (pixmaps[i].pPix) &&
624706f2543Smrg	    !exaPixmapHasGpuCopy (pixmaps[i].pPix))
625706f2543Smrg	{
626706f2543Smrg	    EXA_FALLBACK(("Pixmap %p (%dx%d) pinned in sys\n", pixmaps[i].pPix,
627706f2543Smrg		      pixmaps[i].pPix->drawable.width,
628706f2543Smrg		      pixmaps[i].pPix->drawable.height));
629706f2543Smrg	    can_accel = FALSE;
630706f2543Smrg	    break;
631706f2543Smrg	}
632706f2543Smrg    }
633706f2543Smrg
634706f2543Smrg    if (pExaScr->migration == ExaMigrationSmart) {
635706f2543Smrg	/* If we've got something as a destination that we shouldn't cause to
636706f2543Smrg	 * become newly dirtied, take the unaccelerated route.
637706f2543Smrg	 */
638706f2543Smrg	for (i = 0; i < npixmaps; i++) {
639706f2543Smrg	    if (pixmaps[i].as_dst && !exaPixmapShouldBeInFB (pixmaps[i].pPix) &&
640706f2543Smrg		!exaPixmapIsDirty (pixmaps[i].pPix))
641706f2543Smrg	    {
642706f2543Smrg		for (i = 0; i < npixmaps; i++) {
643706f2543Smrg		    if (!exaPixmapIsDirty (pixmaps[i].pPix))
644706f2543Smrg			exaDoMoveOutPixmap (pixmaps + i);
645706f2543Smrg		}
646706f2543Smrg		return;
647706f2543Smrg	    }
648706f2543Smrg	}
649706f2543Smrg
650706f2543Smrg	/* If we aren't going to accelerate, then we migrate everybody toward
651706f2543Smrg	 * system memory, and kick out if it's free.
652706f2543Smrg	 */
653706f2543Smrg	if (!can_accel) {
654706f2543Smrg	    for (i = 0; i < npixmaps; i++) {
655706f2543Smrg		exaMigrateTowardSys (pixmaps + i);
656706f2543Smrg		if (!exaPixmapIsDirty (pixmaps[i].pPix))
657706f2543Smrg		    exaDoMoveOutPixmap (pixmaps + i);
658706f2543Smrg	    }
659706f2543Smrg	    return;
660706f2543Smrg	}
661706f2543Smrg
662706f2543Smrg	/* Finally, the acceleration path.  Move them all in. */
663706f2543Smrg	for (i = 0; i < npixmaps; i++) {
664706f2543Smrg	    exaMigrateTowardFb(pixmaps + i);
665706f2543Smrg	    exaDoMoveInPixmap(pixmaps + i);
666706f2543Smrg	}
667706f2543Smrg    } else if (pExaScr->migration == ExaMigrationGreedy) {
668706f2543Smrg	/* If we can't accelerate, either because the driver can't or because one of
669706f2543Smrg	 * the pixmaps is pinned in system memory, then we migrate everybody toward
670706f2543Smrg	 * system memory.
671706f2543Smrg	 *
672706f2543Smrg	 * We also migrate toward system if all pixmaps involved are currently in
673706f2543Smrg	 * system memory -- this can mitigate thrashing when there are significantly
674706f2543Smrg	 * more pixmaps active than would fit in memory.
675706f2543Smrg	 *
676706f2543Smrg	 * If not, then we migrate toward FB so that hopefully acceleration can
677706f2543Smrg	 * happen.
678706f2543Smrg	 */
679706f2543Smrg	if (!can_accel) {
680706f2543Smrg	    for (i = 0; i < npixmaps; i++)
681706f2543Smrg		exaMigrateTowardSys (pixmaps + i);
682706f2543Smrg	    return;
683706f2543Smrg	}
684706f2543Smrg
685706f2543Smrg	for (i = 0; i < npixmaps; i++) {
686706f2543Smrg	    if (exaPixmapHasGpuCopy(pixmaps[i].pPix)) {
687706f2543Smrg		/* Found one in FB, so move all to FB. */
688706f2543Smrg		for (j = 0; j < npixmaps; j++)
689706f2543Smrg		    exaMigrateTowardFb(pixmaps + i);
690706f2543Smrg		return;
691706f2543Smrg	    }
692706f2543Smrg	}
693706f2543Smrg
694706f2543Smrg	/* Nobody's in FB, so move all away from FB. */
695706f2543Smrg	for (i = 0; i < npixmaps; i++)
696706f2543Smrg	    exaMigrateTowardSys(pixmaps + i);
697706f2543Smrg    } else if (pExaScr->migration == ExaMigrationAlways) {
698706f2543Smrg	/* Always move the pixmaps out if we can't accelerate.  If we can
699706f2543Smrg	 * accelerate, try to move them all in.  If that fails, then move them
700706f2543Smrg	 * back out.
701706f2543Smrg	 */
702706f2543Smrg	if (!can_accel) {
703706f2543Smrg	    for (i = 0; i < npixmaps; i++)
704706f2543Smrg		exaDoMoveOutPixmap(pixmaps + i);
705706f2543Smrg	    return;
706706f2543Smrg	}
707706f2543Smrg
708706f2543Smrg	/* Now, try to move them all into FB */
709706f2543Smrg	for (i = 0; i < npixmaps; i++) {
710706f2543Smrg	    exaDoMoveInPixmap(pixmaps + i);
711706f2543Smrg	}
712706f2543Smrg
713706f2543Smrg	/* If we couldn't fit everything in, abort */
714706f2543Smrg	for (i = 0; i < npixmaps; i++) {
715706f2543Smrg	    if (!exaPixmapHasGpuCopy(pixmaps[i].pPix)) {
716706f2543Smrg		return;
717706f2543Smrg	    }
718706f2543Smrg	}
719706f2543Smrg
720706f2543Smrg	/* Yay, everything has a gpu copy, mark memory as used */
721706f2543Smrg	for (i = 0; i < npixmaps; i++) {
722706f2543Smrg	    ExaOffscreenMarkUsed (pixmaps[i].pPix);
723706f2543Smrg	}
724706f2543Smrg    }
725706f2543Smrg}
726706f2543Smrg
727706f2543Smrgvoid
728706f2543SmrgexaPrepareAccessReg_classic(PixmapPtr pPixmap, int index, RegionPtr pReg)
729706f2543Smrg{
730706f2543Smrg    ExaMigrationRec pixmaps[1];
731706f2543Smrg
732706f2543Smrg    if (index == EXA_PREPARE_DEST || index == EXA_PREPARE_AUX_DEST) {
733706f2543Smrg	pixmaps[0].as_dst = TRUE;
734706f2543Smrg	pixmaps[0].as_src = FALSE;
735706f2543Smrg    } else {
736706f2543Smrg	pixmaps[0].as_dst = FALSE;
737706f2543Smrg	pixmaps[0].as_src = TRUE;
738706f2543Smrg    }
739706f2543Smrg    pixmaps[0].pPix = pPixmap;
740706f2543Smrg    pixmaps[0].pReg = pReg;
741706f2543Smrg
742706f2543Smrg    exaDoMigration(pixmaps, 1, FALSE);
743706f2543Smrg
744706f2543Smrg    (void)ExaDoPrepareAccess(pPixmap, index);
745706f2543Smrg}
746