diff options
author | marha <marha@users.sourceforge.net> | 2012-03-23 10:05:55 +0100 |
---|---|---|
committer | marha <marha@users.sourceforge.net> | 2012-03-23 10:05:55 +0100 |
commit | 0f834b91a4768673833ab4917e87d86c237bb1a6 (patch) | |
tree | 363489504ed4b2d360259b8de4c9e392918e5d02 /xorg-server/exa/exa_offscreen.c | |
parent | fc72edebf875378459368c5383d9023730cbca54 (diff) | |
download | vcxsrv-0f834b91a4768673833ab4917e87d86c237bb1a6.tar.gz vcxsrv-0f834b91a4768673833ab4917e87d86c237bb1a6.tar.bz2 vcxsrv-0f834b91a4768673833ab4917e87d86c237bb1a6.zip |
libX11 xserver fontconfig mesa pixman xkbcomp xkeyboard-config git update
23 Mar 2012
Diffstat (limited to 'xorg-server/exa/exa_offscreen.c')
-rw-r--r-- | xorg-server/exa/exa_offscreen.c | 1374 |
1 files changed, 678 insertions, 696 deletions
diff --git a/xorg-server/exa/exa_offscreen.c b/xorg-server/exa/exa_offscreen.c index 506da616d..5e1d1398e 100644 --- a/xorg-server/exa/exa_offscreen.c +++ b/xorg-server/exa/exa_offscreen.c @@ -1,696 +1,678 @@ -/*
- * 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.
- */
-
-/** @file
- * This allocator allocates blocks of memory by maintaining a list of areas.
- * When allocating, the contiguous block of areas with the minimum eviction
- * cost is found and evicted in order to make room for the new allocation.
- */
-
-#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;
-
- /* This is unlikely to happen, but could result in a division by zero... */
- 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;
-
- 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;
-
- /* adjust size needed to account for alignment loss for this area */
- real_size = size + (begin->base_offset + begin->size - size) % align;
-
- while (avail < real_size && end != NULL)
- {
- if (end->state == ExaOffscreenLocked) {
- /* Can't more room here, restart after this locked area */
- avail = 0;
- cost = 0;
- begin = end;
- goto restart;
- }
- avail += end->size;
- exaUpdateEvictionCost(end, pExaScr->offScreenCounter);
- cost += end->eviction_cost;
- end = end->next;
- }
-
- /* Check the cost, update best */
- if (avail >= real_size && cost < best_cost) {
- best = begin;
- best_cost = cost;
- }
-
- avail -= begin->size;
- cost -= begin->eviction_cost;
- begin = begin->next;
- }
-
- return best;
-}
-
-/**
- * exaOffscreenAlloc allocates offscreen memory
- *
- * @param pScreen current screen
- * @param size size in bytes of the allocation
- * @param align byte alignment requirement for the offset of the allocated area
- * @param locked whether the allocated area is locked and can't be kicked out
- * @param save callback for when the area is evicted from memory
- * @param privdata private data for the save callback.
- *
- * Allocates offscreen memory from the device associated with pScreen. size
- * and align deteremine where and how large the allocated area is, and locked
- * will mark whether it should be held in card memory. privdata may be any
- * pointer for the save callback when the area is removed.
- *
- * Note that locked areas do get evicted on VT switch unless the driver
- * requested version 2.1 or newer behavior. In that case, the save callback is
- * still called.
- */
-ExaOffscreenArea *
-exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
- Bool locked,
- ExaOffscreenSaveProc save,
- pointer privData)
-{
- ExaOffscreenArea *area;
- ExaScreenPriv (pScreen);
- int real_size = 0, largest_avail = 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;
- }
-
- /* throw out requests that cannot fit */
- 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;
- }
-
- /* Try to find a free space that'll fit. */
- for (area = pExaScr->info->offScreenAreas; area; area = area->next)
- {
- /* skip allocated areas */
- if (area->state != ExaOffscreenAvail)
- continue;
-
- /* adjust size to match alignment requirement */
- real_size = size + (area->base_offset + area->size - size) % align;
-
- /* does it fit? */
- if (real_size <= area->size)
- break;
-
- if (area->size > largest_avail)
- largest_avail = area->size;
- }
-
- if (!area)
- {
- area = exaFindAreaToEvict(pExaScr, size, align);
-
- if (!area)
- {
- DBG_OFFSCREEN (("Alloc 0x%x -> NOSPACE\n", size));
- /* Could not allocate memory */
- ExaOffscreenValidate (pScreen);
- return NULL;
- }
-
- /* adjust size needed to account for alignment loss for this area */
- real_size = size + (area->base_offset + area->size - size) % align;
-
- /*
- * 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 = malloc(sizeof (ExaOffscreenArea));
- if (!new_area)
- return NULL;
- new_area->base_offset = area->base_offset;
-
- new_area->offset = new_area->base_offset;
- new_area->align = 0;
- 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;
- new_area->prev = area->prev;
- if (area->prev->next)
- area->prev->next = new_area;
- else
- pExaScr->info->offScreenAreas = new_area;
- area->prev = new_area;
- area->base_offset = new_area->base_offset + new_area->size;
- area->size = real_size;
- } else
- pExaScr->numOffscreenAvailable--;
-
- /*
- * Mark this area as in use
- */
- 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;
- area->align = align;
-
- ExaOffscreenValidate (pScreen);
-
- DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x (0x%x)\n", size,
- area->base_offset, area->offset));
- return area;
-}
-
-/**
- * Ejects all offscreen areas, and uninitializes the offscreen memory manager.
- */
-void
-ExaOffscreenSwapOut (ScreenPtr pScreen)
-{
- ExaScreenPriv (pScreen);
-
- ExaOffscreenValidate (pScreen);
- /* loop until a single free area spans the space */
- 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);
-}
-
-/** Ejects all pixmaps managed by EXA. */
-static void
-ExaOffscreenEjectPixmaps (ScreenPtr pScreen)
-{
- ExaScreenPriv (pScreen);
-
- ExaOffscreenValidate (pScreen);
- /* loop until a single free area spans the space */
- 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);
-}
-
-/**
- * Prepares EXA for disabling of FB access, or restoring it.
- *
- * In version 2.1, the disabling results in pixmaps being ejected, while other
- * allocations remain. With this plus the prevention of migration while
- * swappedOut is set, EXA by itself should not cause any access of the
- * framebuffer to occur while swapped out. Any remaining issues are the
- * responsibility of the driver.
- *
- * Prior to version 2.1, all allocations, including locked ones, are ejected
- * when access is disabled, and the allocator is torn down while swappedOut
- * is set. This is more drastic, and caused implementation difficulties for
- * many drivers that could otherwise handle the lack of FB access while
- * swapped out.
- */
-void
-exaEnableDisableFBAccess (int index, Bool enable)
-{
- ScreenPtr pScreen = screenInfo.screens[index];
- ExaScreenPriv (pScreen);
-
- if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS)
- return;
-
- 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;
- }
-}
-
-/* merge the next free area into this one */
-static void
-ExaOffscreenMerge (ExaScreenPrivPtr pExaScr, ExaOffscreenArea *area)
-{
- ExaOffscreenArea *next = area->next;
-
- /* account for space */
- area->size += next->size;
- /* frob pointer */
- area->next = next->next;
- if (area->next)
- area->next->prev = area;
- else
- pExaScr->info->offScreenAreas->prev = area;
- free(next);
-
- pExaScr->numOffscreenAvailable--;
-}
-
-/**
- * exaOffscreenFree frees an allocation.
- *
- * @param pScreen current screen
- * @param area offscreen area to free
- *
- * exaOffscreenFree frees an allocation created by exaOffscreenAlloc. Note that
- * the save callback of the area is not called, and it is up to the driver to
- * do any cleanup necessary as a result.
- *
- * @return pointer to the newly freed area. This behavior should not be relied
- * on.
- */
-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;
- /*
- * Find previous area
- */
- if (area == pExaScr->info->offScreenAreas)
- prev = NULL;
- else
- prev = area->prev;
-
- pExaScr->numOffscreenAvailable++;
-
- /* link with next area if free */
- if (next && next->state == ExaOffscreenAvail)
- ExaOffscreenMerge (pExaScr, area);
-
- /* link with prev area if free */
- if (prev && prev->state == ExaOffscreenAvail)
- {
- area = prev;
- ExaOffscreenMerge (pExaScr, 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++;
-}
-
-/**
- * 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->use_gpu_copy = TRUE;
-
- for (area = pExaScr->info->offScreenAreas->prev;
- area != pExaScr->info->offScreenAreas;
- )
- {
- ExaOffscreenArea *prev = area->prev;
- PixmapPtr pSrcPix;
- ExaPixmapPrivPtr pExaSrcPix;
- Bool save_use_gpu_copy;
- 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_use_gpu_copy = pExaSrcPix->use_gpu_copy;
- save_pitch = pSrcPix->devKind;
-
- pExaSrcPix->use_gpu_copy = 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->use_gpu_copy = save_use_gpu_copy;
- 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->use_gpu_copy = save_use_gpu_copy;
- 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.
- *
- * @param pScreen current screen
- *
- * exaOffscreenInit is called by exaDriverInit to set up the memory manager for
- * the screen, if any offscreen memory is available.
- */
-Bool
-exaOffscreenInit (ScreenPtr pScreen)
-{
- ExaScreenPriv (pScreen);
- ExaOffscreenArea *area;
-
- /* Allocate a big free area */
- area = malloc(sizeof (ExaOffscreenArea));
-
- if (!area)
- return FALSE;
-
- area->state = ExaOffscreenAvail;
- area->base_offset = pExaScr->info->offScreenBase;
- area->offset = area->base_offset;
- area->align = 0;
- area->size = pExaScr->info->memorySize - area->base_offset;
- area->save = NULL;
- area->next = NULL;
- area->prev = area;
- area->last_use = 0;
- area->eviction_cost = 0;
-
- /* Add it to the free areas */
- pExaScr->info->offScreenAreas = area;
- pExaScr->offScreenCounter = 1;
- pExaScr->numOffscreenAvailable = 1;
-
- ExaOffscreenValidate (pScreen);
-
- return TRUE;
-}
-
-void
-ExaOffscreenFini (ScreenPtr pScreen)
-{
- ExaScreenPriv (pScreen);
- ExaOffscreenArea *area;
-
- /* just free all of the area records */
- while ((area = pExaScr->info->offScreenAreas))
- {
- pExaScr->info->offScreenAreas = area->next;
- free(area);
- }
-}
+/* + * 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. + */ + +/** @file + * This allocator allocates blocks of memory by maintaining a list of areas. + * When allocating, the contiguous block of areas with the minimum eviction + * cost is found and evicted in order to make room for the new allocation. + */ + +#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; + + /* This is unlikely to happen, but could result in a division by zero... */ + 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; + + 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; + + /* adjust size needed to account for alignment loss for this area */ + real_size = size + (begin->base_offset + begin->size - size) % align; + + while (avail < real_size && end != NULL) { + if (end->state == ExaOffscreenLocked) { + /* Can't more room here, restart after this locked area */ + avail = 0; + cost = 0; + begin = end; + goto restart; + } + avail += end->size; + exaUpdateEvictionCost(end, pExaScr->offScreenCounter); + cost += end->eviction_cost; + end = end->next; + } + + /* Check the cost, update best */ + if (avail >= real_size && cost < best_cost) { + best = begin; + best_cost = cost; + } + + avail -= begin->size; + cost -= begin->eviction_cost; + begin = begin->next; + } + + return best; +} + +/** + * exaOffscreenAlloc allocates offscreen memory + * + * @param pScreen current screen + * @param size size in bytes of the allocation + * @param align byte alignment requirement for the offset of the allocated area + * @param locked whether the allocated area is locked and can't be kicked out + * @param save callback for when the area is evicted from memory + * @param privdata private data for the save callback. + * + * Allocates offscreen memory from the device associated with pScreen. size + * and align deteremine where and how large the allocated area is, and locked + * will mark whether it should be held in card memory. privdata may be any + * pointer for the save callback when the area is removed. + * + * Note that locked areas do get evicted on VT switch unless the driver + * requested version 2.1 or newer behavior. In that case, the save callback is + * still called. + */ +ExaOffscreenArea * +exaOffscreenAlloc(ScreenPtr pScreen, int size, int align, + Bool locked, ExaOffscreenSaveProc save, pointer privData) +{ + ExaOffscreenArea *area; + + ExaScreenPriv(pScreen); + int real_size = 0, largest_avail = 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; + } + + /* throw out requests that cannot fit */ + 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; + } + + /* Try to find a free space that'll fit. */ + for (area = pExaScr->info->offScreenAreas; area; area = area->next) { + /* skip allocated areas */ + if (area->state != ExaOffscreenAvail) + continue; + + /* adjust size to match alignment requirement */ + real_size = size + (area->base_offset + area->size - size) % align; + + /* does it fit? */ + if (real_size <= area->size) + break; + + if (area->size > largest_avail) + largest_avail = area->size; + } + + if (!area) { + area = exaFindAreaToEvict(pExaScr, size, align); + + if (!area) { + DBG_OFFSCREEN(("Alloc 0x%x -> NOSPACE\n", size)); + /* Could not allocate memory */ + ExaOffscreenValidate(pScreen); + return NULL; + } + + /* adjust size needed to account for alignment loss for this area */ + real_size = size + (area->base_offset + area->size - size) % align; + + /* + * 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 = malloc(sizeof(ExaOffscreenArea)); + + if (!new_area) + return NULL; + new_area->base_offset = area->base_offset; + + new_area->offset = new_area->base_offset; + new_area->align = 0; + 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; + new_area->prev = area->prev; + if (area->prev->next) + area->prev->next = new_area; + else + pExaScr->info->offScreenAreas = new_area; + area->prev = new_area; + area->base_offset = new_area->base_offset + new_area->size; + area->size = real_size; + } + else + pExaScr->numOffscreenAvailable--; + + /* + * Mark this area as in use + */ + 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; + area->align = align; + + ExaOffscreenValidate(pScreen); + + DBG_OFFSCREEN(("Alloc 0x%x -> 0x%x (0x%x)\n", size, + area->base_offset, area->offset)); + return area; +} + +/** + * Ejects all offscreen areas, and uninitializes the offscreen memory manager. + */ +void +ExaOffscreenSwapOut(ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + + ExaOffscreenValidate(pScreen); + /* loop until a single free area spans the space */ + 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); +} + +/** Ejects all pixmaps managed by EXA. */ +static void +ExaOffscreenEjectPixmaps(ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + + ExaOffscreenValidate(pScreen); + /* loop until a single free area spans the space */ + 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); +} + +/** + * Prepares EXA for disabling of FB access, or restoring it. + * + * In version 2.1, the disabling results in pixmaps being ejected, while other + * allocations remain. With this plus the prevention of migration while + * swappedOut is set, EXA by itself should not cause any access of the + * framebuffer to occur while swapped out. Any remaining issues are the + * responsibility of the driver. + * + * Prior to version 2.1, all allocations, including locked ones, are ejected + * when access is disabled, and the allocator is torn down while swappedOut + * is set. This is more drastic, and caused implementation difficulties for + * many drivers that could otherwise handle the lack of FB access while + * swapped out. + */ +void +exaEnableDisableFBAccess(int index, Bool enable) +{ + ScreenPtr pScreen = screenInfo.screens[index]; + + ExaScreenPriv(pScreen); + + if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS) + return; + + 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; + } +} + +/* merge the next free area into this one */ +static void +ExaOffscreenMerge(ExaScreenPrivPtr pExaScr, ExaOffscreenArea * area) +{ + ExaOffscreenArea *next = area->next; + + /* account for space */ + area->size += next->size; + /* frob pointer */ + area->next = next->next; + if (area->next) + area->next->prev = area; + else + pExaScr->info->offScreenAreas->prev = area; + free(next); + + pExaScr->numOffscreenAvailable--; +} + +/** + * exaOffscreenFree frees an allocation. + * + * @param pScreen current screen + * @param area offscreen area to free + * + * exaOffscreenFree frees an allocation created by exaOffscreenAlloc. Note that + * the save callback of the area is not called, and it is up to the driver to + * do any cleanup necessary as a result. + * + * @return pointer to the newly freed area. This behavior should not be relied + * on. + */ +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; + /* + * Find previous area + */ + if (area == pExaScr->info->offScreenAreas) + prev = NULL; + else + prev = area->prev; + + pExaScr->numOffscreenAvailable++; + + /* link with next area if free */ + if (next && next->state == ExaOffscreenAvail) + ExaOffscreenMerge(pExaScr, area); + + /* link with prev area if free */ + if (prev && prev->state == ExaOffscreenAvail) { + area = prev; + ExaOffscreenMerge(pExaScr, 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++; +} + +/** + * 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->use_gpu_copy = TRUE; + + for (area = pExaScr->info->offScreenAreas->prev; + area != pExaScr->info->offScreenAreas;) { + ExaOffscreenArea *prev = area->prev; + PixmapPtr pSrcPix; + ExaPixmapPrivPtr pExaSrcPix; + Bool save_use_gpu_copy; + 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_use_gpu_copy = pExaSrcPix->use_gpu_copy; + save_pitch = pSrcPix->devKind; + + pExaSrcPix->use_gpu_copy = 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->use_gpu_copy = save_use_gpu_copy; + 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->use_gpu_copy = save_use_gpu_copy; + 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. + * + * @param pScreen current screen + * + * exaOffscreenInit is called by exaDriverInit to set up the memory manager for + * the screen, if any offscreen memory is available. + */ +Bool +exaOffscreenInit(ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + ExaOffscreenArea *area; + + /* Allocate a big free area */ + area = malloc(sizeof(ExaOffscreenArea)); + + if (!area) + return FALSE; + + area->state = ExaOffscreenAvail; + area->base_offset = pExaScr->info->offScreenBase; + area->offset = area->base_offset; + area->align = 0; + area->size = pExaScr->info->memorySize - area->base_offset; + area->save = NULL; + area->next = NULL; + area->prev = area; + area->last_use = 0; + area->eviction_cost = 0; + + /* Add it to the free areas */ + pExaScr->info->offScreenAreas = area; + pExaScr->offScreenCounter = 1; + pExaScr->numOffscreenAvailable = 1; + + ExaOffscreenValidate(pScreen); + + return TRUE; +} + +void +ExaOffscreenFini(ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + ExaOffscreenArea *area; + + /* just free all of the area records */ + while ((area = pExaScr->info->offScreenAreas)) { + pExaScr->info->offScreenAreas = area->next; + free(area); + } +} |