677 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			677 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * 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, void *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(ScreenPtr pScreen, Bool enable)
 | |
| {
 | |
|     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);
 | |
|     }
 | |
| }
 |