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