exa_offscreen.c revision 05b261ec
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 * and a score for each area. As an area is marked used, its score is 26 * incremented, and periodically all of the areas have their scores decayed by 27 * a fraction. When allocating, the contiguous block of areas with the minimum 28 * score is found and evicted in order to make room for the new allocation. 29 */ 30 31#include "exa_priv.h" 32 33#include <limits.h> 34#include <assert.h> 35#include <stdlib.h> 36 37#if DEBUG_OFFSCREEN 38#define DBG_OFFSCREEN(a) ErrorF a 39#else 40#define DBG_OFFSCREEN(a) 41#endif 42 43#if DEBUG_OFFSCREEN 44static void 45ExaOffscreenValidate (ScreenPtr pScreen) 46{ 47 ExaScreenPriv (pScreen); 48 ExaOffscreenArea *prev = 0, *area; 49 50 assert (pExaScr->info->offScreenAreas->base_offset == 51 pExaScr->info->offScreenBase); 52 for (area = pExaScr->info->offScreenAreas; area; area = area->next) 53 { 54 assert (area->offset >= area->base_offset && 55 area->offset < (area->base_offset + area->size)); 56 if (prev) 57 assert (prev->base_offset + prev->size == area->base_offset); 58 prev = area; 59 } 60 assert (prev->base_offset + prev->size == pExaScr->info->memorySize); 61} 62#else 63#define ExaOffscreenValidate(s) 64#endif 65 66static ExaOffscreenArea * 67ExaOffscreenKickOut (ScreenPtr pScreen, ExaOffscreenArea *area) 68{ 69 if (area->save) 70 (*area->save) (pScreen, area); 71 return exaOffscreenFree (pScreen, area); 72} 73 74/** 75 * exaOffscreenAlloc allocates offscreen memory 76 * 77 * @param pScreen current screen 78 * @param size size in bytes of the allocation 79 * @param align byte alignment requirement for the offset of the allocated area 80 * @param locked whether the allocated area is locked and can't be kicked out 81 * @param save callback for when the area is evicted from memory 82 * @param privdata private data for the save callback. 83 * 84 * Allocates offscreen memory from the device associated with pScreen. size 85 * and align deteremine where and how large the allocated area is, and locked 86 * will mark whether it should be held in card memory. privdata may be any 87 * pointer for the save callback when the area is removed. 88 * 89 * Note that locked areas do get evicted on VT switch unless the driver 90 * requested version 2.1 or newer behavior. In that case, the save callback is 91 * still called. 92 */ 93ExaOffscreenArea * 94exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, 95 Bool locked, 96 ExaOffscreenSaveProc save, 97 pointer privData) 98{ 99 ExaOffscreenArea *area, *begin, *best; 100 ExaScreenPriv (pScreen); 101 int tmp, real_size = 0, best_score; 102#if DEBUG_OFFSCREEN 103 static int number = 0; 104 ErrorF("================= ============ allocating a new pixmap %d\n", ++number); 105#endif 106 107 ExaOffscreenValidate (pScreen); 108 if (!align) 109 align = 1; 110 111 if (!size) 112 { 113 DBG_OFFSCREEN (("Alloc 0x%x -> EMPTY\n", size)); 114 return NULL; 115 } 116 117 /* throw out requests that cannot fit */ 118 if (size > (pExaScr->info->memorySize - pExaScr->info->offScreenBase)) 119 { 120 DBG_OFFSCREEN (("Alloc 0x%x vs (0x%lx) -> TOBIG\n", size, 121 pExaScr->info->memorySize - 122 pExaScr->info->offScreenBase)); 123 return NULL; 124 } 125 126 /* Try to find a free space that'll fit. */ 127 for (area = pExaScr->info->offScreenAreas; area; area = area->next) 128 { 129 /* skip allocated areas */ 130 if (area->state != ExaOffscreenAvail) 131 continue; 132 133 /* adjust size to match alignment requirement */ 134 real_size = size; 135 tmp = area->base_offset % align; 136 if (tmp) 137 real_size += (align - tmp); 138 139 /* does it fit? */ 140 if (real_size <= area->size) 141 break; 142 } 143 144 if (!area) 145 { 146 /* 147 * Kick out existing users to make space. 148 * 149 * First, locate a region which can hold the desired object. 150 */ 151 152 /* prev points at the first object to boot */ 153 best = NULL; 154 best_score = INT_MAX; 155 for (begin = pExaScr->info->offScreenAreas; begin != NULL; 156 begin = begin->next) 157 { 158 int avail, score; 159 ExaOffscreenArea *scan; 160 161 if (begin->state == ExaOffscreenLocked) 162 continue; 163 164 /* adjust size needed to account for alignment loss for this area */ 165 real_size = size; 166 tmp = begin->base_offset % align; 167 if (tmp) 168 real_size += (align - tmp); 169 170 avail = 0; 171 score = 0; 172 /* now see if we can make room here, and how "costly" it'll be. */ 173 for (scan = begin; scan != NULL; scan = scan->next) 174 { 175 if (scan->state == ExaOffscreenLocked) { 176 /* Can't make room here, start after this locked area. */ 177 begin = scan; 178 break; 179 } 180 /* Score should only be non-zero for ExaOffscreenRemovable */ 181 score += scan->score; 182 avail += scan->size; 183 if (avail >= real_size) 184 break; 185 } 186 /* Is it the best option we've found so far? */ 187 if (avail >= real_size && score < best_score) { 188 best = begin; 189 best_score = score; 190 } 191 } 192 area = best; 193 if (!area) 194 { 195 DBG_OFFSCREEN (("Alloc 0x%x -> NOSPACE\n", size)); 196 /* Could not allocate memory */ 197 ExaOffscreenValidate (pScreen); 198 return NULL; 199 } 200 201 /* adjust size needed to account for alignment loss for this area */ 202 real_size = size; 203 tmp = area->base_offset % align; 204 if (tmp) 205 real_size += (align - tmp); 206 207 /* 208 * Kick out first area if in use 209 */ 210 if (area->state != ExaOffscreenAvail) 211 area = ExaOffscreenKickOut (pScreen, area); 212 /* 213 * Now get the system to merge the other needed areas together 214 */ 215 while (area->size < real_size) 216 { 217 assert (area->next && area->next->state == ExaOffscreenRemovable); 218 (void) ExaOffscreenKickOut (pScreen, area->next); 219 } 220 } 221 222 /* save extra space in new area */ 223 if (real_size < area->size) 224 { 225 ExaOffscreenArea *new_area = xalloc (sizeof (ExaOffscreenArea)); 226 if (!new_area) 227 return NULL; 228 new_area->base_offset = area->base_offset + real_size; 229 new_area->offset = new_area->base_offset; 230 new_area->size = area->size - real_size; 231 new_area->state = ExaOffscreenAvail; 232 new_area->save = NULL; 233 new_area->score = 0; 234 new_area->next = area->next; 235 area->next = new_area; 236 area->size = real_size; 237 } 238 /* 239 * Mark this area as in use 240 */ 241 if (locked) 242 area->state = ExaOffscreenLocked; 243 else 244 area->state = ExaOffscreenRemovable; 245 area->privData = privData; 246 area->save = save; 247 area->score = 0; 248 area->offset = (area->base_offset + align - 1); 249 area->offset -= area->offset % align; 250 251 ExaOffscreenValidate (pScreen); 252 253 DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x (0x%x)\n", size, 254 area->base_offset, area->offset)); 255 return area; 256} 257 258/** 259 * Ejects all offscreen areas, and uninitializes the offscreen memory manager. 260 */ 261void 262ExaOffscreenSwapOut (ScreenPtr pScreen) 263{ 264 ExaScreenPriv (pScreen); 265 266 ExaOffscreenValidate (pScreen); 267 /* loop until a single free area spans the space */ 268 for (;;) 269 { 270 ExaOffscreenArea *area = pExaScr->info->offScreenAreas; 271 272 if (!area) 273 break; 274 if (area->state == ExaOffscreenAvail) 275 { 276 area = area->next; 277 if (!area) 278 break; 279 } 280 assert (area->state != ExaOffscreenAvail); 281 (void) ExaOffscreenKickOut (pScreen, area); 282 ExaOffscreenValidate (pScreen); 283 } 284 ExaOffscreenValidate (pScreen); 285 ExaOffscreenFini (pScreen); 286} 287 288/** Ejects all pixmaps managed by EXA. */ 289static void 290ExaOffscreenEjectPixmaps (ScreenPtr pScreen) 291{ 292 ExaScreenPriv (pScreen); 293 294 ExaOffscreenValidate (pScreen); 295 /* loop until a single free area spans the space */ 296 for (;;) 297 { 298 ExaOffscreenArea *area; 299 300 for (area = pExaScr->info->offScreenAreas; area != NULL; 301 area = area->next) 302 { 303 if (area->state == ExaOffscreenRemovable && 304 area->save == exaPixmapSave) 305 { 306 (void) ExaOffscreenKickOut (pScreen, area); 307 ExaOffscreenValidate (pScreen); 308 break; 309 } 310 } 311 if (area == NULL) 312 break; 313 } 314 ExaOffscreenValidate (pScreen); 315} 316 317void 318ExaOffscreenSwapIn (ScreenPtr pScreen) 319{ 320 exaOffscreenInit (pScreen); 321} 322 323/** 324 * Prepares EXA for disabling of FB access, or restoring it. 325 * 326 * In version 2.1, the disabling results in pixmaps being ejected, while other 327 * allocations remain. With this plus the prevention of migration while 328 * swappedOut is set, EXA by itself should not cause any access of the 329 * framebuffer to occur while swapped out. Any remaining issues are the 330 * responsibility of the driver. 331 * 332 * Prior to version 2.1, all allocations, including locked ones, are ejected 333 * when access is disabled, and the allocator is torn down while swappedOut 334 * is set. This is more drastic, and caused implementation difficulties for 335 * many drivers that could otherwise handle the lack of FB access while 336 * swapped out. 337 */ 338void 339exaEnableDisableFBAccess (int index, Bool enable) 340{ 341 ScreenPtr pScreen = screenInfo.screens[index]; 342 ExaScreenPriv (pScreen); 343 344 if (!enable && pExaScr->disableFbCount++ == 0) { 345 if (pExaScr->info->exa_minor < 1) 346 ExaOffscreenSwapOut (pScreen); 347 else 348 ExaOffscreenEjectPixmaps (pScreen); 349 pExaScr->swappedOut = TRUE; 350 } 351 352 if (enable && --pExaScr->disableFbCount == 0) { 353 if (pExaScr->info->exa_minor < 1) 354 ExaOffscreenSwapIn (pScreen); 355 pExaScr->swappedOut = FALSE; 356 } 357} 358 359/* merge the next free area into this one */ 360static void 361ExaOffscreenMerge (ExaOffscreenArea *area) 362{ 363 ExaOffscreenArea *next = area->next; 364 365 /* account for space */ 366 area->size += next->size; 367 /* frob pointer */ 368 area->next = next->next; 369 xfree (next); 370} 371 372/** 373 * exaOffscreenFree frees an allocation. 374 * 375 * @param pScreen current screen 376 * @param area offscreen area to free 377 * 378 * exaOffscreenFree frees an allocation created by exaOffscreenAlloc. Note that 379 * the save callback of the area is not called, and it is up to the driver to 380 * do any cleanup necessary as a result. 381 * 382 * @return pointer to the newly freed area. This behavior should not be relied 383 * on. 384 */ 385ExaOffscreenArea * 386exaOffscreenFree (ScreenPtr pScreen, ExaOffscreenArea *area) 387{ 388 ExaScreenPriv(pScreen); 389 ExaOffscreenArea *next = area->next; 390 ExaOffscreenArea *prev; 391 392 DBG_OFFSCREEN (("Free 0x%x -> 0x%x (0x%x)\n", area->size, 393 area->base_offset, area->offset)); 394 ExaOffscreenValidate (pScreen); 395 396 area->state = ExaOffscreenAvail; 397 area->save = NULL; 398 area->score = 0; 399 /* 400 * Find previous area 401 */ 402 if (area == pExaScr->info->offScreenAreas) 403 prev = NULL; 404 else 405 for (prev = pExaScr->info->offScreenAreas; prev; prev = prev->next) 406 if (prev->next == area) 407 break; 408 409 /* link with next area if free */ 410 if (next && next->state == ExaOffscreenAvail) 411 ExaOffscreenMerge (area); 412 413 /* link with prev area if free */ 414 if (prev && prev->state == ExaOffscreenAvail) 415 { 416 area = prev; 417 ExaOffscreenMerge (area); 418 } 419 420 ExaOffscreenValidate (pScreen); 421 DBG_OFFSCREEN(("\tdone freeing\n")); 422 return area; 423} 424 425void 426ExaOffscreenMarkUsed (PixmapPtr pPixmap) 427{ 428 ExaPixmapPriv (pPixmap); 429 ExaScreenPriv (pPixmap->drawable.pScreen); 430 static int iter = 0; 431 432 if (!pExaPixmap || !pExaPixmap->area) 433 return; 434 435 /* The numbers here are arbitrary. We may want to tune these. */ 436 pExaPixmap->area->score += 100; 437 if (++iter == 10) { 438 ExaOffscreenArea *area; 439 for (area = pExaScr->info->offScreenAreas; area != NULL; 440 area = area->next) 441 { 442 if (area->state == ExaOffscreenRemovable) 443 area->score = (area->score * 7) / 8; 444 } 445 iter = 0; 446 } 447} 448 449/** 450 * exaOffscreenInit initializes the offscreen memory manager. 451 * 452 * @param pScreen current screen 453 * 454 * exaOffscreenInit is called by exaDriverInit to set up the memory manager for 455 * the screen, if any offscreen memory is available. 456 */ 457Bool 458exaOffscreenInit (ScreenPtr pScreen) 459{ 460 ExaScreenPriv (pScreen); 461 ExaOffscreenArea *area; 462 463 /* Allocate a big free area */ 464 area = xalloc (sizeof (ExaOffscreenArea)); 465 466 if (!area) 467 return FALSE; 468 469 area->state = ExaOffscreenAvail; 470 area->base_offset = pExaScr->info->offScreenBase; 471 area->offset = area->base_offset; 472 area->size = pExaScr->info->memorySize - area->base_offset; 473 area->save = NULL; 474 area->next = NULL; 475 area->score = 0; 476 477 /* Add it to the free areas */ 478 pExaScr->info->offScreenAreas = area; 479 480 ExaOffscreenValidate (pScreen); 481 482 return TRUE; 483} 484 485void 486ExaOffscreenFini (ScreenPtr pScreen) 487{ 488 ExaScreenPriv (pScreen); 489 ExaOffscreenArea *area; 490 491 /* just free all of the area records */ 492 while ((area = pExaScr->info->offScreenAreas)) 493 { 494 pExaScr->info->offScreenAreas = area->next; 495 xfree (area); 496 } 497} 498