//========= Copyright Valve Corporation, All rights reserved. ============//
//                       TOGL CODE LICENSE
//
//  Copyright 2011-2014 Valve Corporation
//  All Rights Reserved.
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
// cglmtex.h
//	GLMgr textures
//
//===============================================================================

#ifndef CGLMTEX_H
#define	CGLMTEX_H

#pragma once

#ifdef OSX
#include "glmgrbasics.h"
#endif
#include "tier1/utlhash.h"
#include "tier1/utlmap.h"

//===============================================================================

// forward declarations

class	GLMContext;
class	GLMTester;
class	CGLMTexLayoutTable;
class	CGLMTex;
class	CGLMFBO;

struct	IDirect3DSurface9;

#if GLMDEBUG
extern CGLMTex *g_pFirstCGMLTex;
#endif

// For GL_EXT_texture_sRGB_decode
#ifndef GL_TEXTURE_SRGB_DECODE_EXT
#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48
#endif

#ifndef GL_DECODE_EXT
#define GL_DECODE_EXT             0x8A49
#endif

#ifndef GL_SKIP_DECODE_EXT
#define GL_SKIP_DECODE_EXT        0x8A4A
#endif

//===============================================================================

struct GLMTexFormatDesc
{
	const char	*m_formatSummary;	// for debug visibility
	
	D3DFORMAT	m_d3dFormat;		// what D3D knows it as; see public/bitmap/imageformat.h
	
	GLenum		m_glIntFormat;		// GL internal format
	GLenum		m_glIntFormatSRGB;	// internal format if SRGB flavor
	GLenum		m_glDataFormat;		// GL data format
	GLenum		m_glDataType;		// GL data type
	
	int			m_chunkSize;		// 1 or 4 - 4 is used for compressed textures
	int			m_bytesPerSquareChunk;	// how many bytes for the smallest quantum (m_chunkSize x m_chunkSize)
									// this description lets us calculate size cleanly without conditional logic for compression
};
const GLMTexFormatDesc *GetFormatDesc( D3DFORMAT format );

//===============================================================================

// utility function for generating slabs of texels. mostly for test.
typedef struct
{
	// in
	D3DFORMAT	m_format;
	void		*m_dest;			// dest address
	int			m_chunkCount;		// square chunk count (single texels or compressed blocks)
	int			m_byteCountLimit;	// caller expectation of max number of bytes to write out
	float		r,g,b,a;			// color desired
	
	// out
	int			m_bytesWritten;
}	GLMGenTexelParams;

// return true if successful
bool	GLMGenTexels( GLMGenTexelParams *params );


//===============================================================================

struct GLMTexLayoutSlice
{
	int	m_xSize,m_ySize,m_zSize;		//texel dimensions of this slice
	int	m_storageOffset;				//where in the storage slab does this slice live
	int	m_storageSize;					//how much storage does this slice occupy
};

enum EGLMTexFlags
{
	kGLMTexMipped		=	0x01,
	kGLMTexMippedAuto	=	0x02,
	kGLMTexRenderable	=	0x04,
	kGLMTexIsStencil	=	0x08,
	kGLMTexIsDepth		=	0x10,
	kGLMTexSRGB			=	0x20,
	kGLMTexMultisampled	=	0x40,		// has an RBO backing it.  Cannot combine with Mipped, MippedAuto.  One slice maximum, only targeting GL_TEXTURE_2D.
										// actually not 100% positive on the mipmapping, the RBO itself can't be mipped, but the resulting texture could
										// have mipmaps generated.
	kGLMTexDynamic		=	0x80
};

//===============================================================================

struct GLMTexLayoutKey
{
	// input values: held const, these are the hash key for the form map
	GLenum				m_texGLTarget;				// flavor of texture: GL_TEXTURE_2D, GL_TEXTURE_3D, GLTEXTURE_CUBE_MAP
	D3DFORMAT			m_texFormat;				// D3D texel format
	unsigned long		m_texFlags;					// mipped, autogen mips, render target, ... ?
	unsigned long		m_texSamples;				// zero for a plain tex, 2/4/6/8 for "MSAA tex" (RBO backed)
	int					m_xSize,m_ySize,m_zSize;	// size of base mip
};

bool LessFunc_GLMTexLayoutKey( const GLMTexLayoutKey &a, const GLMTexLayoutKey &b );

#define	GLM_TEX_MAX_MIPS	14
#define	GLM_TEX_MAX_FACES	6
#define	GLM_TEX_MAX_SLICES	(GLM_TEX_MAX_MIPS * GLM_TEX_MAX_FACES)

