EXA: Defragment offscreen memory.

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.

or

* The server has been idle for at least 100ms, and there is more than one
  available offscreen area.

Signed-off-by: Michel Dänzer <daenzer@vmware.com>
This commit is contained in:
Michel Dänzer 2009-05-18 17:48:57 +02:00
parent 8331bde0ad
commit 510cbd43cd
4 changed files with 297 additions and 8 deletions

View File

@ -1048,6 +1048,50 @@ exaCreateScreenResources(ScreenPtr pScreen)
return TRUE; return TRUE;
} }
static void
ExaBlockHandler(int screenNum, pointer blockData, pointer pTimeout,
pointer pReadmask)
{
ScreenPtr pScreen = screenInfo.screens[screenNum];
ExaScreenPriv(pScreen);
unwrap(pExaScr, pScreen, BlockHandler);
(*pScreen->BlockHandler) (screenNum, blockData, pTimeout, pReadmask);
wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler);
/* Try and keep the offscreen memory area tidy every now and then (at most
* once per second) when the server has been idle for at least 100ms.
*/
if (pExaScr->numOffscreenAvailable > 1) {
CARD32 now = GetTimeInMillis();
pExaScr->nextDefragment = now +
max(100, (INT32)(pExaScr->lastDefragment + 1000 - now));
AdjustWaitForDelay(pTimeout, pExaScr->nextDefragment - now);
}
}
static void
ExaWakeupHandler(int screenNum, pointer wakeupData, unsigned long result,
pointer pReadmask)
{
ScreenPtr pScreen = screenInfo.screens[screenNum];
ExaScreenPriv(pScreen);
unwrap(pExaScr, pScreen, WakeupHandler);
(*pScreen->WakeupHandler) (screenNum, wakeupData, result, pReadmask);
wrap(pExaScr, pScreen, WakeupHandler, ExaWakeupHandler);
if (result == 0 && pExaScr->numOffscreenAvailable > 1) {
CARD32 now = GetTimeInMillis();
if ((int)(now - pExaScr->nextDefragment) > 0) {
ExaOffscreenDefragment(pScreen);
pExaScr->lastDefragment = now;
}
}
}
/** /**
* exaCloseScreen() unwraps its wrapped screen functions and tears down EXA's * exaCloseScreen() unwraps its wrapped screen functions and tears down EXA's
* screen private, before calling down to the next CloseSccreen. * screen private, before calling down to the next CloseSccreen.
@ -1063,6 +1107,10 @@ exaCloseScreen(int i, ScreenPtr pScreen)
if (ps->Glyphs == exaGlyphs) if (ps->Glyphs == exaGlyphs)
exaGlyphsFini(pScreen); exaGlyphsFini(pScreen);
if (pScreen->BlockHandler == ExaBlockHandler)
unwrap(pExaScr, pScreen, BlockHandler);
if (pScreen->WakeupHandler == ExaWakeupHandler)
unwrap(pExaScr, pScreen, WakeupHandler);
unwrap(pExaScr, pScreen, CreateGC); unwrap(pExaScr, pScreen, CreateGC);
unwrap(pExaScr, pScreen, CloseScreen); unwrap(pExaScr, pScreen, CloseScreen);
unwrap(pExaScr, pScreen, GetImage); unwrap(pExaScr, pScreen, GetImage);
@ -1223,6 +1271,11 @@ exaDriverInit (ScreenPtr pScreen,
/* /*
* Replace various fb screen functions * Replace various fb screen functions
*/ */
if ((pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS) &&
!(pExaScr->info->flags & EXA_HANDLES_PIXMAPS)) {
wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler);
wrap(pExaScr, pScreen, WakeupHandler, ExaWakeupHandler);
}
wrap(pExaScr, pScreen, CreateGC, exaCreateGC); wrap(pExaScr, pScreen, CreateGC, exaCreateGC);
wrap(pExaScr, pScreen, CloseScreen, exaCloseScreen); wrap(pExaScr, pScreen, CloseScreen, exaCloseScreen);
wrap(pExaScr, pScreen, GetImage, exaGetImage); wrap(pExaScr, pScreen, GetImage, exaGetImage);

View File

