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 42static void 43ExaOffscreenValidate(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 assert(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 63static ExaOffscreenArea * 64ExaOffscreenKickOut(ScreenPtr pScreen, ExaOffscreenArea * area) 65{ 66 if (area->save) 67 (*area->save) (pScreen, area); 68 return exaOffscreenFree(pScreen, area); 69} 70 71static void 72exaUpdateEvictionCost(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 90static ExaOffscreenArea * 91exaFindAreaToEvict(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 determine 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 */ 161ExaOffscreenArea * 162exaOffscreenAlloc(ScreenPtr pScreen, int size, int align, 163 Bool locked, ExaOffscreenSaveProc save, void *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); 234 assert(area->next->state == ExaOffscreenRemovable); 235 (void) ExaOffscreenKickOut(pScreen, area->next); 236 } 237 } 238 239 /* save extra space in new area */ 240 if (real_size < area->size) { 241 ExaOffscreenArea *new_area = malloc(sizeof(ExaOffscreenArea)); 242 243 if (!new_area) 244 return NULL; 245 new_area->base_offset = area->base_offset; 246 247 new_area->offset = new_area->base_offset; 248 new_area->align = 0; 249 new_area->size = area->size - real_size; 250 new_area->state = ExaOffscreenAvail; 251 new_area->save = NULL; 252 new_area->last_use = 0; 253 new_area->eviction_cost = 0; 254 new_area->next = area; 255 new_area->prev = area->prev; 256 if (area->prev->next) 257 area->prev->next = new_area; 258 else 259 pExaScr->info->offScreenAreas = new_area; 260 area->prev = new_area; 261 area->base_offset = new_area->base_offset + new_area->size; 262 area->size = real_size; 263 } 264 else 265 pExaScr->numOffscreenAvailable--; 266 267 /* 268 * Mark this area as in use 269 */ 270 if (locked) 271 area->state = ExaOffscreenLocked; 272 else 273 area->state = ExaOffscreenRemovable; 274 area->privData = privData; 275 area->save = save; 276 area->last_use = pExaScr->offScreenCounter++; 277 area->offset = (area->base_offset + align - 1); 278 area->offset -= area->offset % align; 279 area->align = align; 280 281 ExaOffscreenValidate(pScreen); 282 283 DBG_OFFSCREEN(("Alloc 0x%x -> 0x%x (0x%x)\n", size, 284 area->base_offset, area->offset)); 285 return area; 286} 287 288/** 289 * Ejects all offscreen areas, and uninitializes the offscreen memory manager. 290 */ 291void 292ExaOffscreenSwapOut(ScreenPtr pScreen) 293{ 294 ExaScreenPriv(pScreen); 295 296 ExaOffscreenValidate(pScreen); 297 /* loop until a single free area spans the space */ 298 for (;;) { 299 ExaOffscreenArea *area = pExaScr->info->offScreenAreas; 300 301 if (!area) 302 break; 303 if (area->state == ExaOffscreenAvail) { 304 area = area->next; 305 if (!area) 306 break; 307 } 308 assert(area->state != ExaOffscreenAvail); 309 (void) ExaOffscreenKickOut(pScreen, area); 310 ExaOffscreenValidate(pScreen); 311 } 312 ExaOffscreenValidate(pScreen); 313 ExaOffscreenFini(pScreen); 314} 315 316/** Ejects all pixmaps managed by EXA. */ 317static void 318ExaOffscreenEjectPixmaps(ScreenPtr pScreen) 319{ 320 ExaScreenPriv(pScreen); 321 322 ExaOffscreenValidate(pScreen); 323 /* loop until a single free area spans the space */ 324 for (;;) { 325 ExaOffscreenArea *area; 326 327 for (area = pExaScr->info->offScreenAreas; area != NULL; 328 area = area->next) { 329 if (area->state == ExaOffscreenRemovable && 330 area->save == exaPixmapSave) { 331 (void) ExaOffscreenKickOut(pScreen, area); 332 ExaOffscreenValidate(pScreen); 333 break; 334 } 335 } 336 if (area == NULL) 337 break; 338 } 339 ExaOffscreenValidate(pScreen); 340} 341 342void 343ExaOffscreenSwapIn(ScreenPtr pScreen) 344{ 345 exaOffscreenInit(pScreen); 346} 347 348/** 349 * Prepares EXA for disabling of FB access, or restoring it. 350 * 351 * In version 2.1, the disabling results in pixmaps being ejected, while other 352 * allocations remain. With this plus the prevention of migration while 353 * swappedOut is set, EXA by itself should not cause any access of the 354 * framebuffer to occur while swapped out. Any remaining issues are the 355 * responsibility of the driver. 356 * 357 * Prior to version 2.1, all allocations, including locked ones, are ejected 358 * when access is disabled, and the allocator is torn down while swappedOut 359 * is set. This is more drastic, and caused implementation difficulties for 360 * many drivers that could otherwise handle the lack of FB access while 361 * swapped out. 362 */ 363void 364exaEnableDisableFBAccess(ScreenPtr pScreen, Bool enable) 365{ 366 ExaScreenPriv(pScreen); 367 368 if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) 369 return; 370 371 if (!enable && pExaScr->disableFbCount++ == 0) { 372 if (pExaScr->info->exa_minor < 1) 373 ExaOffscreenSwapOut(pScreen); 374 else 375 ExaOffscreenEjectPixmaps(pScreen); 376 pExaScr->swappedOut = TRUE; 377 } 378 379 if (enable && --pExaScr->disableFbCount == 0) { 380 if (pExaScr->info->exa_minor < 1) 381 ExaOffscreenSwapIn(pScreen); 382 pExaScr->swappedOut = FALSE; 383 } 384} 385 386/* merge the next free area into this one */ 387static void 388ExaOffscreenMerge(ExaScreenPrivPtr pExaScr, ExaOffscreenArea * area) 389{ 390 ExaOffscreenArea *next = area->next; 391 392 /* account for space */ 393 area->size += next->size; 394 /* frob pointer */ 395 area->next = next->next; 396 if (area->next) 397 area->next->prev = area; 398 else 399 pExaScr->info->offScreenAreas->prev = area; 400 free(next); 401 402 pExaScr->numOffscreenAvailable--; 403} 404 405/** 406 * exaOffscreenFree frees an allocation. 407 * 408 * @param pScreen current screen 409 * @param area offscreen area to free 410 * 411 * exaOffscreenFree frees an allocation created by exaOffscreenAlloc. Note that 412 * the save callback of the area is not called, and it is up to the driver to 413 * do any cleanup necessary as a result. 414 * 415 * @return pointer to the newly freed area. This behavior should not be relied 416 * on. 417 */ 418ExaOffscreenArea * 419exaOffscreenFree(ScreenPtr pScreen, ExaOffscreenArea * area) 420{ 421 ExaScreenPriv(pScreen); 422 ExaOffscreenArea *next = area->next; 423 ExaOffscreenArea *prev; 424 425 DBG_OFFSCREEN(("Free 0x%x -> 0x%x (0x%x)\n", area->size, 426 area->base_offset, area->offset)); 427 ExaOffscreenValidate(pScreen); 428 429 area->state = ExaOffscreenAvail; 430 area->save = NULL; 431 area->last_use = 0; 432 area->eviction_cost = 0; 433 /* 434 * Find previous area 435 */ 436 if (area == pExaScr->info->offScreenAreas) 437 prev = NULL; 438 else 439 prev = area->prev; 440 441 pExaScr->numOffscreenAvailable++; 442 443 /* link with next area if free */ 444 if (next && next->state == ExaOffscreenAvail) 445 ExaOffscreenMerge(pExaScr, area); 446 447 /* link with prev area if free */ 448 if (prev && prev->state == ExaOffscreenAvail) { 449 area = prev; 450 ExaOffscreenMerge(pExaScr, area); 451 } 452 453 ExaOffscreenValidate(pScreen); 454 DBG_OFFSCREEN(("\tdone freeing\n")); 455 return area; 456} 457 458void 459ExaOffscreenMarkUsed(PixmapPtr pPixmap) 460{ 461 ExaPixmapPriv(pPixmap); 462 ExaScreenPriv(pPixmap->drawable.pScreen); 463 464 if (!pExaPixmap || !pExaPixmap->area) 465 return; 466 467 pExaPixmap->area->last_use = pExaScr->offScreenCounter++; 468} 469 470/** 471 * Defragment offscreen memory by compacting allocated areas at the end of it, 472 * leaving the total amount of memory available as a single area at the 473 * beginning (when there are no pinned allocations). 474 */ 475_X_HIDDEN ExaOffscreenArea * 476ExaOffscreenDefragment(ScreenPtr pScreen) 477{ 478 ExaScreenPriv(pScreen); 479 ExaOffscreenArea *area, *largest_available = NULL; 480 int largest_size = 0; 481 PixmapPtr pDstPix; 482 ExaPixmapPrivPtr pExaDstPix; 483 484 pDstPix = (*pScreen->CreatePixmap) (pScreen, 0, 0, 0, 0); 485 486 if (!pDstPix) 487 return NULL; 488 489 pExaDstPix = ExaGetPixmapPriv(pDstPix); 490 pExaDstPix->use_gpu_copy = TRUE; 491 492 for (area = pExaScr->info->offScreenAreas->prev; 493 area != pExaScr->info->offScreenAreas;) { 494 ExaOffscreenArea *prev = area->prev; 495 PixmapPtr pSrcPix; 496 ExaPixmapPrivPtr pExaSrcPix; 497 Bool save_use_gpu_copy; 498 int save_pitch; 499 500 if (area->state != ExaOffscreenAvail || 501 prev->state == ExaOffscreenLocked || 502 (prev->state == ExaOffscreenRemovable && 503 prev->save != exaPixmapSave)) { 504 area = prev; 505 continue; 506 } 507 508 if (prev->state == ExaOffscreenAvail) { 509 if (area == largest_available) { 510 largest_available = prev; 511 largest_size += prev->size; 512 } 513 area = prev; 514 ExaOffscreenMerge(pExaScr, area); 515 continue; 516 } 517 518 if (area->size > largest_size) { 519 largest_available = area; 520 largest_size = area->size; 521 } 522 523 pSrcPix = prev->privData; 524 pExaSrcPix = ExaGetPixmapPriv(pSrcPix); 525 526 pExaDstPix->fb_ptr = pExaScr->info->memoryBase + 527 area->base_offset + area->size - prev->size + prev->base_offset - 528 prev->offset; 529 pExaDstPix->fb_ptr -= (unsigned long) pExaDstPix->fb_ptr % prev->align; 530 531 if (pExaDstPix->fb_ptr <= pExaSrcPix->fb_ptr) { 532 area = prev; 533 continue; 534 } 535 536 if (!(pExaScr->info->flags & EXA_SUPPORTS_OFFSCREEN_OVERLAPS) && 537 (pExaSrcPix->fb_ptr + prev->size) > pExaDstPix->fb_ptr) { 538 area = prev; 539 continue; 540 } 541 542 save_use_gpu_copy = pExaSrcPix->use_gpu_copy; 543 save_pitch = pSrcPix->devKind; 544 545 pExaSrcPix->use_gpu_copy = TRUE; 546 pSrcPix->devKind = pExaSrcPix->fb_pitch; 547 548 pDstPix->drawable.width = pSrcPix->drawable.width; 549 pDstPix->devKind = pSrcPix->devKind; 550 pDstPix->drawable.height = pSrcPix->drawable.height; 551 pDstPix->drawable.depth = pSrcPix->drawable.depth; 552 pDstPix->drawable.bitsPerPixel = pSrcPix->drawable.bitsPerPixel; 553 554 if (!pExaScr->info->PrepareCopy(pSrcPix, pDstPix, -1, -1, GXcopy, ~0)) { 555 pExaSrcPix->use_gpu_copy = save_use_gpu_copy; 556 pSrcPix->devKind = save_pitch; 557 area = prev; 558 continue; 559 } 560 561 pExaScr->info->Copy(pDstPix, 0, 0, 0, 0, pDstPix->drawable.width, 562 pDstPix->drawable.height); 563 pExaScr->info->DoneCopy(pDstPix); 564 exaMarkSync(pScreen); 565 566 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)); 567 568 /* Calculate swapped area offsets and sizes */ 569 area->base_offset = prev->base_offset; 570 area->offset = area->base_offset; 571 prev->offset += pExaDstPix->fb_ptr - pExaSrcPix->fb_ptr; 572 assert(prev->offset >= pExaScr->info->offScreenBase); 573 assert(prev->offset < pExaScr->info->memorySize); 574 prev->base_offset = prev->offset; 575 if (area->next) 576 prev->size = area->next->base_offset - prev->base_offset; 577 else 578 prev->size = pExaScr->info->memorySize - prev->base_offset; 579 area->size = prev->base_offset - area->base_offset; 580 581 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)); 582 583 /* Swap areas in list */ 584 if (area->next) 585 area->next->prev = prev; 586 else 587 pExaScr->info->offScreenAreas->prev = prev; 588 if (prev->prev->next) 589 prev->prev->next = area; 590 else 591 pExaScr->info->offScreenAreas = area; 592 prev->next = area->next; 593 area->next = prev; 594 area->prev = prev->prev; 595 prev->prev = area; 596 if (!area->prev->next) 597 pExaScr->info->offScreenAreas = area; 598 599#if DEBUG_OFFSCREEN 600 if (prev->prev == prev || prev->next == prev) 601 ErrorF("Whoops, prev points to itself!\n"); 602 603 if (area->prev == area || area->next == area) 604 ErrorF("Whoops, area points to itself!\n"); 605#endif 606 607 pExaSrcPix->fb_ptr = pExaDstPix->fb_ptr; 608 pExaSrcPix->use_gpu_copy = save_use_gpu_copy; 609 pSrcPix->devKind = save_pitch; 610 } 611 612 pDstPix->drawable.width = 0; 613 pDstPix->drawable.height = 0; 614 pDstPix->drawable.depth = 0; 615 pDstPix->drawable.bitsPerPixel = 0; 616 617 (*pScreen->DestroyPixmap) (pDstPix); 618 619 if (area->state == ExaOffscreenAvail && area->size > largest_size) 620 return area; 621 622 return largest_available; 623} 624 625/** 626 * exaOffscreenInit initializes the offscreen memory manager. 627 * 628 * @param pScreen current screen 629 * 630 * exaOffscreenInit is called by exaDriverInit to set up the memory manager for 631 * the screen, if any offscreen memory is available. 632 */ 633Bool 634exaOffscreenInit(ScreenPtr pScreen) 635{ 636 ExaScreenPriv(pScreen); 637 ExaOffscreenArea *area; 638 639 /* Allocate a big free area */ 640 area = malloc(sizeof(ExaOffscreenArea)); 641 642 if (!area) 643 return FALSE; 644 645 area->state = ExaOffscreenAvail; 646 area->base_offset = pExaScr->info->offScreenBase; 647 area->offset = area->base_offset; 648 area->align = 0; 649 area->size = pExaScr->info->memorySize - area->base_offset; 650 area->save = NULL; 651 area->next = NULL; 652 area->prev = area; 653 area->last_use = 0; 654 area->eviction_cost = 0; 655 656 /* Add it to the free areas */ 657 pExaScr->info->offScreenAreas = area; 658 pExaScr->offScreenCounter = 1; 659 pExaScr->numOffscreenAvailable = 1; 660 661 ExaOffscreenValidate(pScreen); 662 663 return TRUE; 664} 665 666void 667ExaOffscreenFini(ScreenPtr pScreen) 668{ 669 ExaScreenPriv(pScreen); 670 ExaOffscreenArea *area; 671 672 /* just free all of the area records */ 673 while ((area = pExaScr->info->offScreenAreas)) { 674 pExaScr->info->offScreenAreas = area->next; 675 free(area); 676 } 677} 678