#pragma warning( push )
#pragma warning( disable : 4200 )

struct GLMTexLayout
{
	char		*m_layoutSummary;	// for debug visibility

	// const inputs used for hashing
	GLMTexLayoutKey		m_key;
	
	// refcount
	int					m_refCount;

	// derived values:	
	GLMTexFormatDesc	*m_format;					// format specific info
	int					m_mipCount;					// derived by starying at base size and working down towards 1x1
	int					m_faceCount;				// 1 for 2d/3d, 6 for cubemap
	int					m_sliceCount;				// product of faces and mips
	int					m_storageTotalSize;			// size of storage slab required
	
	// slice array
	GLMTexLayoutSlice	m_slices[0];				// dynamically allocated 2-d array [faces][mips]
};

#pragma warning( pop )

class	CGLMTexLayoutTable
{
public:
					CGLMTexLayoutTable();
	
	GLMTexLayout	*NewLayoutRef( GLMTexLayoutKey *pDesiredKey );		// pass in a pointer to layout key - receive ptr to completed layout
	void			DelLayoutRef( GLMTexLayout *layout );		// pass in pointer to completed layout.  refcount is dropped.
	
	void			DumpStats( void );
protected:
	CUtlMap< GLMTexLayoutKey, GLMTexLayout* >	m_layoutMap;
};

//===============================================================================

// a sampler specifies desired state for drawing on a given sampler index
// this is the combination of a texture choice and a set of sampler parameters
// see http://msdn.microsoft.com/en-us/library/bb172602(VS.85).aspx

struct GLMTexLockParams
{
	// input params which identify the slice of interest
	CGLMTex		*m_tex;
	int			m_face;
	int			m_mip;
	
	// identifies the region of the slice
	GLMRegion	m_region;
	
	// tells GLM to force re-read of the texels back from GL
	// i.e. "I know I stepped on those texels with a draw or blit - the GLM copy is stale"
	bool		m_readback;
	bool		m_readonly;
};

struct GLMTexLockDesc
{
	GLMTexLockParams	m_req;	// form of the lock request
	
	bool				m_active;				// set true at lock time. cleared at unlock time.

	int					m_sliceIndex;			// which slice in the layout
	int					m_sliceBaseOffset;		// where is that in the texture data
	int					m_sliceRegionOffset;	// offset to the start (lowest address corner) of the region requested
};

//===============================================================================

#define	GLM_SAMPLER_COUNT	16

#define GLM_MAX_PIXEL_TEX_SAMPLERS	16
#define GLM_MAX_VERTEX_TEX_SAMPLERS	0
typedef CBitVec<GLM_SAMPLER_COUNT> CTexBindMask;

enum EGLMTexSliceFlag
{
	kSliceValid			=	0x01,	// slice has been teximage'd in whole at least once - set to 0 initially
	kSliceStorageValid	=	0x02,	// if backing store is available, this slice's data is a valid copy - set to 0 initially
	kSliceLocked		=	0x04,	// are one or more locks outstanding on this slice
	kSliceFullyDirty	=	0x08,	// does the slice need to be fully downloaded at unlock time (disregard dirty rects)
};

//===============================================================================

#define GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS		(2)
#define GLM_PACKED_SAMPLER_PARAMS_MIN_FILTER_BITS	(2)
#define GLM_PACKED_SAMPLER_PARAMS_MAG_FILTER_BITS	(2)
#define GLM_PACKED_SAMPLER_PARAMS_MIP_FILTER_BITS	(2)
#define GLM_PACKED_SAMPLER_PARAMS_MIN_LOD_BITS		(4)
#define GLM_PACKED_SAMPLER_PARAMS_MAX_ANISO_BITS	(5)
#define GLM_PACKED_SAMPLER_PARAMS_COMPARE_MODE_BITS	(1)
#define GLM_PACKED_SAMPLER_PARAMS_SRGB_BITS			(1)

struct GLMTexPackedSamplingParams
{
	uint32 m_addressU		: GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS;
	uint32 m_addressV		: GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS;
	uint32 m_addressW		: GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS;

	uint32 m_minFilter		: GLM_PACKED_SAMPLER_PARAMS_MIN_FILTER_BITS;
	uint32 m_magFilter		: GLM_PACKED_SAMPLER_PARAMS_MAG_FILTER_BITS;
	uint32 m_mipFilter		: GLM_PACKED_SAMPLER_PARAMS_MIP_FILTER_BITS;

