From: Michel Dänzer <[email protected]> At most once per second, under the following circumstances:
* We can't satisfy an offscreen memory allocation, but there seems to be enough offscreen memory available in total. * The server is going idle. Signed-off-by: Michel Dänzer <[email protected]> --- exa/exa.c | 21 +++++ exa/exa.h | 2 + exa/exa_offscreen.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++-- exa/exa_priv.h | 6 ++ 4 files changed, 233 insertions(+), 7 deletions(-) diff --git a/exa/exa.c b/exa/exa.c index a770332..55e83bf 100644 --- a/exa/exa.c +++ b/exa/exa.c @@ -894,6 +894,8 @@ exaCloseScreen(int i, ScreenPtr pScreen) if (ps->Glyphs == exaGlyphs) exaGlyphsFini(pScreen); + if (!pExaScr->info->CreatePixmap) + unwrap(pExaScr, pScreen, BlockHandler); unwrap(pExaScr, pScreen, CreateGC); unwrap(pExaScr, pScreen, CloseScreen); unwrap(pExaScr, pScreen, GetImage); @@ -922,6 +924,23 @@ exaCloseScreen(int i, ScreenPtr pScreen) return (*pScreen->CloseScreen) (i, pScreen); } +static void +ExaBlockHandler(int screenNum, pointer blockData, pointer pTimeout, + pointer pReadmask) +{ + ScreenPtr pScreen = screenInfo.screens[screenNum]; + ExaScreenPriv(pScreen); + + /* Try and keep the offscreen memory area tidy every now and then when we're + * going idle anyway. + */ + ExaOffscreenDefragment(pScreen); + + unwrap(pExaScr, pScreen, BlockHandler); + (*pScreen->BlockHandler) (screenNum, blockData, pTimeout, pReadmask); + wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler); +} + /** * This function allocates a driver structure for EXA drivers to fill in. By * having EXA allocate the structure, the driver structure can be extended @@ -1053,6 +1072,8 @@ exaDriverInit (ScreenPtr pScreen, /* * Replace various fb screen functions */ + if (!pExaScr->info->CreatePixmap) + wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler); wrap(pExaScr, pScreen, CreateGC, exaCreateGC); wrap(pExaScr, pScreen, CloseScreen, exaCloseScreen); wrap(pExaScr, pScreen, GetImage, exaGetImage); diff --git a/exa/exa.h b/exa/exa.h index 21a0f1a..c48a582 100644 --- a/exa/exa.h +++ b/exa/exa.h @@ -64,7 +64,9 @@ struct _ExaOffscreenArea { ExaOffscreenState state; ExaOffscreenArea *next; + ExaOffscreenArea *prev; + int align; /* required alignment */ unsigned eviction_cost; }; diff --git a/exa/exa_offscreen.c b/exa/exa_offscreen.c index 4aaa2c1..d3e015f 100644 --- a/exa/exa_offscreen.c +++ b/exa/exa_offscreen.c @@ -172,7 +172,7 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, { ExaOffscreenArea *area; ExaScreenPriv (pScreen); - int tmp, real_size = 0; + int tmp, real_size = 0, free_total = 0, largest_avail = 0; #if DEBUG_OFFSCREEN static int number = 0; ErrorF("================= ============ allocating a new pixmap %d\n", ++number); @@ -213,6 +213,27 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int 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) { + area = ExaOffscreenDefragment(pScreen); + + if (area) { + /* adjust size to match alignment requirement */ + real_size = size; + tmp = area->base_offset % align; + if (tmp) + real_size += (align - tmp); + + /* does it fit? */ + if (real_size > area->size) + area = NULL; + } } if (!area) @@ -255,14 +276,27 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, if (!new_area) return NULL; new_area->base_offset = area->base_offset + real_size; + +#if DEBUG_OFFSCREEN + if (new_area->base_offset >= pExaScr->info->memorySize) + ErrorF("new_area->base_offset = 0x%08x >= memorySize!\n", + new_area->base_offset); +#endif + 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; + if (area->next) + area->next->prev = new_area; + else + pExaScr->info->offScreenAreas->prev = new_area; area->next = new_area; + new_area->prev = area; area->size = real_size; } /* @@ -277,6 +311,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); @@ -388,7 +423,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,6 +431,10 @@ 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); } @@ -433,19 +472,17 @@ 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; /* 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,164 @@ 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; + 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) + return NULL; + + pDstPix = (*pScreen->CreatePixmap) (pScreen, 0, 0, 0, 0); + + if (!pDstPix) + return NULL; + + pExaScr->lastDefragment = now; + + 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; + } + + 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; + + pExaScr->info->PrepareCopy (pSrcPix, pDstPix, -1, -1, GXcopy, ~0); + 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,9 +683,11 @@ 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; diff --git a/exa/exa_priv.h b/exa/exa_priv.h index 461e5e3..f787361 100644 --- a/exa/exa_priv.h +++ b/exa/exa_priv.h @@ -133,6 +133,7 @@ typedef struct { typedef void (*EnableDisableFBAccessProcPtr)(int, Bool); typedef struct { ExaDriverPtr info; + ScreenBlockHandlerProcPtr SavedBlockHandler; CreateGCProcPtr SavedCreateGC; CloseScreenProcPtr SavedCloseScreen; GetImageProcPtr SavedGetImage; @@ -158,6 +159,8 @@ typedef struct { unsigned disableFbCount; Bool optimize_migration; unsigned offScreenCounter; + CARD32 lastDefragment; + /* Holds information on fallbacks that cannot be relayed otherwise. */ unsigned int fallback_flags; @@ -439,6 +442,9 @@ ExaOffscreenSwapOut (ScreenPtr pScreen); void ExaOffscreenSwapIn (ScreenPtr pScreen); +ExaOffscreenArea* +ExaOffscreenDefragment (ScreenPtr pScreen); + Bool exaOffscreenInit(ScreenPtr pScreen); -- 1.6.2.rc0 _______________________________________________ xorg-devel mailing list [email protected] http://lists.x.org/mailman/listinfo/xorg-devel
