//========= 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.
//
// glmgr.h
//	singleton class, common basis for managing GL contexts
//	responsible for tracking adapters and contexts
//
//===============================================================================

#ifndef GLMGR_H
#define	GLMGR_H

#pragma once

#undef HAVE_GL_ARB_SYNC
#ifndef OSX
#define HAVE_GL_ARB_SYNC 1
#endif

#include "glbase.h"
#include "glentrypoints.h"
#include "glmdebug.h"
#include "glmdisplay.h"
#include "glmgrext.h"
#include "glmgrbasics.h"
#include "cglmtex.h"
#include "cglmfbo.h"
#include "cglmprogram.h"
#include "cglmbuffer.h"
#include "cglmquery.h"

#include "tier0/tslist.h"
#include "tier0/vprof_telemetry.h"
#include "materialsystem/IShader.h"
#include "dxabstract_types.h"
#include "tier0/icommandline.h"

#undef FORCEINLINE
#define FORCEINLINE inline

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

#define GLM_OPENGL_VENDOR_ID 1
#define GLM_OPENGL_DEFAULT_DEVICE_ID 1
#define GLM_OPENGL_LOW_PERF_DEVICE_ID 2

extern void GLMDebugPrintf( const char *pMsg, ... );

extern uint g_nTotalDrawsOrClears, g_nTotalVBLockBytes, g_nTotalIBLockBytes;

#if GL_TELEMETRY_GPU_ZONES
struct TelemetryGPUStats_t
{
	uint m_nTotalBufferLocksAndUnlocks;
	uint m_nTotalTexLocksAndUnlocks;
	uint m_nTotalBlit2;
	uint m_nTotalResolveTex;
	uint m_nTotalPresent;

	inline void Clear() { memset( this, 0, sizeof( *this ) ); }
	inline uint GetTotal() const { return m_nTotalBufferLocksAndUnlocks + m_nTotalTexLocksAndUnlocks + m_nTotalBlit2 + m_nTotalResolveTex + m_nTotalPresent; }
};
extern TelemetryGPUStats_t g_TelemetryGPUStats;
#endif

struct GLMRect;
typedef void *PseudoGLContextPtr;

// parrot the D3D present parameters, more or less... "adapter" translates into "active display index" per the m_activeDisplayCount below.
class GLMDisplayParams
{
	public:

		// presumption, these indices are in sync with the current display DB that GLMgr has handy
	//int					m_rendererIndex;		// index of renderer (-1 if root context)
	//int					m_displayIndex;			// index of display in renderer - for FS
	//int					m_modeIndex;			// index of mode in display - for FS

	void				*m_focusWindow;			// (VD3DHWND aka WindowRef) - what window does this context display into
	
	bool				m_fsEnable;				// fullscreen on or not
	bool				m_vsyncEnable;			// vsync on or not

	// height and width have to match the display mode info if full screen.
	
	uint				m_backBufferWidth;		// pixel width (aka screen h-resolution if full screen)
	uint				m_backBufferHeight;		// pixel height (aka screen v-resolution if full screen)
	D3DFORMAT           m_backBufferFormat;		// pixel format
	uint				m_multiSampleCount;		// 0 means no MSAA, 2 means 2x MSAA, etc
		// uint				m_multiSampleQuality;	// no MSAA quality control yet
		
	bool				m_enableAutoDepthStencil;	// generally set to 'TRUE' per CShaderDeviceDx8::SetPresentParameters
	D3DFORMAT			m_autoDepthStencilFormat;
	
	uint				m_fsRefreshHz;			// if full screen, this refresh rate (likely 0 for LCD's)
	
	//uint				m_rootRendererID;		// only used if m_rendererIndex is -1.
	//uint				m_rootDisplayMask;		// only used if m_rendererIndex is -1.

	bool				m_mtgl;					// enable multi threaded GL driver
};

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

class GLMgr
{
public:
	
	//===========================================================================
	// class methods - singleton
	static void		NewGLMgr( void );			// instantiate singleton..
	static GLMgr	*aGLMgr( void );			// return singleton..
	static void		DelGLMgr( void );			// tear down singleton..

	//===========================================================================
	// plain methods

					#if 0				// turned all these off while new approach is coded
						void			RefreshDisplayDB( void );	// blow away old display DB, make a new one
						GLMDisplayDB	*GetDisplayDB( void );		// get a ptr to the one GLMgr keeps.  only valid til next refresh.

							// eligible renderers will be ranked by desirability starting at index 0 within the db
							// within each renderer, eligible displays will be ranked some kind of desirability (area? dist from menu bar?) 
							// within each display, eligible modes will be ranked by descending areas
						
							// calls supplying indices are implicitly making reference to the current DB
						bool			CaptureDisplay( int rendIndex, int displayIndex, bool captureAll );		// capture one display or all displays
						void			ReleaseDisplays( void );												// release all captures
						
						int				GetDisplayMode( int rendIndex, int displayIndex );						// retrieve current display res (returns modeIndex)
						void			SetDisplayMode( GLMDisplayParams *params );								// set the display res (only useful for FS)
					#endif
	
	GLMContext		*NewContext( IDirect3DDevice9 *pDevice, GLMDisplayParams *params );		// this will have to change
	void			DelContext( GLMContext *context );

		// with usage of CGLMacro.h we could dispense with the "current context" thing
		// and just declare a member variable of GLMContext, allowing each glXXX call to be routed directly
		// to the correct context
	void		SetCurrentContext( GLMContext *context );	// make current in calling thread only
	GLMContext	*GetCurrentContext( void );
		
protected:
	friend class GLMContext;

	GLMgr();
	~GLMgr();
};


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

// helper function to do enable or disable in one step
FORCEINLINE void glSetEnable( GLenum which, bool enable )
{
	if (enable)
		gGL->glEnable(which);
	else
		gGL->glDisable(which);
}

// helper function for int vs enum clarity
FORCEINLINE void glGetEnumv( GLenum which, GLenum *dst )
{
	gGL->glGetIntegerv( which, (int*)dst );
}

//===========================================================================//
//
// types to support the GLMContext
//
//===========================================================================//

// Each state set/get path we are providing caching for, needs its own struct and a comparison operator.
// we also provide an enum of how many such types there are, handy for building dirty masks etc.

// shorthand macros
#define	EQ(fff) ( (src.fff) == (fff) )

//rasterizer
struct GLAlphaTestEnable_t		{ GLint		enable;													inline bool operator==(const GLAlphaTestEnable_t& src)		const { return EQ(enable);									} };
struct GLAlphaTestFunc_t		{ GLenum	func; GLclampf ref;										inline bool operator==(const GLAlphaTestFunc_t& src)		const { return EQ(func) && EQ(ref);							} };
struct GLCullFaceEnable_t		{ GLint		enable;													inline bool operator==(const GLCullFaceEnable_t& src)		const { return EQ(enable);									} };
struct GLCullFrontFace_t		{ GLenum	value;													inline bool operator==(const GLCullFrontFace_t& src)		const { return EQ(value);									} };
struct GLPolygonMode_t			{ GLenum	values[2];												inline bool operator==(const GLPolygonMode_t& src)			const { return EQ(values[0]) && EQ(values[1]);				} };
struct GLDepthBias_t			{ GLfloat	factor;		GLfloat units;								inline bool operator==(const GLDepthBias_t& src)			const { return EQ(factor) && EQ(units);						} };
struct GLScissorEnable_t		{ GLint		enable;													inline bool operator==(const GLScissorEnable_t& src)		const { return EQ(enable);									} };
struct GLScissorBox_t			{ GLint		x,y;		GLsizei width, height;						inline bool operator==(const GLScissorBox_t& src)			const { return EQ(x) && EQ(y) && EQ(width) && EQ(height);	} };
struct GLAlphaToCoverageEnable_t{ GLint		enable;													inline bool operator==(const GLAlphaToCoverageEnable_t& src) const { return EQ(enable);								} };
struct GLViewportBox_t			{ GLint		x,y;		GLsizei width, height; uint widthheight;	inline bool operator==(const GLViewportBox_t& src)			const { return EQ(x) && EQ(y) && EQ(width) && EQ(height);	} };
struct GLViewportDepthRange_t	{ GLdouble	flNear,flFar;											inline bool operator==(const GLViewportDepthRange_t& src)	const { return EQ(flNear) && EQ(flFar);						} };
struct GLClipPlaneEnable_t		{ GLint		enable;													inline bool operator==(const GLClipPlaneEnable_t& src)		const { return EQ(enable);									} };
struct GLClipPlaneEquation_t	{ GLfloat	x,y,z,w;												inline bool operator==(const GLClipPlaneEquation_t& src)	const { return EQ(x) && EQ(y) && EQ(z) && EQ(w);			} };

//blend
struct GLColorMaskSingle_t		{ signed char		r,g,b,a;												inline bool operator==(const GLColorMaskSingle_t& src)		const { return EQ(r) && EQ(g) && EQ(b) && EQ(a);			} };
struct GLColorMaskMultiple_t	{ signed char		r,g,b,a;												inline bool operator==(const GLColorMaskMultiple_t& src)	const { return EQ(r) && EQ(g) && EQ(b) && EQ(a);			} };
struct GLBlendEnable_t			{ GLint		enable;													inline bool operator==(const GLBlendEnable_t& src)			const { return EQ(enable);									} };
struct GLBlendFactor_t			{ GLenum	srcfactor,dstfactor;									inline bool operator==(const GLBlendFactor_t& src)			const { return EQ(srcfactor) && EQ(dstfactor);				} };
struct GLBlendEquation_t		{ GLenum	equation;												inline bool operator==(const GLBlendEquation_t& src)		const { return EQ(equation);								} };
struct GLBlendColor_t			{ GLfloat	r,g,b,a;												inline bool operator==(const GLBlendColor_t& src)			const { return EQ(r) && EQ(g) && EQ(b) && EQ(a);			} };
struct GLBlendEnableSRGB_t		{ GLint		enable;													inline bool operator==(const GLBlendEnableSRGB_t& src)		const { return EQ(enable);									} };

//depth
struct GLDepthTestEnable_t		{ GLint		enable;													inline bool operator==(const GLDepthTestEnable_t& src)		const { return EQ(enable);									} };
struct GLDepthFunc_t			{ GLenum	func;													inline bool operator==(const GLDepthFunc_t& src)			const { return EQ(func);									} };
struct GLDepthMask_t			{  char		mask;													inline bool operator==(const GLDepthMask_t& src)			const { return EQ(mask);									} };

//stencil
struct GLStencilTestEnable_t	{ GLint		enable;													inline bool operator==(const GLStencilTestEnable_t& src)	const { return EQ(enable);									} };
struct GLStencilFunc_t			{ GLenum	frontfunc, backfunc;	GLint ref;  GLuint mask;		inline bool operator==(const GLStencilFunc_t& src)			const { return EQ(frontfunc) && EQ(backfunc) && EQ(ref) && EQ(mask); } };
struct GLStencilOp_t			{ GLenum	sfail;	GLenum dpfail; GLenum dppass;					inline bool operator==(const GLStencilOp_t& src)			const { return EQ(sfail) && EQ(dpfail) && EQ(dppass);		} };
struct GLStencilWriteMask_t		{ GLint		mask;													inline bool operator==(const GLStencilWriteMask_t& src)	const { return EQ(mask);									} };

//clearing
struct GLClearColor_t			{  GLfloat	r,g,b,a;												inline bool operator==(const GLClearColor_t& src)			const { return EQ(r) && EQ(g) && EQ(b) && EQ(a);			} };
struct GLClearDepth_t			{  GLdouble	d;														inline bool operator==(const GLClearDepth_t& src)			const { return EQ(d);										} };
struct GLClearStencil_t			{  GLint	s;														inline bool operator==(const GLClearStencil_t& src)		const { return EQ(s);										} };

#undef EQ

enum EGLMStateBlockType
{
	kGLAlphaTestEnable,
	kGLAlphaTestFunc,
	
	kGLCullFaceEnable,
	kGLCullFrontFace,

