summaryrefslogtreecommitdiff
path: root/hw/xfree86/exa/exaoffscreen.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/xfree86/exa/exaoffscreen.c')
-rw-r--r--hw/xfree86/exa/exaoffscreen.c374
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);
+ }
+}