#ifdef HAVE_CONFIG_H
#include <kdrive-config.h>
#endif
#include "kdrive.h"
#include "kaa.h"
#define DEBUG_OFFSCREEN 0
#if DEBUG_OFFSCREEN
#define DBG_OFFSCREEN(a) ErrorF a
#else
#define DBG_OFFSCREEN(a)
#endif
#if DEBUG_OFFSCREEN
static void
KdOffscreenValidate (ScreenPtr pScreen)
{
KdScreenPriv (pScreen);
KdOffscreenArea *prev = 0, *area;
assert (pScreenPriv->screen->off_screen_areas->area.offset == 0);
for (area = pScreenPriv->off_screen_areas; area; area = area->next)
{
if (prev)
assert (prev->offset + prev->size == area->offset);
prev = area;
}
assert (prev->offset + prev->size == pScreenPriv->screen->memory_size);
}
#else
#define KdOffscreenValidate(s)
#endif
static KdOffscreenArea *
KdOffscreenKickOut (ScreenPtr pScreen, KdOffscreenArea *area)
{
if (area->save)
(*area->save) (pScreen, area);
return KdOffscreenFree (pScreen, area);
}
KdOffscreenArea *
KdOffscreenAlloc (ScreenPtr pScreen, int size, int align,
Bool locked,
KdOffscreenSaveProc save,
pointer privData)
{
KdOffscreenArea *area, *begin, *best;
KdScreenPriv (pScreen);
int tmp, real_size = 0, best_score;
KdOffscreenValidate (pScreen);
if (!align)
align = 1;
if (!size)
{
DBG_OFFSCREEN (("Alloc 0x%x -> EMPTY\n", size));
return NULL;
}
if (size > (pScreenPriv->screen->memory_size - pScreenPriv->screen->off_screen_base))
{
DBG_OFFSCREEN (("Alloc 0x%x -> TOBIG\n", size));
return NULL;
}
for (area = pScreenPriv->off_screen_areas; area; area = area->next)
{
if (area->state != KdOffscreenAvail)
continue;
real_size = size;
tmp = area->offset % align;
if (tmp)
real_size += (align - tmp);
if (real_size <= area->size)
break;
}
if (!area)
{
best = NULL;
best_score = MAXINT;
for (begin = pScreenPriv->off_screen_areas; begin != NULL;
begin = begin->next)
{
int avail, score;
KdOffscreenArea *scan;
if (begin->state == KdOffscreenLocked)
continue;
real_size = size;
tmp = begin->offset % align;
if (tmp)
real_size += (align - tmp);
avail = 0;
score = 0;
for (scan = begin; scan != NULL; scan = scan->next)
{
if (scan->state == KdOffscreenLocked) {
begin = scan->next;
break;
}
score += scan->score;
avail += scan->size;
if (avail >= real_size)
break;
}
if (avail >= real_size && score < best_score) {
best = begin;
best_score = score;
}
}
area = best;
if (!area)
{
DBG_OFFSCREEN (("Alloc 0x%x -> NOSPACE\n", size));
KdOffscreenValidate (pScreen);
return NULL;
}
real_size = size;
tmp = begin->offset % align;
if (tmp)
real_size += (align - tmp);
if (area->state != KdOffscreenAvail)
area = KdOffscreenKickOut (pScreen, area);
while (area->size < real_size)
{
assert (area->next && area->next->state == KdOffscreenRemovable);
(void) KdOffscreenKickOut (pScreen, area->next);
}
}
if (real_size < area->size)
{
KdOffscreenArea *new_area = xalloc (sizeof (KdOffscreenArea));
if (!new_area)
return NULL;
new_area->offset = area->offset + real_size;
new_area->size = area->size - real_size;
new_area->state = KdOffscreenAvail;
new_area->save = 0;
new_area->score = 0;
new_area->next = area->next;
area->next = new_area;
area->size = real_size;
}
if (locked)
area->state = KdOffscreenLocked;
else
area->state = KdOffscreenRemovable;
area->privData = privData;
area->save = save;
area->score = 0;
area->save_offset = area->offset;
{
int tmp = area->offset % align;
if (tmp)
area->offset += (align - tmp);
}
KdOffscreenValidate (pScreen);
DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x\n", size, area->offset));
return area;
}
void
KdOffscreenSwapOut (ScreenPtr pScreen)
{
KdScreenPriv (pScreen);
KdOffscreenValidate (pScreen);
for (;;)
{
KdOffscreenArea *area = pScreenPriv->off_screen_areas;
if (!area)
break;
if (area->state == KdOffscreenAvail)
{
area = area->next;
if (!area)
break;
}
assert (area->state != KdOffscreenAvail);
(void) KdOffscreenKickOut (pScreen, area);
KdOffscreenValidate (pScreen);
}
KdOffscreenValidate (pScreen);
KdOffscreenFini (pScreen);
}
void
KdOffscreenSwapIn (ScreenPtr pScreen)
{
KdOffscreenInit (pScreen);
}
static void
KdOffscreenMerge (KdOffscreenArea *area)
{
KdOffscreenArea *next = area->next;
area->size += next->size;
area->next = next->next;
xfree (next);
}
KdOffscreenArea *
KdOffscreenFree (ScreenPtr pScreen, KdOffscreenArea *area)
{
KdScreenPriv(pScreen);
KdOffscreenArea *next = area->next;
KdOffscreenArea *prev;
DBG_OFFSCREEN (("Free 0x%x -> 0x%x\n", area->size, area->offset));
KdOffscreenValidate (pScreen);
area->state = KdOffscreenAvail;
area->save = 0;
area->offset = area->save_offset;
area->score = 0;
if (area == pScreenPriv->off_screen_areas)
prev = 0;
else
for (prev = pScreenPriv->off_screen_areas; prev; prev = prev->next)
if (prev->next == area)
break;
if (next && next->state == KdOffscreenAvail)
KdOffscreenMerge (area);
if (prev && prev->state == KdOffscreenAvail)
{
area = prev;
KdOffscreenMerge (area);
}
KdOffscreenValidate (pScreen);
return area;
}
void
KdOffscreenMarkUsed (PixmapPtr pPixmap)
{
KaaPixmapPriv (pPixmap);
KdScreenPriv (pPixmap->drawable.pScreen);
static int iter = 0;
if (!pKaaPixmap->area)
return;
pKaaPixmap->area->score += 100;
if (++iter == 10) {
KdOffscreenArea *area;
for (area = pScreenPriv->off_screen_areas; area != NULL;
area = area->next)
{
if (area->state == KdOffscreenRemovable)
area->score = (area->score * 7) / 8;
}
}
}
Bool
KdOffscreenInit (ScreenPtr pScreen)
{
KdScreenPriv (pScreen);
KdOffscreenArea *area;
area = xalloc (sizeof (KdOffscreenArea));
if (!area)
return FALSE;
area->state = KdOffscreenAvail;
area->offset = pScreenPriv->screen->off_screen_base;
area->size = pScreenPriv->screen->memory_size - area->offset;
area->save = 0;
area->next = NULL;
area->score = 0;
pScreenPriv->off_screen_areas = area;
KdOffscreenValidate (pScreen);
return TRUE;
}
void
KdOffscreenFini (ScreenPtr pScreen)
{
KdScreenPriv (pScreen);
KdOffscreenArea *area;
while ((area = pScreenPriv->off_screen_areas))
{
pScreenPriv->off_screen_areas = area->next;
xfree (area);
}
}