Skip to content
Snippets Groups Projects
Select Git revision
  • awayview-fix2
  • next default protected
  • expos-ability
  • offcenter
  • ringflag
  • team-height
  • skinfix
  • heart-damage
  • ring-fix
  • blockedexposed
  • expos-sphere
  • groundflip
  • exposition
  • master
  • armpit
  • canbustpeanuts
  • jinglebells
  • moremenusounds
  • uncap-palettes
  • udmf-wallscale
  • SRB2_release_2.2.8
  • SRB2_release_2.2.7
  • SRB2_release_2.2.6
  • SRB2_release_2.2.5
  • SRB2_release_2.2.4
  • SRB2_release_2.2.3
  • SRB2_release_2.2.2
  • SRB2_release_2.2.1
  • SRB2_release_2.2.0
  • SRB2_release_2.1.25
  • SRB2_release_2.1.24
  • SRB2_release_2.1.23
  • SRB2_release_2.1.22
  • SRB2_release_2.1.21
  • SRB2_release_2.1.20
  • SRB2_release_2.1.19
  • SRB2_release_2.1.18
  • td-release-v1.0.0
  • SRB2_release_2.1.17
  • SRB2_release_2.1.16a
40 results

hw_cache.c

Blame
  • Forked from STJr / SRB2
    16299 commits behind, 8 commits ahead of the upstream repository.
    hw_cache.c 30.19 KiB
    // Emacs style mode select   -*- C++ -*-
    //-----------------------------------------------------------------------------
    //
    // Copyright (C) 1998-2000 by DooM Legacy Team.
    //
    // This program is free software; you can redistribute it and/or
    // modify it under the terms of the GNU General Public License
    // as published by the Free Software Foundation; either version 2
    // of the License, or (at your option) any later version.
    //
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    //
    //-----------------------------------------------------------------------------
    /// \file
    /// \brief load and convert graphics to the hardware format
    
    #include "../doomdef.h"
    
    #ifdef HWRENDER
    #include "hw_glob.h"
    #include "hw_drv.h"
    
    #include "../doomstat.h"    //gamemode
    #include "../i_video.h"     //rendermode
    #include "../r_data.h"
    #include "../w_wad.h"
    #include "../z_zone.h"
    #include "../v_video.h"
    #include "../r_draw.h"
    
    //Hurdler: 25/04/2000: used for new colormap code in hardware mode
    //static UINT8 *gr_colormap = NULL; // by default it must be NULL ! (because colormap tables are not initialized)
    boolean firetranslucent = false;
    
    // Values set after a call to HWR_ResizeBlock()
    static INT32 blocksize, blockwidth, blockheight;
    
    INT32 patchformat = GR_TEXFMT_AP_88; // use alpha for holes
    INT32 textureformat = GR_TEXFMT_P_8; // use chromakey for hole
    
    // sprite, use alpha and chroma key for hole
    static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
    	INT32 pblockwidth, INT32 pblockheight, INT32 blockmodulo,
    	INT32 ptexturewidth, INT32 ptextureheight,
    	INT32 originx, INT32 originy, // where to draw patch in surface block
    	const patch_t *realpatch, INT32 bpp)
    {
    	INT32 x, x1, x2;
    	INT32 col, ncols;
    	fixed_t xfrac, xfracstep;
    	fixed_t yfrac, yfracstep, position, count;
    	fixed_t scale_y;
    	RGBA_t colortemp;
    	UINT8 *dest;
    	const UINT8 *source;
    	const column_t *patchcol;
    	UINT8 alpha;
    	UINT8 *block = mipmap->grInfo.data;
    	UINT8 texel;
    	UINT16 texelu16;
    
    	x1 = originx;
    	x2 = x1 + SHORT(realpatch->width);
    
    	if (x1 < 0)
    		x = 0;
    	else
    		x = x1;
    
    	if (x2 > ptexturewidth)
    		x2 = ptexturewidth;
    
    	if (!ptexturewidth)
    		return;
    
    	col = x * pblockwidth / ptexturewidth;
    	ncols = ((x2 - x) * pblockwidth) / ptexturewidth;
    
    /*
    	CONS_Debug(DBG_RENDER, "patch %dx%d texture %dx%d block %dx%d\n", SHORT(realpatch->width),
    															SHORT(realpatch->height),
    															ptexturewidth,
    															textureheight,
    															pblockwidth,pblockheight);
    	CONS_Debug(DBG_RENDER, "      col %d ncols %d x %d\n", col, ncols, x);
    */
    
    	// source advance
    	xfrac = 0;
    	if (x1 < 0)
    		xfrac = -x1<<FRACBITS;
    
    	xfracstep = (ptexturewidth << FRACBITS) / pblockwidth;
    	yfracstep = (ptextureheight<< FRACBITS) / pblockheight;
    	if (bpp < 1 || bpp > 4)
    		I_Error("HWR_DrawPatchInCache: no drawer defined for this bpp (%d)\n",bpp);
    
    	for (block += col*bpp; ncols--; block += bpp, xfrac += xfracstep)
    	{
    		INT32 topdelta, prevdelta = -1;
    		patchcol = (const column_t *)((const UINT8 *)realpatch
    		 + LONG(realpatch->columnofs[xfrac>>FRACBITS]));
    
    		scale_y = (pblockheight << FRACBITS) / ptextureheight;
    
    		while (patchcol->topdelta != 0xff)
    		{
    			topdelta = patchcol->topdelta;
    			if (topdelta <= prevdelta)
    				topdelta += prevdelta;
    			prevdelta = topdelta;
    			source = (const UINT8 *)patchcol + 3;
    			count  = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
    			position = originy + topdelta;
    
    			yfrac = 0;
    			//yfracstep = (patchcol->length << FRACBITS) / count;
    			if (position < 0)
    			{
    				yfrac = -position<<FRACBITS;
    				count += (((position * scale_y) + (FRACUNIT/2)) >> FRACBITS);
    				position = 0;
    			}
    
    			position = ((position * scale_y) + (FRACUNIT/2)) >> FRACBITS;
    
    			if (position < 0)
    				position = 0;
    
    			if (position + count >= pblockheight)
    				count = pblockheight - position;
    
    			dest = block + (position*blockmodulo);
    			while (count > 0)
    			{
    				count--;
    
    				texel = source[yfrac>>FRACBITS];
    
    				if (firetranslucent && (transtables[(texel<<8)+0x40000]!=texel))
    					alpha = 0x80;
    				else
    					alpha = 0xff;
    
    				//Hurdler: not perfect, but better than holes
    				if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX && (mipmap->flags & TF_CHROMAKEYED))
    					texel = HWR_CHROMAKEY_EQUIVALENTCOLORINDEX;
    				//Hurdler: 25/04/2000: now support colormap in hardware mode
    				else if (mipmap->colormap)
    					texel = mipmap->colormap[texel];
    
    				// hope compiler will get this switch out of the loops (dreams...)
    				// gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)
    				// Alam: SRB2 uses Mingw, HUGS
    				switch (bpp)
    				{
    					case 2 : texelu16 = (UINT16)((alpha<<8) | texel);
    					         memcpy(dest, &texelu16, sizeof(UINT16));
    					         break;
    					case 3 : colortemp = V_GetColor(texel);
    					         memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
    					         break;
    					case 4 : colortemp = V_GetColor(texel);
    					         colortemp.s.alpha = alpha;
    					         memcpy(dest, &colortemp, sizeof(RGBA_t));
    					         break;
    					// default is 1
    					default: *dest = texel;
    					         break;
    				}
    
    				dest += blockmodulo;
    				yfrac += yfracstep;
    			}
    			patchcol = (const column_t *)((const UINT8 *)patchcol + patchcol->length + 4);
    		}
    	}
    }
    
    
    // resize the patch to be 3dfx compliant
    // set : blocksize = blockwidth * blockheight  (no bpp used)
    //       blockwidth
    //       blockheight
    //note :  8bit (1 byte per pixel) palettized format
    static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight,
    	GrTexInfo *grInfo)
    {
    	//   Build the full textures from patches.
    	static const GrLOD_t gr_lods[9] =
    	{
    		GR_LOD_LOG2_256,
    		GR_LOD_LOG2_128,
    		GR_LOD_LOG2_64,
    		GR_LOD_LOG2_32,
    		GR_LOD_LOG2_16,
    		GR_LOD_LOG2_8,
    		GR_LOD_LOG2_4,
    		GR_LOD_LOG2_2,
    		GR_LOD_LOG2_1
    	};
    
    	typedef struct
    	{
    		GrAspectRatio_t aspect;
    		float           max_s;
    		float           max_t;
    	} booring_aspect_t;
    
    	static const booring_aspect_t gr_aspects[8] =
    	{
    		{GR_ASPECT_LOG2_1x1, 255, 255},
    		{GR_ASPECT_LOG2_2x1, 255, 127},
    		{GR_ASPECT_LOG2_4x1, 255,  63},
    		{GR_ASPECT_LOG2_8x1, 255,  31},
    
    		{GR_ASPECT_LOG2_1x1, 255, 255},
    		{GR_ASPECT_LOG2_1x2, 127, 255},
    		{GR_ASPECT_LOG2_1x4,  63, 255},
    		{GR_ASPECT_LOG2_1x8,  31, 255}
    	};
    
    	INT32     j,k;
    	INT32     max,min;
    
    	// find a power of 2 width/height
    	if (cv_grrounddown.value)
    	{
    		blockwidth = 256;
    		while (originalwidth < blockwidth)
    			blockwidth >>= 1;
    		if (blockwidth < 1)
    			I_Error("3D GenerateTexture : too small");
    
    		blockheight = 256;
    		while (originalheight < blockheight)
    			blockheight >>= 1;
    		if (blockheight < 1)
    			I_Error("3D GenerateTexture : too small");
    	}
    	else if (cv_voodoocompatibility.value)
    	{
    		if (originalwidth > 256 || originalheight > 256)
    		{
    			blockwidth = 256;
    			while (originalwidth < blockwidth)
    				blockwidth >>= 1;
    			if (blockwidth < 1)
    				I_Error("3D GenerateTexture : too small");
    
    			blockheight = 256;
    			while (originalheight < blockheight)
    				blockheight >>= 1;
    			if (blockheight < 1)
    				I_Error("3D GenerateTexture : too small");
    		}
    		else
    		{
    			//size up to nearest power of 2
    			blockwidth = 1;
    			while (blockwidth < originalwidth)
    				blockwidth <<= 1;
    			// scale down the original graphics to fit in 256
    			if (blockwidth > 256)
    				blockwidth = 256;
    				//I_Error("3D GenerateTexture : too big");
    
    			//size up to nearest power of 2
    			blockheight = 1;
    			while (blockheight < originalheight)
    				blockheight <<= 1;
    			// scale down the original graphics to fit in 256
    			if (blockheight > 256)
    				blockheight = 255;
    				//I_Error("3D GenerateTexture : too big");
    		}
    	}
    	else
    	{
    		//size up to nearest power of 2
    		blockwidth = 1;
    		while (blockwidth < originalwidth)
    			blockwidth <<= 1;
    		// scale down the original graphics to fit in 256
    		if (blockwidth > 2048)
    			blockwidth = 2048;
    			//I_Error("3D GenerateTexture : too big");
    
    		//size up to nearest power of 2
    		blockheight = 1;
    		while (blockheight < originalheight)
    			blockheight <<= 1;
    		// scale down the original graphics to fit in 256
    		if (blockheight > 2048)
    			blockheight = 2048;
    			//I_Error("3D GenerateTexture : too big");
    	}
    
    	// do the boring LOD stuff.. blech!
    	if (blockwidth >= blockheight)
    	{
    		max = blockwidth;
    		min = blockheight;
    	}
    	else
    	{
    		max = blockheight;
    		min = blockwidth;
    	}
    
    	for (k = 2048, j = 0; k > max; j++)
    		k>>=1;
    	grInfo->smallLodLog2 = gr_lods[j];
    	grInfo->largeLodLog2 = gr_lods[j];
    
    	for (k = max, j = 0; k > min && j < 4; j++)
    		k>>=1;
    	// aspect ratio too small for 3Dfx (eg: 8x128 is 1x16 : use 1x8)
    	if (j == 4)
    	{
    		j = 3;
    		//CONS_Debug(DBG_RENDER, "HWR_ResizeBlock : bad aspect ratio %dx%d\n", blockwidth,blockheight);
    		if (blockwidth < blockheight)
    			blockwidth = max>>3;
    		else
    			blockheight = max>>3;
    	}
    	if (blockwidth < blockheight)
    		j += 4;
    	grInfo->aspectRatioLog2 = gr_aspects[j].aspect;
    
    	blocksize = blockwidth * blockheight;
    
    	//CONS_Debug(DBG_RENDER, "Width is %d, Height is %d\n", blockwidth, blockheight);
    }
    
    
    static const INT32 format2bpp[16] =
    {
    	0, //0
    	0, //1
    	1, //2  GR_TEXFMT_ALPHA_8
    	1, //3  GR_TEXFMT_INTENSITY_8
    	1, //4  GR_TEXFMT_ALPHA_INTENSITY_44
    	1, //5  GR_TEXFMT_P_8
    	4, //6  GR_RGBA
    	0, //7
    	0, //8
    	0, //9
    	2, //10 GR_TEXFMT_RGB_565
    	2, //11 GR_TEXFMT_ARGB_1555
    	2, //12 GR_TEXFMT_ARGB_4444
    	2, //13 GR_TEXFMT_ALPHA_INTENSITY_88
    	2, //14 GR_TEXFMT_AP_88
    };
    
    static UINT8 *MakeBlock(GLMipmap_t *grMipmap)
    {
    	UINT8 *block;
    	INT32 bpp, i;
    	UINT16 bu16 = ((0x00 <<8) | HWR_CHROMAKEY_EQUIVALENTCOLORINDEX);
    
    	bpp =  format2bpp[grMipmap->grInfo.format];
    	block = Z_Malloc(blocksize*bpp, PU_HWRCACHE, &(grMipmap->grInfo.data));
    
    	switch (bpp)
    	{
    		case 1: memset(block, HWR_PATCHES_CHROMAKEY_COLORINDEX, blocksize); break;
    		case 2:
    				// fill background with chromakey, alpha = 0
    				for (i = 0; i < blocksize; i++)
    				//[segabor]
    					memcpy(block+i*sizeof(UINT16), &bu16, sizeof(UINT16));
    				break;
    		case 4: memset(block, 0x00, blocksize*sizeof(UINT32)); break;
    	}
    
    	return block;
    }
    
    //
    // Create a composite texture from patches, adapt the texture size to a power of 2
    // height and width for the hardware texture cache.
    //
    static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex)
    {
    	UINT8 *block;
    	texture_t *texture;
    	texpatch_t *patch;
    	patch_t *realpatch;
    
    	INT32 i;
    	boolean skyspecial = false; //poor hack for Legacy large skies..
    
    	texture = textures[texnum];
    
    	// hack the Legacy skies..
    	if (texture->name[0] == 'S' &&
    	    texture->name[1] == 'K' &&
    	    texture->name[2] == 'Y' &&
    	    (texture->name[4] == 0 ||
    	     texture->name[5] == 0)
    	   )
    	{
    		skyspecial = true;
    		grtex->mipmap.flags = TF_WRAPXY; // don't use the chromakey for sky
    	}
    	else
    		grtex->mipmap.flags = TF_CHROMAKEYED | TF_WRAPXY;
    
    	HWR_ResizeBlock (texture->width, texture->height, &grtex->mipmap.grInfo);
    	grtex->mipmap.width = (UINT16)blockwidth;
    	grtex->mipmap.height = (UINT16)blockheight;
    	grtex->mipmap.grInfo.format = textureformat;
    
    	block = MakeBlock(&grtex->mipmap);
    
    	if (skyspecial) //Hurdler: not efficient, but better than holes in the sky (and it's done only at level loading)
    	{
    		INT32 j;
    		RGBA_t col;
    
    		col = V_GetColor(HWR_CHROMAKEY_EQUIVALENTCOLORINDEX);
    		for (j = 0; j < blockheight; j++)
    		{
    			for (i = 0; i < blockwidth; i++)
    			{
    				block[4*(j*blockwidth+i)+0] = col.s.red;
    				block[4*(j*blockwidth+i)+1] = col.s.green;
    				block[4*(j*blockwidth+i)+2] = col.s.blue;
    				block[4*(j*blockwidth+i)+3] = 0xff;
    			}
    		}
    	}
    
    	// Composite the columns together.
    	for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
    	{
    		realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
    		HWR_DrawPatchInCache(&grtex->mipmap,
    		                     blockwidth, blockheight,
    		                     blockwidth*format2bpp[grtex->mipmap.grInfo.format],
    		                     texture->width, texture->height,
    		                     patch->originx, patch->originy,
    		                     realpatch,
    		                     format2bpp[grtex->mipmap.grInfo.format]);
    		Z_Unlock(realpatch);
    	}
    	//Hurdler: not efficient at all but I don't remember exactly how HWR_DrawPatchInCache works :(
    	if (format2bpp[grtex->mipmap.grInfo.format]==4)
    	{
    		for (i = 3; i < blocksize*4; i += 4) // blocksize*4 because blocksize doesn't include the bpp
    		{
    			if (block[i] == 0)
    			{
    				grtex->mipmap.flags |= TF_TRANSPARENT;
    				break;
    			}
    		}
    	}
    
    	grtex->scaleX = 1.0f/(texture->width*FRACUNIT);
    	grtex->scaleY = 1.0f/(texture->height*FRACUNIT);
    }
    
    // patch may be NULL if grMipmap has been initialised already and makebitmap is false
    void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap)
    {
    	INT32 newwidth, newheight;
    
    	// don't do it twice (like a cache)
    	if (grMipmap->width == 0)
    	{
    		// save the original patch header so that the GLPatch can be casted
    		// into a standard patch_t struct and the existing code can get the
    		// orginal patch dimensions and offsets.
    		grPatch->width = SHORT(patch->width);
    		grPatch->height = SHORT(patch->height);
    		grPatch->leftoffset = SHORT(patch->leftoffset);
    		grPatch->topoffset = SHORT(patch->topoffset);
    
    		// find the good 3dfx size (boring spec)
    		HWR_ResizeBlock (SHORT(patch->width), SHORT(patch->height), &grMipmap->grInfo);
    		grMipmap->width = (UINT16)blockwidth;
    		grMipmap->height = (UINT16)blockheight;
    
    		// no wrap around, no chroma key
    		grMipmap->flags = 0;
    		// setup the texture info
    		grMipmap->grInfo.format = patchformat;
    	}
    	else
    	{
    		blockwidth = grMipmap->width;
    		blockheight = grMipmap->height;
    		blocksize = blockwidth * blockheight;
    	}
    
    	Z_Free(grMipmap->grInfo.data);
    	grMipmap->grInfo.data = NULL;
    
    	// if rounddown, rounddown patches as well as textures
    	if (cv_grrounddown.value)
    	{
    		newwidth = blockwidth;
    		newheight = blockheight;
    	}
    	else if (cv_voodoocompatibility.value) // Only scales down textures that exceed 256x256.
    	{
    		// no rounddown, do not size up patches, so they don't look 'scaled'
    		newwidth  = min(grPatch->width, blockwidth);
    		newheight = min(grPatch->height, blockheight);
    
    		if (newwidth > 256 || newheight > 256)
    		{
    			newwidth = blockwidth;
    			newheight = blockheight;
    		}
    	}
    	else
    	{
    		// no rounddown, do not size up patches, so they don't look 'scaled'
    		newwidth  = min(grPatch->width, blockwidth);
    		newheight = min(grPatch->height, blockheight);
    	}
    
    	if (makebitmap)
    	{
    		MakeBlock(grMipmap);
    
    		HWR_DrawPatchInCache(grMipmap,
    			newwidth, newheight,
    			blockwidth*format2bpp[grMipmap->grInfo.format],
    			grPatch->width, grPatch->height,
    			0, 0,
    			patch,
    			format2bpp[grMipmap->grInfo.format]);
    	}
    
    	grPatch->max_s = (float)newwidth / (float)blockwidth;
    	grPatch->max_t = (float)newheight / (float)blockheight;
    }
    
    
    // =================================================
    //             CACHING HANDLING
    // =================================================
    
    static size_t gr_numtextures;
    static GLTexture_t *gr_textures; // for ALL Doom textures
    
    void HWR_InitTextureCache(void)
    {
    	gr_numtextures = 0;
    	gr_textures = NULL;
    }
    
    
    // Callback function for HWR_FreeTextureCache.
    static void FreeMipmapColormap(INT32 patchnum, void *patch)
    {
    	GLPatch_t* const grpatch = patch;
    	(void)patchnum; //unused
    	while (grpatch->mipmap.nextcolormap)
    	{
    		GLMipmap_t *grmip = grpatch->mipmap.nextcolormap;
    		grpatch->mipmap.nextcolormap = grmip->nextcolormap;
    		if (grmip->grInfo.data) Z_Free(grmip->grInfo.data);
    		free(grmip);
    	}
    }
    
    void HWR_FreeTextureCache(void)
    {
    	INT32 i;
    	// free references to the textures
    	HWD.pfnClearMipMapCache();
    
    	// free all hardware-converted graphics cached in the heap
    	// our gool is only the textures since user of the texture is the texture cache
    	Z_FreeTags(PU_HWRCACHE, PU_HWRCACHE);
    	Z_FreeTags(PU_HWRCACHE_UNLOCKED, PU_HWRCACHE_UNLOCKED);
    
    	// Alam: free the Z_Blocks before freeing it's users
    
    	// free all skin after each level: must be done after pfnClearMipMapCache!
    	for (i = 0; i < numwadfiles; i++)
    		M_AATreeIterate(wadfiles[i]->hwrcache, FreeMipmapColormap);
    
    	// now the heap don't have any 'user' pointing to our
    	// texturecache info, we can free it
    	if (gr_textures)
    		free(gr_textures);
    	gr_textures = NULL;
    	gr_numtextures = 0;
    }
    
    void HWR_PrepLevelCache(size_t pnumtextures)
    {
    	// problem: the mipmap cache management hold a list of mipmaps.. but they are
    	//           reallocated on each level..
    	//sub-optimal, but 1) just need re-download stuff in hardware cache VERY fast
    	//   2) sprite/menu stuff mixed with level textures so can't do anything else
    
    	// we must free it since numtextures changed
    	HWR_FreeTextureCache();
    
    	gr_numtextures = pnumtextures;
    	gr_textures = calloc(pnumtextures, sizeof (*gr_textures));
    	if (gr_textures == NULL)
    		I_Error("3D can't alloc gr_textures");
    }
    
    void HWR_SetPalette(RGBA_t *palette)
    {
    	//Hudler: 16/10/99: added for OpenGL gamma correction
    	RGBA_t gamma_correction = {0x7F7F7F7F};
    
    	//Hurdler 16/10/99: added for OpenGL gamma correction
    	gamma_correction.s.red   = (UINT8)cv_grgammared.value;
    	gamma_correction.s.green = (UINT8)cv_grgammagreen.value;
    	gamma_correction.s.blue  = (UINT8)cv_grgammablue.value;
    	HWD.pfnSetPalette(palette, &gamma_correction);
    
    	// hardware driver will flush there own cache if cache is non paletized
    	// now flush data texture cache so 32 bit texture are recomputed
    	if (patchformat == GR_RGBA || textureformat == GR_RGBA)
    	{
    		Z_FreeTags(PU_HWRCACHE, PU_HWRCACHE);
    		Z_FreeTags(PU_HWRCACHE_UNLOCKED, PU_HWRCACHE_UNLOCKED);
    	}
    }
    
    // --------------------------------------------------------------------------
    // Make sure texture is downloaded and set it as the source
    // --------------------------------------------------------------------------
    GLTexture_t *HWR_GetTexture(INT32 tex)
    {
    	GLTexture_t *grtex;
    #ifdef PARANOIA
    	if ((unsigned)tex >= gr_numtextures)
    		I_Error(" HWR_GetTexture: tex >= numtextures\n");
    #endif
    	grtex = &gr_textures[tex];
    
    	if (!grtex->mipmap.grInfo.data && !grtex->mipmap.downloaded)
    		HWR_GenerateTexture(tex, grtex);
    
    	HWD.pfnSetTexture(&grtex->mipmap);
    
    	// The system-memory data can be purged now.
    	Z_ChangeTag(grtex->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED);
    
    	return grtex;
    }
    
    
    static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum)
    {
    	size_t size, pflatsize;
    
    	// setup the texture info
    	grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64;
    	grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64;
    	grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1;
    	grMipmap->grInfo.format = GR_TEXFMT_P_8;
    	grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED;
    
    	size = W_LumpLength(flatlumpnum);
    
    	switch (size)
    	{
    		case 4194304: // 2048x2048 lump
    			pflatsize = 2048;
    			break;
    		case 1048576: // 1024x1024 lump
    			pflatsize = 1024;
    			break;
    		case 262144:// 512x512 lump
    			pflatsize = 512;
    			break;
    		case 65536: // 256x256 lump
    			pflatsize = 256;
    			break;
    		case 16384: // 128x128 lump
    			pflatsize = 128;
    			break;
    		case 1024: // 32x32 lump
    			pflatsize = 32;
    			break;
    		default: // 64x64 lump
    			pflatsize = 64;
    			break;
    	}
    	grMipmap->width  = (UINT16)pflatsize;
    	grMipmap->height = (UINT16)pflatsize;
    
    	// the flat raw data needn't be converted with palettized textures
    	W_ReadLump(flatlumpnum, Z_Malloc(W_LumpLength(flatlumpnum),
    		PU_HWRCACHE, &grMipmap->grInfo.data));
    }
    
    
    // Download a Doom 'flat' to the hardware cache and make it ready for use
    void HWR_GetFlat(lumpnum_t flatlumpnum)
    {
    	GLMipmap_t *grmip;
    
    	grmip = &HWR_GetCachedGLPatch(flatlumpnum)->mipmap;
    
    	if (!grmip->downloaded && !grmip->grInfo.data)
    		HWR_CacheFlat(grmip, flatlumpnum);
    
    	HWD.pfnSetTexture(grmip);
    
    	// The system-memory data can be purged now.
    	Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
    }
    
    //
    // HWR_LoadMappedPatch(): replace the skin color of the sprite in cache
    //                          : load it first in doom cache if not already
    //
    static void HWR_LoadMappedPatch(GLMipmap_t *grmip, GLPatch_t *gpatch)
    {
    	if (!grmip->downloaded && !grmip->grInfo.data)
    	{
    		patch_t *patch = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC);
    		HWR_MakePatch(patch, gpatch, grmip, true);
    
    		Z_Free(patch);
    	}
    
    	HWD.pfnSetTexture(grmip);
    
    	// The system-memory data can be purged now.
    	Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
    }
    
    // -----------------+
    // HWR_GetPatch     : Download a patch to the hardware cache and make it ready for use
    // -----------------+
    void HWR_GetPatch(GLPatch_t *gpatch)
    {
    	// is it in hardware cache
    	if (!gpatch->mipmap.downloaded && !gpatch->mipmap.grInfo.data)
    	{
    		// load the software patch, PU_STATIC or the Z_Malloc for hardware patch will
    		// flush the software patch before the conversion! oh yeah I suffered
    		patch_t *ptr = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC);
    		HWR_MakePatch(ptr, gpatch, &gpatch->mipmap, true);
    
    		// this is inefficient.. but the hardware patch in heap is purgeable so it should
    		// not fragment memory, and besides the REAL cache here is the hardware memory
    		Z_Free(ptr);
    	}
    
    	HWD.pfnSetTexture(&gpatch->mipmap);
    
    	// The system-memory patch data can be purged now.
    	Z_ChangeTag(gpatch->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED);
    }
    
    
    // -------------------+
    // HWR_GetMappedPatch : Same as HWR_GetPatch for sprite color
    // -------------------+
    void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap)
    {
    	GLMipmap_t *grmip, *newmip;
    
    	if (colormap == colormaps || colormap == NULL)
    	{
    		// Load the default (green) color in doom cache (temporary?) AND hardware cache
    		HWR_GetPatch(gpatch);
    		return;
    	}
    
    	// search for the mimmap
    	// skip the first (no colormap translated)
    	for (grmip = &gpatch->mipmap; grmip->nextcolormap; )
    	{
    		grmip = grmip->nextcolormap;
    		if (grmip->colormap == colormap)
    		{
    			HWR_LoadMappedPatch(grmip, gpatch);
    			return;
    		}
    	}
    	// not found, create it!
    	// If we are here, the sprite with the current colormap is not already in hardware memory
    
    	//BP: WARNING: don't free it manually without clearing the cache of harware renderer
    	//              (it have a liste of mipmap)
    	//    this malloc is cleared in HWR_FreeTextureCache
    	//    (...) unfortunately z_malloc fragment alot the memory :(so malloc is better
    	newmip = calloc(1, sizeof (*newmip));
    	if (newmip == NULL)
    		I_Error("%s: Out of memory", "HWR_GetMappedPatch");
    	grmip->nextcolormap = newmip;
    
    	newmip->colormap = colormap;
    	HWR_LoadMappedPatch(newmip, gpatch);
    }
    
    void HWR_UnlockCachedPatch(GLPatch_t *gpatch)
    {
    	if (!gpatch)
    		return;
    
    	Z_ChangeTag(gpatch->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED);
    	Z_ChangeTag(gpatch, PU_HWRPATCHINFO_UNLOCKED);
    }
    
    static const INT32 picmode2GR[] =
    {
    	GR_TEXFMT_P_8,                // PALETTE
    	0,                            // INTENSITY          (unsupported yet)
    	GR_TEXFMT_ALPHA_INTENSITY_88, // INTENSITY_ALPHA    (corona use this)
    	0,                            // RGB24              (unsupported yet)
    	GR_RGBA,                      // RGBA32             (opengl only)
    };
    
    static void HWR_DrawPicInCache(UINT8 *block, INT32 pblockwidth, INT32 pblockheight,
    	INT32 blockmodulo, pic_t *pic, INT32 bpp)
    {
    	INT32 i,j;
    	fixed_t posx, posy, stepx, stepy;
    	UINT8 *dest, *src, texel;
    	UINT16 texelu16;
    	INT32 picbpp;
    	RGBA_t col;
    
    	stepy = ((INT32)SHORT(pic->height)<<FRACBITS)/pblockheight;
    	stepx = ((INT32)SHORT(pic->width)<<FRACBITS)/pblockwidth;
    	picbpp = format2bpp[picmode2GR[pic->mode]];
    	posy = 0;
    	for (j = 0; j < pblockheight; j++)
    	{
    		posx = 0;
    		dest = &block[j*blockmodulo];
    		src = &pic->data[(posy>>FRACBITS)*SHORT(pic->width)*picbpp];
    		for (i = 0; i < pblockwidth;i++)
    		{
    			switch (pic->mode)
    			{ // source bpp
    				case PALETTE :
    					texel = src[(posx+FRACUNIT/2)>>FRACBITS];
    					switch (bpp)
    					{ // destination bpp
    						case 1 :
    							*dest++ = texel; break;
    						case 2 :
    							texelu16 = (UINT16)(texel | 0xff00);
    							memcpy(dest, &texelu16, sizeof(UINT16));
    							dest += sizeof(UINT16);
    							break;
    						case 3 :
    							col = V_GetColor(texel);
    							memcpy(dest, &col, sizeof(RGBA_t)-sizeof(UINT8));
    							dest += sizeof(RGBA_t)-sizeof(UINT8);
    							break;
    						case 4 :
    							memcpy(dest, &V_GetColor(texel), sizeof(RGBA_t));
    							dest += sizeof(RGBA_t);
    							break;
    					}
    					break;
    				case INTENSITY :
    					*dest++ = src[(posx+FRACUNIT/2)>>FRACBITS];
    					break;
    				case INTENSITY_ALPHA : // assume dest bpp = 2
    					memcpy(dest, src + ((posx+FRACUNIT/2)>>FRACBITS)*sizeof(UINT16), sizeof(UINT16));
    					dest += sizeof(UINT16);
    					break;
    				case RGB24 :
    					break;  // not supported yet
    				case RGBA32 : // assume dest bpp = 4
    					dest += sizeof(UINT32);
    					memcpy(dest, src + ((posx+FRACUNIT/2)>>FRACBITS)*sizeof(UINT32), sizeof(UINT32));
    					break;
    			}
    			posx += stepx;
    		}
    		posy += stepy;
    	}
    }
    
    // -----------------+
    // HWR_GetPic       : Download a Doom pic (raw row encoded with no 'holes')
    // Returns          :
    // -----------------+
    GLPatch_t *HWR_GetPic(lumpnum_t lumpnum)
    {
    	GLPatch_t *grpatch;
    
    	grpatch = HWR_GetCachedGLPatch(lumpnum);
    
    	if (!grpatch->mipmap.downloaded && !grpatch->mipmap.grInfo.data)
    	{
    		pic_t *pic;
    		UINT8 *block;
    		size_t len;
    		INT32 newwidth, newheight;
    
    		pic = W_CacheLumpNum(lumpnum, PU_CACHE);
    		grpatch->width = SHORT(pic->width);
    		grpatch->height = SHORT(pic->height);
    		len = W_LumpLength(lumpnum) - sizeof (pic_t);
    
    		grpatch->leftoffset = 0;
    		grpatch->topoffset = 0;
    
    		// find the good 3dfx size (boring spec)
    		HWR_ResizeBlock (grpatch->width, grpatch->height, &grpatch->mipmap.grInfo);
    		grpatch->mipmap.width = (UINT16)blockwidth;
    		grpatch->mipmap.height = (UINT16)blockheight;
    
    		if (pic->mode == PALETTE)
    			grpatch->mipmap.grInfo.format = textureformat; // can be set by driver
    		else
    			grpatch->mipmap.grInfo.format = picmode2GR[pic->mode];
    
    		Z_Free(grpatch->mipmap.grInfo.data);
    
    		// allocate block
    		block = MakeBlock(&grpatch->mipmap);
    
    		// if rounddown, rounddown patches as well as textures
    		if (cv_grrounddown.value)
    		{
    			newwidth = blockwidth;
    			newheight = blockheight;
    		}
    		else if (cv_voodoocompatibility.value) // Only scales down textures that exceed 256x256.
    		{
    			// no rounddown, do not size up patches, so they don't look 'scaled'
    			newwidth  = min(SHORT(pic->width),blockwidth);
    			newheight = min(SHORT(pic->height),blockheight);
    
    			if (newwidth > 256 || newheight > 256)
    			{
    				newwidth = blockwidth;
    				newheight = blockheight;
    			}
    		}
    		else
    		{
    			// no rounddown, do not size up patches, so they don't look 'scaled'
    			newwidth  = min(SHORT(pic->width),blockwidth);
    			newheight = min(SHORT(pic->height),blockheight);
    		}
    
    
    		if (grpatch->width  == blockwidth &&
    			grpatch->height == blockheight &&
    			format2bpp[grpatch->mipmap.grInfo.format] == format2bpp[picmode2GR[pic->mode]])
    		{
    			// no conversion needed
    			M_Memcpy(grpatch->mipmap.grInfo.data, pic->data,len);
    		}
    		else
    			HWR_DrawPicInCache(block, newwidth, newheight,
    			                   blockwidth*format2bpp[grpatch->mipmap.grInfo.format],
    			                   pic,
    			                   format2bpp[grpatch->mipmap.grInfo.format]);
    
    		Z_Unlock(pic);
    		Z_ChangeTag(block, PU_HWRCACHE_UNLOCKED);
    
    		grpatch->mipmap.flags = 0;
    		grpatch->max_s = (float)newwidth  / (float)blockwidth;
    		grpatch->max_t = (float)newheight / (float)blockheight;
    	}
    	HWD.pfnSetTexture(&grpatch->mipmap);
    	//CONS_Debug(DBG_RENDER, "picloaded at %x as texture %d\n",grpatch->mipmap.grInfo.data, grpatch->mipmap.downloaded);
    
    	return grpatch;
    }
    
    GLPatch_t *HWR_GetCachedGLPatchPwad(UINT16 wadnum, UINT16 lumpnum)
    {
    	aatree_t *hwrcache = wadfiles[wadnum]->hwrcache;
    	GLPatch_t *grpatch;
    
    	if (!(grpatch = M_AATreeGet(hwrcache, lumpnum)))
    	{
    		grpatch = Z_Calloc(sizeof(GLPatch_t), PU_HWRPATCHINFO, NULL);
    		grpatch->wadnum = wadnum;
    		grpatch->lumpnum = lumpnum;
    		M_AATreeSet(hwrcache, lumpnum, grpatch);
    	}
    
    	return grpatch;
    }
    
    GLPatch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum)
    {
    	return HWR_GetCachedGLPatchPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum));
    }
    
    // Need to do this because they aren't powers of 2
    static void HWR_DrawFadeMaskInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32 pblockheight,
    	lumpnum_t fademasklumpnum, UINT16 fmwidth, UINT16 fmheight)
    {
    	INT32 i,j;
    	fixed_t posx, posy, stepx, stepy;
    	UINT8 *block = mipmap->grInfo.data; // places the data directly into here, it already has the space allocated from HWR_ResizeBlock
    	UINT8 *flat;
    	UINT8 *dest, *src, texel;
    	RGBA_t col;
    
    	// Place the flats data into flat
    	W_ReadLump(fademasklumpnum, Z_Malloc(W_LumpLength(fademasklumpnum),
    		PU_HWRCACHE, &flat));
    
    	stepy = ((INT32)SHORT(fmheight)<<FRACBITS)/pblockheight;
    	stepx = ((INT32)SHORT(fmwidth)<<FRACBITS)/pblockwidth;
    	posy = 0;
    	for (j = 0; j < pblockheight; j++)
    	{
    		posx = 0;
    		dest = &block[j*blockwidth]; // 1bpp
    		src = &flat[(posy>>FRACBITS)*SHORT(fmwidth)];
    		for (i = 0; i < pblockwidth;i++)
    		{
    			// fademask bpp is always 1, and is used just for alpha
    			texel = src[(posx)>>FRACBITS];
    			col = V_GetColor(texel);
    			*dest = col.s.red; // take the red level of the colour and use it for alpha, as fademasks do
    
    			dest++;
    			posx += stepx;
    		}
    		posy += stepy;
    	}
    
    	Z_Free(flat);
    }
    
    static void HWR_CacheFadeMask(GLMipmap_t *grMipmap, lumpnum_t fademasklumpnum)
    {
    	size_t size;
    	UINT16 fmheight = 0, fmwidth = 0;
    
    	// setup the texture info
    	grMipmap->grInfo.format = GR_TEXFMT_ALPHA_8; // put the correct alpha levels straight in so I don't need to convert it later
    	grMipmap->flags = 0;
    
    	size = W_LumpLength(fademasklumpnum);
    
    	switch (size)
    	{
    		// None of these are powers of 2, so I'll need to do what is done for textures and make them powers of 2 before they can be used
    		case 256000: // 640x400
    			fmwidth = 640;
    			fmheight = 400;
    			break;
    		case 64000: // 320x200
    			fmwidth = 320;
    			fmheight = 200;
    			break;
    		case 16000: // 160x100
    			fmwidth = 160;
    			fmheight = 100;
    			break;
    		case 4000: // 80x50 (minimum)
    			fmwidth = 80;
    			fmheight = 50;
    			break;
    		default: // Bad lump
    			CONS_Alert(CONS_WARNING, "Fade mask lump of incorrect size, ignored\n"); // I should avoid this by checking the lumpnum in HWR_RunWipe
    			break;
    	}
    
    	// Thankfully, this will still work for this scenario
    	HWR_ResizeBlock(fmwidth, fmheight, &grMipmap->grInfo);
    
    	grMipmap->width  = blockwidth;
    	grMipmap->height = blockheight;
    
    	MakeBlock(grMipmap);
    
    	HWR_DrawFadeMaskInCache(grMipmap, blockwidth, blockheight, fademasklumpnum, fmwidth, fmheight);
    
    	// I DO need to convert this because it isn't power of 2 and we need the alpha
    }
    
    
    void HWR_GetFadeMask(lumpnum_t fademasklumpnum)
    {
    	GLMipmap_t *grmip;
    
    	grmip = &HWR_GetCachedGLPatch(fademasklumpnum)->mipmap;
    
    	if (!grmip->downloaded && !grmip->grInfo.data)
    		HWR_CacheFadeMask(grmip, fademasklumpnum);
    
    	HWD.pfnSetTexture(grmip);
    
    	// The system-memory data can be purged now.
    	Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
    }
    
    int HWR_GetNumCacheTextures(void)
    {
    	return gr_numtextures;
    }
    
    #endif //HWRENDER