	kGLPolygonMode,

	kGLDepthBias,
	
	kGLScissorEnable,
	kGLScissorBox,

	kGLViewportBox,
	kGLViewportDepthRange,
	
	kGLClipPlaneEnable,
	kGLClipPlaneEquation,
	
	kGLColorMaskSingle,
	kGLColorMaskMultiple,

	kGLBlendEnable,
	kGLBlendFactor,	
	kGLBlendEquation,
	kGLBlendColor,
	kGLBlendEnableSRGB,

	kGLDepthTestEnable,
	kGLDepthFunc,
	kGLDepthMask,

	kGLStencilTestEnable,
	kGLStencilFunc,
	kGLStencilOp,	
	kGLStencilWriteMask,

	kGLClearColor,
	kGLClearDepth,
	kGLClearStencil,

	kGLAlphaToCoverageEnable,
	
	kGLMStateBlockLimit
};

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

// templated functions representing GL R/W bottlenecks
// one set of set/get/getdefault is instantiated for each of the GL*** types above.

// use these from the non array state objects
template<typename T>	void GLContextSet( T *src );
template<typename T>	void GLContextGet( T *dst );
template<typename T>	void GLContextGetDefault( T *dst );

// use these from the array state objects
template<typename T>	void GLContextSetIndexed( T *src, int index );
template<typename T>	void GLContextGetIndexed( T *dst, int index );
template<typename T>	void GLContextGetDefaultIndexed( T *dst, int index );

//===============================================================================
// template specializations for each type of state

//                                                                      --- GLAlphaTestEnable ---
FORCEINLINE void GLContextSet( GLAlphaTestEnable_t *src )
{
	glSetEnable( GL_ALPHA_TEST, src->enable != 0 );
}

FORCEINLINE void GLContextGet( GLAlphaTestEnable_t *dst )
{
	dst->enable = gGL->glIsEnabled( GL_ALPHA_TEST );
}

FORCEINLINE void GLContextGetDefault( GLAlphaTestEnable_t *dst )
{
	dst->enable = GL_FALSE;	
}

//                                                                      --- GLAlphaTestFunc ---
FORCEINLINE void GLContextSet( GLAlphaTestFunc_t *src )
{
	gGL->glAlphaFunc( src->func, src->ref );
}

FORCEINLINE void GLContextGet( GLAlphaTestFunc_t *dst )
{
	glGetEnumv( GL_ALPHA_TEST_FUNC, &dst->func );
	gGL->glGetFloatv( GL_ALPHA_TEST_REF, &dst->ref );
}

FORCEINLINE void GLContextGetDefault( GLAlphaTestFunc_t *dst )
{
	dst->func = GL_ALWAYS;
	dst->ref = 0.0f;
}

//                                                                      --- GLAlphaToCoverageEnable ---
FORCEINLINE void GLContextSet( GLAlphaToCoverageEnable_t *src )
{
	glSetEnable( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB, src->enable != 0 );
}

FORCEINLINE void GLContextGet( GLAlphaToCoverageEnable_t *dst )
{
	dst->enable = gGL->glIsEnabled( GL_SAMPLE_ALPHA_TO_COVERAGE_ARB );
}

FORCEINLINE void GLContextGetDefault( GLAlphaToCoverageEnable_t *dst )
{
	dst->enable = GL_FALSE;	
}

//                                                                      --- GLCullFaceEnable ---
FORCEINLINE void GLContextSet( GLCullFaceEnable_t *src )
{
	glSetEnable( GL_CULL_FACE, src->enable != 0 );
}

FORCEINLINE void GLContextGet( GLCullFaceEnable_t *dst )
{
	dst->enable = gGL->glIsEnabled( GL_CULL_FACE );
}

FORCEINLINE void GLContextGetDefault( GLCullFaceEnable_t *dst )
{
	dst->enable = GL_TRUE;	
}


//                                                                      --- GLCullFrontFace ---
FORCEINLINE void GLContextSet( GLCullFrontFace_t *src )
{
	gGL->glFrontFace( src->value );	// legal values are GL_CW or GL_CCW
}

FORCEINLINE void GLContextGet( GLCullFrontFace_t *dst )
{
	glGetEnumv( GL_FRONT_FACE, &dst->value );
}

FORCEINLINE void GLContextGetDefault( GLCullFrontFace_t *dst )
{
	dst->value = GL_CCW;
}


//                                                                      --- GLPolygonMode ---
FORCEINLINE void GLContextSet( GLPolygonMode_t *src )
{
	gGL->glPolygonMode( GL_FRONT, src->values[0] );
	gGL->glPolygonMode( GL_BACK, src->values[1] );
}

FORCEINLINE void GLContextGet( GLPolygonMode_t *dst )
{
	glGetEnumv( GL_POLYGON_MODE, &dst->values[0] );

}

FORCEINLINE void GLContextGetDefault( GLPolygonMode_t *dst )
{
	dst->values[0] = dst->values[1] = GL_FILL;
}


//                                                                      --- GLDepthBias ---
// note the implicit enable / disable.
// if you set non zero values, it is enabled, otherwise not.
FORCEINLINE void GLContextSet( GLDepthBias_t *src )
{
	bool enable = (src->factor != 0.0f) || (src->units != 0.0f);

	glSetEnable( GL_POLYGON_OFFSET_FILL, enable );
	gGL->glPolygonOffset( src->factor, src->units );
}

FORCEINLINE void GLContextGet( GLDepthBias_t *dst )
{
	gGL->glGetFloatv		( GL_POLYGON_OFFSET_FACTOR, &dst->factor );
	gGL->glGetFloatv		( GL_POLYGON_OFFSET_UNITS, &dst->units );
}

FORCEINLINE void GLContextGetDefault( GLDepthBias_t *dst )
{
	dst->factor		= 0.0;
	dst->units		= 0.0;
}


//                                                                      --- GLScissorEnable ---
FORCEINLINE void GLContextSet( GLScissorEnable_t *src )
{
	glSetEnable( GL_SCISSOR_TEST, src->enable != 0 );
}

FORCEINLINE void GLContextGet( GLScissorEnable_t *dst )
{
	dst->enable = gGL->glIsEnabled( GL_SCISSOR_TEST );
}

FORCEINLINE void GLContextGetDefault( GLScissorEnable_t *dst )
{
	dst->enable = GL_FALSE;
}


//                                                                      --- GLScissorBox ---
FORCEINLINE void GLContextSet( GLScissorBox_t *src )
{
	gGL->glScissor ( src->x, src->y, src->width, src->height );
}

FORCEINLINE void GLContextGet( GLScissorBox_t *dst )
{
	gGL->glGetIntegerv ( GL_SCISSOR_BOX, &dst->x );
}

FORCEINLINE void GLContextGetDefault( GLScissorBox_t *dst )
{
	// hmmmm, good question?  we can't really know a good answer so we pick a silly one
	// and the client better come back with a better answer later.
	dst->x = dst->y = 0;
	dst->width = dst->height = 16;
}


//                                                                      --- GLViewportBox ---

FORCEINLINE void GLContextSet( GLViewportBox_t *src )
{
	Assert( src->width == (int)( src->widthheight & 0xFFFF ) );
	Assert( src->height == (int)( src->widthheight >> 16 ) );
	gGL->glViewport (src->x, src->y, src->width, src->height );
}

FORCEINLINE void GLContextGet( GLViewportBox_t *dst )
{
	gGL->glGetIntegerv	( GL_VIEWPORT, &dst->x ); 
	dst->widthheight = dst->width | ( dst->height << 16 );
}

FORCEINLINE void GLContextGetDefault( GLViewportBox_t *dst )
{
	// as with the scissor box, we don't know yet, so pick a silly one and change it later
	dst->x = dst->y = 0;
	dst->width = dst->height = 16;
	dst->widthheight = dst->width | ( dst->height << 16 );
}


//                                                                      --- GLViewportDepthRange ---
FORCEINLINE void GLContextSet( GLViewportDepthRange_t *src )
{
	gGL->glDepthRange	( src->flNear, src->flFar );
}

FORCEINLINE void GLContextGet( GLViewportDepthRange_t *dst )
{
	gGL->glGetDoublev	( GL_DEPTH_RANGE, &dst->flNear );
}

FORCEINLINE void GLContextGetDefault( GLViewportDepthRange_t *dst )
{
	dst->flNear = 0.0;
	dst->flFar = 1.0;
}

//                                                                      --- GLClipPlaneEnable ---
FORCEINLINE void GLContextSetIndexed( GLClipPlaneEnable_t *src, int index )
{
#if GLMDEBUG
	if (CommandLine()->FindParm("-caps_noclipplanes"))
	{
		if (GLMKnob("caps-key",NULL) > 0.0)
		{
			// caps ON means NO clipping
			src->enable = false;
		}
	}
#endif
	glSetEnable( GL_CLIP_PLANE0 + index, src->enable != 0 );
}

FORCEINLINE void GLContextGetIndexed( GLClipPlaneEnable_t *dst, int index )
{
	dst->enable = gGL->glIsEnabled( GL_CLIP_PLANE0 + index );
}

FORCEINLINE void GLContextGetDefaultIndexed( GLClipPlaneEnable_t *dst, int index )
{
	dst->enable = 0;
}



//                                                                      --- GLClipPlaneEquation ---
FORCEINLINE void GLContextSetIndexed( GLClipPlaneEquation_t *src, int index )
{
	// shove into glGlipPlane
	GLdouble coeffs[4] = { src->x, src->y, src->z, src->w };

	gGL->glClipPlane( GL_CLIP_PLANE0 + index, coeffs );
}

FORCEINLINE void GLContextGetIndexed( GLClipPlaneEquation_t *dst, int index )
{
	DebuggerBreak();	 // do this later
	//	glClipPlane( GL_CLIP_PLANE0 + index, coeffs );
	//	GLdouble coeffs[4] = { src->x, src->y, src->z, src->w };
}

FORCEINLINE void GLContextGetDefaultIndexed( GLClipPlaneEquation_t *dst, int index )
{
	dst->x = 1.0;
	dst->y = 0.0;
	dst->z = 0.0;
	dst->w = 0.0;
}


//                                                                      --- GLColorMaskSingle ---
FORCEINLINE void GLContextSet( GLColorMaskSingle_t *src )
{
	gGL->glColorMask( src->r, src->g, src->b, src->a );
}

FORCEINLINE void GLContextGet( GLColorMaskSingle_t *dst )
{
	gGL->glGetBooleanv( GL_COLOR_WRITEMASK, (GLboolean*)&dst->r);
}

FORCEINLINE void GLContextGetDefault( GLColorMaskSingle_t *dst )
{
	dst->r = dst->g = dst->b = dst->a = 1;
}


//                                                                      --- GLColorMaskMultiple ---
FORCEINLINE void GLContextSetIndexed( GLColorMaskMultiple_t *src, int index )
{
	gGL->glColorMaskIndexedEXT ( index, src->r, src->g, src->b, src->a );
}

FORCEINLINE void GLContextGetIndexed( GLColorMaskMultiple_t *dst, int index )
{
	gGL->glGetBooleanIndexedvEXT ( GL_COLOR_WRITEMASK, index, (GLboolean*)&dst->r );
}

FORCEINLINE void GLContextGetDefaultIndexed( GLColorMaskMultiple_t *dst, int index )
{
	dst->r = dst->g = dst->b = dst->a = 1;
}


//                                                                      --- GLBlendEnable ---
FORCEINLINE void GLContextSet( GLBlendEnable_t *src )
{
	glSetEnable( GL_BLEND, src->enable != 0 );
}

FORCEINLINE void GLContextGet( GLBlendEnable_t *dst )
{
	dst->enable = gGL->glIsEnabled( GL_BLEND );
}

FORCEINLINE void GLContextGetDefault( GLBlendEnable_t *dst )
{
	dst->enable = GL_FALSE;
}


//                                                                      --- GLBlendFactor ---
FORCEINLINE void GLContextSet( GLBlendFactor_t *src )
{
	gGL->glBlendFunc ( src->srcfactor, src->dstfactor );
}

