Imported Upstream version 1.15.1
[deb_xorg-server.git] / exa / exa_offscreen.c
CommitLineData
a09e091a
JB
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
42static void
43ExaOffscreenValidate(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
63static ExaOffscreenArea *
64ExaOffscreenKickOut(ScreenPtr pScreen, ExaOffscreenArea * area)
65{
66 if (area->save)
67 (*area->save) (pScreen, area);
68 return exaOffscreenFree(pScreen, area);
69}
70
71static void
72exaUpdateEvictionCost(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
90static ExaOffscreenArea *
91exaFindAreaToEvict(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 */
161ExaOffscreenArea *
162exaOffscreenAlloc(ScreenPtr pScreen, int size, int align,
163 Bool locked, ExaOffscreenSaveProc save, pointer 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 */
290void
291ExaOffscreenSwapOut(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. */
316static void
317ExaOffscreenEjectPixmaps(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
341void
342ExaOffscreenSwapIn(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 */
362void
363exaEnableDisableFBAccess(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 */
386static void
387ExaOffscreenMerge(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 */
417ExaOffscreenArea *
418exaOffscreenFree(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
457void
458ExaOffscreenMarkUsed(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 *
475ExaOffscreenDefragment(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 */
632Bool
633exaOffscreenInit(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
665void
666ExaOffscreenFini(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}