//============ Copyright (c) Valve Corporation, All rights reserved. ============
//
// cglmtex.h
//	GLMgr textures
//
//===============================================================================

#ifndef CGLMTEX_H
#define	CGLMTEX_H

#pragma once

#ifdef OSX
#include "glmgrbasics.h"
#endif

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

// forward declarations

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

class	IDirect3DSurface9;

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

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

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

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

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


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

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

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

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

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

struct LessThan_GLMTexLayoutKey
{
	bool operator()(const GLMTexLayoutKey &a, const GLMTexLayoutKey &b) const
	{
		#define	DO_LESS(fff) if (a.fff != b.fff) { return (a.fff< b.fff); }
	
		DO_LESS(m_texGLTarget);
		DO_LESS(m_texFormat);
		DO_LESS(m_texFlags);
		DO_LESS(m_xSize);
		DO_LESS(m_ySize)
		DO_LESS(m_zSize);
	
		#undef DO_LESS
		
		return false;	// they are equal
	}
};

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

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

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

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

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

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

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


struct	GLMTexSamplingParams
{
	GLenum	m_addressModes[3];	// S, T, R
	GLfloat	m_borderColor[4];	// R,G,B,A

	GLenum	m_magFilter;
	GLenum	m_minFilter;
	
	GLfloat	m_mipmapBias;
	GLint	m_minMipLevel;
	GLint	m_maxMipLevel;
	GLint	m_maxAniso;
	GLenum	m_compareMode;		// only used for depth and stencil type textures
	bool	m_srgb;				// srgb texture read... 
};

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

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

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

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

#define	GLM_SAMPLER_COUNT	16

typedef unsigned short CTexBindMask;			// 16 bits, who needs bitvec...

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

class CGLMTex
{

public:

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

	friend class IDirect3DDevice9;
	friend class IDirect3DBaseTexture9;
	friend class IDirect3DTexture9;
	friend class IDirect3DSurface9;
	friend class IDirect3DCubeTexture9;
	friend class IDirect3DVolumeTexture9;
	
			CGLMTex( GLMContext *ctx, GLMTexLayout *layout, GLMTexSamplingParams *sampling, const char *debugLabel = NULL );
			~CGLMTex( );
	
	int						CalcSliceIndex( int face, int mip );
	void					CalcTexelDataOffsetAndStrides( int sliceIndex, int x, int y, int z, int *offsetOut, int *yStrideOut, int *zStrideOut );

	void					ApplySamplingParams( GLMTexSamplingParams *params, bool noCheck=FALSE );

	void					ReadTexels( GLMTexLockDesc *desc, bool readWholeSlice=true );
	void					WriteTexels( GLMTexLockDesc *desc, bool writeWholeSlice=true, bool noDataWrite=false );
		// last param lets us send NULL data ptr (only legal with uncompressed formats, beware)
		// this helps out ResetSRGB.
	
	void					ResetSRGB( bool srgb, bool noDataWrite );
		// re-specify texture format to match desired sRGB form
		// noWrite means send NULL for texel source addresses instead of actual data - ideal for RT's

	GLMTexLayout			*m_layout;		// layout of texture (shared across all tex with same layout)
	int						m_minActiveMip;//index of lowest mip that has been written.  used to drive setting of GL_TEXTURE_MAX_LEVEL.
	int						m_maxActiveMip;//index of highest mip that has been written.  used to drive setting of GL_TEXTURE_MAX_LEVEL.
				
	GLMTexSamplingParams	m_sampling;		// mirror of sampling params currently embodied in the texture
											// (consult this at draw time, in order to know if changes need to be made)
						
	GLMContext				*m_ctx;			// link back to parent context

	GLuint					m_texName;			// name of this texture in the context
	bool					m_texClientStorage;	// was CS selecetd for texture
	bool					m_texPreloaded;		// has it been kicked into VRAM with GLMContext::PreloadTex yet

	GLuint					m_rboName;		// name of MSAA RBO backing the tex if MSAA enabled (or zero)
	bool					m_rboDirty;		// has RBO been drawn on - i.e. needs to be blitted back to texture if texture is going to be sampled from
	
	CTexBindMask			m_bindPoints;	// true for each place in the parent ctx where currently
											// bound (indexed via EGLMTexCtxBindingIndex)
										
	int						m_rtAttachCount; // how many RT's have this texture attached somewhere

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

	std::vector<unsigned char>	m_sliceFlags;
	
	char					*m_debugLabel;	// strdup() of debugLabel passed in, or NULL
};


#endif