#include "exa_priv.h"
#include <limits.h>
#include <assert.h>
#include <stdlib.h>
#if DEBUG_OFFSCREEN
#define DBG_OFFSCREEN(a) ErrorF a
#else
#define DBG_OFFSCREEN(a)
#endif
#if DEBUG_OFFSCREEN
static void
ExaOffscreenValidate (ScreenPtr pScreen)
{
ExaScreenPriv (pScreen);
ExaOffscreenArea *prev = 0, *area;
assert (pExaScr->info->offScreenAreas->base_offset ==
pExaScr->info->offScreenBase);
for (area = pExaScr->info->offScreenAreas; area; area = area->next)
{
assert (area->offset >= area->base_offset &&
area->offset < (area->base_offset + area->size));
if (prev)
assert (prev->base_offset + prev->size == area->base_offset);
prev = area;
}
assert (prev->base_offset + prev->size == pExaScr->info->memorySize);
}
#else
#define ExaOffscreenValidate(s)
#endif
static ExaOffscreenArea *
ExaOffscreenKickOut (ScreenPtr pScreen, ExaOffscreenArea *area)
{
if (area->save)
(*area->save) (pScreen, area);
return exaOffscreenFree (pScreen, area);
}
static void
exaUpdateEvictionCost(ExaOffscreenArea *area, unsigned offScreenCounter)
{
unsigned age;
if (area->state == ExaOffscreenAvail)
return;
age = offScreenCounter - area->last_use;
if (age > (UINT_MAX / 2)) {
age = UINT_MAX / 2;
area->last_use = offScreenCounter - age;
}
area->eviction_cost = area->size / age;
}
static ExaOffscreenArea *
exaFindAreaToEvict(ExaScreenPrivPtr pExaScr, int size, int align)
{
ExaOffscreenArea *begin, *end, *best;
unsigned cost, best_cost;
int avail, real_size, tmp;
best_cost = UINT_MAX;
begin = end = pExaScr->info->offScreenAreas;
avail = 0;
cost = 0;
best = 0;
while (end != NULL)
{
restart:
while (begin != NULL && begin->state == ExaOffscreenLocked)
begin = end = begin->next;
if (begin == NULL)
break;
real_size = size;
tmp = begin->base_offset % align;
if (tmp)
real_size += (align - tmp);
while (avail < real_size && end != NULL)
{
if (end->state == ExaOffscreenLocked) {
avail = 0;
cost = 0;
begin = end;
goto restart;
}
avail += end->size;
exaUpdateEvictionCost(end, pExaScr->offScreenCounter);
cost += end->eviction_cost;
end = end->next;
}
if (avail >= real_size && cost < best_cost) {
best = begin;
best_cost = cost;
}
avail -= begin->size;
cost -= begin->eviction_cost;
begin = begin->next;
}
return best;
}
ExaOffscreenArea *
exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
Bool locked,
ExaOffscreenSaveProc save,
pointer privData)
{
ExaOffscreenArea *area;
ExaScreenPriv (pScreen);
int tmp, real_size = 0;
#if DEBUG_OFFSCREEN
static int number = 0;
ErrorF("================= ============ allocating a new pixmap %d\n", ++number);
#endif
ExaOffscreenValidate (pScreen);
if (!align)
align = 1;
if (!size)
{
DBG_OFFSCREEN (("Alloc 0x%x -> EMPTY\n", size));
return NULL;
}
if (size > (pExaScr->info->memorySize - pExaScr->info->offScreenBase))
{
DBG_OFFSCREEN (("Alloc 0x%x vs (0x%lx) -> TOBIG\n", size,
pExaScr->info->memorySize -
pExaScr->info->offScreenBase));
return NULL;
}
for (area = pExaScr->info->offScreenAreas; area; area = area->next)
{
if (area->state != ExaOffscreenAvail)
continue;
real_size = size;
tmp = area->base_offset % align;
if (tmp)
real_size += (align - tmp);
if (real_size <= area->size)
break;
}
if (!area)
{
area = exaFindAreaToEvict(pExaScr, size, align);
if (!area)
{
DBG_OFFSCREEN (("Alloc 0x%x -> NOSPACE\n", size));
ExaOffscreenValidate (pScreen);
return NULL;
}
real_size = size;
tmp = area->base_offset % align;
if (tmp)
real_size += (align - tmp);
if (area->state != ExaOffscreenAvail)
area = ExaOffscreenKickOut (pScreen, area);
while (area->size < real_size)
{
assert (area->next && area->next->state == ExaOffscreenRemovable);
(void) ExaOffscreenKickOut (pScreen, area->next);
}
}
if (real_size < area->size)
{
ExaOffscreenArea *new_area = xalloc (sizeof (ExaOffscreenArea));
if (!new_area)
return NULL;
new_area->base_offset = area->base_offset + real_size;
new_area->offset = new_area->base_offset;
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;
area->size = real_size;
}
if (locked)
area->state = ExaOffscreenLocked;
else
area->state = ExaOffscreenRemovable;
area->privData = privData;
area->save = save;
area->last_use = pExaScr->offScreenCounter++;
area->offset = (area->base_offset + align - 1);
area->offset -= area->offset % align;
ExaOffscreenValidate (pScreen);
DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x (0x%x)\n", size,
area->base_offset, area->offset));
return area;
}
void
ExaOffscreenSwapOut (ScreenPtr pScreen)
{
ExaScreenPriv (pScreen);
ExaOffscreenValidate (pScreen);
for (;;)
{
ExaOffscreenArea *area = pExaScr->info->offScreenAreas;
if (!area)
break;
if (area->state == ExaOffscreenAvail)
{
area = area->next;
if (!area)
break;
}
assert (area->state != ExaOffscreenAvail);
(void) ExaOffscreenKickOut (pScreen, area);
ExaOffscreenValidate (pScreen);
}
ExaOffscreenValidate (pScreen);
ExaOffscreenFini (pScreen);
}
static void
ExaOffscreenEjectPixmaps (ScreenPtr pScreen)
{
ExaScreenPriv (pScreen);
ExaOffscreenValidate (pScreen);
for (;;)
{
ExaOffscreenArea *area;
for (area = pExaScr->info->offScreenAreas; area != NULL;
area = area->next)
{
if (area->state == ExaOffscreenRemovable &&
area->save == exaPixmapSave)
{
(void) ExaOffscreenKickOut (pScreen, area);
ExaOffscreenValidate (pScreen);
break;
}
}
if (area == NULL)
break;
}
ExaOffscreenValidate (pScreen);
}
void
ExaOffscreenSwapIn (ScreenPtr pScreen)
{
exaOffscreenInit (pScreen);
}
void
exaEnableDisableFBAccess (int index, Bool enable)
{
ScreenPtr pScreen = screenInfo.screens[index];
ExaScreenPriv (pScreen);
if (!enable && pExaScr->disableFbCount++ == 0) {
if (pExaScr->info->exa_minor < 1)
ExaOffscreenSwapOut (pScreen);
else
ExaOffscreenEjectPixmaps (pScreen);
pExaScr->swappedOut = TRUE;
}
if (enable && --pExaScr->disableFbCount == 0) {
if (pExaScr->info->exa_minor < 1)
ExaOffscreenSwapIn (pScreen);
pExaScr->swappedOut = FALSE;
}
}
static void
ExaOffscreenMerge (ExaOffscreenArea *area)
{
ExaOffscreenArea *next = area->next;
area->size += next->size;
area->next = next->next;
xfree (next);
}
ExaOffscreenArea *
exaOffscreenFree (ScreenPtr pScreen, ExaOffscreenArea *area)
{
ExaScreenPriv(pScreen);
ExaOffscreenArea *next = area->next;
ExaOffscreenArea *prev;
DBG_OFFSCREEN (("Free 0x%x -> 0x%x (0x%x)\n", area->size,
area->base_offset, area->offset));
ExaOffscreenValidate (pScreen);
area->state = ExaOffscreenAvail;
area->save = NULL;
area->last_use = 0;
area->eviction_cost = 0;
if (area == pExaScr->info->offScreenAreas)
prev = NULL;
else
for (prev = pExaScr->info->offScreenAreas; prev; prev = prev->next)
if (prev->next == area)
break;
if (next && next->state == ExaOffscreenAvail)
ExaOffscreenMerge (area);
if (prev && prev->state == ExaOffscreenAvail)
{
area = prev;
ExaOffscreenMerge (area);
}
ExaOffscreenValidate (pScreen);
DBG_OFFSCREEN(("\tdone freeing\n"));
return area;
}
void
ExaOffscreenMarkUsed (PixmapPtr pPixmap)
{
ExaPixmapPriv (pPixmap);
ExaScreenPriv (pPixmap->drawable.pScreen);
if (!pExaPixmap || !pExaPixmap->area)
return;
pExaPixmap->area->last_use = pExaScr->offScreenCounter++;
}
Bool
exaOffscreenInit (ScreenPtr pScreen)
{
ExaScreenPriv (pScreen);
ExaOffscreenArea *area;
area = xalloc (sizeof (ExaOffscreenArea));
if (!area)
return FALSE;
area->state = ExaOffscreenAvail;
area->base_offset = pExaScr->info->offScreenBase;
area->offset = area->base_offset;
area->size = pExaScr->info->memorySize - area->base_offset;
area->save = NULL;
area->next = NULL;
area->last_use = 0;
area->eviction_cost = 0;
pExaScr->info->offScreenAreas = area;
pExaScr->offScreenCounter = 1;
ExaOffscreenValidate (pScreen);
return TRUE;
}
void
ExaOffscreenFini (ScreenPtr pScreen)
{
ExaScreenPriv (pScreen);
ExaOffscreenArea *area;
while ((area = pExaScr->info->offScreenAreas))
{
pExaScr->info->offScreenAreas = area->next;
xfree (area);
}
}