	uint32 m_minLOD			: GLM_PACKED_SAMPLER_PARAMS_MIN_LOD_BITS;
	uint32 m_maxAniso		: GLM_PACKED_SAMPLER_PARAMS_MAX_ANISO_BITS;
	uint32 m_compareMode	: GLM_PACKED_SAMPLER_PARAMS_COMPARE_MODE_BITS;
	uint32 m_srgb			: GLM_PACKED_SAMPLER_PARAMS_SRGB_BITS;
	uint32 m_isValid		: 1;
};

struct GLMTexSamplingParams
{
	union
	{
		GLMTexPackedSamplingParams m_packed;
		uint32 m_bits;
	};

	uint32 m_borderColor;
	float m_lodBias;

	FORCEINLINE bool operator== (const GLMTexSamplingParams& rhs ) const
	{
		return ( m_bits == rhs.m_bits ) && ( m_borderColor == rhs.m_borderColor ) && ( m_lodBias == rhs.m_lodBias );
	}

	FORCEINLINE void SetToDefaults()
	{
		m_bits = 0;
		m_borderColor = 0;
		m_lodBias = 0.0f;
		m_packed.m_addressU = D3DTADDRESS_WRAP;
		m_packed.m_addressV = D3DTADDRESS_WRAP;
		m_packed.m_addressW = D3DTADDRESS_WRAP;
		m_packed.m_minFilter = D3DTEXF_POINT;
		m_packed.m_magFilter = D3DTEXF_POINT;
		m_packed.m_mipFilter = D3DTEXF_NONE;
		m_packed.m_maxAniso = 1;
		m_packed.m_compareMode = 0;
		m_packed.m_isValid = true;
	}

#ifndef OSX
	FORCEINLINE void SetToSamplerObject( GLuint nSamplerObject ) const
	{
		static const GLenum dxtogl_addressMode[] = { GL_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, (GLenum)-1 };
		static const GLenum dxtogl_magFilter[4] = { GL_NEAREST,	GL_NEAREST,	GL_LINEAR, GL_LINEAR };
		static const GLenum dxtogl_minFilter[4][4] = // indexed by _D3DTEXTUREFILTERTYPE on both axes: [row is min filter][col is mip filter]. 
		{
			/* min = D3DTEXF_NONE */		{	GL_NEAREST,		GL_NEAREST_MIPMAP_NEAREST,	GL_NEAREST_MIPMAP_LINEAR,	(GLenum)-1	},		// D3DTEXF_NONE we just treat like POINT
			/* min = D3DTEXF_POINT */		{	GL_NEAREST,		GL_NEAREST_MIPMAP_NEAREST,	GL_NEAREST_MIPMAP_LINEAR,	(GLenum)-1	},
			/* min = D3DTEXF_LINEAR */		{	GL_LINEAR,		GL_LINEAR_MIPMAP_NEAREST,	GL_LINEAR_MIPMAP_LINEAR,	(GLenum)-1	},
			/* min = D3DTEXF_ANISOTROPIC */	{	GL_LINEAR,		GL_LINEAR_MIPMAP_NEAREST,	GL_LINEAR_MIPMAP_LINEAR,	(GLenum)-1	},		// no diff from prior row, set maxAniso to effect the sampling
		};

		gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_WRAP_S, dxtogl_addressMode[m_packed.m_addressU] );
		gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_WRAP_T, dxtogl_addressMode[m_packed.m_addressV] );
		gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_WRAP_R, dxtogl_addressMode[m_packed.m_addressW] );
		gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_MIN_FILTER, dxtogl_minFilter[m_packed.m_minFilter][m_packed.m_mipFilter] );
		gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_MAG_FILTER, dxtogl_magFilter[m_packed.m_magFilter] );
		gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_MAX_ANISOTROPY_EXT, m_packed.m_maxAniso );

		float flBorderColor[4] = { 0, 0, 0, 0 };
		if ( m_borderColor )
		{
			flBorderColor[0] = ((m_borderColor >> 16) & 0xFF) * (1.0f/255.0f);	//R
			flBorderColor[1] = ((m_borderColor >>  8) & 0xFF) * (1.0f/255.0f);	//G
			flBorderColor[2] = ((m_borderColor      ) & 0xFF) * (1.0f/255.0f);	//B
			flBorderColor[3] = ((m_borderColor >> 24) & 0xFF) * (1.0f/255.0f);	//A
		}
		gGL->glSamplerParameterfv( nSamplerObject, GL_TEXTURE_BORDER_COLOR, flBorderColor ); // <-- this crashes ATI's driver, remark it out
		gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_MIN_LOD, m_packed.m_minLOD );
