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