FORCEINLINE void GLContextGet( GLBlendFactor_t *dst )
{
	glGetEnumv	( GL_BLEND_SRC, &dst->srcfactor );
	glGetEnumv	( GL_BLEND_DST, &dst->dstfactor );
}

FORCEINLINE void GLContextGetDefault( GLBlendFactor_t *dst )
{
	dst->srcfactor = GL_ONE;
	dst->dstfactor = GL_ZERO;
}


//                                                                      --- GLBlendEquation ---
FORCEINLINE void GLContextSet( GLBlendEquation_t *src )
{
	gGL->glBlendEquation ( src->equation );
}

FORCEINLINE void GLContextGet( GLBlendEquation_t *dst )
{
	glGetEnumv	( GL_BLEND_EQUATION, &dst->equation );
}

FORCEINLINE void GLContextGetDefault( GLBlendEquation_t *dst )
{
	dst->equation = GL_FUNC_ADD;
}


//                                                                      --- GLBlendColor ---
FORCEINLINE void GLContextSet( GLBlendColor_t *src )
{
	gGL->glBlendColor ( src->r, src->g, src->b, src->a );
}

FORCEINLINE void GLContextGet( GLBlendColor_t *dst )
{
	gGL->glGetFloatv	( GL_BLEND_COLOR, &dst->r );
}

FORCEINLINE void GLContextGetDefault( GLBlendColor_t *dst )
{
	//solid white
	dst->r = dst->g = dst->b = dst->a = 1.0;
}


//                                                                      --- GLBlendEnableSRGB ---

#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING	0x8210
#define GL_COLOR_ATTACHMENT0						0x8CE0

FORCEINLINE void GLContextSet( GLBlendEnableSRGB_t *src )
{
#if GLMDEBUG
	// just check in debug... this is too expensive to look at on MTGL
	if (src->enable)
	{
		GLboolean	srgb_capable = false;
		gGL->glGetBooleanv( GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgb_capable);

		if (src->enable && !srgb_capable)
		{
			GLMPRINTF(("-Z- srgb-state-set FBO conflict: attempt to enable SRGB on non SRGB capable FBO config"));
		}
	}
#endif
	// this query is not useful unless you have the ARB_framebuffer_srgb ext.
	//GLint encoding = 0;
	//pfnglGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &encoding );

	glSetEnable( GL_FRAMEBUFFER_SRGB_EXT, src->enable != 0 );
}

FORCEINLINE void GLContextGet( GLBlendEnableSRGB_t *dst )
{
	//dst->enable = glIsEnabled( GL_FRAMEBUFFER_SRGB_EXT );
	dst->enable = true; // wtf ?
}

FORCEINLINE void GLContextGetDefault( GLBlendEnableSRGB_t *dst )
{
	dst->enable = GL_FALSE;
}


//                                                                      --- GLDepthTestEnable ---
FORCEINLINE void GLContextSet( GLDepthTestEnable_t *src )
{
	glSetEnable( GL_DEPTH_TEST, src->enable != 0 );
}

FORCEINLINE void GLContextGet( GLDepthTestEnable_t *dst )
{
	dst->enable = gGL->glIsEnabled( GL_DEPTH_TEST );
}

FORCEINLINE void GLContextGetDefault( GLDepthTestEnable_t *dst )
{
	dst->enable = GL_FALSE;
}


//                                                                      --- GLDepthFunc ---
FORCEINLINE void GLContextSet( GLDepthFunc_t *src )
{
	gGL->glDepthFunc				( src->func );
}

FORCEINLINE void GLContextGet( GLDepthFunc_t *dst )
{
	glGetEnumv				( GL_DEPTH_FUNC, &dst->func );
}

FORCEINLINE void GLContextGetDefault( GLDepthFunc_t *dst )
{
	dst->func = GL_GEQUAL;
}


//                                                                      --- GLDepthMask ---
FORCEINLINE void GLContextSet( GLDepthMask_t *src )
{
	gGL->glDepthMask ( src->mask );
}

FORCEINLINE void GLContextGet( GLDepthMask_t *dst )
{
	gGL->glGetBooleanv ( GL_DEPTH_WRITEMASK, (GLboolean*)&dst->mask );
}

FORCEINLINE void GLContextGetDefault( GLDepthMask_t *dst )
{
	dst->mask = GL_TRUE;
}


//                                                                      --- GLStencilTestEnable ---
FORCEINLINE void GLContextSet( GLStencilTestEnable_t *src )
{
	glSetEnable( GL_STENCIL_TEST, src->enable != 0 );
}

FORCEINLINE void GLContextGet( GLStencilTestEnable_t *dst )
{
	dst->enable = gGL->glIsEnabled( GL_STENCIL_TEST );
}

FORCEINLINE void GLContextGetDefault( GLStencilTestEnable_t *dst )
{
	dst->enable = GL_FALSE;
}


//                                                                      --- GLStencilFunc ---
FORCEINLINE void GLContextSet( GLStencilFunc_t *src )
{
	if (src->frontfunc == src->backfunc)
		gGL->glStencilFuncSeparate( GL_FRONT_AND_BACK, src->frontfunc, src->ref, src->mask);
	else
	{
		gGL->glStencilFuncSeparate( GL_FRONT, src->frontfunc, src->ref, src->mask);
		gGL->glStencilFuncSeparate( GL_BACK, src->backfunc, src->ref, src->mask);
	}
}

FORCEINLINE void GLContextGet( GLStencilFunc_t *dst )
{
	glGetEnumv		( GL_STENCIL_FUNC, &dst->frontfunc );
	glGetEnumv		( GL_STENCIL_BACK_FUNC, &dst->backfunc );
	gGL->glGetIntegerv	( GL_STENCIL_REF, &dst->ref );
	gGL->glGetIntegerv	( GL_STENCIL_VALUE_MASK, (GLint*)&dst->mask );
}

FORCEINLINE void GLContextGetDefault( GLStencilFunc_t *dst )
{
	dst->frontfunc	= GL_ALWAYS;
	dst->backfunc	= GL_ALWAYS;
	dst->ref		= 0;
	dst->mask		= 0xFFFFFFFF;
}


//                                                                      --- GLStencilOp --- indexed 0=front, 1=back

FORCEINLINE void GLContextSetIndexed( GLStencilOp_t *src, int index )
{
	GLenum face = (index==0) ? GL_FRONT : GL_BACK;
	gGL->glStencilOpSeparate( face, src->sfail, src->dpfail, src->dppass );
}

FORCEINLINE void GLContextGetIndexed( GLStencilOp_t *dst, int index )
{
	glGetEnumv		( (index==0) ? GL_STENCIL_FAIL : GL_STENCIL_BACK_FAIL, &dst->sfail );
	glGetEnumv		( (index==0) ? GL_STENCIL_PASS_DEPTH_FAIL : GL_STENCIL_BACK_PASS_DEPTH_FAIL, &dst->dpfail );
	glGetEnumv		( (index==0) ? GL_STENCIL_PASS_DEPTH_PASS : GL_STENCIL_BACK_PASS_DEPTH_PASS, &dst->dppass );
}

FORCEINLINE void GLContextGetDefaultIndexed( GLStencilOp_t *dst, int index )
{
	dst->sfail = dst->dpfail = dst->dppass = GL_KEEP;
}


//                                                                      --- GLStencilWriteMask ---
FORCEINLINE void GLContextSet( GLStencilWriteMask_t *src )
{
	gGL->glStencilMask( src->mask );
}

FORCEINLINE void GLContextGet( GLStencilWriteMask_t *dst )
{
	gGL->glGetIntegerv	( GL_STENCIL_WRITEMASK, &dst->mask );
}

FORCEINLINE void GLContextGetDefault( GLStencilWriteMask_t *dst )
{
	dst->mask = 0xFFFFFFFF;
}


//                                                                      --- GLClearColor ---
FORCEINLINE void GLContextSet( GLClearColor_t *src )
{
	gGL->glClearColor( src->r, src->g, src->b, src->a );
}

FORCEINLINE void GLContextGet( GLClearColor_t *dst )
{
	gGL->glGetFloatv		( GL_COLOR_CLEAR_VALUE, &dst->r );
}

FORCEINLINE void GLContextGetDefault( GLClearColor_t *dst )
{
	dst->r = dst->g = dst->b = 0.5;
	dst->a = 1.0;
}


//                                                                      --- GLClearDepth ---
FORCEINLINE void GLContextSet( GLClearDepth_t *src )
{
	gGL->glClearDepth ( src->d );
}

FORCEINLINE void GLContextGet( GLClearDepth_t *dst )
{
	gGL->glGetDoublev ( GL_DEPTH_CLEAR_VALUE, &dst->d );
}

FORCEINLINE void GLContextGetDefault( GLClearDepth_t *dst )
{
	dst->d = 1.0;
}


//                                                                      --- GLClearStencil ---
FORCEINLINE void GLContextSet( GLClearStencil_t *src )
{
	gGL->glClearStencil( src->s );
}

FORCEINLINE void GLContextGet( GLClearStencil_t *dst )
{
	gGL->glGetIntegerv	( GL_STENCIL_CLEAR_VALUE, &dst->s );
}

FORCEINLINE void GLContextGetDefault( GLClearStencil_t *dst )
{
	dst->s = 0;
}

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

// caching state object template.  One of these is instantiated in the context per unique struct type above
template<typename T> class GLState
{
	public:
		inline GLState()
		{
			memset( &data, 0, sizeof(data) );
			Default();
		}
		
		FORCEINLINE void Flush()
		{
			// immediately blast out the state - it makes no sense to delta it or do anything fancy because shaderapi, dxabstract, and OpenGL itself does this for us (and OpenGL calls with multithreaded drivers are very cheap)
			GLContextSet( &data );
		}
				
		// write: client src into cache
		// common case is both false.  dirty is calculated, context write is deferred.
		FORCEINLINE void Write( const T *src )
		{
			data = *src;
			Flush();
		}
						
		// default: write default value to cache, optionally write through
		inline void Default( bool noDefer=false )
		{
			GLContextGetDefault( &data );	// read default values directly to our cache copy
			Flush();
		}

		// read: sel = 0 for cache, 1 for context
		inline void Read( T *dst, int sel )
		{
			if (sel==0)
				*dst = data;
			else
				GLContextGet( dst );
		}
		
		// check: verify that context equals cache, return true if mismatched or if illegal values seen
		inline bool Check ( void )
		{
			T		temp;
			bool	result;

			GLContextGet( &temp );
			result = !(temp == data);
			return result;
		}

		FORCEINLINE const T &GetData() const { return data; }
		
	protected:
		T data;
};

// caching state object template - with multiple values behind it that are indexed
template<typename T, int COUNT> class GLStateArray
{
	public:
		inline GLStateArray()
		{
			memset( &data, 0, sizeof(data) );
			Default();
		}

		// write cache->context if dirty or forced.
		FORCEINLINE void FlushIndex( int index )
		{
			// immediately blast out the state - it makes no sense to delta it or do anything fancy because shaderapi, dxabstract, and OpenGL itself does this for us (and OpenGL calls with multithreaded drivers are very cheap)
			GLContextSetIndexed( &data[index], index );
		};

		// write: client src into cache
		// common case is both false.  dirty is calculated, context write is deferred.
		FORCEINLINE void WriteIndex( T *src, int index )
		{
			data[index] = *src;
			FlushIndex( index );	// dirty becomes false
		};
						
		// write all slots in the array
		FORCEINLINE void Flush()
		{
			for( int i=0; i < COUNT; i++)
			{
				FlushIndex( i );
			}
		}
		
		// default: write default value to cache, optionally write through
		inline void DefaultIndex( int index )
		{
			GLContextGetDefaultIndexed( &data[index], index );	// read default values directly to our cache copy
			Flush();
		};
		
		inline void Default( void )
		{
			for( int i=0; i<COUNT; i++)
			{
				DefaultIndex( i );
			}			
		}

		// read: sel = 0 for cache, 1 for context
		inline void ReadIndex( T *dst, int index, int sel )
		{
			if (sel==0)
				*dst = data[index];
			else
				GLContextGetIndexed( dst, index );
		};
		
		// check: verify that context equals cache, return true if mismatched or if illegal values seen
		inline bool CheckIndex( int index )
		{
			T		temp;
			bool	result;

			GLContextGetIndexed( &temp, index );
			result = !(temp == data[index]);
			
			return result;
		};

		inline bool Check( void )
		{
			//T		temp;
			bool	result = false;

			for( int i=0; i<COUNT; i++)
			{
				result |= CheckIndex( i );
			}
			
			return result;
		};
		
	protected:
		T		data[COUNT];
};


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



