1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20// d_surf.c: rasterization driver surface heap manager
21
22#include "quakedef.h"
23#include "d_local.h"
24#include "r_local.h"
25
26float           surfscale;
27qboolean        r_cache_thrash;         // set if surface cache is thrashing
28
29int                                     sc_size;
30surfcache_t                     *sc_rover, *sc_base;
31
32#define GUARDSIZE       4
33
34
35int     D_SurfaceCacheForRes (int width, int height)
36{
37	int             size, pix;
38
39	if (COM_CheckParm ("-surfcachesize"))
40	{
41		size = Q_atoi(com_argv[COM_CheckParm("-surfcachesize")+1]) * 1024;
42		return size;
43	}
44
45	size = SURFCACHE_SIZE_AT_320X200;
46
47	pix = width*height;
48	if (pix > 64000)
49		size += (pix-64000)*3;
50
51
52	return size;
53}
54
55void D_CheckCacheGuard (void)
56{
57	byte    *s;
58	int             i;
59
60	s = (byte *)sc_base + sc_size;
61	for (i=0 ; i<GUARDSIZE ; i++)
62		if (s[i] != (byte)i)
63			Sys_Error ("D_CheckCacheGuard: failed");
64}
65
66void D_ClearCacheGuard (void)
67{
68	byte    *s;
69	int             i;
70
71	s = (byte *)sc_base + sc_size;
72	for (i=0 ; i<GUARDSIZE ; i++)
73		s[i] = (byte)i;
74}
75
76
77/*
78================
79D_InitCaches
80
81================
82*/
83void D_InitCaches (void *buffer, int size)
84{
85//	if (!msg_suppress_1)
86//		Con_Printf ("%ik surface cache\n", size/1024);
87
88	sc_size = size - GUARDSIZE;
89	sc_base = (surfcache_t *)buffer;
90	sc_rover = sc_base;
91
92	sc_base->next = NULL;
93	sc_base->owner = NULL;
94	sc_base->size = sc_size;
95
96	D_ClearCacheGuard ();
97}
98
99
100/*
101==================
102D_FlushCaches
103==================
104*/
105void D_FlushCaches (void)
106{
107	surfcache_t     *c;
108
109	if (!sc_base)
110		return;
111
112	for (c = sc_base ; c ; c = c->next)
113	{
114		if (c->owner)
115			*c->owner = NULL;
116	}
117
118	sc_rover = sc_base;
119	sc_base->next = NULL;
120	sc_base->owner = NULL;
121	sc_base->size = sc_size;
122}
123
124/*
125=================
126D_SCAlloc
127=================
128*/
129surfcache_t     *D_SCAlloc (int width, int size)
130{
131	surfcache_t             *new;
132	qboolean                wrapped_this_time;
133
134	if ((width < 0) || (width > 256))
135		Sys_Error ("D_SCAlloc: bad cache width %d\n", width);
136
137	if ((size <= 0) || (size > 0x10000))
138		Sys_Error ("D_SCAlloc: bad cache size %d\n", size);
139
140#ifdef __alpha__
141	size = (int)((long)&((surfcache_t *)0)->data[size]);
142#else
143	size = (int)&((surfcache_t *)0)->data[size];
144#endif
145	size = (size + 3) & ~3;
146	if (size > sc_size)
147		Sys_Error ("D_SCAlloc: %i > cache size",size);
148
149// if there is not size bytes after the rover, reset to the start
150	wrapped_this_time = false;
151
152	if ( !sc_rover || (byte *)sc_rover - (byte *)sc_base > sc_size - size)
153	{
154		if (sc_rover)
155		{
156			wrapped_this_time = true;
157		}
158		sc_rover = sc_base;
159	}
160
161// colect and free surfcache_t blocks until the rover block is large enough
162	new = sc_rover;
163	if (sc_rover->owner)
164		*sc_rover->owner = NULL;
165
166	while (new->size < size)
167	{
168	// free another
169		sc_rover = sc_rover->next;
170		if (!sc_rover)
171			Sys_Error ("D_SCAlloc: hit the end of memory");
172		if (sc_rover->owner)
173			*sc_rover->owner = NULL;
174
175		new->size += sc_rover->size;
176		new->next = sc_rover->next;
177	}
178
179// create a fragment out of any leftovers
180	if (new->size - size > 256)
181	{
182		sc_rover = (surfcache_t *)( (byte *)new + size);
183		sc_rover->size = new->size - size;
184		sc_rover->next = new->next;
185		sc_rover->width = 0;
186		sc_rover->owner = NULL;
187		new->next = sc_rover;
188		new->size = size;
189	}
190	else
191		sc_rover = new->next;
192
193	new->width = width;
194// DEBUG
195	if (width > 0)
196		new->height = (size - sizeof(*new) + sizeof(new->data)) / width;
197
198	new->owner = NULL;              // should be set properly after return
199
200	if (d_roverwrapped)
201	{
202		if (wrapped_this_time || (sc_rover >= d_initial_rover))
203			r_cache_thrash = true;
204	}
205	else if (wrapped_this_time)
206	{
207		d_roverwrapped = true;
208	}
209
210D_CheckCacheGuard ();   // DEBUG
211	return new;
212}
213
214
215/*
216=================
217D_SCDump
218=================
219*/
220void D_SCDump (void)
221{
222	surfcache_t             *test;
223
224	for (test = sc_base ; test ; test = test->next)
225	{
226		if (test == sc_rover)
227			Sys_Printf ("ROVER:\n");
228		printf ("%p : %i bytes     %i width\n",test, test->size, test->width);
229	}
230}
231
232//=============================================================================
233
234// if the num is not a power of 2, assume it will not repeat
235
236int     MaskForNum (int num)
237{
238	if (num==128)
239		return 127;
240	if (num==64)
241		return 63;
242	if (num==32)
243		return 31;
244	if (num==16)
245		return 15;
246	return 255;
247}
248
249int D_log2 (int num)
250{
251	int     c;
252
253	c = 0;
254
255	while (num>>=1)
256		c++;
257	return c;
258}
259
260//=============================================================================
261
262/*
263================
264D_CacheSurface
265================
266*/
267surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel)
268{
269	surfcache_t     *cache;
270
271//
272// if the surface is animating or flashing, flush the cache
273//
274	r_drawsurf.texture = R_TextureAnimation (surface->texinfo->texture);
275	r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]];
276	r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]];
277	r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]];
278	r_drawsurf.lightadj[3] = d_lightstylevalue[surface->styles[3]];
279
280//
281// see if the cache holds apropriate data
282//
283	cache = surface->cachespots[miplevel];
284
285	if (cache && !cache->dlight && surface->dlightframe != r_framecount
286			&& cache->texture == r_drawsurf.texture
287			&& cache->lightadj[0] == r_drawsurf.lightadj[0]
288			&& cache->lightadj[1] == r_drawsurf.lightadj[1]
289			&& cache->lightadj[2] == r_drawsurf.lightadj[2]
290			&& cache->lightadj[3] == r_drawsurf.lightadj[3] )
291		return cache;
292
293//
294// determine shape of surface
295//
296	surfscale = 1.0 / (1<<miplevel);
297	r_drawsurf.surfmip = miplevel;
298	r_drawsurf.surfwidth = surface->extents[0] >> miplevel;
299	r_drawsurf.rowbytes = r_drawsurf.surfwidth;
300	r_drawsurf.surfheight = surface->extents[1] >> miplevel;
301
302//
303// allocate memory if needed
304//
305	if (!cache)     // if a texture just animated, don't reallocate it
306	{
307		cache = D_SCAlloc (r_drawsurf.surfwidth,
308						   r_drawsurf.surfwidth * r_drawsurf.surfheight);
309		surface->cachespots[miplevel] = cache;
310		cache->owner = &surface->cachespots[miplevel];
311		cache->mipscale = surfscale;
312	}
313
314	if (surface->dlightframe == r_framecount)
315		cache->dlight = 1;
316	else
317		cache->dlight = 0;
318
319	r_drawsurf.surfdat = (pixel_t *)cache->data;
320
321	cache->texture = r_drawsurf.texture;
322	cache->lightadj[0] = r_drawsurf.lightadj[0];
323	cache->lightadj[1] = r_drawsurf.lightadj[1];
324	cache->lightadj[2] = r_drawsurf.lightadj[2];
325	cache->lightadj[3] = r_drawsurf.lightadj[3];
326
327//
328// draw and light the surface texture
329//
330	r_drawsurf.surf = surface;
331
332	c_surf++;
333	R_DrawSurface ();
334
335	return surface->cachespots[miplevel];
336}
337
338
339