//		gGL->glSamplerParameterfv( nSamplerObject, GL_TEXTURE_LOD_BIAS, &m_lodBias );
		gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_COMPARE_MODE_ARB, m_packed.m_compareMode ? GL_COMPARE_R_TO_TEXTURE_ARB : GL_NONE );
//		gGL->glSamplerParameterf( nSamplerObject, GL_TEXTURE_LOD_BIAS, m_lodBias );
		if ( m_packed.m_compareMode )
		{
			gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL );
		}
		if ( gGL->m_bHave_GL_EXT_texture_sRGB_decode )
		{
			gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_SRGB_DECODE_EXT, m_packed.m_srgb ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT );
		}
	}
#endif // !OSX

	inline void DeltaSetToTarget( GLenum target, const GLMTexSamplingParams &curState )
	{
		static const GLenum dxtogl_addressMode[] = { GL_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, (GLenum)-1 };
		static const GLenum dxtogl_magFilter[4] = { GL_NEAREST,	GL_NEAREST,	GL_LINEAR, GL_LINEAR };
		static const GLenum dxtogl_minFilter[4][4] = // indexed by _D3DTEXTUREFILTERTYPE on both axes: [row is min filter][col is mip filter]. 
		{
			/* min = D3DTEXF_NONE */		{	GL_NEAREST,		GL_NEAREST_MIPMAP_NEAREST,	GL_NEAREST_MIPMAP_LINEAR,	(GLenum)-1	},		// D3DTEXF_NONE we just treat like POINT
			/* min = D3DTEXF_POINT */		{	GL_NEAREST,		GL_NEAREST_MIPMAP_NEAREST,	GL_NEAREST_MIPMAP_LINEAR,	(GLenum)-1	},
			/* min = D3DTEXF_LINEAR */		{	GL_LINEAR,		GL_LINEAR_MIPMAP_NEAREST,	GL_LINEAR_MIPMAP_LINEAR,	(GLenum)-1	},
			/* min = D3DTEXF_ANISOTROPIC */	{	GL_LINEAR,		GL_LINEAR_MIPMAP_NEAREST,	GL_LINEAR_MIPMAP_LINEAR,	(GLenum)-1	},		// no diff from prior row, set maxAniso to effect the sampling
		};

		if ( m_packed.m_addressU != curState.m_packed.m_addressU )
		{
			gGL->glTexParameteri( target, GL_TEXTURE_WRAP_S, dxtogl_addressMode[m_packed.m_addressU] );
		}

		if ( m_packed.m_addressV != curState.m_packed.m_addressV )
		{
			gGL->glTexParameteri( target, GL_TEXTURE_WRAP_T, dxtogl_addressMode[m_packed.m_addressV] );
		}

		if ( m_packed.m_addressW != curState.m_packed.m_addressW )
		{
			gGL->glTexParameteri( target, GL_TEXTURE_WRAP_R, dxtogl_addressMode[m_packed.m_addressW] );
		}

		if ( ( m_packed.m_minFilter != curState.m_packed.m_minFilter ) || 
			 ( m_packed.m_magFilter != curState.m_packed.m_magFilter ) ||
			 ( m_packed.m_mipFilter != curState.m_packed.m_mipFilter ) ||
			 ( m_packed.m_maxAniso != curState.m_packed.m_maxAniso ) )
		{
			gGL->glTexParameteri( target, GL_TEXTURE_MIN_FILTER, dxtogl_minFilter[m_packed.m_minFilter][m_packed.m_mipFilter] );
			gGL->glTexParameteri( target, GL_TEXTURE_MAG_FILTER, dxtogl_magFilter[m_packed.m_magFilter] );
			gGL->glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, m_packed.m_maxAniso );
		}

		if ( m_borderColor != curState.m_borderColor )
		{
			float flBorderColor[4] = { 0, 0, 0, 0 };
			if ( m_borderColor )
			{
				flBorderColor[0] = ((m_borderColor >> 16) & 0xFF) * (1.0f/255.0f);	//R
				flBorderColor[1] = ((m_borderColor >>  8) & 0xFF) * (1.0f/255.0f);	//G
				flBorderColor[2] = ((m_borderColor      ) & 0xFF) * (1.0f/255.0f);	//B
				flBorderColor[3] = ((m_borderColor >> 24) & 0xFF) * (1.0f/255.0f);	//A
			}
		
			gGL->glTexParameterfv( target, GL_TEXTURE_BORDER_COLOR, flBorderColor ); // <-- this crashes ATI's driver, remark it out
		}

		if ( m_packed.m_minLOD != curState.m_packed.m_minLOD )
		{
			gGL->glTexParameteri( target, GL_TEXTURE_MIN_LOD, m_packed.m_minLOD );
		}

		if ( m_lodBias != curState.m_lodBias )
		{
			// Could use TexParameterf instead, but we don't currently grab it. This works fine, too.
			gGL->glTexParameterfv( target, GL_TEXTURE_LOD_BIAS, &m_lodBias );
		}

		if ( m_packed.m_compareMode != curState.m_packed.m_compareMode )
		{
			gGL->glTexParameteri( target, GL_TEXTURE_COMPARE_MODE_ARB, m_packed.m_compareMode ? GL_COMPARE_R_TO_TEXTURE_ARB : GL_NONE );
			if ( m_packed.m_compareMode )
			{
				gGL->glTexParameteri( target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL );
			}
		}

		if ( ( gGL->m_bHave_GL_EXT_texture_sRGB_decode ) && ( m_packed.m_srgb != curState.m_packed.m_srgb ) )
		{
			gGL->glTexParameteri( target, GL_TEXTURE_SRGB_DECODE_EXT, m_packed.m_srgb ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT );
		}
	}

	inline void SetToTarget( GLenum target )
	{
		static const GLenum dxtogl_addressMode[] = { GL_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, (GLenum)-1 };
		static const GLenum dxtogl_magFilter[4] = { GL_NEAREST,	GL_NEAREST,	GL_LINEAR, GL_LINEAR };
		static const GLenum dxtogl_minFilter[4][4] = // indexed by _D3DTEXTUREFILTERTYPE on both axes: [row is min filter][col is mip filter]. 
		{
			/* min = D3DTEXF_NONE */		{	GL_NEAREST,		GL_NEAREST_MIPMAP_NEAREST,	GL_NEAREST_MIPMAP_LINEAR,	(GLenum)-1	},		// D3DTEXF_NONE we just treat like POINT
			/* min = D3DTEXF_POINT */		{	GL_NEAREST,		GL_NEAREST_MIPMAP_NEAREST,	GL_NEAREST_MIPMAP_LINEAR,	(GLenum)-1	},
			/* min = D3DTEXF_LINEAR */		{	GL_LINEAR,		GL_LINEAR_MIPMAP_NEAREST,	GL_LINEAR_MIPMAP_LINEAR,	(GLenum)-1	},
			/* min = D3DTEXF_ANISOTROPIC */	{	GL_LINEAR,		GL_LINEAR_MIPMAP_NEAREST,	GL_LINEAR_MIPMAP_LINEAR,	(GLenum)-1	},		// no diff from prior row, set maxAniso to effect the sampling
		};

		gGL->glTexParameteri( target, GL_TEXTURE_WRAP_S, dxtogl_addressMode[m_packed.m_addressU] );
		gGL->glTexParameteri( target, GL_TEXTURE_WRAP_T, dxtogl_addressMode[m_packed.m_addressV] );
		gGL->glTexParameteri( target, GL_TEXTURE_WRAP_R, dxtogl_addressMode[m_packed.m_addressW] );
		gGL->glTexParameteri( target, GL_TEXTURE_MIN_FILTER, dxtogl_minFilter[m_packed.m_minFilter][m_packed.m_mipFilter] );
		gGL->glTexParameteri( target, GL_TEXTURE_MAG_FILTER, dxtogl_magFilter[m_packed.m_magFilter] );
		gGL->glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, m_packed.m_maxAniso );

		float flBorderColor[4] = { 0, 0, 0, 0 };
		if ( m_borderColor )
		{
			flBorderColor[0] = ((m_borderColor >> 16) & 0xFF) * (1.0f/255.0f);	//R
			flBorderColor[1] = ((m_borderColor >>  8) & 0xFF) * (1.0f/255.0f);	//G
			flBorderColor[2] = ((m_borderColor      ) & 0xFF) * (1.0f/255.0f);	//B
			flBorderColor[3] = ((m_borderColor >> 24) & 0xFF) * (1.0f/255.0f);	//A
		}
		gGL->glTexParameterfv( target, GL_TEXTURE_BORDER_COLOR, flBorderColor ); // <-- this crashes ATI's driver, remark it out
		gGL->glTexParameteri( target, GL_TEXTURE_MIN_LOD, m_packed.m_minLOD );
