//========= Copyright Valve Corporation, All rights reserved. ============//
//

// cglmprogram.h

//	GLMgr programs (ARBVP/ARBfp)

//

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



#ifndef CGLMPROGRAM_H

#define	CGLMPROGRAM_H



#include <sys/stat.h>



#pragma once



// good ARB program references

// http://petewarden.com/notes/archives/2005/05/fragment_progra_2.html

// http://petewarden.com/notes/archives/2005/06/fragment_progra_3.html



// ext links



// http://www.opengl.org/registry/specs/ARB/vertex_program.txt

// http://www.opengl.org/registry/specs/ARB/fragment_program.txt

// http://www.opengl.org/registry/specs/EXT/gpu_program_parameters.txt





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



// tokens not in the SDK headers



//#ifndef	GL_DEPTH_STENCIL_ATTACHMENT_EXT

//	#define GL_DEPTH_STENCIL_ATTACHMENT_EXT 0x84F9

//#endif



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



// forward declarations



class GLMContext;

class CGLMShaderPair;

class CGLMShaderPairCache;



// CGLMProgram can contain two flavors of the same program, one in assembler, one in GLSL.

// these flavors are pretty different in terms of the API's that are used to activate them - 

// for example, assembler programs can just get bound to the context, whereas GLSL programs

// have to be linked.  To some extent we try to hide that detail inside GLM.



// for now, make CGLMProgram a container, it does not set policy or hold a preference as to which

// flavor you want to use.  GLMContext has to handle that. 



enum EGLMProgramType

{

	kGLMVertexProgram,

	kGLMFragmentProgram,

	

	kGLMNumProgramTypes

};



enum EGLMProgramLang

{

	kGLMARB,

	kGLMGLSL,

	

	kGLMNumProgramLangs

};



struct GLMShaderDesc

{

	union

	{

		GLuint		arb;		// ARB program object name

		GLhandleARB	glsl;		// GLSL shader object handle (void*)

	}	m_object;



	// these can change if shader text is edited

	bool	m_textPresent;	// is this flavor(lang) of text present in the buffer?

	int		m_textOffset;	// where is it

	int		m_textLength;	// how big

	

	bool	m_compiled;		// has this text been through a compile attempt

	bool	m_valid;		// and if so, was the compile successful



	int		m_slowMark;		// has it been flagged during a non native draw batch before. increment every time it's slow.

	

	int		m_highWater;	// vount of vec4's in the major uniform array ("vc" on vs, "pc" on ps)

							// written by dxabstract.... gross!

};



GLenum	GLMProgTypeToARBEnum( EGLMProgramType type );	// map vert/frag to ARB asm bind target

GLenum	GLMProgTypeToGLSLEnum( EGLMProgramType type );	// map vert/frag to ARB asm bind target



class CGLMProgram

{

public:

	friend class CGLMShaderPairCache;

	friend class CGLMShaderPair;

	friend class GLMContext;			// only GLMContext can make CGLMProgram objects

	friend class GLMTester;	

	friend class IDirect3D9;

	friend class IDirect3DDevice9;

		

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

	

	// constructor is very light, it just makes one empty program object per flavor.

	CGLMProgram( GLMContext *ctx, EGLMProgramType type );

	~CGLMProgram( );	



	void	SetProgramText			( char *text );				// import text to GLM object - invalidate any prev compiled program

	

	bool	CompileActiveSources	( void );					// compile only the flavors that were provided.

	bool	Compile					( EGLMProgramLang lang );	

	bool	CheckValidity			( EGLMProgramLang lang );



	void	LogSlow					( EGLMProgramLang lang );	// detailed spew when called for first time; one liner or perhaps silence after that

	

	void	GetLabelIndexCombo		( char *labelOut, int labelOutMaxChars, int *indexOut, int *comboOut );	

	void	GetComboIndexNameString	( char *stringOut, int stringOutMaxChars );		// mmmmmmmm-nnnnnnnn-filename

	

#if GLMDEBUG

	bool	PollForChanges( void );			// check mirror for changes.

	void	ReloadStringFromEditable( void );	// populate m_string from editable item (react to change)

	bool	SyncWithEditable( void );

#endif



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

	

	// common stuff



	GLMContext				*m_ctx;					// link back to parent context



	EGLMProgramType			m_type;					// vertex or pixel



	uint					m_serial;				// serial number for hashing

	

	char					*m_text;				// copy of text passed into constructor.  Can change if editable shaders is enabled.

													// note - it can contain multiple flavors, so use CGLMTextSectioner to scan it and locate them

#if GLMDEBUG

	CGLMEditableTextItem	*m_editable;			// editable text item for debugging

#endif	

	

	GLMShaderDesc			m_descs[ kGLMNumProgramLangs ];	