@ -66,6 +66,9 @@ struct _ExaOffscreenArea {
ExaOffscreenArea *next; ExaOffscreenArea *next;
unsigned eviction_cost; unsigned eviction_cost;
ExaOffscreenArea *prev; /* Double-linked list for defragmentation */
int align; /* required alignment */
}; };
/** /**
@ -744,6 +747,16 @@ typedef struct _ExaDriver {
*/ */
#define EXA_SUPPORTS_PREPARE_AUX (1 << 4) #define EXA_SUPPORTS_PREPARE_AUX (1 << 4)
/**
* EXA_SUPPORTS_OFFSCREEN_OVERLAPS indicates to EXA that the driver Copy hooks
* can handle the source and destination occupying overlapping offscreen memory
* areas. This allows the offscreen memory defragmentation code to defragment
* areas where the defragmented position overlaps the fragmented position.
*
* Typically this is supported by traditional 2D engines but not by 3D engines.
*/
#define EXA_SUPPORTS_OFFSCREEN_OVERLAPS (1 << 5)
/** @} */ /** @} */
/* in exa.c */ /* in exa.c */

View File

@ -172,7 +172,7 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
{ {
ExaOffscreenArea *area; ExaOffscreenArea *area;
ExaScreenPriv (pScreen); ExaScreenPriv (pScreen);
int tmp, real_size = 0; int tmp, real_size = 0, free_total = 0, largest_avail = 0;
#if DEBUG_OFFSCREEN #if DEBUG_OFFSCREEN
static int number = 0; static int number = 0;
ErrorF("================= ============ allocating a new pixmap %d\n", ++number); ErrorF("================= ============ allocating a new pixmap %d\n", ++number);
@ -213,6 +213,35 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
/* does it fit? */ /* does it fit? */
if (real_size <= area->size) if (real_size <= area->size)
break; 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;
tmp = area->base_offset % align;
if (tmp)
real_size += (align - tmp);
/* does it fit? */
if (real_size > area->size)
area = NULL;
}
}
} }
if (!area) if (!area)
@ -255,16 +284,31 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
if (!new_area) if (!new_area)
return NULL; return NULL;
new_area->base_offset = area->base_offset + real_size; 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->offset = new_area->base_offset;
new_area->align = 0;
new_area->size = area->size - real_size; new_area->size = area->size - real_size;
new_area->state = ExaOffscreenAvail; new_area->state = ExaOffscreenAvail;
new_area->save = NULL; new_area->save = NULL;
new_area->last_use = 0; new_area->last_use = 0;
new_area->eviction_cost = 0; new_area->eviction_cost = 0;
new_area->next = area->next; new_area->next = area->next;
if (area->next)
area->next->prev = new_area;
else
pExaScr->info->offScreenAreas->prev = new_area;
area->next = new_area; area->next = new_area;
new_area->prev = area;
area->size = real_size; area->size = real_size;
} } else
pExaScr->numOffscreenAvailable--;
/* /*
* Mark this area as in use * Mark this area as in use
*/ */
@ -277,6 +321,7 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
area->last_use = pExaScr->offScreenCounter++; area->last_use = pExaScr->offScreenCounter++;
area->offset = (area->base_offset + align - 1); area->offset = (area->base_offset + align - 1);
area->offset -= area->offset % align; area->offset -= area->offset % align;
area->align = align;
ExaOffscreenValidate (pScreen); ExaOffscreenValidate (pScreen);
@ -391,7 +436,7 @@ exaEnableDisableFBAccess (int index, Bool enable)
/* merge the next free area into this one */ /* merge the next free area into this one */
static void static void
ExaOffscreenMerge (ExaOffscreenArea *area) ExaOffscreenMerge (ExaScreenPrivPtr pExaScr, ExaOffscreenArea *area)
{ {
ExaOffscreenArea *next = area->next; ExaOffscreenArea *next = area->next;
@ -399,7 +444,13 @@ ExaOffscreenMerge (ExaOffscreenArea *area)
area->size += next->size; area->size += next->size;
/* frob pointer */ /* frob pointer */
area->next = next->next; area->next = next->next;
if (area->next)
area->next->prev = area;
else
pExaScr->info->offScreenAreas->prev = area;
xfree (next); xfree (next);
pExaScr->numOffscreenAvailable--;
} }
/** /**
@ -436,19 +487,19 @@ exaOffscreenFree (ScreenPtr pScreen, ExaOffscreenArea *area)
if (area == pExaScr->info->offScreenAreas) if (area == pExaScr->info->offScreenAreas)
prev = NULL; prev = NULL;
else else
for (prev = pExaScr->info->offScreenAreas; prev; prev = prev->next) prev = area->prev;
if (prev->next == area)
break; pExaScr->numOffscreenAvailable++;
/* link with next area if free */ /* link with next area if free */
if (next && next->state == ExaOffscreenAvail) if (next && next->state == ExaOffscreenAvail)
ExaOffscreenMerge (area); ExaOffscreenMerge (pExaScr, area);
/* link with prev area if free */ /* link with prev area if free */
if (prev && prev->state == ExaOffscreenAvail) if (prev && prev->state == ExaOffscreenAvail)
{ {
area = prev; area = prev;
ExaOffscreenMerge (area); ExaOffscreenMerge (pExaScr, area);
} }
ExaOffscreenValidate (pScreen); ExaOffscreenValidate (pScreen);
@ -468,6 +519,167 @@ ExaOffscreenMarkUsed (PixmapPtr pPixmap)
pExaPixmap->area->last_use = pExaScr->offScreenCounter++; pExaPixmap->area->last_use = pExaScr->offScreenCounter++;
} }
/**
* 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. * exaOffscreenInit initializes the offscreen memory manager.
* *
@ -491,15 +703,18 @@ exaOffscreenInit (ScreenPtr pScreen)
area->state = ExaOffscreenAvail; area->state = ExaOffscreenAvail;
area->base_offset = pExaScr->info->offScreenBase; area->base_offset = pExaScr->info->offScreenBase;
area->offset = area->base_offset; area->offset = area->base_offset;
area->align = 0;
area->size = pExaScr->info->memorySize - area->base_offset; area->size = pExaScr->info->memorySize - area->base_offset;
area->save = NULL; area->save = NULL;
area->next = NULL; area->next = NULL;
area->prev = area;
area->last_use = 0; area->last_use = 0;
area->eviction_cost = 0; area->eviction_cost = 0;
/* Add it to the free areas */ /* Add it to the free areas */
pExaScr->info->offScreenAreas = area; pExaScr->info->offScreenAreas = area;
pExaScr->offScreenCounter = 1; pExaScr->offScreenCounter = 1;
pExaScr->numOffscreenAvailable = 1;
ExaOffscreenValidate (pScreen); ExaOffscreenValidate (pScreen);

