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 #include <kdrive-config.h>
29 #define DEBUG_OFFSCREEN 0
31 #define DBG_OFFSCREEN(a) ErrorF a
33 #define DBG_OFFSCREEN(a)
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
)
47 assert (prev
->offset
+ prev
->size
== area
->offset
);
51 assert (prev
->offset
+ prev
->size
== pScreenPriv
->screen
->memory_size
);
54 #define KdOffscreenValidate(s)
57 static KdOffscreenArea
*
58 KdOffscreenKickOut (ScreenPtr pScreen
, KdOffscreenArea
*area
)
61 (*area
->save
) (pScreen
, area
);
62 return KdOffscreenFree (pScreen
, area
);
66 KdOffscreenAlloc (ScreenPtr pScreen
, int size
, int align
,
68 KdOffscreenSaveProc save
,
71 KdOffscreenArea
*area
, *begin
, *best
;
72 KdScreenPriv (pScreen
);
73 int tmp
, real_size
= 0, best_score
;
75 KdOffscreenValidate (pScreen
);
81 DBG_OFFSCREEN (("Alloc 0x%x -> EMPTY\n", size
));
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
));
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
)
99 /* adjust size to match alignment requirement */
101 tmp
= area
->offset
% align
;
103 real_size
+= (align
- tmp
);
106 if (real_size
<= area
->size
)
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 */
121 for (begin
= pScreenPriv
->off_screen_areas
; begin
!= NULL
;
125 KdOffscreenArea
*scan
;
127 if (begin
->state
== KdOffscreenLocked
)
130 /* adjust size to match alignment requirement */
132 tmp
= begin
->offset
% align
;
134 real_size
+= (align
- tmp
);
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. */
146 /* Score should only be non-zero for KdOffscreenRemovable */
147 score
+= scan
->score
;
149 if (avail
>= real_size
)
152 /* Is it the best option we've found so far? */
153 if (avail
>= real_size
&& score
< best_score
) {
161 DBG_OFFSCREEN (("Alloc 0x%x -> NOSPACE\n", size
));
162 /* Could not allocate memory */
163 KdOffscreenValidate (pScreen
);
167 /* adjust size to match alignment requirement */
169 tmp
= begin
->offset
% align
;
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
));
194 new_area
->offset
= area
->offset
+ real_size
;
195 new_area
->size
= area
->size
- real_size
;
196 new_area
->state
= KdOffscreenAvail
;
199 new_area
->next
= area
->next
;
200 area
->next
= new_area
;
201 area
->size
= real_size
;
204 * Mark this area as in use
207 area
->state
= KdOffscreenLocked
;
209 area
->state
= KdOffscreenRemovable
;
210 area
->privData
= privData
;
214 area
->save_offset
= area
->offset
;
216 int tmp
= area
->offset
% align
;
218 area
->offset
+= (align
- tmp
);
221 KdOffscreenValidate (pScreen
);
223 DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x\n", size
, area
->offset
));
228 KdOffscreenSwapOut (ScreenPtr pScreen
)
230 KdScreenPriv (pScreen
);
232 KdOffscreenValidate (pScreen
);
233 /* loop until a single free area spans the space */
236 KdOffscreenArea
*area
= pScreenPriv
->off_screen_areas
;
240 if (area
->state
== KdOffscreenAvail
)
246 assert (area
->state
!= KdOffscreenAvail
);
247 (void) KdOffscreenKickOut (pScreen
, area
);
248 KdOffscreenValidate (pScreen
);
250 KdOffscreenValidate (pScreen
);
251 KdOffscreenFini (pScreen
);
255 KdOffscreenSwapIn (ScreenPtr pScreen
)
257 KdOffscreenInit (pScreen
);
260 /* merge the next free area into this one */
262 KdOffscreenMerge (KdOffscreenArea
*area
)
264 KdOffscreenArea
*next
= area
->next
;
266 /* account for space */
267 area
->size
+= next
->size
;
269 area
->next
= next
->next
;
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
;
285 area
->offset
= area
->save_offset
;
291 if (area
== pScreenPriv
->off_screen_areas
)
294 for (prev
= pScreenPriv
->off_screen_areas
; prev
; prev
= prev
->next
)
295 if (prev
->next
== area
)
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
)
306 KdOffscreenMerge (area
);
309 KdOffscreenValidate (pScreen
);
314 KdOffscreenMarkUsed (PixmapPtr pPixmap
)
316 KaaPixmapPriv (pPixmap
);
317 KdScreenPriv (pPixmap
->drawable
.pScreen
);
320 if (!pKaaPixmap
->area
)
323 /* The numbers here are arbitrary. We may want to tune these. */
324 pKaaPixmap
->area
->score
+= 100;
326 KdOffscreenArea
*area
;
327 for (area
= pScreenPriv
->off_screen_areas
; area
!= NULL
;
330 if (area
->state
== KdOffscreenRemovable
)
331 area
->score
= (area
->score
* 7) / 8;
337 KdOffscreenInit (ScreenPtr pScreen
)
339 KdScreenPriv (pScreen
);
340 KdOffscreenArea
*area
;
342 /* Allocate a big free area */
343 area
= xalloc (sizeof (KdOffscreenArea
));
348 area
->state
= KdOffscreenAvail
;
349 area
->offset
= pScreenPriv
->screen
->off_screen_base
;
350 area
->size
= pScreenPriv
->screen
->memory_size
- area
->offset
;
355 /* Add it to the free areas */
356 pScreenPriv
->off_screen_areas
= area
;
358 KdOffscreenValidate (pScreen
);
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
;