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
86	if (!msg_suppress_1)
87		Con_Printf ("%ik surface cache\n", size/1024);
88
89	sc_size = size - GUARDSIZE;
90	sc_base = (surfcache_t *)buffer;
91	sc_rover = sc_base;
92
93	sc_base->next = NULL;
94	sc_base->owner = NULL;
95	sc_base->size = sc_size;
96
97	D_ClearCacheGuard ();
98}
99
100
101/*
102==================
103D_FlushCaches
104==================
105*/
106void D_FlushCaches (void)
107{
108	surfcache_t     *c;
109
110	if (!sc_base)
111		return;
112
113	for (c = sc_base ; c ; c = c->next)
114	{
115		if (c->owner)
116			*c->owner = NULL;
117	}
118
119	sc_rover = sc_base;
120	sc_base->next = NULL;
121	sc_base->owner = NULL;
122	sc_base->size = sc_size;
123}
124
125/*
126=================
127D_SCAlloc
128=================
129*/
130surfcache_t     *D_SCAlloc (int width, int size)
131{
132	surfcache_t             *new;
133	qboolean                wrapped_this_time;
134
135	if ((width < 0) || (width > 256))
136		Sys_Error ("D_SCAlloc: bad cache width %d\n", width);
137
138	if ((size <= 0) || (size > 0x10000))
139		Sys_Error ("D_SCAlloc: bad cache size %d\n", size);
140
141	size = (int)&((surfcache_t *)0)->data[size];
142	size = (size + 3) & ~3;
143	if (size > sc_size)
144		Sys_Error ("D_SCAlloc: %i > cache size",size);
145
146// if there is not size bytes after the rover, reset to the start
147	wrapped_this_time = false;
148
149	if ( !sc_rover || (byte *)sc_rover - (byte *)sc_base > sc_size - size)
150	{
151		if (sc_rover)
152		{
153			wrapped_this_time = true;
154		}
155		sc_rover = sc_base;
156	}
157
158// colect and free surfcache_t blocks until the rover block is large enough
159	new = sc_rover;
160	if (sc_rover->owner)
161		*sc_rover->owner = NULL;
162
163	while (new->size < size)
164	{
165	// free another
166		sc_rover = sc_rover->next;
167		if (!sc_rover)
168			Sys_Error ("D_SCAlloc: hit the end of memory");
169		if (sc_rover->owner)
170			*sc_rover->owner = NULL;
171
172		new->size += sc_rover->size;
173		new->next = sc_rover->next;
174	}
175
176// create a fragment out of any leftovers
177	if (new->size - size > 256)
178	{
179		sc_rover = (surfcache_t *)( (byte *)new + size);
180		sc_rover->size = new->size - size;
181		sc_rover->next = new->next;
182		sc_rover->width = 0;
183		sc_rover->owner = NULL;
184		new->next = sc_rover;
185		new->size = size;
186	}
187	else
188		sc_rover = new->next;
189
190	new->width = width;
191// DEBUG
192	if (width > 0)
193		new->height = (size - sizeof(*new) + sizeof(new->data)) / width;
194
195	new->owner = NULL;              // should be set properly after return
196
197	if (d_roverwrapped)
198	{
199		if (wrapped_this_time || (sc_rover >= d_initial_rover))
200			r_cache_thrash = true;
201	}
202	else if (wrapped_this_time)
203	{
204		d_roverwrapped = true;
205	}
206
207D_CheckCacheGuard ();   // DEBUG
208	return new;
209}
210
211
212/*
213=================
214D_SCDump
215=================
216*/
217void D_SCDump (void)
218{
219	surfcache_t             *test;
220
221	for (test = sc_base ; test ; test = test->next)
222	{
223		if (test == sc_rover)
224			Sys_Printf ("ROVER:\n");
225		printf ("%p : %i bytes     %i width\n",test, test->size, test->width);
226	}
227}
228
229//=============================================================================
230
231// if the num is not a power of 2, assume it will not repeat
232
233int     MaskForNum (int num)
234{
235	if (num==128)
236		return 127;
237	if (num==64)
238		return 63;
239	if (num==32)
240		return 31;
241	if (num==16)
242		return 15;
243	return 255;
244}
245
246int D_log2 (int num)
247{
248	int     c;
249
250	c = 0;
251
252	while (num>>=1)
253		c++;
254	return c;
255}
256
257//=============================================================================
258
259/*
260================
261D_CacheSurface
262================
263*/
264surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel)
265{
266	surfcache_t     *cache;
267
268//
269// if the surface is animating or flashing, flush the cache
270//
271	r_drawsurf.texture = R_TextureAnimation (surface->texinfo->texture);
272	r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]];
273	r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]];
274	r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]];
275	r_drawsurf.lightadj[3] = d_lightstylevalue[surface->styles[3]];
276
277//
278// see if the cache holds apropriate data
279//
280	cache = surface->cachespots[miplevel];
281
282	if (cache && !cache->dlight && surface->dlightframe != r_framecount
283			&& cache->texture == r_drawsurf.texture
284			&& cache->lightadj[0] == r_drawsurf.lightadj[0]
285			&& cache->lightadj[1] == r_drawsurf.lightadj[1]
286			&& cache->lightadj[2] == r_drawsurf.lightadj[2]
287			&& cache->lightadj[3] == r_drawsurf.lightadj[3] )
288		return cache;
289
290//
291// determine shape of surface
292//
293	surfscale = 1.0 / (1<<miplevel);
294	r_drawsurf.surfmip = miplevel;
295	r_drawsurf.surfwidth = surface->extents[0] >> miplevel;
296	r_drawsurf.rowbytes = r_drawsurf.surfwidth;
297	r_drawsurf.surfheight = surface->extents[1] >> miplevel;
298
299//
300// allocate memory if needed
301//
302	if (!cache)     // if a texture just animated, don't reallocate it
303	{
304		cache = D_SCAlloc (r_drawsurf.surfwidth,
305						   r_drawsurf.surfwidth * r_drawsurf.surfheight);
306		surface->cachespots[miplevel] = cache;
307		cache->owner = &surface->cachespots[miplevel];
308		cache->mipscale = surfscale;
309	}
310
311	if (surface->dlightframe == r_framecount)
312		cache->dlight = 1;
313	else
314		cache->dlight = 0;
315
316	r_drawsurf.surfdat = (pixel_t *)cache->data;
317
318	cache->texture = r_drawsurf.texture;
319	cache->lightadj[0] = r_drawsurf.lightadj[0];
320	cache->lightadj[1] = r_drawsurf.lightadj[1];
321	cache->lightadj[2] = r_drawsurf.lightadj[2];
322	cache->lightadj[3] = r_drawsurf.lightadj[3];
323
324//
325// draw and light the surface texture
326//
327	r_drawsurf.surf = surface;
328
329	c_surf++;
330	R_DrawSurface ();
331
332	return surface->cachespots[miplevel];
333}
334
335
336