struct	GLMTexSampler
{
	CGLMTex *m_pBoundTex;				// tex which is actually bound now
	GLMTexSamplingParams m_samp;		// current 2D sampler state
};

// GLMContext will maintain one of these structures inside the context to represent the current state.
// Client can supply a new one when it wants to change the setup.
//FIXME GLMContext can do the work to migrate from old setup to new setup as efficiently as possible (but it doesn't yet)

struct GLMVertexSetup
{
	uint m_attrMask;					// which attrs are enabled (1<<n) mask where n is a GLMVertexAttributeIndex.
	
	GLMVertexAttributeDesc m_attrs[ kGLMVertexAttributeIndexMax ];

	// copied in from dxabstract, not strictly needed for operation, helps debugging
	unsigned char m_vtxAttribMap[16];

		/* high nibble is usage per _D3DDECLUSAGE
			typedef enum _D3DDECLUSAGE
			{
				D3DDECLUSAGE_POSITION		= 0,
				D3DDECLUSAGE_BLENDWEIGHT	= 1,
				D3DDECLUSAGE_BLENDINDICES	= 2,
				D3DDECLUSAGE_NORMAL			= 3,
				D3DDECLUSAGE_PSIZE			= 4,
				D3DDECLUSAGE_TEXCOORD		= 5,
				D3DDECLUSAGE_TANGENT		= 6,
				D3DDECLUSAGE_BINORMAL		= 7,
				D3DDECLUSAGE_TESSFACTOR		= 8,
				D3DDECLUSAGE_PLUGH			= 9,	// mystery value
				D3DDECLUSAGE_COLOR			= 10,
				D3DDECLUSAGE_FOG			= 11,
				D3DDECLUSAGE_DEPTH			= 12,
				D3DDECLUSAGE_SAMPLE			= 13,
			} D3DDECLUSAGE;
		
			low nibble is usageindex (i.e. POSITION0, POSITION1, etc)
			array position is attrib number.
		*/
};

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

//FIXME magic numbers here

#define	kGLMProgramParamFloat4Limit	256
#define	kGLMProgramParamBoolLimit	16
#define	kGLMProgramParamInt4Limit	16

#define	kGLMVertexProgramParamFloat4Limit	256
#define	kGLMFragmentProgramParamFloat4Limit	256

struct GLMProgramParamsF
{
	float	m_values[kGLMProgramParamFloat4Limit][4];		// float4's 256 of them
			
	int	m_firstDirtySlotNonBone;
	int	m_dirtySlotHighWaterNonBone;						// index of slot past highest dirty non-bone register (assume 0 for base of range)

	int m_dirtySlotHighWaterBone;							// index of slot past highest dirty bone register (0=first bone reg, which is DXABSTRACT_VS_FIRST_BONE_SLOT)
};

struct GLMProgramParamsB
{
	int		m_values[kGLMProgramParamBoolLimit];			// bools, 4 of them
	uint	m_dirtySlotCount;
};

struct GLMProgramParamsI
{
	int		m_values[kGLMProgramParamInt4Limit][4];			// int4s, 16 of them
	uint	m_dirtySlotCount;
};

enum EGLMParamWriteMode
{
	eParamWriteAllSlots,			// glUniform4fv of the maximum size	(not recommended if shader is down-sizing the decl)
	eParamWriteShaderSlots,			// glUniform4fv of the active slot count ("highwater")
	eParamWriteShaderSlotsOptional,	// glUniform4fv of the active slot count ("highwater") - but only if at least one has been written - it's optional
	eParamWriteDirtySlotRange		// glUniform4fv of the 0-N range where N is highest dirty slot
};

enum EGLMAttribWriteMode
{
	eAttribWriteAll,
	eAttribWriteDirty
};

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

#if GLMDEBUG
enum EGLMDebugCallSite
{
	eBeginFrame,		// inside begin frame func - frame number has been inc'd, batch number should be -1
	eClear,				// inside clear func
	eDrawElements,		// inside repeat loop, prior to draw call - batch numberhas been inc'd
	eEndFrame,			// end frame
	ePresent			// before showing pixels
};

// caller should zero one of these out and fill in the m_caller before invoking the hook
struct GLMDebugHookInfo
{
		// info from the caller to the debug hook
	EGLMDebugCallSite	m_caller;

	
		// state the hook uses to keep track of progress within a single run of the caller		
	int					m_iteration;	// which call to the hook is this.  if it's zero, it precedes any action in the caller.

	
		// bools used to communicate between caller and hook
	bool				m_loop;			// hook tells caller to loop around again (don't exit)
	bool				m_holding;		// current mood of hook, are we holding on this batch (i.e. rerun)
	
		// specific info for a draw call
	GLenum				m_drawMode;
	GLuint				m_drawStart;
	GLuint				m_drawEnd;
	GLsizei				m_drawCount;
	GLenum				m_drawType;
	const GLvoid		*m_drawIndices;
};
#endif

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

class CFlushDrawStatesStats
{
public:
	CFlushDrawStatesStats() 
	{  
		Clear(); 
	}

	void Clear()
	{
		memset(this, 0, sizeof(*this));
	}

	uint m_nTotalBatchFlushes;
	uint m_nTotalProgramPairChanges;

	uint m_nNumChangedSamplers;
	uint m_nNumSamplingParamsChanged;
	uint m_nIndexBufferChanged;
	uint m_nVertexBufferChanged;

	uint m_nFirstVSConstant;
	uint m_nNumVSConstants;
	uint m_nNumVSBoneConstants;
	uint m_nFirstPSConstant;
	uint m_nNumPSConstants;
	uint m_nNewPS;
	uint m_nNewVS;
};

//===========================================================================//
#ifndef OSX

#ifndef GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD
#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160
#endif

#define GLMGR_PINNED_MEMORY_BUFFER_SIZE ( 6 * 1024 * 1024 )

class CPinnedMemoryBuffer
{
	CPinnedMemoryBuffer( const CPinnedMemoryBuffer & );
	CPinnedMemoryBuffer & operator= ( const CPinnedMemoryBuffer & );

public:
	CPinnedMemoryBuffer()
	: 
		m_pRawBuf( NULL ) 
		, m_pBuf( NULL )
		, m_nSize( 0 )
		, m_nOfs( 0 )
		, m_nBufferObj( 0 )
#ifdef HAVE_GL_ARB_SYNC
		, m_nSyncObj( 0 )
#endif
	{
	}

	~CPinnedMemoryBuffer()
	{
		Deinit();
	}

	bool Init( uint nSize )
	{
		Deinit();

		// Guarantee 64KB alignment
		m_pRawBuf = malloc( nSize + 65535 );
		m_pBuf = reinterpret_cast<void *>((reinterpret_cast<uint64>(m_pRawBuf) + 65535) & (~65535));
		m_nSize = nSize;
		m_nOfs = 0;

		gGL->glGenBuffersARB( 1, &m_nBufferObj );
		gGL->glBindBufferARB( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_nBufferObj );

		gGL->glBufferDataARB( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_nSize, m_pBuf, GL_STREAM_COPY );

		return true;
	}

	void Deinit()
	{
		if ( !m_pRawBuf )
			return;

		BlockUntilNotBusy();

		gGL->glBindBufferARB(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_nBufferObj );

		gGL->glBufferDataARB( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0, (void*)NULL, GL_STREAM_COPY );

		gGL->glBindBufferARB( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0 );

		gGL->glDeleteBuffersARB( 1, &m_nBufferObj );
		m_nBufferObj = 0;

		free( m_pRawBuf );
		m_pRawBuf = NULL;
		m_pBuf = NULL;
	
		m_nSize = 0;
		m_nOfs = 0;
	}

	inline uint GetSize() const { return m_nSize; }
	inline uint GetOfs() const { return m_nOfs; }
	inline uint GetBytesRemaining() const { return m_nSize - m_nOfs; }
	inline void *GetPtr() const { return m_pBuf; }
	inline GLuint GetHandle() const { return m_nBufferObj; }

	void InsertFence()
	{
#ifdef HAVE_GL_ARB_SYNC
		if ( m_nSyncObj  )
		{
			gGL->glDeleteSync( m_nSyncObj );
		}

		m_nSyncObj = gGL->glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 );
#endif
	}

	void BlockUntilNotBusy()
	{
#ifdef HAVE_GL_ARB_SYNC
		if ( m_nSyncObj )
		{
			gGL->glClientWaitSync( m_nSyncObj, GL_SYNC_FLUSH_COMMANDS_BIT, 3000000000000ULL );

			gGL->glDeleteSync( m_nSyncObj );
								
			m_nSyncObj = 0;
		}
#endif
		m_nOfs = 0;
	}

	void Append( uint nSize )
	{
		m_nOfs += nSize;
		Assert( m_nOfs <= m_nSize );
	}

private:
	void *m_pRawBuf;
	void *m_pBuf;
	uint m_nSize;
	uint m_nOfs;

	GLuint m_nBufferObj;
#ifdef HAVE_GL_ARB_SYNC
	GLsync m_nSyncObj;
#endif
};
#endif // !OSX

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

class GLMContext
{
	public:
		// set/check current context (perq for many other calls)
		void	MakeCurrent( bool bRenderThread = false );
		void	ReleaseCurrent( bool bRenderThread = false );

		// CheckCurrent has been removed (it no longer compiled on Linux). To minimize churn I'm leaving
		// the inline NOP version.
		// DO NOT change this to non-inlined. It's called all over the place from very hot codepaths.
		FORCEINLINE void CheckCurrent( void ) { }
		
		void							PopulateCaps( void );	// fill out later portions of renderer info record which need context queries
		void							DumpCaps( void );		// printf all the caps info (you can call this in release too)
		const GLMRendererInfoFields&	Caps( void );			// peek at the caps record
	
		// state cache/mirror
		void	SetDefaultStates( void );
		void    ForceFlushStates();

		void	VerifyStates( void );

		// textures
		// Lock and Unlock reqs go directly to the tex object
		CGLMTex	*NewTex( GLMTexLayoutKey *key, uint levels=1, const char *debugLabel=NULL );
		void	DelTex( CGLMTex	*tex );	

			// options for Blit (replacement for ResolveTex and BlitTex)
			// pass NULL for dstTex if you want to target GL_BACK with the blit.  You get y-flip with that, don't change the dstrect yourself.		
		void	Blit2( CGLMTex *srcTex, GLMRect *srcRect, int srcFace, int srcMip, CGLMTex *dstTex, GLMRect *dstRect, int dstFace, int dstMip, uint filter );

			// tex blit (via FBO blit)
		void	BlitTex( CGLMTex *srcTex, GLMRect *srcRect, int srcFace, int srcMip, CGLMTex *dstTex, GLMRect *dstRect, int dstFace, int dstMip, uint filter, bool useBlitFB = true );

			//	MSAA resolve - we do this in GLMContext because it has to do a bunch of FBO/blit gymnastics
		void	ResolveTex( CGLMTex *tex, bool forceDirty=false );	
		
			// texture pre-load (residency forcing) - normally done one-time but you can force it
		void	PreloadTex( CGLMTex *tex, bool force=false );

		// samplers
		FORCEINLINE void SetSamplerTex( int sampler, CGLMTex *tex );
				
