2 * Copyright © 2003 Anders Carlsson
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.
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.
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.
36 #define DBG_OFFSCREEN(a) ErrorF a
38 #define DBG_OFFSCREEN(a)
43 ExaOffscreenValidate(ScreenPtr pScreen
)
45 ExaScreenPriv(pScreen
);
46 ExaOffscreenArea
*prev
= 0, *area
;
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
));
54 assert(prev
->base_offset
+ prev
->size
== area
->base_offset
);
57 assert(prev
->base_offset
+ prev
->size
== pExaScr
->info
->memorySize
);
60 #define ExaOffscreenValidate(s)
63 static ExaOffscreenArea
*
64 ExaOffscreenKickOut(ScreenPtr pScreen
, ExaOffscreenArea
* area
)
67 (*area
->save
) (pScreen
, area
);
68 return exaOffscreenFree(pScreen
, area
);
72 exaUpdateEvictionCost(ExaOffscreenArea
* area
, unsigned offScreenCounter
)
76 if (area
->state
== ExaOffscreenAvail
)
79 age
= offScreenCounter
- area
->last_use
;
81 /* This is unlikely to happen, but could result in a division by zero... */
82 if (age
> (UINT_MAX
/ 2)) {
84 area
->last_use
= offScreenCounter
- age
;
87 area
->eviction_cost
= area
->size
/ age
;
90 static ExaOffscreenArea
*
91 exaFindAreaToEvict(ExaScreenPrivPtr pExaScr
, int size
, int align
)
93 ExaOffscreenArea
*begin
, *end
, *best
;
94 unsigned cost
, best_cost
;
98 begin
= end
= pExaScr
->info
->offScreenAreas
;
103 while (end
!= NULL
) {
105 while (begin
!= NULL
&& begin
->state
== ExaOffscreenLocked
)
106 begin
= end
= begin
->next
;
111 /* adjust size needed to account for alignment loss for this area */
112 real_size
= size
+ (begin
->base_offset
+ begin
->size
- size
) % align
;
114 while (avail
< real_size
&& end
!= NULL
) {
115 if (end
->state
== ExaOffscreenLocked
) {
116 /* Can't more room here, restart after this locked area */
123 exaUpdateEvictionCost(end
, pExaScr
->offScreenCounter
);
124 cost
+= end
->eviction_cost
;
128 /* Check the cost, update best */
129 if (avail
>= real_size
&& cost
< best_cost
) {
134 avail
-= begin
->size
;
135 cost
-= begin
->eviction_cost
;
143 * exaOffscreenAlloc allocates offscreen memory
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.
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.
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
162 exaOffscreenAlloc(ScreenPtr pScreen
, int size
, int align
,
163 Bool locked
, ExaOffscreenSaveProc save
, pointer privData
)
165 ExaOffscreenArea
*area
;
167 ExaScreenPriv(pScreen
);
168 int real_size
= 0, largest_avail
= 0;
171 static int number
= 0;
173 ErrorF("================= ============ allocating a new pixmap %d\n",
177 ExaOffscreenValidate(pScreen
);
182 DBG_OFFSCREEN(("Alloc 0x%x -> EMPTY\n", size
));
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
));
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
)
200 /* adjust size to match alignment requirement */
201 real_size
= size
+ (area
->base_offset
+ area
->size
- size
) % align
;
204 if (real_size
<= area
->size
)
207 if (area
->size
> largest_avail
)
208 largest_avail
= area
->size
;
212 area
= exaFindAreaToEvict(pExaScr
, size
, align
);
215 DBG_OFFSCREEN(("Alloc 0x%x -> NOSPACE\n", size
));
216 /* Could not allocate memory */
217 ExaOffscreenValidate(pScreen
);
221 /* adjust size needed to account for alignment loss for this area */
222 real_size
= size
+ (area
->base_offset
+ area
->size
- size
) % align
;
225 * Kick out first area if in use
227 if (area
->state
!= ExaOffscreenAvail
)
228 area
= ExaOffscreenKickOut(pScreen
, area
);
230 * Now get the system to merge the other needed areas together
232 while (area
->size
< real_size
) {
233 assert(area
->next
&& area
->next
->state
== ExaOffscreenRemovable
);
234 (void) ExaOffscreenKickOut(pScreen
, area
->next
);
238 /* save extra space in new area */
239 if (real_size
< area
->size
) {
240 ExaOffscreenArea
*new_area
= malloc(sizeof(ExaOffscreenArea
));
244 new_area
->base_offset
= area
->base_offset
;
246 new_area
->offset
= new_area
->base_offset
;
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
;
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
;
264 pExaScr
->numOffscreenAvailable
--;
267 * Mark this area as in use
270 area
->state
= ExaOffscreenLocked
;
272 area
->state
= ExaOffscreenRemovable
;
273 area
->privData
= privData
;
275 area
->last_use
= pExaScr
->offScreenCounter
++;
276 area
->offset
= (area
->base_offset
+ align
- 1);
277 area
->offset
-= area
->offset
% align
;
280 ExaOffscreenValidate(pScreen
);
282 DBG_OFFSCREEN(("Alloc 0x%x -> 0x%x (0x%x)\n", size
,
283 area
->base_offset
, area
->offset
));
288 * Ejects all offscreen areas, and uninitializes the offscreen memory manager.
291 ExaOffscreenSwapOut(ScreenPtr pScreen
)
293 ExaScreenPriv(pScreen
);
295 ExaOffscreenValidate(pScreen
);
296 /* loop until a single free area spans the space */
298 ExaOffscreenArea
*area
= pExaScr
->info
->offScreenAreas
;
302 if (area
->state
== ExaOffscreenAvail
) {
307 assert(area
->state
!= ExaOffscreenAvail
);
308 (void) ExaOffscreenKickOut(pScreen
, area
);
309 ExaOffscreenValidate(pScreen
);
311 ExaOffscreenValidate(pScreen
);
312 ExaOffscreenFini(pScreen
);
315 /** Ejects all pixmaps managed by EXA. */
317 ExaOffscreenEjectPixmaps(ScreenPtr pScreen
)
319 ExaScreenPriv(pScreen
);
321 ExaOffscreenValidate(pScreen
);
322 /* loop until a single free area spans the space */
324 ExaOffscreenArea
*area
;
326 for (area
= pExaScr
->info
->offScreenAreas
; area
!= NULL
;
328 if (area
->state
== ExaOffscreenRemovable
&&
329 area
->save
== exaPixmapSave
) {
330 (void) ExaOffscreenKickOut(pScreen
, area
);
331 ExaOffscreenValidate(pScreen
);
338 ExaOffscreenValidate(pScreen
);
342 ExaOffscreenSwapIn(ScreenPtr pScreen
)
344 exaOffscreenInit(pScreen
);
348 * Prepares EXA for disabling of FB access, or restoring it.
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.
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
363 exaEnableDisableFBAccess(ScreenPtr pScreen
, Bool enable
)
365 ExaScreenPriv(pScreen
);
367 if (pExaScr
->info
->flags
& EXA_HANDLES_PIXMAPS
)
370 if (!enable
&& pExaScr
->disableFbCount
++ == 0) {
371 if (pExaScr
->info
->exa_minor
< 1)
372 ExaOffscreenSwapOut(pScreen
);
374 ExaOffscreenEjectPixmaps(pScreen
);
375 pExaScr
->swappedOut
= TRUE
;
378 if (enable
&& --pExaScr
->disableFbCount
== 0) {
379 if (pExaScr
->info
->exa_minor
< 1)
380 ExaOffscreenSwapIn(pScreen
);
381 pExaScr
->swappedOut
= FALSE
;
385 /* merge the next free area into this one */
387 ExaOffscreenMerge(ExaScreenPrivPtr pExaScr
, ExaOffscreenArea
* area
)
389 ExaOffscreenArea
*next
= area
->next
;
391 /* account for space */
392 area
->size
+= next
->size
;
394 area
->next
= next
->next
;
396 area
->next
->prev
= area
;
398 pExaScr
->info
->offScreenAreas
->prev
= area
;
401 pExaScr
->numOffscreenAvailable
--;
405 * exaOffscreenFree frees an allocation.
407 * @param pScreen current screen
408 * @param area offscreen area to free
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.
414 * @return pointer to the newly freed area. This behavior should not be relied
418 exaOffscreenFree(ScreenPtr pScreen
, ExaOffscreenArea
* area
)
420 ExaScreenPriv(pScreen
);
421 ExaOffscreenArea
*next
= area
->next
;
422 ExaOffscreenArea
*prev
;
424 DBG_OFFSCREEN(("Free 0x%x -> 0x%x (0x%x)\n", area
->size
,
425 area
->base_offset
, area
->offset
));
426 ExaOffscreenValidate(pScreen
);
428 area
->state
= ExaOffscreenAvail
;
431 area
->eviction_cost
= 0;
435 if (area
== pExaScr
->info
->offScreenAreas
)
440 pExaScr
->numOffscreenAvailable
++;
442 /* link with next area if free */
443 if (next
&& next
->state
== ExaOffscreenAvail
)
444 ExaOffscreenMerge(pExaScr
, area
);
446 /* link with prev area if free */
447 if (prev
&& prev
->state
== ExaOffscreenAvail
) {
449 ExaOffscreenMerge(pExaScr
, area
);
452 ExaOffscreenValidate(pScreen
);
453 DBG_OFFSCREEN(("\tdone freeing\n"));
458 ExaOffscreenMarkUsed(PixmapPtr pPixmap
)
460 ExaPixmapPriv(pPixmap
);
461 ExaScreenPriv(pPixmap
->drawable
.pScreen
);
463 if (!pExaPixmap
|| !pExaPixmap
->area
)
466 pExaPixmap
->area
->last_use
= pExaScr
->offScreenCounter
++;
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).
474 _X_HIDDEN ExaOffscreenArea
*
475 ExaOffscreenDefragment(ScreenPtr pScreen
)
477 ExaScreenPriv(pScreen
);
478 ExaOffscreenArea
*area
, *largest_available
= NULL
;
479 int largest_size
= 0;
481 ExaPixmapPrivPtr pExaDstPix
;
483 pDstPix
= (*pScreen
->CreatePixmap
) (pScreen
, 0, 0, 0, 0);
488 pExaDstPix
= ExaGetPixmapPriv(pDstPix
);
489 pExaDstPix
->use_gpu_copy
= TRUE
;
491 for (area
= pExaScr
->info
->offScreenAreas
->prev
;
492 area
!= pExaScr
->info
->offScreenAreas
;) {
493 ExaOffscreenArea
*prev
= area
->prev
;
495 ExaPixmapPrivPtr pExaSrcPix
;
496 Bool save_use_gpu_copy
;
499 if (area
->state
!= ExaOffscreenAvail
||
500 prev
->state
== ExaOffscreenLocked
||
501 (prev
->state
== ExaOffscreenRemovable
&&
502 prev
->save
!= exaPixmapSave
)) {
507 if (prev
->state
== ExaOffscreenAvail
) {
508 if (area
== largest_available
) {
509 largest_available
= prev
;
510 largest_size
+= prev
->size
;
513 ExaOffscreenMerge(pExaScr
, area
);
517 if (area
->size
> largest_size
) {
518 largest_available
= area
;
519 largest_size
= area
->size
;
522 pSrcPix
= prev
->privData
;
523 pExaSrcPix
= ExaGetPixmapPriv(pSrcPix
);
525 pExaDstPix
->fb_ptr
= pExaScr
->info
->memoryBase
+
526 area
->base_offset
+ area
->size
- prev
->size
+ prev
->base_offset
-
528 pExaDstPix
->fb_ptr
-= (unsigned long) pExaDstPix
->fb_ptr
% prev
->align
;
530 if (pExaDstPix
->fb_ptr
<= pExaSrcPix
->fb_ptr
) {
535 if (!(pExaScr
->info
->flags
& EXA_SUPPORTS_OFFSCREEN_OVERLAPS
) &&
536 (pExaSrcPix
->fb_ptr
+ prev
->size
) > pExaDstPix
->fb_ptr
) {
541 save_use_gpu_copy
= pExaSrcPix
->use_gpu_copy
;
542 save_pitch
= pSrcPix
->devKind
;
544 pExaSrcPix
->use_gpu_copy
= TRUE
;
545 pSrcPix
->devKind
= pExaSrcPix
->fb_pitch
;
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
;
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
;
560 pExaScr
->info
->Copy(pDstPix
, 0, 0, 0, 0, pDstPix
->drawable
.width
,
561 pDstPix
->drawable
.height
);
562 pExaScr
->info
->DoneCopy(pDstPix
);
563 exaMarkSync(pScreen
);
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
));
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
;
575 prev
->size
= area
->next
->base_offset
- prev
->base_offset
;
577 prev
->size
= pExaScr
->info
->memorySize
- prev
->base_offset
;
578 area
->size
= prev
->base_offset
- area
->base_offset
;
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
));
582 /* Swap areas in list */
584 area
->next
->prev
= prev
;
586 pExaScr
->info
->offScreenAreas
->prev
= prev
;
587 if (prev
->prev
->next
)
588 prev
->prev
->next
= area
;
590 pExaScr
->info
->offScreenAreas
= area
;
591 prev
->next
= area
->next
;
593 area
->prev
= prev
->prev
;
595 if (!area
->prev
->next
)
596 pExaScr
->info
->offScreenAreas
= area
;
599 if (prev
->prev
== prev
|| prev
->next
== prev
)
600 ErrorF("Whoops, prev points to itself!\n");
602 if (area
->prev
== area
|| area
->next
== area
)
603 ErrorF("Whoops, area points to itself!\n");
606 pExaSrcPix
->fb_ptr
= pExaDstPix
->fb_ptr
;
607 pExaSrcPix
->use_gpu_copy
= save_use_gpu_copy
;
608 pSrcPix
->devKind
= save_pitch
;
611 pDstPix
->drawable
.width
= 0;
612 pDstPix
->drawable
.height
= 0;
613 pDstPix
->drawable
.depth
= 0;
614 pDstPix
->drawable
.bitsPerPixel
= 0;
616 (*pScreen
->DestroyPixmap
) (pDstPix
);
618 if (area
->state
== ExaOffscreenAvail
&& area
->size
> largest_size
)
621 return largest_available
;
625 * exaOffscreenInit initializes the offscreen memory manager.
627 * @param pScreen current screen
629 * exaOffscreenInit is called by exaDriverInit to set up the memory manager for
630 * the screen, if any offscreen memory is available.
633 exaOffscreenInit(ScreenPtr pScreen
)
635 ExaScreenPriv(pScreen
);
636 ExaOffscreenArea
*area
;
638 /* Allocate a big free area */
639 area
= malloc(sizeof(ExaOffscreenArea
));
644 area
->state
= ExaOffscreenAvail
;
645 area
->base_offset
= pExaScr
->info
->offScreenBase
;
646 area
->offset
= area
->base_offset
;
648 area
->size
= pExaScr
->info
->memorySize
- area
->base_offset
;
653 area
->eviction_cost
= 0;
655 /* Add it to the free areas */
656 pExaScr
->info
->offScreenAreas
= area
;
657 pExaScr
->offScreenCounter
= 1;
658 pExaScr
->numOffscreenAvailable
= 1;
660 ExaOffscreenValidate(pScreen
);
666 ExaOffscreenFini(ScreenPtr pScreen
)
668 ExaScreenPriv(pScreen
);
669 ExaOffscreenArea
*area
;
671 /* just free all of the area records */
672 while ((area
= pExaScr
->info
->offScreenAreas
)) {
673 pExaScr
->info
->offScreenAreas
= area
->next
;