2006-11-26 11:13:41 -07:00
|
|
|
/*
|
|
|
|
* 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
|
2008-11-02 08:26:08 -07:00
|
|
|
* 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.
|
2006-11-26 11:13:41 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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)
|
2007-11-24 10:55:21 -07:00
|
|
|
assert (prev->base_offset + prev->size == area->base_offset);
|
2006-11-26 11:13:41 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-11-02 08:26:08 -07:00
|
|
|
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;
|
2010-07-27 13:02:24 -06:00
|
|
|
int avail, real_size;
|
2008-11-02 08:26:08 -07:00
|
|
|
|
|
|
|
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 */
|
2010-07-27 13:02:24 -06:00
|
|
|
real_size = size + (begin->base_offset + begin->size - size) % align;
|
2008-11-02 08:26:08 -07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*
|
2007-11-24 10:55:21 -07:00
|
|
|
* 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.
|
2006-11-26 11:13:41 -07:00
|
|
|
*
|
2007-11-24 10:55:21 -07:00
|
|
|
* 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.
|
2006-11-26 11:13:41 -07:00
|
|
|
*/
|
|
|
|
ExaOffscreenArea *
|
|
|
|
exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
|
|
|
|
Bool locked,
|
|
|
|
ExaOffscreenSaveProc save,
|
|
|
|
pointer privData)
|
|
|
|
{
|
2008-11-02 08:26:08 -07:00
|
|
|
ExaOffscreenArea *area;
|
2006-11-26 11:13:41 -07:00
|
|
|
ExaScreenPriv (pScreen);
|
2010-07-27 13:02:24 -06:00
|
|
|
int real_size = 0, largest_avail = 0;
|
2006-11-26 11:13:41 -07:00
|
|
|
#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 */
|
2010-07-27 13:02:24 -06:00
|
|
|
real_size = size + (area->base_offset + area->size - size) % align;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
/* does it fit? */
|
|
|
|
if (real_size <= area->size)
|
|
|
|
break;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
if (area->size > largest_avail)
|
|
|
|
largest_avail = area->size;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!area)
|
|
|
|
{
|
2008-11-02 08:26:08 -07:00
|
|
|
area = exaFindAreaToEvict(pExaScr, size, align);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
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 */
|
2010-07-27 13:02:24 -06:00
|
|
|
real_size = size + (area->base_offset + area->size - size) % align;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2010-12-05 08:36:02 -07:00
|
|
|
ExaOffscreenArea *new_area = malloc(sizeof (ExaOffscreenArea));
|
2006-11-26 11:13:41 -07:00
|
|
|
if (!new_area)
|
|
|
|
return NULL;
|
2010-07-27 13:02:24 -06:00
|
|
|
new_area->base_offset = area->base_offset;
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
new_area->offset = new_area->base_offset;
|
2010-07-27 13:02:24 -06:00
|
|
|
new_area->align = 0;
|
2006-11-26 11:13:41 -07:00
|
|
|
new_area->size = area->size - real_size;
|
|
|
|
new_area->state = ExaOffscreenAvail;
|
|
|
|
new_area->save = NULL;
|
2008-11-02 08:26:08 -07:00
|
|
|
new_area->last_use = 0;
|
|
|
|
new_area->eviction_cost = 0;
|
2010-07-27 13:02:24 -06:00
|
|
|
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;
|
2006-11-26 11:13:41 -07:00
|
|
|
area->size = real_size;
|
2010-07-27 13:02:24 -06:00
|
|
|
} else
|
|
|
|
pExaScr->numOffscreenAvailable--;
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
/*
|
|
|
|
* Mark this area as in use
|
|
|
|
*/
|
|
|
|
if (locked)
|
|
|
|
area->state = ExaOffscreenLocked;
|
|
|
|
else
|
|
|
|
area->state = ExaOffscreenRemovable;
|
|
|
|
area->privData = privData;
|
|
|
|
area->save = save;
|
2008-11-02 08:26:08 -07:00
|
|
|
area->last_use = pExaScr->offScreenCounter++;
|
2006-11-26 11:13:41 -07:00
|
|
|
area->offset = (area->base_offset + align - 1);
|
|
|
|
area->offset -= area->offset % align;
|
2010-07-27 13:02:24 -06:00
|
|
|
area->align = align;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
ExaOffscreenValidate (pScreen);
|
|
|
|
|
|
|
|
DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x (0x%x)\n", size,
|
|
|
|
area->base_offset, area->offset));
|
|
|
|
return area;
|
|
|
|
}
|
|
|
|
|
2007-11-24 10:55:21 -07:00
|
|
|
/**
|
|
|
|
* Ejects all offscreen areas, and uninitializes the offscreen memory manager.
|
|
|
|
*/
|
2006-11-26 11:13:41 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2007-11-24 10:55:21 -07:00
|
|
|
/** 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);
|
|
|
|
}
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
void
|
|
|
|
ExaOffscreenSwapIn (ScreenPtr pScreen)
|
|
|
|
{
|
|
|
|
exaOffscreenInit (pScreen);
|
|
|
|
}
|
|
|
|
|
2007-11-24 10:55:21 -07:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2006-11-26 11:13:41 -07:00
|
|
|
void
|
|
|
|
exaEnableDisableFBAccess (int index, Bool enable)
|
|
|
|
{
|
|
|
|
ScreenPtr pScreen = screenInfo.screens[index];
|
|
|
|
ExaScreenPriv (pScreen);
|
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS)
|
|
|
|
return;
|
|
|
|
|
2007-11-24 10:55:21 -07:00
|
|
|
if (!enable && pExaScr->disableFbCount++ == 0) {
|
|
|
|
if (pExaScr->info->exa_minor < 1)
|
|
|
|
ExaOffscreenSwapOut (pScreen);
|
|
|
|
else
|
|
|
|
ExaOffscreenEjectPixmaps (pScreen);
|
2006-11-26 11:13:41 -07:00
|
|
|
pExaScr->swappedOut = TRUE;
|
2007-11-24 10:55:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (enable && --pExaScr->disableFbCount == 0) {
|
|
|
|
if (pExaScr->info->exa_minor < 1)
|
|
|
|
ExaOffscreenSwapIn (pScreen);
|
2006-11-26 11:13:41 -07:00
|
|
|
pExaScr->swappedOut = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* merge the next free area into this one */
|
|
|
|
static void
|
2010-07-27 13:02:24 -06:00
|
|
|
ExaOffscreenMerge (ExaScreenPrivPtr pExaScr, ExaOffscreenArea *area)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
ExaOffscreenArea *next = area->next;
|
|
|
|
|
|
|
|
/* account for space */
|
|
|
|
area->size += next->size;
|
|
|
|
/* frob pointer */
|
|
|
|
area->next = next->next;
|
2010-07-27 13:02:24 -06:00
|
|
|
if (area->next)
|
|
|
|
area->next->prev = area;
|
|
|
|
else
|
|
|
|
pExaScr->info->offScreenAreas->prev = area;
|
2010-12-05 08:36:02 -07:00
|
|
|
free(next);
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
pExaScr->numOffscreenAvailable--;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2008-11-02 08:26:08 -07:00
|
|
|
area->last_use = 0;
|
|
|
|
area->eviction_cost = 0;
|
2006-11-26 11:13:41 -07:00
|
|
|
/*
|
|
|
|
* Find previous area
|
|
|
|
*/
|
|
|
|
if (area == pExaScr->info->offScreenAreas)
|
|
|
|
prev = NULL;
|
|
|
|
else
|
2010-07-27 13:02:24 -06:00
|
|
|
prev = area->prev;
|
|
|
|
|
|
|
|
pExaScr->numOffscreenAvailable++;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
/* link with next area if free */
|
|
|
|
if (next && next->state == ExaOffscreenAvail)
|
2010-07-27 13:02:24 -06:00
|
|
|
ExaOffscreenMerge (pExaScr, area);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
/* link with prev area if free */
|
|
|
|
if (prev && prev->state == ExaOffscreenAvail)
|
|
|
|
{
|
|
|
|
area = prev;
|
2010-07-27 13:02:24 -06:00
|
|
|
ExaOffscreenMerge (pExaScr, area);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
ExaOffscreenValidate (pScreen);
|
|
|
|
DBG_OFFSCREEN(("\tdone freeing\n"));
|
|
|
|
return area;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ExaOffscreenMarkUsed (PixmapPtr pPixmap)
|
|
|
|
{
|
|
|
|
ExaPixmapPriv (pPixmap);
|
|
|
|
ExaScreenPriv (pPixmap->drawable.pScreen);
|
|
|
|
|
2007-11-24 10:55:21 -07:00
|
|
|
if (!pExaPixmap || !pExaPixmap->area)
|
2006-11-26 11:13:41 -07:00
|
|
|
return;
|
|
|
|
|
2008-11-02 08:26:08 -07:00
|
|
|
pExaPixmap->area->last_use = pExaScr->offScreenCounter++;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
/**
|
|
|
|
* 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 */
|
2010-12-05 08:36:02 -07:00
|
|
|
area = malloc(sizeof (ExaOffscreenArea));
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
if (!area)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
area->state = ExaOffscreenAvail;
|
|
|
|
area->base_offset = pExaScr->info->offScreenBase;
|
|
|
|
area->offset = area->base_offset;
|
2010-07-27 13:02:24 -06:00
|
|
|
area->align = 0;
|
2006-11-26 11:13:41 -07:00
|
|
|
area->size = pExaScr->info->memorySize - area->base_offset;
|
|
|
|
area->save = NULL;
|
|
|
|
area->next = NULL;
|
2010-07-27 13:02:24 -06:00
|
|
|
area->prev = area;
|
2008-11-02 08:26:08 -07:00
|
|
|
area->last_use = 0;
|
|
|
|
area->eviction_cost = 0;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
/* Add it to the free areas */
|
|
|
|
pExaScr->info->offScreenAreas = area;
|
2008-11-02 08:26:08 -07:00
|
|
|
pExaScr->offScreenCounter = 1;
|
2010-07-27 13:02:24 -06:00
|
|
|
pExaScr->numOffscreenAvailable = 1;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
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;
|
2010-12-05 08:36:02 -07:00
|
|
|
free(area);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
}
|