		FORCEINLINE void SetSamplerDirty( int sampler );
		FORCEINLINE void SetSamplerMinFilter( int sampler, GLenum Value );
		FORCEINLINE void SetSamplerMagFilter( int sampler, GLenum Value );
		FORCEINLINE void SetSamplerMipFilter( int sampler, GLenum Value );
		FORCEINLINE void SetSamplerAddressU( int sampler, GLenum Value );
		FORCEINLINE void SetSamplerAddressV( int sampler, GLenum Value );
		FORCEINLINE void SetSamplerAddressW( int sampler, GLenum Value );
		FORCEINLINE void SetSamplerStates( int sampler, GLenum AddressU, GLenum AddressV, GLenum AddressW, GLenum minFilter, GLenum magFilter, GLenum mipFilter, int minLod, float lodBias );
		FORCEINLINE void SetSamplerBorderColor( int sampler, DWORD Value );
		FORCEINLINE void SetSamplerMipMapLODBias( int sampler, DWORD Value );
		FORCEINLINE void SetSamplerMaxMipLevel( int sampler, DWORD Value );
		FORCEINLINE void SetSamplerMaxAnisotropy( int sampler, DWORD Value );
		FORCEINLINE void SetSamplerSRGBTexture( int sampler, DWORD Value );
		FORCEINLINE void SetShadowFilter( int sampler, DWORD Value );
		
		// render targets (FBO's)
		CGLMFBO	*NewFBO( void );
		void	DelFBO( CGLMFBO *fbo );		

		// programs
		CGLMProgram	*NewProgram( EGLMProgramType type, char *progString, const char *pShaderName );
		void	DelProgram( CGLMProgram *pProg );
		void	NullProgram( void );											// de-ac all shader state
		
		FORCEINLINE void SetVertexProgram( CGLMProgram *pProg );	
		FORCEINLINE void SetFragmentProgram( CGLMProgram *pProg );
		FORCEINLINE void SetProgram( EGLMProgramType nProgType, CGLMProgram *pProg ) { m_drawingProgram[nProgType] = pProg; m_bDirtyPrograms = true; }
		
		void	SetDrawingLang( EGLMProgramLang lang, bool immediate=false );	// choose ARB or GLSL.  immediate=false defers lang change to top of frame
		
		void	LinkShaderPair( CGLMProgram *vp, CGLMProgram *fp );			// ensure this combo has been linked and is in the GLSL pair cache
		void	ValidateShaderPair( CGLMProgram *vp, CGLMProgram *fp );
		void	ClearShaderPairCache( void );								// call this to shoot down all the linked pairs
		void	QueryShaderPair( int index, GLMShaderPairInfo *infoOut );	// this lets you query the shader pair cache for saving its state
		
		// buffers
		// Lock and Unlock reqs go directly to the buffer object
		CGLMBuffer	*NewBuffer( EGLMBufferType type, uint size, uint options );
		void	DelBuffer( CGLMBuffer *buff );

		FORCEINLINE void SetIndexBuffer( CGLMBuffer *buff ) { BindIndexBufferToCtx( buff );	}

		// FIXME: Remove this, it's no longer used
		FORCEINLINE void SetVertexAttributes( GLMVertexSetup *setup )
		{
			// we now just latch the vert setup and then execute on it at flushdrawstatestime if shaders are enabled.
			if ( setup )
			{
				m_drawVertexSetup = *setup;
			}
			else
			{
				memset( &m_drawVertexSetup, 0, sizeof( m_drawVertexSetup ) );
			}
		}

		// note, no API is exposed for setting a single attribute source.
		// come prepared with a complete block of attributes to use.
			
		// Queries
		CGLMQuery *NewQuery( GLMQueryParams *params );
		void DelQuery( CGLMQuery *query );
			
		// "slot" means a vec4-sized thing
		// these write into .env parameter space
		FORCEINLINE void SetProgramParametersF( EGLMProgramType type, uint baseSlot, float *slotData, uint slotCount );
		FORCEINLINE void SetProgramParametersB( EGLMProgramType type, uint baseSlot, int  *slotData, uint boolCount );	// take "BOOL" aka int
		FORCEINLINE void SetProgramParametersI( EGLMProgramType type, uint baseSlot, int  *slotData, uint slotCount );	// take int4s

		// state sync
		// If lazyUnbinding is true, unbound samplers will not actually be unbound to the GL device.
		FORCEINLINE void FlushDrawStates( uint nStartIndex, uint nEndIndex, uint nBaseVertex );				// pushes all drawing state - samplers, tex, programs, etc.
		void FlushDrawStatesNoShaders();
				
		// drawing
#ifndef OSX
		FORCEINLINE void DrawRangeElements(	GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, uint baseVertex, CGLMBuffer *pIndexBuf );
		void DrawRangeElementsNonInline(	GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, uint baseVertex, CGLMBuffer *pIndexBuf );
#else
		void DrawRangeElements( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, CGLMBuffer *pIndexBuf );
#endif

		void	CheckNative( void );
		
		// clearing
		void	Clear( bool color, unsigned long colorValue, bool depth, float depthValue, bool stencil, unsigned int stencilValue, GLScissorBox_t *rect = NULL );
		
		// display
		//void	SetVSyncEnable( bool vsyncOn );
		//void	SetFullScreen( bool fsOn, int screenIndex );		// will be latched for next BeginFrame
		//void	ActivateFullScreen( bool fsOn, int screenIndex );	// will be called by BeginFrame
		bool	SetDisplayParams( GLMDisplayParams *params );		// either the first time setup, or a change to new setup
		
		void	Present( CGLMTex *tex );		// somewhat hardwired for the time being

		// Called when IDirect3DDevice9::Reset() is called.
		void	Reset();							

		// writers for the state block inputs
		
		FORCEINLINE void	WriteAlphaTestEnable( GLAlphaTestEnable_t *src ) { m_AlphaTestEnable.Write( src ); }
		FORCEINLINE void	WriteAlphaTestFunc( GLAlphaTestFunc_t *src ) { m_AlphaTestFunc.Write( src ); }
		FORCEINLINE void	WriteAlphaToCoverageEnable( GLAlphaToCoverageEnable_t *src ) { m_AlphaToCoverageEnable.Write( src ); }
		FORCEINLINE void	WriteCullFaceEnable( GLCullFaceEnable_t *src ) { m_CullFaceEnable.Write( src ); }
		FORCEINLINE void	WriteCullFrontFace( GLCullFrontFace_t *src ) { m_CullFrontFace.Write( src ); }
		FORCEINLINE void	WritePolygonMode( GLPolygonMode_t *src ) { m_PolygonMode.Write( src ); }
		FORCEINLINE void	WriteDepthBias( GLDepthBias_t *src ) { m_DepthBias.Write( src ); }
		FORCEINLINE void	WriteClipPlaneEnable( GLClipPlaneEnable_t *src, int which ) { m_ClipPlaneEnable.WriteIndex( src, which ); }
		FORCEINLINE void	WriteClipPlaneEquation( GLClipPlaneEquation_t *src, int which ) { m_ClipPlaneEquation.WriteIndex( src, which ); }
		FORCEINLINE void	WriteScissorEnable( GLScissorEnable_t *src ) { m_ScissorEnable.Write( src ); }
		FORCEINLINE void	WriteScissorBox( GLScissorBox_t *src ) { m_ScissorBox.Write( src ); }
		FORCEINLINE void	WriteViewportBox( GLViewportBox_t *src ) { m_ViewportBox.Write( src ); }
		FORCEINLINE void	WriteViewportDepthRange( GLViewportDepthRange_t *src ) { m_ViewportDepthRange.Write( src ); }
		FORCEINLINE void	WriteColorMaskSingle( GLColorMaskSingle_t *src ) { m_ColorMaskSingle.Write( src ); }
		FORCEINLINE void	WriteColorMaskMultiple( GLColorMaskMultiple_t *src, int which ) { m_ColorMaskMultiple.WriteIndex( src, which ); }
		FORCEINLINE void	WriteBlendEnable( GLBlendEnable_t *src ) { m_BlendEnable.Write( src ); }
		FORCEINLINE void	WriteBlendFactor( GLBlendFactor_t *src ) { m_BlendFactor.Write( src ); }
		FORCEINLINE void	WriteBlendEquation( GLBlendEquation_t *src ) { m_BlendEquation.Write( src ); }
		FORCEINLINE void	WriteBlendColor( GLBlendColor_t *src ) { m_BlendColor.Write( src ); }

		FORCEINLINE void	WriteBlendEnableSRGB( GLBlendEnableSRGB_t *src ) 
		{
			if (m_caps.m_hasGammaWrites)	// only if caps allow do we actually push it through to the extension
			{
				m_BlendEnableSRGB.Write( src );
			}
			else
			{
				m_FakeBlendEnableSRGB = src->enable != 0;
			}	
			// note however that we're still tracking what this mode should be, so FlushDrawStates can look at it and adjust the pixel shader
			// if fake SRGB mode is in place (m_caps.m_hasGammaWrites is false)
		}

		FORCEINLINE void	WriteDepthTestEnable( GLDepthTestEnable_t *src ) { m_DepthTestEnable.Write( src ); }
		FORCEINLINE void	WriteDepthFunc( GLDepthFunc_t *src ) { m_DepthFunc.Write( src ); }
		FORCEINLINE void	WriteDepthMask( GLDepthMask_t *src ) { m_DepthMask.Write( src ); }
		FORCEINLINE void	WriteStencilTestEnable( GLStencilTestEnable_t *src ) { m_StencilTestEnable.Write( src ); }
		FORCEINLINE void	WriteStencilFunc( GLStencilFunc_t *src ) { m_StencilFunc.Write( src ); }
		FORCEINLINE void	WriteStencilOp( GLStencilOp_t *src, int which ) { m_StencilOp.WriteIndex( src, which ); }
		FORCEINLINE void	WriteStencilWriteMask( GLStencilWriteMask_t *src ) { m_StencilWriteMask.Write( src ); }
		FORCEINLINE void	WriteClearColor( GLClearColor_t *src ) { m_ClearColor.Write( src ); }
		FORCEINLINE void	WriteClearDepth( GLClearDepth_t *src ) { m_ClearDepth.Write( src ); }
		FORCEINLINE void	WriteClearStencil( GLClearStencil_t *src ) { m_ClearStencil.Write( src ); }

		// debug stuff
		void	BeginFrame( void );
		void	EndFrame( void );
		
		// new interactive debug stuff
#if GLMDEBUG
		void	DebugDump( GLMDebugHookInfo *info, uint options, uint vertDumpMode );
		void	DebugHook( GLMDebugHookInfo *info );
		void	DebugPresent( void );
		void	DebugClear( void );
#endif

		FORCEINLINE void SetMaxUsedVertexShaderConstantsHint( uint nMaxConstants );
		FORCEINLINE DWORD GetCurrentOwnerThreadId() const { return m_nCurOwnerThreadId; }
								
	protected:
		friend class GLMgr;				// only GLMgr can make GLMContext objects
		friend class GLMRendererInfo;	// only GLMgr can make GLMContext objects
		friend class CGLMTex;			// tex needs to be able to do binds
		friend class CGLMFBO;			// fbo needs to be able to do binds
		friend class CGLMProgram;
		friend class CGLMShaderPair;
		friend class CGLMShaderPairCache;
		friend class CGLMBuffer;
		friend class CGLMBufferSpanManager;
		friend class GLMTester;			// tester class needs access back into GLMContext
		
		friend struct IDirect3D9;
		friend struct IDirect3DDevice9;
		friend struct IDirect3DQuery9;
		
		// methods------------------------------------------
		
				// old GLMContext( GLint displayMask, GLint rendererID, PseudoNSGLContextPtr nsglShareCtx );
		GLMContext( IDirect3DDevice9 *pDevice, GLMDisplayParams *params );
		~GLMContext();

#ifndef OSX
		FORCEINLINE GLuint FindSamplerObject( const GLMTexSamplingParams &desiredParams );
#endif