//		gGL->glTexParameterfv( target, GL_TEXTURE_LOD_BIAS, &m_lodBias );
		gGL->glTexParameteri( target, GL_TEXTURE_COMPARE_MODE_ARB, m_packed.m_compareMode ? GL_COMPARE_R_TO_TEXTURE_ARB : GL_NONE );
		if ( m_packed.m_compareMode )
		{
			gGL->glTexParameteri( target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL );
		}
		if ( gGL->m_bHave_GL_EXT_texture_sRGB_decode )
		{
			gGL->glTexParameteri( target, GL_TEXTURE_SRGB_DECODE_EXT, m_packed.m_srgb ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT );
		}
	}
};

//===============================================================================

class CGLMTex
{

public:

	void					Lock( GLMTexLockParams *params, char** addressOut, int* yStrideOut, int *zStrideOut );
	void					Unlock( GLMTexLockParams *params );
	GLuint                                  GetTexName() { return m_texName; }
	
protected:
	friend class GLMContext;			// only GLMContext can make CGLMTex objects
	friend class GLMTester;
	friend class CGLMFBO;

	friend struct IDirect3DDevice9;
	friend struct IDirect3DBaseTexture9;
	friend struct IDirect3DTexture9;
	friend struct IDirect3DSurface9;
	friend struct IDirect3DCubeTexture9;
	friend struct IDirect3DVolumeTexture9;
	
