diff options
Diffstat (limited to 'hw/xfree86/exa/exaoffscreen.c')
-rw-r--r-- | hw/xfree86/exa/exaoffscreen.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/hw/xfree86/exa/exaoffscreen.c b/hw/xfree86/exa/exaoffscreen.c new file mode 100644 index 000000000..def3fbdbb --- /dev/null +++ b/hw/xfree86/exa/exaoffscreen.c @@ -0,0 +1,374 @@ +/* + * Copyright © 2003 Anders Carlsson + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Anders Carlsson not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Anders Carlsson makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * ANDERS CARLSSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL ANDERS CARLSSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "exaPriv.h" + +#define DEBUG_OFFSCREEN 1 +#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->card.offScreenAreas->area.offset == 0); + for (area = pExaScr->info->card.offScreenAreas; area; area = area->next) + { + if (prev) + assert (prev->area.offset + prev->area.size == area->area.offset); + + prev = area; + } + assert (prev->area.offset + prev->area.size == pExaScr->info->card.memorySize); +} +#else +#define ExaOffscreenValidate(s) +#endif + +static ExaOffscreenArea * +ExaOffscreenKickOut (ScreenPtr pScreen, ExaOffscreenArea *area) +{ + if (area->save) + (*area->save) (pScreen, area); + return exaOffscreenFree (pScreen, area); +} + +ExaOffscreenArea * +exaOffscreenAlloc (ScreenPtr pScreen, int size, int align, + Bool locked, + ExaOffscreenSaveProc save, + pointer privData) +{ + ExaOffscreenArea *area, *begin, *best; + ExaScreenPriv (pScreen); + int tmp, real_size = 0, best_score; +#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; + } + + /* throw out requests that cannot fit */ + if (size > (pExaScr->info->card.memorySize - pExaScr->info->card.offScreenBase)) + { + DBG_OFFSCREEN (("Alloc 0x%x -> TOBIG\n", size)); + return NULL; + } + + /* Try to find a free space that'll fit. */ + for (area = pExaScr->info->card.offScreenAreas; area; area = area->next) + { + /* skip allocated areas */ + if (area->state != ExaOffscreenAvail) + continue; + + /* adjust size to match alignment requirement */ + real_size = size; + tmp = area->offset % align; + if (tmp) + real_size += (align - tmp); + + /* does it fit? */ + if (real_size <= area->size) + break; + } + + if (!area) + { + /* + * Kick out existing users to make space. + * + * First, locate a region which can hold the desired object. + */ + + /* prev points at the first object to boot */ + best = NULL; + best_score = MAXINT; + for (begin = pExaScr->info->card.offScreenAreas; begin != NULL; + begin = begin->next) + { + int avail, score; + ExaOffscreenArea *scan; + + if (begin->state == ExaOffscreenLocked) + continue; + + /* adjust size to match alignment requirement */ + real_size = size; + tmp = begin->offset % align; + if (tmp) + real_size += (align - tmp); + + avail = 0; + score = 0; + /* now see if we can make room here, and how "costly" it'll be. */ + for (scan = begin; scan != NULL; scan = scan->next) + { + if (scan->state == ExaOffscreenLocked) { + /* Can't make room here, start after this locked area. */ + begin = scan->next; + break; + } + /* Score should only be non-zero for ExaOffscreenRemovable */ + score += scan->score; + avail += scan->size; + if (avail >= real_size) + break; + } + /* Is it the best option we've found so far? */ + if (avail >= real_size && score < best_score) { + best = begin; + best_score = score; + } + } + area = best; + if (!area) + { + DBG_OFFSCREEN (("Alloc 0x%x -> NOSPACE\n", size)); + /* Could not allocate memory */ + ExaOffscreenValidate (pScreen); + return NULL; + } + + /* adjust size to match alignment requirement */ + real_size = size; + tmp = begin->offset % align; + if (tmp) + real_size += (align - tmp); + + /* + * Kick out first area if in use + */ + if (area->state != ExaOffscreenAvail) + area = ExaOffscreenKickOut (pScreen, area); + /* + * Now get the system to merge the other needed areas together + */ + while (area->size < real_size) + { + assert (area->next && area->next->state == ExaOffscreenRemovable); + (void) ExaOffscreenKickOut (pScreen, area->next); + } + } + + /* save extra space in new area */ + if (real_size < area->size) + { + ExaOffscreenArea *new_area = xalloc (sizeof (ExaOffscreenArea)); + if (!new_area) + return NULL; + new_area->offset = area->offset + real_size; + new_area->size = area->size - real_size; + new_area->state = ExaOffscreenAvail; + new_area->save = 0; + new_area->score = 0; + new_area->next = area->next; + area->next = new_area; + area->size = real_size; + } + /* + * Mark this area as in use + */ + if (locked) + area->state = ExaOffscreenLocked; + else + area->state = ExaOffscreenRemovable; + area->privData = privData; + area->save = save; + area->score = 0; + + area->save_offset = area->offset; + area->offset = (area->offset + align - 1) & ~(align - 1); + + ExaOffscreenValidate (pScreen); + + DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x\n", size, area->offset)); + return area; +} + +void +ExaOffscreenSwapOut (ScreenPtr pScreen) +{ + ExaScreenPriv (pScreen); + + ExaOffscreenValidate (pScreen); + /* loop until a single free area spans the space */ + for (;;) + { + ExaOffscreenArea *area = pExaScr->info->card.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); +} + +void +ExaOffscreenSwapIn (ScreenPtr pScreen) +{ + exaOffscreenInit (pScreen); +} + +/* merge the next free area into this one */ +static void +ExaOffscreenMerge (ExaOffscreenArea *area) +{ + ExaOffscreenArea *next = area->next; + + /* account for space */ + area->size += next->size; + /* frob pointer */ + 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\n", area->size, area->offset)); + ExaOffscreenValidate (pScreen); + + area->state = ExaOffscreenAvail; + area->save = 0; + area->offset = area->save_offset; + area->score = 0; + /* + * Find previous area + */ + if (area == pExaScr->info->card.offScreenAreas) + prev = 0; + else + for (prev = pExaScr->info->card.offScreenAreas; prev; prev = prev->next) + if (prev->next == area) + break; + + /* link with next area if free */ + if (next && next->state == ExaOffscreenAvail) + ExaOffscreenMerge (area); + + /* link with prev area if free */ + 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); + static int iter = 0; + + if (!pExaPixmap->area) + return; + + /* The numbers here are arbitrary. We may want to tune these. */ + pExaPixmap->area->score += 100; + if (++iter == 10) { + ExaOffscreenArea *area; + for (area = pExaScr->info->card.offScreenAreas; area != NULL; + area = area->next) + { + if (area->state == ExaOffscreenRemovable) + area->score = (area->score * 7) / 8; + } + } +} + +Bool +exaOffscreenInit (ScreenPtr pScreen) +{ + ExaScreenPriv (pScreen); + ExaOffscreenArea *area; + + /* Allocate a big free area */ + area = xalloc (sizeof (ExaOffscreenArea)); + + if (!area) + return FALSE; + + + area->state = ExaOffscreenAvail; + area->offset = pExaScr->info->card.offScreenBase; + area->size = pExaScr->info->card.memorySize - area->offset; + area->save = 0; + area->next = NULL; + area->score = 0; + + ErrorF("============ initial memory block of %d\n", area->size); + + /* Add it to the free areas */ + pExaScr->info->card.offScreenAreas = area; + + ExaOffscreenValidate (pScreen); + + return TRUE; +} + +void +ExaOffscreenFini (ScreenPtr pScreen) +{ + ExaScreenPriv (pScreen); + ExaOffscreenArea *area; + + /* just free all of the area records */ + while ((area = pExaScr->info->card.offScreenAreas)) + { + pExaScr->info->card.offScreenAreas = area->next; + xfree (area); + } +} |