		FORCEINLINE void SetBufAndVertexAttribPointer( uint nIndex, GLuint nGLName, GLuint stride, GLuint datatype, GLboolean normalized, GLuint nCompCount, const void *pBuf, uint nRevision )
		{
			VertexAttribs_t &curAttribs = m_boundVertexAttribs[nIndex];
			if ( nGLName != m_nBoundGLBuffer[kGLMVertexBuffer] )
			{
				m_nBoundGLBuffer[kGLMVertexBuffer] = nGLName;
				gGL->glBindBufferARB( GL_ARRAY_BUFFER_ARB, nGLName );
			}
			else if ( ( curAttribs.m_pPtr == pBuf ) && 
					  ( curAttribs.m_revision == nRevision ) &&
				( curAttribs.m_stride == stride ) &&
				( curAttribs.m_datatype == datatype ) &&
				( curAttribs.m_normalized == normalized ) &&
				( curAttribs.m_nCompCount == nCompCount ) )
			{
				return;
			}

			curAttribs.m_nCompCount = nCompCount;
			curAttribs.m_datatype = datatype;
			curAttribs.m_normalized = normalized;
			curAttribs.m_stride = stride;
			curAttribs.m_pPtr = pBuf;
			curAttribs.m_revision = nRevision;
			
			gGL->glVertexAttribPointer( nIndex, nCompCount, datatype, normalized, stride, pBuf );
		}

		struct CurAttribs_t
		{
			uint m_nTotalBufferRevision;
			IDirect3DVertexDeclaration9	*m_pVertDecl;
			D3DStreamDesc m_streams[ D3D_MAX_STREAMS ];
			uint64 m_vtxAttribMap[2];
		};

		CurAttribs_t m_CurAttribs;
		
		FORCEINLINE void ClearCurAttribs() 
		{ 
			m_CurAttribs.m_nTotalBufferRevision = 0;
			m_CurAttribs.m_pVertDecl = NULL;
			memset( m_CurAttribs.m_streams, 0, sizeof( m_CurAttribs.m_streams ) );
			m_CurAttribs.m_vtxAttribMap[0] = 0xBBBBBBBBBBBBBBBBULL;
			m_CurAttribs.m_vtxAttribMap[1] = 0xBBBBBBBBBBBBBBBBULL;
		}
		
		FORCEINLINE void ReleasedShader() {	NullProgram(); }
		
		// textures
		FORCEINLINE void SelectTMU( int tmu )
		{
			if ( tmu != m_activeTexture )
			{
				gGL->glActiveTexture( GL_TEXTURE0 + tmu );
				m_activeTexture = tmu;
			}
		}

		void BindTexToTMU( CGLMTex *tex, int tmu );
				
		// render targets / FBO's
		void BindFBOToCtx( CGLMFBO *fbo, GLenum bindPoint = GL_FRAMEBUFFER_EXT );				// you can also choose GL_READ_FRAMEBUFFER_EXT / GL_DRAW_FRAMEBUFFER_EXT
		
		// buffers
		FORCEINLINE void BindGLBufferToCtx( GLenum nGLBufType, GLuint nGLName, bool bForce = false )
		{
			Assert( ( nGLBufType == GL_ARRAY_BUFFER_ARB ) || ( nGLBufType == GL_ELEMENT_ARRAY_BUFFER_ARB ) );
						
			const uint nIndex = ( nGLBufType == GL_ARRAY_BUFFER_ARB ) ? kGLMVertexBuffer : kGLMIndexBuffer;
			if ( ( bForce ) || ( m_nBoundGLBuffer[nIndex] != nGLName ) )
			{
				m_nBoundGLBuffer[nIndex] = nGLName;
				gGL->glBindBufferARB( nGLBufType, nGLName );
			}
		}

		void BindBufferToCtx( EGLMBufferType type, CGLMBuffer *buff, bool force = false );		// does not twiddle any enables.

		FORCEINLINE void BindIndexBufferToCtx( CGLMBuffer *buff );
		FORCEINLINE void BindVertexBufferToCtx( CGLMBuffer *buff );
		
		GLuint CreateTex( GLenum texBind, GLenum internalFormat );
		void CleanupTex( GLenum texBind, GLMTexLayout* pLayout, GLuint tex );
		void DestroyTex( GLenum texBind, GLMTexLayout* pLayout, GLuint tex );
		GLuint FillTexCache( bool holdOne, int newTextures );
		void PurgeTexCache( );

		// debug font
		void GenDebugFontTex( void );
		void DrawDebugText( float x, float y, float z, float drawCharWidth, float drawCharHeight, char *string );

#ifndef OSX
		CPinnedMemoryBuffer *GetCurPinnedMemoryBuffer( ) { return &m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer]; }
#endif

		CPersistentBuffer* GetCurPersistentBuffer( EGLMBufferType type ) { return &( m_persistentBuffer[m_nCurPersistentBuffer][type] ); }

		// members------------------------------------------
						
		// context
		DWORD							m_nCurOwnerThreadId;
		uint							m_nThreadOwnershipReleaseCounter;

		bool							m_bUseSamplerObjects;
		bool							m_bTexClientStorage;

		IDirect3DDevice9				*m_pDevice;
		GLMRendererInfoFields			m_caps;
	
		bool							m_displayParamsValid;		// is there a param block copied in yet
		GLMDisplayParams				m_displayParams;			// last known display config, either via constructor, or by SetDisplayParams...
		
#if defined( USE_SDL )
		int								m_pixelFormatAttribs[100];	// more than enough
		PseudoNSGLContextPtr			m_nsctx;
		void *							m_ctx;
#endif
		bool							m_bUseBoneUniformBuffers; // if true, we use two uniform buffers for vertex shader constants vs. one

		// texture form table
		CGLMTexLayoutTable				*m_texLayoutTable;

		// context state mirrors
		
		GLState<GLAlphaTestEnable_t>	m_AlphaTestEnable;
		
		GLState<GLAlphaTestFunc_t>		m_AlphaTestFunc;
		
		GLState<GLCullFaceEnable_t>		m_CullFaceEnable;			
		GLState<GLCullFrontFace_t>		m_CullFrontFace;	
		GLState<GLPolygonMode_t>		m_PolygonMode;
		
		GLState<GLDepthBias_t>			m_DepthBias;		
		
		GLStateArray<GLClipPlaneEnable_t,kGLMUserClipPlanes> m_ClipPlaneEnable;
		GLStateArray<GLClipPlaneEquation_t,kGLMUserClipPlanes> m_ClipPlaneEquation;	// dxabstract puts them directly into param slot 253(0) and 254(1)
		
		GLState<GLScissorEnable_t>		m_ScissorEnable;	
		GLState<GLScissorBox_t>			m_ScissorBox;

		GLState<GLAlphaToCoverageEnable_t> m_AlphaToCoverageEnable;	
		
		GLState<GLViewportBox_t>		m_ViewportBox;		
		GLState<GLViewportDepthRange_t>	m_ViewportDepthRange;
		
		GLState<GLColorMaskSingle_t>	m_ColorMaskSingle;	
		GLStateArray<GLColorMaskMultiple_t,8>	m_ColorMaskMultiple;	// need an official constant for the color buffers limit
		
		GLState<GLBlendEnable_t>		m_BlendEnable;		
		GLState<GLBlendFactor_t>		m_BlendFactor;		
		GLState<GLBlendEquation_t> m_BlendEquation;	
		GLState<GLBlendColor_t>	m_BlendColor;		
		GLState<GLBlendEnableSRGB_t>	m_BlendEnableSRGB;		// write to this one to transmit intent to write SRGB encoded pixels to drawing FB
		bool							m_FakeBlendEnableSRGB;	// writes to above will be shunted here if fake SRGB is in effect.
		
		GLState<GLDepthTestEnable_t>	m_DepthTestEnable;	
		GLState<GLDepthFunc_t>			m_DepthFunc;		
		GLState<GLDepthMask_t>			m_DepthMask;		
		
		GLState<GLStencilTestEnable_t>	m_StencilTestEnable;	// global stencil test enable
		GLState<GLStencilFunc_t>		m_StencilFunc;			// holds front and back stencil funcs
		GLStateArray<GLStencilOp_t,2>	m_StencilOp;			// indexed: 0=front 1=back
		GLState<GLStencilWriteMask_t>	m_StencilWriteMask;		
		
		GLState<GLClearColor_t>			m_ClearColor;		
		GLState<GLClearDepth_t>			m_ClearDepth;		
		GLState<GLClearStencil_t>		m_ClearStencil;		
		
		// texture bindings and sampler setup
		int								m_activeTexture;		// mirror for glActiveTexture
		GLMTexSampler					m_samplers[GLM_SAMPLER_COUNT];
		
		uint8							m_nDirtySamplerFlags[GLM_SAMPLER_COUNT];	// 0 if the sampler is dirty, 1 if not
		uint32							m_nNumDirtySamplers;						// # of unique dirty sampler indices in m_nDirtySamplers
		uint8							m_nDirtySamplers[GLM_SAMPLER_COUNT + 1];	// dirty sampler indices

		void MarkAllSamplersDirty();
						
		struct SamplerHashEntry
		{
			GLuint m_samplerObject;
			GLMTexSamplingParams m_params;
		};

		enum 
		{ 
			cSamplerObjectHashBits = 9, cSamplerObjectHashSize = 1 << cSamplerObjectHashBits 
		};
		SamplerHashEntry				m_samplerObjectHash[cSamplerObjectHashSize];
		uint							m_nSamplerObjectHashNumEntries;
					
		// texture lock tracking - CGLMTex objects share usage of this
		CUtlVector< GLMTexLockDesc >	m_texLocks;
		
		// render target binding - check before draw
		// similar to tex sampler mechanism, we track "bound" from "chosen for drawing" separately,
		// so binding for creation/setup need not disrupt any notion of what will be used at draw time
		
		CGLMFBO							*m_boundDrawFBO;		// FBO on GL_DRAW_FRAMEBUFFER bind point
		CGLMFBO							*m_boundReadFBO;		// FBO on GL_READ_FRAMEBUFFER bind point
																// ^ both are set if you bind to GL_FRAMEBUFFER_EXT
		
		CGLMFBO							*m_drawingFBO;			// what FBO should be bound at draw time (to both read/draw bp's).

		CGLMFBO							*m_blitReadFBO;
		CGLMFBO							*m_blitDrawFBO;			// scratch FBO's for framebuffer blit
		
		CGLMFBO							*m_scratchFBO[ kGLMScratchFBOCount ];	// general purpose FBO's for internal use
		
		CUtlVector< CGLMFBO* >			m_fboTable;				// each live FBO goes in the table

		uint							m_fragDataMask;
		
		// program bindings
		EGLMProgramLang					m_drawingLangAtFrameStart;	// selector for start of frame (spills into m_drawingLang)
		EGLMProgramLang					m_drawingLang;				// selector for which language we desire to draw with on the next batch
		CGLMProgram						*m_drawingProgram[ kGLMNumProgramTypes ];
		bool							m_bDirtyPrograms;
		
		GLMProgramParamsF				m_programParamsF[ kGLMNumProgramTypes ];
		GLMProgramParamsB				m_programParamsB[ kGLMNumProgramTypes ];
		GLMProgramParamsI				m_programParamsI[ kGLMNumProgramTypes ];	// two banks, but only the vertex one is used
		EGLMParamWriteMode				m_paramWriteMode;
		
		CGLMProgram						*m_pNullFragmentProgram;		// write opaque black.  Activate when caller asks for null FP

		CGLMProgram						*m_preloadTexVertexProgram;			// programs to help preload textures (dummies)
		CGLMProgram						*m_preload2DTexFragmentProgram;
		CGLMProgram						*m_preload3DTexFragmentProgram;
		CGLMProgram						*m_preloadCubeTexFragmentProgram;

#if defined( OSX ) && defined( GLMDEBUG )
		CGLMProgram						*m_boundProgram[ kGLMNumProgramTypes ];
