| 1 | /* |
| 2 | * Copyright © 2003 Anders Carlsson |
| 3 | * |
| 4 | * Permission to use, copy, modify, distribute, and sell this software and its |
| 5 | * documentation for any purpose is hereby granted without fee, provided that |
| 6 | * the above copyright notice appear in all copies and that both that |
| 7 | * copyright notice and this permission notice appear in supporting |
| 8 | * documentation, and that the name of Anders Carlsson not be used in |
| 9 | * advertising or publicity pertaining to distribution of the software without |
| 10 | * specific, written prior permission. Anders Carlsson makes no |
| 11 | * representations about the suitability of this software for any purpose. It |
| 12 | * is provided "as is" without express or implied warranty. |
| 13 | * |
| 14 | * ANDERS CARLSSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
| 15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
| 16 | * EVENT SHALL ANDERS CARLSSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
| 17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
| 18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| 19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| 20 | * PERFORMANCE OF THIS SOFTWARE. |
| 21 | */ |
| 22 | |
| 23 | /** @file |
| 24 | * This allocator allocates blocks of memory by maintaining a list of areas. |
| 25 | * When allocating, the contiguous block of areas with the minimum eviction |
| 26 | * cost is found and evicted in order to make room for the new allocation. |
| 27 | */ |
| 28 | |
| 29 | #include "exa_priv.h" |
| 30 | |
| 31 | #include <limits.h> |
| 32 | #include <assert.h> |
| 33 | #include <stdlib.h> |
| 34 | |
| 35 | #if DEBUG_OFFSCREEN |
| 36 | #define DBG_OFFSCREEN(a) ErrorF a |
| 37 | #else |
| 38 | #define DBG_OFFSCREEN(a) |
| 39 | #endif |
| 40 | |
| 41 | #if DEBUG_OFFSCREEN |
| 42 | static void |
| 43 | ExaOffscreenValidate(ScreenPtr pScreen) |
| 44 | { |
| 45 | ExaScreenPriv(pScreen); |
| 46 | ExaOffscreenArea *prev = 0, *area; |
| 47 | |
| 48 | assert(pExaScr->info->offScreenAreas->base_offset == |
| 49 | pExaScr->info->offScreenBase); |
| 50 | for (area = pExaScr->info->offScreenAreas; area; area = area->next) { |
| 51 | assert(area->offset >= area->base_offset && |
| 52 | area->offset < (area->base_offset + area->size)); |
| 53 | if (prev) |
| 54 | assert(prev->base_offset + prev->size == area->base_offset); |
| 55 | prev = area; |
| 56 | } |
| 57 | assert(prev->base_offset + prev->size == pExaScr->info->memorySize); |
| 58 | } |
| 59 | #else |
| 60 | #define ExaOffscreenValidate(s) |
| 61 | #endif |
| 62 | |
| 63 | static ExaOffscreenArea * |
| 64 | ExaOffscreenKickOut(ScreenPtr pScreen, ExaOffscreenArea * area) |
| 65 | { |
| 66 | if (area->save) |
| 67 | (*area->save) (pScreen, area); |
| 68 | return exaOffscreenFree(pScreen, area); |
| 69 | } |
| 70 | |
| 71 | static void |
| 72 | exaUpdateEvictionCost(ExaOffscreenArea * area, unsigned offScreenCounter) |
| 73 | { |
| 74 | unsigned age; |
| 75 | |
| 76 | if (area->state == ExaOffscreenAvail) |
| 77 | return; |
| 78 | |
| 79 | age = offScreenCounter - area->last_use; |
| 80 | |
| 81 | /* This is unlikely to happen, but could result in a division by zero... */ |
| 82 | if (age > (UINT_MAX / 2)) { |
| 83 | age = UINT_MAX / 2; |
| 84 | area->last_use = offScreenCounter - age; |
| 85 | } |
| 86 | |
| 87 | area->eviction_cost = area->size / age; |
| 88 | } |
| 89 | |
| 90 | static ExaOffscreenArea * |
| 91 | exaFindAreaToEvict(ExaScreenPrivPtr pExaScr, int size, int align) |
| 92 | { |
| 93 | ExaOffscreenArea *begin, *end, *best; |
| 94 | unsigned cost, best_cost; |
| 95 | int avail, real_size; |
| 96 | |
| 97 | best_cost = UINT_MAX; |
| 98 | begin = end = pExaScr->info->offScreenAreas; |
| 99 | avail = 0; |
| 100 | cost = 0; |
| 101 | best = 0; |
| 102 | |
| 103 | while (end != NULL) { |
| 104 | restart: |
| 105 | while (begin != NULL && begin->state == ExaOffscreenLocked) |
| 106 | begin = end = begin->next; |
| 107 | |
| 108 | if (begin == NULL) |
| 109 | break; |
| 110 | |
| 111 | /* adjust size needed to account for alignment loss for this area */ |
| 112 | real_size = size + (begin->base_offset + begin->size - size) % align; |
| 113 | |
| 114 | while (avail < real_size && end != NULL) { |
| 115 | if (end->state == ExaOffscreenLocked) { |
| 116 | /* Can't more room here, restart after this locked area */ |
| 117 | avail = 0; |
| 118 | cost = 0; |
| 119 | begin = end; |
| 120 | goto restart; |
| 121 | } |
| 122 | avail += end->size; |
| 123 | exaUpdateEvictionCost(end, pExaScr->offScreenCounter); |
| 124 | cost += end->eviction_cost; |
| 125 | end = end->next; |
| 126 | } |
| 127 | |
| 128 | /* Check the cost, update best */ |
| 129 | if (avail >= real_size && cost < best_cost) { |
| 130 | best = begin; |
| 131 | best_cost = cost; |
| 132 | } |
| 133 | |
| 134 | avail -= begin->size; |
| 135 | cost -= begin->eviction_cost; |
| 136 | begin = begin->next; |
| 137 | } |
| 138 | |
| 139 | return best; |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * exaOffscreenAlloc allocates offscreen memory |
| 144 | * |
| 145 | * @param pScreen current screen |
| 146 | * @param size size in bytes of the allocation |
| 147 | * @param align byte alignment requirement for the offset of the allocated area |
| 148 | * @param locked whether the allocated area is locked and can't be kicked out |
| 149 | * @param save callback for when the area is evicted from memory |
| 150 | * @param privdata private data for the save callback. |
| 151 | * |
| 152 | * Allocates offscreen memory from the device associated with pScreen. size |
| 153 | * and align deteremine where and how large the allocated area is, and locked |
| 154 | * will mark whether it should be held in card memory. privdata may be any |
| 155 | * pointer for the save callback when the area is removed. |
| 156 | * |
| 157 | * Note that locked areas do get evicted on VT switch unless the driver |
| 158 | * requested version 2.1 or newer behavior. In that case, the save callback is |
| 159 | * still called. |
| 160 | */ |
| 161 | ExaOffscreenArea * |
| 162 | exaOffscreenAlloc(ScreenPtr pScreen, int size, int align, |
| 163 | Bool locked, ExaOffscreenSaveProc save, pointer privData) |
| 164 | { |
| 165 | ExaOffscreenArea *area; |
| 166 | |
| 167 | ExaScreenPriv(pScreen); |
| 168 | int real_size = 0, largest_avail = 0; |
| 169 | |
| 170 | #if DEBUG_OFFSCREEN |
| 171 | static int number = 0; |
| 172 | |
| 173 | ErrorF("================= ============ allocating a new pixmap %d\n", |
| 174 | ++number); |
| 175 | #endif |
| 176 | |
| 177 | ExaOffscreenValidate(pScreen); |
| 178 | if (!align) |
| 179 | align = 1; |
| 180 | |
| 181 | if (!size) { |
| 182 | DBG_OFFSCREEN(("Alloc 0x%x -> EMPTY\n", size)); |
| 183 | return NULL; |
| 184 | } |
| 185 | |
| 186 | /* throw out requests that cannot fit */ |
| 187 | if (size > (pExaScr->info->memorySize - pExaScr->info->offScreenBase)) { |
| 188 | DBG_OFFSCREEN(("Alloc 0x%x vs (0x%lx) -> TOBIG\n", size, |
| 189 | pExaScr->info->memorySize - |
| 190 | pExaScr->info->offScreenBase)); |
| 191 | return NULL; |
| 192 | } |
| 193 | |
| 194 | /* Try to find a free space that'll fit. */ |
| 195 | for (area = pExaScr->info->offScreenAreas; area; area = area->next) { |
| 196 | /* skip allocated areas */ |
| 197 | if (area->state != ExaOffscreenAvail) |
| 198 | continue; |
| 199 | |
| 200 | /* adjust size to match alignment requirement */ |
| 201 | real_size = size + (area->base_offset + area->size - size) % align; |
| 202 | |
| 203 | /* does it fit? */ |
| 204 | if (real_size <= area->size) |
| 205 | break; |
| 206 | |
| 207 | if (area->size > largest_avail) |
| 208 | largest_avail = area->size; |
| 209 | } |
| 210 | |
| 211 | if (!area) { |
| 212 | area = exaFindAreaToEvict(pExaScr, size, align); |
| 213 | |
| 214 | if (!area) { |
| 215 | DBG_OFFSCREEN(("Alloc 0x%x -> NOSPACE\n", size)); |
| 216 | /* Could not allocate memory */ |
| 217 | ExaOffscreenValidate(pScreen); |
| 218 | return NULL; |
| 219 | } |
| 220 | |
| 221 | /* adjust size needed to account for alignment loss for this area */ |
| 222 | real_size = size + (area->base_offset + area->size - size) % align; |
| 223 | |
| 224 | /* |
| 225 | * Kick out first area if in use |
| 226 | */ |
| 227 | if (area->state != ExaOffscreenAvail) |
| 228 | area = ExaOffscreenKickOut(pScreen, area); |
| 229 | /* |
| 230 | * Now get the system to merge the other needed areas together |
| 231 | */ |
| 232 | while (area->size < real_size) { |
| 233 | assert(area->next && area->next->state == ExaOffscreenRemovable); |
| 234 | (void) ExaOffscreenKickOut(pScreen, area->next); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | /* save extra space in new area */ |
| 239 | if (real_size < area->size) { |
| 240 | ExaOffscreenArea *new_area = malloc(sizeof(ExaOffscreenArea)); |
| 241 | |
| 242 | if (!new_area) |
| 243 | return NULL; |
| 244 | new_area->base_offset = area->base_offset; |
| 245 | |
| 246 | new_area->offset = new_area->base_offset; |
| 247 | new_area->align = 0; |
| 248 | new_area->size = area->size - real_size; |
| 249 | new_area->state = ExaOffscreenAvail; |
| 250 | new_area->save = NULL; |
| 251 | new_area->last_use = 0; |
| 252 | new_area->eviction_cost = 0; |
| 253 | new_area->next = area; |
| 254 | new_area->prev = area->prev; |
| 255 | if (area->prev->next) |
| 256 | area->prev->next = new_area; |
| 257 | else |
| 258 | pExaScr->info->offScreenAreas = new_area; |
| 259 | area->prev = new_area; |
| 260 | area->base_offset = new_area->base_offset + new_area->size; |
| 261 | area->size = real_size; |
| 262 | } |
| 263 | else |
| 264 | pExaScr->numOffscreenAvailable--; |
| 265 | |
| 266 | /* |
| 267 | * Mark this area as in use |
| 268 | */ |
| 269 | if (locked) |
| 270 | area->state = ExaOffscreenLocked; |
| 271 | else |
| 272 | area->state = ExaOffscreenRemovable; |
| 273 | area->privData = privData; |
| 274 | area->save = save; |
| 275 | area->last_use = pExaScr->offScreenCounter++; |
| 276 | area->offset = (area->base_offset + align - 1); |
| 277 | area->offset -= area->offset % align; |
| 278 | area->align = align; |
| 279 | |
| 280 | ExaOffscreenValidate(pScreen); |
| 281 | |
| 282 | DBG_OFFSCREEN(("Alloc 0x%x -> 0x%x (0x%x)\n", size, |
| 283 | area->base_offset, area->offset)); |
| 284 | return area; |
| 285 | } |
| 286 | |
| 287 | /** |
| 288 | * Ejects all offscreen areas, and uninitializes the offscreen memory manager. |
| 289 | */ |
| 290 | void |
| 291 | ExaOffscreenSwapOut(ScreenPtr pScreen) |
| 292 | { |
| 293 | ExaScreenPriv(pScreen); |
| 294 | |
| 295 | ExaOffscreenValidate(pScreen); |
| 296 | /* loop until a single free area spans the space */ |
| 297 | for (;;) { |
| 298 | ExaOffscreenArea *area = pExaScr->info->offScreenAreas; |
| 299 | |
| 300 | if (!area) |
| 301 | break; |
| 302 | if (area->state == ExaOffscreenAvail) { |
| 303 | area = area->next; |
| 304 | if (!area) |
| 305 | break; |
| 306 | } |
| 307 | assert(area->state != ExaOffscreenAvail); |
| 308 | (void) ExaOffscreenKickOut(pScreen, area); |
| 309 | ExaOffscreenValidate(pScreen); |
| 310 | } |
| 311 | ExaOffscreenValidate(pScreen); |
| 312 | ExaOffscreenFini(pScreen); |
| 313 | } |
| 314 | |
| 315 | /** Ejects all pixmaps managed by EXA. */ |
| 316 | static void |
| 317 | ExaOffscreenEjectPixmaps(ScreenPtr pScreen) |
| 318 | { |
| 319 | ExaScreenPriv(pScreen); |
| 320 | |
| 321 | ExaOffscreenValidate(pScreen); |
| 322 | /* loop until a single free area spans the space */ |
| 323 | for (;;) { |
| 324 | ExaOffscreenArea *area; |
| 325 | |
| 326 | for (area = pExaScr->info->offScreenAreas; area != NULL; |
| 327 | area = area->next) { |
| 328 | if (area->state == ExaOffscreenRemovable && |
| 329 | area->save == exaPixmapSave) { |
| 330 | (void) ExaOffscreenKickOut(pScreen, area); |
| 331 | ExaOffscreenValidate(pScreen); |
| 332 | break; |
| 333 | } |
| 334 | } |
| 335 | if (area == NULL) |
| 336 | break; |
| 337 | } |
| 338 | ExaOffscreenValidate(pScreen); |
| 339 | } |
| 340 | |
| 341 | void |
| 342 | ExaOffscreenSwapIn(ScreenPtr pScreen) |
| 343 | { |
| 344 | exaOffscreenInit(pScreen); |
| 345 | } |
| 346 | |
| 347 | /** |
| 348 | * Prepares EXA for disabling of FB access, or restoring it. |
| 349 | * |
| 350 | * In version 2.1, the disabling results in pixmaps being ejected, while other |
| 351 | * allocations remain. With this plus the prevention of migration while |
| 352 | * swappedOut is set, EXA by itself should not cause any access of the |
| 353 | * framebuffer to occur while swapped out. Any remaining issues are the |
| 354 | * responsibility of the driver. |
| 355 | * |
| 356 | * Prior to version 2.1, all allocations, including locked ones, are ejected |
| 357 | * when access is disabled, and the allocator is torn down while swappedOut |
| 358 | * is set. This is more drastic, and caused implementation difficulties for |
| 359 | * many drivers that could otherwise handle the lack of FB access while |
| 360 | * swapped out. |
| 361 | */ |
| 362 | void |
| 363 | exaEnableDisableFBAccess(ScreenPtr pScreen, Bool enable) |
| 364 | { |
| 365 | ExaScreenPriv(pScreen); |
| 366 | |
| 367 | if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) |
| 368 | return; |
| 369 | |
| 370 | if (!enable && pExaScr->disableFbCount++ == 0) { |
| 371 | if (pExaScr->info->exa_minor < 1) |
| 372 | ExaOffscreenSwapOut(pScreen); |
| 373 | else |
| 374 | ExaOffscreenEjectPixmaps(pScreen); |
| 375 | pExaScr->swappedOut = TRUE; |
| 376 | } |
| 377 | |
| 378 | if (enable && --pExaScr->disableFbCount == 0) { |
| 379 | if (pExaScr->info->exa_minor < 1) |
| 380 | ExaOffscreenSwapIn(pScreen); |
| 381 | pExaScr->swappedOut = FALSE; |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | /* merge the next free area into this one */ |
| 386 | static void |
| 387 | ExaOffscreenMerge(ExaScreenPrivPtr pExaScr, ExaOffscreenArea * area) |
| 388 | { |
| 389 | ExaOffscreenArea *next = area->next; |
| 390 | |
| 391 | /* account for space */ |
| 392 | area->size += next->size; |
| 393 | /* frob pointer */ |
| 394 | area->next = next->next; |
| 395 | if (area->next) |
| 396 | area->next->prev = area; |
| 397 | else |
| 398 | pExaScr->info->offScreenAreas->prev = area; |
| 399 | free(next); |
| 400 | |
| 401 | pExaScr->numOffscreenAvailable--; |
| 402 | } |
| 403 | |
| 404 | /** |
| 405 | * exaOffscreenFree frees an allocation. |
| 406 | * |
| 407 | * @param pScreen current screen |
| 408 | * @param area offscreen area to free |
| 409 | * |
| 410 | * exaOffscreenFree frees an allocation created by exaOffscreenAlloc. Note that |
| 411 | * the save callback of the area is not called, and it is up to the driver to |
| 412 | * do any cleanup necessary as a result. |
| 413 | * |
| 414 | * @return pointer to the newly freed area. This behavior should not be relied |
| 415 | * on. |
| 416 | */ |
| 417 | ExaOffscreenArea * |
| 418 | exaOffscreenFree(ScreenPtr pScreen, ExaOffscreenArea * area) |
| 419 | { |
| 420 | ExaScreenPriv(pScreen); |
| 421 | ExaOffscreenArea *next = area->next; |
| 422 | ExaOffscreenArea *prev; |
| 423 | |
| 424 | DBG_OFFSCREEN(("Free 0x%x -> 0x%x (0x%x)\n", area->size, |
| 425 | area->base_offset, area->offset)); |
| 426 | ExaOffscreenValidate(pScreen); |
| 427 | |
| 428 | area->state = ExaOffscreenAvail; |
| 429 | area->save = NULL; |
| 430 | area->last_use = 0; |
| 431 | area->eviction_cost = 0; |
| 432 | /* |
| 433 | * Find previous area |
| 434 | */ |
| 435 | if (area == pExaScr->info->offScreenAreas) |
| 436 | prev = NULL; |
| 437 | else |
| 438 | prev = area->prev; |
| 439 | |
| 440 | pExaScr->numOffscreenAvailable++; |
| 441 | |
| 442 | /* link with next area if free */ |
| 443 | if (next && next->state == ExaOffscreenAvail) |
| 444 | ExaOffscreenMerge(pExaScr, area); |
| 445 | |
| 446 | /* link with prev area if free */ |
| 447 | if (prev && prev->state == ExaOffscreenAvail) { |
| 448 | area = prev; |
| 449 | ExaOffscreenMerge(pExaScr, area); |
| 450 | } |
| 451 | |
| 452 | ExaOffscreenValidate(pScreen); |
| 453 | DBG_OFFSCREEN(("\tdone freeing\n")); |
| 454 | return area; |
| 455 | } |
| 456 | |
| 457 | void |
| 458 | ExaOffscreenMarkUsed(PixmapPtr pPixmap) |
| 459 | { |
| 460 | ExaPixmapPriv(pPixmap); |
| 461 | ExaScreenPriv(pPixmap->drawable.pScreen); |
| 462 | |
| 463 | if (!pExaPixmap || !pExaPixmap->area) |
| 464 | return; |
| 465 | |
| 466 | pExaPixmap->area->last_use = pExaScr->offScreenCounter++; |
| 467 | } |
| 468 | |
| 469 | /** |
| 470 | * Defragment offscreen memory by compacting allocated areas at the end of it, |
| 471 | * leaving the total amount of memory available as a single area at the |
| 472 | * beginning (when there are no pinned allocations). |
| 473 | */ |
| 474 | _X_HIDDEN ExaOffscreenArea * |
| 475 | ExaOffscreenDefragment(ScreenPtr pScreen) |
| 476 | { |
| 477 | ExaScreenPriv(pScreen); |
| 478 | ExaOffscreenArea *area, *largest_available = NULL; |
| 479 | int largest_size = 0; |
| 480 | PixmapPtr pDstPix; |
| 481 | ExaPixmapPrivPtr pExaDstPix; |
| 482 | |
| 483 | pDstPix = (*pScreen->CreatePixmap) (pScreen, 0, 0, 0, 0); |
| 484 | |
| 485 | if (!pDstPix) |
| 486 | return NULL; |
| 487 | |
| 488 | pExaDstPix = ExaGetPixmapPriv(pDstPix); |
| 489 | pExaDstPix->use_gpu_copy = TRUE; |
| 490 | |
| 491 | for (area = pExaScr->info->offScreenAreas->prev; |
| 492 | area != pExaScr->info->offScreenAreas;) { |
| 493 | ExaOffscreenArea *prev = area->prev; |
| 494 | PixmapPtr pSrcPix; |
| 495 | ExaPixmapPrivPtr pExaSrcPix; |
| 496 | Bool save_use_gpu_copy; |
| 497 | int save_pitch; |
| 498 | |
| 499 | if (area->state != ExaOffscreenAvail || |
| 500 | prev->state == ExaOffscreenLocked || |
| 501 | (prev->state == ExaOffscreenRemovable && |
| 502 | prev->save != exaPixmapSave)) { |
| 503 | area = prev; |
| 504 | continue; |
| 505 | } |
| 506 | |
| 507 | if (prev->state == ExaOffscreenAvail) { |
| 508 | if (area == largest_available) { |
| 509 | largest_available = prev; |
| 510 | largest_size += prev->size; |
| 511 | } |
| 512 | area = prev; |
| 513 | ExaOffscreenMerge(pExaScr, area); |
| 514 | continue; |
| 515 | } |
| 516 | |
| 517 | if (area->size > largest_size) { |
| 518 | largest_available = area; |
| 519 | largest_size = area->size; |
| 520 | } |
| 521 | |
| 522 | pSrcPix = prev->privData; |
| 523 | pExaSrcPix = ExaGetPixmapPriv(pSrcPix); |
| 524 | |
| 525 | pExaDstPix->fb_ptr = pExaScr->info->memoryBase + |
| 526 | area->base_offset + area->size - prev->size + prev->base_offset - |
| 527 | prev->offset; |
| 528 | pExaDstPix->fb_ptr -= (unsigned long) pExaDstPix->fb_ptr % prev->align; |
| 529 | |
| 530 | if (pExaDstPix->fb_ptr <= pExaSrcPix->fb_ptr) { |
| 531 | area = prev; |
| 532 | continue; |
| 533 | } |
| 534 | |
| 535 | if (!(pExaScr->info->flags & EXA_SUPPORTS_OFFSCREEN_OVERLAPS) && |
| 536 | (pExaSrcPix->fb_ptr + prev->size) > pExaDstPix->fb_ptr) { |
| 537 | area = prev; |
| 538 | continue; |
| 539 | } |
| 540 | |
| 541 | save_use_gpu_copy = pExaSrcPix->use_gpu_copy; |
| 542 | save_pitch = pSrcPix->devKind; |
| 543 | |
| 544 | pExaSrcPix->use_gpu_copy = TRUE; |
| 545 | pSrcPix->devKind = pExaSrcPix->fb_pitch; |
| 546 | |
| 547 | pDstPix->drawable.width = pSrcPix->drawable.width; |
| 548 | pDstPix->devKind = pSrcPix->devKind; |
| 549 | pDstPix->drawable.height = pSrcPix->drawable.height; |
| 550 | pDstPix->drawable.depth = pSrcPix->drawable.depth; |
| 551 | pDstPix->drawable.bitsPerPixel = pSrcPix->drawable.bitsPerPixel; |
| 552 | |
| 553 | if (!pExaScr->info->PrepareCopy(pSrcPix, pDstPix, -1, -1, GXcopy, ~0)) { |
| 554 | pExaSrcPix->use_gpu_copy = save_use_gpu_copy; |
| 555 | pSrcPix->devKind = save_pitch; |
| 556 | area = prev; |
| 557 | continue; |
| 558 | } |
| 559 | |
| 560 | pExaScr->info->Copy(pDstPix, 0, 0, 0, 0, pDstPix->drawable.width, |
| 561 | pDstPix->drawable.height); |
| 562 | pExaScr->info->DoneCopy(pDstPix); |
| 563 | exaMarkSync(pScreen); |
| 564 | |
| 565 | DBG_OFFSCREEN(("Before swap: prev=0x%08x-0x%08x-0x%08x area=0x%08x-0x%08x-0x%08x\n", prev->base_offset, prev->offset, prev->base_offset + prev->size, area->base_offset, area->offset, area->base_offset + area->size)); |
| 566 | |
| 567 | /* Calculate swapped area offsets and sizes */ |
| 568 | area->base_offset = prev->base_offset; |
| 569 | area->offset = area->base_offset; |
| 570 | prev->offset += pExaDstPix->fb_ptr - pExaSrcPix->fb_ptr; |
| 571 | assert(prev->offset >= pExaScr->info->offScreenBase && |
| 572 | prev->offset < pExaScr->info->memorySize); |
| 573 | prev->base_offset = prev->offset; |
| 574 | if (area->next) |
| 575 | prev->size = area->next->base_offset - prev->base_offset; |
| 576 | else |
| 577 | prev->size = pExaScr->info->memorySize - prev->base_offset; |
| 578 | area->size = prev->base_offset - area->base_offset; |
| 579 | |
| 580 | DBG_OFFSCREEN(("After swap: area=0x%08x-0x%08x-0x%08x prev=0x%08x-0x%08x-0x%08x\n", area->base_offset, area->offset, area->base_offset + area->size, prev->base_offset, prev->offset, prev->base_offset + prev->size)); |
| 581 | |
| 582 | /* Swap areas in list */ |
| 583 | if (area->next) |
| 584 | area->next->prev = prev; |
| 585 | else |
| 586 | pExaScr->info->offScreenAreas->prev = prev; |
| 587 | if (prev->prev->next) |
| 588 | prev->prev->next = area; |
| 589 | else |
| 590 | pExaScr->info->offScreenAreas = area; |
| 591 | prev->next = area->next; |
| 592 | area->next = prev; |
| 593 | area->prev = prev->prev; |
| 594 | prev->prev = area; |
| 595 | if (!area->prev->next) |
| 596 | pExaScr->info->offScreenAreas = area; |
| 597 | |
| 598 | #if DEBUG_OFFSCREEN |
| 599 | if (prev->prev == prev || prev->next == prev) |
| 600 | ErrorF("Whoops, prev points to itself!\n"); |
| 601 | |
| 602 | if (area->prev == area || area->next == area) |
| 603 | ErrorF("Whoops, area points to itself!\n"); |
| 604 | #endif |
| 605 | |
| 606 | pExaSrcPix->fb_ptr = pExaDstPix->fb_ptr; |
| 607 | pExaSrcPix->use_gpu_copy = save_use_gpu_copy; |
| 608 | pSrcPix->devKind = save_pitch; |
| 609 | } |
| 610 | |
| 611 | pDstPix->drawable.width = 0; |
| 612 | pDstPix->drawable.height = 0; |
| 613 | pDstPix->drawable.depth = 0; |
| 614 | pDstPix->drawable.bitsPerPixel = 0; |
| 615 | |
| 616 | (*pScreen->DestroyPixmap) (pDstPix); |
| 617 | |
| 618 | if (area->state == ExaOffscreenAvail && area->size > largest_size) |
| 619 | return area; |
| 620 | |
| 621 | return largest_available; |
| 622 | } |
| 623 | |
| 624 | /** |
| 625 | * exaOffscreenInit initializes the offscreen memory manager. |
| 626 | * |
| 627 | * @param pScreen current screen |
| 628 | * |
| 629 | * exaOffscreenInit is called by exaDriverInit to set up the memory manager for |
| 630 | * the screen, if any offscreen memory is available. |
| 631 | */ |
| 632 | Bool |
| 633 | exaOffscreenInit(ScreenPtr pScreen) |
| 634 | { |
| 635 | ExaScreenPriv(pScreen); |
| 636 | ExaOffscreenArea *area; |
| 637 | |
| 638 | /* Allocate a big free area */ |
| 639 | area = malloc(sizeof(ExaOffscreenArea)); |
| 640 | |
| 641 | if (!area) |
| 642 | return FALSE; |
| 643 | |
| 644 | area->state = ExaOffscreenAvail; |
| 645 | area->base_offset = pExaScr->info->offScreenBase; |
| 646 | area->offset = area->base_offset; |
| 647 | area->align = 0; |
| 648 | area->size = pExaScr->info->memorySize - area->base_offset; |
| 649 | area->save = NULL; |
| 650 | area->next = NULL; |
| 651 | area->prev = area; |
| 652 | area->last_use = 0; |
| 653 | area->eviction_cost = 0; |
| 654 | |
| 655 | /* Add it to the free areas */ |
| 656 | pExaScr->info->offScreenAreas = area; |
| 657 | pExaScr->offScreenCounter = 1; |
| 658 | pExaScr->numOffscreenAvailable = 1; |
| 659 | |
| 660 | ExaOffscreenValidate(pScreen); |
| 661 | |
| 662 | return TRUE; |
| 663 | } |
| 664 | |
| 665 | void |
| 666 | ExaOffscreenFini(ScreenPtr pScreen) |
| 667 | { |
| 668 | ExaScreenPriv(pScreen); |
| 669 | ExaOffscreenArea *area; |
| 670 | |
| 671 | /* just free all of the area records */ |
| 672 | while ((area = pExaScr->info->offScreenAreas)) { |
| 673 | pExaScr->info->offScreenAreas = area->next; |
| 674 | free(area); |
| 675 | } |
| 676 | } |