	uint					m_samplerMask;			// (1<<n) mask of sampler active locs, if this is a fragment shader (dxabstract sets this field)

};	



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



struct GLMShaderPairInfo

{

	int		m_status;		// -1 means req'd index was out of bounds (loop stop..)  0 means not present.  1 means present/active.

	

	char	m_vsName[ 128 ];

	int		m_vsStaticIndex;

	int		m_vsDynamicIndex;

	

	char	m_psName[ 128 ];

	int		m_psStaticIndex;

	int		m_psDynamicIndex;

};





class CGLMShaderPair					// a container for a linked GLSL shader pair, and metadata obtained post-link

{



public:



	friend class CGLMProgram;

	friend class GLMContext;

	friend class CGLMShaderPairCache;

		

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

	

	// constructor just sets up a GLSL program object and leaves it empty.

	CGLMShaderPair( GLMContext *ctx  );

	~CGLMShaderPair( );	



	bool	SetProgramPair			( CGLMProgram *vp, CGLMProgram *fp );

		// true result means successful link and query



	bool	RefreshProgramPair		( void );

		// re-link and re-query the uniforms

	

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

	

	// common stuff



	GLMContext				*m_ctx;					// link back to parent context



	CGLMProgram				*m_vertexProg;	

	CGLMProgram				*m_fragmentProg;



	GLhandleARB				m_program;				// linked program object



	// need meta data for attribs / samplers / params

	// actually we only need it for samplers and params.

	// attributes are hardwired.

	

	// vertex stage uniforms

	GLint					m_locVertexParams;		// "vc" per dx9asmtogl2 convention

	GLint					m_locVertexInteger0;	// "i0"

	GLint					m_locVertexBool0;		// "b0"

	GLint					m_locVertexBool1;		// "b1"

	GLint					m_locVertexBool2;		// "b2"

	GLint					m_locVertexBool3;		// "b3"

	

	// fragment stage uniforms

	GLint					m_locFragmentParams;			// "pc" per dx9asmtogl2 convention

	GLint					m_locFragmentFakeSRGBEnable;	// "flSRGBWrite" - set to 1.0 to effect sRGB encoding on output

	float					m_fakeSRGBEnableValue;			// shadow to avoid redundant sets of the m_locFragmentFakeSRGBEnable uniform

		// init it to -1.0 at link or relink, so it will trip on any legit incoming value (0.0 or 1.0)



	GLint					m_locSamplers[ 16 ];			// "sampler0 ... sampler1..."



	// other stuff

	bool					m_valid;				// true on successful link

	bool					m_samplersFixed;		// set on first draw (can't write the uniforms until the program is in use, and we don't want to mess with cur program inside cglmprogram)

	uint					m_revision;				// if this pair is relinked, bump this number.

};	



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



// N-row, M-way associative cache with LRU per row.

// still needs some metric dump ability and some parameter tuning.

// extra credit would be to make an auto-tuner.



struct CGLMPairCacheEntry

{

	long long		m_lastMark;				// a mark of zero means an empty entry

	CGLMProgram		*m_vertexProg;

	CGLMProgram		*m_fragmentProg;

	uint			m_extraKeyBits;

	CGLMShaderPair	*m_pair;

};



class CGLMShaderPairCache				// cache for linked GLSL shader pairs

{



public:



protected:

	friend class CGLMShaderPair;

	friend class CGLMProgram;

	friend class GLMContext;

		

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

	

	CGLMShaderPairCache( GLMContext *ctx  );

	~CGLMShaderPairCache( );	



	CGLMShaderPair	*SelectShaderPair	( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits );

	void			QueryShaderPair		( int index, GLMShaderPairInfo *infoOut );

	

	// shoot down linked pairs that use the program in the arg

	// return true if any had to be skipped due to conflict with currently bound pair

	bool			PurgePairsWithShader( CGLMProgram *prog );

	

	// purge everything (when would GLM know how to do this ?  at context destroy time, but any other times?)

	// return true if any had to be skipped due to conflict with currently bound pair

	bool			Purge				( void );

	

	// stats

	void			DumpStats			( void );

	

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



	uint			HashRowIndex		( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits );

	CGLMPairCacheEntry*	HashRowPtr		( uint hashRowIndex );

	void			HashRowProbe		( CGLMPairCacheEntry *row, CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits, int *hitwayOut, int *emptywayOut, int *oldestwayOut );

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



	// common stuff



	GLMContext				*m_ctx;					// link back to parent context



	long long				m_mark;



	uint					m_rowsLg2;

	uint					m_rows;

	

	uint					m_waysLg2;

	uint					m_ways;

	

	uint					m_entryCount;

	

	CGLMPairCacheEntry		*m_entries;				// array[ m_rows ][ m_ways ]



	uint					*m_evictions;			// array[ m_rows ];

	uint					*m_hits;				// array[ m_rows ];

};	





#endif