1 /*
2  * Copyright © 2003 Anders Carlsson
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of Anders Carlsson not be used in
9  * advertising or publicity pertaining to distribution of the software without
10  * specific, written prior permission.  Anders Carlsson makes no
11  * representations about the suitability of this software for any purpose.  It
12  * is provided "as is" without express or implied warranty.
13  *
14  * ANDERS CARLSSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL ANDERS CARLSSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 /** @file
24  * This allocator allocates blocks of memory by maintaining a list of areas.
25  * When allocating, the contiguous block of areas with the minimum eviction
26  * cost is found and evicted in order to make room for the new allocation.
27  */
28 
29 #include "exa_priv.h"
30 
31 #include <limits.h>
32 #include <assert.h>
33 #include <stdlib.h>
34 
35 #if DEBUG_OFFSCREEN
36 #define DBG_OFFSCREEN(a) ErrorF a
37 #else
38 #define DBG_OFFSCREEN(a)
39 #endif
40 
41 #if DEBUG_OFFSCREEN
42 static void
ExaOffscreenValidate(ScreenPtr pScreen)43 ExaOffscreenValidate(ScreenPtr pScreen)
44 {
45     ExaScreenPriv(pScreen);
46     ExaOffscreenArea *prev = 0, *area;
47 
48     assert(pExaScr->info->offScreenAreas->base_offset ==
49            pExaScr->info->offScreenBase);
50     for (area = pExaScr->info->offScreenAreas; area; area = area->next) {
51         assert(area->offset >= area->base_offset &&
52                area->offset < (area->base_offset + area->size));
53         if (prev)
54             assert(prev->base_offset + prev->size == area->base_offset);
55         prev = area;
56     }
57     assert(prev->base_offset + prev->size == pExaScr->info->memorySize);
58 }
59 #else
60 #define ExaOffscreenValidate(s)
61 #endif
62 
63 static ExaOffscreenArea *
ExaOffscreenKickOut(ScreenPtr pScreen,ExaOffscreenArea * area)64 ExaOffscreenKickOut(ScreenPtr pScreen, ExaOffscreenArea * area)
65 {
66     if (area->save)
67         (*area->save) (pScreen, area);
68     return exaOffscreenFree(pScreen, area);
69 }
70 
71 static void
exaUpdateEvictionCost(ExaOffscreenArea * area,unsigned offScreenCounter)72 exaUpdateEvictionCost(ExaOffscreenArea * area, unsigned offScreenCounter)
73 {
74     unsigned age;
75 
76     if (area->state == ExaOffscreenAvail)
77         return;
78 
79     age = offScreenCounter - area->last_use;
80 
81     /* This is unlikely to happen, but could result in a division by zero... */
82     if (age > (UINT_MAX / 2)) {
83         age = UINT_MAX / 2;
84         area->last_use = offScreenCounter - age;
85     }
86 
87     area->eviction_cost = area->size / age;
88 }
89 
90 static ExaOffscreenArea *
exaFindAreaToEvict(ExaScreenPrivPtr pExaScr,int size,int align)91 exaFindAreaToEvict(ExaScreenPrivPtr pExaScr, int size, int align)
92 {
93     ExaOffscreenArea *begin, *end, *best;
94     unsigned cost, best_cost;
95     int avail, real_size;
96 
97     best_cost = UINT_MAX;
98     begin = end = pExaScr->info->offScreenAreas;
99     avail = 0;
100     cost = 0;
101     best = 0;
102 
103     while (end != NULL) {
104  restart:
105         while (begin != NULL && begin->state == ExaOffscreenLocked)
106             begin = end = begin->next;
107 
108         if (begin == NULL)
109             break;
110 
111         /* adjust size needed to account for alignment loss for this area */
112         real_size = size + (begin->base_offset + begin->size - size) % align;
113 
114         while (avail < real_size && end != NULL) {
115             if (end->state == ExaOffscreenLocked) {
116                 /* Can't more room here, restart after this locked area */
117                 avail = 0;
118                 cost = 0;
119                 begin = end;
120                 goto restart;
121             }
122             avail += end->size;
123             exaUpdateEvictionCost(end, pExaScr->offScreenCounter);
124             cost += end->eviction_cost;
125             end = end->next;
126         }
127 
128         /* Check the cost, update best */
129         if (avail >= real_size && cost < best_cost) {
130             best = begin;
131             best_cost = cost;
132         }
133 
134         avail -= begin->size;
135         cost -= begin->eviction_cost;
136         begin = begin->next;
137     }
138 
139     return best;
140 }
141 
142 /**
143  * exaOffscreenAlloc allocates offscreen memory
144  *
145  * @param pScreen current screen
146  * @param size size in bytes of the allocation
147  * @param align byte alignment requirement for the offset of the allocated area
148  * @param locked whether the allocated area is locked and can't be kicked out
149  * @param save callback for when the area is evicted from memory
150  * @param privdata private data for the save callback.
151  *
152  * Allocates offscreen memory from the device associated with pScreen.  size
153  * and align deteremine where and how large the allocated area is, and locked
154  * will mark whether it should be held in card memory.  privdata may be any
155  * pointer for the save callback when the area is removed.
156  *
157  * Note that locked areas do get evicted on VT switch unless the driver
158  * requested version 2.1 or newer behavior.  In that case, the save callback is
159  * still called.
160  */
161 ExaOffscreenArea *
exaOffscreenAlloc(ScreenPtr pScreen,int size,int align,Bool locked,ExaOffscreenSaveProc save,void * privData)162 exaOffscreenAlloc(ScreenPtr pScreen, int size, int align,
163                   Bool locked, ExaOffscreenSaveProc save, void *privData)
164 {
165     ExaOffscreenArea *area;
166 
167     ExaScreenPriv(pScreen);
168     int real_size = 0, largest_avail = 0;
169 
170 #if DEBUG_OFFSCREEN
171     static int number = 0;
172 
173     ErrorF("================= ============ allocating a new pixmap %d\n",
174            ++number);
175 #endif
176 
177     ExaOffscreenValidate(pScreen);
178     if (!align)
179         align = 1;
180 
181     if (!size) {
182         DBG_OFFSCREEN(("Alloc 0x%x -> EMPTY\n", size));
183         return NULL;
184     }
185 
186     /* throw out requests that cannot fit */
187     if (size > (pExaScr->info->memorySize - pExaScr->info->offScreenBase)) {
188         DBG_OFFSCREEN(("Alloc 0x%x vs (0x%lx) -> TOBIG\n", size,
189                        pExaScr->info->memorySize -
190                        pExaScr->info->offScreenBase));
191         return NULL;
192     }
193 
194     /* Try to find a free space that'll fit. */
195     for (area = pExaScr->info->offScreenAreas; area; area = area->next) {
196         /* skip allocated areas */
197         if (area->state != ExaOffscreenAvail)
198             continue;
199 
200         /* adjust size to match alignment requirement */
201         real_size = size + (area->base_offset + area->size - size) % align;
202 
203         /* does it fit? */
204         if (real_size <= area->size)
205             break;
206 
207         if (area->size > largest_avail)
208             largest_avail = area->size;
209     }
210 
211     if (!area) {
212         area = exaFindAreaToEvict(pExaScr, size, align);
213 
214         if (!area) {
215             DBG_OFFSCREEN(("Alloc 0x%x -> NOSPACE\n", size));
216             /* Could not allocate memory */
217             ExaOffscreenValidate(pScreen);
218             return NULL;
219         }
220 
221         /* adjust size needed to account for alignment loss for this area */
222         real_size = size + (area->base_offset + area->size - size) % align;
223 
224         /*
225          * Kick out first area if in use
226          */
227         if (area->state != ExaOffscreenAvail)
228             area = ExaOffscreenKickOut(pScreen, area);
229         /*
230          * Now get the system to merge the other needed areas together
231          */
232         while (area->size < real_size) {
233             assert(area->next && area->next->state == ExaOffscreenRemovable);
234             (void) ExaOffscreenKickOut(pScreen, area->next);
235         }
236     }
237 
238     /* save extra space in new area */
239     if (real_size < area->size) {
240         ExaOffscreenArea *new_area = malloc(sizeof(ExaOffscreenArea));
241 
242         if (!new_area)
243             return NULL;
244         new_area->base_offset = area->base_offset;
245 
246         new_area->offset = new_area->base_offset;
247         new_area->align = 0;
248         new_area->size = area->size - real_size;
249         new_area->state = ExaOffscreenAvail;
250         new_area->save = NULL;
251         new_area->last_use = 0;
252         new_area->eviction_cost = 0;
253         new_area->next = area;
254         new_area->prev = area->prev;
255         if (area->prev->next)
256             area->prev->next = new_area;
257         else
258             pExaScr->info->offScreenAreas = new_area;
259         area->prev = new_area;
260         area->base_offset = new_area->base_offset + new_area->size;
261         area->size = real_size;
262     }
263     else
264         pExaScr->numOffscreenAvailable--;
265 
266     /*
267      * Mark this area as in use
268      */
269     if (locked)
270         area->state = ExaOffscreenLocked;
271     else
272         area->state = ExaOffscreenRemovable;
273     area->privData = privData;
274     area->save = save;
275     area->last_use = pExaScr->offScreenCounter++;
276     area->offset = (area->base_offset + align - 1);
277     area->offset -= area->offset % align;
278     area->align = align;
279 
280     ExaOffscreenValidate(pScreen);
281 
282     DBG_OFFSCREEN(("Alloc 0x%x -> 0x%x (0x%x)\n", size,
283                    area->base_offset, area->offset));
284     return area;
285 }
286 
287 /**
288  * Ejects all offscreen areas, and uninitializes the offscreen memory manager.
289  */
290 void
ExaOffscreenSwapOut(ScreenPtr pScreen)291 ExaOffscreenSwapOut(ScreenPtr pScreen)
292 {
293     ExaScreenPriv(pScreen);
294 
295     ExaOffscreenValidate(pScreen);
296     /* loop until a single free area spans the space */
297     for (;;) {
298         ExaOffscreenArea *area = pExaScr->info->offScreenAreas;
299 
300         if (!area)
301             break;
302         if (area->state == ExaOffscreenAvail) {
303             area = area->next;
304             if (!area)
305                 break;
306         }
307         assert(area->state != ExaOffscreenAvail);
308         (void) ExaOffscreenKickOut(pScreen, area);
309         ExaOffscreenValidate(pScreen);
310     }
311     ExaOffscreenValidate(pScreen);
312     ExaOffscreenFini(pScreen);
313 }
314 
315 /** Ejects all pixmaps managed by EXA. */
316 static void
ExaOffscreenEjectPixmaps(ScreenPtr pScreen)317 ExaOffscreenEjectPixmaps(ScreenPtr pScreen)
318 {
319     ExaScreenPriv(pScreen);
320 
321     ExaOffscreenValidate(pScreen);
322     /* loop until a single free area spans the space */
323     for (;;) {
324         ExaOffscreenArea *area;
325 
326         for (area = pExaScr->info->offScreenAreas; area != NULL;
327              area = area->next) {
328             if (area->state == ExaOffscreenRemovable &&
329                 area->save == exaPixmapSave) {
330                 (void) ExaOffscreenKickOut(pScreen, area);
331                 ExaOffscreenValidate(pScreen);
332                 break;
333             }
334         }
335         if (area == NULL)
336             break;
337     }
338     ExaOffscreenValidate(pScreen);
339 }
340 
341 void
ExaOffscreenSwapIn(ScreenPtr pScreen)342 ExaOffscreenSwapIn(ScreenPtr pScreen)
343 {
344     exaOffscreenInit(pScreen);
345 }
346 
347 /**
348  * Prepares EXA for disabling of FB access, or restoring it.
349  *
350  * In version 2.1, the disabling results in pixmaps being ejected, while other
351  * allocations remain.  With this plus the prevention of migration while
352  * swappedOut is set, EXA by itself should not cause any access of the
353  * framebuffer to occur while swapped out.  Any remaining issues are the
354  * responsibility of the driver.
355  *
356  * Prior to version 2.1, all allocations, including locked ones, are ejected
357  * when access is disabled, and the allocator is torn down while swappedOut
358  * is set.  This is more drastic, and caused implementation difficulties for
359  * many drivers that could otherwise handle the lack of FB access while
360  * swapped out.
361  */
362 void
exaEnableDisableFBAccess(ScreenPtr pScreen,Bool enable)363 exaEnableDisableFBAccess(ScreenPtr pScreen, Bool enable)
364 {
365     ExaScreenPriv(pScreen);
366 
367     if (pExaScr->info->flags & EXA_HANDLES_PIXMAPS)
368         return;
369 
370     if (!enable && pExaScr->disableFbCount++ == 0) {
371         if (pExaScr->info->exa_minor < 1)
372             ExaOffscreenSwapOut(pScreen);
373         else
374             ExaOffscreenEjectPixmaps(pScreen);
375         pExaScr->swappedOut = TRUE;
376     }
377 
378     if (enable && --pExaScr->disableFbCount == 0) {
379         if (pExaScr->info->exa_minor < 1)
380             ExaOffscreenSwapIn(pScreen);
381         pExaScr->swappedOut = FALSE;
382     }
383 }
384 
385 /* merge the next free area into this one */
386 static void
ExaOffscreenMerge(ExaScreenPrivPtr pExaScr,ExaOffscreenArea * area)387 ExaOffscreenMerge(ExaScreenPrivPtr pExaScr, ExaOffscreenArea * area)
388 {
389     ExaOffscreenArea *next = area->next;
390 
391     /* account for space */
392     area->size += next->size;
393     /* frob pointer */
394     area->next = next->next;
395     if (area->next)
396         area->next->prev = area;
397     else
398         pExaScr->info->offScreenAreas->prev = area;
399     free(next);
400 
401     pExaScr->numOffscreenAvailable--;
402 }
403 
404 /**
405  * exaOffscreenFree frees an allocation.
406  *
407  * @param pScreen current screen
408  * @param area offscreen area to free
409  *
410  * exaOffscreenFree frees an allocation created by exaOffscreenAlloc.  Note that
411  * the save callback of the area is not called, and it is up to the driver to
412  * do any cleanup necessary as a result.
413  *
414  * @return pointer to the newly freed area. This behavior should not be relied
415  * on.
416  */
417 ExaOffscreenArea *
exaOffscreenFree(ScreenPtr pScreen,ExaOffscreenArea * area)418 exaOffscreenFree(ScreenPtr pScreen, ExaOffscreenArea * area)
419 {
420     ExaScreenPriv(pScreen);
421     ExaOffscreenArea *next = area->next;
422     ExaOffscreenArea *prev;
423 
424     DBG_OFFSCREEN(("Free 0x%x -> 0x%x (0x%x)\n", area->size,
425                    area->base_offset, area->offset));
426     ExaOffscreenValidate(pScreen);
427 
428     area->state = ExaOffscreenAvail;
429     area->save = NULL;
430     area->last_use = 0;
431     area->eviction_cost = 0;
432     /*
433      * Find previous area
434      */
435     if (area == pExaScr->info->offScreenAreas)
436         prev = NULL;
437     else
438         prev = area->prev;
439 
440     pExaScr->numOffscreenAvailable++;
441 
442     /* link with next area if free */
443     if (next && next->state == ExaOffscreenAvail)
444         ExaOffscreenMerge(pExaScr, area);
445 
446     /* link with prev area if free */
447     if (prev && prev->state == ExaOffscreenAvail) {
448         area = prev;
449         ExaOffscreenMerge(pExaScr, area);
450     }
451 
452     ExaOffscreenValidate(pScreen);
453     DBG_OFFSCREEN(("\tdone freeing\n"));
454     return area;
455 }
456 
457 void
ExaOffscreenMarkUsed(PixmapPtr pPixmap)458 ExaOffscreenMarkUsed(PixmapPtr pPixmap)
459 {
460     ExaPixmapPriv(pPixmap);
461     ExaScreenPriv(pPixmap->drawable.pScreen);
462 
463     if (!pExaPixmap || !pExaPixmap->area)
464         return;
465 
466     pExaPixmap->area->last_use = pExaScr->offScreenCounter++;
467 }
468 
469 /**
470  * Defragment offscreen memory by compacting allocated areas at the end of it,
471  * leaving the total amount of memory available as a single area at the
472  * beginning (when there are no pinned allocations).
473  */
474 _X_HIDDEN ExaOffscreenArea *
ExaOffscreenDefragment(ScreenPtr pScreen)475 ExaOffscreenDefragment(ScreenPtr pScreen)
476 {
477     ExaScreenPriv(pScreen);
478     ExaOffscreenArea *area, *largest_available = NULL;
479     int largest_size = 0;
480     PixmapPtr pDstPix;
481     ExaPixmapPrivPtr pExaDstPix;
482 
483     pDstPix = (*pScreen->CreatePixmap) (pScreen, 0, 0, 0, 0);
484 
485     if (!pDstPix)
486         return NULL;
487 
488     pExaDstPix = ExaGetPixmapPriv(pDstPix);
489     pExaDstPix->use_gpu_copy = TRUE;
490 
491     for (area = pExaScr->info->offScreenAreas->prev;
492          area != pExaScr->info->offScreenAreas;) {
493         ExaOffscreenArea *prev = area->prev;
494         PixmapPtr pSrcPix;
495         ExaPixmapPrivPtr pExaSrcPix;
496         Bool save_use_gpu_copy;
497         int save_pitch;
498 
499         if (area->state != ExaOffscreenAvail ||
500             prev->state == ExaOffscreenLocked ||
501             (prev->state == ExaOffscreenRemovable &&
502              prev->save != exaPixmapSave)) {
503             area = prev;
504             continue;
505         }
506 
507         if (prev->state == ExaOffscreenAvail) {
508             if (area == largest_available) {
509                 largest_available = prev;
510                 largest_size += prev->size;
511             }
512             area = prev;
513             ExaOffscreenMerge(pExaScr, area);
514             continue;
515         }
516 
517         if (area->size > largest_size) {
518             largest_available = area;
519             largest_size = area->size;
520         }
521 
522         pSrcPix = prev->privData;
523         pExaSrcPix = ExaGetPixmapPriv(pSrcPix);
524 
525         pExaDstPix->fb_ptr = pExaScr->info->memoryBase +
526             area->base_offset + area->size - prev->size + prev->base_offset -
527             prev->offset;
528         pExaDstPix->fb_ptr -= (unsigned long) pExaDstPix->fb_ptr % prev->align;
529 
530         if (pExaDstPix->fb_ptr <= pExaSrcPix->fb_ptr) {
531             area = prev;
532             continue;
533         }
534 
535         if (!(pExaScr->info->flags & EXA_SUPPORTS_OFFSCREEN_OVERLAPS) &&
536             (pExaSrcPix->fb_ptr + prev->size) > pExaDstPix->fb_ptr) {
537             area = prev;
538             continue;
539         }
540 
541         save_use_gpu_copy = pExaSrcPix->use_gpu_copy;
542         save_pitch = pSrcPix->devKind;
543 
544         pExaSrcPix->use_gpu_copy = TRUE;
545         pSrcPix->devKind = pExaSrcPix->fb_pitch;
546 
547         pDstPix->drawable.width = pSrcPix->drawable.width;
548         pDstPix->devKind = pSrcPix->devKind;
549         pDstPix->drawable.height = pSrcPix->drawable.height;
550         pDstPix->drawable.depth = pSrcPix->drawable.depth;
551         pDstPix->drawable.bitsPerPixel = pSrcPix->drawable.bitsPerPixel;
552 
553         if (!pExaScr->info->PrepareCopy(pSrcPix, pDstPix, -1, -1, GXcopy, ~0)) {
554             pExaSrcPix->use_gpu_copy = save_use_gpu_copy;
555             pSrcPix->devKind = save_pitch;
556             area = prev;
557             continue;
558         }
559 
560         pExaScr->info->Copy(pDstPix, 0, 0, 0, 0, pDstPix->drawable.width,
561                             pDstPix->drawable.height);
562         pExaScr->info->DoneCopy(pDstPix);
563         exaMarkSync(pScreen);
564 
565         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));
566 
567         /* Calculate swapped area offsets and sizes */
568         area->base_offset = prev->base_offset;
569         area->offset = area->base_offset;
570         prev->offset += pExaDstPix->fb_ptr - pExaSrcPix->fb_ptr;
571         assert(prev->offset >= pExaScr->info->offScreenBase &&
572                prev->offset < pExaScr->info->memorySize);
573         prev->base_offset = prev->offset;
574         if (area->next)
575             prev->size = area->next->base_offset - prev->base_offset;
576         else
577             prev->size = pExaScr->info->memorySize - prev->base_offset;
578         area->size = prev->base_offset - area->base_offset;
579 
580         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));
581 
582         /* Swap areas in list */
583         if (area->next)
584             area->next->prev = prev;
585         else
586             pExaScr->info->offScreenAreas->prev = prev;
587         if (prev->prev->next)
588             prev->prev->next = area;
589         else
590             pExaScr->info->offScreenAreas = area;
591         prev->next = area->next;
592         area->next = prev;
593         area->prev = prev->prev;
594         prev->prev = area;
595         if (!area->prev->next)
596             pExaScr->info->offScreenAreas = area;
597 
598 #if DEBUG_OFFSCREEN
599         if (prev->prev == prev || prev->next == prev)
600             ErrorF("Whoops, prev points to itself!\n");
601 
602         if (area->prev == area || area->next == area)
603             ErrorF("Whoops, area points to itself!\n");
604 #endif
605 
606         pExaSrcPix->fb_ptr = pExaDstPix->fb_ptr;
607         pExaSrcPix->use_gpu_copy = save_use_gpu_copy;
608         pSrcPix->devKind = save_pitch;
609     }
610 
611     pDstPix->drawable.width = 0;
612     pDstPix->drawable.height = 0;
613     pDstPix->drawable.depth = 0;
614     pDstPix->drawable.bitsPerPixel = 0;
615 
616     (*pScreen->DestroyPixmap) (pDstPix);
617 
618     if (area->state == ExaOffscreenAvail && area->size > largest_size)
619         return area;
620 
621     return largest_available;
622 }
623 
624 /**
625  * exaOffscreenInit initializes the offscreen memory manager.
626  *
627  * @param pScreen current screen
628  *
629  * exaOffscreenInit is called by exaDriverInit to set up the memory manager for
630  * the screen, if any offscreen memory is available.
631  */
632 Bool
exaOffscreenInit(ScreenPtr pScreen)633 exaOffscreenInit(ScreenPtr pScreen)
634 {
635     ExaScreenPriv(pScreen);
636     ExaOffscreenArea *area;
637 
638     /* Allocate a big free area */
639     area = malloc(sizeof(ExaOffscreenArea));
640 
641     if (!area)
642         return FALSE;
643 
644     area->state = ExaOffscreenAvail;
645     area->base_offset = pExaScr->info->offScreenBase;
646     area->offset = area->base_offset;
647     area->align = 0;
648     area->size = pExaScr->info->memorySize - area->base_offset;
649     area->save = NULL;
650     area->next = NULL;
651     area->prev = area;
652     area->last_use = 0;
653     area->eviction_cost = 0;
654 
655     /* Add it to the free areas */
656     pExaScr->info->offScreenAreas = area;
657     pExaScr->offScreenCounter = 1;
658     pExaScr->numOffscreenAvailable = 1;
659 
660     ExaOffscreenValidate(pScreen);
661 
662     return TRUE;
663 }
664 
665 void
ExaOffscreenFini(ScreenPtr pScreen)666 ExaOffscreenFini(ScreenPtr pScreen)
667 {
668     ExaScreenPriv(pScreen);
669     ExaOffscreenArea *area;
670 
671     /* just free all of the area records */
672     while ((area = pExaScr->info->offScreenAreas)) {
673         pExaScr->info->offScreenAreas = area->next;
674         free(area);
675     }
676 }
677