#endif

		CGLMShaderPairCache				*m_pairCache;				// GLSL only
		CGLMShaderPair					*m_pBoundPair;				// GLSL only

		FORCEINLINE void NewLinkedProgram() { ClearCurAttribs(); }

		//uint							m_boundPairRevision;		// GLSL only
		//GLhandleARB						m_boundPairProgram;			// GLSL only

		// buffer bindings
		GLuint							m_nBoundGLBuffer[kGLMNumBufferTypes];

		struct VertexAttribs_t
		{
			GLuint m_nCompCount;
			GLenum m_datatype;
			GLboolean m_normalized;
			GLuint m_stride;
			const void *m_pPtr;
			uint m_revision;
		};

		VertexAttribs_t					m_boundVertexAttribs[ kGLMVertexAttributeIndexMax ];	// tracked per attrib for dupe-set-absorb
		uint							m_lastKnownVertexAttribMask;								// tracked for dupe-enable-absorb
		int								m_nNumSetVertexAttributes;
						
		// FIXME: Remove this, it's no longer used
		GLMVertexSetup					m_drawVertexSetup;

		EGLMAttribWriteMode				m_attribWriteMode;
		
		bool							m_slowCheckEnable;		// turn this on or no native checking is done ("-glmassertslow" or "-glmsspewslow")
		bool							m_slowAssertEnable;		// turn this on to assert on a non-native batch	"-glmassertslow"
		bool							m_slowSpewEnable;	// turn this on to log non-native batches to stdout "-glmspewslow"
		bool							m_checkglErrorsAfterEveryBatch;	// turn this on to check for GL errors after each batch (slow) ("-glcheckerrors")
		
		// debug font texture
		CGLMTex							*m_debugFontTex;		// might be NULL unless you call GenDebugFontTex
		CGLMBuffer						*m_debugFontIndices;	// up to 1024 indices (256 chars times 4)
		CGLMBuffer						*m_debugFontVertices;	// up to 1024 verts

		// batch/frame debugging support
		int								m_debugFrameIndex;			// init to -1. Increment at BeginFrame
												
		int							    m_nMaxUsedVertexProgramConstantsHint;
		
		uint32							m_dwRenderThreadId;
		volatile bool					m_bIsThreading;

		uint m_nCurFrame;
		uint m_nBatchCounter;

		struct TextureEntry_t
		{
			GLenum m_nTexBind;
			GLenum m_nInternalFormat;
			GLuint m_nTexName;
		};

		GLuint							m_destroyPBO;
		CUtlVector< TextureEntry_t >	m_availableTextures;

#ifndef OSX
		enum { cNumPinnedMemoryBuffers = 4 };
		CPinnedMemoryBuffer m_PinnedMemoryBuffers[cNumPinnedMemoryBuffers];
		uint m_nCurPinnedMemoryBuffer;
#endif

		enum { cNumPersistentBuffers = 3 };
		CPersistentBuffer	m_persistentBuffer[cNumPersistentBuffers][kGLMNumBufferTypes];
		uint				m_nCurPersistentBuffer;

		void SaveColorMaskAndSetToDefault();
		void RestoreSavedColorMask();
		GLColorMaskSingle_t				m_SavedColorMask;
				
#if GLMDEBUG
		// interactive (DebugHook) debug support

		// using these you can implement frame advance, batch single step, and batch rewind (let it run til next frame and hold on prev batch #)
		int								m_holdFrameBegin;		// -1 if no hold req'd, otherwise # of frame to hold at (at beginframe time)
		int								m_holdFrameEnd;			// -1 if no hold req'd, otherwise # of frame to hold at (at endframe time)

		int								m_holdBatch,m_holdBatchFrame;	// -1 if no hold, else # of batch&frame to hold at (both must be set)
																// these can be expired/cleared to -1 if the frame passes without a hit
																// may be desirable to re-pause in that event, as user was expecting a hold to occur

		bool							m_debugDelayEnable;		// allow sleep delay
		uint							m_debugDelay;			// sleep time per hook call in microseconds (for usleep())
		
		// pre-draw global toggles / options
		bool							m_autoClearColor,m_autoClearDepth,m_autoClearStencil;
		float							m_autoClearColorValues[4];
		
		// debug knobs
		int								m_selKnobIndex;
		float							m_selKnobMinValue,m_selKnobMaxValue,m_selKnobIncrement;
#endif

#if GL_BATCH_PERF_ANALYSIS
		uint m_nTotalVSUniformCalls;
		uint m_nTotalVSUniformBoneCalls;
		uint m_nTotalVSUniformsSet;
		uint m_nTotalVSUniformsBoneSet;
		uint m_nTotalPSUniformCalls;
		uint m_nTotalPSUniformsSet;
		
		CFlushDrawStatesStats m_FlushStats;
#endif

	void ProcessTextureDeletes();
	CTSQueue<CGLMTex*> m_DeleteTextureQueue;
};

#ifndef OSX

FORCEINLINE void GLMContext::DrawRangeElements(	GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, uint baseVertex, CGLMBuffer *pIndexBuf )
{
#if GL_ENABLE_INDEX_VERIFICATION
	DrawRangeElementsNonInline( mode, start, end, count, type, indices, baseVertex, pIndexBuf );
#else

#if GLMDEBUG
	GLM_FUNC;
#else
	//tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %d-%d count:%d mode:%d type:%d", __FUNCTION__, start, end, count, mode, type );
#endif

	++m_nBatchCounter;

	SetIndexBuffer( pIndexBuf );

	void *indicesActual = (void*)indices;

	if ( pIndexBuf->m_bPseudo )
	{
		// you have to pass actual address, not offset
		indicesActual = (void*)( (int)indicesActual + (int)pIndexBuf->m_pPseudoBuf );
	}
	if (pIndexBuf->m_bUsingPersistentBuffer)
	{
		indicesActual = (void*)( (int)indicesActual + (int)pIndexBuf->m_nPersistentBufferStartOffset );
	}

//#if GLMDEBUG
#if 0
	bool	hasVP = m_drawingProgram[ kGLMVertexProgram ] != NULL;
	bool	hasFP = m_drawingProgram[ kGLMFragmentProgram ] != NULL;

	// init debug hook information
	GLMDebugHookInfo info;
	memset( &info, 0, sizeof(info) );
	info.m_caller = eDrawElements;

	// relay parameters we're operating under
	info.m_drawMode = mode;
	info.m_drawStart = start;
	info.m_drawEnd = end;
	info.m_drawCount = count;
	info.m_drawType = type;
	info.m_drawIndices = indices;
		
	do
	{
		// obey global options re pre-draw clear
		if ( m_autoClearColor || m_autoClearDepth || m_autoClearStencil )
		{
			GLMPRINTF(("-- DrawRangeElements auto clear" ));
			this->DebugClear();
		}

		// always sync with editable shader text prior to draw
#if GLMDEBUG
		//FIXME disengage this path if context is in GLSL mode..
		// it will need fixes to get the shader pair re-linked etc if edits happen anyway.

		if (m_drawingProgram[ kGLMVertexProgram ])
		{
			m_drawingProgram[ kGLMVertexProgram ]->SyncWithEditable();
		}
		else
		{
			AssertOnce(!"drawing with no vertex program bound");
		}


		if (m_drawingProgram[ kGLMFragmentProgram ])
		{
			m_drawingProgram[ kGLMFragmentProgram ]->SyncWithEditable();
		}
		else
		{
			AssertOnce(!"drawing with no fragment program bound");
		}
#endif
		// do the drawing
		if (hasVP && hasFP)
		{
			gGL->glDrawRangeElementsBaseVertex( mode, start, end, count, type, indicesActual, baseVertex );

			if ( m_slowCheckEnable )
			{
				CheckNative();
			}
		}
		this->DebugHook( &info );

	} while ( info.m_loop );
#else
	Assert( m_drawingLang == kGLMGLSL );

	if ( m_pBoundPair )
	{
		gGL->glDrawRangeElementsBaseVertex( mode, start, end, count, type, indicesActual, baseVertex );

#if GLMDEBUG
		if ( m_slowCheckEnable )
		{
			CheckNative();
		}
#endif
	}
#endif

#endif // GL_ENABLE_INDEX_VERIFICATION
}

#endif // #ifndef OSX

FORCEINLINE void GLMContext::SetVertexProgram( CGLMProgram *pProg )
{
	m_drawingProgram[kGLMVertexProgram] = pProg;
	m_bDirtyPrograms = true;
}

FORCEINLINE void GLMContext::SetFragmentProgram( CGLMProgram *pProg )
{
	m_drawingProgram[kGLMFragmentProgram] = pProg ? pProg : m_pNullFragmentProgram;
	m_bDirtyPrograms = true;
}

// "slot" means a vec4-sized thing
// these write into .env parameter space
FORCEINLINE void GLMContext::SetProgramParametersF( EGLMProgramType type, uint baseSlot, float *slotData, uint slotCount )
{
#if GLMDEBUG
	GLM_FUNC;
#endif

	Assert( baseSlot < kGLMProgramParamFloat4Limit );
	Assert( baseSlot+slotCount <= kGLMProgramParamFloat4Limit );

#if GLMDEBUG
	GLMPRINTF(("-S-GLMContext::SetProgramParametersF %s slots %d - %d: ", (type==kGLMVertexProgram) ? "VS" : "FS", baseSlot, baseSlot + slotCount - 1 ));
	for( uint i=0; i<slotCount; i++ )
	{
		GLMPRINTF((		"-S-    %03d: [ %7.4f %7.4f %7.4f %7.4f ]",
			baseSlot+i,
			slotData[i*4], slotData[i*4+1], slotData[i*4+2], slotData[i*4+3]
		));
	}
#endif

	memcpy( &m_programParamsF[type].m_values[baseSlot][0], slotData, (4 * sizeof(float)) * slotCount );

	if ( ( type == kGLMVertexProgram ) && ( m_bUseBoneUniformBuffers ) )
	{
		// changes here to handle vertex shaders which use constants before and after the bone array i.e. before c58 and after c216
		// a better change may be to modify the shaders and place the bone consts at either start or end - would simplify this and the flush code
		// the current supporting code (shader translator(dx9asmtogl2), param setting(here) and flushing(glmgr_flush.inl) should work unchanged, even if the const mapping is changed.
		int firstDirty = (int)baseSlot;
		int highWater  = (int)(baseSlot + slotCount);

		if ( highWater <= DXABSTRACT_VS_FIRST_BONE_SLOT )
		{
			m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = MIN( m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone, firstDirty );
			m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone, highWater );
		}
		else if ( highWater <= (DXABSTRACT_VS_LAST_BONE_SLOT+1) )
		{
			if ( firstDirty < DXABSTRACT_VS_FIRST_BONE_SLOT )
			{
				m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = MIN( m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone, firstDirty );
				m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone, MIN( DXABSTRACT_VS_FIRST_BONE_SLOT, highWater ) );
				firstDirty = DXABSTRACT_VS_FIRST_BONE_SLOT;
			}

			int nNumActualBones = ( firstDirty + slotCount ) - DXABSTRACT_VS_FIRST_BONE_SLOT;
			m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone, nNumActualBones );
		}
		else
		{
			const int maxBoneSlots = ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT;

			if ( firstDirty > DXABSTRACT_VS_LAST_BONE_SLOT )
			{
				m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = MIN( m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone, firstDirty - maxBoneSlots );
				m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone, highWater - maxBoneSlots );
			}
			else if ( firstDirty >= DXABSTRACT_VS_FIRST_BONE_SLOT )
			{
				m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone = DXABSTRACT_VS_LAST_BONE_SLOT + 1;

				m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = MIN( m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone, DXABSTRACT_VS_FIRST_BONE_SLOT );
				m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone, highWater - maxBoneSlots );
			}
			else
			{
				int nNumActualBones = ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT;
				m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone, nNumActualBones );

				m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = MIN( m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone, firstDirty );
				m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone, highWater - maxBoneSlots );
			}
		}
	}
	else
	{
		m_programParamsF[type].m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[type].m_dirtySlotHighWaterNonBone, (int)(baseSlot + slotCount) );
		m_programParamsF[type].m_firstDirtySlotNonBone = MIN( m_programParamsF[type].m_firstDirtySlotNonBone, (int)baseSlot );
	}
}

