First import
[xorg_rtime.git] / xorg-server-1.4 / hw / kdrive / src / koffscreen.c
blobefe03642f39a5d00d72eabf9701d0df502f1fac5
1 /*
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.
23 #ifdef HAVE_CONFIG_H
24 #include <kdrive-config.h>
25 #endif
26 #include "kdrive.h"
27 #include "kaa.h"
29 #define DEBUG_OFFSCREEN 0
30 #if DEBUG_OFFSCREEN
31 #define DBG_OFFSCREEN(a) ErrorF a
32 #else
33 #define DBG_OFFSCREEN(a)
34 #endif
36 #if DEBUG_OFFSCREEN
37 static void
38 KdOffscreenValidate (ScreenPtr pScreen)
40 KdScreenPriv (pScreen);
41 KdOffscreenArea *prev = 0, *area;
43 assert (pScreenPriv->screen->off_screen_areas->area.offset == 0);
44 for (area = pScreenPriv->off_screen_areas; area; area = area->next)
46 if (prev)
47 assert (prev->offset + prev->size == area->offset);
49 prev = area;
51 assert (prev->offset + prev->size == pScreenPriv->screen->memory_size);
53 #else
54 #define KdOffscreenValidate(s)
55 #endif
57 static KdOffscreenArea *
58 KdOffscreenKickOut (ScreenPtr pScreen, KdOffscreenArea *area)
60 if (area->save)
61 (*area->save) (pScreen, area);
62 return KdOffscreenFree (pScreen, area);
65 KdOffscreenArea *
66 KdOffscreenAlloc (ScreenPtr pScreen, int size, int align,
67 Bool locked,
68 KdOffscreenSaveProc save,
69 pointer privData)
71 KdOffscreenArea *area, *begin, *best;
72 KdScreenPriv (pScreen);
73 int tmp, real_size = 0, best_score;
75 KdOffscreenValidate (pScreen);
76 if (!align)
77 align = 1;
79 if (!size)
81 DBG_OFFSCREEN (("Alloc 0x%x -> EMPTY\n", size));
82 return NULL;
85 /* throw out requests that cannot fit */
86 if (size > (pScreenPriv->screen->memory_size - pScreenPriv->screen->off_screen_base))
88 DBG_OFFSCREEN (("Alloc 0x%x -> TOBIG\n", size));
89 return NULL;
92 /* Try to find a free space that'll fit. */
93 for (area = pScreenPriv->off_screen_areas; area; area = area->next)
95 /* skip allocated areas */
96 if (area->state != KdOffscreenAvail)
97 continue;
99 /* adjust size to match alignment requirement */
100 real_size = size;
101 tmp = area->offset % align;
102 if (tmp)
103 real_size += (align - tmp);
105 /* does it fit? */
106 if (real_size <= area->size)
107 break;
110 if (!area)
113 * Kick out existing users to make space.
115 * First, locate a region which can hold the desired object.
118 /* prev points at the first object to boot */
119 best = NULL;
120 best_score = MAXINT;
121 for (begin = pScreenPriv->off_screen_areas; begin != NULL;
122 begin = begin->next)
124 int avail, score;
125 KdOffscreenArea *scan;
127 if (begin->state == KdOffscreenLocked)
128 continue;
130 /* adjust size to match alignment requirement */
131 real_size = size;
132 tmp = begin->offset % align;
133 if (tmp)
134 real_size += (align - tmp);
136 avail = 0;
137 score = 0;
138 /* now see if we can make room here, and how "costly" it'll be. */
139 for (scan = begin; scan != NULL; scan = scan->next)
141 if (scan->state == KdOffscreenLocked) {
142 /* Can't make room here, start after this locked area. */
143 begin = scan->next;
144 break;
146 /* Score should only be non-zero for KdOffscreenRemovable */
147 score += scan->score;
148 avail += scan->size;
149 if (avail >= real_size)
150 break;
152 /* Is it the best option we've found so far? */
153 if (avail >= real_size && score < best_score) {
154 best = begin;
155 best_score = score;
158 area = best;
159 if (!area)
161 DBG_OFFSCREEN (("Alloc 0x%x -> NOSPACE\n", size));
162 /* Could not allocate memory */
163 KdOffscreenValidate (pScreen);
164 return NULL;
167 /* adjust size to match alignment requirement */
168 real_size = size;
169 tmp = begin->offset % align;
170 if (tmp)
171 real_size += (align - tmp);
174 * Kick out first area if in use
176 if (area->state != KdOffscreenAvail)
177 area = KdOffscreenKickOut (pScreen, area);
179 * Now get the system to merge the other needed areas together
181 while (area->size < real_size)
183 assert (area->next && area->next->state == KdOffscreenRemovable);
184 (void) KdOffscreenKickOut (pScreen, area->next);
188 /* save extra space in new area */
189 if (real_size < area->size)
191 KdOffscreenArea *new_area = xalloc (sizeof (KdOffscreenArea));
192 if (!new_area)
193 return NULL;
194 new_area->offset = area->offset + real_size;
195 new_area->size = area->size - real_size;
196 new_area->state = KdOffscreenAvail;
197 new_area->save = 0;
198 new_area->score = 0;
199 new_area->next = area->next;
200 area->next = new_area;
201 area->size = real_size;
204 * Mark this area as in use
206 if (locked)
207 area->state = KdOffscreenLocked;
208 else
209 area->state = KdOffscreenRemovable;
210 area->privData = privData;
211 area->save = save;
212 area->score = 0;
214 area->save_offset = area->offset;
216 int tmp = area->offset % align;
217 if (tmp)
218 area->offset += (align - tmp);
221 KdOffscreenValidate (pScreen);
223 DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x\n", size, area->offset));
224 return area;
227 void
228 KdOffscreenSwapOut (ScreenPtr pScreen)
230 KdScreenPriv (pScreen);
232 KdOffscreenValidate (pScreen);
233 /* loop until a single free area spans the space */
234 for (;;)
236 KdOffscreenArea *area = pScreenPriv->off_screen_areas;
238 if (!area)
239 break;
240 if (area->state == KdOffscreenAvail)
242 area = area->next;
243 if (!area)
244 break;
246 assert (area->state != KdOffscreenAvail);
247 (void) KdOffscreenKickOut (pScreen, area);
248 KdOffscreenValidate (pScreen);
250 KdOffscreenValidate (pScreen);
251 KdOffscreenFini (pScreen);
254 void
255 KdOffscreenSwapIn (ScreenPtr pScreen)
257 KdOffscreenInit (pScreen);
260 /* merge the next free area into this one */
261 static void
262 KdOffscreenMerge (KdOffscreenArea *area)
264 KdOffscreenArea *next = area->next;
266 /* account for space */
267 area->size += next->size;
268 /* frob pointer */
269 area->next = next->next;
270 xfree (next);
273 KdOffscreenArea *
274 KdOffscreenFree (ScreenPtr pScreen, KdOffscreenArea *area)
276 KdScreenPriv(pScreen);
277 KdOffscreenArea *next = area->next;
278 KdOffscreenArea *prev;
280 DBG_OFFSCREEN (("Free 0x%x -> 0x%x\n", area->size, area->offset));
281 KdOffscreenValidate (pScreen);
283 area->state = KdOffscreenAvail;
284 area->save = 0;
285 area->offset = area->save_offset;
286 area->score = 0;
289 * Find previous area
291 if (area == pScreenPriv->off_screen_areas)
292 prev = 0;
293 else
294 for (prev = pScreenPriv->off_screen_areas; prev; prev = prev->next)
295 if (prev->next == area)
296 break;
298 /* link with next area if free */
299 if (next && next->state == KdOffscreenAvail)
300 KdOffscreenMerge (area);
302 /* link with prev area if free */
303 if (prev && prev->state == KdOffscreenAvail)
305 area = prev;
306 KdOffscreenMerge (area);
309 KdOffscreenValidate (pScreen);
310 return area;
313 void
314 KdOffscreenMarkUsed (PixmapPtr pPixmap)
316 KaaPixmapPriv (pPixmap);
317 KdScreenPriv (pPixmap->drawable.pScreen);
318 static int iter = 0;
320 if (!pKaaPixmap->area)
321 return;
323 /* The numbers here are arbitrary. We may want to tune these. */
324 pKaaPixmap->area->score += 100;
325 if (++iter == 10) {
326 KdOffscreenArea *area;
327 for (area = pScreenPriv->off_screen_areas; area != NULL;
328 area = area->next)
330 if (area->state == KdOffscreenRemovable)
331 area->score = (area->score * 7) / 8;
336 Bool
337 KdOffscreenInit (ScreenPtr pScreen)
339 KdScreenPriv (pScreen);
340 KdOffscreenArea *area;
342 /* Allocate a big free area */
343 area = xalloc (sizeof (KdOffscreenArea));
345 if (!area)
346 return FALSE;
348 area->state = KdOffscreenAvail;
349 area->offset = pScreenPriv->screen->off_screen_base;
350 area->size = pScreenPriv->screen->memory_size - area->offset;
351 area->save = 0;
352 area->next = NULL;
353 area->score = 0;
355 /* Add it to the free areas */
356 pScreenPriv->off_screen_areas = area;
358 KdOffscreenValidate (pScreen);
360 return TRUE;
363 void
364 KdOffscreenFini (ScreenPtr pScreen)
366 KdScreenPriv (pScreen);
367 KdOffscreenArea *area;
369 /* just free all of the area records */
370 while ((area = pScreenPriv->off_screen_areas))
372 pScreenPriv->off_screen_areas = area->next;
373 xfree (area);