			CGLMTex( GLMContext *ctx, GLMTexLayout *layout, uint levels, const char *debugLabel = NULL );
			~CGLMTex( );
	
	int						CalcSliceIndex( int face, int mip );
	void					CalcTexelDataOffsetAndStrides( int sliceIndex, int x, int y, int z, int *offsetOut, int *yStrideOut, int *zStrideOut );
		
	GLubyte					*ReadTexels( GLMTexLockDesc *desc, bool readWholeSlice=true, bool readOnly=false );
	void					WriteTexels( GLMTexLockDesc *desc, bool writeWholeSlice=true, bool noDataWrite=false );
		// last param lets us send NULL data ptr (only legal with uncompressed formats, beware)
		// this helps out ResetSRGB.

#if defined( OSX )
	void					HandleSRGBMismatch( bool srgb, int &srgbFlipCount );
	void					ResetSRGB( bool srgb, bool noDataWrite );
	// re-specify texture format to match desired sRGB form
	// noWrite means send NULL for texel source addresses instead of actual data - ideal for RT's
#endif
				
	bool					IsRBODirty() const;
	void					ForceRBONonDirty();
	void					ForceRBODirty();
			
		// re-specify texture format to match desired sRGB form
		// noWrite means send NULL for texel source addresses instead of actual data - ideal for RT's

	GLuint					m_texName;			// name of this texture in the context
	GLuint					m_pbo;
	GLubyte					*m_mapped;
	GLenum					m_texGLTarget;
	uint					m_nSamplerType;		// SAMPLER_2D, etc.
	
	GLMTexSamplingParams	m_SamplingParams;

	GLMTexLayout			*m_layout;		// layout of texture (shared across all tex with same layout)
	
	uint					m_nLastResolvedBatchCounter;
					
	int						m_minActiveMip;//index of lowest mip that has been written.  used to drive setting of GL_TEXTURE_MAX_LEVEL.
	int						m_maxActiveMip;//index of highest mip that has been written.  used to drive setting of GL_TEXTURE_MAX_LEVEL.
						
	GLMContext				*m_ctx;			// link back to parent context
		
	
	CGLMFBO                 *m_pBlitSrcFBO;
	CGLMFBO                 *m_pBlitDstFBO;
	GLuint					m_rboName;		// name of MSAA RBO backing the tex if MSAA enabled (or zero)
													
	int						m_rtAttachCount; // how many RT's have this texture attached somewhere

	char					*m_backing;		// backing storage if available
	
	int						m_lockCount;	// lock reqs are stored in the GLMContext for tracking

	CUtlVector<unsigned char>	m_sliceFlags;
			
	char					*m_debugLabel;	// strdup() of debugLabel passed in, or NULL
	
	bool					m_texClientStorage;	// was CS selected for texture
	bool					m_texPreloaded;		// has it been kicked into VRAM with GLMContext::PreloadTex yet

	int						m_srgbFlipCount;
#if GLMDEBUG
	CGLMTex					*m_pPrevTex;
	CGLMTex					*m_pNextTex;
#endif
};

#endif
