diff options
Diffstat (limited to 'xorg-server/exa/exa_offscreen.c')
-rw-r--r-- | xorg-server/exa/exa_offscreen.c | 249 |
1 files changed, 225 insertions, 24 deletions
diff --git a/xorg-server/exa/exa_offscreen.c b/xorg-server/exa/exa_offscreen.c index 4aaa2c132..eb53b2a30 100644 --- a/xorg-server/exa/exa_offscreen.c +++ b/xorg-server/exa/exa_offscreen.c @@ -93,7 +93,7 @@ exaFindAreaToEvict(ExaScreenPrivPtr pExaScr, int size, int align) { ExaOffscreenArea *begin, *end, *best; unsigned cost, best_cost; - int avail, real_size, tmp; + int avail, real_size; best_cost = UINT_MAX; begin = end = pExaScr->info->offScreenAreas; @@ -111,10 +111,7 @@ exaFindAreaToEvict(ExaScreenPrivPtr pExaScr, int size, int align) break; /* adjust size needed to account for alignment loss for this area */ - real_size = size; - tmp = begin->base_offset % align; - if (tmp) - real_size += (align - tmp); + real_size = size + (begin->base_offset + begin->size - size) % align; while (avail < real_size && end != NULL) { @@ -172,7 +169,7 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, { ExaOffscreenArea *area; ExaScreenPriv (pScreen); - int tmp, real_size = 0; + int real_size = 0, free_total = 0, largest_avail = 0; #if DEBUG_OFFSCREEN static int number = 0; ErrorF("================= ============ allocating a new pixmap %d\n", ++number); @@ -205,14 +202,37 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, continue; /* adjust size to match alignment requirement */ - real_size = size; - tmp = area->base_offset % align; - if (tmp) - real_size += (align - tmp); + real_size = size + (area->base_offset + area->size - size) % align; /* does it fit? */ if (real_size <= area->size) break; + + free_total += area->size; + + if (area->size > largest_avail) + largest_avail = area->size; + } + + if (!area && free_total >= size) { + CARD32 now = GetTimeInMillis(); + + /* Don't defragment more than once per second, to avoid adding more + * overhead than we're trying to prevent + */ + if (abs((INT32) (now - pExaScr->lastDefragment)) > 1000) { + area = ExaOffscreenDefragment(pScreen); + pExaScr->lastDefragment = now; + + if (area) { + /* adjust size to match alignment requirement */ + real_size = size + (area->base_offset + area->size - size) % align; + + /* does it fit? */ + if (real_size > area->size) + area = NULL; + } + } } if (!area) @@ -228,10 +248,7 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, } /* adjust size needed to account for alignment loss for this area */ - real_size = size; - tmp = area->base_offset % align; - if (tmp) - real_size += (align - tmp); + real_size = size + (area->base_offset + area->size - size) % align; /* * Kick out first area if in use @@ -254,17 +271,27 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, ExaOffscreenArea *new_area = xalloc (sizeof (ExaOffscreenArea)); if (!new_area) return NULL; - new_area->base_offset = area->base_offset + real_size; + new_area->base_offset = area->base_offset; + new_area->offset = new_area->base_offset; + new_area->align = 0; new_area->size = area->size - real_size; new_area->state = ExaOffscreenAvail; new_area->save = NULL; new_area->last_use = 0; new_area->eviction_cost = 0; - new_area->next = area->next; - area->next = new_area; + new_area->next = area; + new_area->prev = area->prev; + if (area->prev->next) + area->prev->next = new_area; + else + pExaScr->info->offScreenAreas = new_area; + area->prev = new_area; + area->base_offset = new_area->base_offset + new_area->size; area->size = real_size; - } + } else + pExaScr->numOffscreenAvailable--; + /* * Mark this area as in use */ @@ -277,6 +304,7 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, area->last_use = pExaScr->offScreenCounter++; area->offset = (area->base_offset + align - 1); area->offset -= area->offset % align; + area->align = align; ExaOffscreenValidate (pScreen); @@ -371,6 +399,9 @@ exaEnableDisableFBAccess (int index, Bool enable) ScreenPtr pScreen = screenInfo.screens[index]; ExaScreenPriv (pScreen); + if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) + return; + if (!enable && pExaScr->disableFbCount++ == 0) { if (pExaScr->info->exa_minor < 1) ExaOffscreenSwapOut (pScreen); @@ -388,7 +419,7 @@ exaEnableDisableFBAccess (int index, Bool enable) /* merge the next free area into this one */ static void -ExaOffscreenMerge (ExaOffscreenArea *area) +ExaOffscreenMerge (ExaScreenPrivPtr pExaScr, ExaOffscreenArea *area) { ExaOffscreenArea *next = area->next; @@ -396,7 +427,13 @@ ExaOffscreenMerge (ExaOffscreenArea *area) area->size += next->size; /* frob pointer */ area->next = next->next; + if (area->next) + area->next->prev = area; + else + pExaScr->info->offScreenAreas->prev = area; xfree (next); + + pExaScr->numOffscreenAvailable--; } /** @@ -433,19 +470,19 @@ exaOffscreenFree (ScreenPtr pScreen, ExaOffscreenArea *area) if (area == pExaScr->info->offScreenAreas) prev = NULL; else - for (prev = pExaScr->info->offScreenAreas; prev; prev = prev->next) - if (prev->next == area) - break; + prev = area->prev; + + pExaScr->numOffscreenAvailable++; /* link with next area if free */ if (next && next->state == ExaOffscreenAvail) - ExaOffscreenMerge (area); + ExaOffscreenMerge (pExaScr, area); /* link with prev area if free */ if (prev && prev->state == ExaOffscreenAvail) { area = prev; - ExaOffscreenMerge (area); + ExaOffscreenMerge (pExaScr, area); } ExaOffscreenValidate (pScreen); @@ -466,6 +503,167 @@ ExaOffscreenMarkUsed (PixmapPtr pPixmap) } /** + * Defragment offscreen memory by compacting allocated areas at the end of it, + * leaving the total amount of memory available as a single area at the + * beginning (when there are no pinned allocations). + */ +_X_HIDDEN ExaOffscreenArea* +ExaOffscreenDefragment (ScreenPtr pScreen) +{ + ExaScreenPriv (pScreen); + ExaOffscreenArea *area, *largest_available = NULL; + int largest_size = 0; + PixmapPtr pDstPix; + ExaPixmapPrivPtr pExaDstPix; + + pDstPix = (*pScreen->CreatePixmap) (pScreen, 0, 0, 0, 0); + + if (!pDstPix) + return NULL; + + pExaDstPix = ExaGetPixmapPriv (pDstPix); + pExaDstPix->offscreen = TRUE; + + for (area = pExaScr->info->offScreenAreas->prev; + area != pExaScr->info->offScreenAreas; + ) + { + ExaOffscreenArea *prev = area->prev; + PixmapPtr pSrcPix; + ExaPixmapPrivPtr pExaSrcPix; + Bool save_offscreen; + int save_pitch; + + if (area->state != ExaOffscreenAvail || + prev->state == ExaOffscreenLocked || + (prev->state == ExaOffscreenRemovable && + prev->save != exaPixmapSave)) { + area = prev; + continue; + } + + if (prev->state == ExaOffscreenAvail) { + if (area == largest_available) { + largest_available = prev; + largest_size += prev->size; + } + area = prev; + ExaOffscreenMerge (pExaScr, area); + continue; + } + + if (area->size > largest_size) { + largest_available = area; + largest_size = area->size; + } + + pSrcPix = prev->privData; + pExaSrcPix = ExaGetPixmapPriv (pSrcPix); + + pExaDstPix->fb_ptr = pExaScr->info->memoryBase + + area->base_offset + area->size - prev->size + prev->base_offset - + prev->offset; + pExaDstPix->fb_ptr -= (unsigned long)pExaDstPix->fb_ptr % prev->align; + + if (pExaDstPix->fb_ptr <= pExaSrcPix->fb_ptr) { + area = prev; + continue; + } + + if (!(pExaScr->info->flags & EXA_SUPPORTS_OFFSCREEN_OVERLAPS) && + (pExaSrcPix->fb_ptr + prev->size) > pExaDstPix->fb_ptr) { + area = prev; + continue; + } + + save_offscreen = pExaSrcPix->offscreen; + save_pitch = pSrcPix->devKind; + + pExaSrcPix->offscreen = TRUE; + pSrcPix->devKind = pExaSrcPix->fb_pitch; + + pDstPix->drawable.width = pSrcPix->drawable.width; + pDstPix->devKind = pSrcPix->devKind; + pDstPix->drawable.height = pSrcPix->drawable.height; + pDstPix->drawable.depth = pSrcPix->drawable.depth; + pDstPix->drawable.bitsPerPixel = pSrcPix->drawable.bitsPerPixel; + + if (!pExaScr->info->PrepareCopy (pSrcPix, pDstPix, -1, -1, GXcopy, ~0)) { + pExaSrcPix->offscreen = save_offscreen; + pSrcPix->devKind = save_pitch; + area = prev; + continue; + } + + pExaScr->info->Copy (pDstPix, 0, 0, 0, 0, pDstPix->drawable.width, + pDstPix->drawable.height); + pExaScr->info->DoneCopy (pDstPix); + exaMarkSync (pScreen); + + 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)); + + /* Calculate swapped area offsets and sizes */ + area->base_offset = prev->base_offset; + area->offset = area->base_offset; + prev->offset += pExaDstPix->fb_ptr - pExaSrcPix->fb_ptr; + assert(prev->offset >= pExaScr->info->offScreenBase && + prev->offset < pExaScr->info->memorySize); + prev->base_offset = prev->offset; + if (area->next) + prev->size = area->next->base_offset - prev->base_offset; + else + prev->size = pExaScr->info->memorySize - prev->base_offset; + area->size = prev->base_offset - area->base_offset; + + 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)); + + /* Swap areas in list */ + if (area->next) + area->next->prev = prev; + else + pExaScr->info->offScreenAreas->prev = prev; + if (prev->prev->next) + prev->prev->next = area; + else + pExaScr->info->offScreenAreas = area; + prev->next = area->next; + area->next = prev; + area->prev = prev->prev; + prev->prev = area; + if (!area->prev->next) + pExaScr->info->offScreenAreas = area; + +#if DEBUG_OFFSCREEN + if (prev->prev == prev || prev->next == prev) + ErrorF("Whoops, prev points to itself!\n"); + + if (area->prev == area || area->next == area) + ErrorF("Whoops, area points to itself!\n"); +#endif + + pExaSrcPix->fb_ptr = pExaDstPix->fb_ptr; + pExaSrcPix->offscreen = save_offscreen; + pSrcPix->devKind = save_pitch; + } + + pDstPix->drawable.width = 0; + pDstPix->drawable.height = 0; + pDstPix->drawable.depth = 0; + pDstPix->drawable.bitsPerPixel = 0; + + (*pScreen->DestroyPixmap) (pDstPix); + + if (area->state == ExaOffscreenAvail && area->size > largest_size) + return area; + + return largest_available; +} + +/** * exaOffscreenInit initializes the offscreen memory manager. * * @param pScreen current screen @@ -488,15 +686,18 @@ exaOffscreenInit (ScreenPtr pScreen) area->state = ExaOffscreenAvail; area->base_offset = pExaScr->info->offScreenBase; area->offset = area->base_offset; + area->align = 0; area->size = pExaScr->info->memorySize - area->base_offset; area->save = NULL; area->next = NULL; + area->prev = area; area->last_use = 0; area->eviction_cost = 0; /* Add it to the free areas */ pExaScr->info->offScreenAreas = area; pExaScr->offScreenCounter = 1; + pExaScr->numOffscreenAvailable = 1; ExaOffscreenValidate (pScreen); |