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 assert(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 determine 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
, void *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
) {
234 assert(area
->next
->state
== ExaOffscreenRemovable
);
235 (void) ExaOffscreenKickOut(pScreen
, area
->next
);
239 /* save extra space in new area */
240 if (real_size
< area
->size
) {
241 ExaOffscreenArea
*new_area
= malloc(sizeof(ExaOffscreenArea
));
245 new_area
->base_offset
= area
->base_offset
;
247 new_area
->offset
= new_area
->base_offset
;
249 new_area
->size
= area
->size
- real_size
;
250 new_area
->state
= ExaOffscreenAvail
;
251 new_area
->save
= NULL
;
252 new_area
->last_use
= 0;
253 new_area
->eviction_cost
= 0;
254 new_area
->next
= area
;
255 new_area
->prev
= area
->prev
;
256 if (area
->prev
->next
)
257 area
->prev
->next
= new_area
;
259 pExaScr
->info
->offScreenAreas
= new_area
;
260 area
->prev
= new_area
;
261 area
->base_offset
= new_area
->base_offset
+ new_area
->size
;
262 area
->size
= real_size
;
265 pExaScr
->numOffscreenAvailable
--;
268 * Mark this area as in use
271 area
->state
= ExaOffscreenLocked
;
273 area
->state
= ExaOffscreenRemovable
;
274 area
->privData
= privData
;
276 area
->last_use
= pExaScr
->offScreenCounter
++;
277 area
->offset
= (area
->base_offset
+ align
- 1);
278 area
->offset
-= area
->offset
% align
;
281 ExaOffscreenValidate(pScreen
);
283 DBG_OFFSCREEN(("Alloc 0x%x -> 0x%x (0x%x)\n", size
,
284 area
->base_offset
, area
->offset
));
289 * Ejects all offscreen areas, and uninitializes the offscreen memory manager.
292 ExaOffscreenSwapOut(ScreenPtr pScreen
)
294 ExaScreenPriv(pScreen
);
296 ExaOffscreenValidate(pScreen
);
297 /* loop until a single free area spans the space */
299 ExaOffscreenArea
*area
= pExaScr
->info
->offScreenAreas
;
303 if (area
->state
== ExaOffscreenAvail
) {
308 assert(area
->state
!= ExaOffscreenAvail
);
309 (void) ExaOffscreenKickOut(pScreen
, area
);
310 ExaOffscreenValidate(pScreen
);
312 ExaOffscreenValidate(pScreen
);
313 ExaOffscreenFini(pScreen
);
316 /** Ejects all pixmaps managed by EXA. */
318 ExaOffscreenEjectPixmaps(ScreenPtr pScreen
)
320 ExaScreenPriv(pScreen
);
322 ExaOffscreenValidate(pScreen
);
323 /* loop until a single free area spans the space */
325 ExaOffscreenArea
*area
;
327 for (area
= pExaScr
->info
->offScreenAreas
; area
!= NULL
;
329 if (area
->state
== ExaOffscreenRemovable
&&
330 area
->save
== exaPixmapSave
) {
331 (void) ExaOffscreenKickOut(pScreen
, area
);
332 ExaOffscreenValidate(pScreen
);
339 ExaOffscreenValidate(pScreen
);
343 ExaOffscreenSwapIn(ScreenPtr pScreen
)
345 exaOffscreenInit(pScreen
);
349 * Prepares EXA for disabling of FB access, or restoring it.
351 * In version 2.1, the disabling results in pixmaps being ejected, while other
352 * allocations remain. With this plus the prevention of migration while
353 * swappedOut is set, EXA by itself should not cause any access of the
354 * framebuffer to occur while swapped out. Any remaining issues are the
355 * responsibility of the driver.
357 * Prior to version 2.1, all allocations, including locked ones, are ejected
358 * when access is disabled, and the allocator is torn down while swappedOut
359 * is set. This is more drastic, and caused implementation difficulties for
360 * many drivers that could otherwise handle the lack of FB access while
364 exaEnableDisableFBAccess(ScreenPtr pScreen
, Bool enable
)
366 ExaScreenPriv(pScreen
);
368 if (pExaScr
->info
->flags
& EXA_HANDLES_PIXMAPS
)
371 if (!enable
&& pExaScr
->disableFbCount
++ == 0) {
372 if (pExaScr
->info
->exa_minor
< 1)
373 ExaOffscreenSwapOut(pScreen
);
375 ExaOffscreenEjectPixmaps(pScreen
);
376 pExaScr
->swappedOut
= TRUE
;
379 if (enable
&& --pExaScr
->disableFbCount
== 0) {
380 if (pExaScr
->info
->exa_minor
< 1)
381 ExaOffscreenSwapIn(pScreen
);
382 pExaScr
->swappedOut
= FALSE
;
386 /* merge the next free area into this one */
388 ExaOffscreenMerge(ExaScreenPrivPtr pExaScr
, ExaOffscreenArea
* area
)
390 ExaOffscreenArea
*next
= area
->next
;
392 /* account for space */
393 area
->size
+= next
->size
;
395 area
->next
= next
->next
;
397 area
->next
->prev
= area
;
399 pExaScr
->info
->offScreenAreas
->prev
= area
;
402 pExaScr
->numOffscreenAvailable
--;
406 * exaOffscreenFree frees an allocation.
408 * @param pScreen current screen
409 * @param area offscreen area to free
411 * exaOffscreenFree frees an allocation created by exaOffscreenAlloc. Note that
412 * the save callback of the area is not called, and it is up to the driver to
413 * do any cleanup necessary as a result.
415 * @return pointer to the newly freed area. This behavior should not be relied
419 exaOffscreenFree(ScreenPtr pScreen
, ExaOffscreenArea
* area
)
421 ExaScreenPriv(pScreen
);
422 ExaOffscreenArea
*next
= area
->next
;
423 ExaOffscreenArea
*prev
;
425 DBG_OFFSCREEN(("Free 0x%x -> 0x%x (0x%x)\n", area
->size
,
426 area
->base_offset
, area
->offset
));
427 ExaOffscreenValidate(pScreen
);
429 area
->state
= ExaOffscreenAvail
;
432 area
->eviction_cost
= 0;
436 if (area
== pExaScr
->info
->offScreenAreas
)
441 pExaScr
->numOffscreenAvailable
++;
443 /* link with next area if free */
444 if (next
&& next
->state
== ExaOffscreenAvail
)
445 ExaOffscreenMerge(pExaScr
, area
);
447 /* link with prev area if free */
448 if (prev
&& prev
->state
== ExaOffscreenAvail
) {
450 ExaOffscreenMerge(pExaScr
, area
);
453 ExaOffscreenValidate(pScreen
);
454 DBG_OFFSCREEN(("\tdone freeing\n"));
459 ExaOffscreenMarkUsed(PixmapPtr pPixmap
)
461 ExaPixmapPriv(pPixmap
);
462 ExaScreenPriv(pPixmap
->drawable
.pScreen
);
464 if (!pExaPixmap
|| !pExaPixmap
->area
)
467 pExaPixmap
->area
->last_use
= pExaScr
->offScreenCounter
++;
471 * Defragment offscreen memory by compacting allocated areas at the end of it,
472 * leaving the total amount of memory available as a single area at the
473 * beginning (when there are no pinned allocations).
475 _X_HIDDEN ExaOffscreenArea
*
476 ExaOffscreenDefragment(ScreenPtr pScreen
)
478 ExaScreenPriv(pScreen
);
479 ExaOffscreenArea
*area
, *largest_available
= NULL
;
480 int largest_size
= 0;
482 ExaPixmapPrivPtr pExaDstPix
;
484 pDstPix
= (*pScreen
->CreatePixmap
) (pScreen
, 0, 0, 0, 0);
489 pExaDstPix
= ExaGetPixmapPriv(pDstPix
);
490 pExaDstPix
->use_gpu_copy
= TRUE
;
492 for (area
= pExaScr
->info
->offScreenAreas
->prev
;
493 area
!= pExaScr
->info
->offScreenAreas
;) {
494 ExaOffscreenArea
*prev
= area
->prev
;
496 ExaPixmapPrivPtr pExaSrcPix
;
497 Bool save_use_gpu_copy
;
500 if (area
->state
!= ExaOffscreenAvail
||
501 prev
->state
== ExaOffscreenLocked
||
502 (prev
->state
== ExaOffscreenRemovable
&&
503 prev
->save
!= exaPixmapSave
)) {
508 if (prev
->state
== ExaOffscreenAvail
) {
509 if (area
== largest_available
) {
510 largest_available
= prev
;
511 largest_size
+= prev
->size
;
514 ExaOffscreenMerge(pExaScr
, area
);
518 if (area
->size
> largest_size
) {
519 largest_available
= area
;
520 largest_size
= area
->size
;
523 pSrcPix
= prev
->privData
;
524 pExaSrcPix
= ExaGetPixmapPriv(pSrcPix
);
526 pExaDstPix
->fb_ptr
= pExaScr
->info
->memoryBase
+
527 area
->base_offset
+ area
->size
- prev
->size
+ prev
->base_offset
-
529 pExaDstPix
->fb_ptr
-= (unsigned long) pExaDstPix
->fb_ptr
% prev
->align
;
531 if (pExaDstPix
->fb_ptr
<= pExaSrcPix
->fb_ptr
) {
536 if (!(pExaScr
->info
->flags
& EXA_SUPPORTS_OFFSCREEN_OVERLAPS
) &&
537 (pExaSrcPix
->fb_ptr
+ prev
->size
) > pExaDstPix
->fb_ptr
) {
542 save_use_gpu_copy
= pExaSrcPix
->use_gpu_copy
;
543 save_pitch
= pSrcPix
->devKind
;
545 pExaSrcPix
->use_gpu_copy
= TRUE
;
546 pSrcPix
->devKind
= pExaSrcPix
->fb_pitch
;
548 pDstPix
->drawable
.width
= pSrcPix
->drawable
.width
;
549 pDstPix
->devKind
= pSrcPix
->devKind
;
550 pDstPix
->drawable
.height
= pSrcPix
->drawable
.height
;
551 pDstPix
->drawable
.depth
= pSrcPix
->drawable
.depth
;
552 pDstPix
->drawable
.bitsPerPixel
= pSrcPix
->drawable
.bitsPerPixel
;
554 if (!pExaScr
->info
->PrepareCopy(pSrcPix
, pDstPix
, -1, -1, GXcopy
, ~0)) {
555 pExaSrcPix
->use_gpu_copy
= save_use_gpu_copy
;
556 pSrcPix
->devKind
= save_pitch
;
561 pExaScr
->info
->Copy(pDstPix
, 0, 0, 0, 0, pDstPix
->drawable
.width
,
562 pDstPix
->drawable
.height
);
563 pExaScr
->info
->DoneCopy(pDstPix
);
564 exaMarkSync(pScreen
);
566 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
));
568 /* Calculate swapped area offsets and sizes */
569 area
->base_offset
= prev
->base_offset
;
570 area
->offset
= area
->base_offset
;
571 prev
->offset
+= pExaDstPix
->fb_ptr
- pExaSrcPix
->fb_ptr
;
572 assert(prev
->offset
>= pExaScr
->info
->offScreenBase
);
573 assert(prev
->offset
< pExaScr
->info
->memorySize
);
574 prev
->base_offset
= prev
->offset
;
576 prev
->size
= area
->next
->base_offset
- prev
->base_offset
;
578 prev
->size
= pExaScr
->info
->memorySize
- prev
->base_offset
;
579 area
->size
= prev
->base_offset
- area
->base_offset
;
581 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
));
583 /* Swap areas in list */
585 area
->next
->prev
= prev
;
587 pExaScr
->info
->offScreenAreas
->prev
= prev
;
588 if (prev
->prev
->next
)
589 prev
->prev
->next
= area
;
591 pExaScr
->info
->offScreenAreas
= area
;
592 prev
->next
= area
->next
;
594 area
->prev
= prev
->prev
;
596 if (!area
->prev
->next
)
597 pExaScr
->info
->offScreenAreas
= area
;
600 if (prev
->prev
== prev
|| prev
->next
== prev
)
601 ErrorF("Whoops, prev points to itself!\n");
603 if (area
->prev
== area
|| area
->next
== area
)
604 ErrorF("Whoops, area points to itself!\n");
607 pExaSrcPix
->fb_ptr
= pExaDstPix
->fb_ptr
;
608 pExaSrcPix
->use_gpu_copy
= save_use_gpu_copy
;
609 pSrcPix
->devKind
= save_pitch
;
612 pDstPix
->drawable
.width
= 0;
613 pDstPix
->drawable
.height
= 0;
614 pDstPix
->drawable
.depth
= 0;
615 pDstPix
->drawable
.bitsPerPixel
= 0;
617 (*pScreen
->DestroyPixmap
) (pDstPix
);
619 if (area
->state
== ExaOffscreenAvail
&& area
->size
> largest_size
)
622 return largest_available
;
626 * exaOffscreenInit initializes the offscreen memory manager.
628 * @param pScreen current screen
630 * exaOffscreenInit is called by exaDriverInit to set up the memory manager for
631 * the screen, if any offscreen memory is available.
634 exaOffscreenInit(ScreenPtr pScreen
)
636 ExaScreenPriv(pScreen
);
637 ExaOffscreenArea
*area
;
639 /* Allocate a big free area */
640 area
= malloc(sizeof(ExaOffscreenArea
));
645 area
->state
= ExaOffscreenAvail
;
646 area
->base_offset
= pExaScr
->info
->offScreenBase
;
647 area
->offset
= area
->base_offset
;
649 area
->size
= pExaScr
->info
->memorySize
- area
->base_offset
;
654 area
->eviction_cost
= 0;
656 /* Add it to the free areas */
657 pExaScr
->info
->offScreenAreas
= area
;
658 pExaScr
->offScreenCounter
= 1;
659 pExaScr
->numOffscreenAvailable
= 1;
661 ExaOffscreenValidate(pScreen
);
667 ExaOffscreenFini(ScreenPtr pScreen
)
669 ExaScreenPriv(pScreen
);
670 ExaOffscreenArea
*area
;
672 /* just free all of the area records */
673 while ((area
= pExaScr
->info
->offScreenAreas
)) {
674 pExaScr
->info
->offScreenAreas
= area
->next
;