View File

@ -145,6 +145,8 @@ typedef struct {
typedef void (*EnableDisableFBAccessProcPtr)(int, Bool); typedef void (*EnableDisableFBAccessProcPtr)(int, Bool);
typedef struct { typedef struct {
ExaDriverPtr info; ExaDriverPtr info;
ScreenBlockHandlerProcPtr SavedBlockHandler;
ScreenWakeupHandlerProcPtr SavedWakeupHandler;
CreateGCProcPtr SavedCreateGC; CreateGCProcPtr SavedCreateGC;
CloseScreenProcPtr SavedCloseScreen; CloseScreenProcPtr SavedCloseScreen;
GetImageProcPtr SavedGetImage; GetImageProcPtr SavedGetImage;
@ -170,6 +172,9 @@ typedef struct {
unsigned disableFbCount; unsigned disableFbCount;
Bool optimize_migration; Bool optimize_migration;
unsigned offScreenCounter; unsigned offScreenCounter;
unsigned numOffscreenAvailable;
CARD32 lastDefragment;
CARD32 nextDefragment;
/* Store all accessed pixmaps, so we can check for duplicates. */ /* Store all accessed pixmaps, so we can check for duplicates. */
PixmapPtr prepare_access[6]; PixmapPtr prepare_access[6];
@ -460,6 +465,9 @@ ExaOffscreenSwapOut (ScreenPtr pScreen);
void void
ExaOffscreenSwapIn (ScreenPtr pScreen); ExaOffscreenSwapIn (ScreenPtr pScreen);
ExaOffscreenArea*
ExaOffscreenDefragment (ScreenPtr pScreen);
Bool Bool
exaOffscreenInit(ScreenPtr pScreen); exaOffscreenInit(ScreenPtr pScreen);