FORCEINLINE void GLMContext::SetProgramParametersB( EGLMProgramType type, uint baseSlot, int *slotData, uint boolCount )
{
#if GLMDEBUG
	GLM_FUNC;
#endif

	Assert( m_drawingLang == kGLMGLSL );
	Assert( type==kGLMVertexProgram || type==kGLMFragmentProgram );

	Assert( baseSlot < kGLMProgramParamBoolLimit );
	Assert( baseSlot+boolCount <= kGLMProgramParamBoolLimit );

#if GLMDEBUG
	GLMPRINTF(("-S-GLMContext::SetProgramParametersB %s bools %d - %d: ", (type==kGLMVertexProgram) ? "VS" : "FS", baseSlot, baseSlot + boolCount - 1 ));
	for( uint i=0; i<boolCount; i++ )
	{
		GLMPRINTF((		"-S-    %03d: %d (bool)",
			baseSlot+i,
			slotData[i]
		));
	}
#endif

	memcpy( &m_programParamsB[type].m_values[baseSlot], slotData, sizeof(int) * boolCount );
	
	if ( (baseSlot+boolCount) > m_programParamsB[type].m_dirtySlotCount)
		m_programParamsB[type].m_dirtySlotCount = baseSlot+boolCount;
}

FORCEINLINE void GLMContext::SetProgramParametersI( EGLMProgramType type, uint baseSlot, int *slotData, uint slotCount )	// groups of 4 ints...
{
#if GLMDEBUG
	GLM_FUNC;
#endif

	Assert( m_drawingLang == kGLMGLSL );
	Assert( type==kGLMVertexProgram );

	Assert( baseSlot < kGLMProgramParamInt4Limit );
	Assert( baseSlot+slotCount <= kGLMProgramParamInt4Limit );

#if GLMDEBUG
	GLMPRINTF(("-S-GLMContext::SetProgramParametersI %s slots %d - %d: ", (type==kGLMVertexProgram) ? "VS" : "FS", baseSlot, baseSlot + slotCount - 1 ));
	for( uint i=0; i<slotCount; i++ )
	{
		GLMPRINTF((		"-S-    %03d: %d %d %d %d (int4)",
			baseSlot+i,
			slotData[i*4],slotData[i*4+1],slotData[i*4+2],slotData[i*4+3]
		));
	}
#endif

	memcpy( &m_programParamsI[type].m_values[baseSlot][0], slotData, (4*sizeof(int)) * slotCount );
	
	if ( (baseSlot + slotCount) > m_programParamsI[type].m_dirtySlotCount)
	{
		m_programParamsI[type].m_dirtySlotCount = baseSlot + slotCount;
	}
}

FORCEINLINE void GLMContext::SetSamplerDirty( int sampler )
{
	Assert( sampler < GLM_SAMPLER_COUNT );
	m_nDirtySamplers[m_nNumDirtySamplers] = sampler;
	m_nNumDirtySamplers += m_nDirtySamplerFlags[sampler];
	m_nDirtySamplerFlags[sampler] = 0;
}

FORCEINLINE void GLMContext::SetSamplerTex( int sampler, CGLMTex *tex ) 
{ 
	Assert( sampler < GLM_SAMPLER_COUNT );
	m_samplers[sampler].m_pBoundTex = tex;
	if ( tex )
	{
			if ( !gGL->m_bHave_GL_EXT_direct_state_access )
			{
				if ( sampler != m_activeTexture )
				{
					gGL->glActiveTexture( GL_TEXTURE0 + sampler );
					m_activeTexture = sampler;
				}

				gGL->glBindTexture( tex->m_texGLTarget, tex->m_texName );
			}
			else
			{
				gGL->glBindMultiTextureEXT( GL_TEXTURE0 + sampler, tex->m_texGLTarget, tex->m_texName );
			}
		}
	
	if ( !m_bUseSamplerObjects )
	{
		SetSamplerDirty( sampler );
	}
}

FORCEINLINE void GLMContext::SetSamplerMinFilter( int sampler, GLenum Value )
{
	Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIN_FILTER_BITS ) );
	m_samplers[sampler].m_samp.m_packed.m_minFilter = Value;
}

FORCEINLINE void GLMContext::SetSamplerMagFilter( int sampler, GLenum Value )
{
	Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MAG_FILTER_BITS ) );
	m_samplers[sampler].m_samp.m_packed.m_magFilter = Value;
}

FORCEINLINE void GLMContext::SetSamplerMipFilter( int sampler, GLenum Value )
{
	Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIP_FILTER_BITS ) );
	m_samplers[sampler].m_samp.m_packed.m_mipFilter = Value;
}

FORCEINLINE void GLMContext::SetSamplerAddressU( int sampler, GLenum Value )
{
	Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) );
	m_samplers[sampler].m_samp.m_packed.m_addressU = Value;
}

FORCEINLINE void GLMContext::SetSamplerAddressV( int sampler, GLenum Value )
{
	Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) );
	m_samplers[sampler].m_samp.m_packed.m_addressV = Value;
}

FORCEINLINE void GLMContext::SetSamplerAddressW( int sampler, GLenum Value )
{
	Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) );
	m_samplers[sampler].m_samp.m_packed.m_addressW = Value;
}

FORCEINLINE void GLMContext::SetSamplerStates( int sampler, GLenum AddressU, GLenum AddressV, GLenum AddressW, GLenum minFilter, GLenum magFilter, GLenum mipFilter, int minLod, float lodBias )
{
	Assert( AddressU < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) );
	Assert( AddressV < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) );
	Assert( AddressW < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) );
	Assert( minFilter < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIN_FILTER_BITS ) );
	Assert( magFilter < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MAG_FILTER_BITS ) );
	Assert( mipFilter < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIP_FILTER_BITS ) );
	Assert( minLod < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIN_LOD_BITS ) );

	GLMTexSamplingParams &params = m_samplers[sampler].m_samp;
	params.m_packed.m_addressU = AddressU;
	params.m_packed.m_addressV = AddressV;
	params.m_packed.m_addressW = AddressW;
	params.m_packed.m_minFilter = minFilter;
	params.m_packed.m_magFilter = magFilter;
	params.m_packed.m_mipFilter = mipFilter;
	params.m_packed.m_minLOD = minLod;

	params.m_lodBias = lodBias;
}

FORCEINLINE void GLMContext::SetSamplerBorderColor( int sampler, DWORD Value )
{
	m_samplers[sampler].m_samp.m_borderColor = Value;
}

FORCEINLINE void GLMContext::SetSamplerMipMapLODBias( int sampler, DWORD Value )
{
	typedef union {
		DWORD asDword;
		float asFloat;
	} Convert_t;

	Convert_t c;
	c.asDword = Value;

	m_samplers[sampler].m_samp.m_lodBias = c.asFloat;
}

FORCEINLINE void GLMContext::SetSamplerMaxMipLevel( int sampler, DWORD Value )
{
	Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIN_LOD_BITS ) );
	m_samplers[sampler].m_samp.m_packed.m_minLOD = Value;
}

FORCEINLINE void GLMContext::SetSamplerMaxAnisotropy( int sampler, DWORD Value )
{
	Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MAX_ANISO_BITS ) );
	m_samplers[sampler].m_samp.m_packed.m_maxAniso = Value;
}

FORCEINLINE void GLMContext::SetSamplerSRGBTexture( int sampler, DWORD Value )
{
	Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_SRGB_BITS ) );
	m_samplers[sampler].m_samp.m_packed.m_srgb = Value;
}

FORCEINLINE void GLMContext::SetShadowFilter( int sampler, DWORD Value )
{
	Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_COMPARE_MODE_BITS ) );
	m_samplers[sampler].m_samp.m_packed.m_compareMode = Value;
}

FORCEINLINE void GLMContext::BindIndexBufferToCtx( CGLMBuffer *buff )
{
	GLMPRINTF(( "--- GLMContext::BindIndexBufferToCtx buff %p, GL name %d", buff, (buff) ? buff->m_nHandle : -1 ));
		
	Assert( !buff || ( buff->m_buffGLTarget == GL_ELEMENT_ARRAY_BUFFER_ARB ) );

	GLuint nGLName = buff ? buff->GetHandle() : 0;

	if ( m_nBoundGLBuffer[ kGLMIndexBuffer] == nGLName )
		return;

	m_nBoundGLBuffer[ kGLMIndexBuffer] = nGLName;
	gGL->glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, nGLName );
}

FORCEINLINE void GLMContext::BindVertexBufferToCtx( CGLMBuffer *buff )
{
	GLMPRINTF(( "--- GLMContext::BindVertexBufferToCtx buff %p, GL name %d", buff, (buff) ? buff->m_nHandle : -1 ));

	Assert( !buff || ( buff->m_buffGLTarget == GL_ARRAY_BUFFER_ARB ) );

	GLuint nGLName = buff ? buff->GetHandle() : 0;

	if ( m_nBoundGLBuffer[ kGLMVertexBuffer] == nGLName )
		return;

	m_nBoundGLBuffer[ kGLMVertexBuffer] = nGLName;
	gGL->glBindBufferARB( GL_ARRAY_BUFFER_ARB, nGLName );
}

FORCEINLINE void GLMContext::SetMaxUsedVertexShaderConstantsHint( uint nMaxConstants )
{
	static bool bUseMaxVertexShadeConstantHints = !CommandLine()->CheckParm("-disablemaxvertexshaderconstanthints");
	if ( bUseMaxVertexShadeConstantHints )
	{
		m_nMaxUsedVertexProgramConstantsHint = nMaxConstants;
	}
}

struct GLMTestParams
{
	GLMContext	*m_ctx;
	int			*m_testList;			// -1 termed
	
	bool		m_glErrToDebugger;
	bool		m_glErrToConsole;
	
	bool		m_intlErrToDebugger;
	bool		m_intlErrToConsole;
	
	int			m_frameCount;			// how many frames to test.
};

class GLMTester
{
	public:
	
	GLMTester(GLMTestParams *params);
	~GLMTester();
	

	// optionally callable by test routines to get basic drawables wired up
	void	StdSetup( void );
	void	StdCleanup( void );
	
	// callable by test routines to clear the frame or present it
	void	Clear( void );
	void	Present( int seed );

	// error reporting
	void	CheckGLError( const char *comment );					// obey m_params setting for console / debugger response
	void	InternalError( int errcode, char *comment );	// if errcode!=0, obey m_params setting for console / debugger response
	
	void	RunTests();

	void	RunOneTest( int testindex );

	// test routines themselves
	void	Test0();
	void	Test1();
	void	Test2();
	void	Test3();

	GLMTestParams	m_params;		// copy of caller's params, do not mutate...
	
	// std-setup stuff
	int				m_drawWidth, m_drawHeight;
	CGLMFBO			*m_drawFBO;
	CGLMTex			*m_drawColorTex;
	CGLMTex			*m_drawDepthTex;
};

class CShowPixelsParams
{
public:
	GLuint					m_srcTexName;
	int						m_width,m_height;
	bool					m_vsyncEnable;
	bool					m_fsEnable;		// want receiving view to be full screen.  for now, just target the main screen.  extend later.
	bool					m_useBlit;		// use FBO blit - sending context says it is available.
	bool					m_noBlit;		// the back buffer has already been populated by the caller (perhaps via direct MSAA resolve from multisampled RT tex)
	bool					m_onlySyncView;	// react to full/windowed state change only, do not present bits
};

#define	kMaxCrawlFrames	100
#define	kMaxCrawlText		(kMaxCrawlFrames * 256)
class CStackCrawlParams
{
	public:
	uint					m_frameLimit;							// input: max frames to retrieve
	uint					m_frameCount;							// output: frames found
	void					*m_crawl[kMaxCrawlFrames];				// call site addresses
	char					*m_crawlNames[kMaxCrawlFrames];			// pointers into text following, one per decoded name
	char					m_crawlText[kMaxCrawlText];
};

#endif // GLMGR_H