diff --git a/public/togles/glfuncs.inl b/public/togles/glfuncs.inl new file mode 100644 index 00000000..1f6b3b45 --- /dev/null +++ b/public/togles/glfuncs.inl @@ -0,0 +1,2 @@ +#include "togl/linuxwin/glfuncs.h" + diff --git a/public/togles/linuxwin/cglmbuffer.h b/public/togles/linuxwin/cglmbuffer.h new file mode 100644 index 00000000..10782243 --- /dev/null +++ b/public/togles/linuxwin/cglmbuffer.h @@ -0,0 +1,262 @@ +//========= 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. +// +// cglmprogram.h +// GLMgr buffers (index / vertex) +// ... maybe add PBO later as well +//=============================================================================== + +#ifndef CGLMBUFFER_H +#define CGLMBUFFER_H + +#pragma once + +//=============================================================================== + +extern bool g_bUsePseudoBufs; + +// forward declarations + +class GLMContext; + +enum EGLMBufferType +{ + kGLMVertexBuffer, + kGLMIndexBuffer, + kGLMUniformBuffer, // for bindable uniform + kGLMPixelBuffer, // for PBO + + kGLMNumBufferTypes +}; + +// pass this in "options" to constructor to make a dynamic buffer +#define GLMBufferOptionDynamic 0x00000001 + +struct GLMBuffLockParams +{ + uint m_nOffset; + uint m_nSize; + bool m_bNoOverwrite; + bool m_bDiscard; +}; + +#define GL_STATIC_BUFFER_SIZE ( 2048 * 1024 ) +#define GL_MAX_STATIC_BUFFERS 2 + +extern void glBufferSubDataMaxSize( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data, uint nMaxSizePerCall = 128 * 1024 ); + +//===========================================================================// + +// Creates an immutable storage for a buffer object +// https://www.opengl.org/registry/specs/ARB/buffer_storage.txt +class CPersistentBuffer +{ +public: + + CPersistentBuffer(); + ~CPersistentBuffer(); + + void Init( EGLMBufferType type,uint nSize ); + void Deinit(); + + void InsertFence(); + void BlockUntilNotBusy(); + + void Append( uint nSize ); + + inline uint GetBytesRemaining() const { return m_nSize - m_nOffset; } + inline uint GetOffset() const { return m_nOffset; } + inline void *GetPtr() const { return m_pImmutablePersistentBuf; } + inline GLuint GetHandle() const { return m_nHandle; } + +private: + + CPersistentBuffer( const CPersistentBuffer & ); + CPersistentBuffer & operator= (const CPersistentBuffer &); + + uint m_nSize; + + EGLMBufferType m_type; + GLenum m_buffGLTarget; // GL_ARRAY_BUFFER_ARB / GL_ELEMENT_BUFFER_ARB + GLuint m_nHandle; // handle of this program in the GL context + + // Holds a pointer to the persistently mapped buffer + void* m_pImmutablePersistentBuf; + + uint m_nOffset; + +#ifdef HAVE_GL_ARB_SYNC + GLsync m_nSyncObj; +#endif +}; + +//=============================================================================== + +#if GL_ENABLE_INDEX_VERIFICATION + +struct GLDynamicBuf_t +{ + GLenum m_nGLType; + uint m_nHandle; + uint m_nActualBufSize; + uint m_nSize; + + uint m_nLockOffset; + uint m_nLockSize; +}; + +class CGLMBufferSpanManager +{ + CGLMBufferSpanManager( const CGLMBufferSpanManager& ); + CGLMBufferSpanManager& operator= ( const CGLMBufferSpanManager& ); + +public: + CGLMBufferSpanManager(); + ~CGLMBufferSpanManager(); + + void Init( GLMContext *pContext, EGLMBufferType nBufType, uint nInitialCapacity, uint nBufSize, bool bDynamic ); + void Deinit(); + + inline GLMContext *GetContext() const { return m_pCtx; } + + inline GLenum GetGLBufType() const { return ( m_nBufType == kGLMVertexBuffer ) ? GL_ARRAY_BUFFER_ARB : GL_ELEMENT_ARRAY_BUFFER_ARB; } + + struct ActiveSpan_t + { + uint m_nStart; + uint m_nEnd; + + GLDynamicBuf_t m_buf; + bool m_bOriginalAlloc; + + inline ActiveSpan_t() { } + inline ActiveSpan_t( uint nStart, uint nEnd, GLDynamicBuf_t &buf, bool bOriginalAlloc ) : m_nStart( nStart ), m_nEnd( nEnd ), m_buf( buf ), m_bOriginalAlloc( bOriginalAlloc ) { Assert( nStart <= nEnd ); } + }; + + ActiveSpan_t *AddSpan( uint nOffset, uint nMaxSize, uint nActualSize, bool bDiscard, bool bNoOverwrite ); + + void DiscardAllSpans(); + + bool IsValid( uint nOffset, uint nSize ) const; + +private: + bool AllocDynamicBuf( uint nSize, GLDynamicBuf_t &buf ); + void ReleaseDynamicBuf( GLDynamicBuf_t &buf ); + + GLMContext *m_pCtx; + + EGLMBufferType m_nBufType; + + uint m_nBufSize; + bool m_bDynamic; + + CUtlVector m_ActiveSpans; + CUtlVector m_DeletedSpans; + + int m_nSpanEndMax; + + int m_nNumAllocatedBufs; + int m_nTotalBytesAllocated; +}; + +#endif // GL_ENABLE_INDEX_VERIFICATION + +class CGLMBuffer +{ +public: + void Lock( GLMBuffLockParams *pParams, char **pAddressOut ); + void Unlock( int nActualSize = -1, const void *pActualData = NULL ); + + GLuint GetHandle() const; + + friend class GLMContext; // only GLMContext can make CGLMBuffer objects + friend class GLMTester; + friend struct IDirect3D9; + friend struct IDirect3DDevice9; + + CGLMBuffer( GLMContext *pCtx, EGLMBufferType type, uint size, uint options ); + ~CGLMBuffer(); + + void SetModes( bool bAsyncMap, bool bExplicitFlush, bool bForce = false ); + void FlushRange( uint offset, uint size ); + +#if GL_ENABLE_INDEX_VERIFICATION + bool IsSpanValid( uint nOffset, uint nSize ) const; +#endif + + GLMContext *m_pCtx; // link back to parent context + EGLMBufferType m_type; + uint m_nSize; + uint m_nActualSize; + + bool m_bDynamic; + + GLenum m_buffGLTarget; // GL_ARRAY_BUFFER_ARB / GL_ELEMENT_BUFFER_ARB + GLuint m_nHandle; // name of this program in the context + + uint m_nRevision; // bump anytime the size changes or buffer is orphaned + + bool m_bEnableAsyncMap; // mirror of the buffer state + bool m_bEnableExplicitFlush; // mirror of the buffer state + + bool m_bMapped; // is it currently mapped + + uint m_dirtyMinOffset; // when equal, range is empty + uint m_dirtyMaxOffset; + + float *m_pLastMappedAddress; + + int m_nPinnedMemoryOfs; + + uint m_nPersistentBufferStartOffset; + bool m_bUsingPersistentBuffer; + + bool m_bPseudo; // true if the m_name is 0, and the backing is plain RAM + + // in pseudo mode, there is just one RAM buffer that acts as the backing. + // expectation is that this mode would only be used for dynamic indices. + // since indices have to be consumed (copied to command stream) prior to return from a drawing call, + // there's no need to do any fencing or multibuffering. orphaning in particular becomes a no-op. + + char *m_pActualPseudoBuf; // storage for pseudo buffer + char *m_pPseudoBuf; // storage for pseudo buffer + char *m_pStaticBuffer; + + GLMBuffLockParams m_LockParams; + + static char ALIGN16 m_StaticBuffers[ GL_MAX_STATIC_BUFFERS ][ GL_STATIC_BUFFER_SIZE ] ALIGN16_POST; + static bool m_bStaticBufferUsed[ GL_MAX_STATIC_BUFFERS ]; + +#if GL_ENABLE_INDEX_VERIFICATION + CGLMBufferSpanManager m_BufferSpanManager; +#endif + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + uint m_nDirtyRangeStart; + uint m_nDirtyRangeEnd; +#endif +}; + +#endif // CGLMBUFFER_H + diff --git a/public/togles/linuxwin/cglmfbo.h b/public/togles/linuxwin/cglmfbo.h new file mode 100644 index 00000000..93ebb11f --- /dev/null +++ b/public/togles/linuxwin/cglmfbo.h @@ -0,0 +1,104 @@ +//========= 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. +// +// cglmfbo.h +// GLMgr FBO's (render targets) +// +//=============================================================================== + +#ifndef CGLMFBO_H +#define CGLMFBO_H + +#pragma once + +// good FBO references / recaps +// http://www.songho.ca/opengl/gl_fbo.html +// http://www.gamedev.net/reference/articles/article2331.asp + +// ext links + +// http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt +// http://www.opengl.org/registry/specs/EXT/framebuffer_multisample.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; + +enum EGLMFBOAttachment +{ + kAttColor0, kAttColor1, kAttColor2, kAttColor3, + kAttDepth, kAttStencil, kAttDepthStencil, + kAttCount +}; + +struct GLMFBOTexAttachParams +{ + CGLMTex *m_tex; + int m_face; // keep zero if not cube map + int m_mip; // keep zero if notmip mapped + int m_zslice; // keep zero if not a 3D tex +}; + +class CGLMFBO +{ + friend class GLMContext; + friend class GLMTester; + friend class CGLMTex; + + friend struct IDirect3D9; + friend struct IDirect3DDevice9; + +public: + CGLMFBO( GLMContext *ctx ); + ~CGLMFBO( ); + +protected: + void TexAttach( GLMFBOTexAttachParams *params, EGLMFBOAttachment attachIndex, GLenum fboBindPoint = GL_FRAMEBUFFER_EXT ); + void TexDetach( EGLMFBOAttachment attachIndex, GLenum fboBindPoint = GL_FRAMEBUFFER_EXT ); + // you can also pass GL_READ_FRAMEBUFFER_EXT or GL_DRAW_FRAMEBUFFER_EXT to selectively bind the receiving FBO to one or the other. + + void TexScrub( CGLMTex *tex ); + // search and destroy any attachment for the named texture + + bool IsReady( void ); // aka FBO completeness check - ready to draw + + GLMContext *m_ctx; // link back to parent context + + GLuint m_name; // name of this FBO in the context + + GLMFBOTexAttachParams m_attach[ kAttCount ]; // indexed by EGLMFBOAttachment +}; + + +#endif \ No newline at end of file diff --git a/public/togles/linuxwin/cglmprogram.h b/public/togles/linuxwin/cglmprogram.h new file mode 100644 index 00000000..64dbc121 --- /dev/null +++ b/public/togles/linuxwin/cglmprogram.h @@ -0,0 +1,458 @@ +//========= 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. +// +// cglmprogram.h +// GLMgr programs (ARBVP/ARBfp) +// +//=============================================================================== + +#ifndef CGLMPROGRAM_H +#define CGLMPROGRAM_H + +#include + +#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; // count of vec4's in the major uniform array ("vc" on vs, "pc" on ps) + // written by dxabstract.... gross! + int m_VSHighWaterBone; // count of vec4's in the bone-specific uniform array (only valid for vertex shaders) +}; + +GLenum GLMProgTypeToARBEnum( EGLMProgramType type ); // map vert/frag to ARB asm bind target +GLenum GLMProgTypeToGLSLEnum( EGLMProgramType type ); // map vert/frag to ARB asm bind target + +#define GL_SHADER_PAIR_CACHE_STATS 0 + +class CGLMProgram +{ +public: + friend class CGLMShaderPairCache; + friend class CGLMShaderPair; + friend class GLMContext; // only GLMContext can make CGLMProgram objects + friend class GLMTester; + friend struct IDirect3D9; + friend struct 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 + void SetShaderName ( const char *name ); // only used for debugging/telemetry markup + + void CompileActiveSources ( void ); // compile only the flavors that were provided. + void 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 + + unsigned long m_nHashTag; // 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<> 16 ); + // Apply half pixel offset to output vertices to account for the pixel center difference between D3D9 and OpenGL. + // We output vertices in clip space, which ranges from [-1,1], so 1.0/width in clip space transforms into .5/width in screenspace, see: "Viewports and Clipping (Direct3D 9)" in the DXSDK + float v[4] = { 1.0f / fWidth, 1.0f / fHeight, fWidth, fHeight }; + if ( m_locVertexScreenParams >= 0 ) + gGL->glUniform4fv( m_locVertexScreenParams, 1, v ); + } + + //=============================== + + // 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_locVertexBoneParams; // "vcbones" + GLint m_locVertexInteger0; // "i0" + + enum { cMaxVertexShaderBoolUniforms = 4, cMaxFragmentShaderBoolUniforms = 1 }; + + GLint m_locVertexBool[cMaxVertexShaderBoolUniforms]; // "b0", etc. + GLint m_locFragmentBool[cMaxFragmentShaderBoolUniforms]; // "fb0", etc. + bool m_bHasBoolOrIntUniforms; + + // fragment stage uniforms + GLint m_locFragmentParams; // "pc" per dx9asmtogl2 convention + + int m_NumUniformBufferParams[kGLMNumProgramTypes]; + GLint m_UniformBufferParams[kGLMNumProgramTypes][256]; + + 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[ GLM_SAMPLER_COUNT ]; // "sampler0 ... sampler1..." + + // other stuff + bool m_valid; // true on successful link + bool m_bCheckLinkStatus; + uint m_revision; // if this pair is relinked, bump this number. + + GLint m_locVertexScreenParams; // vcscreen + uint m_nScreenWidthHeight; + +}; + +//=============================================================================== + +// 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( ); + + FORCEINLINE 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 ); + + //=============================== + + FORCEINLINE uint HashRowIndex( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits ) const; + FORCEINLINE CGLMPairCacheEntry* HashRowPtr( uint hashRowIndex ) const; + + FORCEINLINE void HashRowProbe( CGLMPairCacheEntry *row, CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits, int &hitway, int &emptyway, int &oldestway ); + + CGLMShaderPair *SelectShaderPairInternal( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits, int rowIndex ); + //=============================== + + // common stuff + + GLMContext *m_ctx; // link back to parent context + + long long m_mark; + + uint m_rowsLg2; + uint m_rows; + uint m_rowsMask; + + uint m_waysLg2; + uint m_ways; + + uint m_entryCount; + + CGLMPairCacheEntry *m_entries; // array[ m_rows ][ m_ways ] + + uint *m_evictions; // array[ m_rows ]; + +#if GL_SHADER_PAIR_CACHE_STATS + uint *m_hits; // array[ m_rows ]; +#endif +}; + +FORCEINLINE uint CGLMShaderPairCache::HashRowIndex( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits ) const +{ + return ( vp->m_nHashTag + fp->m_nHashTag + extraKeyBits * 7 ) & m_rowsMask; +} + +FORCEINLINE CGLMPairCacheEntry* CGLMShaderPairCache::HashRowPtr( uint hashRowIndex ) const +{ + return &m_entries[ hashRowIndex * m_ways ]; +} + +FORCEINLINE void CGLMShaderPairCache::HashRowProbe( CGLMPairCacheEntry *row, CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits, int& hitway, int& emptyway, int& oldestway ) +{ + hitway = -1; + emptyway = -1; + oldestway = -1; + + // scan this row to see if the desired pair is present + CGLMPairCacheEntry *cursor = row; + long long oldestmark = 0xFFFFFFFFFFFFFFFFLL; + + for( uint way = 0; way < m_ways; ++way ) + { + if ( cursor->m_lastMark != 0 ) // occupied slot + { + // check if this is the oldest one on the row - only occupied slots are checked + if ( cursor->m_lastMark < oldestmark ) + { + oldestway = way; + oldestmark = cursor->m_lastMark; + } + + if ( ( cursor->m_vertexProg == vp ) && ( cursor->m_fragmentProg == fp ) && ( cursor->m_extraKeyBits == extraKeyBits ) ) // match? + { + // found it + hitway = way; + break; + } + } + else + { + // empty way, log it if first one seen + if (emptyway<0) + { + emptyway = way; + } + } + cursor++; + } +} + +FORCEINLINE CGLMShaderPair *CGLMShaderPairCache::SelectShaderPair( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits ) +{ + // select row where pair would be found if it exists + uint rowIndex = HashRowIndex( vp, fp, extraKeyBits ); + + CGLMPairCacheEntry *pCursor = HashRowPtr( rowIndex ); + + if ( ( pCursor->m_fragmentProg != fp ) || ( pCursor->m_vertexProg != vp ) || ( pCursor->m_extraKeyBits != extraKeyBits ) ) + { + CGLMPairCacheEntry *pLastCursor = pCursor + m_ways; + + ++pCursor; + + while ( pCursor != pLastCursor ) + { + if ( ( pCursor->m_fragmentProg == fp ) && ( pCursor->m_vertexProg == vp ) && ( pCursor->m_extraKeyBits == extraKeyBits ) ) // match? + break; + ++pCursor; + }; + + if ( pCursor == pLastCursor ) + return SelectShaderPairInternal( vp, fp, extraKeyBits, rowIndex ); + } + + // found it. mark it and return + pCursor->m_lastMark = m_mark++; + +#if GL_SHADER_PAIR_CACHE_STATS + // count the hit + m_hits[ rowIndex ] ++; +#endif + + return pCursor->m_pair; +} + +#endif diff --git a/public/togles/linuxwin/cglmquery.h b/public/togles/linuxwin/cglmquery.h new file mode 100644 index 00000000..168e3f7c --- /dev/null +++ b/public/togles/linuxwin/cglmquery.h @@ -0,0 +1,108 @@ +//========= 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. +// +// cglmquery.h +// GLMgr queries +// +//=============================================================================== + +#ifndef CGLMQUERY_H +#define CGLMQUERY_H + +#pragma once + +//=============================================================================== + +// forward declarations + +class GLMContext; +class CGLMQuery; + +//=============================================================================== + +enum EGLMQueryType +{ + EOcclusion, + EFence, + EGLMQueryCount +}; + +struct GLMQueryParams +{ + EGLMQueryType m_type; +}; + +class CGLMQuery +{ + // leave everything public til it's running +public: + friend class GLMContext; // only GLMContext can make CGLMTex objects + friend struct IDirect3DDevice9; + friend struct IDirect3DQuery9; + + GLMContext *m_ctx; // link back to parent context + GLMQueryParams m_params; // params created with + + GLuint m_name; // name of the query object per se - could be fence, could be query object ... NOT USED WITH GL_ARB_sync! +#ifdef HAVE_GL_ARB_SYNC + GLsync m_syncobj; // GL_ARB_sync object. NOT USED WITH GL_NV_fence or GL_APPLE_fence! +#else + GLuint m_syncobj; +#endif + + bool m_started; + bool m_stopped; + bool m_done; + + bool m_nullQuery; // was gl_nullqueries true at Start time - if so, continue to act like a null query through Stop/IsDone/Complete time + // restated - only Start should examine the convar. + static uint s_nTotalOcclusionQueryCreatesOrDeletes; + + CGLMQuery( GLMContext *ctx, GLMQueryParams *params ); + ~CGLMQuery( ); + + // for an occlusion query: + // Start = BeginQuery query-start goes into stream + // Stop = EndQuery query-end goes into stream - a fence is also set so we can probe for completion + // IsDone = TestFence use the added fence to ask if query-end has passed (i.e. will Complete block?) + // Complete = GetQueryObjectuivARB(uint id, enum pname, uint *params) - extract the sample count + + // for a fence query: + // Start = SetFence fence goes into command stream + // Stop = NOP fences are self finishing - no need to call Stop on a fence + // IsDone = TestFence ask if fence passed + // Complete = FinishFence + + void Start ( void ); + void Stop ( void ); + bool IsDone ( void ); + void Complete ( uint *result ); + + // accessors for the started/stopped state + bool IsStarted ( void ); + bool IsStopped ( void ); +}; + + +#endif diff --git a/public/togles/linuxwin/cglmtex.h b/public/togles/linuxwin/cglmtex.h new file mode 100644 index 00000000..55321316 --- /dev/null +++ b/public/togles/linuxwin/cglmtex.h @@ -0,0 +1,547 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// cglmtex.h +// GLMgr textures +// +//=============================================================================== + +#ifndef CGLMTEX_H +#define CGLMTEX_H + +#pragma once + +#ifdef OSX +#include "glmgrbasics.h" +#endif +#include "tier1/utlhash.h" +#include "tier1/utlmap.h" + +//=============================================================================== + +// forward declarations + +class GLMContext; +class GLMTester; +class CGLMTexLayoutTable; +class CGLMTex; +class CGLMFBO; + +struct IDirect3DSurface9; + +#if GLMDEBUG +extern CGLMTex *g_pFirstCGMLTex; +#endif + +// For GL_EXT_texture_sRGB_decode +#ifndef GL_TEXTURE_SRGB_DECODE_EXT +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#endif + +#ifndef GL_DECODE_EXT +#define GL_DECODE_EXT 0x8A49 +#endif + +#ifndef GL_SKIP_DECODE_EXT +#define GL_SKIP_DECODE_EXT 0x8A4A +#endif + +//=============================================================================== + +struct GLMTexFormatDesc +{ + const char *m_formatSummary; // for debug visibility + + D3DFORMAT m_d3dFormat; // what D3D knows it as; see public/bitmap/imageformat.h + + GLenum m_glIntFormat; // GL internal format + GLenum m_glIntFormatSRGB; // internal format if SRGB flavor + GLenum m_glDataFormat; // GL data format + GLenum m_glDataType; // GL data type + + int m_chunkSize; // 1 or 4 - 4 is used for compressed textures + int m_bytesPerSquareChunk; // how many bytes for the smallest quantum (m_chunkSize x m_chunkSize) + // this description lets us calculate size cleanly without conditional logic for compression +}; +const GLMTexFormatDesc *GetFormatDesc( D3DFORMAT format ); + +//=============================================================================== + +// utility function for generating slabs of texels. mostly for test. +typedef struct +{ + // in + D3DFORMAT m_format; + void *m_dest; // dest address + int m_chunkCount; // square chunk count (single texels or compressed blocks) + int m_byteCountLimit; // caller expectation of max number of bytes to write out + float r,g,b,a; // color desired + + // out + int m_bytesWritten; +} GLMGenTexelParams; + +// return true if successful +bool GLMGenTexels( GLMGenTexelParams *params ); + + +//=============================================================================== + +struct GLMTexLayoutSlice +{ + int m_xSize,m_ySize,m_zSize; //texel dimensions of this slice + int m_storageOffset; //where in the storage slab does this slice live + int m_storageSize; //how much storage does this slice occupy +}; + +enum EGLMTexFlags +{ + kGLMTexMipped = 0x01, + kGLMTexMippedAuto = 0x02, + kGLMTexRenderable = 0x04, + kGLMTexIsStencil = 0x08, + kGLMTexIsDepth = 0x10, + kGLMTexSRGB = 0x20, + kGLMTexMultisampled = 0x40, // has an RBO backing it. Cannot combine with Mipped, MippedAuto. One slice maximum, only targeting GL_TEXTURE_2D. + // actually not 100% positive on the mipmapping, the RBO itself can't be mipped, but the resulting texture could + // have mipmaps generated. +}; + +//=============================================================================== + +struct GLMTexLayoutKey +{ + // input values: held const, these are the hash key for the form map + GLenum m_texGLTarget; // flavor of texture: GL_TEXTURE_2D, GL_TEXTURE_3D, GLTEXTURE_CUBE_MAP + D3DFORMAT m_texFormat; // D3D texel format + unsigned long m_texFlags; // mipped, autogen mips, render target, ... ? + unsigned long m_texSamples; // zero for a plain tex, 2/4/6/8 for "MSAA tex" (RBO backed) + int m_xSize,m_ySize,m_zSize; // size of base mip +}; + +bool LessFunc_GLMTexLayoutKey( const GLMTexLayoutKey &a, const GLMTexLayoutKey &b ); + +#define GLM_TEX_MAX_MIPS 14 +#define GLM_TEX_MAX_FACES 6 +#define GLM_TEX_MAX_SLICES (GLM_TEX_MAX_MIPS * GLM_TEX_MAX_FACES) + +#pragma warning( push ) +#pragma warning( disable : 4200 ) + +struct GLMTexLayout +{ + char *m_layoutSummary; // for debug visibility + + // const inputs used for hashing + GLMTexLayoutKey m_key; + + // refcount + int m_refCount; + + // derived values: + GLMTexFormatDesc *m_format; // format specific info + int m_mipCount; // derived by starying at base size and working down towards 1x1 + int m_faceCount; // 1 for 2d/3d, 6 for cubemap + int m_sliceCount; // product of faces and mips + int m_storageTotalSize; // size of storage slab required + + // slice array + GLMTexLayoutSlice m_slices[0]; // dynamically allocated 2-d array [faces][mips] +}; + +#pragma warning( pop ) + +class CGLMTexLayoutTable +{ +public: + CGLMTexLayoutTable(); + + GLMTexLayout *NewLayoutRef( GLMTexLayoutKey *pDesiredKey ); // pass in a pointer to layout key - receive ptr to completed layout + void DelLayoutRef( GLMTexLayout *layout ); // pass in pointer to completed layout. refcount is dropped. + + void DumpStats( void ); +protected: + CUtlMap< GLMTexLayoutKey, GLMTexLayout* > m_layoutMap; +}; + +//=============================================================================== + +// a sampler specifies desired state for drawing on a given sampler index +// this is the combination of a texture choice and a set of sampler parameters +// see http://msdn.microsoft.com/en-us/library/bb172602(VS.85).aspx + +struct GLMTexLockParams +{ + // input params which identify the slice of interest + CGLMTex *m_tex; + int m_face; + int m_mip; + + // identifies the region of the slice + GLMRegion m_region; + + // tells GLM to force re-read of the texels back from GL + // i.e. "I know I stepped on those texels with a draw or blit - the GLM copy is stale" + bool m_readback; +}; + +struct GLMTexLockDesc +{ + GLMTexLockParams m_req; // form of the lock request + + bool m_active; // set true at lock time. cleared at unlock time. + + int m_sliceIndex; // which slice in the layout + int m_sliceBaseOffset; // where is that in the texture data + int m_sliceRegionOffset; // offset to the start (lowest address corner) of the region requested +}; + +//=============================================================================== + +#define GLM_SAMPLER_COUNT 16 + +#define GLM_MAX_PIXEL_TEX_SAMPLERS 16 +#define GLM_MAX_VERTEX_TEX_SAMPLERS 0 +typedef CBitVec CTexBindMask; + +enum EGLMTexSliceFlag +{ + kSliceValid = 0x01, // slice has been teximage'd in whole at least once - set to 0 initially + kSliceStorageValid = 0x02, // if backing store is available, this slice's data is a valid copy - set to 0 initially + kSliceLocked = 0x04, // are one or more locks outstanding on this slice + kSliceFullyDirty = 0x08, // does the slice need to be fully downloaded at unlock time (disregard dirty rects) +}; + +//=============================================================================== + +#define GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS (2) +#define GLM_PACKED_SAMPLER_PARAMS_MIN_FILTER_BITS (2) +#define GLM_PACKED_SAMPLER_PARAMS_MAG_FILTER_BITS (2) +#define GLM_PACKED_SAMPLER_PARAMS_MIP_FILTER_BITS (2) +#define GLM_PACKED_SAMPLER_PARAMS_MIN_LOD_BITS (4) +#define GLM_PACKED_SAMPLER_PARAMS_MAX_ANISO_BITS (5) +#define GLM_PACKED_SAMPLER_PARAMS_COMPARE_MODE_BITS (1) +#define GLM_PACKED_SAMPLER_PARAMS_SRGB_BITS (1) + +struct GLMTexPackedSamplingParams +{ + uint32 m_addressU : GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS; + uint32 m_addressV : GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS; + uint32 m_addressW : GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS; + + uint32 m_minFilter : GLM_PACKED_SAMPLER_PARAMS_MIN_FILTER_BITS; + uint32 m_magFilter : GLM_PACKED_SAMPLER_PARAMS_MAG_FILTER_BITS; + uint32 m_mipFilter : GLM_PACKED_SAMPLER_PARAMS_MIP_FILTER_BITS; + + uint32 m_minLOD : GLM_PACKED_SAMPLER_PARAMS_MIN_LOD_BITS; + uint32 m_maxAniso : GLM_PACKED_SAMPLER_PARAMS_MAX_ANISO_BITS; + uint32 m_compareMode : GLM_PACKED_SAMPLER_PARAMS_COMPARE_MODE_BITS; + uint32 m_srgb : GLM_PACKED_SAMPLER_PARAMS_SRGB_BITS; + uint32 m_isValid : 1; +}; + +struct GLMTexSamplingParams +{ + union + { + GLMTexPackedSamplingParams m_packed; + uint32 m_bits; + }; + + uint32 m_borderColor; + float m_lodBias; + + FORCEINLINE bool operator== (const GLMTexSamplingParams& rhs ) const + { + return ( m_bits == rhs.m_bits ) && ( m_borderColor == rhs.m_borderColor ) && ( m_lodBias == rhs.m_lodBias ); + } + + FORCEINLINE void SetToDefaults() + { + m_bits = 0; + m_borderColor = 0; + m_lodBias = 0.0f; + m_packed.m_addressU = D3DTADDRESS_WRAP; + m_packed.m_addressV = D3DTADDRESS_WRAP; + m_packed.m_addressW = D3DTADDRESS_WRAP; + m_packed.m_minFilter = D3DTEXF_POINT; + m_packed.m_magFilter = D3DTEXF_POINT; + m_packed.m_mipFilter = D3DTEXF_NONE; + m_packed.m_maxAniso = 1; + m_packed.m_compareMode = 0; + m_packed.m_isValid = true; + } + +#ifndef OSX + FORCEINLINE void SetToSamplerObject( GLuint nSamplerObject ) const + { + static const GLenum dxtogl_addressMode[] = { GL_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, (GLenum)-1 }; + static const GLenum dxtogl_magFilter[4] = { GL_NEAREST, GL_NEAREST, GL_LINEAR, GL_LINEAR }; + static const GLenum dxtogl_minFilter[4][4] = // indexed by _D3DTEXTUREFILTERTYPE on both axes: [row is min filter][col is mip filter]. + { + /* min = D3DTEXF_NONE */ { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, (GLenum)-1 }, // D3DTEXF_NONE we just treat like POINT + /* min = D3DTEXF_POINT */ { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, (GLenum)-1 }, + /* min = D3DTEXF_LINEAR */ { GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, (GLenum)-1 }, + /* min = D3DTEXF_ANISOTROPIC */ { GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, (GLenum)-1 }, // no diff from prior row, set maxAniso to effect the sampling + }; + + gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_WRAP_S, dxtogl_addressMode[m_packed.m_addressU] ); + gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_WRAP_T, dxtogl_addressMode[m_packed.m_addressV] ); + gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_WRAP_R, dxtogl_addressMode[m_packed.m_addressW] ); + gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_MIN_FILTER, dxtogl_minFilter[m_packed.m_minFilter][m_packed.m_mipFilter] ); + gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_MAG_FILTER, dxtogl_magFilter[m_packed.m_magFilter] ); + gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_MAX_ANISOTROPY_EXT, m_packed.m_maxAniso ); + + float flBorderColor[4] = { 0, 0, 0, 0 }; + if ( m_borderColor ) + { + flBorderColor[0] = ((m_borderColor >> 16) & 0xFF) * (1.0f/255.0f); //R + flBorderColor[1] = ((m_borderColor >> 8) & 0xFF) * (1.0f/255.0f); //G + flBorderColor[2] = ((m_borderColor ) & 0xFF) * (1.0f/255.0f); //B + flBorderColor[3] = ((m_borderColor >> 24) & 0xFF) * (1.0f/255.0f); //A + } + gGL->glSamplerParameterfv( nSamplerObject, GL_TEXTURE_BORDER_COLOR, flBorderColor ); // <-- this crashes ATI's driver, remark it out + gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_MIN_LOD, m_packed.m_minLOD ); + gGL->glSamplerParameterfv( nSamplerObject, GL_TEXTURE_LOD_BIAS, &m_lodBias ); + gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_COMPARE_MODE_ARB, m_packed.m_compareMode ? GL_COMPARE_R_TO_TEXTURE_ARB : GL_NONE ); + gGL->glSamplerParameterf( nSamplerObject, GL_TEXTURE_LOD_BIAS, m_lodBias ); + if ( m_packed.m_compareMode ) + { + gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL ); + } + if ( gGL->m_bHave_GL_EXT_texture_sRGB_decode ) + { + gGL->glSamplerParameteri( nSamplerObject, GL_TEXTURE_SRGB_DECODE_EXT, m_packed.m_srgb ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT ); + } + } +#endif // !OSX + + inline void DeltaSetToTarget( GLenum target, const GLMTexSamplingParams &curState ) + { + static const GLenum dxtogl_addressMode[] = { GL_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, (GLenum)-1 }; + static const GLenum dxtogl_magFilter[4] = { GL_NEAREST, GL_NEAREST, GL_LINEAR, GL_LINEAR }; + static const GLenum dxtogl_minFilter[4][4] = // indexed by _D3DTEXTUREFILTERTYPE on both axes: [row is min filter][col is mip filter]. + { + /* min = D3DTEXF_NONE */ { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, (GLenum)-1 }, // D3DTEXF_NONE we just treat like POINT + /* min = D3DTEXF_POINT */ { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, (GLenum)-1 }, + /* min = D3DTEXF_LINEAR */ { GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, (GLenum)-1 }, + /* min = D3DTEXF_ANISOTROPIC */ { GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, (GLenum)-1 }, // no diff from prior row, set maxAniso to effect the sampling + }; + + if ( m_packed.m_addressU != curState.m_packed.m_addressU ) + { + gGL->glTexParameteri( target, GL_TEXTURE_WRAP_S, dxtogl_addressMode[m_packed.m_addressU] ); + } + + if ( m_packed.m_addressV != curState.m_packed.m_addressV ) + { + gGL->glTexParameteri( target, GL_TEXTURE_WRAP_T, dxtogl_addressMode[m_packed.m_addressV] ); + } + + if ( m_packed.m_addressW != curState.m_packed.m_addressW ) + { + gGL->glTexParameteri( target, GL_TEXTURE_WRAP_R, dxtogl_addressMode[m_packed.m_addressW] ); + } + + if ( ( m_packed.m_minFilter != curState.m_packed.m_minFilter ) || + ( m_packed.m_magFilter != curState.m_packed.m_magFilter ) || + ( m_packed.m_mipFilter != curState.m_packed.m_mipFilter ) || + ( m_packed.m_maxAniso != curState.m_packed.m_maxAniso ) ) + { + gGL->glTexParameteri( target, GL_TEXTURE_MIN_FILTER, dxtogl_minFilter[m_packed.m_minFilter][m_packed.m_mipFilter] ); + gGL->glTexParameteri( target, GL_TEXTURE_MAG_FILTER, dxtogl_magFilter[m_packed.m_magFilter] ); + gGL->glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, m_packed.m_maxAniso ); + } + + if ( m_borderColor != curState.m_borderColor ) + { + float flBorderColor[4] = { 0, 0, 0, 0 }; + if ( m_borderColor ) + { + flBorderColor[0] = ((m_borderColor >> 16) & 0xFF) * (1.0f/255.0f); //R + flBorderColor[1] = ((m_borderColor >> 8) & 0xFF) * (1.0f/255.0f); //G + flBorderColor[2] = ((m_borderColor ) & 0xFF) * (1.0f/255.0f); //B + flBorderColor[3] = ((m_borderColor >> 24) & 0xFF) * (1.0f/255.0f); //A + } + + gGL->glTexParameterfv( target, GL_TEXTURE_BORDER_COLOR, flBorderColor ); // <-- this crashes ATI's driver, remark it out + } + + if ( m_packed.m_minLOD != curState.m_packed.m_minLOD ) + { + gGL->glTexParameteri( target, GL_TEXTURE_MIN_LOD, m_packed.m_minLOD ); + } + + if ( m_lodBias != curState.m_lodBias ) + { + // Could use TexParameterf instead, but we don't currently grab it. This works fine, too. + gGL->glTexParameterfv( target, GL_TEXTURE_LOD_BIAS, &m_lodBias ); + } + + if ( m_packed.m_compareMode != curState.m_packed.m_compareMode ) + { + gGL->glTexParameteri( target, GL_TEXTURE_COMPARE_MODE_ARB, m_packed.m_compareMode ? GL_COMPARE_R_TO_TEXTURE_ARB : GL_NONE ); + if ( m_packed.m_compareMode ) + { + gGL->glTexParameteri( target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL ); + } + } + + if ( ( gGL->m_bHave_GL_EXT_texture_sRGB_decode ) && ( m_packed.m_srgb != curState.m_packed.m_srgb ) ) + { + gGL->glTexParameteri( target, GL_TEXTURE_SRGB_DECODE_EXT, m_packed.m_srgb ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT ); + } + } + + inline void SetToTarget( GLenum target ) + { + static const GLenum dxtogl_addressMode[] = { GL_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, (GLenum)-1 }; + static const GLenum dxtogl_magFilter[4] = { GL_NEAREST, GL_NEAREST, GL_LINEAR, GL_LINEAR }; + static const GLenum dxtogl_minFilter[4][4] = // indexed by _D3DTEXTUREFILTERTYPE on both axes: [row is min filter][col is mip filter]. + { + /* min = D3DTEXF_NONE */ { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, (GLenum)-1 }, // D3DTEXF_NONE we just treat like POINT + /* min = D3DTEXF_POINT */ { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, (GLenum)-1 }, + /* min = D3DTEXF_LINEAR */ { GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, (GLenum)-1 }, + /* min = D3DTEXF_ANISOTROPIC */ { GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, (GLenum)-1 }, // no diff from prior row, set maxAniso to effect the sampling + }; + + gGL->glTexParameteri( target, GL_TEXTURE_WRAP_S, dxtogl_addressMode[m_packed.m_addressU] ); + gGL->glTexParameteri( target, GL_TEXTURE_WRAP_T, dxtogl_addressMode[m_packed.m_addressV] ); + gGL->glTexParameteri( target, GL_TEXTURE_WRAP_R, dxtogl_addressMode[m_packed.m_addressW] ); + gGL->glTexParameteri( target, GL_TEXTURE_MIN_FILTER, dxtogl_minFilter[m_packed.m_minFilter][m_packed.m_mipFilter] ); + gGL->glTexParameteri( target, GL_TEXTURE_MAG_FILTER, dxtogl_magFilter[m_packed.m_magFilter] ); + gGL->glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, m_packed.m_maxAniso ); + + float flBorderColor[4] = { 0, 0, 0, 0 }; + if ( m_borderColor ) + { + flBorderColor[0] = ((m_borderColor >> 16) & 0xFF) * (1.0f/255.0f); //R + flBorderColor[1] = ((m_borderColor >> 8) & 0xFF) * (1.0f/255.0f); //G + flBorderColor[2] = ((m_borderColor ) & 0xFF) * (1.0f/255.0f); //B + flBorderColor[3] = ((m_borderColor >> 24) & 0xFF) * (1.0f/255.0f); //A + } + gGL->glTexParameterfv( target, GL_TEXTURE_BORDER_COLOR, flBorderColor ); // <-- this crashes ATI's driver, remark it out + gGL->glTexParameteri( target, GL_TEXTURE_MIN_LOD, m_packed.m_minLOD ); + gGL->glTexParameterfv( target, GL_TEXTURE_LOD_BIAS, &m_lodBias ); + gGL->glTexParameteri( target, GL_TEXTURE_COMPARE_MODE_ARB, m_packed.m_compareMode ? GL_COMPARE_R_TO_TEXTURE_ARB : GL_NONE ); + if ( m_packed.m_compareMode ) + { + gGL->glTexParameteri( target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL ); + } + if ( gGL->m_bHave_GL_EXT_texture_sRGB_decode ) + { + gGL->glTexParameteri( target, GL_TEXTURE_SRGB_DECODE_EXT, m_packed.m_srgb ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT ); + } + } +}; + +//=============================================================================== + +class CGLMTex +{ + +public: + + void Lock( GLMTexLockParams *params, char** addressOut, int* yStrideOut, int *zStrideOut ); + void Unlock( GLMTexLockParams *params ); + GLuint GetTexName() { return m_texName; } + +protected: + friend class GLMContext; // only GLMContext can make CGLMTex objects + friend class GLMTester; + friend class CGLMFBO; + + friend struct IDirect3DDevice9; + friend struct IDirect3DBaseTexture9; + friend struct IDirect3DTexture9; + friend struct IDirect3DSurface9; + friend struct IDirect3DCubeTexture9; + friend struct IDirect3DVolumeTexture9; + + CGLMTex( GLMContext *ctx, GLMTexLayout *layout, uint levels, const char *debugLabel = NULL ); + ~CGLMTex( ); + + int CalcSliceIndex( int face, int mip ); + void CalcTexelDataOffsetAndStrides( int sliceIndex, int x, int y, int z, int *offsetOut, int *yStrideOut, int *zStrideOut ); + + 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. + +#if defined( OSX ) + void HandleSRGBMismatch( bool srgb, int &srgbFlipCount ); + void ResetSRGB( bool srgb, bool noDataWrite ); + // re-specify texture format to match desired sRGB form + // noWrite means send NULL for texel source addresses instead of actual data - ideal for RT's +#endif + + bool IsRBODirty() const; + void ForceRBONonDirty(); + void ForceRBODirty(); + + // re-specify texture format to match desired sRGB form + // noWrite means send NULL for texel source addresses instead of actual data - ideal for RT's + + GLuint m_texName; // name of this texture in the context + GLenum m_texGLTarget; + uint m_nSamplerType; // SAMPLER_2D, etc. + + GLMTexSamplingParams m_SamplingParams; + + GLMTexLayout *m_layout; // layout of texture (shared across all tex with same layout) + + uint m_nLastResolvedBatchCounter; + + int m_minActiveMip;//index of lowest mip that has been written. used to drive setting of GL_TEXTURE_MAX_LEVEL. + int m_maxActiveMip;//index of highest mip that has been written. used to drive setting of GL_TEXTURE_MAX_LEVEL. + + GLMContext *m_ctx; // link back to parent context + + + CGLMFBO *m_pBlitSrcFBO; + CGLMFBO *m_pBlitDstFBO; + GLuint m_rboName; // name of MSAA RBO backing the tex if MSAA enabled (or zero) + + int m_rtAttachCount; // how many RT's have this texture attached somewhere + + char *m_backing; // backing storage if available + + int m_lockCount; // lock reqs are stored in the GLMContext for tracking + + CUtlVector m_sliceFlags; + + char *m_debugLabel; // strdup() of debugLabel passed in, or NULL + + bool m_texClientStorage; // was CS selected for texture + bool m_texPreloaded; // has it been kicked into VRAM with GLMContext::PreloadTex yet + + int m_srgbFlipCount; +#if GLMDEBUG + CGLMTex *m_pPrevTex; + CGLMTex *m_pNextTex; +#endif +}; + +#endif diff --git a/public/togles/linuxwin/dxabstract.h b/public/togles/linuxwin/dxabstract.h new file mode 100644 index 00000000..a4dfc073 --- /dev/null +++ b/public/togles/linuxwin/dxabstract.h @@ -0,0 +1,1444 @@ +//========= 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. +// +// dxabstract.h +// +//================================================================================================== +#ifndef DXABSTRACT_H +#define DXABSTRACT_H + +#ifdef DX_TO_GL_ABSTRACTION + +#include "togl/rendermechanism.h" + +#include "tier0/platform.h" +#include "tier0/dbg.h" +#include "tier1/utlmap.h" + +// turn this on to get refcount logging from IUnknown +#define IUNKNOWN_ALLOC_SPEW 0 +#define IUNKNOWN_ALLOC_SPEW_MARK_ALL 0 + + +TOGL_INTERFACE void toglGetClientRect( VD3DHWND hWnd, RECT *destRect ); + + +struct TOGL_CLASS IUnknown +{ + int m_refcount[2]; + bool m_mark; + + IUnknown() + { + m_refcount[0] = 1; + m_refcount[1] = 0; + m_mark = (IUNKNOWN_ALLOC_SPEW_MARK_ALL != 0); // either all are marked, or only the ones that have SetMark(true) called on them + + #if IUNKNOWN_ALLOC_SPEW + if (m_mark) + { + GLMPRINTF(("-A- IUnew (%08x) refc -> (%d,%d) ",this,m_refcount[0],m_refcount[1])); + } + #endif + }; + + virtual ~IUnknown() + { + #if IUNKNOWN_ALLOC_SPEW + if (m_mark) + { + GLMPRINTF(("-A- IUdel (%08x) ",this )); + } + #endif + }; + + void AddRef( int which=0, char *comment = NULL ) + { + Assert( which >= 0 ); + Assert( which < 2 ); + m_refcount[which]++; + + #if IUNKNOWN_ALLOC_SPEW + if (m_mark) + { + GLMPRINTF(("-A- IUAddRef (%08x,%d) refc -> (%d,%d) [%s]",this,which,m_refcount[0],m_refcount[1],comment?comment:"...")) ; + if (!comment) + { + GLMPRINTF(("")) ; // place to hang a breakpoint + } + } + #endif + }; + + ULONG __stdcall Release( int which=0, char *comment = NULL ) + { + Assert( which >= 0 ); + Assert( which < 2 ); + + //int oldrefcs[2] = { m_refcount[0], m_refcount[1] }; + bool deleting = false; + + m_refcount[which]--; + if ( (!m_refcount[0]) && (!m_refcount[1]) ) + { + deleting = true; + } + + #if IUNKNOWN_ALLOC_SPEW + if (m_mark) + { + GLMPRINTF(("-A- IURelease (%08x,%d) refc -> (%d,%d) [%s] %s",this,which,m_refcount[0],m_refcount[1],comment?comment:"...",deleting?"->DELETING":"")); + if (!comment) + { + GLMPRINTF(("")) ; // place to hang a breakpoint + } + } + #endif + + if (deleting) + { + if (m_mark) + { + GLMPRINTF(("")) ; // place to hang a breakpoint + } + delete this; + return 0; + } + else + { + return m_refcount[0]; + } + }; + + void SetMark( bool markValue, char *comment=NULL ) + { + #if IUNKNOWN_ALLOC_SPEW + if (!m_mark && markValue) // leading edge detect + { + // print the same thing that the constructor would have printed if it had been marked from the beginning + // i.e. it's anticipated that callers asking for marking will do so right at create time + GLMPRINTF(("-A- IUSetMark (%08x) refc -> (%d,%d) (%s) ",this,m_refcount[0],m_refcount[1],comment?comment:"...")); + } + #endif + + m_mark = markValue; + } +}; + +// ------------------------------------------------------------------------------------------------------------------------------ // +// INTERFACES +// ------------------------------------------------------------------------------------------------------------------------------ // + +struct TOGL_CLASS IDirect3DResource9 : public IUnknown +{ + IDirect3DDevice9 *m_device; // parent device + D3DRESOURCETYPE m_restype; + + DWORD SetPriority(DWORD PriorityNew); +}; + +struct TOGL_CLASS IDirect3DBaseTexture9 : public IDirect3DResource9 // "A Texture.." +{ + D3DSURFACE_DESC m_descZero; // desc of top level. + CGLMTex *m_tex; // a CGLMTex can represent all forms of tex + + virtual ~IDirect3DBaseTexture9(); + D3DRESOURCETYPE TOGLMETHODCALLTYPE GetType(); + DWORD TOGLMETHODCALLTYPE GetLevelCount(); + HRESULT TOGLMETHODCALLTYPE GetLevelDesc(UINT Level,D3DSURFACE_DESC *pDesc); +}; + +struct TOGL_CLASS IDirect3DTexture9 : public IDirect3DBaseTexture9 // "Texture 2D" +{ + IDirect3DSurface9 *m_surfZero; // surf of top level. + virtual ~IDirect3DTexture9(); + HRESULT TOGLMETHODCALLTYPE LockRect(UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags); + HRESULT TOGLMETHODCALLTYPE UnlockRect(UINT Level); + HRESULT TOGLMETHODCALLTYPE GetSurfaceLevel(UINT Level,IDirect3DSurface9** ppSurfaceLevel); +}; + +struct TOGL_CLASS IDirect3DCubeTexture9 : public IDirect3DBaseTexture9 // "Texture Cube Map" +{ + IDirect3DSurface9 *m_surfZero[6]; // surfs of top level. + virtual ~IDirect3DCubeTexture9(); + HRESULT TOGLMETHODCALLTYPE GetCubeMapSurface(D3DCUBEMAP_FACES FaceType,UINT Level,IDirect3DSurface9** ppCubeMapSurface); + HRESULT TOGLMETHODCALLTYPE GetLevelDesc(UINT Level,D3DSURFACE_DESC *pDesc); +}; + +struct TOGL_CLASS IDirect3DVolumeTexture9 : public IDirect3DBaseTexture9 // "Texture 3D" +{ + IDirect3DSurface9 *m_surfZero; // surf of top level. + D3DVOLUME_DESC m_volDescZero; // volume desc top level + virtual ~IDirect3DVolumeTexture9(); + HRESULT TOGLMETHODCALLTYPE LockBox(UINT Level,D3DLOCKED_BOX* pLockedVolume,CONST D3DBOX* pBox,DWORD Flags); + HRESULT TOGLMETHODCALLTYPE UnlockBox(UINT Level); + HRESULT TOGLMETHODCALLTYPE GetLevelDesc( UINT level, D3DVOLUME_DESC *pDesc ); +}; + + +// for the moment, a "D3D surface" is modeled as a GLM tex, a face, and a mip. +// no Create method, these are filled in by the various create surface methods. + +struct TOGL_CLASS IDirect3DSurface9 : public IDirect3DResource9 +{ + virtual ~IDirect3DSurface9(); + HRESULT TOGLMETHODCALLTYPE LockRect(D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags); + HRESULT TOGLMETHODCALLTYPE UnlockRect(); + HRESULT TOGLMETHODCALLTYPE GetDesc(D3DSURFACE_DESC *pDesc); + + D3DSURFACE_DESC m_desc; + CGLMTex *m_tex; + int m_face; + int m_mip; +}; + +struct TOGL_CLASS IDirect3D9 : public IUnknown +{ + virtual ~IDirect3D9(); + + UINT TOGLMETHODCALLTYPE GetAdapterCount(); + + HRESULT TOGLMETHODCALLTYPE GetDeviceCaps (UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps); + HRESULT TOGLMETHODCALLTYPE GetAdapterIdentifier (UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier); + HRESULT TOGLMETHODCALLTYPE CheckDeviceFormat (UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat); + UINT TOGLMETHODCALLTYPE GetAdapterModeCount (UINT Adapter,D3DFORMAT Format); + HRESULT TOGLMETHODCALLTYPE EnumAdapterModes (UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode); + HRESULT TOGLMETHODCALLTYPE CheckDeviceType (UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed); + HRESULT TOGLMETHODCALLTYPE GetAdapterDisplayMode (UINT Adapter,D3DDISPLAYMODE* pMode); + HRESULT TOGLMETHODCALLTYPE CheckDepthStencilMatch (UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat); + HRESULT TOGLMETHODCALLTYPE CheckDeviceMultiSampleType (UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels); + + HRESULT TOGLMETHODCALLTYPE CreateDevice (UINT Adapter,D3DDEVTYPE DeviceType,VD3DHWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface); +}; + +struct TOGL_CLASS IDirect3DVertexDeclaration9 : public IUnknown +{ + IDirect3DDevice9 *m_device; + uint m_elemCount; + D3DVERTEXELEMENT9_GL m_elements[ MAX_D3DVERTEXELEMENTS ]; + + uint8 m_VertexAttribDescToStreamIndex[256]; + + virtual ~IDirect3DVertexDeclaration9(); +}; + +struct TOGL_CLASS IDirect3DQuery9 : public IDirect3DResource9 //was IUnknown +{ + D3DQUERYTYPE m_type; // D3DQUERYTYPE_OCCLUSION or D3DQUERYTYPE_EVENT + GLMContext *m_ctx; + CGLMQuery *m_query; + + uint m_nIssueStartThreadID, m_nIssueEndThreadID; + uint m_nIssueStartDrawCallIndex, m_nIssueEndDrawCallIndex; + uint m_nIssueStartFrameIndex, m_nIssueEndFrameIndex; + uint m_nIssueStartQueryCreationCounter, m_nIssueEndQueryCreationCounter; + + virtual ~IDirect3DQuery9(); + + HRESULT Issue(DWORD dwIssueFlags); + HRESULT GetData(void* pData,DWORD dwSize,DWORD dwGetDataFlags); +}; + +struct TOGL_CLASS IDirect3DVertexBuffer9 : public IDirect3DResource9 //was IUnknown +{ + GLMContext *m_ctx; + CGLMBuffer *m_vtxBuffer; + D3DVERTEXBUFFER_DESC m_vtxDesc; // to satisfy GetDesc + + virtual ~IDirect3DVertexBuffer9(); + HRESULT Lock(UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags); + HRESULT Unlock(); + void UnlockActualSize( uint nActualSize, const void *pActualData = NULL ); +}; + +struct TOGL_CLASS IDirect3DIndexBuffer9 : public IDirect3DResource9 //was IUnknown +{ + GLMContext *m_ctx; + CGLMBuffer *m_idxBuffer; + D3DINDEXBUFFER_DESC m_idxDesc; // to satisfy GetDesc + + virtual ~IDirect3DIndexBuffer9(); + + HRESULT Lock(UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags); + HRESULT Unlock(); + void UnlockActualSize( uint nActualSize, const void *pActualData = NULL ); + + HRESULT GetDesc(D3DINDEXBUFFER_DESC *pDesc); +}; + +struct TOGL_CLASS IDirect3DPixelShader9 : public IDirect3DResource9 //was IUnknown +{ + CGLMProgram *m_pixProgram; + uint m_pixHighWater; // count of active constant slots referenced by shader. + uint m_pixSamplerMask; // (1< CD3DMATRIXAllocator; + typedef class TOGL_CLASS CUtlVector CD3DMATRIXStack; +#else + typedef class CUtlMemory CD3DMATRIXAllocator; + typedef class CUtlVector CD3DMATRIXStack; +#endif + +struct TOGL_CLASS ID3DXMatrixStack //: public IUnknown +{ + int m_refcount[2]; + bool m_mark; + CD3DMATRIXStack m_stack; + int m_stackTop; // top of stack is at the highest index, this is that index. push increases, pop decreases. + + ID3DXMatrixStack(); + void AddRef( int which=0, char *comment = NULL ); + ULONG Release( int which=0, char *comment = NULL ); + + HRESULT Create( void ); + + D3DXMATRIX* GetTop(); + void Push(); + void Pop(); + void LoadIdentity(); + void LoadMatrix( const D3DXMATRIX *pMat ); + void MultMatrix( const D3DXMATRIX *pMat ); + void MultMatrixLocal( const D3DXMATRIX *pMat ); + HRESULT ScaleLocal(FLOAT x, FLOAT y, FLOAT z); + + // Left multiply the current matrix with the computed rotation + // matrix, counterclockwise about the given axis with the given angle. + // (rotation is about the local origin of the object) + HRESULT RotateAxisLocal(CONST D3DXVECTOR3* pV, FLOAT Angle); + + // Left multiply the current matrix with the computed translation + // matrix. (transformation is about the local origin of the object) + HRESULT TranslateLocal(FLOAT x, FLOAT y, FLOAT z); +}; + +typedef ID3DXMatrixStack* LPD3DXMATRIXSTACK; + +struct RenderTargetState_t +{ + void clear() { V_memset( this, 0, sizeof( *this ) ); } + + CGLMTex *m_pRenderTargets[4]; + CGLMTex *m_pDepthStencil; + + inline bool RefersTo( CGLMTex * pSurf ) const + { + for ( uint i = 0; i < 4; i++ ) + if ( m_pRenderTargets[i] == pSurf ) + return true; + + if ( m_pDepthStencil == pSurf ) + return true; + + return false; + } + + static inline bool LessFunc( const RenderTargetState_t &lhs, const RenderTargetState_t &rhs ) + { + COMPILE_TIME_ASSERT( sizeof( lhs.m_pRenderTargets[0] ) == sizeof( uint32 ) ); + uint64 lhs0 = reinterpret_cast(lhs.m_pRenderTargets)[0]; + uint64 rhs0 = reinterpret_cast(rhs.m_pRenderTargets)[0]; + if ( lhs0 < rhs0 ) + return true; + else if ( lhs0 == rhs0 ) + { + uint64 lhs1 = reinterpret_cast(lhs.m_pRenderTargets)[1]; + uint64 rhs1 = reinterpret_cast(rhs.m_pRenderTargets)[1]; + if ( lhs1 < rhs1 ) + return true; + else if ( lhs1 == rhs1 ) + { + return lhs.m_pDepthStencil < rhs.m_pDepthStencil; + } + } + return false; + } + + inline bool operator < ( const RenderTargetState_t &rhs ) const + { + return LessFunc( *this, rhs ); + } +}; + +typedef CUtlMap< RenderTargetState_t, CGLMFBO *> CGLMFBOMap; + +class simple_bitmap; + +struct TOGL_CLASS IDirect3DDevice9 : public IUnknown +{ + friend class GLMContext; + friend struct IDirect3DBaseTexture9; + friend struct IDirect3DTexture9; + friend struct IDirect3DCubeTexture9; + friend struct IDirect3DVolumeTexture9; + friend struct IDirect3DSurface9; + friend struct IDirect3DVertexBuffer9; + friend struct IDirect3DIndexBuffer9; + friend struct IDirect3DPixelShader9; + friend struct IDirect3DVertexShader9; + friend struct IDirect3DQuery9; + friend struct IDirect3DVertexDeclaration9; + + IDirect3DDevice9(); + virtual ~IDirect3DDevice9(); + + // Create call invoked from IDirect3D9 + HRESULT TOGLMETHODCALLTYPE Create( IDirect3DDevice9Params *params ); + + // + // Basics + // + HRESULT TOGLMETHODCALLTYPE Reset(D3DPRESENT_PARAMETERS* pPresentationParameters); + HRESULT TOGLMETHODCALLTYPE SetViewport(CONST D3DVIEWPORT9* pViewport); + HRESULT TOGLMETHODCALLTYPE GetViewport(D3DVIEWPORT9* pViewport); + HRESULT TOGLMETHODCALLTYPE BeginScene(); + HRESULT TOGLMETHODCALLTYPE Clear(DWORD Count,CONST D3DRECT* pRects,DWORD Flags,D3DCOLOR Color,float Z,DWORD Stencil); + HRESULT TOGLMETHODCALLTYPE EndScene(); + HRESULT TOGLMETHODCALLTYPE Present(CONST RECT* pSourceRect,CONST RECT* pDestRect,VD3DHWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion); + + // textures + HRESULT TOGLMETHODCALLTYPE CreateTexture(UINT Width,UINT Height,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DTexture9** ppTexture,VD3DHANDLE* pSharedHandle, char *debugLabel=NULL); + HRESULT TOGLMETHODCALLTYPE CreateCubeTexture(UINT EdgeLength,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DCubeTexture9** ppCubeTexture,VD3DHANDLE* pSharedHandle, char *debugLabel=NULL); + HRESULT TOGLMETHODCALLTYPE CreateVolumeTexture(UINT Width,UINT Height,UINT Depth,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DVolumeTexture9** ppVolumeTexture,VD3DHANDLE* pSharedHandle, char *debugLabel=NULL); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetTexture(DWORD Stage,IDirect3DBaseTexture9* pTexture); + HRESULT TOGLMETHODCALLTYPE SetTextureNonInline(DWORD Stage,IDirect3DBaseTexture9* pTexture); + + HRESULT TOGLMETHODCALLTYPE GetTexture(DWORD Stage,IDirect3DBaseTexture9** ppTexture); + + // render targets, color and depthstencil, surfaces, blit + HRESULT TOGLMETHODCALLTYPE CreateRenderTarget(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultisampleQuality,BOOL Lockable,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle, char *debugLabel=NULL); + HRESULT TOGLMETHODCALLTYPE SetRenderTarget(DWORD RenderTargetIndex,IDirect3DSurface9* pRenderTarget); + HRESULT TOGLMETHODCALLTYPE GetRenderTarget(DWORD RenderTargetIndex,IDirect3DSurface9** ppRenderTarget); + + HRESULT TOGLMETHODCALLTYPE CreateOffscreenPlainSurface(UINT Width,UINT Height,D3DFORMAT Format,D3DPOOL Pool,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle); + + HRESULT TOGLMETHODCALLTYPE CreateDepthStencilSurface(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultisampleQuality,BOOL Discard,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle); + HRESULT TOGLMETHODCALLTYPE SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil); + HRESULT TOGLMETHODCALLTYPE GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface); + + HRESULT TOGLMETHODCALLTYPE GetRenderTargetData(IDirect3DSurface9* pRenderTarget,IDirect3DSurface9* pDestSurface); // ? is anyone using this ? + HRESULT TOGLMETHODCALLTYPE GetFrontBufferData(UINT iSwapChain,IDirect3DSurface9* pDestSurface); + HRESULT TOGLMETHODCALLTYPE StretchRect(IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect,IDirect3DSurface9* pDestSurface,CONST RECT* pDestRect,D3DTEXTUREFILTERTYPE Filter); + + // pixel shaders + HRESULT TOGLMETHODCALLTYPE CreatePixelShader(CONST DWORD* pFunction,IDirect3DPixelShader9** ppShader, const char *pShaderName, char *debugLabel = NULL, const uint32 *pCentroidMask = NULL ); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetPixelShader(IDirect3DPixelShader9* pShader); + HRESULT TOGLMETHODCALLTYPE SetPixelShaderNonInline(IDirect3DPixelShader9* pShader); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetPixelShaderConstantF(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount); + HRESULT TOGLMETHODCALLTYPE SetPixelShaderConstantFNonInline(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount); + + HRESULT TOGLMETHODCALLTYPE SetPixelShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount); + HRESULT TOGLMETHODCALLTYPE SetPixelShaderConstantI(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount); + + // vertex shaders + HRESULT TOGLMETHODCALLTYPE CreateVertexShader(CONST DWORD* pFunction,IDirect3DVertexShader9** ppShader, const char *pShaderName, char *debugLabel = NULL); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetVertexShader(IDirect3DVertexShader9* pShader); + HRESULT TOGLMETHODCALLTYPE SetVertexShaderNonInline(IDirect3DVertexShader9* pShader); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetVertexShaderConstantF(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount); + HRESULT TOGLMETHODCALLTYPE SetVertexShaderConstantFNonInline(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetVertexShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount); + HRESULT TOGLMETHODCALLTYPE SetVertexShaderConstantBNonInline(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetVertexShaderConstantI(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount); + HRESULT TOGLMETHODCALLTYPE SetVertexShaderConstantINonInline(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount); + + // POSIX only - preheating for a specific vertex/pixel shader pair - trigger GLSL link inside GLM + HRESULT TOGLMETHODCALLTYPE LinkShaderPair( IDirect3DVertexShader9* vs, IDirect3DPixelShader9* ps ); + HRESULT TOGLMETHODCALLTYPE ValidateShaderPair( IDirect3DVertexShader9* vs, IDirect3DPixelShader9* ps ); + HRESULT TOGLMETHODCALLTYPE QueryShaderPair( int index, GLMShaderPairInfo *infoOut ); + + // vertex buffers + HRESULT TOGLMETHODCALLTYPE CreateVertexDeclaration(CONST D3DVERTEXELEMENT9* pVertexElements,IDirect3DVertexDeclaration9** ppDecl); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl); + HRESULT TOGLMETHODCALLTYPE SetVertexDeclarationNonInline(IDirect3DVertexDeclaration9* pDecl); + + HRESULT TOGLMETHODCALLTYPE SetFVF(DWORD FVF); // we might not be using these ? + HRESULT TOGLMETHODCALLTYPE GetFVF(DWORD* pFVF); + + HRESULT CreateVertexBuffer(UINT Length,DWORD Usage,DWORD FVF,D3DPOOL Pool,IDirect3DVertexBuffer9** ppVertexBuffer,VD3DHANDLE* pSharedHandle); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetStreamSource(UINT StreamNumber,IDirect3DVertexBuffer9* pStreamData,UINT OffsetInBytes,UINT Stride); + HRESULT SetStreamSourceNonInline(UINT StreamNumber,IDirect3DVertexBuffer9* pStreamData,UINT OffsetInBytes,UINT Stride); + + // index buffers + HRESULT TOGLMETHODCALLTYPE CreateIndexBuffer(UINT Length,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DIndexBuffer9** ppIndexBuffer,VD3DHANDLE* pSharedHandle); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetIndices(IDirect3DIndexBuffer9* pIndexData); + HRESULT TOGLMETHODCALLTYPE SetIndicesNonInline(IDirect3DIndexBuffer9* pIndexData); + + // State management. + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetRenderStateInline(D3DRENDERSTATETYPE State,DWORD Value); + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetRenderStateConstInline(D3DRENDERSTATETYPE State,DWORD Value); + HRESULT TOGLMETHODCALLTYPE SetRenderState(D3DRENDERSTATETYPE State,DWORD Value); + + FORCEINLINE HRESULT TOGLMETHODCALLTYPE SetSamplerState(DWORD Sampler,D3DSAMPLERSTATETYPE Type,DWORD Value); + HRESULT TOGLMETHODCALLTYPE SetSamplerStateNonInline(DWORD Sampler,D3DSAMPLERSTATETYPE Type,DWORD Value); + + FORCEINLINE void TOGLMETHODCALLTYPE SetSamplerStates(DWORD Sampler, DWORD AddressU, DWORD AddressV, DWORD AddressW, DWORD MinFilter, DWORD MagFilter, DWORD MipFilter, DWORD MinLod, float LodBias ); + void TOGLMETHODCALLTYPE SetSamplerStatesNonInline(DWORD Sampler, DWORD AddressU, DWORD AddressV, DWORD AddressW, DWORD MinFilter, DWORD MagFilter, DWORD MipFilter, DWORD MinLod, float LodBias ); + +#ifdef OSX + // required for 10.6 support + HRESULT TOGLMETHODCALLTYPE FlushIndexBindings(void); // push index buffer (set index ptr) + HRESULT TOGLMETHODCALLTYPE FlushVertexBindings(uint baseVertexIndex); // push vertex streams (set attrib ptrs) +#endif + + // Draw. + HRESULT TOGLMETHODCALLTYPE DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType,UINT StartVertex,UINT PrimitiveCount); + HRESULT TOGLMETHODCALLTYPE DrawIndexedPrimitive(D3DPRIMITIVETYPE PrimitiveType,INT BaseVertexIndex,UINT MinVertexIndex,UINT NumVertices,UINT startIndex,UINT primCount); + HRESULT TOGLMETHODCALLTYPE DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType,UINT MinVertexIndex,UINT NumVertices,UINT PrimitiveCount,CONST void* pIndexData,D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride); + + // misc + BOOL TOGLMETHODCALLTYPE ShowCursor(BOOL bShow); + HRESULT TOGLMETHODCALLTYPE ValidateDevice(DWORD* pNumPasses); + HRESULT TOGLMETHODCALLTYPE SetMaterial(CONST D3DMATERIAL9* pMaterial); + HRESULT TOGLMETHODCALLTYPE LightEnable(DWORD Index,BOOL Enable); + HRESULT TOGLMETHODCALLTYPE SetScissorRect(CONST RECT* pRect); + HRESULT TOGLMETHODCALLTYPE CreateQuery(D3DQUERYTYPE Type,IDirect3DQuery9** ppQuery); + HRESULT TOGLMETHODCALLTYPE GetDeviceCaps(D3DCAPS9* pCaps); + HRESULT TOGLMETHODCALLTYPE TestCooperativeLevel(); + HRESULT TOGLMETHODCALLTYPE EvictManagedResources(); + HRESULT TOGLMETHODCALLTYPE SetLight(DWORD Index,CONST D3DLIGHT9*); + void TOGLMETHODCALLTYPE SetGammaRamp(UINT iSwapChain,DWORD Flags,CONST D3DGAMMARAMP* pRamp); + + + void TOGLMETHODCALLTYPE SaveGLState(); + void TOGLMETHODCALLTYPE RestoreGLState(); + + // Talk to JasonM about this one. It's tricky in GL. + HRESULT TOGLMETHODCALLTYPE SetClipPlane(DWORD Index,CONST float* pPlane); + + // + // + // **** FIXED FUNCTION STUFF - None of this stuff needs support in GL. + // + // + HRESULT TOGLMETHODCALLTYPE SetTransform(D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix); + HRESULT TOGLMETHODCALLTYPE SetTextureStageState(DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value); + + void TOGLMETHODCALLTYPE AcquireThreadOwnership( ); + void TOGLMETHODCALLTYPE ReleaseThreadOwnership( ); + inline DWORD TOGLMETHODCALLTYPE GetCurrentOwnerThreadId() const { return m_ctx->m_nCurOwnerThreadId; } + + FORCEINLINE void TOGLMETHODCALLTYPE SetMaxUsedVertexShaderConstantsHint( uint nMaxReg ); + void TOGLMETHODCALLTYPE SetMaxUsedVertexShaderConstantsHintNonInline( uint nMaxReg ); + + void DumpStatsToConsole( const CCommand *pArgs ); + +#if GLMDEBUG + void DumpTextures( const CCommand *pArgs ); +#endif + +private: + IDirect3DDevice9( const IDirect3DDevice9& ); + IDirect3DDevice9& operator= ( const IDirect3DDevice9& ); + // Flushing changes to GL + void FlushClipPlaneEquation(); + void InitStates(); + void FullFlushStates(); + void UpdateBoundFBO(); + void ResetFBOMap(); + void ScrubFBOMap( CGLMTex *pTex ); + + // response to retired objects (when refcount goes to zero and they self-delete..) + void ReleasedVertexDeclaration( IDirect3DVertexDeclaration9 *pDecl ); + void ReleasedTexture( IDirect3DBaseTexture9 *baseTex ); // called from texture destructor - need to scrub samplers + void ReleasedCGLMTex( CGLMTex *pTex ); + void ReleasedSurface( IDirect3DSurface9 *surface ); // called from any surface destructor - need to scrub RT table if an RT + void ReleasedPixelShader( IDirect3DPixelShader9 *pixelShader ); // called from IDirect3DPixelShader9 destructor + void ReleasedVertexShader( IDirect3DVertexShader9 *vertexShader ); // called from IDirect3DVertexShader9 destructor + void ReleasedVertexBuffer( IDirect3DVertexBuffer9 *vertexBuffer ); // called from IDirect3DVertexBuffer9 destructor + void ReleasedIndexBuffer( IDirect3DIndexBuffer9 *indexBuffer ); // called from IDirect3DIndexBuffer9 destructor + void ReleasedQuery( IDirect3DQuery9 *query ); // called from IDirect3DQuery9 destructor + + // Member variables + + DWORD m_nValidMarker; +public: + IDirect3DDevice9Params m_params; // mirror of the creation inputs +private: + + // D3D flavor stuff + IDirect3DSurface9 *m_pRenderTargets[4]; + IDirect3DSurface9 *m_pDepthStencil; + + IDirect3DSurface9 *m_pDefaultColorSurface; // default color surface. + IDirect3DSurface9 *m_pDefaultDepthStencilSurface; // queried by GetDepthStencilSurface. + + IDirect3DVertexDeclaration9 *m_pVertDecl; // Set by SetVertexDeclaration... + D3DStreamDesc m_streams[ D3D_MAX_STREAMS ]; // Set by SetStreamSource.. + CGLMBuffer *m_vtx_buffers[ D3D_MAX_STREAMS ]; + CGLMBuffer *m_pDummy_vtx_buffer; + D3DIndexDesc m_indices; // Set by SetIndices.. + + IDirect3DVertexShader9 *m_vertexShader; // Set by SetVertexShader... + IDirect3DPixelShader9 *m_pixelShader; // Set by SetPixelShader... + + IDirect3DBaseTexture9 *m_textures[GLM_SAMPLER_COUNT]; // set by SetTexture... NULL if stage inactive + + // GLM flavor stuff + GLMContext *m_ctx; + CGLMFBOMap *m_pFBOs; + bool m_bFBODirty; + + struct ObjectStats_t + { + int m_nTotalFBOs; + int m_nTotalVertexShaders; + int m_nTotalPixelShaders; + int m_nTotalVertexDecls; + int m_nTotalIndexBuffers; + int m_nTotalVertexBuffers; + int m_nTotalRenderTargets; + int m_nTotalTextures; + int m_nTotalSurfaces; + int m_nTotalQueries; + + void clear() { V_memset( this, 0, sizeof(* this ) ); } + + ObjectStats_t &operator -= ( const ObjectStats_t &rhs ) + { + m_nTotalFBOs -= rhs.m_nTotalFBOs; + m_nTotalVertexShaders -= rhs.m_nTotalVertexShaders; + m_nTotalPixelShaders -= rhs.m_nTotalPixelShaders; + m_nTotalVertexDecls -= rhs.m_nTotalVertexDecls; + m_nTotalIndexBuffers -= rhs.m_nTotalIndexBuffers; + m_nTotalVertexBuffers -= rhs.m_nTotalVertexBuffers; + m_nTotalRenderTargets -= rhs.m_nTotalRenderTargets; + m_nTotalTextures -= rhs.m_nTotalTextures; + m_nTotalSurfaces -= rhs.m_nTotalSurfaces; + m_nTotalQueries -= m_nTotalQueries; + return *this; + } + }; + ObjectStats_t m_ObjectStats; + ObjectStats_t m_PrevObjectStats; + void PrintObjectStats( const ObjectStats_t &stats ); + + // GL state + struct + { + // render state buckets + GLAlphaTestEnable_t m_AlphaTestEnable; + GLAlphaTestFunc_t m_AlphaTestFunc; + + GLAlphaToCoverageEnable_t m_AlphaToCoverageEnable; + + GLDepthTestEnable_t m_DepthTestEnable; + GLDepthMask_t m_DepthMask; + GLDepthFunc_t m_DepthFunc; + + GLClipPlaneEnable_t m_ClipPlaneEnable[kGLMUserClipPlanes]; + GLClipPlaneEquation_t m_ClipPlaneEquation[kGLMUserClipPlanes]; + + GLColorMaskSingle_t m_ColorMaskSingle; + GLColorMaskMultiple_t m_ColorMaskMultiple; + + GLCullFaceEnable_t m_CullFaceEnable; + GLCullFrontFace_t m_CullFrontFace; + GLPolygonMode_t m_PolygonMode; + GLDepthBias_t m_DepthBias; + GLScissorEnable_t m_ScissorEnable; + GLScissorBox_t m_ScissorBox; + GLViewportBox_t m_ViewportBox; + GLViewportDepthRange_t m_ViewportDepthRange; + + GLBlendEnable_t m_BlendEnable; + GLBlendFactor_t m_BlendFactor; + GLBlendEquation_t m_BlendEquation; + GLBlendColor_t m_BlendColor; + GLBlendEnableSRGB_t m_BlendEnableSRGB; + + GLStencilTestEnable_t m_StencilTestEnable; + GLStencilFunc_t m_StencilFunc; + GLStencilOp_t m_StencilOp; + GLStencilWriteMask_t m_StencilWriteMask; + + GLClearColor_t m_ClearColor; + GLClearDepth_t m_ClearDepth; + GLClearStencil_t m_ClearStencil; + + bool m_FogEnable; // not really pushed to GL, just latched here + + // samplers + //GLMTexSamplingParams m_samplers[GLM_SAMPLER_COUNT]; + } gl; + +#if GL_BATCH_PERF_ANALYSIS + simple_bitmap *m_pBatch_vis_bitmap; + uint m_nBatchVisY; + uint m_nBatchVisFrameIndex, m_nBatchVisFileIdx; + uint m_nNumProgramChanges; + + uint m_nTotalD3DCalls; + double m_flTotalD3DTime; + uint m_nTotalGLCalls; + double m_flTotalGLTime; + uint m_nTotalPrims; + + uint m_nOverallProgramChanges; + uint m_nOverallDraws; + uint m_nOverallPrims; + uint m_nOverallD3DCalls; + double m_flOverallD3DTime; + uint m_nOverallGLCalls; + double m_flOverallGLTime; + + double m_flOverallPresentTime; + double m_flOverallPresentTimeSquared; + double m_flOverallSwapWindowTime; + double m_flOverallSwapWindowTimeSquared; + uint m_nOverallPresents; +#endif +}; + +FORCEINLINE HRESULT TOGLMETHODCALLTYPE IDirect3DDevice9::SetSamplerState( DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value ) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetSamplerStateNonInline( Sampler, Type, Value ); +#else + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + Assert( Sampler < GLM_SAMPLER_COUNT ); + + m_ctx->SetSamplerDirty( Sampler ); + + switch( Type ) + { + case D3DSAMP_ADDRESSU: + m_ctx->SetSamplerAddressU( Sampler, Value ); + break; + case D3DSAMP_ADDRESSV: + m_ctx->SetSamplerAddressV( Sampler, Value ); + break; + case D3DSAMP_ADDRESSW: + m_ctx->SetSamplerAddressW( Sampler, Value ); + break; + case D3DSAMP_BORDERCOLOR: + m_ctx->SetSamplerBorderColor( Sampler, Value ); + break; + case D3DSAMP_MAGFILTER: + m_ctx->SetSamplerMagFilter( Sampler, Value ); + break; + case D3DSAMP_MIPFILTER: + m_ctx->SetSamplerMipFilter( Sampler, Value ); + break; + case D3DSAMP_MINFILTER: + m_ctx->SetSamplerMinFilter( Sampler, Value ); + break; + case D3DSAMP_MIPMAPLODBIAS: + m_ctx->SetSamplerMipMapLODBias( Sampler, Value ); + break; + case D3DSAMP_MAXMIPLEVEL: + m_ctx->SetSamplerMaxMipLevel( Sampler, Value); + break; + case D3DSAMP_MAXANISOTROPY: + m_ctx->SetSamplerMaxAnisotropy( Sampler, Value); + break; + case D3DSAMP_SRGBTEXTURE: + //m_samplers[ Sampler ].m_srgb = Value; + m_ctx->SetSamplerSRGBTexture(Sampler, Value); + break; + case D3DSAMP_SHADOWFILTER: + m_ctx->SetShadowFilter(Sampler, Value); + break; + + default: DXABSTRACT_BREAK_ON_ERROR(); break; + } + return S_OK; +#endif +} + +FORCEINLINE void TOGLMETHODCALLTYPE IDirect3DDevice9::SetSamplerStates( + DWORD Sampler, DWORD AddressU, DWORD AddressV, DWORD AddressW, + DWORD MinFilter, DWORD MagFilter, DWORD MipFilter, DWORD MinLod, + float LodBias) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + SetSamplerStatesNonInline( Sampler, AddressU, AddressV, AddressW, MinFilter, MagFilter, MipFilter, MinLod, LodBias ); +#else + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + Assert( Sampler < GLM_SAMPLER_COUNT); + + m_ctx->SetSamplerDirty( Sampler ); + + m_ctx->SetSamplerStates( Sampler, AddressU, AddressV, AddressW, MinFilter, MagFilter, MipFilter, MinLod, LodBias ); +#endif +} + +FORCEINLINE HRESULT TOGLMETHODCALLTYPE IDirect3DDevice9::SetTexture(DWORD Stage,IDirect3DBaseTexture9* pTexture) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetTextureNonInline( Stage, pTexture ); +#else + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + Assert( Stage < GLM_SAMPLER_COUNT ); + m_textures[Stage] = pTexture; + m_ctx->SetSamplerTex( Stage, pTexture ? pTexture->m_tex : NULL ); + return S_OK; +#endif +} + +inline GLenum D3DCompareFuncToGL( DWORD function ) +{ + switch ( function ) + { + case D3DCMP_NEVER : return GL_NEVER; // Always fail the test. + case D3DCMP_LESS : return GL_LESS; // Accept the new pixel if its value is less than the value of the current pixel. + case D3DCMP_EQUAL : return GL_EQUAL; // Accept the new pixel if its value equals the value of the current pixel. + case D3DCMP_LESSEQUAL : return GL_LEQUAL; // Accept the new pixel if its value is less than or equal to the value of the current pixel. ** + case D3DCMP_GREATER : return GL_GREATER; // Accept the new pixel if its value is greater than the value of the current pixel. + case D3DCMP_NOTEQUAL : return GL_NOTEQUAL; // Accept the new pixel if its value does not equal the value of the current pixel. + case D3DCMP_GREATEREQUAL: return GL_GEQUAL; // Accept the new pixel if its value is greater than or equal to the value of the current pixel. + case D3DCMP_ALWAYS : return GL_ALWAYS; // Always pass the test. + default : DXABSTRACT_BREAK_ON_ERROR(); return 0xFFFFFFFF; + } +} + +FORCEINLINE GLenum D3DBlendOperationToGL( DWORD operation ) +{ + switch (operation) + { + case D3DBLENDOP_ADD : return GL_FUNC_ADD; // The result is the destination added to the source. Result = Source + Destination + case D3DBLENDOP_SUBTRACT : return GL_FUNC_SUBTRACT; // The result is the destination subtracted from to the source. Result = Source - Destination + case D3DBLENDOP_REVSUBTRACT : return GL_FUNC_REVERSE_SUBTRACT; // The result is the source subtracted from the destination. Result = Destination - Source + case D3DBLENDOP_MIN : return GL_MIN; // The result is the minimum of the source and destination. Result = MIN(Source, Destination) + case D3DBLENDOP_MAX : return GL_MAX; // The result is the maximum of the source and destination. Result = MAX(Source, Destination) + default: + DXABSTRACT_BREAK_ON_ERROR(); + return 0xFFFFFFFF; + break; + } +} + +FORCEINLINE GLenum D3DBlendFactorToGL( DWORD equation ) +{ + switch (equation) + { + case D3DBLEND_ZERO : return GL_ZERO; // Blend factor is (0, 0, 0, 0). + case D3DBLEND_ONE : return GL_ONE; // Blend factor is (1, 1, 1, 1). + case D3DBLEND_SRCCOLOR : return GL_SRC_COLOR; // Blend factor is (Rs, Gs, Bs, As). + case D3DBLEND_INVSRCCOLOR : return GL_ONE_MINUS_SRC_COLOR; // Blend factor is (1 - Rs, 1 - Gs, 1 - Bs, 1 - As). + case D3DBLEND_SRCALPHA : return GL_SRC_ALPHA; // Blend factor is (As, As, As, As). + case D3DBLEND_INVSRCALPHA : return GL_ONE_MINUS_SRC_ALPHA; // Blend factor is ( 1 - As, 1 - As, 1 - As, 1 - As). + case D3DBLEND_DESTALPHA : return GL_DST_ALPHA; // Blend factor is (Ad Ad Ad Ad). + case D3DBLEND_INVDESTALPHA : return GL_ONE_MINUS_DST_ALPHA; // Blend factor is (1 - Ad 1 - Ad 1 - Ad 1 - Ad). + case D3DBLEND_DESTCOLOR : return GL_DST_COLOR; // Blend factor is (Rd, Gd, Bd, Ad). + case D3DBLEND_INVDESTCOLOR : return GL_ONE_MINUS_DST_COLOR; // Blend factor is (1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad). + case D3DBLEND_SRCALPHASAT : return GL_SRC_ALPHA_SATURATE; // Blend factor is (f, f, f, 1); where f = min(As, 1 - Ad). + + /* + // these are weird.... break if we hit them + case D3DBLEND_BOTHSRCALPHA : Assert(0); return GL_ZERO; // Obsolete. Starting with DirectX 6, you can achieve the same effect by setting the source and destination blend factors to D3DBLEND_SRCALPHA and D3DBLEND_INVSRCALPHA in separate calls. + case D3DBLEND_BOTHINVSRCALPHA: Assert(0); return GL_ZERO; // Source blend factor is (1 - As, 1 - As, 1 - As, 1 - As), and destination blend factor is (As, As, As, As); the destination blend selection is overridden. This blend mode is supported only for the D3DRS_SRCBLEND render state. + case D3DBLEND_BLENDFACTOR : Assert(0); return GL_ZERO; // Constant color blending factor used by the frame-buffer blender. This blend mode is supported only if D3DPBLENDCAPS_BLENDFACTOR is set in the SrcBlendCaps or DestBlendCaps members of D3DCAPS9. + + dxabstract.h has not heard of these, so let them hit the debugger if they come through + case D3DBLEND_INVBLENDFACTOR: //Inverted constant color-blending factor used by the frame-buffer blender. This blend mode is supported only if the D3DPBLENDCAPS_BLENDFACTOR bit is set in the SrcBlendCaps or DestBlendCaps members of D3DCAPS9. + case D3DBLEND_SRCCOLOR2: // Blend factor is (PSOutColor[1]r, PSOutColor[1]g, PSOutColor[1]b, not used). This flag is available in Direct3D 9Ex only. + case D3DBLEND_INVSRCCOLOR2: // Blend factor is (1 - PSOutColor[1]r, 1 - PSOutColor[1]g, 1 - PSOutColor[1]b, not used)). This flag is available in Direct3D 9Ex only. + */ + default: + DXABSTRACT_BREAK_ON_ERROR(); + return 0xFFFFFFFF; + break; + } +} + + +FORCEINLINE GLenum D3DStencilOpToGL( DWORD operation ) +{ + switch( operation ) + { + case D3DSTENCILOP_KEEP : return GL_KEEP; + case D3DSTENCILOP_ZERO : return GL_ZERO; + case D3DSTENCILOP_REPLACE : return GL_REPLACE; + case D3DSTENCILOP_INCRSAT : return GL_INCR; + case D3DSTENCILOP_DECRSAT : return GL_DECR; + case D3DSTENCILOP_INVERT : return GL_INVERT; + case D3DSTENCILOP_INCR : return GL_INCR_WRAP_EXT; + case D3DSTENCILOP_DECR : return GL_DECR_WRAP_EXT; + default : DXABSTRACT_BREAK_ON_ERROR(); return 0xFFFFFFFF; + } +} + +FORCEINLINE HRESULT TOGLMETHODCALLTYPE IDirect3DDevice9::SetRenderStateInline( D3DRENDERSTATETYPE State, DWORD Value ) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetRenderState( State, Value ); +#else + TOGL_NULL_DEVICE_CHECK; + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + + switch (State) + { + case D3DRS_ZENABLE: // kGLDepthTestEnable + { + gl.m_DepthTestEnable.enable = Value; + m_ctx->WriteDepthTestEnable( &gl.m_DepthTestEnable ); + break; + } + case D3DRS_ZWRITEENABLE: // kGLDepthMask + { + gl.m_DepthMask.mask = Value; + m_ctx->WriteDepthMask( &gl.m_DepthMask ); + break; + } + case D3DRS_ZFUNC: + { + // kGLDepthFunc + GLenum func = D3DCompareFuncToGL( Value ); + gl.m_DepthFunc.func = func; + m_ctx->WriteDepthFunc( &gl.m_DepthFunc ); + break; + } + case D3DRS_COLORWRITEENABLE: // kGLColorMaskSingle + { + gl.m_ColorMaskSingle.r = ((Value & D3DCOLORWRITEENABLE_RED) != 0) ? 0xFF : 0x00; + gl.m_ColorMaskSingle.g = ((Value & D3DCOLORWRITEENABLE_GREEN)!= 0) ? 0xFF : 0x00; + gl.m_ColorMaskSingle.b = ((Value & D3DCOLORWRITEENABLE_BLUE) != 0) ? 0xFF : 0x00; + gl.m_ColorMaskSingle.a = ((Value & D3DCOLORWRITEENABLE_ALPHA)!= 0) ? 0xFF : 0x00; + m_ctx->WriteColorMaskSingle( &gl.m_ColorMaskSingle ); + break; + } + case D3DRS_CULLMODE: // kGLCullFaceEnable / kGLCullFrontFace + { + switch (Value) + { + case D3DCULL_NONE: + { + gl.m_CullFaceEnable.enable = false; + gl.m_CullFrontFace.value = GL_CCW; //doesn't matter + + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + break; + } + + case D3DCULL_CW: + { + gl.m_CullFaceEnable.enable = true; + gl.m_CullFrontFace.value = GL_CW; //origGL_CCW; + + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + break; + } + case D3DCULL_CCW: + { + gl.m_CullFaceEnable.enable = true; + gl.m_CullFrontFace.value = GL_CCW; //origGL_CW; + + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + break; + } + default: + { + DXABSTRACT_BREAK_ON_ERROR(); + break; + } + } + break; + } + //-------------------------------------------------------------------------------------------- alphablend stuff + case D3DRS_ALPHABLENDENABLE: // kGLBlendEnable + { + gl.m_BlendEnable.enable = Value; + m_ctx->WriteBlendEnable( &gl.m_BlendEnable ); + break; + } + case D3DRS_BLENDOP: // kGLBlendEquation // D3D blend-op ==> GL blend equation + { + GLenum equation = D3DBlendOperationToGL( Value ); + gl.m_BlendEquation.equation = equation; + m_ctx->WriteBlendEquation( &gl.m_BlendEquation ); + break; + } + case D3DRS_SRCBLEND: // kGLBlendFactor // D3D blend-factor ==> GL blend factor + case D3DRS_DESTBLEND: // kGLBlendFactor + { + GLenum factor = D3DBlendFactorToGL( Value ); + + if (State==D3DRS_SRCBLEND) + { + gl.m_BlendFactor.srcfactor = factor; + } + else + { + gl.m_BlendFactor.dstfactor = factor; + } + m_ctx->WriteBlendFactor( &gl.m_BlendFactor ); + break; + } + case D3DRS_SRGBWRITEENABLE: // kGLBlendEnableSRGB + { + gl.m_BlendEnableSRGB.enable = Value; + m_ctx->WriteBlendEnableSRGB( &gl.m_BlendEnableSRGB ); + break; + } + //-------------------------------------------------------------------------------------------- alphatest stuff + case D3DRS_ALPHATESTENABLE: + { + gl.m_AlphaTestEnable.enable = Value; + m_ctx->WriteAlphaTestEnable( &gl.m_AlphaTestEnable ); + break; + } + case D3DRS_ALPHAREF: + { + gl.m_AlphaTestFunc.ref = Value / 255.0f; + m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); + break; + } + case D3DRS_ALPHAFUNC: + { + GLenum func = D3DCompareFuncToGL( Value );; + gl.m_AlphaTestFunc.func = func; + m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); + break; + } + //-------------------------------------------------------------------------------------------- stencil stuff + case D3DRS_STENCILENABLE: // GLStencilTestEnable_t + { + gl.m_StencilTestEnable.enable = Value; + m_ctx->WriteStencilTestEnable( &gl.m_StencilTestEnable ); + break; + } + case D3DRS_STENCILFAIL: // GLStencilOp_t "what do you do if stencil test fails" + { + GLenum stencilop = D3DStencilOpToGL( Value ); + gl.m_StencilOp.sfail = stencilop; + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + break; + } + case D3DRS_STENCILZFAIL: // GLStencilOp_t "what do you do if stencil test passes *but* depth test fails, if depth test happened" + { + GLenum stencilop = D3DStencilOpToGL( Value ); + gl.m_StencilOp.dpfail = stencilop; + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + break; + } + case D3DRS_STENCILPASS: // GLStencilOp_t "what do you do if stencil test and depth test both pass" + { + GLenum stencilop = D3DStencilOpToGL( Value ); + gl.m_StencilOp.dppass = stencilop; + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + break; + } + case D3DRS_STENCILFUNC: // GLStencilFunc_t + { + GLenum stencilfunc = D3DCompareFuncToGL( Value ); + gl.m_StencilFunc.frontfunc = gl.m_StencilFunc.backfunc = stencilfunc; + + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + break; + } + case D3DRS_STENCILREF: // GLStencilFunc_t + { + gl.m_StencilFunc.ref = Value; + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + break; + } + case D3DRS_STENCILMASK: // GLStencilFunc_t + { + gl.m_StencilFunc.mask = Value; + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + break; + } + case D3DRS_STENCILWRITEMASK: // GLStencilWriteMask_t + { + gl.m_StencilWriteMask.mask = Value; + m_ctx->WriteStencilWriteMask( &gl.m_StencilWriteMask ); + break; + } + case D3DRS_FOGENABLE: // none of these are implemented yet... erk + { + gl.m_FogEnable = (Value != 0); + GLMPRINTF(("-D- fogenable = %d",Value )); + break; + } + case D3DRS_SCISSORTESTENABLE: // kGLScissorEnable + { + gl.m_ScissorEnable.enable = Value; + m_ctx->WriteScissorEnable( &gl.m_ScissorEnable ); + break; + } + case D3DRS_DEPTHBIAS: // kGLDepthBias + { + // the value in the dword is actually a float + float fvalue = *(float*)&Value; + gl.m_DepthBias.units = fvalue; + + m_ctx->WriteDepthBias( &gl.m_DepthBias ); + break; + } + // good ref on these: http://aras-p.info/blog/2008/06/12/depth-bias-and-the-power-of-deceiving-yourself/ + case D3DRS_SLOPESCALEDEPTHBIAS: + { + // the value in the dword is actually a float + float fvalue = *(float*)&Value; + gl.m_DepthBias.factor = fvalue; + + m_ctx->WriteDepthBias( &gl.m_DepthBias ); + break; + } + // Alpha to coverage + case D3DRS_ADAPTIVETESS_Y: + { + gl.m_AlphaToCoverageEnable.enable = Value; + m_ctx->WriteAlphaToCoverageEnable( &gl.m_AlphaToCoverageEnable ); + break; + } + case D3DRS_CLIPPLANEENABLE: // kGLClipPlaneEnable + { + // d3d packs all the enables into one word. + // we break that out so we don't do N glEnable calls to sync - + // GLM is tracking one unique enable per plane. + for( int i=0; iWriteClipPlaneEnable( &gl.m_ClipPlaneEnable[x], x ); + break; + } + //-------------------------------------------------------------------------------------------- polygon/fill mode + case D3DRS_FILLMODE: + { + GLuint mode = 0; + switch(Value) + { + case D3DFILL_POINT: mode = GL_POINT; break; + case D3DFILL_WIREFRAME: mode = GL_LINE; break; + case D3DFILL_SOLID: mode = GL_FILL; break; + default: DXABSTRACT_BREAK_ON_ERROR(); break; + } + gl.m_PolygonMode.values[0] = gl.m_PolygonMode.values[1] = mode; + m_ctx->WritePolygonMode( &gl.m_PolygonMode ); + break; + } + } + + return S_OK; +#endif +} + +FORCEINLINE HRESULT TOGLMETHODCALLTYPE IDirect3DDevice9::SetRenderStateConstInline( D3DRENDERSTATETYPE State, DWORD Value ) +{ + // State is a compile time constant - luckily no need to do anything special to get the compiler to optimize this case. + return SetRenderStateInline( State, Value ); +} + +FORCEINLINE HRESULT TOGLMETHODCALLTYPE IDirect3DDevice9::SetIndices(IDirect3DIndexBuffer9* pIndexData) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetIndicesNonInline( pIndexData ); +#else + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + // just latch it. + m_indices.m_idxBuffer = pIndexData; + return S_OK; +#endif +} + +FORCEINLINE HRESULT TOGLMETHODCALLTYPE IDirect3DDevice9::SetStreamSource(UINT StreamNumber,IDirect3DVertexBuffer9* pStreamData,UINT OffsetInBytes,UINT Stride) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetStreamSourceNonInline( StreamNumber, pStreamData, OffsetInBytes, Stride ); +#else + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + Assert( StreamNumber < D3D_MAX_STREAMS ); + Assert( ( Stride & 3 ) == 0 ); // we support non-DWORD aligned strides, but on some drivers (like AMD's) perf goes off a cliff + + // perfectly legal to see a vertex buffer of NULL get passed in here. + // so we need an array to track these. + // OK, we are being given the stride, we don't need to calc it.. + + GLMPRINTF(("-X- IDirect3DDevice9::SetStreamSource setting stream #%d to D3D buf %p (GL name %d); offset %d, stride %d", StreamNumber, pStreamData, (pStreamData) ? pStreamData->m_vtxBuffer->m_name: -1, OffsetInBytes, Stride)); + + if ( !pStreamData ) + { + OffsetInBytes = 0; + Stride = 0; + + m_vtx_buffers[ StreamNumber ] = m_pDummy_vtx_buffer; + } + else + { + // We do not support strides of 0 + Assert( Stride > 0 ); + m_vtx_buffers[ StreamNumber ] = pStreamData->m_vtxBuffer; + } + + m_streams[ StreamNumber ].m_vtxBuffer = pStreamData; + m_streams[ StreamNumber ].m_offset = OffsetInBytes; + m_streams[ StreamNumber ].m_stride = Stride; + + return S_OK; +#endif +} + +FORCEINLINE HRESULT TOGLMETHODCALLTYPE IDirect3DDevice9::SetVertexShaderConstantF(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) // groups of 4 floats! +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetVertexShaderConstantFNonInline( StartRegister, pConstantData, Vector4fCount ); +#else + TOGL_NULL_DEVICE_CHECK; + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + m_ctx->SetProgramParametersF( kGLMVertexProgram, StartRegister, (float *)pConstantData, Vector4fCount ); + return S_OK; +#endif +} + +FORCEINLINE HRESULT TOGLMETHODCALLTYPE IDirect3DDevice9::SetVertexShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetVertexShaderConstantBNonInline( StartRegister, pConstantData, BoolCount ); +#else + TOGL_NULL_DEVICE_CHECK; + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + m_ctx->SetProgramParametersB( kGLMVertexProgram, StartRegister, (int *)pConstantData, BoolCount ); + return S_OK; +#endif +} + +FORCEINLINE HRESULT IDirect3DDevice9::SetVertexShaderConstantI(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) // groups of 4 ints! +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetVertexShaderConstantINonInline( StartRegister, pConstantData, Vector4iCount ); +#else + TOGL_NULL_DEVICE_CHECK; + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + m_ctx->SetProgramParametersI( kGLMVertexProgram, StartRegister, (int *)pConstantData, Vector4iCount ); + return S_OK; +#endif +} + +FORCEINLINE HRESULT TOGLMETHODCALLTYPE IDirect3DDevice9::SetPixelShaderConstantF(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetPixelShaderConstantFNonInline(StartRegister, pConstantData, Vector4fCount); +#else + TOGL_NULL_DEVICE_CHECK; + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + m_ctx->SetProgramParametersF( kGLMFragmentProgram, StartRegister, (float *)pConstantData, Vector4fCount ); + return S_OK; +#endif +} + +HRESULT IDirect3DDevice9::SetVertexShader(IDirect3DVertexShader9* pShader) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetVertexShaderNonInline(pShader); +#else + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + m_ctx->SetVertexProgram( pShader ? pShader->m_vtxProgram : NULL ); + m_vertexShader = pShader; + return S_OK; +#endif +} + +FORCEINLINE HRESULT TOGLMETHODCALLTYPE IDirect3DDevice9::SetPixelShader(IDirect3DPixelShader9* pShader) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetPixelShaderNonInline(pShader); +#else + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + m_ctx->SetFragmentProgram( pShader ? pShader->m_pixProgram : NULL ); + m_pixelShader = pShader; + return S_OK; +#endif +} + +FORCEINLINE HRESULT IDirect3DDevice9::SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetVertexDeclarationNonInline(pDecl); +#else + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + m_pVertDecl = pDecl; + return S_OK; +#endif +} + +FORCEINLINE void IDirect3DDevice9::SetMaxUsedVertexShaderConstantsHint( uint nMaxReg ) +{ +#if GLMDEBUG || GL_BATCH_PERF_ANALYSIS + return SetMaxUsedVertexShaderConstantsHintNonInline( nMaxReg ); +#else + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + m_ctx->SetMaxUsedVertexShaderConstantsHint( nMaxReg ); +#endif +} + +// ------------------------------------------------------------------------------------------------------------------------------ // +// D3DX +// ------------------------------------------------------------------------------------------------------------------------------ // +struct ID3DXInclude +{ + virtual HRESULT Open(D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes); + virtual HRESULT Close(LPCVOID pData); +}; +typedef ID3DXInclude* LPD3DXINCLUDE; + + +struct TOGL_CLASS ID3DXBuffer : public IUnknown +{ + void* GetBufferPointer(); + DWORD GetBufferSize(); +}; + +typedef ID3DXBuffer* LPD3DXBUFFER; + +class ID3DXConstantTable : public IUnknown +{ +}; +typedef ID3DXConstantTable* LPD3DXCONSTANTTABLE; + +TOGL_INTERFACE const char* D3DXGetPixelShaderProfile( IDirect3DDevice9 *pDevice ); + +TOGL_INTERFACE D3DXMATRIX* D3DXMatrixMultiply( D3DXMATRIX *pOut, CONST D3DXMATRIX *pM1, CONST D3DXMATRIX *pM2 ); +TOGL_INTERFACE D3DXVECTOR3* D3DXVec3TransformCoord( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM ); + +TOGL_INTERFACE HRESULT D3DXCreateMatrixStack( DWORD Flags, LPD3DXMATRIXSTACK* ppStack); +TOGL_INTERFACE void D3DXMatrixIdentity( D3DXMATRIX * ); + +TOGL_INTERFACE D3DXINLINE D3DXVECTOR3* D3DXVec3Subtract( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV1, CONST D3DXVECTOR3 *pV2 ) +{ + pOut->x = pV1->x - pV2->x; + pOut->y = pV1->y - pV2->y; + pOut->z = pV1->z - pV2->z; + return pOut; +} + +TOGL_INTERFACE D3DXINLINE D3DXVECTOR3* D3DXVec3Cross( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV1, CONST D3DXVECTOR3 *pV2 ) +{ + D3DXVECTOR3 v; + + v.x = pV1->y * pV2->z - pV1->z * pV2->y; + v.y = pV1->z * pV2->x - pV1->x * pV2->z; + v.z = pV1->x * pV2->y - pV1->y * pV2->x; + + *pOut = v; + return pOut; +} + +TOGL_INTERFACE D3DXINLINE FLOAT D3DXVec3Dot( CONST D3DXVECTOR3 *pV1, CONST D3DXVECTOR3 *pV2 ) +{ + return pV1->x * pV2->x + pV1->y * pV2->y + pV1->z * pV2->z; +} + +TOGL_INTERFACE D3DXMATRIX* D3DXMatrixInverse( D3DXMATRIX *pOut, FLOAT *pDeterminant, CONST D3DXMATRIX *pM ); + +TOGL_INTERFACE D3DXMATRIX* D3DXMatrixTranspose( D3DXMATRIX *pOut, CONST D3DXMATRIX *pM ); + +TOGL_INTERFACE D3DXPLANE* D3DXPlaneNormalize( D3DXPLANE *pOut, CONST D3DXPLANE *pP); + +TOGL_INTERFACE D3DXVECTOR4* D3DXVec4Transform( D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pV, CONST D3DXMATRIX *pM ); + + +TOGL_INTERFACE D3DXVECTOR4* D3DXVec4Normalize( D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pV ); + +TOGL_INTERFACE D3DXMATRIX* D3DXMatrixTranslation( D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z ); + +// Build an ortho projection matrix. (right-handed) +TOGL_INTERFACE D3DXMATRIX* D3DXMatrixOrthoOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn,FLOAT zf ); + +TOGL_INTERFACE D3DXMATRIX* D3DXMatrixPerspectiveRH( D3DXMATRIX *pOut, FLOAT w, FLOAT h, FLOAT zn, FLOAT zf ); + +TOGL_INTERFACE D3DXMATRIX* D3DXMatrixPerspectiveOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn, FLOAT zf ); + +// Transform a plane by a matrix. The vector (a,b,c) must be normal. +// M should be the inverse transpose of the transformation desired. +TOGL_INTERFACE D3DXPLANE* D3DXPlaneTransform( D3DXPLANE *pOut, CONST D3DXPLANE *pP, CONST D3DXMATRIX *pM ); + +TOGL_INTERFACE IDirect3D9 *Direct3DCreate9(UINT SDKVersion); + +TOGL_INTERFACE void D3DPERF_SetOptions( DWORD dwOptions ); + +TOGL_INTERFACE HRESULT D3DXCompileShader( + LPCSTR pSrcData, + UINT SrcDataLen, + CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, + LPCSTR pFunctionName, + LPCSTR pProfile, + DWORD Flags, + LPD3DXBUFFER* ppShader, + LPD3DXBUFFER* ppErrorMsgs, + LPD3DXCONSTANTTABLE* ppConstantTable); + +// fake D3D usage constant for SRGB tex creation +#define D3DUSAGE_TEXTURE_SRGB (0x80000000L) + +#else + + //USE_ACTUAL_DX + #ifndef WIN32 + #error sorry man + #endif + + #ifdef _X360 + #include "d3d9.h" + #include "d3dx9.h" + #else + #include + #include "../../dx9sdk/include/d3d9.h" + #include "../../dx9sdk/include/d3dx9.h" + #endif + typedef HWND VD3DHWND; + +#endif // DX_TO_GL_ABSTRACTION + +#endif // DXABSTRACT_H diff --git a/public/togles/linuxwin/dxabstract_types.h b/public/togles/linuxwin/dxabstract_types.h new file mode 100644 index 00000000..9713ec71 --- /dev/null +++ b/public/togles/linuxwin/dxabstract_types.h @@ -0,0 +1,1748 @@ +//========= 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. +// +// dxabstract_types.h +// +//================================================================================================== +#ifndef DXABSTRACT_TYPES_H +#define DXABSTRACT_TYPES_H + +#pragma once + +#if GL_BATCH_PERF_ANALYSIS + class simple_bitmap; +#endif + +struct IUnknown; +struct IDirect3D9; +struct IDirect3DDevice9; +struct IDirect3DResource9; +struct IDirect3DBaseTexture9; +struct IDirect3DTexture9; +struct IDirect3DCubeTexture9; +struct IDirect3DVolumeTexture9; +struct IDirect3DSurface9; +struct IDirect3DVertexDeclaration9; +struct IDirect3DQuery9; +struct IDirect3DVertexBuffer9; +struct IDirect3DIndexBuffer9; +struct IDirect3DPixelShader9; +struct IDirect3DVertexShader9; +struct IDirect3DDevice9Params; + +class GLMContext; +struct GLMRect; +struct GLMShaderPairInfo; +class CGLMBuffer; +class CGLMQuery; +class CGLMTex; +class CGLMProgram; +class CGLMFBO; + +#ifdef TOGL_DLL_EXPORT + #define TOGL_INTERFACE DLL_EXPORT + #define TOGL_OVERLOAD DLL_GLOBAL_EXPORT + #define TOGL_CLASS DLL_CLASS_EXPORT + #define TOGL_GLOBAL DLL_GLOBAL_EXPORT +#else + #define TOGL_INTERFACE DLL_IMPORT + #define TOGL_OVERLOAD DLL_GLOBAL_IMPORT + #define TOGL_CLASS DLL_CLASS_IMPORT + #define TOGL_GLOBAL DLL_GLOBAL_IMPORT +#endif + +#define TOGLMETHODCALLTYPE __stdcall +//#define TOGLMETHODCALLTYPE + +#define DXABSTRACT_BREAK_ON_ERROR() DebuggerBreak() + +typedef void* VD3DHWND; +typedef void* VD3DHANDLE; + +#define MAKEFOURCC(ch0, ch1, ch2, ch3) ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 )) + +// +// +// Stuff that would be in windows.h +// +// +#if !defined(_WINNT_) + + typedef int INT; + typedef unsigned long ULONG; + typedef long LONG; + typedef float FLOAT; + typedef unsigned int DWORD; + typedef unsigned short WORD; + typedef long long LONGLONG; + typedef unsigned int UINT; + typedef long HRESULT; + typedef unsigned char BYTE; + #define CONST const + + #if defined(POSIX) + typedef size_t ULONG_PTR; + #else + typedef unsigned long ULONG_PTR; + #endif + + typedef ULONG_PTR SIZE_T; + + typedef const char* LPCSTR; + typedef char* LPSTR; + typedef DWORD* LPDWORD; + + #define ZeroMemory RtlZeroMemory + #define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length)) + + typedef union _LARGE_INTEGER + { + struct + { + DWORD LowPart; + LONG HighPart; + }; + struct + { + DWORD LowPart; + LONG HighPart; + } u; + LONGLONG QuadPart; + } LARGE_INTEGER; + + typedef struct _GUID + { + bool operator==( const struct _GUID &other ) const; + + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[ 8 ]; + } GUID; + + typedef struct _RECT + { + int left; + int top; + int right; + int bottom; + } RECT; + + typedef struct tagPOINT + { + LONG x; + LONG y; + } POINT, *PPOINT, *LPPOINT; + + typedef struct _MEMORYSTATUS + { + DWORD dwLength; + SIZE_T dwTotalPhys; + } MEMORYSTATUS, *LPMEMORYSTATUS; + + typedef DWORD COLORREF; + #define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16))) + + #define MAKE_HRESULT(sev,fac,code) ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) ) + + #define S_FALSE ((HRESULT)0x00000001L) + #define S_OK 0 + #define E_FAIL ((HRESULT)0x80004005L) + #define E_OUTOFMEMORY ((HRESULT)0x8007000EL) + + #define FAILED(hr) ((HRESULT)(hr) < 0) + #define SUCCEEDED(hr) ((HRESULT)(hr) >= 0) + + struct RGNDATA + { + }; + + typedef const void* LPCVOID; +#endif + +//----------------------------------------------------------------------------- + +typedef enum _D3DFORMAT D3DFORMAT; + +#define D3DSI_OPCODE_MASK 0x0000FFFF +#define D3DSP_TEXTURETYPE_MASK 0x78000000 + +#define D3DUSAGE_AUTOGENMIPMAP 0x00000400L +#define D3DSP_DCL_USAGE_MASK 0x0000000f + +#define D3DSP_OPCODESPECIFICCONTROL_MASK 0x00ff0000 +#define D3DSP_OPCODESPECIFICCONTROL_SHIFT 16 + +// Comparison for dynamic conditional instruction opcodes (i.e. if, breakc) +typedef enum _D3DSHADER_COMPARISON +{ + // < = > + D3DSPC_RESERVED0= 0, // 0 0 0 + D3DSPC_GT = 1, // 0 0 1 + D3DSPC_EQ = 2, // 0 1 0 + D3DSPC_GE = 3, // 0 1 1 + D3DSPC_LT = 4, // 1 0 0 + D3DSPC_NE = 5, // 1 0 1 + D3DSPC_LE = 6, // 1 1 0 + D3DSPC_RESERVED1= 7 // 1 1 1 +} D3DSHADER_COMPARISON; + + +// Comparison is part of instruction opcode token: +#define D3DSHADER_COMPARISON_SHIFT D3DSP_OPCODESPECIFICCONTROL_SHIFT +#define D3DSHADER_COMPARISON_MASK (0x7<>8)&0xFF) +#define D3DSHADER_VERSION_MINOR(_Version) (((_Version)>>0)&0xFF) + +#define D3DSHADER_ADDRESSMODE_SHIFT 13 +#define D3DSHADER_ADDRESSMODE_MASK (1 << D3DSHADER_ADDRESSMODE_SHIFT) + +#define D3DPS_END() 0x0000FFFF + +// ps_2_0 texld controls +#define D3DSI_TEXLD_PROJECT (0x01 << D3DSP_OPCODESPECIFICCONTROL_SHIFT) +#define D3DSI_TEXLD_BIAS (0x02 << D3DSP_OPCODESPECIFICCONTROL_SHIFT) + + +// destination parameter write mask +#define D3DSP_WRITEMASK_0 0x00010000 // Component 0 (X;Red) +#define D3DSP_WRITEMASK_1 0x00020000 // Component 1 (Y;Green) +#define D3DSP_WRITEMASK_2 0x00040000 // Component 2 (Z;Blue) +#define D3DSP_WRITEMASK_3 0x00080000 // Component 3 (W;Alpha) +#define D3DSP_WRITEMASK_ALL 0x000F0000 // All Components + +#define D3DVS_SWIZZLE_SHIFT 16 +#define D3DVS_SWIZZLE_MASK 0x00FF0000 + +// The following bits define where to take component X from: + +#define D3DVS_X_X (0 << D3DVS_SWIZZLE_SHIFT) +#define D3DVS_X_Y (1 << D3DVS_SWIZZLE_SHIFT) +#define D3DVS_X_Z (2 << D3DVS_SWIZZLE_SHIFT) +#define D3DVS_X_W (3 << D3DVS_SWIZZLE_SHIFT) + +// The following bits define where to take component Y from: + +#define D3DVS_Y_X (0 << (D3DVS_SWIZZLE_SHIFT + 2)) +#define D3DVS_Y_Y (1 << (D3DVS_SWIZZLE_SHIFT + 2)) +#define D3DVS_Y_Z (2 << (D3DVS_SWIZZLE_SHIFT + 2)) +#define D3DVS_Y_W (3 << (D3DVS_SWIZZLE_SHIFT + 2)) + +// The following bits define where to take component Z from: + +#define D3DVS_Z_X (0 << (D3DVS_SWIZZLE_SHIFT + 4)) +#define D3DVS_Z_Y (1 << (D3DVS_SWIZZLE_SHIFT + 4)) +#define D3DVS_Z_Z (2 << (D3DVS_SWIZZLE_SHIFT + 4)) +#define D3DVS_Z_W (3 << (D3DVS_SWIZZLE_SHIFT + 4)) + +// The following bits define where to take component W from: + +#define D3DVS_W_X (0 << (D3DVS_SWIZZLE_SHIFT + 6)) +#define D3DVS_W_Y (1 << (D3DVS_SWIZZLE_SHIFT + 6)) +#define D3DVS_W_Z (2 << (D3DVS_SWIZZLE_SHIFT + 6)) +#define D3DVS_W_W (3 << (D3DVS_SWIZZLE_SHIFT + 6)) + +// source parameter modifiers +#define D3DSP_SRCMOD_SHIFT 24 +#define D3DSP_SRCMOD_MASK 0x0F000000 + +// ------------------------------------------------------------------------------------------------------------------------------ // +// ENUMS +// ------------------------------------------------------------------------------------------------------------------------------ // + +typedef enum _D3DSHADER_PARAM_SRCMOD_TYPE +{ + D3DSPSM_NONE = 0<= 2.0 + + + D3DDECLTYPE_UBYTE4N = 8, // Each of 4 bytes is normalized by dividing to 255.0 + D3DDECLTYPE_SHORT2N = 9, // 2D signed short normalized (v[0]/32767.0,v[1]/32767.0,0,1) + D3DDECLTYPE_SHORT4N = 10, // 4D signed short normalized (v[0]/32767.0,v[1]/32767.0,v[2]/32767.0,v[3]/32767.0) + D3DDECLTYPE_USHORT2N = 11, // 2D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,0,1) + D3DDECLTYPE_USHORT4N = 12, // 4D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,v[2]/65535.0,v[3]/65535.0) + D3DDECLTYPE_UDEC3 = 13, // 3D unsigned 10 10 10 format expanded to (value, value, value, 1) + D3DDECLTYPE_DEC3N = 14, // 3D signed 10 10 10 format normalized and expanded to (v[0]/511.0, v[1]/511.0, v[2]/511.0, 1) + D3DDECLTYPE_FLOAT16_2 = 15, // Two 16-bit floating point values, expanded to (value, value, 0, 1) + D3DDECLTYPE_FLOAT16_4 = 16, // Four 16-bit floating point values + D3DDECLTYPE_UNUSED = 17, // When the type field in a decl is unused. +} D3DDECLTYPE; + +typedef enum _D3DDECLMETHOD +{ + D3DDECLMETHOD_DEFAULT = 0, + D3DDECLMETHOD_PARTIALU, + D3DDECLMETHOD_PARTIALV, + D3DDECLMETHOD_CROSSUV, // Normal + D3DDECLMETHOD_UV, + D3DDECLMETHOD_LOOKUP, // Lookup a displacement map + D3DDECLMETHOD_LOOKUPPRESAMPLED, // Lookup a pre-sampled displacement map +} D3DDECLMETHOD; + +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; + +typedef enum _D3DPRIMITIVETYPE +{ + D3DPT_POINTLIST = 1, + D3DPT_LINELIST = 2, + D3DPT_TRIANGLELIST = 4, + D3DPT_TRIANGLESTRIP = 5, + D3DPT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DPRIMITIVETYPE; + +// ------------------------------------------------------------------------------------------------------------------------------ // +// STRUCTURES +// ------------------------------------------------------------------------------------------------------------------------------ // + +typedef struct TOGL_CLASS D3DXPLANE +{ + float& operator[]( int i ); + bool operator==( const D3DXPLANE &o ); + bool operator!=( const D3DXPLANE &o ); + operator float*(); + operator const float*() const; + + float a, b, c, d; +} D3DXPLANE; + +typedef enum _D3DVERTEXBLENDFLAGS +{ + D3DVBF_DISABLE = 0, // Disable vertex blending + D3DVBF_1WEIGHTS = 1, // 2 matrix blending + D3DVBF_2WEIGHTS = 2, // 3 matrix blending + D3DVBF_3WEIGHTS = 3, // 4 matrix blending + D3DVBF_TWEENING = 255, // blending using D3DRS_TWEENFACTOR + D3DVBF_0WEIGHTS = 256, // one matrix is used with weight 1.0 + D3DVBF_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} D3DVERTEXBLENDFLAGS; + +typedef struct _D3DINDEXBUFFER_DESC +{ + D3DFORMAT Format; + D3DRESOURCETYPE Type; + DWORD Usage; + D3DPOOL Pool; + UINT Size; +} D3DINDEXBUFFER_DESC; + +typedef struct _D3DVERTEXELEMENT9 +{ + WORD Stream; // Stream index + WORD Offset; // Offset in the stream in bytes + BYTE Type; // Data type + BYTE Method; // Processing method + BYTE Usage; // Semantics + BYTE UsageIndex; // Semantic index +} D3DVERTEXELEMENT9, *LPD3DVERTEXELEMENT9; + + +#define MAX_DEVICE_IDENTIFIER_STRING 512 +typedef struct _D3DADAPTER_IDENTIFIER9 +{ + char Driver[MAX_DEVICE_IDENTIFIER_STRING]; + char Description[MAX_DEVICE_IDENTIFIER_STRING]; + char DeviceName[32]; /* Device name for GDI (ex. \\.\DISPLAY1) */ + + LARGE_INTEGER DriverVersion; /* Defined for 32 bit components */ + + DWORD VendorId; + DWORD DeviceId; + DWORD SubSysId; + DWORD Revision; + DWORD VideoMemory; + +} D3DADAPTER_IDENTIFIER9; + +typedef struct _D3DCOLORVALUE +{ + float r; + float g; + float b; + float a; +} D3DCOLORVALUE; + +typedef struct _D3DMATERIAL9 +{ + D3DCOLORVALUE Diffuse; /* Diffuse color RGBA */ + D3DCOLORVALUE Ambient; /* Ambient color RGB */ + D3DCOLORVALUE Specular; /* Specular 'shininess' */ + D3DCOLORVALUE Emissive; /* Emissive color RGB */ + float Power; /* Sharpness if specular highlight */ +} D3DMATERIAL9; + +typedef struct _D3DVOLUME_DESC +{ + D3DFORMAT Format; + D3DRESOURCETYPE Type; + DWORD Usage; + D3DPOOL Pool; + + UINT Width; + UINT Height; + UINT Depth; +} D3DVOLUME_DESC; + +typedef struct _D3DVIEWPORT9 +{ + DWORD X; + DWORD Y; /* Viewport Top left */ + DWORD Width; + DWORD Height; /* Viewport Dimensions */ + float MinZ; /* Min/max of clip Volume */ + float MaxZ; +} D3DVIEWPORT9; + +typedef struct _D3DPSHADERCAPS2_0 +{ + DWORD Caps; + INT DynamicFlowControlDepth; + INT NumTemps; + INT StaticFlowControlDepth; + INT NumInstructionSlots; +} D3DPSHADERCAPS2_0; + +typedef struct _D3DCAPS9 +{ + /* Device Info */ + D3DDEVTYPE DeviceType; + + /* Caps from DX7 Draw */ + DWORD Caps; + DWORD Caps2; + + /* Cursor Caps */ + DWORD CursorCaps; + + /* 3D Device Caps */ + DWORD DevCaps; + + DWORD PrimitiveMiscCaps; + DWORD RasterCaps; + DWORD TextureCaps; + DWORD TextureFilterCaps; // D3DPTFILTERCAPS for IDirect3DTexture9's + + DWORD MaxTextureWidth, MaxTextureHeight; + DWORD MaxVolumeExtent; + + DWORD MaxTextureAspectRatio; + DWORD MaxAnisotropy; + + DWORD TextureOpCaps; + DWORD MaxTextureBlendStages; + DWORD MaxSimultaneousTextures; + + DWORD VertexProcessingCaps; + DWORD MaxActiveLights; + DWORD MaxUserClipPlanes; + DWORD MaxVertexBlendMatrices; + DWORD MaxVertexBlendMatrixIndex; + + DWORD MaxPrimitiveCount; // max number of primitives per DrawPrimitive call + DWORD MaxStreams; + + DWORD VertexShaderVersion; + DWORD MaxVertexShaderConst; // number of vertex shader constant registers + + DWORD PixelShaderVersion; + + // Here are the DX9 specific ones + DWORD DevCaps2; + D3DPSHADERCAPS2_0 PS20Caps; + + DWORD NumSimultaneousRTs; // Will be at least 1 + DWORD MaxVertexShader30InstructionSlots; + DWORD MaxPixelShader30InstructionSlots; + + // only on Posix/GL + DWORD FakeSRGBWrite; // 1 for parts which can't support SRGB writes due to driver issues - 0 for others + DWORD MixedSizeTargets; // 1 for parts which can mix attachment sizes (RT's color vs depth) + DWORD CanDoSRGBReadFromRTs; // 0 when we're on Leopard, 1 when on Snow Leopard +} D3DCAPS9; + +typedef struct _D3DDISPLAYMODE +{ + UINT Width; + UINT Height; + UINT RefreshRate; + D3DFORMAT Format; +} D3DDISPLAYMODE; + +typedef struct _D3DGAMMARAMP +{ + WORD red [256]; + WORD green[256]; + WORD blue [256]; +} D3DGAMMARAMP; + + +/* Resize Optional Parameters */ +typedef struct _D3DPRESENT_PARAMETERS_ +{ + UINT BackBufferWidth; + UINT BackBufferHeight; + D3DFORMAT BackBufferFormat; + UINT BackBufferCount; + + D3DMULTISAMPLE_TYPE MultiSampleType; + DWORD MultiSampleQuality; + + D3DSWAPEFFECT SwapEffect; + VD3DHWND hDeviceWindow; + BOOL Windowed; + BOOL EnableAutoDepthStencil; + D3DFORMAT AutoDepthStencilFormat; + DWORD Flags; + + /* FullScreen_RefreshRateInHz must be zero for Windowed mode */ + UINT FullScreen_RefreshRateInHz; + UINT PresentationInterval; +} D3DPRESENT_PARAMETERS; + +typedef struct _D3DDEVICE_CREATION_PARAMETERS +{ + UINT AdapterOrdinal; + D3DDEVTYPE DeviceType; + VD3DHWND hFocusWindow; + DWORD BehaviorFlags; +} D3DDEVICE_CREATION_PARAMETERS; + +/* Structures for LockBox */ +typedef struct _D3DBOX +{ + UINT Left; + UINT Top; + UINT Right; + UINT Bottom; + UINT Front; + UINT Back; +} D3DBOX; + +typedef struct _D3DLOCKED_BOX +{ + INT RowPitch; + INT SlicePitch; + void* pBits; +} D3DLOCKED_BOX; + +typedef struct _D3DSURFACE_DESC +{ + D3DFORMAT Format; + D3DRESOURCETYPE Type; + DWORD Usage; + D3DPOOL Pool; + + D3DMULTISAMPLE_TYPE MultiSampleType; + DWORD MultiSampleQuality; + UINT Width; + UINT Height; +} D3DSURFACE_DESC; + + +typedef struct _D3DLOCKED_RECT +{ + INT Pitch; + void* pBits; +} D3DLOCKED_RECT; + + +typedef struct _D3DRASTER_STATUS +{ + BOOL InVBlank; + UINT ScanLine; +} D3DRASTER_STATUS; + +typedef enum _D3DLIGHTTYPE +{ + D3DLIGHT_POINT = 1, + D3DLIGHT_SPOT = 2, + D3DLIGHT_DIRECTIONAL = 3, + D3DLIGHT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DLIGHTTYPE; + +typedef struct TOGL_CLASS _D3DVECTOR +{ + float x; + float y; + float z; +} D3DVECTOR; + +class TOGL_CLASS D3DXVECTOR2 +{ +public: + operator FLOAT* (); + operator CONST FLOAT* () const; + + float x,y; +}; + +class TOGL_CLASS D3DXVECTOR3 : public D3DVECTOR +{ +public: + D3DXVECTOR3() {} + D3DXVECTOR3( float a, float b, float c ); + operator FLOAT* (); + operator CONST FLOAT* () const; +}; + +typedef enum _D3DXINCLUDE_TYPE +{ + D3DXINC_LOCAL, + + // force 32-bit size enum + D3DXINC_FORCE_DWORD = 0x7fffffff + +} D3DXINCLUDE_TYPE; + +typedef struct _D3DLIGHT9 +{ + D3DLIGHTTYPE Type; /* Type of light source */ + D3DCOLORVALUE Diffuse; /* Diffuse color of light */ + D3DCOLORVALUE Specular; /* Specular color of light */ + D3DCOLORVALUE Ambient; /* Ambient color of light */ + D3DVECTOR Position; /* Position in world space */ + D3DVECTOR Direction; /* Direction in world space */ + float Range; /* Cutoff range */ + float Falloff; /* Falloff */ + float Attenuation0; /* Constant attenuation */ + float Attenuation1; /* Linear attenuation */ + float Attenuation2; /* Quadratic attenuation */ + float Theta; /* Inner angle of spotlight cone */ + float Phi; /* Outer angle of spotlight cone */ +} D3DLIGHT9; + +class TOGL_CLASS D3DXVECTOR4 +{ +public: + D3DXVECTOR4() {} + D3DXVECTOR4( float a, float b, float c, float d ); + + float x,y,z,w; +}; + +//---------------------------------------------------------------------------- +// D3DXMACRO: +// ---------- +// Preprocessor macro definition. The application pass in a NULL-terminated +// array of this structure to various D3DX APIs. This enables the application +// to #define tokens at runtime, before the file is parsed. +//---------------------------------------------------------------------------- + +typedef struct _D3DXMACRO +{ + LPCSTR Name; + LPCSTR Definition; + +} D3DXMACRO, *LPD3DXMACRO; + +// ------------------------------------------------------------------------------------------------------------------------------ // +// ------------------------------------------------------------------------------------------------------------------------------ // +// **** FIXED FUNCTION STUFF - None of this stuff needs support in GL. +// +// Also look for any functions marked with "**** FIXED FUNCTION STUFF" +// +// It's only laying around here so we don't have to chop up the shader system a lot to strip out the fixed function code paths. +// ------------------------------------------------------------------------------------------------------------------------------ // +// ------------------------------------------------------------------------------------------------------------------------------ // + +// **** FIXED FUNCTION STUFF - None of this stuff needs support in GL. +typedef enum _D3DTRANSFORMSTATETYPE +{ + D3DTS_VIEW = 2, + D3DTS_PROJECTION = 3, + D3DTS_TEXTURE0 = 16, + D3DTS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTRANSFORMSTATETYPE; + +// **** FIXED FUNCTION STUFF - None of this stuff needs support in GL. +typedef enum _D3DTEXTUREOP +{ + // Control + D3DTOP_DISABLE = 1, // disables stage + D3DTOP_SELECTARG1 = 2, // the default + D3DTOP_SELECTARG2 = 3, + + // Modulate + D3DTOP_MODULATE = 4, // multiply args together + D3DTOP_MODULATE2X = 5, // multiply and 1 bit + D3DTOP_MODULATE4X = 6, // multiply and 2 bits + + // Add + D3DTOP_ADD = 7, // add arguments together + D3DTOP_ADDSIGNED = 8, // add with -0.5 bias + D3DTOP_ADDSIGNED2X = 9, // as above but left 1 bit + D3DTOP_SUBTRACT = 10, // Arg1 - Arg2, with no saturation + D3DTOP_ADDSMOOTH = 11, // add 2 args, subtract product + // Arg1 + Arg2 - Arg1*Arg2 + // = Arg1 + (1-Arg1)*Arg2 + + // Linear alpha blend: Arg1*(Alpha) + Arg2*(1-Alpha) + D3DTOP_BLENDDIFFUSEALPHA = 12, // iterated alpha + D3DTOP_BLENDTEXTUREALPHA = 13, // texture alpha + D3DTOP_BLENDFACTORALPHA = 14, // alpha from D3DRS_TEXTUREFACTOR + + // Linear alpha blend with pre-multiplied arg1 input: Arg1 + Arg2*(1-Alpha) + D3DTOP_BLENDTEXTUREALPHAPM = 15, // texture alpha + D3DTOP_BLENDCURRENTALPHA = 16, // by alpha of current color + + // Specular mapping + D3DTOP_PREMODULATE = 17, // modulate with next texture before use + D3DTOP_MODULATEALPHA_ADDCOLOR = 18, // Arg1.RGB + Arg1.A*Arg2.RGB + // COLOROP only + D3DTOP_MODULATECOLOR_ADDALPHA = 19, // Arg1.RGB*Arg2.RGB + Arg1.A + // COLOROP only + D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20, // (1-Arg1.A)*Arg2.RGB + Arg1.RGB + // COLOROP only + D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21, // (1-Arg1.RGB)*Arg2.RGB + Arg1.A + // COLOROP only + + // Bump mapping + D3DTOP_BUMPENVMAP = 22, // per pixel env map perturbation + D3DTOP_BUMPENVMAPLUMINANCE = 23, // with luminance channel + + // This can do either diffuse or specular bump mapping with correct input. + // Performs the function (Arg1.R*Arg2.R + Arg1.G*Arg2.G + Arg1.B*Arg2.B) + // where each component has been scaled and offset to make it signed. + // The result is replicated into all four (including alpha) channels. + // This is a valid COLOROP only. + D3DTOP_DOTPRODUCT3 = 24, + + // Triadic ops + D3DTOP_MULTIPLYADD = 25, // Arg0 + Arg1*Arg2 + D3DTOP_LERP = 26, // (Arg0)*Arg1 + (1-Arg0)*Arg2 + + D3DTOP_FORCE_DWORD = 0x7fffffff, +} D3DTEXTUREOP; + +// **** FIXED FUNCTION STUFF - None of this stuff needs support in GL. +typedef enum _D3DTEXTURESTAGESTATETYPE +{ + D3DTSS_COLOROP = 1, /* D3DTEXTUREOP - per-stage blending controls for color channels */ + D3DTSS_COLORARG1 = 2, /* D3DTA_* (texture arg) */ + D3DTSS_COLORARG2 = 3, /* D3DTA_* (texture arg) */ + D3DTSS_ALPHAOP = 4, /* D3DTEXTUREOP - per-stage blending controls for alpha channel */ + D3DTSS_ALPHAARG1 = 5, /* D3DTA_* (texture arg) */ + D3DTSS_ALPHAARG2 = 6, /* D3DTA_* (texture arg) */ + D3DTSS_BUMPENVMAT00 = 7, /* float (bump mapping matrix) */ + D3DTSS_BUMPENVMAT01 = 8, /* float (bump mapping matrix) */ + D3DTSS_BUMPENVMAT10 = 9, /* float (bump mapping matrix) */ + D3DTSS_BUMPENVMAT11 = 10, /* float (bump mapping matrix) */ + D3DTSS_TEXCOORDINDEX = 11, /* identifies which set of texture coordinates index this texture */ + D3DTSS_BUMPENVLOFFSET = 23, /* float offset for bump map luminance */ + D3DTSS_TEXTURETRANSFORMFLAGS = 24, /* D3DTEXTURETRANSFORMFLAGS controls texture transform */ + D3DTSS_COLORARG0 = 26, /* D3DTA_* third arg for triadic ops */ + D3DTSS_RESULTARG = 28, /* D3DTA_* arg for result (CURRENT or TEMP) */ + + + D3DTSS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTEXTURESTAGESTATETYPE; + +//===========================================================================// + +enum GLMVertexAttributeIndex +{ + kGLMGenericAttr00 = 0, + kGLMGenericAttr01, + kGLMGenericAttr02, + kGLMGenericAttr03, + kGLMGenericAttr04, + kGLMGenericAttr05, + kGLMGenericAttr06, + kGLMGenericAttr07, + kGLMGenericAttr08, + kGLMGenericAttr09, + kGLMGenericAttr10, + kGLMGenericAttr11, + kGLMGenericAttr12, + kGLMGenericAttr13, + kGLMGenericAttr14, + kGLMGenericAttr15, + + kGLMVertexAttributeIndexMax // ideally < 32 +}; + +struct GLMVertexAttributeDesc // all the info you need to do vertex setup for one attribute +{ + CGLMBuffer *m_pBuffer; // NULL allowed in which case m_offset is the full 32-bit pointer.. so you can draw from plain RAM if desired + GLuint m_nCompCount; // comp count of the attribute (1-4) + GLenum m_datatype; // data type of the attribute (GL_FLOAT, GL_UNSIGNED_BYTE, etc) + GLuint m_stride; + GLuint m_offset; // net offset to attribute 'zero' within the buffer. + GLuint m_streamOffset; // net offset to attribute 'zero' within the buffer. + GLboolean m_normalized; // apply to any fixed point data that needs normalizing, esp color bytes + + inline uint GetDataTypeSizeInBytes() const + { + switch ( m_datatype ) + { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + return 1; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_HALF_FLOAT: + return 2; + case GL_INT: + case GL_FLOAT: + return 4; + default: + Assert( 0 ); + break; + } + return 0; + } + + inline uint GetTotalAttributeSizeInBytes() const { Assert( m_nCompCount ); return m_nCompCount * GetDataTypeSizeInBytes(); } + + // may need a seed value at some point to be able to disambiguate re-lifed buffers holding same pointer + // simpler alternative is to do shoot-down inside the vertex/index buffer free calls. + // I'd rather not have to have each attribute fiddling a ref count on the buffer to which it refers.. + +//#define EQ(fff) ( (src.fff) == (fff) ) + // test in decreasing order of likelihood of difference, but do not include the buffer revision as caller is not supplying it.. + //inline bool operator== ( const GLMVertexAttributeDesc& src ) const { return EQ( m_pBuffer ) && EQ( m_offset ) && EQ( m_stride ) && EQ( m_datatype ) && EQ( m_normalized ) && EQ( m_nCompCount ); } +//#undef EQ + + uint m_bufferRevision; // only set in GLM context's copy, to disambiguate references that are same offset / same buffer but cross an orphan event +}; + +#define MAX_D3DVERTEXELEMENTS 16 + +struct D3DVERTEXELEMENT9_GL +{ + // fields right out of the original decl element (copied) + D3DVERTEXELEMENT9 m_dxdecl; // d3d info + // WORD Stream; // Stream index + // WORD Offset; // Offset in the stream in bytes + // BYTE Type; // Data type + // BYTE Method; // Processing method + // BYTE Usage; // Semantics + // BYTE UsageIndex; // Semantic index + + GLMVertexAttributeDesc m_gldecl; + // CGLMBuffer *m_buffer; // late-dropped from selected stream desc (left NULL, will replace with stream source buffer at sync time) + // GLuint m_datasize; // component count (1,2,3,4) of the attrib + // GLenum m_datatype; // data type of the attribute (GL_FLOAT et al) + // GLuint m_stride; // late-dropped from stream desc + // GLuint m_offset; // net offset to attribute 'zero' within the stream data. Add the stream offset before passing to GL. + // GLuint m_normalized; // net offset to attribute 'zero' within the stream data. Add the stream offset before passing to GL. +}; + +struct IDirect3DDevice9Params +{ + UINT m_adapter; + D3DDEVTYPE m_deviceType; + VD3DHWND m_focusWindow; + DWORD m_behaviorFlags; + D3DPRESENT_PARAMETERS m_presentationParameters; +}; + +#define D3D_MAX_STREAMS 5 //9 +struct D3DStreamDesc +{ + IDirect3DVertexBuffer9 *m_vtxBuffer; + uint m_offset; + uint m_stride; +}; + +struct D3DIndexDesc +{ + IDirect3DIndexBuffer9 *m_idxBuffer; +}; + +// we latch sampler values until draw time and then convert them all to GL form +// note these are similar in name to the fields of a GLMTexSamplingParams but contents are not +// particularly in the texture filtering area + +struct D3DSamplerDesc +{ + DWORD m_srgb; // D3DSAMP_SRGBTEXTURE 0 = no SRGB sampling +}; + +// Tracking and naming sampler dimensions +#define SAMPLER_TYPE_2D 0 +#define SAMPLER_TYPE_CUBE 1 +#define SAMPLER_TYPE_3D 2 +#define SAMPLER_TYPE_UNUSED 3 + +#endif // DXABSTRACT_TYPES_H diff --git a/public/togles/linuxwin/glbase.h b/public/togles/linuxwin/glbase.h new file mode 100644 index 00000000..9c830e6d --- /dev/null +++ b/public/togles/linuxwin/glbase.h @@ -0,0 +1,117 @@ +//========= 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. +// +// glbase.h +// +//=============================================================================== + +#ifndef GLBASE_H +#define GLBASE_H + +#ifdef DX_TO_GL_ABSTRACTION + +#undef HAVE_GL_ARB_SYNC + +#ifndef OSX +#define HAVE_GL_ARB_SYNC 1 +#endif + +#ifdef USE_SDL +#include "SDL_opengl.h" +#endif + +#ifdef OSX +#include +#include +#endif + +#ifdef DX_TO_GL_ABSTRACTION + #ifndef WIN32 + #define Debugger DebuggerBreak + #endif + #undef CurrentTime + + #if defined( USE_SDL ) + #include "SDL.h" + #endif +#endif + +//=============================================================================== +// glue to call out to Obj-C land (these are in glmgrcocoa.mm) +#ifdef OSX + typedef void _PseudoNSGLContext; // aka NSOpenGLContext + typedef _PseudoNSGLContext *PseudoNSGLContextPtr; + + CGLContextObj GetCGLContextFromNSGL( PseudoNSGLContextPtr nsglCtx ); +#endif + +// Set TOGL_SUPPORT_NULL_DEVICE to 1 to support the NULL ref device +#define TOGL_SUPPORT_NULL_DEVICE 0 + +#if TOGL_SUPPORT_NULL_DEVICE + #define TOGL_NULL_DEVICE_CHECK if( m_params.m_deviceType == D3DDEVTYPE_NULLREF ) return S_OK; + #define TOGL_NULL_DEVICE_CHECK_RET_VOID if( m_params.m_deviceType == D3DDEVTYPE_NULLREF ) return; +#else + #define TOGL_NULL_DEVICE_CHECK + #define TOGL_NULL_DEVICE_CHECK_RET_VOID +#endif + +// GL_ENABLE_INDEX_VERIFICATION enables index range verification on all dynamic IB/VB's (obviously slow) +#define GL_ENABLE_INDEX_VERIFICATION 0 + +// GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION (currently win32 only) - If 1, VirtualAlloc/VirtualProtect is used to detect cases where the app locks a buffer, copies the ptr away, unlocks, then tries to later write to the buffer. +#define GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION 0 + +#define GL_BATCH_TELEMETRY_ZONES 0 + +// GL_BATCH_PERF_ANALYSIS - Enables gl_batch_vis, and various per-batch telemetry statistics messages. +#define GL_BATCH_PERF_ANALYSIS 0 +#define GL_BATCH_PERF_ANALYSIS_WRITE_PNGS 0 + +// GL_TELEMETRY_ZONES - Causes every single OpenGL call to generate a telemetry event +#define GL_TELEMETRY_ZONES 0 + +// GL_DUMP_ALL_API_CALLS - Causes a debug message to be printed for every API call if s_bDumpCalls bool is set to 1 +#define GL_DUMP_ALL_API_CALLS 0 + +// Must also enable PIX_ENABLE to use GL_TELEMETRY_GPU_ZONES. +#define GL_TELEMETRY_GPU_ZONES 0 + +// Records global # of OpenGL calls/total cycles spent inside GL +#define GL_TRACK_API_TIME GL_BATCH_PERF_ANALYSIS + +#define GL_USE_EXECUTE_HELPER_FOR_ALL_API_CALLS ( GL_TELEMETRY_ZONES || GL_TRACK_API_TIME || GL_DUMP_ALL_API_CALLS ) + +#if GL_BATCH_PERF_ANALYSIS + #define GL_BATCH_PERF(...) __VA_ARGS__ +#else + #define GL_BATCH_PERF(...) +#endif + +#define kGLMUserClipPlanes 2 +#define kGLMScratchFBOCount 4 + +#endif // DX_TO_GL_ABSTRACTION + +#endif // GLBASE_H diff --git a/public/togles/linuxwin/glentrypoints.h b/public/togles/linuxwin/glentrypoints.h new file mode 100644 index 00000000..0f6e8616 --- /dev/null +++ b/public/togles/linuxwin/glentrypoints.h @@ -0,0 +1,403 @@ +//========= 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. +// +// glentrypoints.h +// +//=============================================================================== + +#ifndef GLENTRYPOINTS_H +#define GLENTRYPOINTS_H + +#pragma once + +#ifdef DX_TO_GL_ABSTRACTION + +#include "tier0/platform.h" +#include "tier0/vprof_telemetry.h" +#include "interface.h" +#include "togl/rendermechanism.h" + +void *VoidFnPtrLookup_GlMgr(const char *fn, bool &okay, const bool bRequired, void *fallback=NULL); + +#if GL_USE_EXECUTE_HELPER_FOR_ALL_API_CALLS +class CGLExecuteHelperBase +{ +public: + inline void StartCall(const char *pName); + inline void StopCall(const char *pName); +#if GL_TRACK_API_TIME + TmU64 m_nStartTime; +#endif +}; + +template < class FunctionType, typename Result > +class CGLExecuteHelper : public CGLExecuteHelperBase +{ +public: + inline CGLExecuteHelper(FunctionType pFn, const char *pName ) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(a); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(a, b); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(a, b, c); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(a, b, c, d); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(a, b, c, d, e); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e, F f) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(a, b, c, d, e, f); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e, F f, G g) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(a, b, c, d, e, f, g); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e, F f, G g, H h) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(a, b, c, d, e, f, g, h); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e, F f, G g, H h, I i) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(a, b, c, d, e, f, g, h, i); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e, F f, G g, H h, I i, J j) : m_pFn( pFn ) { StartCall(pName); m_Result = (*m_pFn)(a, b, c, d, e, f, g, h, i, j); StopCall(pName); } + + inline operator Result() const { return m_Result; } + inline operator char*() const { return (char*)m_Result; } + + FunctionType m_pFn; + + Result m_Result; +}; + +template < class FunctionType> +class CGLExecuteHelper : public CGLExecuteHelperBase +{ +public: + inline CGLExecuteHelper(FunctionType pFn, const char *pName ) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(a); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(a, b); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(a, b, c); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(a, b, c, d); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(a, b, c, d, e); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e, F f) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(a, b, c, d, e, f); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e, F f, G g) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(a, b, c, d, e, f, g); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e, F f, G g, H h) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(a, b, c, d, e, f, g, h); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e, F f, G g, H h, I i) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(a, b, c, d, e, f, g, h, i); StopCall(pName); } + template inline CGLExecuteHelper(FunctionType pFn, const char *pName, A a, B b, C c, D d, E e, F f, G g, H h, I i, J j) : m_pFn( pFn ) { StartCall(pName); (*m_pFn)(a, b, c, d, e, f, g, h, i, j); StopCall(pName); } + + FunctionType m_pFn; +}; +#endif + +template < class FunctionType, typename Result > +class CDynamicFunctionOpenGLBase +{ +public: + // Construct with a NULL function pointer. You must manually call + // Lookup() before you can call a dynamic function through this interface. + CDynamicFunctionOpenGLBase() : m_pFn(NULL) {} + + // Construct and do a lookup right away. You will need to make sure that + // the lookup actually succeeded, as the gl library might have failed to load + // or (fn) might not exist in it. + CDynamicFunctionOpenGLBase(const char *fn, FunctionType fallback=NULL) : m_pFn(NULL) + { + Lookup(fn, fallback); + } + + // Construct and do a lookup right away. See comments in Lookup() about what (okay) does. + CDynamicFunctionOpenGLBase(const char *fn, bool &okay, FunctionType fallback=NULL) : m_pFn(NULL) + { + Lookup(fn, okay, fallback); + } + + // Load library if necessary, look up symbol. Returns true and sets + // m_pFn on successful lookup, returns false otherwise. If the + // function pointer is already looked up, this return true immediately. + // Use Reset() first if you want to look up the symbol again. + // This function will return false immediately unless (okay) is true. + // This allows you to chain lookups like this: + // bool okay = true; + // x.Lookup(lib, "x", okay); + // y.Lookup(lib, "y", okay); + // z.Lookup(lib, "z", okay); + // if (okay) { printf("All functions were loaded successfully!\n"); } + // If you supply a fallback, it'll be used if the lookup fails (and if + // non-NULL, means this will always return (okay)). + bool Lookup(const char *fn, bool &okay, FunctionType fallback=NULL) + { + if (!okay) + return false; + else if (this->m_pFn == NULL) + { + this->m_pFn = (FunctionType) VoidFnPtrLookup_GlMgr(fn, okay, false, (void *) fallback); + this->SetFuncName( fn ); + } + okay = m_pFn != NULL; + return okay; + } + + // Load library if necessary, look up symbol. Returns true and sets + // m_pFn on successful lookup, returns false otherwise. If the + // function pointer is already looked up, this return true immediately. + // Use Reset() first if you want to look up the symbol again. + // This function will return false immediately unless (okay) is true. + // If you supply a fallback, it'll be used if the lookup fails (and if + // non-NULL, means this will always return true). + bool Lookup(const char *fn, FunctionType fallback=NULL) + { + bool okay = true; + return Lookup(fn, okay, fallback); + } + + // Invalidates the current lookup. Makes the function pointer NULL. You + // will need to call Lookup() before you can call a dynamic function + // through this interface again. + void Reset() { m_pFn = NULL; } + + // Force this to be a specific function pointer. + void Force(FunctionType ptr) { m_pFn = ptr; } + + // Retrieve the actual function pointer. + FunctionType Pointer() const { return m_pFn; } + +#if GL_USE_EXECUTE_HELPER_FOR_ALL_API_CALLS + #if GL_TELEMETRY_ZONES || GL_DUMP_ALL_API_CALLS + #define GL_FUNC_NAME m_szName + #else + #define GL_FUNC_NAME "" + #endif + + inline CGLExecuteHelper operator() () const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME ); } + + template + inline CGLExecuteHelper operator() (T a) const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME, a); } + + template + inline CGLExecuteHelper operator() (T a, U b) const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME, a, b); } + + template + inline CGLExecuteHelper operator() (T a, U b, V c ) const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME, a, b, c); } + + template + inline CGLExecuteHelper operator() (T a, U b, V c, W d) const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME, a, b, c, d); } + + template + inline CGLExecuteHelper operator() (T a, U b, V c, W d, X e) const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME, a, b, c, d, e); } + + template + inline CGLExecuteHelper operator() (T a, U b, V c, W d, X e, Y f) const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME, a, b, c, d, e, f); } + + template + inline CGLExecuteHelper operator() (T a, U b, V c, W d, X e, Y f, Z g) const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME, a, b, c, d, e, f, g); } + + template + inline CGLExecuteHelper operator() (T a, U b, V c, W d, X e, Y f, Z g, A h) const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME, a, b, c, d, e, f, g, h); } + + template + inline CGLExecuteHelper operator() (T a, U b, V c, W d, X e, Y f, Z g, A h, B i) const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME, a, b, c, d, e, f, g, h, i); } + + template + inline CGLExecuteHelper operator() (T a, U b, V c, W d, X e, Y f, Z g, A h, B i, C j) const { return CGLExecuteHelper(m_pFn, GL_FUNC_NAME, a, b, c, d, e, f, g, h, i, j); } +#else + operator FunctionType() const { return m_pFn; } +#endif + + // Can be used to verify that we have an actual function looked up and + // ready to call: if (!MyDynFunc) { printf("Function not found!\n"); } + operator bool () const { return m_pFn != NULL; } + bool operator !() const { return m_pFn == NULL; } + +protected: + FunctionType m_pFn; + +#if GL_TELEMETRY_ZONES || GL_DUMP_ALL_API_CALLS + char m_szName[32]; + inline void SetFuncName(const char *pFn) { V_strncpy( m_szName, pFn, sizeof( m_szName ) ); } +#else + inline void SetFuncName(const char *pFn) { (void)pFn; } +#endif +}; + +// This works a lot like CDynamicFunctionMustInit, but we use SDL_GL_GetProcAddress(). +template < const bool bRequired, class FunctionType, typename Result > +class CDynamicFunctionOpenGL : public CDynamicFunctionOpenGLBase< FunctionType, Result > +{ +private: // forbid default constructor. + CDynamicFunctionOpenGL() {} + +public: + CDynamicFunctionOpenGL(const char *fn, FunctionType fallback=NULL) + { + bool okay = true; + Lookup(fn, okay, fallback); + this->SetFuncName( fn ); + } + + CDynamicFunctionOpenGL(const char *fn, bool &okay, FunctionType fallback=NULL) + { + Lookup(fn, okay, fallback); + this->SetFuncName( fn ); + } + + // Please note this is not virtual. + // !!! FIXME: we might want to fall back and try "EXT" or "ARB" versions in some case. + bool Lookup(const char *fn, bool &okay, FunctionType fallback=NULL) + { + if (this->m_pFn == NULL) + { + this->m_pFn = (FunctionType) VoidFnPtrLookup_GlMgr(fn, okay, bRequired, (void *) fallback); + this->SetFuncName( fn ); + } + return okay; + } +}; + +enum GLDriverStrings_t +{ + cGLVendorString, + cGLRendererString, + cGLVersionString, + cGLExtensionsString, + + cGLTotalDriverStrings +}; + +enum GLDriverProvider_t +{ + cGLDriverProviderUnknown, + cGLDriverProviderNVIDIA, + cGLDriverProviderAMD, + cGLDriverProviderIntel, + cGLDriverProviderIntelOpenSource, + cGLDriverProviderApple, + + cGLTotalDriverProviders +}; + +// This provides all the entry points for a given OpenGL context. +// ENTRY POINTS ARE ONLY VALID FOR THE CONTEXT THAT WAS CURRENT WHEN +// YOU LOOKED THEM UP. 99% of the time, this is not a problem, but +// that 1% is really hard to track down. Always access the GL +// through this class! +class COpenGLEntryPoints +{ + COpenGLEntryPoints( const COpenGLEntryPoints & ); + COpenGLEntryPoints &operator= ( const COpenGLEntryPoints & ); + +public: + // The GL context you are looking up entry points for must be current when you construct this object! + COpenGLEntryPoints(); + ~COpenGLEntryPoints(); + + void ClearEntryPoints(); + uint64 m_nTotalGLCycles, m_nTotalGLCalls; + + int m_nOpenGLVersionMajor; // if GL_VERSION is 2.1.0, this will be set to 2. + int m_nOpenGLVersionMinor; // if GL_VERSION is 2.1.0, this will be set to 1. + int m_nOpenGLVersionPatch; // if GL_VERSION is 2.1.0, this will be set to 0. + bool m_bHave_OpenGL; + + char *m_pGLDriverStrings[cGLTotalDriverStrings]; + GLDriverProvider_t m_nDriverProvider; + +#ifdef OSX +#define GL_EXT(x,glmajor,glminor) bool m_bHave_##x; +#define GL_FUNC(ext,req,ret,fn,arg,call) CDynamicFunctionOpenGL< req, ret (*) arg, ret > fn; +#define GL_FUNC_VOID(ext,req,fn,arg,call) CDynamicFunctionOpenGL< req, void (*) arg, void > fn; +#else +#define GL_EXT(x,glmajor,glminor) bool m_bHave_##x; +#define GL_FUNC(ext,req,ret,fn,arg,call) CDynamicFunctionOpenGL< req, ret (APIENTRY *) arg, ret > fn; +#define GL_FUNC_VOID(ext,req,fn,arg,call) CDynamicFunctionOpenGL< req, void (APIENTRY *) arg, void > fn; +#endif + #include "togl/glfuncs.inl" + #undef GL_FUNC_VOID + #undef GL_FUNC + #undef GL_EXT + + bool HasSwapTearExtension() const + { +#ifdef _WIN32 + return m_bHave_WGL_EXT_swap_control_tear; +#else + return m_bHave_GLX_EXT_swap_control_tear; +#endif + } +}; + +// This will be set to the current OpenGL context's entry points. +extern COpenGLEntryPoints *gGL; +typedef void * (*GL_GetProcAddressCallbackFunc_t)(const char *, bool &, const bool, void *); + +#ifdef TOGL_DLL_EXPORT + DLL_EXPORT COpenGLEntryPoints *ToGLConnectLibraries( CreateInterfaceFn factory ); + DLL_EXPORT void ToGLDisconnectLibraries(); + DLL_EXPORT COpenGLEntryPoints *GetOpenGLEntryPoints(GL_GetProcAddressCallbackFunc_t callback); + DLL_EXPORT void ClearOpenGLEntryPoints(); +#else + DLL_IMPORT COpenGLEntryPoints *ToGLConnectLibraries( CreateInterfaceFn factory ); + DLL_IMPORT void ToGLDisconnectLibraries(); + DLL_IMPORT COpenGLEntryPoints *GetOpenGLEntryPoints(GL_GetProcAddressCallbackFunc_t callback); + DLL_IMPORT void ClearOpenGLEntryPoints(); +#endif + +#if GL_USE_EXECUTE_HELPER_FOR_ALL_API_CALLS +inline void CGLExecuteHelperBase::StartCall(const char *pName) +{ + (void)pName; + +#if GL_TELEMETRY_ZONES + tmEnter( TELEMETRY_LEVEL3, TMZF_NONE, pName ); +#endif + +#if GL_TRACK_API_TIME + m_nStartTime = tmFastTime(); +#endif + +#if GL_DUMP_ALL_API_CALLS + static bool s_bDumpCalls; + if ( s_bDumpCalls ) + { + char buf[128]; + buf[0] = 'G'; + buf[1] = 'L'; + buf[2] = ':'; + size_t l = strlen( pName ); + memcpy( buf + 3, pName, l ); + buf[3 + l] = '\n'; + buf[4 + l] = '\0'; + Plat_DebugString( buf ); + } +#endif +} + +inline void CGLExecuteHelperBase::StopCall(const char *pName) +{ +#if GL_TRACK_API_TIME + uint64 nTotalCycles = tmFastTime() - m_nStartTime; +#endif + +#if GL_TELEMETRY_ZONES + tmLeave( TELEMETRY_LEVEL3 ); +#endif + +#if GL_TRACK_API_TIME + //double flMilliseconds = g_Telemetry.flRDTSCToMilliSeconds * nTotalCycles; + if (gGL) + { + gGL->m_nTotalGLCycles += nTotalCycles; + gGL->m_nTotalGLCalls++; + } +#endif +} +#endif + +#endif // DX_TO_GL_ABSTRACTION + +#endif // GLENTRYPOINTS_H diff --git a/public/togles/linuxwin/glfuncs.h b/public/togles/linuxwin/glfuncs.h new file mode 100644 index 00000000..f543b4af --- /dev/null +++ b/public/togles/linuxwin/glfuncs.h @@ -0,0 +1,277 @@ +//========= 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. +// !!! FIXME: Some of these aren't base OpenGL...pick out the extensions. +// !!! FIXME: Also, look up these -1, -1 versions numbers. +GL_FUNC(OpenGL,true,GLenum,glGetError,(void),()) +GL_FUNC_VOID(OpenGL,true,glActiveTexture,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glAlphaFunc,(GLenum a,GLclampf b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glAttachObjectARB,(GLhandleARB a,GLhandleARB b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glBegin,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glBindAttribLocationARB,(GLhandleARB a,GLuint b,const GLcharARB *c),(a,b,c)) +GL_FUNC_VOID(OpenGL,true,glBindBufferARB,(GLenum a,GLuint b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glBindProgramARB,(GLenum a,GLuint b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glBindTexture,(GLenum a,GLuint b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glBlendColor,(GLclampf a,GLclampf b,GLclampf c,GLclampf d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glBlendEquation,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glBlendFunc,(GLenum a,GLenum b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glBufferDataARB,(GLenum a,GLsizeiptrARB b,const GLvoid *c,GLenum d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glClear,(GLbitfield a),(a)) +GL_FUNC_VOID(OpenGL,true,glClearColor,(GLclampf a,GLclampf b,GLclampf c,GLclampf d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glClearDepth,(GLclampd a),(a)) +GL_FUNC_VOID(OpenGL,true,glClearStencil,(GLint a),(a)) +GL_FUNC_VOID(OpenGL,true,glClipPlane,(GLenum a,const GLdouble *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glColorMask,(GLboolean a,GLboolean b,GLboolean c,GLboolean d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glCompileShaderARB,(GLhandleARB a),(a)) +GL_FUNC_VOID(OpenGL,true,glCompressedTexImage2D,(GLenum a,GLint b,GLenum c,GLsizei d,GLsizei e,GLint f,GLsizei g,const GLvoid *h),(a,b,c,d,e,f,g,h)) +GL_FUNC_VOID(OpenGL,true,glCompressedTexImage3D,(GLenum a,GLint b,GLenum c,GLsizei d,GLsizei e,GLsizei f,GLint g,GLsizei h,const GLvoid *i),(a,b,c,d,e,f,g,h,i)) +GL_FUNC(OpenGL,true,GLhandleARB,glCreateProgramObjectARB,(void),()) +GL_FUNC(OpenGL,true,GLhandleARB,glCreateShaderObjectARB,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glDeleteBuffersARB,(GLsizei a,const GLuint *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glDeleteObjectARB,(GLhandleARB a),(a)) +GL_FUNC_VOID(OpenGL,true,glDeleteProgramsARB,(GLsizei a,const GLuint *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glDeleteQueriesARB,(GLsizei a,const GLuint *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glDeleteShader,(GLuint a),(a)) +GL_FUNC_VOID(OpenGL,true,glDeleteTextures,(GLsizei a,const GLuint *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glDepthFunc,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glDepthMask,(GLboolean a),(a)) +GL_FUNC_VOID(OpenGL,true,glDepthRange,(GLclampd a,GLclampd b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glDetachObjectARB,(GLhandleARB a,GLhandleARB b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glDisable,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glDisableVertexAttribArray,(GLuint a),(a)) +GL_FUNC_VOID(OpenGL,true,glDrawArrays,(GLenum a,GLint b,GLsizei c),(a,b,c)) +GL_FUNC_VOID(OpenGL,true,glDrawBuffer,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glDrawBuffers,(GLsizei a,const GLenum *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glDrawRangeElements,(GLenum a,GLuint b,GLuint c,GLsizei d,GLenum e,const GLvoid *f),(a,b,c,d,e,f)) +#ifndef OSX // 10.6/GL 2.1 compatability +GL_FUNC_VOID(OpenGL,true,glDrawRangeElementsBaseVertex,(GLenum a,GLuint b,GLuint c,GLsizei d,GLenum e,const GLvoid *f, GLenum g),(a,b,c,d,e,f,g)) +#endif +GL_FUNC_VOID(OpenGL,true,glEnable,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glEnableVertexAttribArray,(GLuint a),(a)) +GL_FUNC_VOID(OpenGL,true,glEnd,(void),()) +GL_FUNC_VOID(OpenGL,true,glFinish,(void),()) +GL_FUNC_VOID(OpenGL,true,glFlush,(void),()) +GL_FUNC_VOID(OpenGL,true,glFrontFace,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glGenBuffersARB,(GLsizei a,GLuint *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glGenProgramsARB,(GLsizei a,GLuint *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glGenQueriesARB,(GLsizei a,GLuint *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glGenTextures,(GLsizei a,GLuint *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glGetBooleanv,(GLenum a,GLboolean *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glGetCompressedTexImage,(GLenum a,GLint b,GLvoid *c),(a,b,c)) +GL_FUNC_VOID(OpenGL,true,glGetDoublev,(GLenum a,GLdouble *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glGetFloatv,(GLenum a,GLfloat *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glGetInfoLogARB,(GLhandleARB a,GLsizei b,GLsizei *c,GLcharARB *d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glGetIntegerv,(GLenum a,GLint *b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glGetObjectParameterivARB,(GLhandleARB a,GLenum b,GLint *c),(a,b,c)) +GL_FUNC_VOID(OpenGL,true,glGetProgramivARB,(GLenum a,GLenum b,GLint *c),(a,b,c)) +GL_FUNC(OpenGL,true,const GLubyte *,glGetString,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glGetTexImage,(GLenum a,GLint b,GLenum c,GLenum d,GLvoid *e),(a,b,c,d,e)) +GL_FUNC(OpenGL,true,GLint,glGetUniformLocationARB,(GLhandleARB a,const GLcharARB *b),(a,b)) +GL_FUNC(OpenGL,true,GLboolean,glIsEnabled,(GLenum a),(a)) +GL_FUNC(OpenGL,true,GLboolean,glIsTexture,(GLuint a),(a)) +GL_FUNC_VOID(OpenGL,true,glLinkProgramARB,(GLhandleARB a),(a)) +GL_FUNC(OpenGL,true,GLvoid*,glMapBufferARB,(GLenum a,GLenum b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glOrtho,(GLdouble a,GLdouble b,GLdouble c,GLdouble d,GLdouble e,GLdouble f),(a,b,c,d,e,f)) +GL_FUNC_VOID(OpenGL,true,glPixelStorei,(GLenum a,GLint b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glPolygonMode,(GLenum a,GLenum b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glPolygonOffset,(GLfloat a,GLfloat b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glPopAttrib,(void),()) +GL_FUNC_VOID(OpenGL,true,glProgramStringARB,(GLenum a,GLenum b,GLsizei c,const GLvoid *d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glPushAttrib,(GLbitfield a),(a)) +GL_FUNC_VOID(OpenGL,true,glReadBuffer,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glScissor,(GLint a,GLint b,GLsizei c,GLsizei d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glShaderSourceARB,(GLhandleARB a,GLsizei b,const GLcharARB **c,const GLint *d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glStencilFunc,(GLenum a,GLint b,GLuint c),(a,b,c)) +GL_FUNC_VOID(OpenGL,true,glStencilMask,(GLuint a),(a)) +GL_FUNC_VOID(OpenGL,true,glStencilOp,(GLenum a,GLenum b,GLenum c),(a,b,c)) +GL_FUNC_VOID(OpenGL,true,glTexCoord2f,(GLfloat a,GLfloat b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glTexImage2D,(GLenum a,GLint b,GLint c,GLsizei d,GLsizei e,GLint f,GLenum g,GLenum h,const GLvoid *i),(a,b,c,d,e,f,g,h,i)) +GL_FUNC_VOID(OpenGL,true,glTexImage3D,(GLenum a,GLint b,GLint c,GLsizei d,GLsizei e,GLsizei f,GLint g,GLenum h,GLenum i,const GLvoid *j),(a,b,c,d,e,f,g,h,i,j)) +GL_FUNC_VOID(OpenGL,true,glTexParameterfv,(GLenum a,GLenum b,const GLfloat *c),(a,b,c)) +GL_FUNC_VOID(OpenGL,true,glTexParameteri,(GLenum a,GLenum b,GLint c),(a,b,c)) +GL_FUNC_VOID(OpenGL,true,glTexSubImage2D,(GLenum a,GLint b,GLint c,GLint d,GLsizei e,GLsizei f,GLenum g,GLenum h,const GLvoid *i),(a,b,c,d,e,f,g,h,i)) +GL_FUNC_VOID(OpenGL,true,glUniform1f,(GLint a,GLfloat b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glUniform1i,(GLint a,GLint b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glUniform1iARB,(GLint a,GLint b),(a,b)) +GL_FUNC_VOID(OpenGL,true,glUniform4fv,(GLint a,GLsizei b,const GLfloat *c),(a,b,c)) +GL_FUNC(OpenGL,true,GLboolean,glUnmapBuffer,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glUseProgram,(GLuint a),(a)) +GL_FUNC_VOID(OpenGL,true,glVertex3f,(GLfloat a,GLfloat b,GLfloat c),(a,b,c)) +GL_FUNC_VOID(OpenGL,true,glVertexAttribPointer,(GLuint a,GLint b,GLenum c,GLboolean d,GLsizei e,const GLvoid *f),(a,b,c,d,e,f)) +GL_FUNC_VOID(OpenGL,true,glViewport,(GLint a,GLint b,GLsizei c,GLsizei d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glEnableClientState,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glDisableClientState,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glClientActiveTexture,(GLenum a),(a)) +GL_FUNC_VOID(OpenGL,true,glVertexPointer,(GLint a,GLenum b,GLsizei c,const GLvoid *d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glTexCoordPointer,(GLint a,GLenum b,GLsizei c,const GLvoid *d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glProgramEnvParameters4fvEXT,(GLenum a,GLuint b,GLsizei c,const GLfloat *d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glColor4sv,(const GLshort *a),(a)) +GL_FUNC_VOID(OpenGL,true,glStencilOpSeparate,(GLenum a,GLenum b,GLenum c,GLenum d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glStencilFuncSeparate,(GLenum a,GLenum b,GLint c,GLuint d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glGetTexLevelParameteriv,(GLenum a,GLint b,GLenum c,GLint *d),(a,b,c,d)) +GL_FUNC_VOID(OpenGL,true,glColor4f,(GLfloat a,GLfloat b,GLfloat c,GLfloat d),(a,b,c,d)) +GL_EXT(GL_EXT_framebuffer_object,-1,-1) +GL_FUNC_VOID(GL_EXT_framebuffer_object,false,glBindFramebufferEXT,(GLenum a,GLuint b),(a,b)) +GL_FUNC_VOID(GL_EXT_framebuffer_object,false,glBindRenderbufferEXT,(GLenum a,GLuint b),(a,b)) +GL_FUNC(GL_EXT_framebuffer_object,false,GLenum,glCheckFramebufferStatusEXT,(GLenum a),(a)) +GL_FUNC_VOID(GL_EXT_framebuffer_object,false,glDeleteRenderbuffersEXT,(GLsizei a,const GLuint *b),(a,b)) +GL_FUNC_VOID(GL_EXT_framebuffer_object,false,glFramebufferRenderbufferEXT,(GLenum a,GLenum b,GLenum c,GLuint d),(a,b,c,d)) +GL_FUNC_VOID(GL_EXT_framebuffer_object,false,glFramebufferTexture2DEXT,(GLenum a,GLenum b,GLenum c,GLuint d,GLint e),(a,b,c,d,e)) +GL_FUNC_VOID(GL_EXT_framebuffer_object,false,glFramebufferTexture3DEXT,(GLenum a,GLenum b,GLenum c,GLuint d,GLint e,GLint f),(a,b,c,d,e,f)) +GL_FUNC_VOID(GL_EXT_framebuffer_object,false,glGenFramebuffersEXT,(GLsizei a,GLuint *b),(a,b)) +GL_FUNC_VOID(GL_EXT_framebuffer_object,false,glGenRenderbuffersEXT,(GLsizei a,GLuint *b),(a,b)) +GL_FUNC_VOID(GL_EXT_framebuffer_object,false,glDeleteFramebuffersEXT,(GLsizei a,const GLuint *b),(a,b)) +GL_EXT(GL_EXT_framebuffer_blit,-1,-1) +GL_FUNC_VOID(GL_EXT_framebuffer_blit,false,glBlitFramebufferEXT,(GLint a,GLint b,GLint c,GLint d,GLint e,GLint f,GLint g,GLint h,GLbitfield i,GLenum j),(a,b,c,d,e,f,g,h,i,j)) +GL_EXT(GL_EXT_framebuffer_multisample,-1,-1) +GL_FUNC_VOID(GL_EXT_framebuffer_multisample,false,glRenderbufferStorageMultisampleEXT,(GLenum a,GLsizei b,GLenum c,GLsizei d,GLsizei e),(a,b,c,d,e)) +GL_EXT(GL_APPLE_fence,-1,-1) +GL_FUNC(GL_APPLE_fence,false,GLboolean,glTestFenceAPPLE,(GLuint a),(a)) +GL_FUNC_VOID(GL_APPLE_fence,false,glSetFenceAPPLE,(GLuint a),(a)) +GL_FUNC_VOID(GL_APPLE_fence,false,glFinishFenceAPPLE,(GLuint a),(a)) +GL_FUNC_VOID(GL_APPLE_fence,false,glDeleteFencesAPPLE,(GLsizei a,const GLuint *b),(a,b)) +GL_FUNC_VOID(GL_APPLE_fence,false,glGenFencesAPPLE,(GLsizei a,GLuint *b),(a,b)) +GL_EXT(GL_NV_fence,-1,-1) +GL_FUNC(GL_NV_fence,false,GLboolean,glTestFenceNV,(GLuint a),(a)) +GL_FUNC_VOID(GL_NV_fence,false,glSetFenceNV,(GLuint a,GLenum b),(a,b)) +GL_FUNC_VOID(GL_NV_fence,false,glFinishFenceNV,(GLuint a),(a)) +GL_FUNC_VOID(GL_NV_fence,false,glDeleteFencesNV,(GLsizei a,const GLuint *b),(a,b)) +GL_FUNC_VOID(GL_NV_fence,false,glGenFencesNV,(GLsizei a,GLuint *b),(a,b)) +GL_EXT(GL_ARB_sync,3,2) +#ifdef HAVE_GL_ARB_SYNC +GL_FUNC_VOID(GL_ARB_sync,false,glGetSynciv,(GLsync a, GLenum b, GLsizei c, GLsizei *d, GLint *e),(a,b,c,d,e)) +GL_FUNC(GL_ARB_sync,false,GLenum,glClientWaitSync,(GLsync a, GLbitfield b, GLuint64 c),(a,b,c)) +GL_FUNC_VOID(GL_ARB_sync,false,glWaitSync,(GLsync a, GLbitfield b, GLuint64 c),(a,b,c)) +GL_FUNC_VOID(GL_ARB_sync,false,glDeleteSync,(GLsync a),(a)) +GL_FUNC(GL_ARB_sync,false,GLsync,glFenceSync,(GLenum a, GLbitfield b),(a,b)) +#endif +GL_EXT(GL_EXT_draw_buffers2,-1,-1) +GL_FUNC_VOID(GL_EXT_draw_buffers2,true,glColorMaskIndexedEXT,(GLuint a,GLboolean b,GLboolean c,GLboolean d,GLboolean e),(a,b,c,d,e)) +GL_FUNC_VOID(GL_EXT_draw_buffers2,true,glEnableIndexedEXT,(GLenum a,GLuint b),(a,b)) +GL_FUNC_VOID(GL_EXT_draw_buffers2,true,glDisableIndexedEXT,(GLenum a,GLuint b),(a,b)) +GL_FUNC_VOID(GL_EXT_draw_buffers2,true,glGetBooleanIndexedvEXT,(GLenum a,GLuint b,GLboolean *c),(a,b,c)) +GL_EXT(GL_EXT_bindable_uniform,-1,-1) +GL_FUNC_VOID(GL_EXT_bindable_uniform,false,glUniformBufferEXT,(GLuint a,GLint b,GLuint c),(a,b,c)) +GL_FUNC(GL_EXT_bindable_uniform,false,int,glGetUniformBufferSizeEXT,(GLenum a, GLenum b),(a,b)) +GL_FUNC(GL_EXT_bindable_uniform,false,GLintptr,glGetUniformOffsetEXT,(GLenum a, GLenum b),(a,b)) +GL_EXT(GL_APPLE_flush_buffer_range,-1,-1) +GL_FUNC_VOID(GL_APPLE_flush_buffer_range,false,glBufferParameteriAPPLE,(GLenum a,GLenum b,GLint c),(a,b,c)) +GL_FUNC_VOID(GL_APPLE_flush_buffer_range,false,glFlushMappedBufferRangeAPPLE,(GLenum a,GLintptr b,GLsizeiptr c),(a,b,c)) +GL_EXT(GL_ARB_map_buffer_range,-1,-1) +GL_FUNC(GL_ARB_map_buffer_range,false,void*,glMapBufferRange,(GLenum a,GLintptr b,GLsizeiptr c,GLbitfield d),(a,b,c,d)) +GL_FUNC_VOID(GL_ARB_map_buffer_range,false,glFlushMappedBufferRange,(GLenum a,GLintptr b,GLsizeiptr c),(a,b,c)) +GL_EXT(GL_ARB_vertex_buffer_object,-1,-1) +GL_FUNC_VOID(GL_ARB_vertex_buffer_object,true,glBufferSubData,(GLenum a,GLintptr b,GLsizeiptr c,const GLvoid *d),(a,b,c,d)) +GL_EXT(GL_ARB_occlusion_query,-1,-1) +GL_FUNC_VOID(GL_ARB_occlusion_query,false,glBeginQueryARB,(GLenum a,GLuint b),(a,b)) +GL_FUNC_VOID(GL_ARB_occlusion_query,false,glEndQueryARB,(GLenum a),(a)) +GL_FUNC_VOID(GL_ARB_occlusion_query,false,glGetQueryObjectivARB,(GLuint a,GLenum b,GLint *c),(a,b,c)) +GL_FUNC_VOID(GL_ARB_occlusion_query,false,glGetQueryObjectuivARB,(GLuint a,GLenum b,GLuint *c),(a,b,c)) +GL_EXT(GL_APPLE_texture_range,-1,-1) +GL_FUNC_VOID(GL_APPLE_texture_range,false,glTextureRangeAPPLE,(GLenum a,GLsizei b,void *c),(a,b,c)) +GL_FUNC_VOID(GL_APPLE_texture_range,false,glGetTexParameterPointervAPPLE,(GLenum a,GLenum b,void* *c),(a,b,c)) +GL_EXT(GL_APPLE_client_storage,-1,-1) +GL_EXT(GL_ARB_uniform_buffer,-1,-1) +GL_EXT(GL_ARB_vertex_array_bgra,-1,-1) +GL_EXT(GL_EXT_vertex_array_bgra,-1,-1) +GL_EXT(GL_ARB_framebuffer_object,3,0) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glBindFramebuffer,(GLenum a,GLuint b),(a,b)) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glBindRenderbuffer,(GLenum a,GLuint b),(a,b)) +GL_FUNC(GL_ARB_framebuffer_object,false,GLenum,glCheckFramebufferStatus,(GLenum a),(a)) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glDeleteRenderbuffers,(GLsizei a,const GLuint *b),(a,b)) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glFramebufferRenderbuffer,(GLenum a,GLenum b,GLenum c,GLuint d),(a,b,c,d)) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glFramebufferTexture2D,(GLenum a,GLenum b,GLenum c,GLuint d,GLint e),(a,b,c,d,e)) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glFramebufferTexture3D,(GLenum a,GLenum b,GLenum c,GLuint d,GLint e,GLint f),(a,b,c,d,e,f)) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glGenFramebuffers,(GLsizei a,GLuint *b),(a,b)) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glGenRenderbuffers,(GLsizei a,GLuint *b),(a,b)) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glDeleteFramebuffers,(GLsizei a,const GLuint *b),(a,b)) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glBlitFramebuffer,(GLint a,GLint b,GLint c,GLint d,GLint e,GLint f,GLint g,GLint h,GLbitfield i,GLenum j),(a,b,c,d,e,f,g,h,i,j)) +GL_FUNC_VOID(GL_ARB_framebuffer_object,false,glRenderbufferStorageMultisample,(GLenum a,GLsizei b,GLenum c,GLsizei d,GLsizei e),(a,b,c,d,e)) +GL_EXT(GL_GREMEDY_string_marker,-1,-1) +GL_FUNC_VOID(GL_GREMEDY_string_marker,false,glStringMarkerGREMEDY,(GLsizei a,const void *b),(a,b)) +GL_EXT(GL_ARB_debug_output,-1,-1) +#ifdef OSX +GL_FUNC_VOID(GL_ARB_debug_output,false,glDebugMessageCallbackARB,(void ( *a)(GLenum, GLenum , GLuint , GLenum , GLsizei , const GLchar* , GLvoid*) ,void* b),(a,b)) +#else +GL_FUNC_VOID(GL_ARB_debug_output,false,glDebugMessageCallbackARB,(void (APIENTRY *a)(GLenum, GLenum , GLuint , GLenum , GLsizei , const GLchar* , GLvoid*) ,void* b),(a,b)) +#endif +GL_FUNC_VOID(GL_ARB_debug_output,false,glDebugMessageControlARB,(GLenum a, GLenum b, GLenum c, GLsizei d, const GLuint* e, GLboolean f),(a,b,c,d,e,f)) + +GL_EXT(GL_EXT_direct_state_access,-1,-1) +GL_FUNC_VOID(GL_EXT_direct_state_access,false,glBindMultiTextureEXT,(GLenum a,GLuint b, GLuint c),(a,b,c)) +GL_EXT(GL_NV_bindless_texture,-1,-1) + +#ifndef OSX +GL_FUNC_VOID(OpenGL, true, glGenSamplers, (GLuint a, GLuint *b), (a, b)) +GL_FUNC_VOID(OpenGL, true, glDeleteSamplers, (GLsizei a, const GLuint *b), (a, b)) +GL_FUNC_VOID(OpenGL, true, glBindSampler, (GLuint a, GLuint b), (a, b)) +GL_FUNC_VOID(OpenGL, true, glSamplerParameteri, (GLuint a, GLenum b, GLint c), (a, b, c)) +GL_FUNC_VOID(OpenGL, true, glSamplerParameterf, (GLuint a, GLenum b, GLfloat c), (a, b, c)) +GL_FUNC_VOID(OpenGL, true, glSamplerParameterfv, (GLuint a, GLenum b, const GLfloat *c), (a, b, c)) +GL_FUNC(GL_NV_bindless_texture, false, GLuint64, glGetTextureHandleNV, (GLuint texture), (texture)) +GL_FUNC(GL_NV_bindless_texture, false, GLuint64, glGetTextureSamplerHandleNV, (GLuint texture, GLuint sampler), (texture, sampler)) +GL_FUNC_VOID(GL_NV_bindless_texture, false, glMakeTextureHandleResidentNV, (GLuint64 handle), (handle)) +GL_FUNC_VOID(GL_NV_bindless_texture, false, glMakeTextureHandleNonResidentNV, (GLuint64 handle), (handle)) +GL_FUNC_VOID(GL_NV_bindless_texture, false, glUniformHandleui64NV, (GLint location, GLuint64 value), (location, value)) +GL_FUNC_VOID(GL_NV_bindless_texture, false, glUniformHandleui64vNV, (int location, GLsizei count, const GLuint64 *value), (location count, value)) +GL_FUNC_VOID(GL_NV_bindless_texture, false, glProgramUniformHandleui64NV, (GLuint program, GLint location, GLuint64 value), (program, location, value)) +GL_FUNC_VOID(GL_NV_bindless_texture, false, glProgramUniformHandleui64vNV, (GLuint program, GLint location, GLsizei count, const GLuint64 *values), (program, location, count, values)) +GL_FUNC(GL_NV_bindless_texture, false, GLboolean, glIsTextureHandleResidentNV, (GLuint64 handle), (handle)) +GL_FUNC_VOID(OpenGL,true,glGenQueries,(GLsizei n, GLuint *ids), (n, ids)) +GL_FUNC_VOID(OpenGL,true,glDeleteQueries,(GLsizei n, const GLuint *ids),(n, ids)) +GL_FUNC_VOID(OpenGL,true,glBeginQuery,(GLenum target, GLuint id), (target, id)) +GL_FUNC_VOID(OpenGL,true,glEndQuery,(GLenum target), (target)) +GL_FUNC_VOID(OpenGL,true,glQueryCounter,(GLuint id, GLenum target), (id, target)) +GL_FUNC_VOID(OpenGL,true,glGetQueryObjectiv,(GLuint id, GLenum pname, GLint *params), (id, pname, params)) +GL_FUNC_VOID(OpenGL,true,glGetQueryObjectui64v,(GLuint id, GLenum pname, GLuint64 *params), (id, pname, params)) +GL_FUNC_VOID(OpenGL,true,glCopyBufferSubData,(GLenum readtarget, GLenum writetarget, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size),(readtarget, writetarget, readoffset, writeoffset, size)) +#endif // !OSX + +GL_EXT(GL_AMD_pinned_memory,-1,-1) +GL_EXT(GL_EXT_framebuffer_multisample_blit_scaled,-1,-1) + +#ifndef OSX +GL_FUNC_VOID(OpenGL,true,glGenVertexArrays,(GLsizei n, GLuint *arrays),(n, arrays)) +GL_FUNC_VOID(OpenGL,true,glDeleteVertexArrays,(GLsizei n, GLuint *arrays),(n, arrays)) +GL_FUNC_VOID(OpenGL,true,glBindVertexArray,(GLuint a),(a)) +#endif // !OSX + +GL_EXT(GL_EXT_texture_sRGB_decode,-1,-1) +GL_FUNC_VOID(OpenGL,true,glPushClientAttrib,(GLbitfield a),(a)) +GL_FUNC_VOID(OpenGL,true,glPopClientAttrib,(void),()) +GL_EXT(GL_NVX_gpu_memory_info,-1,-1) +GL_EXT(GL_ATI_meminfo,-1,-1) +GL_EXT(GL_EXT_texture_compression_s3tc,-1,-1) +GL_EXT(GL_EXT_texture_compression_dxt1,-1,-1) +GL_EXT(GL_ANGLE_texture_compression_dxt3,-1,-1) +GL_EXT(GL_ANGLE_texture_compression_dxt5,-1,-1) + +GL_EXT( GL_ARB_buffer_storage, 4, 4 ) +GL_FUNC_VOID( GL_ARB_buffer_storage, false, glBufferStorage, (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags), (target, size, data, flags) ) + +// This one is an OS extension. We'll add a little helper function to look for it. +#ifdef _WIN32 + GL_EXT(WGL_EXT_swap_control_tear,-1,-1) +#else + GL_EXT(GLX_EXT_swap_control_tear,-1,-1) +#endif diff --git a/public/togles/linuxwin/glmdebug.h b/public/togles/linuxwin/glmdebug.h new file mode 100644 index 00000000..08cb6f91 --- /dev/null +++ b/public/togles/linuxwin/glmdebug.h @@ -0,0 +1,181 @@ +//========= 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. +#ifndef GLMDEBUG_H +#define GLMDEBUG_H + +#include "tier0/platform.h" +#if defined( OSX ) +#include +#endif + +// include this anywhere you need to be able to compile-out code related specifically to GLM debugging. + +// we expect DEBUG to be driven by the build system so you can include this header anywhere. +// when we come out, GLMDEBUG will be defined to a value - 0, 1, or 2 +// 0 means no GLM debugging is possible +// 1 means it's possible and resulted from being a debug build +// 2 means it's possible and resulted from being manually forced on for a release build + +#ifdef POSIX + #ifndef GLMDEBUG + #ifdef DEBUG + #define GLMDEBUG 1 // normally 1 here, testing + #else + // #define GLMDEBUG 2 // don't check this in enabled.. + #endif + + #ifndef GLMDEBUG + #define GLMDEBUG 0 + #endif + #endif +#else + #ifndef GLMDEBUG + #define GLMDEBUG 0 + #endif +#endif + + +//=============================================================================== +// debug channels + +enum EGLMDebugChannel +{ + ePrintf, + eDebugger, + eGLProfiler +}; + +#if GLMDEBUG + // make all these prototypes disappear in non GLMDEBUG + void GLMDebugInitialize( bool forceReinit=false ); + + bool GLMDetectOGLP( void ); + bool GLMDetectGDB( void ); + uint GLMDetectAvailableChannels( void ); + + uint GLMDebugChannelMask( uint *newValue = NULL ); + // note that GDB and OGLP can both come and go during run - forceCheck will allow that to be detected. + // mask returned is in form of 1< < + eComment, // 3 one off messages --- + eMatrixData, // 4 matrix data -M- + eShaderData, // 5 shader data (params) -S- + eFrameBufData, // 6 FBO data (attachments) -F- + eDXStuff, // 7 dxabstract spew -X- + eAllocations, // 8 tracking allocs and frees -A- + eSlowness, // 9 slow things happening (srgb flips..) -Z- + eDefaultFlavor, // not specified (no marker) + eFlavorCount +}; +uint GLMDebugFlavorMask( uint *newValue = NULL ); + +// make all these prototypes disappear in non GLMDEBUG +#if GLMDEBUG + // these are unconditional outputs, they don't interrogate the string + void GLMStringOut( const char *string ); + void GLMStringOutIndented( const char *string, int indentColumns ); + + #ifdef TOGL_DLL_EXPORT + // these will look at the string to guess its flavor: <, >, ---, -M-, -S- + DLL_EXPORT void GLMPrintfVA( const char *fmt, va_list vargs ); + DLL_EXPORT void GLMPrintf( const char *fmt, ... ); + #else + DLL_IMPORT void GLMPrintfVA( const char *fmt, va_list vargs ); + DLL_IMPORT void GLMPrintf( const char *fmt, ... ); + #endif + + // these take an explicit flavor with a default value + void GLMPrintStr( const char *str, EGLMDebugFlavor flavor = eDefaultFlavor ); + + #define GLMPRINTTEXT_NUMBEREDLINES 0x80000000 + void GLMPrintText( const char *str, EGLMDebugFlavor flavor = eDefaultFlavor, uint options=0 ); // indent each newline + + int GLMIncIndent( int indentDelta ); + int GLMGetIndent( void ); + void GLMSetIndent( int indent ); + +#endif + +// helpful macro if you are in a position to call GLM functions directly (i.e. you live in materialsystem / shaderapidx9) +#if GLMDEBUG + #define GLMPRINTF(args) GLMPrintf args + #define GLMPRINTSTR(args) GLMPrintStr args + #define GLMPRINTTEXT(args) GLMPrintText args +#else + #define GLMPRINTF(args) + #define GLMPRINTSTR(args) + #define GLMPRINTTEXT(args) +#endif + + +//=============================================================================== +// knob twiddling +#ifdef TOGL_DLL_EXPORT + DLL_EXPORT float GLMKnob( char *knobname, float *setvalue ); // Pass NULL to not-set the knob value + DLL_EXPORT float GLMKnobToggle( char *knobname ); +#else + DLL_IMPORT float GLMKnob( char *knobname, float *setvalue ); // Pass NULL to not-set the knob value + DLL_IMPORT float GLMKnobToggle( char *knobname ); +#endif + +//=============================================================================== +// other stuff + +#if GLMDEBUG +void GLMTriggerDebuggerBreak(); +inline void GLMDebugger( void ) +{ + if (GLMDebugChannelMask() & (1< +#include +#include +#include +#include +#endif + +#ifdef MAC_OS_X_VERSION_10_9 +typedef uint32_t CGDirectDisplayID; +typedef uint32_t CGOpenGLDisplayMask; +typedef double CGRefreshRate; +#endif + +typedef void _PseudoNSGLContext; // aka NSOpenGLContext +typedef _PseudoNSGLContext *PseudoNSGLContextPtr; + +struct GLMDisplayModeInfoFields +{ + uint m_modePixelWidth; + uint m_modePixelHeight; + uint m_modeRefreshHz; + // are we even going to talk about bit depth... not yet +}; + +struct GLMDisplayInfoFields +{ +#ifdef OSX + CGDirectDisplayID m_cgDisplayID; + CGOpenGLDisplayMask m_glDisplayMask; // result of CGDisplayIDToOpenGLDisplayMask on the cg_displayID. +#endif + uint m_displayPixelWidth; + uint m_displayPixelHeight; +}; + +struct GLMRendererInfoFields +{ + /*properties of interest and their desired values. + + kCGLRPFullScreen = 54, true + kCGLRPAccelerated = 73, true + kCGLRPWindow = 80, true + + kCGLRPRendererID = 70, informational + kCGLRPDisplayMask = 84, informational + kCGLRPBufferModes = 100, informational + kCGLRPColorModes = 103, informational + kCGLRPAccumModes = 104, informational + kCGLRPDepthModes = 105, informational + kCGLRPStencilModes = 106, informational + kCGLRPMaxAuxBuffers = 107, informational + kCGLRPMaxSampleBuffers = 108, informational + kCGLRPMaxSamples = 109, informational + kCGLRPSampleModes = 110, informational + kCGLRPSampleAlpha = 111, informational + kCGLRPVideoMemory = 120, informational + kCGLRPTextureMemory = 121, informational + kCGLRPRendererCount = 128 number of renderers in the CGLRendererInfoObj under examination + + kCGLRPOffScreen = 53, D/C + kCGLRPRobust = 75, FALSE or D/C - aka we're asking for no-fallback + kCGLRPBackingStore = 76, D/C + kCGLRPMPSafe = 78, D/C + kCGLRPMultiScreen = 81, D/C + kCGLRPCompliant = 83, D/C + */ + + + //--------------------------- info we have from CGL renderer queries, IOKit, Gestalt + //--------------------------- these are set up in the displayDB by CocoaMgr + GLint m_fullscreen; + GLint m_accelerated; + GLint m_windowed; + + GLint m_rendererID; + GLint m_displayMask; + GLint m_bufferModes; + GLint m_colorModes; + GLint m_accumModes; + GLint m_depthModes; + GLint m_stencilModes; + + GLint m_maxAuxBuffers; + GLint m_maxSampleBuffers; + GLint m_maxSamples; + GLint m_sampleModes; + GLint m_sampleAlpha; + + GLint m_vidMemory; + GLint m_texMemory; + + uint m_pciVendorID; + uint m_pciDeviceID; + char m_pciModelString[64]; + char m_driverInfoString[64]; + + //--------------------------- OS version related - set up by CocoaMgr + + // OS version found + uint m_osComboVersion; // 0x00XXYYZZ : XX major, YY minor, ZZ minor minor : 10.6.3 --> 0x000A0603. 10.5.8 --> 0x000A0508. + + //--------------------------- shorthands - also set up by CocoaMgr - driven by vendorid / deviceid + + bool m_ati; + bool m_atiR5xx; + bool m_atiR6xx; + bool m_atiR7xx; + bool m_atiR8xx; + bool m_atiNewer; + + bool m_intel; + bool m_intel95x; + bool m_intel3100; + bool m_intelHD4000; + + bool m_nv; + bool m_nvG7x; + bool m_nvG8x; + bool m_nvNewer; + + //--------------------------- context query results - left blank in the display DB - but valid in a GLMContext (call ctx->Caps() to get a const ref) + + // booleans + bool m_hasGammaWrites; // aka glGetBooleanv(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT) / glEnable(GL_FRAMEBUFFER_SRGB_EXT) + bool m_hasMixedAttachmentSizes; // aka ARB_fbo in 10.6.3 - test for min OS vers, then exported ext string + bool m_hasBGRA; // aka GL_BGRA vertex attribs in 10.6.3 - - test for min OS vers, then exported ext string + bool m_hasNewFullscreenMode; // aka 10.6.x "big window" fullscreen mode + bool m_hasNativeClipVertexMode; // aka GLSL gl_ClipVertex does not fall back to SW- OS version and folklore-based + bool m_hasOcclusionQuery; // occlusion query: do you speak it ?! + bool m_hasFramebufferBlit; // framebuffer blit: know what I'm sayin?! + bool m_hasPerfPackage1; // means new MTGL, fast OQ, fast uniform upload, NV can resolve flipped (late summer 2010 post 10.6.4 update) + + // counts + int m_maxAniso; // aniso limit - context query + + // other exts + bool m_hasBindableUniforms; + int m_maxVertexBindableUniforms; + int m_maxFragmentBindableUniforms; + int m_maxBindableUniformSize; + + bool m_hasUniformBuffers; + + // runtime options that aren't negotiable once set + bool m_hasDualShaders; // must supply CLI arg "-glmdualshaders" or we go GLSL only + + //--------------------------- " can'ts " - specific problems that need to be worked around + + bool m_cantBlitReliably; // Intel chipsets have problems blitting sRGB sometimes + bool m_cantAttachSRGB; // NV G8x on 10.5.8 can't have srgb tex on FBO color - separate issue from hasGammaWrites + bool m_cantResolveFlipped; // happens on NV in 10.6.4 and prior - console variable "gl_can_resolve_flipped" can overrule + bool m_cantResolveScaled; // happens everywhere per GL spec but may be relaxed some day - console variable "gl_can_resolve_scaled" can overrule + bool m_costlyGammaFlips; // this means that sRGB sampling state affects shader code gen, resulting in state-dependent code regen + + + //--------------------------- " bads " - known bad drivers + bool m_badDriver1064NV; // this is the bad NVIDIA driver on 10.6.4 - stutter, tex corruption, black screen issues + bool m_badDriver108Intel; // this is the bad Intel HD4000 driver on 10.8 - intermittent crash on GLSL compilation. +}; + + + +#endif diff --git a/public/togles/linuxwin/glmdisplaydb.h b/public/togles/linuxwin/glmdisplaydb.h new file mode 100644 index 00000000..2c21b9a5 --- /dev/null +++ b/public/togles/linuxwin/glmdisplaydb.h @@ -0,0 +1,157 @@ +//========= 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. +#ifndef GLMDISPLAYDB_H +#define GLMDISPLAYDB_H + +#include "tier1/utlvector.h" + +//=============================================================================== +// modes, displays, and renderers +//=============================================================================== + +// GLMDisplayModeInfoFields is in glmdisplay.h + +class GLMDisplayMode +{ +public: + GLMDisplayModeInfoFields m_info; + + GLMDisplayMode( uint width, uint height, uint refreshHz ); + GLMDisplayMode() { }; + ~GLMDisplayMode( void ); + + void Init( uint width, uint height, uint refreshHz ); + void Dump( int which ); +}; + +//=============================================================================== + +// GLMDisplayInfoFields is in glmdisplay.h + +class GLMDisplayInfo +{ +public: + GLMDisplayInfoFields m_info; + CUtlVector< GLMDisplayMode* > *m_modes; // starts out NULL, set by PopulateModes + GLMDisplayMode m_DesktopMode; + +#ifdef OSX + GLMDisplayInfo( CGDirectDisplayID displayID, CGOpenGLDisplayMask displayMask ); +#else + GLMDisplayInfo( void ); +#endif + + ~GLMDisplayInfo( void ); + + void PopulateModes( void ); + + void Dump( int which ); + +#ifdef OSX +private: + int m_display; +#endif +}; + +//=============================================================================== + +// GLMRendererInfoFields is in glmdisplay.h + +class GLMRendererInfo +{ +public: + GLMRendererInfoFields m_info; +#ifdef OSX + CUtlVector< GLMDisplayInfo* > *m_displays; // starts out NULL, set by PopulateDisplays +#else + GLMDisplayInfo *m_display; +#endif + +#ifdef OSX + GLMRendererInfo ( GLMRendererInfoFields *info ); +#else + GLMRendererInfo (); +#endif + ~GLMRendererInfo ( void ); + +#ifndef OSX + void Init( GLMRendererInfoFields *info ); +#endif + void PopulateDisplays(); + void Dump( int which ); +}; + +//=============================================================================== + +#ifdef OSX +// this is just a tuple describing fake adapters which are really renderer/display pairings. +// dxabstract bridges the gap between the d3d adapter-centric world and the GL renderer+display world. +// this makes it straightforward to handle cases like two video cards with two displays on one, and one on the other - +// you get three fake adapters which represent each useful screen. + +// the constraint that dxa will have to follow though, is that if the user wants to change their +// display selection for full screen, they would only be able to pick on that has the same underlying renderer. +// can't change fakeAdapter from one to another with different GL renderer under it. Screen hop but no card hop. + +struct GLMFakeAdapter +{ + int m_rendererIndex; + int m_displayIndex; +}; +#endif + +class GLMDisplayDB +{ +public: +#ifdef OSX + CUtlVector< GLMRendererInfo* > *m_renderers; // starts out NULL, set by PopulateRenderers + CUtlVector< GLMFakeAdapter > m_fakeAdapters; +#else + GLMRendererInfo m_renderer; +#endif + + GLMDisplayDB ( void ); + ~GLMDisplayDB ( void ); + + virtual void PopulateRenderers( void ); + virtual void PopulateFakeAdapters( uint realRendererIndex ); // fake adapters = one real adapter times however many displays are on it + virtual void Populate( void ); + + // The info-get functions return false on success. + virtual int GetFakeAdapterCount( void ); + virtual bool GetFakeAdapterInfo( int fakeAdapterIndex, int *rendererOut, int *displayOut, GLMRendererInfoFields *rendererInfoOut, GLMDisplayInfoFields *displayInfoOut ); + + virtual int GetRendererCount( void ); + virtual bool GetRendererInfo( int rendererIndex, GLMRendererInfoFields *infoOut ); + + virtual int GetDisplayCount( int rendererIndex ); + virtual bool GetDisplayInfo( int rendererIndex, int displayIndex, GLMDisplayInfoFields *infoOut ); + + virtual int GetModeCount( int rendererIndex, int displayIndex ); + virtual bool GetModeInfo( int rendererIndex, int displayIndex, int modeIndex, GLMDisplayModeInfoFields *infoOut ); + + virtual void Dump( void ); +}; + +#endif // GLMDISPLAYDB_H diff --git a/public/togles/linuxwin/glmgr.h b/public/togles/linuxwin/glmgr.h new file mode 100644 index 00000000..7e76a682 --- /dev/null +++ b/public/togles/linuxwin/glmgr.h @@ -0,0 +1,2435 @@ +//========= 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 void GLContextSet( T *src ); +template void GLContextGet( T *dst ); +template void GLContextGetDefault( T *dst ); + +// use these from the array state objects +template void GLContextSetIndexed( T *src, int index ); +template void GLContextGetIndexed( T *dst, int index ); +template 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 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 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((reinterpret_cast(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 m_AlphaTestEnable; + + GLState m_AlphaTestFunc; + + GLState m_CullFaceEnable; + GLState m_CullFrontFace; + GLState m_PolygonMode; + + GLState m_DepthBias; + + GLStateArray m_ClipPlaneEnable; + GLStateArray m_ClipPlaneEquation; // dxabstract puts them directly into param slot 253(0) and 254(1) + + GLState m_ScissorEnable; + GLState m_ScissorBox; + + GLState m_AlphaToCoverageEnable; + + GLState m_ViewportBox; + GLState m_ViewportDepthRange; + + GLState m_ColorMaskSingle; + GLStateArray m_ColorMaskMultiple; // need an official constant for the color buffers limit + + GLState m_BlendEnable; + GLState m_BlendFactor; + GLState m_BlendEquation; + GLState m_BlendColor; + GLState 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 m_DepthTestEnable; + GLState m_DepthFunc; + GLState m_DepthMask; + + GLState m_StencilTestEnable; // global stencil test enable + GLState m_StencilFunc; // holds front and back stencil funcs + GLStateArray m_StencilOp; // indexed: 0=front 1=back + GLState m_StencilWriteMask; + + GLState m_ClearColor; + GLState m_ClearDepth; + GLState 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 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 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 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 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 ¶ms = 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 diff --git a/public/togles/linuxwin/glmgrbasics.h b/public/togles/linuxwin/glmgrbasics.h new file mode 100644 index 00000000..a32938ad --- /dev/null +++ b/public/togles/linuxwin/glmgrbasics.h @@ -0,0 +1,332 @@ +//========= 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. +// +// glmgrbasics.h +// types, common headers, forward declarations, utilities +// +//=============================================================================== + +#ifndef GLMBASICS_H +#define GLMBASICS_H + +#pragma once + +#ifdef USE_SDL +#include "SDL_opengl.h" +#endif + +#ifdef OSX + #include + #include + #include + #include + +#ifndef MAC_OS_X_VERSION_10_9 + #include + #include +#endif +#endif + +#include "tier0/platform.h" + +#include "bitmap/imageformat.h" +#include "bitvec.h" +#include "tier1/checksum_md5.h" +#include "tier1/utlvector.h" +#include "tier1/convar.h" + +#include + +#include "dxabstract_types.h" + +struct GLMRect; +typedef void *PseudoGLContextPtr; + +// types + + // 3-d integer box (used for texture lock/unlock etc) +struct GLMRegion +{ + int xmin,xmax; + int ymin,ymax; + int zmin,zmax; +}; + +struct GLMRect // follows GL convention - if coming from the D3D rect you will need to fiddle the Y's +{ + int xmin; // left + int ymin; // bottom + int xmax; // right + int ymax; // top +}; + +// macros + +//#define GLMassert(x) assert(x) + +// forward decls +class GLMgr; // singleton +class GLMContext; // GL context +class CGLMContextTester; // testing class +class CGLMTex; +class CGLMFBO; +class CGLMProgram; +class CGLMBuffer; + + +// utilities + +typedef enum +{ + // D3D codes + eD3D_DEVTYPE, + eD3D_FORMAT, + eD3D_RTYPE, + eD3D_USAGE, + eD3D_RSTATE, // render state + eD3D_SIO, // D3D shader bytecode + eD3D_VTXDECLUSAGE, + + // CGL codes + eCGL_RENDID, + + // OpenGL error codes + eGL_ERROR, + + // OpenGL enums + eGL_ENUM, + eGL_RENDERER + +} GLMThing_t; + +// these will look at the string to guess its flavor: <, >, ---, -M-, -S- +#ifdef TOGL_DLL_EXPORT + DLL_EXPORT const char* GLMDecode( GLMThing_t type, unsigned long value ); // decode a numeric const +#else + DLL_IMPORT const char* GLMDecode( GLMThing_t type, unsigned long value ); // decode a numeric const +#endif + +const char* GLMDecodeMask( GLMThing_t type, unsigned long value ); // decode a bitmask + +FORCEINLINE void GLMStop( void ) { DXABSTRACT_BREAK_ON_ERROR(); } + +void GLMEnableTrace( bool on ); + +//=============================================================================== +// output functions + +// expose these in release now +// Mimic PIX events so we can decorate debug spew +DLL_EXPORT void GLMBeginPIXEvent( const char *str ); +DLL_EXPORT void GLMEndPIXEvent( void ); + +class CScopedGLMPIXEvent +{ + CScopedGLMPIXEvent( const CScopedGLMPIXEvent & ); + CScopedGLMPIXEvent& operator= ( const CScopedGLMPIXEvent & ); +public: + inline CScopedGLMPIXEvent( const char *pName ) { GLMBeginPIXEvent( pName ); } + inline ~CScopedGLMPIXEvent() { GLMEndPIXEvent( ); } +}; + +#if GLMDEBUG + + +//=============================================================================== +// classes + +// helper class making function tracking easier to wire up + +class GLMFuncLogger +{ + public: + + // simple function log + GLMFuncLogger( const char *funcName ) + { + m_funcName = funcName; + m_earlyOut = false; + + GLMPrintf( ">%s", m_funcName ); + }; + + // more advanced version lets you pass args (i.e. called parameters or anything else of interest) + // no macro for this one, since no easy way to pass through the args as well as the funcname + GLMFuncLogger( const char *funcName, char *fmt, ... ) + { + m_funcName = funcName; + m_earlyOut = false; + + // this acts like GLMPrintf here + // all the indent policy is down in GLMPrintfVA + // which means we need to inject a ">" at the front of the format string to make this work... sigh. + + char modifiedFmt[2000]; + modifiedFmt[0] = '>'; + strcpy( modifiedFmt+1, fmt ); + + va_list vargs; + va_start(vargs, fmt); + GLMPrintfVA( modifiedFmt, vargs ); + va_end( vargs ); + } + + ~GLMFuncLogger( ) + { + if (m_earlyOut) + { + GLMPrintf( "<%s (early out)", m_funcName ); + } + else + { + GLMPrintf( "<%s", m_funcName ); + } + }; + + void EarlyOut( void ) + { + m_earlyOut = true; + }; + + const char *m_funcName; // set at construction time + bool m_earlyOut; +}; + +// handy macro to go with the function tracking class +#define GLM_FUNC GLMFuncLogger _logger_ ( __FUNCTION__ ) +#else +#define GLM_FUNC tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ ) +#endif + + +// class to keep an in-memory mirror of a file which may be getting edited during run +class CGLMFileMirror +{ +public: + CGLMFileMirror( char *fullpath ); // just associates mirror with file. if file exists it will be read. + //if non existent it will be created with size zero + ~CGLMFileMirror( ); + + bool HasData( void ); // see if data avail + void GetData( char **dataPtr, uint *dataSizePtr ); // read it out + void SetData( char *data, uint dataSize ); // put data in (and write it to disk) + bool PollForChanges( void ); // check disk copy. If different, read it back in and return true. + + void UpdateStatInfo( void ); // make sure stat info is current for our file + void ReadFile( void ); + void WriteFile( void ); + + void OpenInEditor( bool foreground=false ); // pass TRUE if you would like the editor to pop to foreground + + /// how about a "wait for change" method.. + + char *m_path; // fullpath to file + bool m_exists; + struct stat m_stat; // stat results for the file (last time checked) + + char *m_data; // content of file + uint m_size; // length of content + +}; + +// class based on the file mirror, that makes it easy to edit them outside the app. + +// it receives an initial block of text from the engine, and hashes it. ("orig") +// it munges it by duplicating all the text after the "!!" line, and appending it in commented form. ("munged") +// a mirror file is activated, using a filename based on the hash from the orig text. +// if there is already content on disk matching that filename, use that content *unless* the 'blitz' parameter is set. +// (i.e. engine is instructing this subsystem to wipe out any old/modified variants of the text) + + +class CGLMEditableTextItem +{ +public: + CGLMEditableTextItem( char *text, uint size, bool forceOverwrite, char *prefix, char *suffix = NULL ); // create a text blob from text source, optional filename suffix + ~CGLMEditableTextItem( ); + + bool HasData( void ); + bool PollForChanges( void ); // return true if stale i.e. you need to get a new edition + void GetCurrentText( char **textOut, uint *sizeOut ); // query for read access to the active blob (could be the original, could be external edited copy) + void OpenInEditor( bool foreground=false ); // call user attention to this text + + // internal methods + void GenHashOfOrigText( void ); + void GenBaseNameAndFullPath( char *prefix, char *suffix ); + void GenMungedText( bool fromMirror ); + + // members + // orig + uint m_origSize; + char *m_origText; // what was submitted + unsigned char m_origDigest[MD5_DIGEST_LENGTH]; // digest of what was submitted + + // munged + uint m_mungedSize; + char *m_mungedText; // re-processed edition, initial content submission to the file mirror + + // mirror + char *m_mirrorBaseName; // generated from the hash of the orig text, plus the label / prefix + char *m_mirrorFullPath; // base name + CGLMFileMirror *m_mirror; // file mirror itself. holds "official" copy for GetCurrentText to return. +}; + + +// debug font +extern unsigned char g_glmDebugFontMap[16384]; + +// class for cracking multi-part text blobs +// sections are demarcated by beginning-of-line markers submitted in a table by the caller + +struct GLMTextSection +{ + int m_markerIndex; // based on table of markers passed in to constructor + uint m_textOffset; // where is the text - offset + int m_textLength; // how big is the section +}; + +class CGLMTextSectioner +{ +public: + CGLMTextSectioner( char *text, int textSize, const char **markers ); // constructor finds all the sections + ~CGLMTextSectioner( ); + + int Count( void ); // how many sections found + void GetSection( int index, uint *offsetOut, uint *lengthOut, int *markerIndexOut ); + // find section, size, what marker + // note that more than one section can be marked similarly. + // so policy isn't made here, you walk the sections and decide what to do if there are dupes. + + //members + + //section table + CUtlVector< GLMTextSection > m_sectionTable; +}; + +#ifndef OSX +void GLMGPUTimestampManagerInit(); +void GLMGPUTimestampManagerDeinit(); +void GLMGPUTimestampManagerTick(); +#endif + +#endif // GLMBASICS_H diff --git a/public/togles/linuxwin/glmgrext.h b/public/togles/linuxwin/glmgrext.h new file mode 100644 index 00000000..393942a7 --- /dev/null +++ b/public/togles/linuxwin/glmgrext.h @@ -0,0 +1,123 @@ +//========= 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. +// +// glmgrext.h +// helper file for extension testing and runtime importing of entry points +// +//=============================================================================== + +#pragma once + +#ifdef OSX +#include +#include +#elif defined(DX_TO_GL_ABSTRACTION) +#include +#include +#else +#error +#endif + +#ifndef GL_EXT_framebuffer_sRGB + #define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 + #define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#endif + +#ifndef ARB_texture_rg + #define GL_COMPRESSED_RED 0x8225 + #define GL_COMPRESSED_RG 0x8226 + #define GL_RG 0x8227 + #define GL_RG_INTEGER 0x8228 + #define GL_R8 0x8229 + #define GL_R16 0x822A + #define GL_RG8 0x822B + #define GL_RG16 0x822C + #define GL_R16F 0x822D + #define GL_R32F 0x822E + #define GL_RG16F 0x822F + #define GL_RG32F 0x8230 + #define GL_R8I 0x8231 + #define GL_R8UI 0x8232 + #define GL_R16I 0x8233 + #define GL_R16UI 0x8234 + #define GL_R32I 0x8235 + #define GL_R32UI 0x8236 + #define GL_RG8I 0x8237 + #define GL_RG8UI 0x8238 + #define GL_RG16I 0x8239 + #define GL_RG16UI 0x823A + #define GL_RG32I 0x823B + #define GL_RG32UI 0x823C +#endif + +#ifndef GL_EXT_bindable_uniform + #define GL_UNIFORM_BUFFER_EXT 0x8DEE +#endif + +// unpublished extension enums (thus the "X") + +// from EXT_framebuffer_multisample_blit_scaled.. +#define XGL_SCALED_RESOLVE_FASTEST_EXT 0x90BA +#define XGL_SCALED_RESOLVE_NICEST_EXT 0x90BB + +#ifndef GL_TEXTURE_MINIMIZE_STORAGE_APPLE +#define GL_TEXTURE_MINIMIZE_STORAGE_APPLE 0x85B6 +#endif + +#ifndef GL_ALL_COMPLETED_NV +#define GL_ALL_COMPLETED_NV 0x84F2 +#endif + +#ifndef GL_MAP_READ_BIT +#define GL_MAP_READ_BIT 0x0001 +#endif + +#ifndef GL_MAP_WRITE_BIT +#define GL_MAP_WRITE_BIT 0x0002 +#endif + +#ifndef GL_MAP_INVALIDATE_RANGE_BIT +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#endif + +#ifndef GL_MAP_INVALIDATE_BUFFER_BIT +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#endif + +#ifndef GL_MAP_FLUSH_EXPLICIT_BIT +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#endif + +#ifndef GL_MAP_UNSYNCHRONIZED_BIT +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#endif + +#ifndef GL_MAP_PERSISTENT_BIT +#define GL_MAP_PERSISTENT_BIT 0x0040 +#endif + +#ifndef GL_MAP_COHERENT_BIT +#define GL_MAP_COHERENT_BIT 0x0080 +#endif + diff --git a/public/togles/rendermechanism.h b/public/togles/rendermechanism.h new file mode 100644 index 00000000..47be332d --- /dev/null +++ b/public/togles/rendermechanism.h @@ -0,0 +1,73 @@ +//========= 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. +// +#ifndef RENDERMECHANISM_H +#define RENDERMECHANISM_H + +#if defined(DX_TO_GL_ABSTRACTION) + +#undef PROTECTED_THINGS_ENABLE + +#include +#include + +#include "tier0/basetypes.h" +#include "tier0/platform.h" + +#include "togl/linuxwin/glmdebug.h" +#include "togl/linuxwin/glbase.h" +#include "togl/linuxwin/glentrypoints.h" +#include "togl/linuxwin/glmdisplay.h" +#include "togl/linuxwin/glmdisplaydb.h" +#include "togl/linuxwin/glmgrbasics.h" +#include "togl/linuxwin/glmgrext.h" +#include "togl/linuxwin/cglmbuffer.h" +#include "togl/linuxwin/cglmtex.h" +#include "togl/linuxwin/cglmfbo.h" +#include "togl/linuxwin/cglmprogram.h" +#include "togl/linuxwin/cglmquery.h" +#include "togl/linuxwin/glmgr.h" +#include "togl/linuxwin/dxabstract_types.h" +#include "togl/linuxwin/dxabstract.h" + +#else + //USE_ACTUAL_DX + #ifdef WIN32 + #ifdef _X360 + #include "d3d9.h" + #include "d3dx9.h" + #else + #include + #include "../../dx9sdk/include/d3d9.h" + #include "../../dx9sdk/include/d3dx9.h" + #endif + typedef HWND VD3DHWND; + #endif + + #define GLMPRINTF(args) + #define GLMPRINTSTR(args) + #define GLMPRINTTEXT(args) +#endif // defined(DX_TO_GL_ABSTRACTION) + +#endif // RENDERMECHANISM_H diff --git a/togles/linuxwin/asanstubs.cpp b/togles/linuxwin/asanstubs.cpp new file mode 100644 index 00000000..ee4ce50b --- /dev/null +++ b/togles/linuxwin/asanstubs.cpp @@ -0,0 +1,17 @@ +typedef unsigned int uint; + +#include "../public/togl/linuxwin/glmdisplay.h" +#include "../public/togl/linuxwin/glmdisplaydb.h" + +void GLMDisplayDB::PopulateRenderers( void ) { } +void GLMDisplayDB::PopulateFakeAdapters( uint realRendererIndex ) { } // fake adapters = one real adapter times however many displays are on +void GLMDisplayDB::Populate( void ) { } +int GLMDisplayDB::GetFakeAdapterCount( void ) { return 1; } +bool GLMDisplayDB::GetFakeAdapterInfo( int fakeAdapterIndex, int *rendererOut, int *displayOut, GLMRendererInfoFields *rendererInfoOut, GLMDisplayInfoFields *displayInfoOut ) { return true; } +int GLMDisplayDB::GetRendererCount( void ) { return 1; } +bool GLMDisplayDB::GetRendererInfo( int rendererIndex, GLMRendererInfoFields *infoOut ) { return true; } +int GLMDisplayDB::GetDisplayCount( int rendererIndex ) { return 1; } +bool GLMDisplayDB::GetDisplayInfo( int rendererIndex, int displayIndex, GLMDisplayInfoFields *infoOut ) { return true; } +int GLMDisplayDB::GetModeCount( int rendererIndex, int displayIndex ) { } +bool GLMDisplayDB::GetModeInfo( int rendererIndex, int displayIndex, int modeIndex, GLMDisplayModeInfoFields *infoOut ) { return false; } +void GLMDisplayDB::Dump( void ) { } diff --git a/togles/linuxwin/cglmbuffer.cpp b/togles/linuxwin/cglmbuffer.cpp new file mode 100644 index 00000000..ecba047b --- /dev/null +++ b/togles/linuxwin/cglmbuffer.cpp @@ -0,0 +1,1215 @@ +//========= 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. +// +// cglmbuffer.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +// 7LS TODO : took out cmdline here +bool g_bUsePseudoBufs = false; //( Plat_GetCommandLineA() ) ? ( strstr( Plat_GetCommandLineA(), "-gl_enable_pseudobufs" ) != NULL ) : false; +#ifdef OSX +// Significant perf degradation on some OSX parts if static buffers not disabled +bool g_bDisableStaticBuffer = true; +#else +bool g_bDisableStaticBuffer = false; //( Plat_GetCommandLineA() ) ? ( strstr( Plat_GetCommandLineA(), "-gl_disable_static_buffer" ) != NULL ) : false; +#endif + +// http://www.opengl.org/registry/specs/ARB/vertex_buffer_object.txt +// http://www.opengl.org/registry/specs/ARB/pixel_buffer_object.txt + +// gl_bufmode: zero means we mark all vertex/index buffers static + +// non zero means buffers are initially marked static.. +// ->but can shift to dynamic upon first 'discard' (orphaning) + +// #define REPORT_LOCK_TIME 0 + +ConVar gl_bufmode( "gl_bufmode", "1" ); + +char ALIGN16 CGLMBuffer::m_StaticBuffers[ GL_MAX_STATIC_BUFFERS ][ GL_STATIC_BUFFER_SIZE ] ALIGN16_POST; +bool CGLMBuffer::m_bStaticBufferUsed[ GL_MAX_STATIC_BUFFERS ]; + +extern bool g_bNullD3DDevice; + +//===========================================================================// + +static uint gMaxPersistentOffset[kGLMNumBufferTypes] = +{ + 0, + 0, + 0, + 0 +}; +CON_COMMAND( gl_persistent_buffer_max_offset, "" ) +{ + ConMsg( "OpenGL Persistent buffer max offset :\n" ); + ConMsg( " Vertex buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMVertexBuffer], gMaxPersistentOffset[kGLMVertexBuffer] / (1024.0f*1024.0f) ); + ConMsg( " Index buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMIndexBuffer], gMaxPersistentOffset[kGLMIndexBuffer] / (1024.0f*1024.0f) ); + ConMsg( " Uniform buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMUniformBuffer], gMaxPersistentOffset[kGLMUniformBuffer] / (1024.0f*1024.0f) ); + ConMsg( " Pixel buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMPixelBuffer], gMaxPersistentOffset[kGLMPixelBuffer] / (1024.0f*1024.0f) ); +} + +CPersistentBuffer::CPersistentBuffer() +: + m_nSize( 0 ) + , m_nHandle( 0 ) + , m_pImmutablePersistentBuf( NULL ) + , m_nOffset( 0 ) +#ifdef HAVE_GL_ARB_SYNC + , m_nSyncObj( 0 ) +#endif +{} + +CPersistentBuffer::~CPersistentBuffer() +{ + Deinit(); +} + +void CPersistentBuffer::Init( EGLMBufferType type,uint nSize ) +{ + Assert( gGL->m_bHave_GL_ARB_buffer_storage ); + Assert( gGL->m_bHave_GL_ARB_map_buffer_range ); + + m_nSize = nSize; + m_nOffset = 0; + m_type = type; + + switch ( type ) + { + case kGLMVertexBuffer: m_buffGLTarget = GL_ARRAY_BUFFER_ARB; break; + case kGLMIndexBuffer: m_buffGLTarget = GL_ELEMENT_ARRAY_BUFFER_ARB; break; + + default: Assert( nSize == 0 ); + } + + if ( m_nSize > 0 ) + { + gGL->glGenBuffersARB( 1, &m_nHandle ); + gGL->glBindBufferARB( m_buffGLTarget, m_nHandle ); + + // Create persistent immutable buffer that we will permanently map. This buffer can be written from any thread (not just + // the renderthread) + gGL->glBufferStorage( m_buffGLTarget, m_nSize, (const GLvoid *)NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT ); // V_GL_REQ: GL_ARB_buffer_storage, GL_ARB_map_buffer_range, GL_VERSION_4_4 + + // Map the buffer for all of eternity. Pointer can be used from multiple threads. + m_pImmutablePersistentBuf = gGL->glMapBufferRange( m_buffGLTarget, 0, m_nSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT ); // V_GL_REQ: GL_ARB_map_buffer_range, GL_ARB_buffer_storage, GL_VERSION_4_4 + Assert( m_pImmutablePersistentBuf != NULL ); + } +} + +void CPersistentBuffer::Deinit() +{ + if ( !m_pImmutablePersistentBuf ) + { + return; + } + + BlockUntilNotBusy(); + + gGL->glBindBufferARB( m_buffGLTarget, m_nHandle ); + gGL->glUnmapBuffer( m_buffGLTarget ); + gGL->glBindBufferARB( m_buffGLTarget, 0 ); + + gGL->glDeleteBuffersARB( 1, &m_nHandle ); + + m_nSize = 0; + m_nHandle = 0; + m_nOffset = 0; + m_pImmutablePersistentBuf = NULL; +} + +void CPersistentBuffer::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 CPersistentBuffer::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_nOffset = 0; +} + +void CPersistentBuffer::Append( uint nSize ) +{ + m_nOffset += nSize; + Assert( m_nOffset <= m_nSize ); + + gMaxPersistentOffset[m_type] = Max( m_nOffset, gMaxPersistentOffset[m_type] ); +} + +//===========================================================================// + +#if GL_ENABLE_INDEX_VERIFICATION + +CGLMBufferSpanManager::CGLMBufferSpanManager() : + m_pCtx( NULL ), + m_nBufType( kGLMVertexBuffer ), + m_nBufSize( 0 ), + m_bDynamic( false ), + m_nSpanEndMax( -1 ), + m_nNumAllocatedBufs( 0 ), + m_nTotalBytesAllocated( 0 ) +{ +} + +CGLMBufferSpanManager::~CGLMBufferSpanManager() +{ + Deinit(); +} + +void CGLMBufferSpanManager::Init( GLMContext *pContext, EGLMBufferType nBufType, uint nInitialCapacity, uint nBufSize, bool bDynamic ) +{ + Assert( ( nBufType == kGLMIndexBuffer ) || ( nBufType == kGLMVertexBuffer ) ); + + m_pCtx = pContext; + m_nBufType = nBufType; + + m_nBufSize = nBufSize; + m_bDynamic = bDynamic; + + m_ActiveSpans.EnsureCapacity( nInitialCapacity ); + m_DeletedSpans.EnsureCapacity( nInitialCapacity ); + m_nSpanEndMax = -1; + + m_nNumAllocatedBufs = 0; + m_nTotalBytesAllocated = 0; +} + +bool CGLMBufferSpanManager::AllocDynamicBuf( uint nSize, GLDynamicBuf_t &buf ) +{ + buf.m_nGLType = GetGLBufType(); + buf.m_nActualBufSize = nSize; + buf.m_nHandle = 0; + buf.m_nSize = nSize; + + m_nNumAllocatedBufs++; + m_nTotalBytesAllocated += buf.m_nActualBufSize; + + return true; +} + +void CGLMBufferSpanManager::ReleaseDynamicBuf( GLDynamicBuf_t &buf ) +{ + Assert( m_nNumAllocatedBufs > 0 ); + m_nNumAllocatedBufs--; + + Assert( m_nTotalBytesAllocated >= (int)buf.m_nActualBufSize ); + m_nTotalBytesAllocated -= buf.m_nActualBufSize; +} + +void CGLMBufferSpanManager::Deinit() +{ + if ( !m_pCtx ) + return; + + for ( int i = 0; i < m_ActiveSpans.Count(); i++ ) + { + if ( m_ActiveSpans[i].m_bOriginalAlloc ) + ReleaseDynamicBuf( m_ActiveSpans[i].m_buf ); + } + m_ActiveSpans.SetCountNonDestructively( 0 ); + + for ( int i = 0; i < m_DeletedSpans.Count(); i++ ) + ReleaseDynamicBuf( m_DeletedSpans[i].m_buf ); + + m_DeletedSpans.SetCountNonDestructively( 0 ); + + m_pCtx->BindGLBufferToCtx( GetGLBufType(), NULL, true ); + + m_nSpanEndMax = -1; + m_pCtx = NULL; + + Assert( !m_nNumAllocatedBufs ); + Assert( !m_nTotalBytesAllocated ); +} + +void CGLMBufferSpanManager::DiscardAllSpans() +{ + for ( int i = 0; i < m_ActiveSpans.Count(); i++ ) + { + if ( m_ActiveSpans[i].m_bOriginalAlloc ) + ReleaseDynamicBuf( m_ActiveSpans[i].m_buf ); + } + m_ActiveSpans.SetCountNonDestructively( 0 ); + + for ( int i = 0; i < m_DeletedSpans.Count(); i++ ) + ReleaseDynamicBuf( m_DeletedSpans[i].m_buf ); + + m_DeletedSpans.SetCountNonDestructively( 0 ); + + m_nSpanEndMax = -1; + + Assert( !m_nNumAllocatedBufs ); + Assert( !m_nTotalBytesAllocated ); +} + +// TODO: Add logic to detect incorrect usage of bNoOverwrite. +CGLMBufferSpanManager::ActiveSpan_t *CGLMBufferSpanManager::AddSpan( uint nOffset, uint nMaxSize, uint nActualSize, bool bDiscard, bool bNoOverwrite ) +{ + (void)bDiscard; + (void)bNoOverwrite; + + const uint nStart = nOffset; + const uint nSize = nActualSize; + const uint nEnd = nStart + nSize; + + GLDynamicBuf_t newDynamicBuf; + if ( !AllocDynamicBuf( nSize, newDynamicBuf ) ) + { + DXABSTRACT_BREAK_ON_ERROR(); + return NULL; + } + + if ( (int)nStart < m_nSpanEndMax ) + { + // Lock region potentially overlaps another previously locked region (since the last discard) - this is a very rarely (if ever) taken path in Source1 games. + int i = 0; + while ( i < m_ActiveSpans.Count() ) + { + ActiveSpan_t &existingSpan = m_ActiveSpans[i]; + if ( ( nEnd <= existingSpan.m_nStart ) || ( nStart >= existingSpan.m_nEnd ) ) + { + i++; + continue; + } + + Warning( "GL performance warning: AddSpan() at offset %u max size %u actual size %u, on a %s %s buffer of total size %u, overwrites an existing active lock span at offset %u size %u!\n", + nOffset, nMaxSize, nActualSize, + m_bDynamic ? "dynamic" : "static", ( m_nBufType == kGLMVertexBuffer ) ? "vertex" : "index", m_nBufSize, + existingSpan.m_nStart, existingSpan.m_nEnd - existingSpan.m_nStart ); + + if ( ( nStart <= existingSpan.m_nStart ) && ( nEnd >= existingSpan.m_nEnd ) ) + { + if ( existingSpan.m_bOriginalAlloc ) + { + // New span totally covers existing span + // Can't immediately delete the span's buffer because it could be referred to by another (child) span. + m_DeletedSpans.AddToTail( existingSpan ); + } + + // Delete span + m_ActiveSpans[i] = m_ActiveSpans[ m_ActiveSpans.Count() - 1 ]; + m_ActiveSpans.SetCountNonDestructively( m_ActiveSpans.Count() - 1 ); + continue; + } + + // New span does NOT fully cover the existing span (partial overlap) + if ( nStart < existingSpan.m_nStart ) + { + // New span starts before existing span, but ends somewhere inside, so shrink it (start moves "right") + existingSpan.m_nStart = nEnd; + } + else if ( nEnd > existingSpan.m_nEnd ) + { + // New span ends after existing span, but starts somewhere inside (end moves "left") + existingSpan.m_nEnd = nStart; + } + else //if ( ( nStart >= existingSpan.m_nStart ) && ( nEnd <= existingSpan.m_nEnd ) ) + { + // New span lies inside of existing span + if ( nStart == existingSpan.m_nStart ) + { + // New span begins inside the existing span (start moves "right") + existingSpan.m_nStart = nEnd; + } + else + { + if ( nEnd < existingSpan.m_nEnd ) + { + // New span is completely inside existing span + m_ActiveSpans.AddToTail( ActiveSpan_t( nEnd, existingSpan.m_nEnd, existingSpan.m_buf, false ) ); + } + + existingSpan.m_nEnd = nStart; + } + } + + Assert( existingSpan.m_nStart < existingSpan.m_nEnd ); + i++; + } + } + + newDynamicBuf.m_nLockOffset = nStart; + newDynamicBuf.m_nLockSize = nSize; + + m_ActiveSpans.AddToTail( ActiveSpan_t( nStart, nEnd, newDynamicBuf, true ) ); + m_nSpanEndMax = MAX( m_nSpanEndMax, (int)nEnd ); + + return &m_ActiveSpans.Tail(); +} + +bool CGLMBufferSpanManager::IsValid( uint nOffset, uint nSize ) const +{ + const uint nEnd = nOffset + nSize; + + int nTotalBytesRemaining = nSize; + + for ( int i = m_ActiveSpans.Count() - 1; i >= 0; --i ) + { + const ActiveSpan_t &span = m_ActiveSpans[i]; + + if ( span.m_nEnd <= nOffset ) + continue; + if ( span.m_nStart >= nEnd ) + continue; + + uint nIntersectStart = MAX( span.m_nStart, nOffset ); + uint nIntersectEnd = MIN( span.m_nEnd, nEnd ); + Assert( nIntersectStart <= nIntersectEnd ); + + nTotalBytesRemaining -= ( nIntersectEnd - nIntersectStart ); + Assert( nTotalBytesRemaining >= 0 ); + if ( nTotalBytesRemaining <= 0 ) + break; + } + + return nTotalBytesRemaining == 0; +} +#endif // GL_ENABLE_INDEX_VERIFICATION + +// glBufferSubData() with a max size limit, to work around NVidia's threaded driver limits (anything > than roughly 256KB triggers a sync with the server thread). +void glBufferSubDataMaxSize( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data, uint nMaxSizePerCall ) +{ +#if TOGL_SUPPORT_NULL_DEVICE + if ( g_bNullD3DDevice ) return; +#endif + + uint nBytesLeft = size; + uint nOfs = 0; + while ( nBytesLeft ) + { + uint nBytesToCopy = MIN( nMaxSizePerCall, nBytesLeft ); + + gGL->glBufferSubData( target, offset + nOfs, nBytesToCopy, static_cast( data ) + nOfs ); + + nBytesLeft -= nBytesToCopy; + nOfs += nBytesToCopy; + } +} + +CGLMBuffer::CGLMBuffer( GLMContext *pCtx, EGLMBufferType type, uint size, uint options ) +{ + m_pCtx = pCtx; + m_type = type; + + m_bDynamic = ( options & GLMBufferOptionDynamic ) != 0; + + switch ( m_type ) + { + case kGLMVertexBuffer: m_buffGLTarget = GL_ARRAY_BUFFER_ARB; break; + case kGLMIndexBuffer: m_buffGLTarget = GL_ELEMENT_ARRAY_BUFFER_ARB; break; + case kGLMUniformBuffer: m_buffGLTarget = GL_UNIFORM_BUFFER_EXT; break; + case kGLMPixelBuffer: m_buffGLTarget = GL_PIXEL_UNPACK_BUFFER_ARB; break; + + default: Assert(!"Unknown buffer type" ); DXABSTRACT_BREAK_ON_ERROR(); + } + + m_nSize = size; + m_nActualSize = size; + m_bMapped = false; + m_pLastMappedAddress = NULL; + + m_pStaticBuffer = NULL; + m_nPinnedMemoryOfs = -1; + m_nPersistentBufferStartOffset = 0; + m_bUsingPersistentBuffer = false; + + m_bEnableAsyncMap = false; + m_bEnableExplicitFlush = false; + m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock + + m_pCtx->CheckCurrent(); + m_nRevision = rand(); + + m_pPseudoBuf = NULL; + m_pActualPseudoBuf = NULL; + + m_bPseudo = false; + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + m_bPseudo = true; +#endif + +#if GL_ENABLE_INDEX_VERIFICATION + m_BufferSpanManager.Init( m_pCtx, m_type, 512, m_nSize, m_bDynamic ); + + if ( m_type == kGLMIndexBuffer ) + m_bPseudo = true; +#endif + + if ( g_bUsePseudoBufs && m_bDynamic ) + { + m_bPseudo = true; + } + + if ( m_bPseudo ) + { + m_nHandle = 0; + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + m_nDirtyRangeStart = 0xFFFFFFFF; + m_nDirtyRangeEnd = 0; + + m_nActualSize = ALIGN_VALUE( ( m_nSize + sizeof( uint32 ) ), 4096 ); + m_pPseudoBuf = m_pActualPseudoBuf = (char *)VirtualAlloc( NULL, m_nActualSize, MEM_COMMIT, PAGE_READWRITE ); + if ( !m_pPseudoBuf ) + { + Error( "VirtualAlloc() failed!\n" ); + } + + for ( uint i = 0; i < m_nActualSize / sizeof( uint32 ); i++ ) + { + reinterpret_cast< uint32 * >( m_pPseudoBuf )[i] = 0xDEADBEEF; + } + + DWORD nOldProtect; + BOOL bResult = VirtualProtect( m_pActualPseudoBuf, m_nActualSize, PAGE_READONLY, &nOldProtect ); + if ( !bResult ) + { + Error( "VirtualProtect() failed!\n" ); + } +#else + m_nActualSize = size + 15; + m_pActualPseudoBuf = (char*)malloc( m_nActualSize ); + m_pPseudoBuf = (char*)(((intp)m_pActualPseudoBuf + 15) & ~15); +#endif + + m_pCtx->BindBufferToCtx( m_type, NULL ); // exit with no buffer bound + } + else + { + gGL->glGenBuffersARB( 1, &m_nHandle ); + + m_pCtx->BindBufferToCtx( m_type, this ); // causes glBindBufferARB + + // buffers start out static, but if they get orphaned and gl_bufmode is non zero, + // then they will get flipped to dynamic. + + GLenum hint = GL_STATIC_DRAW_ARB; + switch (m_type) + { + case kGLMVertexBuffer: hint = m_bDynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break; + case kGLMIndexBuffer: hint = m_bDynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break; + case kGLMUniformBuffer: hint = GL_DYNAMIC_DRAW_ARB; break; + case kGLMPixelBuffer: hint = m_bDynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break; + + default: Assert(!"Unknown buffer type" ); DXABSTRACT_BREAK_ON_ERROR(); + } + + gGL->glBufferDataARB( m_buffGLTarget, m_nSize, (const GLvoid*)NULL, hint ); // may ultimately need more hints to set the usage correctly (esp for streaming) + + SetModes( false, true, true ); + + m_pCtx->BindBufferToCtx( m_type, NULL ); // unbind me + } +} + +CGLMBuffer::~CGLMBuffer( ) +{ + m_pCtx->CheckCurrent(); + + if ( m_bPseudo ) + { +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + BOOL bResult = VirtualFree( m_pActualPseudoBuf, 0, MEM_RELEASE ); + if ( !bResult ) + { + Error( "VirtualFree() failed!\n" ); + } +#else + free( m_pActualPseudoBuf ); +#endif + m_pActualPseudoBuf = NULL; + m_pPseudoBuf = NULL; + } + else + { + gGL->glDeleteBuffersARB( 1, &m_nHandle ); + } + + m_pCtx = NULL; + m_nHandle = 0; + + m_pLastMappedAddress = NULL; + +#if GL_ENABLE_INDEX_VERIFICATION + m_BufferSpanManager.Deinit(); +#endif +} + +void CGLMBuffer::SetModes( bool bAsyncMap, bool bExplicitFlush, bool bForce ) +{ + // assumes buffer is bound. called by constructor and by Lock. + + if ( m_bPseudo ) + { + // ignore it... + } + else + { + if ( bForce || ( m_bEnableAsyncMap != bAsyncMap ) ) + { + // note the sense of the parameter, it's TRUE if you *want* serialization, so for async you turn it to false. + if ( ( gGL->m_bHave_GL_APPLE_flush_buffer_range ) && ( !gGL->m_bHave_GL_ARB_map_buffer_range ) ) + { + gGL->glBufferParameteriAPPLE( m_buffGLTarget, GL_BUFFER_SERIALIZED_MODIFY_APPLE, bAsyncMap == false ); + } + m_bEnableAsyncMap = bAsyncMap; + } + + if ( bForce || ( m_bEnableExplicitFlush != bExplicitFlush ) ) + { + // Note that the GL_ARB_map_buffer_range path handles this in the glMapBufferRange() call in Lock(). + // note the sense of the parameter, it's TRUE if you *want* auto-flush-on-unmap, so for explicit-flush, you turn it to false. + if ( ( gGL->m_bHave_GL_APPLE_flush_buffer_range ) && ( !gGL->m_bHave_GL_ARB_map_buffer_range ) ) + { + gGL->glBufferParameteriAPPLE( m_buffGLTarget, GL_BUFFER_FLUSHING_UNMAP_APPLE, bExplicitFlush == false ); + } + m_bEnableExplicitFlush = bExplicitFlush; + } + } +} + +#if GL_ENABLE_INDEX_VERIFICATION +bool CGLMBuffer::IsSpanValid( uint nOffset, uint nSize ) const +{ + return m_BufferSpanManager.IsValid( nOffset, nSize ); +} +#endif + +void CGLMBuffer::FlushRange( uint offset, uint size ) +{ + if ( m_pStaticBuffer ) + { + } + else if ( m_bPseudo ) + { + // nothing to do + } + else + { +#ifdef REPORT_LOCK_TIME + double flStart = Plat_FloatTime(); +#endif + + // assumes buffer is bound. + if ( gGL->m_bHave_GL_ARB_map_buffer_range ) + { + gGL->glFlushMappedBufferRange( m_buffGLTarget, (GLintptr)( offset - m_dirtyMinOffset ), (GLsizeiptr)size ); + } + else if ( gGL->m_bHave_GL_APPLE_flush_buffer_range ) + { + gGL->glFlushMappedBufferRangeAPPLE( m_buffGLTarget, (GLintptr)offset, (GLsizeiptr)size ); + } + +#ifdef REPORT_LOCK_TIME + double flEnd = Plat_FloatTime(); + if ( flEnd - flStart > 5.0 / 1000.0 ) + { + int nDelta = ( int )( ( flEnd - flStart ) * 1000 ); + if ( nDelta > 2 ) + { + Msg( "**** " ); + } + Msg( "glFlushMappedBufferRange Time %d: ( Name=%d BufSize=%d ) Target=%p Offset=%d FlushSize=%d\n", nDelta, m_nHandle, m_nSize, m_buffGLTarget, offset - m_dirtyMinOffset, size ); + } +#endif + + // If you don't have any extension support here, you'll flush the whole buffer on unmap. Performance loss, but it's still safe and correct. + } +} + +void CGLMBuffer::Lock( GLMBuffLockParams *pParams, char **pAddressOut ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "CGLMBuffer::Lock" ); + g_TelemetryGPUStats.m_nTotalBufferLocksAndUnlocks++; +#endif + + char *resultPtr = NULL; + + if ( m_bMapped ) + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + + m_pCtx->CheckCurrent(); + + Assert( pParams->m_nSize ); + + m_LockParams = *pParams; + + if ( pParams->m_nOffset >= m_nSize ) + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + + if ( ( pParams->m_nOffset + pParams->m_nSize ) > m_nSize) + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + +#if GL_ENABLE_INDEX_VERIFICATION + if ( pParams->m_bDiscard ) + { + m_BufferSpanManager.DiscardAllSpans(); + } +#endif + + m_pStaticBuffer = NULL; + bool bUsingPersistentBuffer = false; + + uint padding = 0; + if ( m_bDynamic && gGL->m_bHave_GL_ARB_buffer_storage ) + { + // Compute padding to add to make sure the start offset is valid + CPersistentBuffer *pTempBuffer = m_pCtx->GetCurPersistentBuffer( m_type ); + uint persistentBufferOffset = pTempBuffer->GetOffset(); + + if (pParams->m_nOffset > persistentBufferOffset) + { + // Make sure the start offset if valid (adding padding to the persistent buffer) + padding = pParams->m_nOffset - persistentBufferOffset; + } + } + + if ( m_bPseudo ) + { + if ( pParams->m_bDiscard ) + { + m_nRevision++; + } + + // async map modes are a no-op + + // calc lock address + resultPtr = m_pPseudoBuf + pParams->m_nOffset; + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + BOOL bResult; + DWORD nOldProtect; + if ( pParams->m_bDiscard ) + { + bResult = VirtualProtect( m_pActualPseudoBuf, m_nSize, PAGE_READWRITE, &nOldProtect ); + if ( !bResult ) + { + Error( "VirtualProtect() failed!\n" ); + } + + m_nDirtyRangeStart = 0xFFFFFFFF; + m_nDirtyRangeEnd = 0; + + for ( uint i = 0; i < m_nSize / sizeof( uint32 ); i++ ) + { + reinterpret_cast< uint32 * >( m_pPseudoBuf )[i] = 0xDEADBEEF; + } + + bResult = VirtualProtect( m_pActualPseudoBuf, m_nSize, PAGE_READONLY, &nOldProtect ); + if ( !bResult ) + { + Error( "VirtualProtect() failed!\n" ); + } + } + uint nProtectOfs = m_LockParams.m_nOffset & 4095; + uint nProtectEnd = ( m_LockParams.m_nOffset + m_LockParams.m_nSize + 4095 ) & ~4095; + uint nProtectSize = nProtectEnd - nProtectOfs; + bResult = VirtualProtect( m_pActualPseudoBuf + nProtectOfs, nProtectSize, PAGE_READWRITE, &nOldProtect ); + if ( !bResult ) + { + Error( "VirtualProtect() failed!\n" ); + } +#endif + } + else if ( m_bDynamic && gGL->m_bHave_GL_ARB_buffer_storage && ( m_pCtx->GetCurPersistentBuffer( m_type )->GetBytesRemaining() >= ( pParams->m_nSize + padding ) ) ) + { + CPersistentBuffer *pTempBuffer = m_pCtx->GetCurPersistentBuffer( m_type ); + + // Make sure the start offset if valid (adding padding to the persistent buffer) + pTempBuffer->Append( padding ); + + uint persistentBufferOffset = pTempBuffer->GetOffset(); + uint startOffset = persistentBufferOffset - pParams->m_nOffset; + + if ( pParams->m_bDiscard || ( startOffset != m_nPersistentBufferStartOffset ) ) + { + m_nRevision++; + // Offset to be added to the vertex and index buffer when setting the vertex and index buffer (before drawing) + // Since we are using a immutable buffer storage, the persistent buffer is actually bigger than + // buffer size requested upon creation. We keep appending to the end of the persistent buffer + // and therefore need to keep track of the start of the actual buffer (in the persistent one) + m_nPersistentBufferStartOffset = startOffset; + + //DevMsg( "Discard (%s): startOffset = %d\n", pParams->m_bDiscard ? "true" : "false", m_nPersistentBufferStartOffset ); + } + + resultPtr = static_cast(pTempBuffer->GetPtr()) + persistentBufferOffset; + bUsingPersistentBuffer = true; + + //DevMsg( " --> buff=%x, startOffset=%d, paramsOffset=%d, persistOffset = %d\n", this, m_nPersistentBufferStartOffset, pParams->m_nOffset, persistentBufferOffset ); + } +#ifndef OSX + else if ( m_bDynamic && gGL->m_bHave_GL_AMD_pinned_memory && ( m_pCtx->GetCurPinnedMemoryBuffer()->GetBytesRemaining() >= pParams->m_nSize ) ) + { + if ( pParams->m_bDiscard ) + { + m_nRevision++; + } + + m_dirtyMinOffset = pParams->m_nOffset; + m_dirtyMaxOffset = pParams->m_nOffset + pParams->m_nSize; + + CPinnedMemoryBuffer *pTempBuffer = m_pCtx->GetCurPinnedMemoryBuffer(); + + m_nPinnedMemoryOfs = pTempBuffer->GetOfs(); + + resultPtr = static_cast( pTempBuffer->GetPtr() ) + m_nPinnedMemoryOfs; + + pTempBuffer->Append( pParams->m_nSize ); + } +#endif // OSX + else if ( !g_bDisableStaticBuffer && ( pParams->m_bDiscard || pParams->m_bNoOverwrite ) && ( pParams->m_nSize <= GL_STATIC_BUFFER_SIZE ) ) + { +#if TOGL_SUPPORT_NULL_DEVICE + if ( !g_bNullD3DDevice ) +#endif + { + if ( pParams->m_bDiscard ) + { + m_pCtx->BindBufferToCtx( m_type, this ); + + // observe gl_bufmode on any orphan event. + // if orphaned and bufmode is nonzero, flip it to dynamic. + GLenum hint = gl_bufmode.GetInt() ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; + gGL->glBufferDataARB( m_buffGLTarget, m_nSize, (const GLvoid*)NULL, hint ); + + m_nRevision++; // revision grows on orphan event + } + } + + m_dirtyMinOffset = pParams->m_nOffset; + m_dirtyMaxOffset = pParams->m_nOffset + pParams->m_nSize; + + switch ( m_type ) + { + case kGLMVertexBuffer: + { + m_pStaticBuffer = m_StaticBuffers[ 0 ]; + break; + } + case kGLMIndexBuffer: + { + m_pStaticBuffer = m_StaticBuffers[ 1 ]; + break; + } + default: + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + } + + resultPtr = m_pStaticBuffer; + } + else + { + // bind (yes, even for pseudo - this binds name 0) + m_pCtx->BindBufferToCtx( m_type, this ); + + // perform discard if requested + if ( pParams->m_bDiscard ) + { + // observe gl_bufmode on any orphan event. + // if orphaned and bufmode is nonzero, flip it to dynamic. + + // We always want to call glBufferData( ..., NULL ) on discards, even though we're using the GL_MAP_INVALIDATE_BUFFER_BIT flag, because this flag is actually only a hint according to AMD. + GLenum hint = gl_bufmode.GetInt() ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; + gGL->glBufferDataARB( m_buffGLTarget, m_nSize, (const GLvoid*)NULL, hint ); + + m_nRevision++; // revision grows on orphan event + } + + // adjust async map option appropriately, leave explicit flush unchanged + SetModes( pParams->m_bNoOverwrite, m_bEnableExplicitFlush ); + + // map + char *mapPtr; + if ( gGL->m_bHave_GL_ARB_map_buffer_range ) + { + // m_bEnableAsyncMap is actually pParams->m_bNoOverwrite + GLbitfield parms = GL_MAP_WRITE_BIT | ( m_bEnableAsyncMap ? GL_MAP_UNSYNCHRONIZED_BIT : 0 ) | ( pParams->m_bDiscard ? GL_MAP_INVALIDATE_BUFFER_BIT : 0 ) | ( m_bEnableExplicitFlush ? GL_MAP_FLUSH_EXPLICIT_BIT : 0 ); + +#ifdef REPORT_LOCK_TIME + double flStart = Plat_FloatTime(); +#endif + + mapPtr = (char*)gGL->glMapBufferRange( m_buffGLTarget, pParams->m_nOffset, pParams->m_nSize, parms); + +#ifdef REPORT_LOCK_TIME + double flEnd = Plat_FloatTime(); + if ( flEnd - flStart > 5.0 / 1000.0 ) + { + int nDelta = ( int )( ( flEnd - flStart ) * 1000 ); + if ( nDelta > 2 ) + { + Msg( "**** " ); + } + Msg( "glMapBufferRange Time=%d: ( Name=%d BufSize=%d ) Target=%p Offset=%d LockSize=%d ", nDelta, m_nHandle, m_nSize, m_buffGLTarget, pParams->m_nOffset, pParams->m_nSize ); + if ( parms & GL_MAP_WRITE_BIT ) + { + Msg( "GL_MAP_WRITE_BIT "); + } + if ( parms & GL_MAP_UNSYNCHRONIZED_BIT ) + { + Msg( "GL_MAP_UNSYNCHRONIZED_BIT "); + } + if ( parms & GL_MAP_INVALIDATE_BUFFER_BIT ) + { + Msg( "GL_MAP_INVALIDATE_BUFFER_BIT "); + } + if ( parms & GL_MAP_INVALIDATE_RANGE_BIT ) + { + Msg( "GL_MAP_INVALIDATE_RANGE_BIT "); + } + if ( parms & GL_MAP_FLUSH_EXPLICIT_BIT ) + { + Msg( "GL_MAP_FLUSH_EXPLICIT_BIT "); + } + Msg( "\n" ); + } +#endif + } + else + { + mapPtr = (char*)gGL->glMapBufferARB( m_buffGLTarget, GL_WRITE_ONLY_ARB ); + } + + Assert( mapPtr ); + + // calculate offset location + resultPtr = mapPtr; + if ( !gGL->m_bHave_GL_ARB_map_buffer_range ) + { + resultPtr += pParams->m_nOffset; + } + + // set range + m_dirtyMinOffset = pParams->m_nOffset; + m_dirtyMaxOffset = pParams->m_nOffset + pParams->m_nSize; + } + + if ( m_bUsingPersistentBuffer != bUsingPersistentBuffer ) + { + // Up the revision number when switching from a persistent to a non persistent buffer (or vice versa) + // Ensure the right GL buffer is bound before drawing (and vertex attribs properly set) + m_nRevision++; + m_bUsingPersistentBuffer = bUsingPersistentBuffer; + } + + m_bMapped = true; + + m_pLastMappedAddress = (float*)resultPtr; + + *pAddressOut = resultPtr; +} + +void CGLMBuffer::Unlock( int nActualSize, const void *pActualData ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "CGLMBuffer::Unlock" ); + g_TelemetryGPUStats.m_nTotalBufferLocksAndUnlocks++; +#endif + + m_pCtx->CheckCurrent(); + + if ( !m_bMapped ) + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + + if ( nActualSize < 0 ) + { + nActualSize = m_LockParams.m_nSize; + } + + if ( nActualSize > (int)m_LockParams.m_nSize ) + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + if ( m_bPseudo ) + { + // Check guard DWORD to detect buffer overruns (but are still within the last 4KB page so they don't get caught via pagefaults) + if ( *reinterpret_cast< const uint32 * >( m_pPseudoBuf + m_nSize ) != 0xDEADBEEF ) + { + // If this fires the client app has overwritten the guard DWORD beyond the end of the buffer. + DXABSTRACT_BREAK_ON_ERROR(); + } + + static const uint s_nInitialValues[4] = { 0xEF, 0xBE, 0xAD, 0xDE }; + + int nActualModifiedStart, nActualModifiedEnd; + for ( nActualModifiedStart = 0; nActualModifiedStart < (int)m_LockParams.m_nSize; ++nActualModifiedStart ) + if ( reinterpret_cast< const uint8 * >( m_pLastMappedAddress )[nActualModifiedStart] != s_nInitialValues[ ( m_LockParams.m_nOffset + nActualModifiedStart ) & 3 ] ) + break; + + for ( nActualModifiedEnd = m_LockParams.m_nSize - 1; nActualModifiedEnd > nActualModifiedStart; --nActualModifiedEnd ) + if ( reinterpret_cast< const uint8 * >( m_pLastMappedAddress )[nActualModifiedEnd] != s_nInitialValues[ ( m_LockParams.m_nOffset + nActualModifiedEnd ) & 3 ] ) + break; + + int nNumActualBytesModified = 0; + + if ( nActualModifiedEnd >= nActualModifiedStart ) + { + // The modified check is conservative (i.e. it should always err on the side of detecting <= actual bytes than where actually modified, never more). + // We primarily care about the case where the user lies about the actual # of modified bytes, which can lead to difficult to debug/inconsistent problems with some drivers. + // Round up/down the modified range, because the user's data may alias with the initial buffer values (0xDEADBEEF) so we may miss some bytes that where written. + if ( m_type == kGLMIndexBuffer ) + { + nActualModifiedStart &= ~1; + nActualModifiedEnd = MIN( (int)m_LockParams.m_nSize, ( ( nActualModifiedEnd + 1 ) + 1 ) & ~1 ) - 1; + } + else + { + nActualModifiedStart &= ~3; + nActualModifiedEnd = MIN( (int)m_LockParams.m_nSize, ( ( nActualModifiedEnd + 1 ) + 3 ) & ~3 ) - 1; + } + + nNumActualBytesModified = nActualModifiedEnd + 1; + + if ( nActualSize < nNumActualBytesModified ) + { + // The caller may be lying about the # of actually modified bytes in this lock. + // Has this lock region been previously locked? If so, it may have been previously overwritten before. Otherwise, the region had to be the 0xDEADBEEF fill DWORD at lock time. + if ( ( m_nDirtyRangeStart > m_nDirtyRangeEnd ) || + ( m_LockParams.m_nOffset > m_nDirtyRangeEnd ) || ( ( m_LockParams.m_nOffset + m_LockParams.m_nSize ) <= m_nDirtyRangeStart ) ) + { + // If this fires the client has lied about the actual # of bytes they've modified in the buffer - this will cause unreliable rendering on AMD drivers (because AMD actually pays attention to the actual # of flushed bytes). + DXABSTRACT_BREAK_ON_ERROR(); + } + } + + m_nDirtyRangeStart = MIN( m_nDirtyRangeStart, m_LockParams.m_nOffset + nActualModifiedStart ); + m_nDirtyRangeEnd = MAX( m_nDirtyRangeEnd, m_LockParams.m_nOffset + nActualModifiedEnd ); + } + +#if GL_ENABLE_INDEX_VERIFICATION + if ( nActualModifiedEnd >= nActualModifiedStart ) + { + int n = nActualModifiedEnd + 1; + if ( n != nActualSize ) + { + // The actual detected modified size is < than the reported size, which is common because the last few DWORD's of the vertex format may not actually be used/written (or read by the vertex shader). So just fudge it so the batch consumption checks work. + if ( ( (int)nActualSize - n ) <= 32 ) + { + n = nActualSize; + } + } + + m_BufferSpanManager.AddSpan( m_LockParams.m_nOffset + nActualModifiedStart, m_LockParams.m_nSize, n - nActualModifiedStart, m_LockParams.m_bDiscard, m_LockParams.m_bNoOverwrite ); + } +#endif + } +#elif GL_ENABLE_INDEX_VERIFICATION + if ( nActualSize > 0 ) + { + m_BufferSpanManager.AddSpan( m_LockParams.m_nOffset, m_LockParams.m_nSize, nActualSize, m_LockParams.m_bDiscard, m_LockParams.m_bNoOverwrite ); + } +#endif + +#if GL_BATCH_PERF_ANALYSIS + if ( m_type == kGLMIndexBuffer ) + g_nTotalIBLockBytes += nActualSize; + else if ( m_type == kGLMVertexBuffer ) + g_nTotalVBLockBytes += nActualSize; +#endif + +#ifndef OSX + if ( m_nPinnedMemoryOfs >= 0 ) + { +#if TOGL_SUPPORT_NULL_DEVICE + if ( !g_bNullD3DDevice ) + { +#endif + if ( nActualSize ) + { + m_pCtx->BindBufferToCtx( m_type, this ); + + gGL->glCopyBufferSubData( + GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, + m_buffGLTarget, + m_nPinnedMemoryOfs, + m_dirtyMinOffset, + nActualSize ); + } + +#if TOGL_SUPPORT_NULL_DEVICE + } +#endif + + m_nPinnedMemoryOfs = -1; + } + else +#endif // !OSX + if ( m_bUsingPersistentBuffer ) + { + if ( nActualSize ) + { + CPersistentBuffer *pTempBuffer = m_pCtx->GetCurPersistentBuffer( m_type ); + pTempBuffer->Append( nActualSize ); + + //DevMsg( " <-- actualSize=%d, persistOffset = %d\n", nActualSize, pTempBuffer->GetOffset() ); + } + } + else if ( m_pStaticBuffer ) + { +#if TOGL_SUPPORT_NULL_DEVICE + if ( !g_bNullD3DDevice ) +#endif + { + if ( nActualSize ) + { + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "UnlockSubData" ); + + #ifdef REPORT_LOCK_TIME + double flStart = Plat_FloatTime(); + #endif + m_pCtx->BindBufferToCtx( m_type, this ); + + Assert( nActualSize <= (int)( m_dirtyMaxOffset - m_dirtyMinOffset ) ); + + glBufferSubDataMaxSize( m_buffGLTarget, m_dirtyMinOffset, nActualSize, pActualData ? pActualData : m_pStaticBuffer ); + + #ifdef REPORT_LOCK_TIME + double flEnd = Plat_FloatTime(); + if ( flEnd - flStart > 5.0 / 1000.0 ) + { + int nDelta = ( int )( ( flEnd - flStart ) * 1000 ); + if ( nDelta > 2 ) + { + Msg( "**** " ); + } + // Msg( "glBufferSubData Time=%d: ( Name=%d BufSize=%d ) Target=%p Offset=%d Size=%d\n", nDelta, m_nHandle, m_nSize, m_buffGLTarget, m_dirtyMinOffset, m_dirtyMaxOffset - m_dirtyMinOffset ); + } + #endif + } + } + + m_pStaticBuffer = NULL; + } + else if ( m_bPseudo ) + { + if ( pActualData ) + { + memcpy( m_pLastMappedAddress, pActualData, nActualSize ); + } + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + uint nProtectOfs = m_LockParams.m_nOffset & 4095; + uint nProtectEnd = ( m_LockParams.m_nOffset + m_LockParams.m_nSize + 4095 ) & ~4095; + uint nProtectSize = nProtectEnd - nProtectOfs; + + DWORD nOldProtect; + BOOL bResult = VirtualProtect( m_pActualPseudoBuf + nProtectOfs, nProtectSize, PAGE_READONLY, &nOldProtect ); + if ( !bResult ) + { + Error( "VirtualProtect() failed!\n" ); + } +#endif + } + else + { + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "UnlockUnmap" ); + + if ( pActualData ) + { + memcpy( m_pLastMappedAddress, pActualData, nActualSize ); + } + + m_pCtx->BindBufferToCtx( m_type, this ); + + Assert( nActualSize <= (int)( m_dirtyMaxOffset - m_dirtyMinOffset ) ); + + // time to do explicit flush (currently m_bEnableExplicitFlush is always true) + if ( m_bEnableExplicitFlush ) + { + FlushRange( m_dirtyMinOffset, nActualSize ); + } + + // clear dirty range no matter what + m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock + +#ifdef REPORT_LOCK_TIME + double flStart = Plat_FloatTime(); +#endif + + gGL->glUnmapBuffer( m_buffGLTarget ); + +#ifdef REPORT_LOCK_TIME + double flEnd = Plat_FloatTime(); + if ( flEnd - flStart > 5.0 / 1000.0 ) + { + int nDelta = ( int )( ( flEnd - flStart ) * 1000 ); + if ( nDelta > 2 ) + { + Msg( "**** " ); + } + Msg( "glUnmapBuffer Time=%d: ( Name=%d BufSize=%d ) Target=%p\n", nDelta, m_nHandle, m_nSize, m_buffGLTarget ); + } +#endif + } + + m_bMapped = false; +} + +GLuint CGLMBuffer::GetHandle() const +{ + return ( m_bUsingPersistentBuffer ? m_pCtx->GetCurPersistentBuffer( m_type )->GetHandle() : m_nHandle ); +} diff --git a/togles/linuxwin/cglmfbo.cpp b/togles/linuxwin/cglmfbo.cpp new file mode 100644 index 00000000..ec2418d4 --- /dev/null +++ b/togles/linuxwin/cglmfbo.cpp @@ -0,0 +1,355 @@ +//========= 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. +// +// cglmfbo.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +CGLMFBO::CGLMFBO( GLMContext *ctx ) +{ + m_ctx = ctx; + m_ctx->CheckCurrent(); + + gGL->glGenFramebuffersEXT( 1, &m_name ); + + memset( m_attach, 0, sizeof( m_attach ) ); +} + + +CGLMFBO::~CGLMFBO( ) +{ + m_ctx->CheckCurrent(); + + // detach all known attached textures first... necessary ? + for( int index = 0; index < kAttCount; index++) + { + if (m_attach[ index ].m_tex) + { + TexDetach( (EGLMFBOAttachment)index ); + } + } + + gGL->glDeleteFramebuffersEXT( 1, &m_name ); + + m_name = 0; + m_ctx = NULL; +} + +// the tex attach path should also select a specific slice of the texture... +// and we need a way to make renderbuffers.. + +static GLenum EncodeAttachmentFBO( EGLMFBOAttachment index ) +{ + if (index < kAttDepth) + { + return GL_COLOR_ATTACHMENT0_EXT + (int) index; + } + else + { + switch( index ) + { + case kAttDepth: + return GL_DEPTH_ATTACHMENT_EXT; + break; + + case kAttStencil: + return GL_STENCIL_ATTACHMENT_EXT; + break; + + case kAttDepthStencil: + return GL_DEPTH_STENCIL_ATTACHMENT_EXT; + break; + + default: + GLMStop(); // bad news + break; + } + } + + GLMStop(); // bad news + // shouldn't get here + return GL_COLOR_ATTACHMENT0_EXT; +} + +void CGLMFBO::TexAttach( GLMFBOTexAttachParams *params, EGLMFBOAttachment attachIndex, GLenum fboBindPoint ) +{ + // force our parent context to be current + m_ctx->MakeCurrent(); + + // bind to context (will cause FBO object creation on first use) + m_ctx->BindFBOToCtx( this, fboBindPoint ); + + // it's either a plain 2D, a 2D face of a cube map, or a slice of a 3D. + CGLMTex *tex = params->m_tex; + + // always detach what is currently there, if anything + this->TexDetach( attachIndex, fboBindPoint ); + + if (!tex) + { + // andif they pass NULL to us, then we are done. + return; + } + + GLMTexLayout *layout = tex->m_layout; + + GLenum target = tex->m_layout->m_key.m_texGLTarget; + + GLenum attachIndexGL = EncodeAttachmentFBO( attachIndex ); + + switch( target ) + { + case GL_TEXTURE_2D: + { + // we will attach the underlying RBO on a multisampled tex, iff the tex has one, **and** we're not being asked to attach it to the read buffer. + // if we get a req to attach an MSAA tex to the read buffer, chances are it's BlitTex calling, andit has already resolved the tex, so in those + // cases you really do want to attach the texture and not the RBO to the FBO in question. + + bool useRBO = false; // initial state + + if (layout->m_key.m_texFlags & kGLMTexMultisampled) + { + // it is an MSAA tex + if (fboBindPoint == GL_READ_FRAMEBUFFER_EXT) + { + // I think you just want to read a resolved tex. + // But I will check that it is resolved first.. + Assert( tex->IsRBODirty() == false ); + } + else + { + // you want to draw into it. You get the RBO bound instead of the tex. + useRBO = true; + } + } + + if (useRBO) + { + // MSAA path - attach the RBO, not the texture, and mark the RBO dirty + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // you have to attach it both places... + // http://www.opengl.org/wiki/GL_EXT_framebuffer_object + + // bind the RBO to the GL_RENDERBUFFER_EXT target + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, tex->m_rboName ); + + // attach the GL_RENDERBUFFER_EXT target to the depth and stencil attach points + gGL->glFramebufferRenderbufferEXT( fboBindPoint, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, tex->m_rboName); + + gGL->glFramebufferRenderbufferEXT( fboBindPoint, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, tex->m_rboName); + + // no need to leave the RBO hanging on + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + } + else + { + // color attachment (likely 0) + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, tex->m_rboName ); + + gGL->glFramebufferRenderbufferEXT( fboBindPoint, attachIndexGL, GL_RENDERBUFFER_EXT, tex->m_rboName); + + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + } + tex->ForceRBODirty(); + } + else + { + // regular path - attaching a texture2d + + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // you have to attach it both places... + // http://www.opengl.org/wiki/GL_EXT_framebuffer_object + + gGL->glFramebufferTexture2DEXT( fboBindPoint, GL_DEPTH_ATTACHMENT_EXT, target, tex->m_texName, params->m_mip ); + + gGL->glFramebufferTexture2DEXT( fboBindPoint, GL_STENCIL_ATTACHMENT_EXT, target, tex->m_texName, params->m_mip ); + } + else + { + gGL->glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, target, tex->m_texName, params->m_mip ); + } + } + } + break; + + case GL_TEXTURE_3D: + { + gGL->glFramebufferTexture3DEXT( fboBindPoint, attachIndexGL, target, tex->m_texName, params->m_mip, params->m_zslice ); + } + break; + + case GL_TEXTURE_CUBE_MAP: + { + // adjust target to steer to the proper face of the cube map + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + params->m_face; + + gGL->glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, target, tex->m_texName, params->m_mip ); + } + break; + } + + // log the attached tex + m_attach[ attachIndex ] = *params; + + // indicate that the tex has been bound to an RT + tex->m_rtAttachCount++; +} + +void CGLMFBO::TexDetach( EGLMFBOAttachment attachIndex, GLenum fboBindPoint ) +{ + // force our parent context to be current + m_ctx->MakeCurrent(); + + // bind to context (will cause FBO object creation on first use) + m_ctx->BindFBOToCtx( this, fboBindPoint ); + + if (m_attach[ attachIndex ].m_tex) + { + CGLMTex *tex = m_attach[ attachIndex ].m_tex; + GLMTexLayout *layout = tex->m_layout; + GLenum target = tex->m_layout->m_key.m_texGLTarget; + + GLenum attachIndexGL = EncodeAttachmentFBO( attachIndex ); + + switch( target ) + { + case GL_TEXTURE_2D: + { + if (layout->m_key.m_texFlags & kGLMTexMultisampled) + { + // MSAA path - detach the RBO, not the texture + // (is this the right time to resolve? probably better to wait until someone tries to sample the texture) + + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // detach the GL_RENDERBUFFER_EXT target at depth and stencil attach points + gGL->glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + + gGL->glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + } + else + { + // color attachment (likely 0) + gGL->glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, attachIndexGL, GL_RENDERBUFFER_EXT, 0); + } + } + else + { + // plain tex detach + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // you have to detach it both places... + // http://www.opengl.org/wiki/GL_EXT_framebuffer_object + + gGL->glFramebufferTexture2DEXT( fboBindPoint, GL_DEPTH_ATTACHMENT_EXT, target, 0, 0 ); + gGL->glFramebufferTexture2DEXT( fboBindPoint, GL_STENCIL_ATTACHMENT_EXT, target, 0, 0 ); + } + else + { + gGL->glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, target, 0, 0 ); + } + } + } + break; + + case GL_TEXTURE_3D: + { + gGL->glFramebufferTexture3DEXT( fboBindPoint, attachIndexGL, target, 0, 0, 0 ); + } + break; + + case GL_TEXTURE_CUBE_MAP: + { + gGL->glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, target, 0, 0 ); + } + break; + } + + // un-log the attached tex + memset( &m_attach[ attachIndex ], 0, sizeof( m_attach[0] ) ); + + // drop the RT attach count + tex->m_rtAttachCount--; + } + else + { + //Debugger(); // odd, but not harmful - typ comes from D3D code passing NULL into SetRenderTarget + } +} + +void CGLMFBO::TexScrub( CGLMTex *tex ) +{ + // see if it's attached anywhere + for( int attachIndex = 0; attachIndex < kAttCount; attachIndex++ ) + { + if (m_attach[ attachIndex ].m_tex == tex) + { + // blammo + TexDetach( (EGLMFBOAttachment)attachIndex, GL_DRAW_FRAMEBUFFER_EXT ); + } + } +} + + +bool CGLMFBO::IsReady( void ) +{ + bool result = false; + + // ensure our parent context is current + m_ctx->CheckCurrent(); + + // bind to context (will cause FBO object creation on first use) + m_ctx->BindFBOToCtx( this ); + + GLenum status; + status = gGL->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + switch(status) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + result = true; + break; + + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + result = false; + DebuggerBreak(); + /* choose different formats */ + break; + + default: + result = false; + DebuggerBreak(); + /* programming error; will fail on all hardware */ + break; + } + return result; +} diff --git a/togles/linuxwin/cglmprogram.cpp b/togles/linuxwin/cglmprogram.cpp new file mode 100644 index 00000000..8c0103b3 --- /dev/null +++ b/togles/linuxwin/cglmprogram.cpp @@ -0,0 +1,1570 @@ +//========= 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. +// +// cglmprogram.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +#include "filesystem.h" +#include "tier1/fmtstr.h" +#include "tier1/KeyValues.h" +#include "tier0/fasttimer.h" + +#if GLMDEBUG && defined( _MSC_VER ) +#include +#endif + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +#if GLMDEBUG +#define GLM_FREE_SHADER_TEXT 0 +#else +#define GLM_FREE_SHADER_TEXT 1 +#endif + +//=============================================================================== + +ConVar gl_shaderpair_cacherows_lg2( "gl_paircache_rows_lg2", "10"); // 10 is minimum +ConVar gl_shaderpair_cacheways_lg2( "gl_paircache_ways_lg2", "5"); // 5 is minimum +ConVar gl_shaderpair_cachelog( "gl_shaderpair_cachelog", "0" ); + +static CCycleCount gShaderCompileTime; +static int gShaderCompileCount = 0; +static CCycleCount gShaderCompileQueryTime; +static CCycleCount gShaderLinkTime; +static int gShaderLinkCount = 0; +static CCycleCount gShaderLinkQueryTime; +CON_COMMAND( gl_shader_compile_time_dump, "Dump stats shader compile time." ) +{ + ConMsg( "Shader Compile Time: %u ms (Count: %d) / Query: %u ms \n", (uint32)gShaderCompileTime.GetMilliseconds(), gShaderCompileCount, (uint32)gShaderCompileQueryTime.GetMilliseconds() ); + ConMsg( "Shader Link Time : %u ms (Count: %d) / Query: %u ms \n", (uint32)gShaderLinkTime.GetMilliseconds(), gShaderLinkCount, (uint32)gShaderLinkQueryTime.GetMilliseconds() ); +} + +//=============================================================================== + + +GLenum GLMProgTypeToARBEnum( EGLMProgramType type ) +{ + GLenum result = 0; + switch(type) + { + case kGLMVertexProgram: result = GL_VERTEX_PROGRAM_ARB; break; + case kGLMFragmentProgram: result = GL_FRAGMENT_PROGRAM_ARB; break; + default: Assert( !"bad program type"); result = 0; break; + } + return result; +} + +GLenum GLMProgTypeToGLSLEnum( EGLMProgramType type ) +{ + GLenum result = 0; + switch(type) + { + case kGLMVertexProgram: result = GL_VERTEX_SHADER_ARB; break; + case kGLMFragmentProgram: result = GL_FRAGMENT_SHADER_ARB; break; + default: Assert( !"bad program type"); result = 0; break; + } + return result; +} + +CGLMProgram::CGLMProgram( GLMContext *ctx, EGLMProgramType type ) +{ + m_ctx = ctx; + m_ctx->CheckCurrent(); + + m_type = type; + m_nHashTag = rand() ^ ( rand() << 15 ); + m_text = NULL; // no text yet + +#if GLMDEBUG + m_editable = NULL; +#endif + + memset( &m_descs, 0, sizeof( m_descs ) ); + + m_samplerMask = 0; // dxabstract sets this field later + m_samplerTypes = 0; + m_fragDataMask = 0; + m_numDrawBuffers = 0; + memset( &m_drawBuffers, 0, sizeof( m_drawBuffers ) ); + + m_maxSamplers = GLM_SAMPLER_COUNT; + m_nNumUsedSamplers = GLM_SAMPLER_COUNT; + m_maxVertexAttrs = kGLMVertexAttributeIndexMax; + + // create an ARB vp/fp program object name. No need to bind it yet. + GLMShaderDesc *arbDesc = &m_descs[ kGLMARB ]; + Assert(gGL); + gGL->glGenProgramsARB( 1, &arbDesc->m_object.arb ); + + // create a GLSL shader object. + GLMShaderDesc *glslDesc = &m_descs[ kGLMGLSL ]; + GLenum glslStage = GLMProgTypeToGLSLEnum( m_type ); + + glslDesc->m_object.glsl = gGL->glCreateShaderObjectARB( glslStage );; + + m_shaderName[0] = '\0'; + + m_bTranslatedProgram = false; + + m_nCentroidMask = 0; + m_nShadowDepthSamplerMask = 0; + + m_labelName[0] = '\0'; + m_labelIndex = -1; + m_labelCombo = -1; + + // no text has arrived yet. That's done in SetProgramText. +} + +CGLMProgram::~CGLMProgram( ) +{ + m_ctx->CheckCurrent(); + + // if there is an arb program, delete it + GLMShaderDesc *arbDesc = &m_descs[ kGLMARB ]; + if (arbDesc->m_object.arb) + { + gGL->glDeleteProgramsARB( 1, &arbDesc->m_object.arb ); + arbDesc->m_object.arb = 0; + } + + // if there is a GLSL shader, delete it + GLMShaderDesc *glslDesc = &m_descs[kGLMGLSL]; + if (glslDesc->m_object.glsl) + { + gGL->glDeleteShader( (uint)glslDesc->m_object.glsl ); // why do I need a cast here again ? + glslDesc->m_object.glsl = 0; + } + +#if GLMDEBUG + if (m_editable) + { + delete m_editable; + m_editable = NULL; + } +#endif + + if (m_text) + { + free( m_text ); + m_text = NULL; + } + m_ctx = NULL; +} + +enum EShaderSection +{ + kGLMARBVertex, kGLMARBVertexDisabled, + kGLMARBFragment, kGLMARBFragmentDisabled, + kGLMGLSLVertex, kGLMGLSLVertexDisabled, + kGLMGLSLFragment, kGLMGLSLFragmentDisabled, + +}; + +const char *g_shaderSectionMarkers[] = // match ordering of enum +{ + "!!ARBvp", "-!!ARBvp", // enabled and disabled markers. so you can have multiple flavors in a blob and activate the one you want. + "!!ARBfp", "-!!ARBfp", + "//GLSLvp", "-//GLSLvp", + "//GLSLfp", "-//GLSLfp", + NULL +}; + +void CGLMProgram::SetShaderName( const char *name ) +{ + V_strncpy( m_shaderName, name, sizeof( m_shaderName ) ); +} + +void CGLMProgram::SetProgramText( char *text ) +{ + // free old text if any + // clone new text + // scan newtext to find sections + // walk sections, and mark descs to indicate where text is at + + if (m_text) + { + free( m_text ); + m_text = NULL; + } + + // scrub desc text references + for( int i=0; im_textPresent = false; + desc->m_textOffset = 0; + desc->m_textLength = 0; + } + + m_text = strdup( text ); + Assert( m_text != NULL ); + + #if GLMDEBUG + // create editable text item, if it does not already exist + if (!m_editable) + { + char *suffix = ""; + + switch(m_type) + { + case kGLMVertexProgram: suffix = ".vsh"; break; + case kGLMFragmentProgram: suffix = ".fsh"; break; + default: GLMDebugger(); + } + +#ifdef POSIX + CFmtStr debugShaderPath( "%s/debugshaders/", getenv( "HOME" ) ); +#else + CFmtStr debugShaderPath( "debugshaders/" ); +#endif + _mkdir( debugShaderPath.Access() ); + m_editable = new CGLMEditableTextItem( m_text, strlen(m_text), false, debugShaderPath.Access(), suffix ); + + // pull our string back from the editable (it has probably munged it) + if (m_editable->HasData()) + { + ReloadStringFromEditable(); + } + } + #endif + + + // scan the text and find sections + CGLMTextSectioner sections( m_text, strlen( m_text ), g_shaderSectionMarkers ); + + int sectionCount = sections.Count(); + for( int i=0; i < sectionCount; i++ ) + { + uint subtextOffset = 0; + uint subtextLength = 0; + int markerIndex = 0; + + sections.GetSection( i, &subtextOffset, &subtextLength, &markerIndex ); + + // act on the section + GLMShaderDesc *desc = NULL; + switch( m_type ) + { + case kGLMVertexProgram: + switch( markerIndex ) + { + case kGLMARBVertex: + case kGLMGLSLVertex: + desc = &m_descs[ (markerIndex==kGLMARBVertex) ? kGLMARB : kGLMGLSL]; + + // these steps are generic across both langs + desc->m_textPresent = true; + desc->m_textOffset = subtextOffset; + desc->m_textLength = subtextLength; + desc->m_compiled = false; + desc->m_valid = false; + break; + + case kGLMARBVertexDisabled: + case kGLMGLSLVertexDisabled: + // ignore quietly + break; + + default: Assert(!"Mismatched section marker seen in SetProgramText (VP)"); break; + } + break; + + case kGLMFragmentProgram: + switch( markerIndex ) + { + case kGLMARBFragment: + case kGLMGLSLFragment: + desc = &m_descs[ (markerIndex==kGLMARBFragment) ? kGLMARB : kGLMGLSL]; + + // these steps are generic across both langs + desc->m_textPresent = true; + desc->m_textOffset = subtextOffset; + desc->m_textLength = subtextLength; + desc->m_compiled = false; + desc->m_valid = false; + break; + + case kGLMARBFragmentDisabled: + case kGLMGLSLFragmentDisabled: + // ignore quietly + break; + + default: Assert(!"Mismatched section marker seen in SetProgramText (VP)"); break; + } + break; + } + } + + // find the label string + // example: + // trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234 + + char *lineStr = strstr( m_text, "// trans#" ); + if (lineStr) + { + int scratch = -1; + + if (this->m_type == kGLMVertexProgram) + { + sscanf( lineStr, "// trans#%d label:vs-file %s vs-index %d vs-combo %d", &scratch, m_labelName, &m_labelIndex, &m_labelCombo ); + } + else + { + sscanf( lineStr, "// trans#%d label:ps-file %s ps-index %d ps-combo %d", &scratch, m_labelName, &m_labelIndex, &m_labelCombo ); + } + } +} + +void CGLMProgram::CompileActiveSources ( void ) +{ + // compile everything we have text for + for( int i=0; iFindParm( "-gl_time_shader_compiles" ) != 0); + // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles. + CFastTimer shaderCompileTimer; + if (bTimeShaderCompiles) + { + shaderCompileTimer.Start(); + } + + bool noisy = false; noisy; + int loglevel = gl_shaderpair_cachelog.GetInt(); + + switch( lang ) + { + case kGLMARB: + { + GLMShaderDesc *arbDesc; + + arbDesc = &m_descs[ kGLMARB ]; + + // make sure no GLSL program is set up + gGL->glUseProgram(0); + // bind our program container to context + GLenum arbTarget = GLMProgTypeToARBEnum( m_type ); + + glSetEnable( arbTarget, true ); // unclear if I need this to compile or just to draw... + + gGL->glBindProgramARB( arbTarget, arbDesc->m_object.arb ); // object created or just re-bound + + char *section = m_text + arbDesc->m_textOffset; + char *lastCharOfSection = section + arbDesc->m_textLength; // actually it's one past the last textual character + lastCharOfSection; + + #if GLMDEBUG + if(noisy) + { + GLMPRINTF((">-D- CGLMProgram::Compile submitting following text for ARB %s program (name %d) ---------------------", + arbTarget == GL_FRAGMENT_PROGRAM_ARB ? "fragment" : "vertex", + arbDesc->m_object.arb )); + + // we don't have a "print this many chars" call yet + // just temporarily null terminate the text we want to print + + char saveChar = *lastCharOfSection; + + *lastCharOfSection= 0; + GLMPRINTTEXT(( section, eDebugDump )); + *lastCharOfSection= saveChar; + + GLMPRINTF(("<-D- CGLMProgram::Compile ARB EOT--" )); + } + #endif + + gGL->glProgramStringARB( arbTarget, GL_PROGRAM_FORMAT_ASCII_ARB, arbDesc->m_textLength, section ); + arbDesc->m_compiled = true; // compiled but not necessarily valid + + CheckValidity( lang ); + // leave it bound n enabled, don't care (draw will sort it all out) + } + break; + + case kGLMGLSL: + { + GLMShaderDesc *glslDesc; + + glslDesc = &m_descs[ kGLMGLSL ]; + + GLenum glslStage = GLMProgTypeToGLSLEnum( m_type ); + glslStage; + + // there's no binding to do for GLSL. but make sure no ARB stuff is bound for tidiness. + glSetEnable( GL_VERTEX_PROGRAM_ARB, false ); + glSetEnable( GL_FRAGMENT_PROGRAM_ARB, false ); // add check errors on these + + gGL->glBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 ); + gGL->glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, 0 ); + + // no GLSL program either + gGL->glUseProgram(0); + + // pump text into GLSL shader object + + char *section = m_text + glslDesc->m_textOffset; + char *lastCharOfSection = section + glslDesc->m_textLength; // actually it's one past the last textual character + lastCharOfSection; + + #if GLMDEBUG + if(noisy) + { + GLMPRINTF((">-D- CGLMProgram::Compile submitting following text for GLSL %s program (name %d) ---------------------", + glslStage == GL_FRAGMENT_SHADER_ARB ? "fragment" : "vertex", + glslDesc->m_object.glsl )); + + // we don't have a "print this many chars" call yet + // just temporarily null terminate the text we want to print + + char saveChar = *lastCharOfSection; + + *lastCharOfSection= 0; + GLMPRINTTEXT(( section, eDebugDump )); + *lastCharOfSection= saveChar; + + GLMPRINTF(("<-D- CGLMProgram::Compile GLSL EOT--" )); + } + #endif + + gGL->glShaderSourceARB( glslDesc->m_object.glsl, 1, (const GLchar **)§ion, &glslDesc->m_textLength); + +#if GLM_FREE_SHADER_TEXT + // Free the shader program text - not needed anymore (GL has its own copy) + if ( m_text && !m_descs[kGLMARB].m_textPresent ) + { + free( m_text ); + m_text = NULL; + } +#endif + + // compile + gGL->glCompileShaderARB( glslDesc->m_object.glsl ); + glslDesc->m_compiled = true; // compiled but not necessarily valid + + // Check shader validity at creation time. This will cause the driver to not be able to + // multi-thread/defer shader compiles, but it is useful for getting error messages on the + // shader when it is compiled + bool bValidateShaderEarly = (CommandLine()->FindParm( "-gl_validate_shader_early" ) != 0); + if (bValidateShaderEarly) + { + CheckValidity( lang ); + } + + if (loglevel>=2) + { + char tempname[128]; + //int tempindex = -1; + //int tempcombo = -1; + + //GetLabelIndexCombo( tempname, sizeof(tempname), &tempindex, &tempcombo ); + //printf("\ncompile: - [ %s/%d/%d ] on GL name %d ", tempname, tempindex, tempcombo, glslDesc->m_object.glsl ); + + + GetComboIndexNameString( tempname, sizeof(tempname) ); + printf("\ncompile: %s on GL name %d ", tempname, glslDesc->m_object.glsl ); + } + } + break; + } + + if (bTimeShaderCompiles) + { + shaderCompileTimer.End(); + gShaderCompileTime += shaderCompileTimer.GetDuration(); + gShaderCompileCount++; + } +} + +#if GLMDEBUG + + bool CGLMProgram::PollForChanges( void ) + { + bool result = false; + if (m_editable) + { + result = m_editable->PollForChanges(); + } + return result; + } + + void CGLMProgram::ReloadStringFromEditable( void ) + { + uint dataSize=0; + char *data=NULL; + + m_editable->GetCurrentText( &data, &dataSize ); + + char *buf = (char *)malloc( dataSize+1 ); // we will NULL terminate it, since the mirror copy might not be + memcpy( buf, data, dataSize ); + buf[dataSize] = 0; + + SetProgramText( buf ); + + free( buf ); + } + + bool CGLMProgram::SyncWithEditable( void ) + { + bool result = false; + + if (m_editable->PollForChanges()) + { + ReloadStringFromEditable(); + + CompileActiveSources(); + + // invalidate shader pair cache entries using this shader.. + m_ctx->m_pairCache->PurgePairsWithShader( this ); + + result = true; // result true means "it changed" + } + return result; + } + +#endif + + +// attributes which are general to both stages +// VP and FP: +// +// 0x88A0 PROGRAM_INSTRUCTIONS_ARB VP FP +// 0x88A1 MAX_PROGRAM_INSTRUCTIONS_ARB VP FP +// 0x88A2 PROGRAM_NATIVE_INSTRUCTIONS_ARB VP FP +// 0x88A3 MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB VP FP +// +// 0x88A4 PROGRAM_TEMPORARIES_ARB VP FP +// 0x88A5 MAX_PROGRAM_TEMPORARIES_ARB VP FP +// 0x88A6 PROGRAM_NATIVE_TEMPORARIES_ARB VP FP +// 0x88A7 MAX_PROGRAM_NATIVE_TEMPORARIES_ARB VP FP +// +// 0x88A8 PROGRAM_PARAMETERS_ARB VP FP +// 0x88A9 MAX_PROGRAM_PARAMETERS_ARB VP FP +// 0x88AA PROGRAM_NATIVE_PARAMETERS_ARB VP FP +// 0x88AB MAX_PROGRAM_NATIVE_PARAMETERS_ARB VP FP +// +// 0x88AC PROGRAM_ATTRIBS_ARB VP FP +// 0x88AD MAX_PROGRAM_ATTRIBS_ARB VP FP +// 0x88AE PROGRAM_NATIVE_ATTRIBS_ARB VP FP +// 0x88AF MAX_PROGRAM_NATIVE_ATTRIBS_ARB VP FP +// +// 0x88B4 MAX_PROGRAM_LOCAL_PARAMETERS_ARB VP FP +// 0x88B5 MAX_PROGRAM_ENV_PARAMETERS_ARB VP FP +// 0x88B6 PROGRAM_UNDER_NATIVE_LIMITS_ARB VP FP +// +// VP only: +// +// 0x88B0 PROGRAM_ADDRESS_REGISTERS_ARB VP +// 0x88B1 MAX_PROGRAM_ADDRESS_REGISTERS_ARB VP +// 0x88B2 PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB VP +// 0x88B3 MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB VP +// +// FP only: +// +// 0x8805 PROGRAM_ALU_INSTRUCTIONS_ARB FP +// 0x880B MAX_PROGRAM_ALU_INSTRUCTIONS_ARB FP +// 0x8808 PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB FP +// 0x880E MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB FP + +// 0x8806 PROGRAM_TEX_INSTRUCTIONS_ARB FP +// 0x880C MAX_PROGRAM_TEX_INSTRUCTIONS_ARB FP +// 0x8809 PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB FP +// 0x880F MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB FP + +// 0x8807 PROGRAM_TEX_INDIRECTIONS_ARB FP +// 0x880D MAX_PROGRAM_TEX_INDIRECTIONS_ARB FP +// 0x880A PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB FP +// 0x8810 MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB FP + +struct GLMShaderLimitDesc +{ + GLenum m_valueEnum; + GLenum m_limitEnum; + const char *m_debugName; + char m_flags; + // m_flags - 0x01 for VP, 0x02 for FP, or set both if applicable to both +}; + +// macro to help make the table of what to check +#ifndef LMD +#define LMD( val, flags ) { GL_PROGRAM_##val##_ARB, GL_MAX_PROGRAM_##val##_ARB, #val, flags } +#else +#error you need to use a different name for this macro. +#endif + +GLMShaderLimitDesc g_glmShaderLimitDescs[] = +{ + // VP and FP.. + LMD( INSTRUCTIONS, 3 ), + LMD( NATIVE_INSTRUCTIONS, 3 ), + LMD( NATIVE_TEMPORARIES, 3 ), + LMD( PARAMETERS, 3 ), + LMD( NATIVE_PARAMETERS, 3 ), + LMD( ATTRIBS, 3 ), + LMD( NATIVE_ATTRIBS, 3 ), + + // VP only.. + LMD( ADDRESS_REGISTERS, 1 ), + LMD( NATIVE_ADDRESS_REGISTERS, 1 ), + + // FP only.. + LMD( ALU_INSTRUCTIONS, 2 ), + LMD( NATIVE_ALU_INSTRUCTIONS, 2 ), + LMD( TEX_INSTRUCTIONS, 2 ), + LMD( NATIVE_TEX_INSTRUCTIONS, 2 ), + LMD( TEX_INDIRECTIONS, 2 ), + LMD( NATIVE_TEX_INDIRECTIONS, 2 ), + + { 0, 0, NULL, 0 } +}; + +#undef LMD + +bool CGLMProgram::CheckValidity( EGLMProgramLang lang ) +{ + static char *targnames[] = { "vertex", "fragment" }; + + bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0); + // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles. + CFastTimer shaderCompileTimer; + if (bTimeShaderCompiles) + { + shaderCompileTimer.Start(); + } + + bool bValid = false; + + switch(lang) + { + case kGLMARB: + { + GLMShaderDesc *arbDesc; + arbDesc = &m_descs[ kGLMARB ]; + + GLenum arbTarget = GLMProgTypeToARBEnum( m_type ); + + Assert( arbDesc->m_compiled ); + + arbDesc->m_valid = true; // assume success til we see otherwise + + // assume program is bound. is there anything wrong with it ? + + GLint isNative=0; + gGL->glGetProgramivARB( arbTarget, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &isNative ); + + // If the program is over the hardware's limits, print out some information + if (isNative!=1) + { + arbDesc->m_valid = false; + + // check everything we can check + char checkmask = (1<m_valueEnum !=0; desc++ ) + { + if ( desc->m_flags & checkmask ) + { + // test it + GLint value = 0; + GLint limit = 0; + gGL->glGetProgramivARB(arbTarget, desc->m_valueEnum, &value); + + gGL->glGetProgramivARB(arbTarget, desc->m_limitEnum, &limit); + + if (value > limit) + { + GLMPRINTF(("-D- Invalid %s program: program has %d %s; limit is %d", targnames[ m_type ], value, desc->m_debugName, limit )); + } + } + } + } + + // syntax error check + GLint errorLine; + gGL->glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errorLine ); + + if ( errorLine!=-1 ) + { + const GLubyte* errorString = gGL->glGetString(GL_PROGRAM_ERROR_STRING_ARB); errorString; + GLMPRINTF(( "-D- Syntax error in ARB %s program: %s",targnames[ m_type ], errorString )); + arbDesc->m_valid = false; + } + if (!arbDesc->m_valid) + { + char *temp = strdup(m_text); + temp[ arbDesc->m_textOffset + arbDesc->m_textLength ] = 0; + GLMPRINTF(("-D- ----- ARB compile failed; bad source follows -----" )); + GLMPRINTTEXT(( temp + arbDesc->m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES )); + GLMPRINTF(("-D- -----end-----" )); + free( temp ); + } + + bValid = arbDesc->m_valid; + } + break; + + case kGLMGLSL: + { + GLMShaderDesc *glslDesc; + GLcharARB *logString = NULL; + glslDesc = &m_descs[ kGLMGLSL ]; + + GLenum glslStage = GLMProgTypeToGLSLEnum( m_type ); glslStage; + + Assert( glslDesc->m_compiled ); + + glslDesc->m_valid = true; // assume success til we see otherwise + + // GLSL error check + int compiled = 0, length = 0, laux = 0; + + gGL->glGetObjectParameterivARB( (GLhandleARB)glslDesc->m_object.glsl, GL_OBJECT_COMPILE_STATUS_ARB, &compiled); + gGL->glGetObjectParameterivARB( (GLhandleARB)glslDesc->m_object.glsl, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length); + if ( length > 0 ) + { + logString = (GLcharARB *)malloc(length * sizeof(GLcharARB)); + gGL->glGetInfoLogARB((GLhandleARB)glslDesc->m_object.glsl, length, &laux, logString); + } + // we may not be able to check "native limits" stuff until link time. meh + + if (!compiled) + { + glslDesc->m_valid = false; + } + + if (!glslDesc->m_valid) + { + GLMPRINTF(("-D- ----- GLSL compile failed: \n %s \n",logString )); +#if !GLM_FREE_SHADER_TEXT + char *temp = strdup(m_text); + temp[ glslDesc->m_textOffset + glslDesc->m_textLength ] = 0; + GLMPRINTTEXT(( temp + glslDesc->m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES )); + free( temp ); +#endif + GLMPRINTF(("-D- -----end-----" )); + } + + if ( logString ) + free( logString ); + + bValid = glslDesc->m_valid; + } + break; + } + + if ( !bValid ) + { + GLMDebugPrintf( "Compile of \"%s\" Failed:\n%s\n", m_shaderName, m_text ? m_text : "" ); + } + AssertOnce( bValid ); + + if (bTimeShaderCompiles) + { + shaderCompileTimer.End(); + gShaderCompileQueryTime += shaderCompileTimer.GetDuration(); + } + + return bValid; +} + +void CGLMProgram::LogSlow( EGLMProgramLang lang ) +{ + // find the desc, see if it's marked + GLMShaderDesc *desc = &m_descs[ lang ]; + + if (!desc->m_slowMark) + { +#if !GLM_FREE_SHADER_TEXT + // log it + printf( "\n-------------- Slow %s ( CGLMProgram @ %p, lang %s, name %d ) : \n%s \n", + m_type==kGLMVertexProgram ? "VS" : "FS", + this, + lang==kGLMGLSL ? "GLSL" : "ARB", + (int)(lang==kGLMGLSL ? (int)desc->m_object.glsl : (int)desc->m_object.arb), + m_text + ); +#endif + } + else // complain on a decreasing basis (powers of two) + { + if ( (desc->m_slowMark & (desc->m_slowMark-1)) == 0 ) + { + // short blurb + printf( "\n Slow %s ( CGLMProgram @ %p, lang %s, name %d ) (%d times)", + m_type==kGLMVertexProgram ? "VS" : "FS", + this, + lang==kGLMGLSL ? "GLSL" : "ARB", + (int)(lang==kGLMGLSL ? (int)desc->m_object.glsl : (int)desc->m_object.arb), + desc->m_slowMark+1 + ); + } + } + + // mark it + desc->m_slowMark++; + + +} + +void CGLMProgram::GetLabelIndexCombo ( char *labelOut, int labelOutMaxChars, int *indexOut, int *comboOut ) +{ + // find the label string + // example: + // trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234 + // Done in SetProgramText + + *labelOut = 0; + *indexOut = -1; + + if ((strlen( m_labelName ) != 0)) + { + Q_strncpy( labelOut, m_labelName, labelOutMaxChars ); + *indexOut = m_labelIndex; + *comboOut = m_labelCombo; + } +} + +void CGLMProgram::GetComboIndexNameString ( char *stringOut, int stringOutMaxChars ) // mmmmmmmm-nnnnnnnn-filename +{ + // find the label string + // example: + // trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234 + // Done in SetProgramText + + *stringOut = 0; + + int len = strlen( m_labelName ); + + if ( (len+20) < stringOutMaxChars ) + { + // output formatted version + sprintf( stringOut, "%08X-%08X-%s", m_labelCombo, m_labelIndex, m_labelName ); + } +} + +//=============================================================================== + + +CGLMShaderPair::CGLMShaderPair( GLMContext *ctx ) +{ + m_ctx = ctx; + m_vertexProg = m_fragmentProg = NULL; + + m_program = gGL->glCreateProgramObjectARB(); + + m_locVertexParams = -1; + m_locVertexBoneParams = -1; + m_locVertexScreenParams = -1; + m_nScreenWidthHeight = 0xFFFFFFFF; + m_locVertexInteger0 = -1; // "i0" + memset( m_locVertexBool, 0xFF, sizeof( m_locVertexBool ) ); + memset( m_locFragmentBool, 0xFF, sizeof( m_locFragmentBool ) ); + m_bHasBoolOrIntUniforms = false; + + m_locFragmentParams = -1; + + m_locFragmentFakeSRGBEnable = -1; + m_fakeSRGBEnableValue = -1.0f; + + memset( m_locSamplers, 0xFF, sizeof( m_locSamplers ) ); + + m_valid = false; + m_bCheckLinkStatus = false; + m_revision = 0; // bumps to 1 once linked +} + +CGLMShaderPair::~CGLMShaderPair( ) +{ + if (m_program) + { + gGL->glDeleteObjectARB( (GLhandleARB)m_program ); + m_program = 0; + } +} + +bool CGLMShaderPair::ValidateProgramPair() +{ + if ( m_vertexProg && m_vertexProg->m_descs[kGLMGLSL].m_textPresent && !m_vertexProg->m_descs[kGLMGLSL].m_valid ) + { + m_vertexProg->CheckValidity( kGLMGLSL ); + } + if (m_fragmentProg && m_fragmentProg->m_descs[kGLMGLSL].m_textPresent && !m_fragmentProg->m_descs[kGLMGLSL].m_valid) + { + m_fragmentProg->CheckValidity( kGLMGLSL ); + } + + if ( !m_valid && m_bCheckLinkStatus ) + { + bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0); + // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles. + CFastTimer shaderCompileTimer; + if (bTimeShaderCompiles) + { + shaderCompileTimer.Start(); + } + + // check for success + GLint result = 0; + gGL->glGetObjectParameterivARB( m_program, GL_OBJECT_LINK_STATUS_ARB, &result ); // want GL_TRUE + m_bCheckLinkStatus = false; + + if (result == GL_TRUE) + { + // success + + m_valid = true; + m_revision++; + } + else + { + GLint length = 0; + GLint laux = 0; + + // do some digging + gGL->glGetObjectParameterivARB( m_program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length ); + + GLcharARB *logString = (GLcharARB *)malloc( length * sizeof(GLcharARB) ); + gGL->glGetInfoLogARB( m_program, length, &laux, logString ); + + GLMPRINTF( ("-D- ----- GLSL link failed: \n %s ", logString) ); +#if !GLM_FREE_SHADER_TEXT + char *vtemp = strdup( m_vertexProg->m_text ); + vtemp[m_vertexProg->m_descs[kGLMGLSL].m_textOffset + m_vertexProg->m_descs[kGLMGLSL].m_textLength] = 0; + + char *ftemp = strdup( m_fragmentProg->m_text ); + ftemp[m_fragmentProg->m_descs[kGLMGLSL].m_textOffset + m_fragmentProg->m_descs[kGLMGLSL].m_textLength] = 0; + + GLMPRINTF( ("-D- ----- GLSL vertex program selected: %08x (handle %08x)", m_vertexProg, m_vertexProg->m_descs[kGLMGLSL].m_object.glsl) ); + GLMPRINTTEXT( (vtemp + m_vertexProg->m_descs[kGLMGLSL].m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES) ); + + GLMPRINTF( ("-D- ----- GLSL fragment program selected: %08x (handle %08x)", m_fragmentProg, m_fragmentProg->m_descs[kGLMGLSL].m_object.glsl) ); + GLMPRINTTEXT( (ftemp + m_fragmentProg->m_descs[kGLMGLSL].m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES) ); + + free( ftemp ); + free( vtemp ); +#endif + free( logString ); + + GLMPRINTF( ("-D- -----end-----") ); + } + + if (m_valid) + { + gGL->glUseProgram( m_program ); + + m_ctx->NewLinkedProgram(); + + m_locVertexParams = gGL->glGetUniformLocationARB( m_program, "vc" ); + m_locVertexBoneParams = gGL->glGetUniformLocationARB( m_program, "vcbones" ); + m_locVertexScreenParams = gGL->glGetUniformLocationARB( m_program, "vcscreen" ); + m_nScreenWidthHeight = 0xFFFFFFFF; + + m_locVertexInteger0 = gGL->glGetUniformLocationARB( m_program, "i0" ); + + m_bHasBoolOrIntUniforms = false; + if (m_locVertexInteger0 >= 0) + m_bHasBoolOrIntUniforms = true; + + for (uint i = 0; i < cMaxVertexShaderBoolUniforms; i++) + { + char buf[256]; + V_snprintf( buf, sizeof(buf), "b%d", i ); + m_locVertexBool[i] = gGL->glGetUniformLocationARB( m_program, buf ); + if (m_locVertexBool[i] != -1) + m_bHasBoolOrIntUniforms = true; + } + + for (uint i = 0; i < cMaxFragmentShaderBoolUniforms; i++) + { + char buf[256]; + V_snprintf( buf, sizeof(buf), "fb%d", i ); + m_locFragmentBool[i] = gGL->glGetUniformLocationARB( m_program, buf ); + if (m_locFragmentBool[i] != -1) + m_bHasBoolOrIntUniforms = true; + } + + m_locFragmentParams = gGL->glGetUniformLocationARB( m_program, "pc" ); + + for (uint i = 0; i < kGLMNumProgramTypes; i++) + { + m_NumUniformBufferParams[i] = 0; + + if (i == kGLMVertexProgram) + { + if (m_locVertexParams < 0) + continue; + } + else if (m_locFragmentParams < 0) + continue; + + const uint nNum = (i == kGLMVertexProgram) ? m_vertexProg->m_descs[kGLMGLSL].m_highWater : m_fragmentProg->m_descs[kGLMGLSL].m_highWater; + + uint j; + for (j = 0; j < nNum; j++) + { + char buf[256]; + V_snprintf( buf, sizeof(buf), "%cc[%i]", "vp"[i], j ); + // Grab the handle of each array element, so we can more efficiently update array elements in the middle. + int l = m_UniformBufferParams[i][j] = gGL->glGetUniformLocationARB( m_program, buf ); + if (l < 0) + break; + } + + m_NumUniformBufferParams[i] = j; + } + + m_locFragmentFakeSRGBEnable = gGL->glGetUniformLocationARB( m_program, "flSRGBWrite" ); + m_fakeSRGBEnableValue = -1.0f; + + for (int sampler = 0; sampler < 16; sampler++) + { + char tmp[16]; + sprintf( tmp, "sampler%d", sampler ); // sampler0 .. sampler1.. etc + + GLint nLoc = gGL->glGetUniformLocationARB( m_program, tmp ); + m_locSamplers[sampler] = nLoc; + if (nLoc >= 0) + { + gGL->glUniform1iARB( nLoc, sampler ); + } + } + } + else + { + m_locVertexParams = -1; + m_locVertexBoneParams = -1; + m_locVertexScreenParams = -1; + m_nScreenWidthHeight = 0xFFFFFFFF; + + m_locVertexInteger0 = -1; + memset( m_locVertexBool, 0xFF, sizeof(m_locVertexBool) ); + memset( m_locFragmentBool, 0xFF, sizeof(m_locFragmentBool) ); + m_bHasBoolOrIntUniforms = false; + + m_locFragmentParams = -1; + m_locFragmentFakeSRGBEnable = -1; + m_fakeSRGBEnableValue = -999; + + memset( m_locSamplers, 0xFF, sizeof(m_locSamplers) ); + + m_revision = 0; + } + + if (bTimeShaderCompiles) + { + shaderCompileTimer.End(); + gShaderLinkQueryTime += shaderCompileTimer.GetDuration(); + } + } + + return m_valid; +} + +// glUseProgram() will be called as a side effect! +bool CGLMShaderPair::SetProgramPair( CGLMProgram *vp, CGLMProgram *fp ) +{ + bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0); + // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles. + CFastTimer shaderCompileTimer; + if (bTimeShaderCompiles) + { + shaderCompileTimer.Start(); + } + + m_valid = false; // assume failure + + // No need to check that vp and fp are valid at this point (ie shader compile succeed) + // It is permissible to attach a shader object to a program before source code has been loaded + // into the shader object or before the shader object has been compiled. The program won't + // link if one or more of the shader objects has not been successfully compiled. + // (Defer querying the compile and link status to take advantage of GLSL shaders + // building in parallels) + bool vpgood = (vp != NULL); + bool fpgood = (fp != NULL); + + if ( !fpgood ) + { + // fragment side allowed to be "null". + fp = m_ctx->m_pNullFragmentProgram; + } + + if ( vpgood && fpgood ) + { + if ( vp->m_nCentroidMask != fp->m_nCentroidMask ) + { + Warning( "CGLMShaderPair::SetProgramPair: Centroid masks differ at link time of vertex shader %s and pixel shader %s!\n", + vp->m_shaderName, fp->m_shaderName ); + } + + // attempt link. but first, detach any previously attached programs + if (m_vertexProg) + { + gGL->glDetachObjectARB(m_program, m_vertexProg->m_descs[kGLMGLSL].m_object.glsl); + m_vertexProg = NULL; + } + + if (m_fragmentProg) + { + gGL->glDetachObjectARB(m_program, m_fragmentProg->m_descs[kGLMGLSL].m_object.glsl); + m_fragmentProg = NULL; + } + + // now attach + + gGL->glAttachObjectARB( m_program, vp->m_descs[kGLMGLSL].m_object.glsl ); + m_vertexProg = vp; + + gGL->glAttachObjectARB( m_program, fp->m_descs[kGLMGLSL].m_object.glsl ); + m_fragmentProg = fp; + + // force the locations for input attributes v0-vN to be at locations 0-N + // use the vertex attrib map to know which slots are live or not... oy! we don't have that map yet... but it's OK. + // fallback - just force v0-v15 to land in locations 0-15 as a standard. + + for( int i = 0; i < 16; i++ ) + { + char tmp[16]; + sprintf(tmp, "v%d", i); // v0 v1 v2 ... et al + + gGL->glBindAttribLocationARB( m_program, i, tmp ); + } +#if !GLM_FREE_SHADER_TEXT + if (CommandLine()->CheckParm("-dumpallshaders")) + { + // Dump all shaders, for debugging. + FILE* pFile = fopen("shaderdump.txt", "a+"); + if (pFile) + { + fprintf(pFile, "--------------VP:%s\n%s\n", vp->m_shaderName, vp->m_text); + fprintf(pFile, "--------------FP:%s\n%s\n", fp->m_shaderName, fp->m_text); + fclose(pFile); + } + } +#endif + + // now link + gGL->glLinkProgramARB( m_program ); + m_bCheckLinkStatus = true; + } + else + { + // fail + Assert(!"Can't link these programs"); + } + + // Check shader validity at creation time. This will cause the driver to not be able to + // multi-thread/defer shader compiles, but it is useful for getting error messages on the + // shader when it is compiled + bool bValidateShaderEarly = (CommandLine()->FindParm( "-gl_validate_shader_early" ) != 0); + if (bValidateShaderEarly) + { + ValidateProgramPair(); + } + + if (bTimeShaderCompiles) + { + shaderCompileTimer.End(); + gShaderLinkTime += shaderCompileTimer.GetDuration(); + gShaderLinkCount++; + } + + return m_valid; +} + + +bool CGLMShaderPair::RefreshProgramPair ( void ) +{ + // re-link and re-query the uniforms. + + // since SetProgramPair knows how to detach previously attached shader objects, just pass the same ones in again. + CGLMProgram *vp = m_vertexProg; + CGLMProgram *fp = m_fragmentProg; + + bool vpgood = (vp!=NULL) && (vp->m_descs[ kGLMGLSL ].m_valid); + bool fpgood = (fp!=NULL) && (fp->m_descs[ kGLMGLSL ].m_valid); + + if (vpgood && fpgood) + { + SetProgramPair( vp, fp ); + } + else + { + DebuggerBreak(); + return false; + } + + return false; +} + + +//=============================================================================== + +CGLMShaderPairCache::CGLMShaderPairCache( GLMContext *ctx ) +{ + m_ctx = ctx; + + m_mark = 1; + + m_rowsLg2 = gl_shaderpair_cacherows_lg2.GetInt(); + if (m_rowsLg2 < 10) + m_rowsLg2 = 10; + m_rows = 1<Purge(); + (void)purgeResult; + Assert( !purgeResult ); + + if (m_entries) + { + free( m_entries ); + m_entries = NULL; + } + + if (m_evictions) + { + free( m_evictions ); + m_evictions = NULL; + } + +#if GL_SHADER_PAIR_CACHE_STATS + if (m_hits) + { + free( m_hits ); + m_hits = NULL; + } +#endif +} + +// Set this convar internally to build or add to the shader pair cache file (link hints) +// We really only expect this to work on POSIX +static ConVar glm_cacheprograms( "glm_cacheprograms", "0", FCVAR_DEVELOPMENTONLY ); + +#define PROGRAM_CACHE_FILE "program_cache.cfg" + +static void WriteToProgramCache( CGLMShaderPair *pair ) +{ + KeyValues *pProgramCache = new KeyValues( "programcache" ); + pProgramCache->LoadFromFile( g_pFullFileSystem, PROGRAM_CACHE_FILE, "MOD" ); + + if ( !pProgramCache ) + { + Warning( "Could not write to program cache file!\n" ); + return; + } + + // extract values of interest which represent a pair of shaders + + char vprogramName[128]; + int vprogramStaticIndex = -1; + int vprogramDynamicIndex = -1; + pair->m_vertexProg->GetLabelIndexCombo( vprogramName, sizeof(vprogramName), &vprogramStaticIndex, &vprogramDynamicIndex ); + + + char pprogramName[128]; + int pprogramStaticIndex = -1; + int pprogramDynamicIndex = -1; + pair->m_fragmentProg->GetLabelIndexCombo( pprogramName, sizeof(pprogramName), &pprogramStaticIndex, &pprogramDynamicIndex ); + + // make up a key - this thing is really a list of tuples, so need not be keyed by anything particular + KeyValues *pProgramKey = pProgramCache->CreateNewKey(); + Assert( pProgramKey ); + + pProgramKey->SetString ( "vs", vprogramName ); + pProgramKey->SetString ( "ps", pprogramName ); + + pProgramKey->SetInt ( "vs_static", vprogramStaticIndex ); + pProgramKey->SetInt ( "ps_static", pprogramStaticIndex ); + + pProgramKey->SetInt ( "vs_dynamic", vprogramDynamicIndex ); + pProgramKey->SetInt ( "ps_dynamic", pprogramDynamicIndex ); + + pProgramCache->SaveToFile( g_pFullFileSystem, PROGRAM_CACHE_FILE, "MOD" ); + pProgramCache->deleteThis(); +} + +// Calls glUseProgram() as a side effect +CGLMShaderPair *CGLMShaderPairCache::SelectShaderPairInternal( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits, int rowIndex ) +{ + CGLMShaderPair *result = NULL; + +#if GLMDEBUG + int loglevel = gl_shaderpair_cachelog.GetInt(); +#else + const int loglevel = 0; +#endif + + char vtempname[128]; + int vtempindex = -1; vtempindex; + int vtempcombo = -1; vtempcombo; + + char ptempname[128]; + int ptempindex = -1; ptempindex; + int ptempcombo = -1; ptempcombo; + + CGLMPairCacheEntry *row = HashRowPtr( rowIndex ); + + // Re-probe to find the oldest and first unoccupied entry (this func should be very rarely called if the cache is properly configured so re-scanning shouldn't matter). + int hitway, emptyway, oldestway; + HashRowProbe( row, vp, fp, extraKeyBits, hitway, emptyway, oldestway ); + Assert( hitway == -1 ); + + // we missed. if there is no empty way, then somebody's getting evicted. + int destway = -1; + + if (emptyway>=0) + { + destway = emptyway; + + if (loglevel >= 2) // misses logged at level 3 and higher + { + printf("\nSSP: miss - row %05d - ", rowIndex ); + } + } + else + { + // evict the oldest way + Assert( oldestway >= 0); // better not come back negative + + CGLMPairCacheEntry *evict = row + oldestway; + + Assert( evict->m_pair != NULL ); + Assert( evict->m_pair != m_ctx->m_pBoundPair ); // just check + + ///////////////////////FIXME may need to do a shoot-down if the pair being evicted is currently active in the context + + m_evictions[ rowIndex ]++; + + // log eviction if desired + if (loglevel >= 2) // misses logged at level 3 and higher + { + //evict->m_vertexProg->GetLabelIndexCombo( vtempname, sizeof(vtempname), &vtempindex, &vtempcombo ); + //evict->m_fragmentProg->GetLabelIndexCombo( ptempname, sizeof(ptempname), &ptempindex, &ptempcombo ); + //printf("\nSSP: miss - row %05d - [ %s/%d/%d %s/%d/%d ]'s %d'th eviction - ", rowIndex, vtempname, vtempindex, vtempcombo, ptempname, ptempindex, ptempcombo, m_evictions[ rowIndex ] ); + + evict->m_vertexProg->GetComboIndexNameString( vtempname, sizeof(vtempname) ); + evict->m_fragmentProg->GetComboIndexNameString( ptempname, sizeof(ptempname) ); + printf("\nSSP: miss - row %05d - [ %s + %s ]'s %d'th eviction - ", rowIndex, vtempname, ptempname, m_evictions[ rowIndex ] ); + } + + delete evict->m_pair; evict->m_pair = NULL; + memset( evict, 0, sizeof(*evict) ); + + destway = oldestway; + } + + // make the new entry + CGLMPairCacheEntry *newentry = row + destway; + + newentry->m_lastMark = m_mark; + newentry->m_vertexProg = vp; + newentry->m_fragmentProg = fp; + newentry->m_extraKeyBits = extraKeyBits; + newentry->m_pair = new CGLMShaderPair( m_ctx ); + Assert( newentry->m_pair ); + newentry->m_pair->SetProgramPair( vp, fp ); + + if (loglevel >= 2) // say a little bit more + { + //newentry->m_vertexProg->GetLabelIndexCombo( vtempname, sizeof(vtempname), &vtempindex, &vtempcombo ); + //newentry->m_fragmentProg->GetLabelIndexCombo( ptempname, sizeof(ptempname), &ptempindex, &ptempcombo ); + //printf("new [ %s/%d/%d %s/%d/%d ]", vtempname, vtempindex, vtempcombo, ptempname, ptempindex, ptempcombo ); + + newentry->m_vertexProg->GetComboIndexNameString( vtempname, sizeof(vtempname) ); + newentry->m_fragmentProg->GetComboIndexNameString( ptempname, sizeof(ptempname) ); + printf("new [ %s + %s ]", vtempname, ptempname ); + } + + m_mark = m_mark+1; + if (!m_mark) // somewhat unlikely this will ever be reached.. but we need to avoid zero as a mark value + { + m_mark = 1; + } + + result = newentry->m_pair; + + if (glm_cacheprograms.GetInt()) + { + WriteToProgramCache( newentry->m_pair ); + } + + return result; +} + +void CGLMShaderPairCache::QueryShaderPair( int index, GLMShaderPairInfo *infoOut ) +{ + if ( (index<0) || ( static_cast(index) >= (m_rows*m_ways) ) ) + { + // no such location + memset( infoOut, 0, sizeof(*infoOut) ); + + infoOut->m_status = -1; + } + else + { + // locate the entry, and see if an active pair is present. + // if so, extract info and return with m_status=1. + // if not, exit with m_status = 0. + + CGLMPairCacheEntry *entry = &m_entries[index]; + + if (entry->m_pair) + { + // live + // extract values of interest for caller + + entry->m_pair->m_vertexProg->GetLabelIndexCombo ( infoOut->m_vsName, sizeof(infoOut->m_vsName), &infoOut->m_vsStaticIndex, &infoOut->m_vsDynamicIndex ); + entry->m_pair->m_fragmentProg->GetLabelIndexCombo ( infoOut->m_psName, sizeof(infoOut->m_psName), &infoOut->m_psStaticIndex, &infoOut->m_psDynamicIndex ); + + infoOut->m_status = 1; + } + else + { + // not + memset( infoOut, 0, sizeof(*infoOut) ); + infoOut->m_status = 0; + } + } +} + +bool CGLMShaderPairCache::PurgePairsWithShader( CGLMProgram *prog ) +{ + bool result = false; + + // walk all rows*ways + int limit = m_rows * m_ways; + for( int i=0; i < limit; i++) + { + CGLMPairCacheEntry *entry = &m_entries[i]; + + if (entry->m_pair) + { + //scrub it, if not currently bound, and if the supplied shader matches either stage + if ( (entry->m_vertexProg==prog) || (entry->m_fragmentProg==prog) ) + { + // found it, but does it conflict with bound pair ? + if (entry->m_pair == m_ctx->m_pBoundPair) + { + m_ctx->m_pBoundPair = NULL; + m_ctx->m_bDirtyPrograms = true; + } + delete entry->m_pair; + memset( entry, 0, sizeof(*entry) ); + } + } + } + return result; +} + +bool CGLMShaderPairCache::Purge( void ) +{ + bool result = false; + + // walk all rows*ways + int limit = m_rows * m_ways; + for( int i=0; i < limit; i++) + { + CGLMPairCacheEntry *entry = &m_entries[i]; + + if (entry->m_pair) + { + //scrub it, unless the pair is the currently bound pair in our parent glm context + if (entry->m_pair != m_ctx->m_pBoundPair) + { + delete entry->m_pair; + memset( entry, 0, sizeof(*entry) ); + } + else + { + result = true; + } + } + } + return result; +} + +void CGLMShaderPairCache::DumpStats ( void ) +{ +#if GL_SHADER_PAIR_CACHE_STATS + printf("\n------------------\npair cache stats"); + int total = 0; + for( uint row=0; row < m_rows; row++ ) + { + if ( (m_evictions[row] != 0) || (m_hits[row] != 0) ) + { + printf("\n row %d : %d evictions, %d hits",row,m_evictions[row], m_hits[row]); + total += m_evictions[row]; + } + } + printf("\n\npair cache evictions: %d\n-----------------------\n",total ); +#endif +} + + //=============================== + + diff --git a/togles/linuxwin/cglmquery.cpp b/togles/linuxwin/cglmquery.cpp new file mode 100644 index 00000000..199780a4 --- /dev/null +++ b/togles/linuxwin/cglmquery.cpp @@ -0,0 +1,363 @@ +//========= 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. +// +// cglmquery.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +#ifndef _WIN32 + #include +#endif + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +//=============================================================================== + +// http://www.opengl.org/registry/specs/ARB/occlusion_query.txt +// Workaround for "Calling either GenQueriesARB or DeleteQueriesARB while any query of any target is active causes an INVALID_OPERATION error to be generated." +uint CGLMQuery::s_nTotalOcclusionQueryCreatesOrDeletes; + +extern ConVar gl_errorcheckall; +extern ConVar gl_errorcheckqueries; +extern ConVar gl_errorchecknone; + +// how many microseconds to wait after a failed query-available test +// presently on MTGL this doesn't happen, but it could change, keep this handy +ConVar gl_nullqueries( "gl_nullqueries", "0" ); + + +//=============================================================================== + +CGLMQuery::CGLMQuery( GLMContext *ctx, GLMQueryParams *params ) +{ + // get the type of query requested + // generate name(s) needed + // set initial state appropriately + + m_ctx = ctx; + m_params = *params; + + m_name = 0; + m_syncobj = 0; + + m_started = m_stopped = m_done = false; + + m_nullQuery = false; + // assume value of convar at start time + // does not change during individual query lifetime + // started null = stays null + // started live = stays live + + switch(m_params.m_type) + { + case EOcclusion: + { + //make an occlusion query (and a fence to go with it) + gGL->glGenQueriesARB( 1, &m_name ); + s_nTotalOcclusionQueryCreatesOrDeletes++; + GLMPRINTF(("-A- CGLMQuery(OQ) created name %d", m_name)); + } + break; + + case EFence: + //make a fence - no aux fence needed + + m_syncobj = 0; + + if (gGL->m_bHave_GL_ARB_sync) + { /* GL_ARB_sync doesn't separate gen and set, so we do glFenceSync() later. */ } + else if (gGL->m_bHave_GL_NV_fence) + gGL->glGenFencesNV(1, &m_name ); + else if (gGL->m_bHave_GL_APPLE_fence) + gGL->glGenFencesAPPLE(1, &m_name ); + + GLMPRINTF(("-A- CGLMQuery(fence) created name %d", m_name)); + break; + } + +} + +CGLMQuery::~CGLMQuery() +{ + GLMPRINTF(("-A-> ~CGLMQuery")); + + // make sure query has completed (might not be necessary) + // delete the name(s) + + switch(m_params.m_type) + { + case EOcclusion: + { + // do a finish occlusion query ? + GLMPRINTF(("-A- ~CGLMQuery(OQ) deleting name %d", m_name)); + gGL->glDeleteQueriesARB(1, &m_name ); + s_nTotalOcclusionQueryCreatesOrDeletes++; + } + break; + + case EFence: + { + // do a finish fence ? + GLMPRINTF(("-A- ~CGLMQuery(fence) deleting name %llu", gGL->m_bHave_GL_ARB_sync ? (unsigned long long) m_syncobj : (unsigned long long) m_name)); +#ifdef HAVE_GL_ARB_SYNC + if (gGL->m_bHave_GL_ARB_sync) + gGL->glDeleteSync( m_syncobj ); + else +#endif + if (gGL->m_bHave_GL_NV_fence) + gGL->glDeleteFencesNV(1, &m_name ); + else if (gGL->m_bHave_GL_APPLE_fence) + gGL->glDeleteFencesAPPLE(1, &m_name ); + } + break; + } + + m_name = 0; + m_syncobj = 0; + + GLMPRINTF(("-A-< ~CGLMQuery")); +} + + + + +void CGLMQuery::Start( void ) // "start counting" +{ + m_nullQuery = (gl_nullqueries.GetInt() != 0); // latch value for remainder of query life + + m_started = true; + m_stopped = false; + m_done = false; + + switch(m_params.m_type) + { + case EOcclusion: + { + if (m_nullQuery) + { + // do nothing.. + } + else + { + gGL->glBeginQueryARB( GL_SAMPLES_PASSED_ARB, m_name ); + } + } + break; + + case EFence: +#ifdef HAVE_GL_ARB_SYNC + if (gGL->m_bHave_GL_ARB_sync) + { + if (m_syncobj != 0) gGL->glDeleteSync(m_syncobj); + m_syncobj = gGL->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + else +#endif + if (gGL->m_bHave_GL_NV_fence) + gGL->glSetFenceNV( m_name, GL_ALL_COMPLETED_NV ); + else if (gGL->m_bHave_GL_APPLE_fence) + gGL->glSetFenceAPPLE( m_name ); + + m_stopped = true; // caller should not call Stop on a fence, it self-stops + break; + } +} + +void CGLMQuery::Stop( void ) // "stop counting" +{ + Assert(m_started); + + if ( m_stopped ) + return; + + switch(m_params.m_type) + { + case EOcclusion: + { + if (m_nullQuery) + { + // do nothing.. + } + else + { + gGL->glEndQueryARB( GL_SAMPLES_PASSED_ARB ); // we are only putting the request-to-stop-counting into the cmd stream. + } + } + break; + + case EFence: + // nop - you don't "end" a fence, you just test it and/or finish it out in Complete + break; + } + + m_stopped = true; +} + +bool CGLMQuery::IsDone( void ) +{ + Assert(m_started); + Assert(m_stopped); + + if(!m_done) // you can ask more than once, but we only check until it comes back as done. + { + // on occlusion: glGetQueryObjectivARB - large cost on pre SLGU, cheap after + // on fence: glTestFence* on the fence + switch(m_params.m_type) + { + case EOcclusion: // just test the fence that was set after the query begin + { + if (m_nullQuery) + { + // do almost nothing.. but claim work is complete + m_done = true; + } + else + { + // prepare to pay a big price on drivers prior to 10.6.4+SLGU + + GLint available = 0; + gGL->glGetQueryObjectivARB(m_name, GL_QUERY_RESULT_AVAILABLE_ARB, &available ); + + m_done = (available != 0); + } + } + break; + + case EFence: + { +#ifdef HAVE_GL_ARB_SYNC + if (gGL->m_bHave_GL_ARB_sync) + m_done = (gGL->glClientWaitSync( m_syncobj, 0, 0 ) == GL_ALREADY_SIGNALED); + else +#endif + if ( m_name == 0 ) + m_done = true; + else if (gGL->m_bHave_GL_NV_fence) + m_done = gGL->glTestFenceNV( m_name ) != 0; + else if (gGL->m_bHave_GL_APPLE_fence) + m_done = gGL->glTestFenceAPPLE( m_name ) != 0; + + if (m_done) + { + if (gGL->m_bHave_GL_ARB_sync) + { /* no-op; we already know it's set to GL_ALREADY_SIGNALED. */ } + else + { + if (gGL->m_bHave_GL_NV_fence) + gGL->glFinishFenceNV( m_name ); // no set fence goes un-finished + else if (gGL->m_bHave_GL_APPLE_fence) + gGL->glFinishFenceAPPLE( m_name ); // no set fence goes un-finished + } + } + } + break; + } + } + + return m_done; +} + +void CGLMQuery::Complete( uint *result ) +{ + uint resultval = 0; + //bool bogus_available = false; + + // blocking call if not done + Assert(m_started); + Assert(m_stopped); + + switch(m_params.m_type) + { + case EOcclusion: + { + if (m_nullQuery) + { + m_done = true; + resultval = 0; // we did say "null queries..." + } + else + { + gGL->glGetQueryObjectuivARB( m_name, GL_QUERY_RESULT_ARB, &resultval); + m_done = true; + } + } + break; + + case EFence: + { + if(!m_done) + { +#ifdef HAVE_GL_ARB_SYNC + if (gGL->m_bHave_GL_ARB_sync) + { + if (gGL->glClientWaitSync( m_syncobj, 0, 0 ) != GL_ALREADY_SIGNALED) + { + GLenum syncstate; + do { + const GLuint64 timeout = 10 * ((GLuint64)1000 * 1000 * 1000); // 10 seconds in nanoseconds. + (void)timeout; + syncstate = gGL->glClientWaitSync( m_syncobj, GL_SYNC_FLUSH_COMMANDS_BIT, 0 ); + } while (syncstate == GL_TIMEOUT_EXPIRED); // any errors or success break out of this loop. + } + } + else +#endif + if (gGL->m_bHave_GL_NV_fence) + gGL->glFinishFenceNV( m_name ); + else if (gGL->m_bHave_GL_APPLE_fence) + gGL->glFinishFenceAPPLE( m_name ); + + m_done = true; // for clarity or if they try to Complete twice + } + } + break; + } + + Assert( m_done ); + + // reset state for re-use - i.e. you have to call Complete if you want to re-use the object + m_started = m_stopped = m_done = false; + + if (result) // caller may pass NULL if not interested in result, for example to clear a fence + { + *result = resultval; + } +} + + + + // accessors for the started/stopped state +bool CGLMQuery::IsStarted ( void ) +{ + return m_started; +} + +bool CGLMQuery::IsStopped ( void ) +{ + return m_stopped; +} + diff --git a/togles/linuxwin/cglmtex.cpp b/togles/linuxwin/cglmtex.cpp new file mode 100644 index 00000000..30eb9e46 --- /dev/null +++ b/togles/linuxwin/cglmtex.cpp @@ -0,0 +1,1990 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// cglmtex.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +#include "tier0/icommandline.h" +#include "glmtexinlines.h" + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +#if defined(OSX) +#include "appframework/ilaunchermgr.h" +extern ILauncherMgr *g_pLauncherMgr; +#endif + +//=============================================================================== + +#if GLMDEBUG +CGLMTex *g_pFirstCGMLTex; +#endif + +ConVar gl_pow2_tempmem( "gl_pow2_tempmem", "0", FCVAR_INTERNAL_USE, + "If set, use power-of-two allocations for temporary texture memory during uploads. " + "May help with fragmentation on certain systems caused by heavy churn of large allocations." ); + +#define TEXSPACE_LOGGING 0 + +// encoding layout to an index where the bits read +// 4 : 1 if compressed +// 2 : 1 if not power of two +// 1 : 1 if mipmapped + +bool pwroftwo (int val ) +{ + return (val & (val-1)) == 0; +} + +int sEncodeLayoutAsIndex( GLMTexLayoutKey *key ) +{ + int index = 0; + + if (key->m_texFlags & kGLMTexMipped) + { + index |= 1; + } + + if ( ! ( pwroftwo(key->m_xSize) && pwroftwo(key->m_ySize) && pwroftwo(key->m_zSize) ) ) + { + // if not all power of two + index |= 2; + } + + if (GetFormatDesc( key->m_texFormat )->m_chunkSize >1 ) + { + index |= 4; + } + + return index; +} + +static unsigned long g_texGlobalBytes[8]; + +//=============================================================================== + +const GLMTexFormatDesc g_formatDescTable[] = +{ + // not yet handled by this table: + // D3DFMT_INDEX16, D3DFMT_VERTEXDATA // D3DFMT_INDEX32, + // WTF { D3DFMT_R5G6R5 ???, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 1, 2 }, + // WTF { D3DFMT_A ???, GL_ALPHA8, GL_ALPHA, GL_UNSIGNED_BYTE, 1, 1 }, + // ??? D3DFMT_V8U8, + // ??? D3DFMT_Q8W8V8U8, + // ??? D3DFMT_X8L8V8U8, + // ??? D3DFMT_R32F, + // ??? D3DFMT_D24X4S4 unsure how to handle or if it is ever used.. + // ??? D3DFMT_D15S1 ever used ? + // ??? D3DFMT_D24X8 ever used? + + // summ-name d3d-format gl-int-format gl-int-format-srgb gl-data-format gl-data-type chunksize, bytes-per-sqchunk + { "_D16", D3DFMT_D16, GL_DEPTH_COMPONENT16, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 1, 2 }, + { "_D24X8", D3DFMT_D24X8, GL_DEPTH_COMPONENT24, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 1, 4 }, // ??? unsure on this one + { "_D24S8", D3DFMT_D24S8, GL_DEPTH24_STENCIL8_EXT, 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, 1, 4 }, + + { "_A8R8G8B8", D3DFMT_A8R8G8B8, GL_RGBA8, GL_SRGB8_ALPHA8_EXT, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 1, 4 }, + { "_A4R4G4B4", D3DFMT_A4R4G4B4, GL_RGBA4, 0, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV, 1, 2 }, + { "_X8R8G8B8", D3DFMT_X8R8G8B8, GL_RGB8, GL_SRGB8_EXT, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 1, 4 }, + + { "_X1R5G5B5", D3DFMT_X1R5G5B5, GL_RGB5, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 1, 2 }, + { "_A1R5G5B5", D3DFMT_A1R5G5B5, GL_RGB5_A1, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 1, 2 }, + + { "_L8", D3DFMT_L8, GL_LUMINANCE8, GL_SLUMINANCE8_EXT, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1, 1 }, + { "_A8L8", D3DFMT_A8L8, GL_LUMINANCE8_ALPHA8, GL_SLUMINANCE8_ALPHA8_EXT, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 1, 2 }, + + { "_DXT1", D3DFMT_DXT1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_BYTE, 4, 8 }, + { "_DXT3", D3DFMT_DXT3, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_BYTE, 4, 16 }, + { "_DXT5", D3DFMT_DXT5, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_BYTE, 4, 16 }, + + { "_A16B16G16R16F", D3DFMT_A16B16G16R16F, GL_RGBA16F_ARB, 0, GL_RGBA, GL_HALF_FLOAT_ARB, 1, 8 }, + { "_A16B16G16R16", D3DFMT_A16B16G16R16, GL_RGBA16, 0, GL_RGBA, GL_UNSIGNED_SHORT, 1, 8 }, // 16bpc integer tex + + { "_A32B32G32R32F", D3DFMT_A32B32G32R32F, GL_RGBA32F_ARB, 0, GL_RGBA, GL_FLOAT, 1, 16 }, + + { "_R8G8B8", D3DFMT_R8G8B8, GL_RGB8, GL_SRGB8_EXT, GL_BGR, GL_UNSIGNED_BYTE, 1, 3 }, + + { "_A8", D3DFMT_A8, GL_ALPHA8, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 1, 1 }, + { "_R5G6B5", D3DFMT_R5G6B5, GL_RGB, GL_SRGB_EXT, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 1, 2 }, + + // fakey tex formats: the stated GL format and the memory layout may not agree (U8V8 for example) + + // _Q8W8V8U8 we just pass through as RGBA bytes. Shader does scale/bias fix + { "_Q8W8V8U8", D3DFMT_Q8W8V8U8, GL_RGBA8, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 1, 4 }, // straight ripoff of D3DFMT_A8R8G8B8 + + // U8V8 is exposed to the client as 2-bytes per texel, but we download it as 3-byte RGB. + // WriteTexels needs to do that conversion from rg8 to rgb8 in order to be able to download it correctly + { "_V8U8", D3DFMT_V8U8, GL_RGB8, 0, GL_RG, GL_BYTE, 1, 2 }, + + { "_R32F", D3DFMT_R32F, GL_R32F, GL_R32F, GL_RED, GL_FLOAT, 1, 4 }, +//$ TODO: Need to merge bitmap changes over from Dota to get these formats. +#if 0 + { "_A2R10G10B10", D3DFMT_A2R10G10B10, GL_RGB10_A2, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_10_10_10_2, 1, 4 }, + { "_A2B10G10R10", D3DFMT_A2B10G10R10, GL_RGB10_A2, GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_10_10_10_2, 1, 4 }, +#endif + + /* + // NV shadow depth tex + D3DFMT_NV_INTZ = 0x5a544e49, // MAKEFOURCC('I','N','T','Z') + D3DFMT_NV_RAWZ = 0x5a574152, // MAKEFOURCC('R','A','W','Z') + + // NV null tex + D3DFMT_NV_NULL = 0x4c4c554e, // MAKEFOURCC('N','U','L','L') + + // ATI shadow depth tex + D3DFMT_ATI_D16 = 0x36314644, // MAKEFOURCC('D','F','1','6') + D3DFMT_ATI_D24S8 = 0x34324644, // MAKEFOURCC('D','F','2','4') + + // ATI 1N and 2N compressed tex + D3DFMT_ATI_2N = 0x32495441, // MAKEFOURCC('A', 'T', 'I', '2') + D3DFMT_ATI_1N = 0x31495441, // MAKEFOURCC('A', 'T', 'I', '1') + */ +}; + +int g_formatDescTableCount = sizeof(g_formatDescTable) / sizeof( g_formatDescTable[0] ); + +const GLMTexFormatDesc *GetFormatDesc( D3DFORMAT format ) +{ + for( int i=0; i= range) DebuggerBreak(); + + *valuebuf = (*valuebuf << width) | scaled; +} + +// return true if successful +bool GLMGenTexels( GLMGenTexelParams *params ) +{ + unsigned char chunkbuf[256]; // can't think of any chunk this big.. + + const GLMTexFormatDesc *format = GetFormatDesc( params->m_format ); + + if (!format) + { + return FALSE; // fail + } + + // this section just generates one square chunk in the desired format + unsigned long *temp32 = (unsigned long*)chunkbuf; + unsigned int chunksize = 0; // we can sanity check against the format table with this + + switch( params->m_format ) + { + // comment shows byte order in RAM + // lowercase is bit arrangement in a byte + + case D3DFMT_A8R8G8B8: // B G R A + InsertTexelComponentFixed( params->a, 8, temp32 ); // A is inserted first and winds up at most significant bits after insertions follow + InsertTexelComponentFixed( params->r, 8, temp32 ); + InsertTexelComponentFixed( params->g, 8, temp32 ); + InsertTexelComponentFixed( params->b, 8, temp32 ); + chunksize = 4; + break; + + case D3DFMT_A4R4G4B4: // [ggggbbbb] [aaaarrrr] RA (nibbles) + InsertTexelComponentFixed( params->a, 4, temp32 ); + InsertTexelComponentFixed( params->r, 4, temp32 ); + InsertTexelComponentFixed( params->g, 4, temp32 ); + InsertTexelComponentFixed( params->b, 4, temp32 ); + chunksize = 2; + break; + + case D3DFMT_X8R8G8B8: // B G R X + InsertTexelComponentFixed( 0.0, 8, temp32 ); + InsertTexelComponentFixed( params->r, 8, temp32 ); + InsertTexelComponentFixed( params->g, 8, temp32 ); + InsertTexelComponentFixed( params->b, 8, temp32 ); + chunksize = 4; + break; + + case D3DFMT_X1R5G5B5: // [gggbbbbb] [xrrrrrgg] + InsertTexelComponentFixed( 0.0, 1, temp32 ); + InsertTexelComponentFixed( params->r, 5, temp32 ); + InsertTexelComponentFixed( params->g, 5, temp32 ); + InsertTexelComponentFixed( params->b, 5, temp32 ); + chunksize = 2; + break; + + case D3DFMT_A1R5G5B5: // [gggbbbbb] [arrrrrgg] + InsertTexelComponentFixed( params->a, 1, temp32 ); + InsertTexelComponentFixed( params->r, 5, temp32 ); + InsertTexelComponentFixed( params->g, 5, temp32 ); + InsertTexelComponentFixed( params->b, 5, temp32 ); + chunksize = 2; + break; + + case D3DFMT_L8: // L // caller, use R for L + InsertTexelComponentFixed( params->r, 8, temp32 ); + chunksize = 1; + break; + + case D3DFMT_A8L8: // L A // caller, use R for L and A for A + InsertTexelComponentFixed( params->a, 8, temp32 ); + InsertTexelComponentFixed( params->r, 8, temp32 ); + chunksize = 2; + break; + + case D3DFMT_R8G8B8: // B G R + InsertTexelComponentFixed( params->r, 8, temp32 ); + InsertTexelComponentFixed( params->g, 8, temp32 ); + InsertTexelComponentFixed( params->b, 8, temp32 ); + chunksize = 3; + break; + + case D3DFMT_A8: // A + InsertTexelComponentFixed( params->a, 8, temp32 ); + chunksize = 1; + break; + + case D3DFMT_R5G6B5: // [gggbbbbb] [rrrrrggg] + InsertTexelComponentFixed( params->r, 5, temp32 ); + InsertTexelComponentFixed( params->g, 6, temp32 ); + InsertTexelComponentFixed( params->b, 5, temp32 ); + chunksize = 2; + break; + + case D3DFMT_DXT1: + { + memset( temp32, 0, 8 ); // zap 8 bytes + + // two 565 RGB words followed by 32 bits of 2-bit interp values for a 4x4 block + // we write the same color to both slots and all zeroes for the mask (one color total) + + unsigned long dxt1_color = 0; + + // generate one such word and clone it + InsertTexelComponentFixed( params->r, 5, &dxt1_color ); + InsertTexelComponentFixed( params->g, 6, &dxt1_color ); + InsertTexelComponentFixed( params->b, 5, &dxt1_color ); + + // dupe + dxt1_color = dxt1_color | (dxt1_color<<16); + + // write into chunkbuf + *(unsigned long*)&chunkbuf[0] = dxt1_color; + + // color mask bits after that are already set to all zeroes. chunk is done. + chunksize = 8; + } + break; + + case D3DFMT_DXT3: + { + memset( temp32, 0, 16 ); // zap 16 bytes + + // eight bytes of alpha (16 4-bit alpha nibbles) + // followed by a DXT1 block + + unsigned long dxt3_alpha = 0; + for( int i=0; i<8; i++) + { + // splat same alpha through block + InsertTexelComponentFixed( params->a, 4, &dxt3_alpha ); + } + + unsigned long dxt3_color = 0; + + // generate one such word and clone it + InsertTexelComponentFixed( params->r, 5, &dxt3_color ); + InsertTexelComponentFixed( params->g, 6, &dxt3_color ); + InsertTexelComponentFixed( params->b, 5, &dxt3_color ); + + // dupe + dxt3_color = dxt3_color | (dxt3_color<<16); + + // write into chunkbuf + *(unsigned long*)&chunkbuf[0] = dxt3_alpha; + *(unsigned long*)&chunkbuf[4] = dxt3_alpha; + *(unsigned long*)&chunkbuf[8] = dxt3_color; + *(unsigned long*)&chunkbuf[12] = dxt3_color; + + chunksize = 16; + } + break; + + case D3DFMT_DXT5: + { + memset( temp32, 0, 16 ); // zap 16 bytes + + // DXT5 has 8 bytes of compressed alpha, then 8 bytes of compressed RGB like DXT1. + + // the 8 alpha bytes are 2 bytes of endpoint alpha values, then 16x3 bits of interpolants. + // so to write a single alpha value, just figure out the value, store it in both the first two bytes then store zeroes. + + InsertTexelComponentFixed( params->a, 8, (unsigned long*)&chunkbuf[0] ); + InsertTexelComponentFixed( params->a, 8, (unsigned long*)&chunkbuf[0] ); + // rest of the alpha mask was already zeroed. + + // now do colors + unsigned long dxt5_color = 0; + + // generate one such word and clone it + InsertTexelComponentFixed( params->r, 5, &dxt5_color ); + InsertTexelComponentFixed( params->g, 6, &dxt5_color ); + InsertTexelComponentFixed( params->b, 5, &dxt5_color ); + + // dupe + dxt5_color = dxt5_color | (dxt5_color<<16); + + // write into chunkbuf + *(unsigned long*)&chunkbuf[8] = dxt5_color; + *(unsigned long*)&chunkbuf[12] = dxt5_color; + + chunksize = 16; + } + break; + + + case D3DFMT_A32B32G32R32F: + { + *(float*)&chunkbuf[0] = params->r; + *(float*)&chunkbuf[4] = params->g; + *(float*)&chunkbuf[8] = params->b; + *(float*)&chunkbuf[12] = params->a; + + chunksize = 16; + } + break; + + case D3DFMT_A16B16G16R16: + memset( chunkbuf, 0, 8 ); + // R and G wind up in the first 32 bits + // B and A wind up in the second 32 bits + + InsertTexelComponentFixed( params->a, 16, (unsigned long*)&chunkbuf[4] ); // winds up as MSW of second word (note [4]) - thus last in RAM + InsertTexelComponentFixed( params->b, 16, (unsigned long*)&chunkbuf[4] ); + + InsertTexelComponentFixed( params->g, 16, (unsigned long*)&chunkbuf[0] ); + InsertTexelComponentFixed( params->r, 16, (unsigned long*)&chunkbuf[0] ); // winds up as LSW of first word, thus first in RAM + + chunksize = 8; + break; + + // not done yet + + + //case D3DFMT_D16: + //case D3DFMT_D24X8: + //case D3DFMT_D24S8: + + //case D3DFMT_A16B16G16R16F: + + default: + return FALSE; // fail + break; + } + + // once the chunk buffer is filled.. + + // sanity check the reported chunk size. + if (static_cast(chunksize) != format->m_bytesPerSquareChunk) + { + DebuggerBreak(); + return FALSE; + } + + // verify that the amount you want to write will not exceed the limit byte count + unsigned long destByteCount = chunksize * params->m_chunkCount; + + if (static_cast(destByteCount) > params->m_byteCountLimit) + { + DebuggerBreak(); + return FALSE; + } + + // write the bytes. + unsigned char *destP = (unsigned char*)params->m_dest; + for( int chunk=0; chunk < params->m_chunkCount; chunk++) + { + for( uint byteindex = 0; byteindex < chunksize; byteindex++) + { + *destP++ = chunkbuf[byteindex]; + } + } + params->m_bytesWritten = destP - (unsigned char*)params->m_dest; + + return TRUE; +} + + +//=============================================================================== +bool LessFunc_GLMTexLayoutKey( const GLMTexLayoutKey &a, const GLMTexLayoutKey &b ) +{ + #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_texSamples); + DO_LESS(m_xSize); + DO_LESS(m_ySize) + DO_LESS(m_zSize); + + #undef DO_LESS + + return false; // they are equal +} + +CGLMTexLayoutTable::CGLMTexLayoutTable() +{ + m_layoutMap.SetLessFunc( LessFunc_GLMTexLayoutKey ); +} + +GLMTexLayout *CGLMTexLayoutTable::NewLayoutRef( GLMTexLayoutKey *pDesiredKey ) +{ + GLMTexLayoutKey tempKey; + GLMTexLayoutKey *key = pDesiredKey; + + // look up 'key' in the map and see if it's a hit, if so, bump the refcount and return + // if not, generate a completed layout based on the key, add to map, set refcount to 1, return that + + const GLMTexFormatDesc *formatDesc = GetFormatDesc( key->m_texFormat ); + + //bool compression = (formatDesc->m_chunkSize > 1) != 0; + if (!formatDesc) + { + GLMStop(); // bad news + } + + if ( gGL->m_bHave_GL_EXT_texture_sRGB_decode ) + { + if ( ( formatDesc->m_glIntFormatSRGB != 0 ) && ( ( key->m_texFlags & kGLMTexSRGB ) == 0 ) ) + { + tempKey = *pDesiredKey; + key = &tempKey; + + // Slam on SRGB texture flag, and we'll use GL_EXT_texture_sRGB_decode to selectively turn it off in the samplers + key->m_texFlags |= kGLMTexSRGB; + } + } + + unsigned short index = m_layoutMap.Find( *key ); + if (index != m_layoutMap.InvalidIndex()) + { + // found it + //printf(" -hit- "); + GLMTexLayout *layout = m_layoutMap[ index ]; + + // bump ref count + layout->m_refCount ++; + + return layout; + } + else + { + //printf(" -miss- "); + // need to make a new one + // to allocate it, we need to know how big to make it (slice count) + + // figure out how many mip levels are in play + int mipCount = 1; + if (key->m_texFlags & kGLMTexMipped) + { + int largestAxis = key->m_xSize; + + if (key->m_ySize > largestAxis) + largestAxis = key->m_ySize; + + if (key->m_zSize > largestAxis) + largestAxis = key->m_zSize; + + mipCount = 0; + while( largestAxis > 0 ) + { + mipCount ++; + largestAxis >>= 1; + } + } + + int faceCount = 1; + if (key->m_texGLTarget == GL_TEXTURE_CUBE_MAP) + { + faceCount = 6; + } + + int sliceCount = mipCount * faceCount; + + if (key->m_texFlags & kGLMTexMultisampled) + { + Assert( (key->m_texGLTarget == GL_TEXTURE_2D) ); + Assert( sliceCount == 1 ); + + // assume non mipped + Assert( (key->m_texFlags & kGLMTexMipped) == 0 ); + Assert( (key->m_texFlags & kGLMTexMippedAuto) == 0 ); + + // assume renderable and srgb + Assert( (key->m_texFlags & kGLMTexRenderable) !=0 ); + //Assert( (key->m_texFlags & kGLMTexSRGB) !=0 ); //FIXME don't assert on making depthstencil surfaces which are non srgb + + // double check sample count (FIXME need real limit check here against device/driver) + Assert( (key->m_texSamples==2) || (key->m_texSamples==4) || (key->m_texSamples==6) || (key->m_texSamples==8) ); + } + + // now we know enough to allocate and populate the new tex layout. + + // malloc the new layout + int layoutSize = sizeof( GLMTexLayout ) + (sliceCount * sizeof( GLMTexLayoutSlice )); + GLMTexLayout *layout = (GLMTexLayout *)malloc( layoutSize ); + memset( layout, 0, layoutSize ); + + // clone the key in there + memset( &layout->m_key, 0x00, sizeof(layout->m_key) ); + layout->m_key = *key; + + // set refcount + layout->m_refCount = 1; + + // save the format desc + layout->m_format = (GLMTexFormatDesc *)formatDesc; + + // we know the mipcount from before + layout->m_mipCount = mipCount; + + // we know the face count too + layout->m_faceCount = faceCount; + + // slice count is the product + layout->m_sliceCount = mipCount * faceCount; + + // we can now fill in the slices. + GLMTexLayoutSlice *slicePtr = &layout->m_slices[0]; + int storageOffset = 0; + + //bool compressed = (formatDesc->m_chunkSize > 1); // true if DXT + + for( int mip = 0; mip < mipCount; mip ++ ) + { + for( int face = 0; face < faceCount; face++ ) + { + // note application of chunk size which is 1 for uncompressed, and 4 for compressed tex (DXT) + // note also that the *dimensions* must scale down to 1 + // but that the *storage* cannot go below 4x4. + // we introduce the "storage sizes" which are clamped, to compute the storage footprint. + + int storage_x,storage_y,storage_z; + + slicePtr->m_xSize = layout->m_key.m_xSize >> mip; + slicePtr->m_xSize = MAX( slicePtr->m_xSize, 1 ); // dimension can't go to zero + storage_x = MAX( slicePtr->m_xSize, formatDesc->m_chunkSize ); // storage extent can't go below chunk size + + slicePtr->m_ySize = layout->m_key.m_ySize >> mip; + slicePtr->m_ySize = MAX( slicePtr->m_ySize, 1 ); // dimension can't go to zero + storage_y = MAX( slicePtr->m_ySize, formatDesc->m_chunkSize ); // storage extent can't go below chunk size + + slicePtr->m_zSize = layout->m_key.m_zSize >> mip; + slicePtr->m_zSize = MAX( slicePtr->m_zSize, 1 ); // dimension can't go to zero + storage_z = MAX( slicePtr->m_zSize, 1); // storage extent for Z cannot go below '1'. + + //if (compressed) NO NO NO do not lie about the dimensionality, just fudge the storage. + //{ + // // round up to multiple of 4 in X and Y axes + // slicePtr->m_xSize = (slicePtr->m_xSize+3) & (~3); + // slicePtr->m_ySize = (slicePtr->m_ySize+3) & (~3); + //} + + int xchunks = (storage_x / formatDesc->m_chunkSize ); + int ychunks = (storage_y / formatDesc->m_chunkSize ); + + slicePtr->m_storageSize = (xchunks * ychunks * formatDesc->m_bytesPerSquareChunk) * storage_z; + slicePtr->m_storageOffset = storageOffset; + + storageOffset += slicePtr->m_storageSize; + storageOffset = ( (storageOffset+0x0F) & (~0x0F)); // keep each MIP starting on a 16 byte boundary. + + slicePtr++; + } + } + + layout->m_storageTotalSize = storageOffset; + //printf("\n size %08x for key (x=%d y=%d z=%d, fmt=%08x, bpsc=%d)", layout->m_storageTotalSize, key->m_xSize, key->m_ySize, key->m_zSize, key->m_texFormat, formatDesc->m_bytesPerSquareChunk ); + + // generate summary + // "target, format, +/- mips, base size" + char scratch[1024]; + + char *targetname = "?"; + switch( key->m_texGLTarget ) + { + case GL_TEXTURE_2D: targetname = "2D "; break; + case GL_TEXTURE_3D: targetname = "3D "; break; + case GL_TEXTURE_CUBE_MAP: targetname = "CUBE"; break; + } + + sprintf( scratch, "[%s %s %dx%dx%d mips=%d slices=%d flags=%02lX%s]", + targetname, + formatDesc->m_formatSummary, + layout->m_key.m_xSize, layout->m_key.m_ySize, layout->m_key.m_zSize, + mipCount, + sliceCount, + layout->m_key.m_texFlags, + (layout->m_key.m_texFlags & kGLMTexSRGB) ? " SRGB" : "" + ); + layout->m_layoutSummary = strdup( scratch ); + //GLMPRINTF(("-D- new tex layout [ %s ]", scratch )); + + // then insert into map. disregard returned index. + m_layoutMap.Insert( layout->m_key, layout ); + + return layout; + } +} + +void CGLMTexLayoutTable::DelLayoutRef( GLMTexLayout *layout ) +{ + // locate layout in hash, drop refcount + // (some GC step later on will harvest expired layouts - not like it's any big challenge to re-generate them) + + unsigned short index = m_layoutMap.Find( layout->m_key ); + if (index != m_layoutMap.InvalidIndex()) + { + // found it + GLMTexLayout *layout = m_layoutMap[ index ]; + + // drop ref count + layout->m_refCount --; + + //assert( layout->m_refCount >= 0 ); + } + else + { + // that's bad + GLMStop(); + } +} + +void CGLMTexLayoutTable::DumpStats( ) +{ + for (uint i=0; im_refCount, layout->m_storageTotalSize, (layout->m_refCount*layout->m_storageTotalSize), layout->m_layoutSummary ); + } +} + +ConVar gl_texmsaalog ( "gl_texmsaalog", "0"); + +ConVar gl_rt_forcergba ( "gl_rt_forcergba", "1" ); // on teximage of a renderable tex, pass GL_RGBA in place of GL_BGRA + +ConVar gl_minimize_rt_tex ( "gl_minimize_rt_tex", "0" ); // if 1, set the GL_TEXTURE_MINIMIZE_STORAGE_APPLE texture parameter to cut off mipmaps for RT's +ConVar gl_minimize_all_tex ( "gl_minimize_all_tex", "1" ); // if 1, set the GL_TEXTURE_MINIMIZE_STORAGE_APPLE texture parameter to cut off mipmaps for textures which are unmipped +ConVar gl_minimize_tex_log ( "gl_minimize_tex_log", "0" ); // if 1, printf the names of the tex that got minimized + +CGLMTex::CGLMTex( GLMContext *ctx, GLMTexLayout *layout, uint levels, const char *debugLabel ) +{ +#if GLMDEBUG + m_pPrevTex = NULL; + m_pNextTex = g_pFirstCGMLTex; + if ( m_pNextTex ) + { + Assert( m_pNextTex->m_pPrevTex == NULL ); + m_pNextTex->m_pPrevTex = this; + } + g_pFirstCGMLTex = this; +#endif + + // caller has responsibility to make 'ctx' current, but we check to be sure. + ctx->CheckCurrent(); + + m_nLastResolvedBatchCounter = ctx->m_nBatchCounter; + + // note layout requested + m_layout = layout; + m_texGLTarget = m_layout->m_key.m_texGLTarget; + + m_nSamplerType = SAMPLER_TYPE_UNUSED; + switch ( m_texGLTarget ) + { + case GL_TEXTURE_CUBE_MAP: m_nSamplerType = SAMPLER_TYPE_CUBE; break; + case GL_TEXTURE_2D: m_nSamplerType = SAMPLER_TYPE_2D; break; + case GL_TEXTURE_3D: m_nSamplerType = SAMPLER_TYPE_3D; break; + default: + Assert( 0 ); + break; + } + + m_maxActiveMip = -1; //index of highest mip that has been written - increase as each mip arrives + m_minActiveMip = 999; //index of lowest mip that has been written - lower it as each mip arrives + + // note context owner + m_ctx = ctx; + + // clear the bind point flags + //m_bindPoints.ClearAll(); + + // clear the RT attach count + m_rtAttachCount = 0; + + // come up with a GL name for this texture. + m_texName = ctx->CreateTex( m_texGLTarget, m_layout->m_format->m_glIntFormat ); + + m_pBlitSrcFBO = NULL; + m_pBlitDstFBO = NULL; + + // Sense whether to try and apply client storage upon teximage/subimage. + // This should only be true if we're running on OSX 10.6 or it was explicitly + // enabled with -gl_texclientstorage on the command line. + m_texClientStorage = ctx->m_bTexClientStorage; + + // flag that we have not yet been explicitly kicked into VRAM.. + m_texPreloaded = false; + + // clone the debug label if there is one. + m_debugLabel = debugLabel ? strdup(debugLabel) : NULL; + + // if tex is MSAA renderable, make an RBO, else zero the RBO name and dirty bit + if (layout->m_key.m_texFlags & kGLMTexMultisampled) + { + gGL->glGenRenderbuffersEXT( 1, &m_rboName ); + + // so we have enough info to go ahead and bind the RBO and put storage on it? + // try it. + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_rboName ); + + // quietly clamp if sample count exceeds known limit for the device + int sampleCount = layout->m_key.m_texSamples; + + if (sampleCount > ctx->Caps().m_maxSamples) + { + sampleCount = ctx->Caps().m_maxSamples; // clamp + } + + GLenum msaaFormat = (layout->m_key.m_texFlags & kGLMTexSRGB) ? layout->m_format->m_glIntFormatSRGB : layout->m_format->m_glIntFormat; + gGL->glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, + sampleCount, // not "layout->m_key.m_texSamples" + msaaFormat, + layout->m_key.m_xSize, + layout->m_key.m_ySize ); + + if (gl_texmsaalog.GetInt()) + { + printf( "\n == MSAA Tex %p %s : MSAA RBO is intformat %s (%x)", this, m_debugLabel?m_debugLabel:"", GLMDecode( eGL_ENUM, msaaFormat ), msaaFormat ); + } + + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + } + else + { + m_rboName = 0; + } + + // at this point we have the complete description of the texture, and a name for it, but no data and no actual GL object. + // we know this name has bever seen duty before, so we're going to hard-bind it to TMU 0, displacing any other tex that might have been bound there. + // any previously bound tex will be unbound and appropriately marked as a result. + // the active TMU will be set as a side effect. + CGLMTex *pPrevTex = ctx->m_samplers[0].m_pBoundTex; + ctx->BindTexToTMU( this, 0 ); + + m_SamplingParams.SetToDefaults(); + m_SamplingParams.SetToTarget( m_texGLTarget ); + + // OK, our texture now exists and is bound on the active TMU. Not drawable yet though. + + // Create backing storage and fill it + if ( !(layout->m_key.m_texFlags & kGLMTexRenderable) && m_texClientStorage ) + { + m_backing = (char *)malloc( m_layout->m_storageTotalSize ); + memset( m_backing, 0, m_layout->m_storageTotalSize ); + + // track bytes allocated for non-RT's + int formindex = sEncodeLayoutAsIndex( &layout->m_key ); + + g_texGlobalBytes[ formindex ] += m_layout->m_storageTotalSize; + + #if TEXSPACE_LOGGING + printf( "\n Tex %s added %d bytes in form %d which is now %d bytes", m_debugLabel ? m_debugLabel : "-", m_layout->m_storageTotalSize, formindex, g_texGlobalBytes[ formindex ] ); + printf( "\n\t\t[ %d %d %d %d %d %d %d %d ]", + g_texGlobalBytes[ 0 ],g_texGlobalBytes[ 1 ],g_texGlobalBytes[ 2 ],g_texGlobalBytes[ 3 ], + g_texGlobalBytes[ 4 ],g_texGlobalBytes[ 5 ],g_texGlobalBytes[ 6 ],g_texGlobalBytes[ 7 ] + ); + #endif + } + else + { + m_backing = NULL; + + m_texClientStorage = false; + } + + // init lock count + // lock reqs are tracked by the owning context + m_lockCount = 0; + + m_sliceFlags.SetCount( m_layout->m_sliceCount ); + for( int i=0; i< m_layout->m_sliceCount; i++) + { + m_sliceFlags[i] = 0; + // kSliceValid = false (we have not teximaged each slice yet) + // kSliceStorageValid = false (the storage allocated does not reflect what is in the tex) + // kSliceLocked = false (the slices are not locked) + // kSliceFullyDirty = false (this does not come true til first lock) + } + + // texture minimize parameter keeps driver from allocing mips when it should not, by being explicit about the ones that have no mips. + + bool setMinimizeParameter = false; + bool minimize_rt = (gl_minimize_rt_tex.GetInt()!=0); + bool minimize_all = (gl_minimize_all_tex.GetInt()!=0); + + if (layout->m_key.m_texFlags & kGLMTexRenderable) + { + // it's an RT. if mips were not explicitly requested, and "gl_minimize_rt_tex" is true, set the minimize parameter. + if ( (minimize_rt || minimize_all) && ( !(layout->m_key.m_texFlags & kGLMTexMipped) ) ) + { + setMinimizeParameter = true; + } + } + else + { + // not an RT. if mips were not requested, and "gl_minimize_all_tex" is true, set the minimize parameter. + if ( minimize_all && ( !(layout->m_key.m_texFlags & kGLMTexMipped) ) ) + { + setMinimizeParameter = true; + } + } + + if (setMinimizeParameter) + { + if (gl_minimize_tex_log.GetInt()) + { + printf("\n minimizing storage for tex '%s' [%s] ", m_debugLabel?m_debugLabel:"-", m_layout->m_layoutSummary ); + } + + if (gGL->m_bHave_GL_APPLE_texture_range) + gGL->glTexParameteri( m_layout->m_key.m_texGLTarget, GL_TEXTURE_MINIMIZE_STORAGE_APPLE, 1 ); + } + + // after a lot of pain with texture completeness... + // always push black into all slices of all newly created textures. + + #if 0 + bool pushRenderableSlices = (m_layout->m_key.m_texFlags & kGLMTexRenderable) != 0; + bool pushTexSlices = true; // just do it everywhere (m_layout->m_mipCount>1) && (m_layout->m_format->m_chunkSize !=1) ; + if (pushTexSlices) + { + // fill storage with mostly-opaque purple + + GLMGenTexelParams genp; + memset( &genp, 0, sizeof(genp) ); + + genp.m_format = m_layout->m_format->m_d3dFormat; + const GLMTexFormatDesc *format = GetFormatDesc( genp.m_format ); + + genp.m_dest = m_backing; // dest addr + genp.m_chunkCount = m_layout->m_storageTotalSize / format->m_bytesPerSquareChunk; // fill the whole slab + genp.m_byteCountLimit = m_layout->m_storageTotalSize; // limit writes to this amount + + genp.r = 1.0; + genp.g = 0.0; + genp.b = 1.0; + genp.a = 0.75; + + GLMGenTexels( &genp ); + } + #endif + + //if (pushRenderableSlices || pushTexSlices) + if ( !( ( layout->m_key.m_texFlags & kGLMTexMipped ) && ( levels == ( unsigned ) m_layout->m_mipCount ) ) ) + { + for( int face=0; face m_faceCount; face++) + { + for( int mip=0; mip m_mipCount; mip++) + { + // we're not really going to lock, we're just going to write the blank data from the backing store we just made + GLMTexLockDesc desc; + + desc.m_req.m_tex = this; + desc.m_req.m_face = face; + desc.m_req.m_mip = mip; + + desc.m_sliceIndex = CalcSliceIndex( face, mip ); + + GLMTexLayoutSlice *slice = &m_layout->m_slices[ desc.m_sliceIndex ]; + + desc.m_req.m_region.xmin = desc.m_req.m_region.ymin = desc.m_req.m_region.zmin = 0; + desc.m_req.m_region.xmax = slice->m_xSize; + desc.m_req.m_region.ymax = slice->m_ySize; + desc.m_req.m_region.zmax = slice->m_zSize; + + desc.m_sliceBaseOffset = slice->m_storageOffset; // doesn't really matter... we're just pushing zeroes.. + desc.m_sliceRegionOffset = 0; + + WriteTexels( &desc, true, (layout->m_key.m_texFlags & kGLMTexRenderable)!=0 ); // write whole slice - but disable data source if it's an RT, as there's no backing + } + } + } + GLMPRINTF(("-A- -**TEXNEW '%-60s' name=%06d size=%09d storage=%08x label=%s ", m_layout->m_layoutSummary, m_texName, m_layout->m_storageTotalSize, m_backing, m_debugLabel ? m_debugLabel : "-" )); + + ctx->BindTexToTMU( pPrevTex, 0 ); +} + +CGLMTex::~CGLMTex( ) +{ +#if GLMDEBUG + if ( m_pPrevTex ) + { + Assert( m_pPrevTex->m_pNextTex == this ); + m_pPrevTex->m_pNextTex = m_pNextTex; + } + else + { + Assert( g_pFirstCGMLTex == this ); + g_pFirstCGMLTex = m_pNextTex; + } + if ( m_pNextTex ) + { + Assert( m_pNextTex->m_pPrevTex == this ); + m_pNextTex->m_pPrevTex = m_pPrevTex; + } + m_pNextTex = m_pPrevTex = NULL; +#endif + + if ( !(m_layout->m_key.m_texFlags & kGLMTexRenderable) ) + { + int formindex = sEncodeLayoutAsIndex( &m_layout->m_key ); + + g_texGlobalBytes[ formindex ] -= m_layout->m_storageTotalSize; + + #if TEXSPACE_LOGGING + printf( "\n Tex %s freed %d bytes in form %d which is now %d bytes", m_debugLabel ? m_debugLabel : "-", m_layout->m_storageTotalSize, formindex, g_texGlobalBytes[ formindex ] ); + printf( "\n\t\t[ %d %d %d %d %d %d %d %d ]", + g_texGlobalBytes[ 0 ],g_texGlobalBytes[ 1 ],g_texGlobalBytes[ 2 ],g_texGlobalBytes[ 3 ], + g_texGlobalBytes[ 4 ],g_texGlobalBytes[ 5 ],g_texGlobalBytes[ 6 ],g_texGlobalBytes[ 7 ] + ); + #endif + } + + GLMPRINTF(("-A- -**TEXDEL '%-60s' name=%06d size=%09d storage=%08x label=%s ", m_layout->m_layoutSummary, m_texName, m_layout->m_storageTotalSize, m_backing, m_debugLabel ? m_debugLabel : "-" )); + // check first to see if we were still bound anywhere or locked... these should be failures. + + if ( m_pBlitSrcFBO ) + { + m_ctx->DelFBO( m_pBlitSrcFBO ); + m_pBlitSrcFBO = NULL; + } + + if ( m_pBlitDstFBO ) + { + m_ctx->DelFBO( m_pBlitDstFBO ); + m_pBlitDstFBO = NULL; + } + + if ( m_rboName ) + { + gGL->glDeleteRenderbuffersEXT( 1, &m_rboName ); + m_rboName = 0; + } + + // if all that is OK, then delete the underlying tex + if ( m_texName ) + { + m_ctx->DestroyTex( m_texGLTarget, m_layout, m_texName ); + m_texName = 0; + } + + // release our usage of the layout + m_ctx->m_texLayoutTable->DelLayoutRef( m_layout ); + m_layout = NULL; + + if (m_backing) + { + free( m_backing ); + m_backing = NULL; + } + + if (m_debugLabel) + { + free( m_debugLabel ); + m_debugLabel = NULL; + } + + m_ctx = NULL; +} + +int CGLMTex::CalcSliceIndex( int face, int mip ) +{ + // faces of the same mip level are adjacent. "face major" storage + int index = (mip * m_layout->m_faceCount) + face; + + return index; +} + +void CGLMTex::CalcTexelDataOffsetAndStrides( int sliceIndex, int x, int y, int z, int *offsetOut, int *yStrideOut, int *zStrideOut ) +{ + int offset = 0; + int yStride = 0; + int zStride = 0; + + GLMTexFormatDesc *format = m_layout->m_format; + if (format->m_chunkSize==1) + { + // figure out row stride and layer stride + yStride = format->m_bytesPerSquareChunk * m_layout->m_slices[sliceIndex].m_xSize; // bytes per texel row (y stride) + zStride = yStride * m_layout->m_slices[sliceIndex].m_ySize; // bytes per texel layer (if 3D tex) + + offset = x * format->m_bytesPerSquareChunk; // lateral offset + offset += (y * yStride); // scanline offset + offset += (z * zStride); // should be zero for 2D tex + } + else + { + yStride = format->m_bytesPerSquareChunk * (m_layout->m_slices[sliceIndex].m_xSize / format->m_chunkSize); + zStride = yStride * (m_layout->m_slices[sliceIndex].m_ySize / format->m_chunkSize); + + // compressed format. scale the x,y,z values into chunks. + // assert if any of them are not multiples of a chunk. + int chunkx = x / format->m_chunkSize; + int chunky = y / format->m_chunkSize; + int chunkz = z / format->m_chunkSize; + + if ( (chunkx * format->m_chunkSize) != x) + { + GLMStop(); + } + + if ( (chunky * format->m_chunkSize) != y) + { + GLMStop(); + } + + if ( (chunkz * format->m_chunkSize) != z) + { + GLMStop(); + } + + offset = chunkx * format->m_bytesPerSquareChunk; // lateral offset + offset += (chunky * yStride); // chunk row offset + offset += (chunkz * zStride); // should be zero for 2D tex + } + + *offsetOut = offset; + *yStrideOut = yStride; + *zStrideOut = zStride; +} + +void CGLMTex::ReadTexels( GLMTexLockDesc *desc, bool readWholeSlice ) +{ + GLMRegion readBox; + + if (readWholeSlice) + { + readBox.xmin = readBox.ymin = readBox.zmin = 0; + + readBox.xmax = m_layout->m_slices[ desc->m_sliceIndex ].m_xSize; + readBox.ymax = m_layout->m_slices[ desc->m_sliceIndex ].m_ySize; + readBox.zmax = m_layout->m_slices[ desc->m_sliceIndex ].m_zSize; + } + else + { + readBox = desc->m_req.m_region; + } + + CGLMTex *pPrevTex = m_ctx->m_samplers[0].m_pBoundTex; + m_ctx->BindTexToTMU( this, 0 ); // SelectTMU(n) is a side effect + + if (readWholeSlice) + { + // make this work first.... then write the partial path + // (Hmmmm, I don't think we will ever actually need a partial path - + // since we have no notion of a partially valid slice of storage + + GLMTexFormatDesc *format = m_layout->m_format; + GLenum target = m_layout->m_key.m_texGLTarget; + + void *sliceAddress = m_backing + m_layout->m_slices[ desc->m_sliceIndex ].m_storageOffset; // this would change for PBO + //int sliceSize = m_layout->m_slices[ desc->m_sliceIndex ].m_storageSize; + + // interestingly enough, we can use the same path for both 2D and 3D fetch + + switch( target ) + { + case GL_TEXTURE_CUBE_MAP: + + // adjust target to steer to the proper face, then fall through to the 2D texture path. + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + desc->m_req.m_face; + + case GL_TEXTURE_2D: + case GL_TEXTURE_3D: + { + // check compressed or not + if (format->m_chunkSize != 1) + { + // compressed path + // http://www.opengl.org/sdk/docs/man/xhtml/glGetCompressedTexImage.xml + + gGL->glGetCompressedTexImage( target, // target + desc->m_req.m_mip, // level + sliceAddress ); // destination + } + else + { + // uncompressed path + // http://www.opengl.org/sdk/docs/man/xhtml/glGetTexImage.xml + + gGL->glGetTexImage( target, // target + desc->m_req.m_mip, // level + format->m_glDataFormat, // dataformat + format->m_glDataType, // datatype + sliceAddress ); // destination + } + } + break; + } + } + else + { + GLMStop(); + } + + m_ctx->BindTexToTMU( pPrevTex, 0 ); +} + +// TexSubImage should work properly on every driver stack and GPU--enabling by default. +ConVar gl_enabletexsubimage( "gl_enabletexsubimage", "1" ); + +void CGLMTex::WriteTexels( GLMTexLockDesc *desc, bool writeWholeSlice, bool noDataWrite ) +{ + //if ( m_nBindlessHashNumEntries ) + // return; + + GLMRegion writeBox; + + bool needsExpand = false; + char *expandTemp = NULL; + + switch( m_layout->m_format->m_d3dFormat) + { + case D3DFMT_V8U8: + { + needsExpand = true; + writeWholeSlice = true; + + // shoot down client storage if we have to generate a new flavor of the data + m_texClientStorage = false; + } + break; + } + + if (writeWholeSlice) + { + writeBox.xmin = writeBox.ymin = writeBox.zmin = 0; + + writeBox.xmax = m_layout->m_slices[ desc->m_sliceIndex ].m_xSize; + writeBox.ymax = m_layout->m_slices[ desc->m_sliceIndex ].m_ySize; + writeBox.zmax = m_layout->m_slices[ desc->m_sliceIndex ].m_zSize; + } + else + { + writeBox = desc->m_req.m_region; + } + + // first thing is to get the GL texture bound to a TMU, or just select one if already bound + // to get this running we will just always slam TMU 0 and let the draw time code fix it back + // a later optimization would be to hoist the bind call to the caller, do it exactly once + + CGLMTex *pPrevTex = m_ctx->m_samplers[0].m_pBoundTex; + m_ctx->BindTexToTMU( this, 0 ); // SelectTMU(n) is a side effect + + GLMTexFormatDesc *format = m_layout->m_format; + + GLenum target = m_layout->m_key.m_texGLTarget; + GLenum glDataFormat = format->m_glDataFormat; // this could change if expansion kicks in + GLenum glDataType = format->m_glDataType; + + GLMTexLayoutSlice *slice = &m_layout->m_slices[ desc->m_sliceIndex ]; + void *sliceAddress = m_backing ? (m_backing + slice->m_storageOffset) : NULL; // this would change for PBO + + // allow use of subimage if the target is texture2D and it has already been teximage'd + bool mayUseSubImage = false; + if ( (target==GL_TEXTURE_2D) && (m_sliceFlags[ desc->m_sliceIndex ] & kSliceValid) ) + { + mayUseSubImage = gl_enabletexsubimage.GetInt() != 0; + } + + // check flavor, 2D, 3D, or cube map + // we also have the choice to use subimage if this is a tex already created. (open question as to benefit) + + + // SRGB select. At this level (writetexels) we firmly obey the m_texFlags. + // (mechanism not policy) + + GLenum intformat = (m_layout->m_key.m_texFlags & kGLMTexSRGB) ? format->m_glIntFormatSRGB : format->m_glIntFormat; + if (CommandLine()->FindParm("-disable_srgbtex")) + { + // force non srgb flavor - experiment to make ATI r600 happy on 10.5.8 (maybe x1600 too!) + intformat = format->m_glIntFormat; + } + + Assert( intformat != 0 ); + + if (m_layout->m_key.m_texFlags & kGLMTexSRGB) + { + Assert( m_layout->m_format->m_glDataFormat != GL_DEPTH_COMPONENT ); + Assert( m_layout->m_format->m_glDataFormat != GL_DEPTH_STENCIL_EXT ); + Assert( m_layout->m_format->m_glDataFormat != GL_ALPHA ); + } + + // adjust min and max mip written + if (desc->m_req.m_mip > m_maxActiveMip) + { + m_maxActiveMip = desc->m_req.m_mip; + + gGL->glTexParameteri( target, GL_TEXTURE_MAX_LEVEL, desc->m_req.m_mip); + } + + if (desc->m_req.m_mip < m_minActiveMip) + { + m_minActiveMip = desc->m_req.m_mip; + + gGL->glTexParameteri( target, GL_TEXTURE_BASE_LEVEL, desc->m_req.m_mip); + } + + if (needsExpand) + { + int expandSize = 0; + + switch( m_layout->m_format->m_d3dFormat) + { + case D3DFMT_V8U8: + { + // figure out new size based on 3byte RGB format + // easy, just take the two byte size and grow it by 50% + expandSize = (slice->m_storageSize * 3) / 2; + expandTemp = (char*)malloc( expandSize ); + + char *src = (char*)sliceAddress; + char *dst = expandTemp; + + // transfer RG's to RGB's + while(expandSize>0) + { + *dst = *src++; // move first byte + *dst = *src++; // move second byte + *reinterpret_cast(dst) = 0xBB; // pad third byte + + expandSize -= 3; + } + + // move the slice pointer + sliceAddress = expandTemp; + + // change the data format we tell GL about + glDataFormat = GL_RGB; + } + break; + + default: Assert(!"Don't know how to expand that format.."); + } + + } + + // set up the client storage now, one way or another + // If this extension isn't supported, we just end up with two copies of the texture, one in the GL and one in app memory. + // So it's safe to just go on as if this extension existed and hold the possibly-unnecessary extra RAM. + if (gGL->m_bHave_GL_APPLE_client_storage) + { + gGL->glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, m_texClientStorage ); + } + + switch( target ) + { + case GL_TEXTURE_CUBE_MAP: + + // adjust target to steer to the proper face, then fall through to the 2D texture path. + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + desc->m_req.m_face; + + case GL_TEXTURE_2D: + { + // check compressed or not + if (format->m_chunkSize != 1) + { + Assert( writeWholeSlice ); //subimage not implemented in this path yet + + // compressed path + // http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexImage2D.xml + gGL->glCompressedTexImage2D( target, // target + desc->m_req.m_mip, // level + intformat, // internalformat - don't use format->m_glIntFormat because we have the SRGB select going on above + slice->m_xSize, // width + slice->m_ySize, // height + 0, // border + slice->m_storageSize, // imageSize + sliceAddress ); // data + + + } + else + { + if (mayUseSubImage) + { + // go subimage2D if it's a replacement, not a creation + + + gGL->glPixelStorei( GL_UNPACK_ROW_LENGTH, slice->m_xSize ); // in pixels + gGL->glPixelStorei( GL_UNPACK_SKIP_PIXELS, writeBox.xmin ); // in pixels + gGL->glPixelStorei( GL_UNPACK_SKIP_ROWS, writeBox.ymin ); // in pixels + + gGL->glTexSubImage2D( target, + desc->m_req.m_mip, // level + writeBox.xmin, // xoffset into dest + writeBox.ymin, // yoffset into dest + writeBox.xmax - writeBox.xmin, // width (was slice->m_xSize) + writeBox.ymax - writeBox.ymin, // height (was slice->m_ySize) + glDataFormat, // format + glDataType, // type + sliceAddress // data (will be offsetted by the SKIP_PIXELS and SKIP_ROWS - let GL do the math to find the first source texel) + ); + + gGL->glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + gGL->glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0 ); + gGL->glPixelStorei( GL_UNPACK_SKIP_ROWS, 0 ); + + /* + //http://www.opengl.org/sdk/docs/man/xhtml/glTexSubImage2D.xml + glTexSubImage2D( target, + desc->m_req.m_mip, // level + 0, // xoffset + 0, // yoffset + slice->m_xSize, // width + slice->m_ySize, // height + glDataFormat, // format + glDataType, // type + sliceAddress // data + ); + */ + } + else + { + if (m_layout->m_key.m_texFlags & kGLMTexRenderable) + { + if (gl_rt_forcergba.GetInt()) + { + if (glDataFormat == GL_BGRA) + { + // change it + glDataFormat = GL_RGBA; + } + } + } + + // uncompressed path + // http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/teximage2d.html + gGL->glTexImage2D( target, // target + desc->m_req.m_mip, // level + intformat, // internalformat - don't use format->m_glIntFormat because we have the SRGB select going on above + slice->m_xSize, // width + slice->m_ySize, // height + 0, // border + glDataFormat, // dataformat + glDataType, // datatype + noDataWrite ? NULL : sliceAddress ); // data (optionally suppressed in case ResetSRGB desires) + + if (m_layout->m_key.m_texFlags & kGLMTexMultisampled) + { + if (gl_texmsaalog.GetInt()) + { + printf( "\n == MSAA Tex %p %s : glTexImage2D for flat tex using intformat %s (%x)", this, m_debugLabel?m_debugLabel:"", GLMDecode( eGL_ENUM, intformat ), intformat ); + printf( "\n" ); + } + } + + m_sliceFlags[ desc->m_sliceIndex ] |= kSliceValid; // for next time, we can subimage.. + } + } + } + break; + + case GL_TEXTURE_3D: + { + // check compressed or not + if (format->m_chunkSize != 1) + { + // compressed path + // http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexImage3D.xml + + gGL->glCompressedTexImage3D( target, // target + desc->m_req.m_mip, // level + intformat, // internalformat + slice->m_xSize, // width + slice->m_ySize, // height + slice->m_zSize, // depth + 0, // border + slice->m_storageSize, // imageSize + sliceAddress ); // data + } + else + { + // uncompressed path + // http://www.opengl.org/sdk/docs/man/xhtml/glTexImage3D.xml + gGL->glTexImage3D( target, // target + desc->m_req.m_mip, // level + intformat, // internalformat + slice->m_xSize, // width + slice->m_ySize, // height + slice->m_zSize, // depth + 0, // border + glDataFormat, // dataformat + glDataType, // datatype + noDataWrite ? NULL : sliceAddress ); // data (optionally suppressed in case ResetSRGB desires) + } + } + break; + } + + if (gGL->m_bHave_GL_APPLE_client_storage) + { + gGL->glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE ); + } + + if ( expandTemp ) + { + free( expandTemp ); + } + + m_ctx->BindTexToTMU( pPrevTex, 0 ); +} + + +void CGLMTex::Lock( GLMTexLockParams *params, char** addressOut, int* yStrideOut, int *zStrideOut ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "CGLMTex::Lock" ); + g_TelemetryGPUStats.m_nTotalTexLocksAndUnlocks++; +#endif + + // locate appropriate slice in layout record + int sliceIndex = CalcSliceIndex( params->m_face, params->m_mip ); + + GLMTexLayoutSlice *slice = &m_layout->m_slices[sliceIndex]; + + // obtain offset + //int sliceBaseOffset = slice->m_storageOffset; + + // cross check region req against slice bounds - figure out if it matches, exceeds, or is less than the whole slice. + char exceed = (params->m_region.xmin < 0) || (params->m_region.xmax > slice->m_xSize) || + (params->m_region.ymin < 0) || (params->m_region.ymax > slice->m_ySize) || + (params->m_region.zmin < 0) || (params->m_region.zmax > slice->m_zSize); + + char partial = (params->m_region.xmin > 0) || (params->m_region.xmax < slice->m_xSize) || + (params->m_region.ymin > 0) || (params->m_region.ymax < slice->m_ySize) || + (params->m_region.zmin > 0) || (params->m_region.zmax < slice->m_zSize); + + bool copyout = false; // set if a readback of the texture slice from GL is needed + + if (exceed) + { + // illegal rect, out of bounds + GLMStop(); + } + + // on return, these things need to be true + + // a - there needs to be storage allocated, which we will return an address within + // b - the region corresponding to the slice being locked, will have valid data there for the whole slice. + // c - the slice is marked as locked + // d - the params of the lock request have been saved in the lock table (in the context) + + // so step 1 is unambiguous. If there's no backing storage, make some. + if (!m_backing) + { + if ( gl_pow2_tempmem.GetBool() ) + { + uint32_t unStoragePow2 = m_layout->m_storageTotalSize; + // Round up to next power of 2 + unStoragePow2--; + unStoragePow2 |= unStoragePow2 >> 1; + unStoragePow2 |= unStoragePow2 >> 2; + unStoragePow2 |= unStoragePow2 >> 4; + unStoragePow2 |= unStoragePow2 >> 8; + unStoragePow2 |= unStoragePow2 >> 16; + unStoragePow2++; + m_backing = (char *)calloc( unStoragePow2, 1 ); + } + else + { + m_backing = (char *)calloc( m_layout->m_storageTotalSize, 1 ); + } + + // clear the kSliceStorageValid bit on all slices + for( int i=0; im_sliceCount; i++) + { + m_sliceFlags[i] &= ~kSliceStorageValid; + } + } + + // work on this slice now + + // storage is known to exist at this point, but we need to check if its contents are valid for this slice. + // this is tracked per-slice so we don't hoist all the texels back out of GL across all slices if caller only + // wanted to lock some of them. + + // (i.e. if we just alloced it, it's blank) + // if storage is invalid, but the texture itself is valid, hoist the texels back to the storage and mark it valid. + // if storage is invalid, and texture itself is also invalid, go ahead and mark storage as valid and fully dirty... to force teximage. + + // ???????????? we need to go over this more carefully re "slice valid" (it has been teximaged) vs "storage valid" (it has been copied out). + + unsigned char *sliceFlags = &m_sliceFlags[ sliceIndex ]; + + if (params->m_readback) + { + // caller is letting us know that it wants to readback the real texels. + *sliceFlags |= kSliceStorageValid; + *sliceFlags |= kSliceValid; + *sliceFlags &= ~(kSliceFullyDirty); + copyout = true; + } + else + { + // caller is pushing texels. + if (! (*sliceFlags & kSliceStorageValid) ) + { + // storage is invalid. check texture state + if ( *sliceFlags & kSliceValid ) + { + // kSliceValid set: the texture itself has a valid slice, but we don't have it in our backing copy, so copy it out. + copyout = true; + } + else + { + // kSliceValid not set: the texture does not have a valid slice to copy out - it hasn't been teximage'd yet. + // set the "full dirty" bit to make sure we teximage the whole thing on unlock. + *sliceFlags |= kSliceFullyDirty; + + // assert if they did not ask to lock the full slice size on this go-round + if (partial) + { + // choice here - + // 1 - stop cold, we don't know how to subimage yet. + // 2 - grin and bear it, mark whole slice dirty (ah, we already did... so, do nothing). + // choice 2: // GLMStop(); + } + } + + // one way or another, upon reaching here the slice storage is valid for read. + *sliceFlags |= kSliceStorageValid; + } + } + + + // when we arrive here, there is storage, and the content of the storage for this slice is valid + // (or zeroes if it's the first lock) + + // log the lock request in the context. + int newdesc = m_ctx->m_texLocks.AddToTail(); + + GLMTexLockDesc *desc = &m_ctx->m_texLocks[newdesc]; + + desc->m_req = *params; + desc->m_active = true; + desc->m_sliceIndex = sliceIndex; + desc->m_sliceBaseOffset = m_layout->m_slices[sliceIndex].m_storageOffset; + + // to calculate the additional offset we need to look at the rect's min corner + // combined with the per-texel size and Y/Z stride + // also cross check it for 4x multiple if there is compression in play + + int offsetInSlice = 0; + int yStride = 0; + int zStride = 0; + + CalcTexelDataOffsetAndStrides( sliceIndex, params->m_region.xmin, params->m_region.ymin, params->m_region.zmin, &offsetInSlice, &yStride, &zStride ); + + // for compressed case... + // since there is presently no way to texsubimage a DXT when the rect does not cover the whole width, + // we will probably need to inflate the dirty rect in the recorded lock req so that the entire span is + // pushed across at unlock time. + + desc->m_sliceRegionOffset = offsetInSlice + desc->m_sliceBaseOffset; + + if (copyout) + { + // read the whole slice + // (odds are we'll never request anything but a whole slice to be read..) + ReadTexels( desc, true ); + } // this would be a good place to fill with scrub value if in debug... + + *addressOut = m_backing + desc->m_sliceRegionOffset; + *yStrideOut = yStride; + *zStrideOut = zStride; + + m_lockCount++; +} + +void CGLMTex::Unlock( GLMTexLockParams *params ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "CGLMTex::Unlock" ); + g_TelemetryGPUStats.m_nTotalTexLocksAndUnlocks++; +#endif + + // look for an active lock request on this face and mip (doesn't necessarily matter which one, if more than one) + // and mark it inactive. + // --> if you can't find one, fail. first line of defense against mismatched locks/unlocks.. + + int i=0; + bool found = false; + while( !found && (im_texLocks.Count()) ) + { + GLMTexLockDesc *desc = &m_ctx->m_texLocks[i]; + + // is lock at index 'i' targeted at the texture/face/mip in question? + if ( (desc->m_req.m_tex == this) && (desc->m_req.m_face == params->m_face) & (desc->m_req.m_mip == params->m_mip) && (desc->m_active) ) + { + // matched and active, so retire it + desc->m_active = false; + + // stop searching + found = true; + } + i++; + } + + if (!found) + { + GLMStop(); // bad news + } + + // found - so drop lock count + m_lockCount--; + + if (m_lockCount <0) + { + GLMStop(); // bad news + } + + if (m_lockCount==0) + { + // there should not be any active locks remaining on this texture. + + // motivation to defer all texel pushing til *all* open locks are closed out - + // if/when we back the texture with a PBO, we will need to unmap that PBO before teximaging from it; + // by waiting for all the locks to clear this gives us an unambiguous signal to act on. + + // scan through all the retired locks for this texture and push the texels for each one. + // after each one is dispatched, remove it from the pile. + + int j=0; + while( jm_texLocks.Count() ) + { + GLMTexLockDesc *desc = &m_ctx->m_texLocks[j]; + + if ( desc->m_req.m_tex == this ) + { + // if it's active, something is wrong + if (desc->m_active) + { + GLMStop(); + } + + // write the texels + bool fullyDirty = false; + + fullyDirty |= ((m_sliceFlags[ desc->m_sliceIndex ] & kSliceFullyDirty) != 0); + + // this is not optimal and will result in full downloads on any dirty. + // we're papering over the fact that subimage isn't done yet. + // but this is safe if the slice of storage is all valid. + + // at some point we'll need to actually compare the lock box against the slice bounds. + + // fullyDirty |= (m_sliceFlags[ desc->m_sliceIndex ] & kSliceStorageValid); + + WriteTexels( desc, fullyDirty ); + + // logical place to trigger preloading + // only do it for an RT tex, if it is not yet attached to any FBO. + // also, only do it if the slice number is the last slice in the tex. + if ( desc->m_sliceIndex == (m_layout->m_sliceCount-1) ) + { + if ( !(m_layout->m_key.m_texFlags & kGLMTexRenderable) || (m_rtAttachCount==0) ) + { + m_ctx->PreloadTex( this ); + // printf("( slice %d of %d )", desc->m_sliceIndex, m_layout->m_sliceCount ); + } + } + + m_ctx->m_texLocks.FastRemove( j ); // remove from the pile, don't advance index + } + else + { + j++; // move on to next one + } + } + + // clear the locked and full-dirty flags for all slices + for( int slice=0; slice < m_layout->m_sliceCount; slice++) + { + m_sliceFlags[slice] &= ~( kSliceLocked | kSliceFullyDirty ); + } + + // The 3D texture upload code seems to rely on the host copy, probably + // because it reuploads the whole thing each slice; we only use 3D textures + // for the 32x32x32 colorpsace conversion lookups and debugging the problem + // would not save any more memory. + if ( !m_texClientStorage && ( m_texGLTarget == GL_TEXTURE_2D ) ) + { + free(m_backing); + m_backing = NULL; + } + } +} + +#if defined( OSX ) + +void CGLMTex::HandleSRGBMismatch( bool srgb, int &srgbFlipCount ) +{ + bool srgbCapableTex = false; // not yet known + bool renderableTex = false; // not yet known. + + srgbCapableTex = m_layout->m_format->m_glIntFormatSRGB != 0; + renderableTex = ( m_layout->m_key.m_texFlags & kGLMTexRenderable ) != 0; + // we can fix it if it's not a renderable, and an sRGB enabled format variation is available. + + if ( srgbCapableTex && !renderableTex ) + { + char *texname = m_debugLabel; + if (!texname) texname = "-"; + + m_srgbFlipCount++; + +#if GLMDEBUG + //policy: print the ones that have flipped 1 or N times + static bool print_allflips = CommandLine()->FindParm("-glmspewallsrgbflips"); + static bool print_firstflips = CommandLine()->FindParm("-glmspewfirstsrgbflips"); + static bool print_freqflips = CommandLine()->FindParm("-glmspewfreqsrgbflips"); + static bool print_crawls = CommandLine()->FindParm("-glmspewsrgbcrawls"); + static bool print_maxcrawls = CommandLine()->FindParm("-glmspewsrgbmaxcrawls"); + bool print_it = false; + + if (print_allflips) + { + print_it = true; + } + if (print_firstflips) // report on first flip + { + print_it |= m_srgbFlipCount==1; + } + if (print_freqflips) // report on 50th flip + { + print_it |= m_srgbFlipCount==50; + } + + if ( print_it ) + { + char *formatStr; + formatStr = "srgb change (samp=%d): tex '%-30s' %08x %s (srgb=%d, %d times)"; + + if (strlen(texname) >= 30) + { + formatStr = "srgb change (samp=%d): tex '%s' %08x %s (srgb=%d, %d times)"; + } + + printf( "\n" ); + printf( formatStr, index, texname, m_layout->m_layoutSummary, (int)srgb, m_srgbFlipCount ); + +#ifdef POSIX + if (print_crawls) + { + static char *interesting_crawl_substrs[] = { "CShader::OnDrawElements", NULL }; // add more as needed + + CStackCrawlParams cp; + memset( &cp, 0, sizeof(cp) ); + cp.m_frameLimit = 20; + + g_pLauncherMgr->GetStackCrawl(&cp); + + for( int i=0; i< cp.m_frameCount; i++) + { + // for each row of crawl, decide if name is interesting + bool hit = print_maxcrawls; + + for( char **match = interesting_crawl_substrs; (!hit) && (*match != NULL); match++) + { + if (strstr(cp.m_crawlNames[i], *match)) + { + hit = true; + } + } + + if (hit) + { + printf( "\n\t%s", cp.m_crawlNames[i] ); + } + } + printf( "\n"); + } +#endif + } +#endif // GLMDEBUG + +#if GLMDEBUG && 0 + //"toi" = texture of interest + static char s_toi[256] = "colorcorrection"; + if (strstr( texname, s_toi )) + { + // breakpoint on this if you like + GLMPRINTF(( "srgb change %d for %s", m_srgbFlipCount, texname )); + } +#endif + + // re-submit the tex unless we're stifling it + static bool s_nosrgbflips = CommandLine()->FindParm( "-glmnosrgbflips" ); + if ( !s_nosrgbflips ) + { + ResetSRGB( srgb, false ); + } + } + else + { + //GLMPRINTF(("-Z- srgb sampling conflict: NOT fixing tex %08x [%s] (srgb req: %d) because (tex-srgb-capable=%d tex-renderable=%d)", m_textures[index], m_textures[index]->m_tex->m_layout->m_layoutSummary, (int)glsamp->m_srgb, (int)srgbCapableTex, (int)renderableTex )); + // we just leave the sampler state where it is, and that's life + } +} + + +void CGLMTex::ResetSRGB( bool srgb, bool noDataWrite ) +{ + // see if requested SRGB state differs from the known one + bool wasSRGB = (m_layout->m_key.m_texFlags & kGLMTexSRGB); + GLMTexLayout *oldLayout = m_layout; // need to m_ctx->m_texLayoutTable->DelLayoutRef on this one if we flip + + if (srgb != wasSRGB) + { + // we're going to need a new layout (though the storage size should be the same - check it) + GLMTexLayoutKey newKey = m_layout->m_key; + + newKey.m_texFlags &= (~kGLMTexSRGB); // turn off that bit + newKey.m_texFlags |= srgb ? kGLMTexSRGB : 0; // turn on that bit if it should be so + + // get new layout + GLMTexLayout *newLayout = m_ctx->m_texLayoutTable->NewLayoutRef( &newKey ); + + // if SRGB requested, verify that the layout we just got can do it. + // if it can't, delete the new layout ref and bail. + if (srgb && (newLayout->m_format->m_glIntFormatSRGB == 0)) + { + Assert( !"Can't enable SRGB mode on this format" ); + m_ctx->m_texLayoutTable->DelLayoutRef( newLayout ); + return; + } + + // check sizes and fail if no match + if( newLayout->m_storageTotalSize != oldLayout->m_storageTotalSize ) + { + Assert( !"Bug: layout sizes don't match on SRGB change" ); + m_ctx->m_texLayoutTable->DelLayoutRef( newLayout ); + return; + } + + // commit to new layout + m_layout = newLayout; + m_texGLTarget = m_layout->m_key.m_texGLTarget; + + // check same size + Assert( m_layout->m_storageTotalSize == oldLayout->m_storageTotalSize ); + + Assert( newLayout != oldLayout ); + + // release old + m_ctx->m_texLayoutTable->DelLayoutRef( oldLayout ); + oldLayout = NULL; + + // force texel re-DL + + // note this messes with TMU 0 as side effect of WriteTexels + // so we save and restore the TMU 0 binding first + + // since we're likely to be called in dxabstract when it is syncing sampler state, we can't go trampling the bindings. + // a refinement would be to have each texture make a note of which TMU they're bound on, and just use that active TMU for DL instead of 0. + CGLMTex *tmu0save = m_ctx->m_samplers[0].m_pBoundTex; + + for( int face=0; face m_faceCount; face++) + { + for( int mip=0; mip m_mipCount; mip++) + { + // we're not really going to lock, we're just going to rewrite the orig data + GLMTexLockDesc desc; + + desc.m_req.m_tex = this; + desc.m_req.m_face = face; + desc.m_req.m_mip = mip; + + desc.m_sliceIndex = CalcSliceIndex( face, mip ); + + GLMTexLayoutSlice *slice = &m_layout->m_slices[ desc.m_sliceIndex ]; + + desc.m_req.m_region.xmin = desc.m_req.m_region.ymin = desc.m_req.m_region.zmin = 0; + desc.m_req.m_region.xmax = slice->m_xSize; + desc.m_req.m_region.ymax = slice->m_ySize; + desc.m_req.m_region.zmax = slice->m_zSize; + + desc.m_sliceBaseOffset = slice->m_storageOffset; // doesn't really matter... we're just pushing zeroes.. + desc.m_sliceRegionOffset = 0; + + WriteTexels( &desc, true, noDataWrite ); // write whole slice. and avoid pushing real bits if the caller requests (RT's) + } + } + + // put it back + m_ctx->BindTexToTMU( tmu0save, 0 ); + } +} +#endif + +bool CGLMTex::IsRBODirty() const +{ + return m_nLastResolvedBatchCounter != m_ctx->m_nBatchCounter; +} + +void CGLMTex::ForceRBONonDirty() +{ + m_nLastResolvedBatchCounter = m_ctx->m_nBatchCounter; +} + +void CGLMTex::ForceRBODirty() +{ + m_nLastResolvedBatchCounter = m_ctx->m_nBatchCounter - 1; +} diff --git a/togles/linuxwin/dx9asmtogl2.cpp b/togles/linuxwin/dx9asmtogl2.cpp new file mode 100644 index 00000000..22be8fc9 --- /dev/null +++ b/togles/linuxwin/dx9asmtogl2.cpp @@ -0,0 +1,3838 @@ +//========= 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. +//------------------------------------------------------------------------------ +// DX9AsmToGL2.cpp +//------------------------------------------------------------------------------ +// Immediately include gl.h, etc. here to avoid compilation warnings. +#include +#include + +#include "togl/rendermechanism.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include "tier1/utlbuffer.h" +#include "dx9asmtogl2.h" + +#include "materialsystem/IShader.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef POSIX +#define strcat_s( a, b, c) V_strcat( a, c, b ) +#endif + +#define DST_REGISTER 0 +#define SRC_REGISTER 1 + +// Flags to PrintUsageAndIndexToString. +#define SEMANTIC_OUTPUT 0x01 +#define SEMANTIC_INPUT 0x02 + +#define UNDECLARED_OUTPUT 0xFFFFFFFF +#define UNDECLARED_INPUT 0xFFFFFFFF + +#ifndef POSIX +#define Debugger() Assert(0) +#endif + +//#define Assert(n) if( !(n) ){ TranslationError(); } + + +static char *g_szVecZeros[] = { NULL, "0.0", "vec2( 0.0, 0.0 )", "vec3( 0.0, 0.0, 0.0 )", "vec4( 0.0, 0.0, 0.0, 0.0 )" }; +static char *g_szVecOnes[] = { NULL, "1.0", "vec2( 1.0, 1.0 )", "vec3( 1.0, 1.0, 1.0 )", "vec4( 1.0, 1.0, 1.0, 1.0 )" }; +static char *g_szDefaultSwizzle = "xyzw"; +static char *g_szDefaultSwizzleStrings[] = { "x", "y", "z", "w" }; +static char *g_szSamplerStrings[] = { "2D", "CUBE", "3D" }; + +static const char *g_pAtomicTempVarName = "atomic_temp_var"; +static const char *g_pTangentAttributeName = "g_tangent"; + +int __cdecl SortInts( const int *a, const int *b ) +{ + if ( *a < *b ) + return -1; + else if ( *a > *b ) + return 1; + else + return 0; +} + +void StripExtraTrailingZeros( char *pStr ) +{ + int len = (int)V_strlen( pStr ); + while ( len >= 2 && pStr[len-1] == '0' && pStr[len-2] != '.' ) + { + pStr[len-1] = 0; + --len; + } +} + +void D3DToGL::PrintToBufWithIndents( CUtlBuffer &buf, const char *pFormat, ... ) +{ + va_list marker; + va_start( marker, pFormat ); + + char szTemp[1024]; + V_vsnprintf( szTemp, sizeof( szTemp ), pFormat, marker ); + va_end( marker ); + + PrintIndentation( (char*)buf.Base(), buf.Size() ); + strcat_s( (char*)buf.Base(), buf.Size(), szTemp ); +} + +void PrintToBuf( CUtlBuffer &buf, const char *pFormat, ... ) +{ + va_list marker; + va_start( marker, pFormat ); + + char szTemp[1024]; + V_vsnprintf( szTemp, sizeof( szTemp ), pFormat, marker ); + va_end( marker ); + + strcat_s( (char*)buf.Base(), buf.Size(), szTemp ); +} + +void PrintToBuf( char *pOut, int nOutSize, const char *pFormat, ... ) +{ + int nStrlen = V_strlen( pOut ); + pOut += nStrlen; + nOutSize -= nStrlen; + + va_list marker; + va_start( marker, pFormat ); + V_vsnprintf( pOut, nOutSize, pFormat, marker ); + va_end( marker ); +} + +// Return the number of letters following the dot. +// Returns 4 if there is no dot. +// (So "r0.xy" returns 2 and "r0" returns 4). +int GetNumWriteMaskEntries( const char *pParam ) +{ + const char *pDot = strchr( pParam, '.' ); + if ( pDot ) + return V_strlen( pDot + 1 ); + else + return 4; +} + +const char* GetSwizzleDot( const char *pParam ) +{ + const char *pDot = strrchr( pParam, '.' ); + + const char *pSquareClose = strrchr( pParam, ']' ); + + if ( pSquareClose ) + { + // The test against ']' catches cases like, so we point to the last dot vc[int(va_r.x) + 29].x + if ( pDot && ( pSquareClose < pDot ) ) + return pDot; + else + return NULL; + } + + // Make sure the next character is a valid swizzle since we want to treat strings like vec4( gl_Normal, 0.0 ) as a whole param name. + if ( pDot && ( ( *(pDot+1) == 'x' ) || ( *(pDot+1) == 'y' ) || ( *(pDot+1) == 'z' ) || ( *(pDot+1) == 'w' ) || + ( *(pDot+1) == 'r' ) || ( *(pDot+1) == 'g' ) || ( *(pDot+1) == 'b' ) || ( *(pDot+1) == 'z' ) ) ) + { + return pDot; + } + + return NULL; +} + +int GetNumSwizzleComponents( const char *pParam ) +{ + // Special scalar output which won't accept a swizzle + if ( !V_stricmp( pParam, "gl_FogFragCoord" ) ) + return 1; + + // Special scalar output which won't accept a swizzle + if ( !V_stricmp( pParam, "gl_FragDepth" ) ) + return 1; + + // Special scalar output which won't accept a swizzle + if ( !V_stricmp( pParam, "a0" ) ) + return 1; + + const char *pDot = GetSwizzleDot( pParam ); + if ( pDot ) + { + pDot++; // Step over the dot + + int nNumSwizzleComponents = 0; + while ( ( *pDot == 'x' ) || ( *pDot == 'y' ) || ( *pDot == 'z' ) || ( *pDot == 'w' ) || + ( *pDot == 'r' ) || ( *pDot == 'g' ) || ( *pDot == 'b' ) || ( *pDot == 'z' ) ) + { + nNumSwizzleComponents++; + pDot++; + } + + return nNumSwizzleComponents; + } + + return 0; +} + +char GetSwizzleComponent( const char *pParam, int n ) +{ + Assert( n < 4 ); + + const char *pDot = GetSwizzleDot( pParam ); + if ( pDot ) + { + ++pDot; + int nComponents = (int)V_strlen( pDot ); + Assert( nComponents > 0 ); + + if ( n < nComponents ) + return pDot[n]; + else + return pDot[nComponents-1]; + } + + return g_szDefaultSwizzle[n]; +} + +// Replace the parameter name and leave the swizzle intact. +// So "somevar.xyz" becomes "othervar.xyz". +void ReplaceParamName( const char *pSrc, const char *pNewParamName, char *pOut, int nOutLen ) +{ + // Start with the new parameter name. + V_strncpy( pOut, pNewParamName, nOutLen ); + + // Now add the swizzle if necessary. + const char *pDot = GetSwizzleDot( pSrc ); + if ( pDot ) + { + V_strncat( pOut, pDot, nOutLen ); + } +} + +void GetParamNameWithoutSwizzle( const char *pParam, char *pOut, int nOutLen ) +{ + char *pParamStart = (char *) pParam; + const char *pDot = GetSwizzleDot( pParam ); // dot followed by valid swizzle characters + bool bAbsWrapper = false; + + // Check for abs() or -abs() wrapper and strip it off during the fixup + if ( !V_strncmp( pParam, "abs(", 4 ) || !V_strncmp( pParam, "-abs(", 5 ) ) + { + const char *pOpenParen = strchr( pParam, '(' ); // FIRST opening paren + const char *pClosingParen = strrchr( pParam, ')' ); // LAST closing paren + + Assert ( pOpenParen && pClosingParen ); + pClosingParen; // hush compiler + + pParamStart = (char *) pOpenParen; + pParamStart++; + bAbsWrapper = true; + } + + if ( pDot ) + { + int nToCopy = MIN( nOutLen-1, pDot - pParamStart ); + memcpy( pOut, pParamStart, nToCopy ); + pOut[nToCopy] = 0; + } + else + { + V_strncpy( pOut, pParamStart, bAbsWrapper ? nOutLen - 1 : nOutLen ); + } +} + +bool DoParamNamesMatch( const char *pParam1, const char *pParam2 ) +{ + char szTemp[2][256]; + GetParamNameWithoutSwizzle( pParam1, szTemp[0], sizeof( szTemp[0] ) ); + GetParamNameWithoutSwizzle( pParam2, szTemp[1], sizeof( szTemp[1] ) ); + return ( V_stricmp( szTemp[0], szTemp[1] ) == 0 ); +} + + + +// Extract the n'th component of the swizzle mask. +// If n would exceed the length of the swizzle mask, then it looks up into "xyzw". +void WriteParamWithSingleMaskEntry( const char *pParam, int n, char *pOut, int nOutLen ) +{ + bool bCloseParen = false; + if ( !V_strncmp( pParam, "-abs(", 5 ) ) + { + V_strcpy( pOut, "-abs(" ); + bCloseParen = true; + + pOut += 5; nOutLen -= 5; + } + else if ( !V_strncmp( pParam, "abs(", 4 ) ) + { + V_strcpy( pOut, "abs(" ); + bCloseParen = true; + + pOut += 4; nOutLen -= 4; + } + + GetParamNameWithoutSwizzle( pParam, pOut, nOutLen ); + PrintToBuf( pOut, nOutLen, "." ); + PrintToBuf( pOut, nOutLen, "%c", GetSwizzleComponent( pParam, n ) ); + + if ( bCloseParen ) + { + PrintToBuf( pOut, nOutLen, ")" ); + } +} + + +float uint32ToFloat( uint32 dw ) +{ + return *((float*)&dw); +} + +CUtlString EnsureNumSwizzleComponents( const char *pSrcRegisterName, int nComponents ) +{ + int nExisting = GetNumSwizzleComponents( pSrcRegisterName ); + if ( nExisting == nComponents ) + return pSrcRegisterName; + + bool bAbsWrapper = false; // Parameter wrapped in an abs() + bool bAbsNegative = false; // -abs() + char szSrcRegister[128]; + V_strncpy( szSrcRegister, pSrcRegisterName, sizeof(szSrcRegister) ); + + // Check for abs() or -abs() wrapper and strip it off during the fixup + if ( !V_strncmp( pSrcRegisterName, "abs(", 4 ) || !V_strncmp( pSrcRegisterName, "-abs(", 5 ) ) + { + bAbsWrapper = true; + bAbsNegative = pSrcRegisterName[0] == '-'; + + const char *pOpenParen = strchr( pSrcRegisterName, '(' ); // FIRST opening paren + const char *pClosingParen = strrchr( pSrcRegisterName, ')' ); // LAST closing paren + + Assert ( pOpenParen && pClosingParen ); // If we start with abs( and don't get both parens, something is very wrong + + // Copy out just the register name with no abs() + int nRegNameLength = pClosingParen - pOpenParen - 1; + V_strncpy( szSrcRegister, pOpenParen+1, nRegNameLength + 1 ); // Kind of a weird function...copy more than you need and slam the last char to NULL-terminate + } + + char szReg[256]; + GetParamNameWithoutSwizzle( szSrcRegister, szReg, sizeof( szReg ) ); + if ( nComponents == 0 ) + return szReg; + + PrintToBuf( szReg, sizeof( szReg ), "." ); + if ( nExisting > nComponents ) + { + // DX ASM will sometimes have statements like "NRM r0.xyz, r1.yzww", where it just doesn't use the last part of r1. So we won't either. + for ( int i=0; i < nComponents; i++ ) + { + PrintToBuf( szReg, sizeof( szReg ), "%c", GetSwizzleComponent( szSrcRegister, i ) ); + } + } + else + { + if ( nExisting == 0 ) + { + // We've got something like r0 and need N more components, so add as much of "xyzw" is needed. + for ( int i=0; i < nComponents; i++ ) + PrintToBuf( szReg, sizeof( szReg ), "%c", g_szDefaultSwizzle[i] ); + } + else + { + // We've got something like r0.x and need N more components, so replicate the X so it looks like r0.xxx + V_strncpy( szReg, szSrcRegister, sizeof( szReg ) ); + char cLast = szSrcRegister[ V_strlen( szSrcRegister ) - 1 ]; + for ( int i=nExisting; i < nComponents; i++ ) + { + PrintToBuf( szReg, sizeof( szReg ), "%c", cLast ); + } + } + } + + if ( bAbsWrapper ) + { + char szTemp[128]; + V_strncpy( szTemp, szReg, sizeof(szTemp) ); + V_snprintf( szReg, sizeof( szReg ), "%sabs(%s)", bAbsNegative ? "-" : "", szTemp ) ; + } + + return szReg; +} + +static void TranslationError() +{ + GLMDebugPrintf( "D3DToGL: GLSL translation error!\n" ); + DebuggerBreakIfDebugging(); + + Error( "D3DToGL: GLSL translation error!\n" ); +} + +D3DToGL::D3DToGL() +{ +} + +uint32 D3DToGL::GetNextToken( void ) +{ + uint32 dwToken = *m_pdwNextToken; + m_pdwNextToken++; + return dwToken; +} + +void D3DToGL::SkipTokens( uint32 numToSkip ) +{ + m_pdwNextToken += numToSkip; +} + +uint32 D3DToGL::Opcode( uint32 dwToken ) +{ + return ( dwToken & D3DSI_OPCODE_MASK ); +} + +uint32 D3DToGL::OpcodeSpecificData (uint32 dwToken) +{ + return ( ( dwToken & D3DSP_OPCODESPECIFICCONTROL_MASK ) >> D3DSP_OPCODESPECIFICCONTROL_SHIFT ); +} + +uint32 D3DToGL::TextureType ( uint32 dwToken ) +{ + return ( dwToken & D3DSP_TEXTURETYPE_MASK ); // Note this one doesn't shift due to weird D3DSAMPLER_TEXTURE_TYPE enum +} + + + +// Print GLSL intrinsic corresponding to particular instruction +bool D3DToGL::OpenIntrinsic( uint32 inst, char* buff, int nBufLen, uint32 destDimension, uint32 nArgumentDimension ) +{ + // Some GLSL intrinsics need type conversion, which we do in this routine + // As a result, the caller must sometimes close both parentheses, not just one + bool bDoubleClose = false; + + if ( nArgumentDimension == 0 ) + { + nArgumentDimension = 4; + } + + switch ( inst ) + { + case D3DSIO_RSQ: + V_snprintf( buff, nBufLen, "inversesqrt( " ); + break; + case D3DSIO_DP3: + case D3DSIO_DP4: + if ( destDimension == 1 ) + { + V_snprintf( buff, nBufLen, "dot( " ); + } + else + { + if ( !destDimension ) + destDimension = 4; + V_snprintf( buff, nBufLen, "vec%d( dot( ", destDimension ); + bDoubleClose = true; + } + break; + case D3DSIO_MIN: + V_snprintf( buff, nBufLen, "min( " ); + break; + case D3DSIO_MAX: + V_snprintf( buff, nBufLen, "max( " ); + break; + case D3DSIO_SLT: + if ( nArgumentDimension == 1 ) + { + V_snprintf( buff, nBufLen, "float( " ); // lessThan doesn't have a scalar version + } + else + { + Assert( nArgumentDimension > 1 ); + V_snprintf( buff, nBufLen, "vec%d( lessThan( ", nArgumentDimension ); + bDoubleClose = true; + } + break; + case D3DSIO_SGE: + if ( nArgumentDimension == 1 ) + { + V_snprintf( buff, nBufLen, "float( " ); // greaterThanEqual doesn't have a scalar version + } + else + { + Assert( nArgumentDimension > 1 ); + V_snprintf( buff, nBufLen, "vec%d( greaterThanEqual( ", nArgumentDimension ); + bDoubleClose = true; + } + break; + case D3DSIO_EXP: + V_snprintf( buff, nBufLen, "exp( " ); // exp2 ? + break; + case D3DSIO_LOG: + V_snprintf( buff, nBufLen, "log( " ); // log2 ? + break; + case D3DSIO_LIT: + TranslationError(); + V_snprintf( buff, nBufLen, "lit( " ); // gonna have to write this one + break; + case D3DSIO_DST: + V_snprintf( buff, nBufLen, "dst( " ); // gonna have to write this one + break; + case D3DSIO_LRP: + Assert( !m_bVertexShader ); + V_snprintf( buff, nBufLen, "mix( " ); + break; + case D3DSIO_FRC: + V_snprintf( buff, nBufLen, "fract( " ); + break; + case D3DSIO_M4x4: + TranslationError(); + V_snprintf( buff, nBufLen, "m4x4" ); + break; + case D3DSIO_M4x3: + case D3DSIO_M3x4: + case D3DSIO_M3x3: + case D3DSIO_M3x2: + case D3DSIO_CALL: + case D3DSIO_CALLNZ: + case D3DSIO_LOOP: + case D3DSIO_RET: + case D3DSIO_ENDLOOP: + case D3DSIO_LABEL: + case D3DSIO_DCL: + TranslationError(); + break; + case D3DSIO_POW: + V_snprintf( buff, nBufLen, "pow( " ); + break; + case D3DSIO_CRS: + V_snprintf( buff, nBufLen, "cross( " ); + break; + case D3DSIO_SGN: + TranslationError(); + V_snprintf( buff, nBufLen, "sign( " ); + break; + case D3DSIO_ABS: + V_snprintf( buff, nBufLen, "abs( " ); + break; + case D3DSIO_NRM: + TranslationError(); + V_snprintf( buff, nBufLen, "normalize( " ); + break; + case D3DSIO_SINCOS: + TranslationError(); + V_snprintf( buff, nBufLen, "sincos( " ); // gonna have to write this one + break; + case D3DSIO_REP: + case D3DSIO_ENDREP: + case D3DSIO_IF: + case D3DSIO_IFC: + case D3DSIO_ELSE: + case D3DSIO_ENDIF: + case D3DSIO_BREAK: + case D3DSIO_BREAKC: // TODO: these are the reason we even need GLSL...gotta make these work + TranslationError(); + break; + case D3DSIO_DEFB: + case D3DSIO_DEFI: + TranslationError(); + break; + case D3DSIO_TEXCOORD: + V_snprintf( buff, nBufLen, "texcoord" ); + break; + case D3DSIO_TEXKILL: + V_snprintf( buff, nBufLen, "kill( " ); // wrap the discard instruction? + break; + case D3DSIO_TEX: + TranslationError(); + V_snprintf( buff, nBufLen, "TEX" ); // We shouldn't get here + break; + case D3DSIO_TEXBEM: + case D3DSIO_TEXBEML: + case D3DSIO_TEXREG2AR: + case D3DSIO_TEXREG2GB: + case D3DSIO_TEXM3x2PAD: + case D3DSIO_TEXM3x2TEX: + case D3DSIO_TEXM3x3PAD: + case D3DSIO_TEXM3x3TEX: + case D3DSIO_TEXM3x3SPEC: + case D3DSIO_TEXM3x3VSPEC: + TranslationError(); + break; + case D3DSIO_EXPP: + V_snprintf( buff, nBufLen, "exp( " ); + break; + case D3DSIO_LOGP: + V_snprintf( buff, nBufLen, "log( " ); + break; + case D3DSIO_CND: + TranslationError(); + break; + case D3DSIO_DEF: + TranslationError(); + V_snprintf( buff, nBufLen, "DEF" ); + break; + case D3DSIO_TEXREG2RGB: + case D3DSIO_TEXDP3TEX: + case D3DSIO_TEXM3x2DEPTH: + case D3DSIO_TEXDP3: + case D3DSIO_TEXM3x3: + TranslationError(); + break; + case D3DSIO_TEXDEPTH: + V_snprintf( buff, nBufLen, "texdepth" ); + break; + case D3DSIO_CMP: + TranslationError(); + Assert( !m_bVertexShader ); + V_snprintf( buff, nBufLen, "CMP" ); + break; + case D3DSIO_BEM: + TranslationError(); + break; + case D3DSIO_DP2ADD: + TranslationError(); + break; + case D3DSIO_DSX: + case D3DSIO_DSY: + TranslationError(); + break; + case D3DSIO_TEXLDD: + V_snprintf( buff, nBufLen, "texldd" ); + break; + case D3DSIO_SETP: + TranslationError(); + break; + case D3DSIO_TEXLDL: + V_snprintf( buff, nBufLen, "texldl" ); + break; + case D3DSIO_BREAKP: + case D3DSIO_PHASE: + TranslationError(); + break; + } + + return bDoubleClose; +} + + +const char* D3DToGL::GetGLSLOperatorString( uint32 inst ) +{ + if ( inst == D3DSIO_ADD ) + return "+"; + else if ( inst == D3DSIO_SUB ) + return "-"; + else if ( inst == D3DSIO_MUL ) + return "*"; + + Error( "GetGLSLOperatorString: unknown operator" ); + return "zzzz"; +} + + +// Print ASM opcode +void D3DToGL::PrintOpcode( uint32 inst, char* buff, int nBufLen ) +{ + switch ( inst ) + { + case D3DSIO_NOP: + V_snprintf( buff, nBufLen, "NOP" ); + TranslationError(); + break; + case D3DSIO_MOV: + V_snprintf( buff, nBufLen, "MOV" ); + break; + case D3DSIO_ADD: + V_snprintf( buff, nBufLen, "ADD" ); + break; + case D3DSIO_SUB: + V_snprintf( buff, nBufLen, "SUB" ); + break; + case D3DSIO_MAD: + V_snprintf( buff, nBufLen, "MAD" ); + break; + case D3DSIO_MUL: + V_snprintf( buff, nBufLen, "MUL" ); + break; + case D3DSIO_RCP: + V_snprintf( buff, nBufLen, "RCP" ); + break; + case D3DSIO_RSQ: + V_snprintf( buff, nBufLen, "RSQ" ); + break; + case D3DSIO_DP3: + V_snprintf( buff, nBufLen, "DP3" ); + break; + case D3DSIO_DP4: + V_snprintf( buff, nBufLen, "DP4" ); + break; + case D3DSIO_MIN: + V_snprintf( buff, nBufLen, "MIN" ); + break; + case D3DSIO_MAX: + V_snprintf( buff, nBufLen, "MAX" ); + break; + case D3DSIO_SLT: + V_snprintf( buff, nBufLen, "SLT" ); + break; + case D3DSIO_SGE: + V_snprintf( buff, nBufLen, "SGE" ); + break; + case D3DSIO_EXP: + V_snprintf( buff, nBufLen, "EX2" ); + break; + case D3DSIO_LOG: + V_snprintf( buff, nBufLen, "LG2" ); + break; + case D3DSIO_LIT: + V_snprintf( buff, nBufLen, "LIT" ); + break; + case D3DSIO_DST: + V_snprintf( buff, nBufLen, "DST" ); + break; + case D3DSIO_LRP: + Assert( !m_bVertexShader ); + V_snprintf( buff, nBufLen, "LRP" ); + break; + case D3DSIO_FRC: + V_snprintf( buff, nBufLen, "FRC" ); + break; + case D3DSIO_M4x4: + V_snprintf( buff, nBufLen, "m4x4" ); + break; + case D3DSIO_M4x3: + case D3DSIO_M3x4: + case D3DSIO_M3x3: + case D3DSIO_M3x2: + case D3DSIO_CALL: + case D3DSIO_CALLNZ: + case D3DSIO_LOOP: + case D3DSIO_RET: + case D3DSIO_ENDLOOP: + case D3DSIO_LABEL: + TranslationError(); + break; + case D3DSIO_DCL: + V_snprintf( buff, nBufLen, "DCL" ); + break; + case D3DSIO_POW: + V_snprintf( buff, nBufLen, "POW" ); + break; + case D3DSIO_CRS: + V_snprintf( buff, nBufLen, "XPD" ); + break; + case D3DSIO_SGN: + TranslationError(); + V_snprintf( buff, nBufLen, "SGN" ); + break; + case D3DSIO_ABS: + V_snprintf( buff, nBufLen, "ABS" ); + break; + case D3DSIO_NRM: + TranslationError(); + V_snprintf( buff, nBufLen, "NRM" ); + break; + case D3DSIO_SINCOS: + Assert( !m_bVertexShader ); + V_snprintf( buff, nBufLen, "SCS" ); + break; + case D3DSIO_REP: + case D3DSIO_ENDREP: + case D3DSIO_IF: + case D3DSIO_IFC: + case D3DSIO_ELSE: + case D3DSIO_ENDIF: + case D3DSIO_BREAK: + case D3DSIO_BREAKC: + TranslationError(); + break; + case D3DSIO_MOVA: + Assert( m_bVertexShader ); + V_snprintf( buff, nBufLen, "MOV" ); // We're always moving into a temp instead, so this is MOV instead of ARL + break; + case D3DSIO_DEFB: + case D3DSIO_DEFI: + TranslationError(); + break; + case D3DSIO_TEXCOORD: + V_snprintf( buff, nBufLen, "texcoord" ); + break; + case D3DSIO_TEXKILL: + V_snprintf( buff, nBufLen, "KIL" ); + break; + case D3DSIO_TEX: + V_snprintf( buff, nBufLen, "TEX" ); + break; + case D3DSIO_TEXBEM: + case D3DSIO_TEXBEML: + case D3DSIO_TEXREG2AR: + case D3DSIO_TEXREG2GB: + case D3DSIO_TEXM3x2PAD: + case D3DSIO_TEXM3x2TEX: + case D3DSIO_TEXM3x3PAD: + case D3DSIO_TEXM3x3TEX: + case D3DSIO_TEXM3x3SPEC: + case D3DSIO_TEXM3x3VSPEC: + TranslationError(); + break; + case D3DSIO_EXPP: + V_snprintf( buff, nBufLen, "EXP" ); + break; + case D3DSIO_LOGP: + V_snprintf( buff, nBufLen, "LOG" ); + break; + case D3DSIO_CND: + TranslationError(); + break; + case D3DSIO_DEF: + V_snprintf( buff, nBufLen, "DEF" ); + break; + case D3DSIO_TEXREG2RGB: + case D3DSIO_TEXDP3TEX: + case D3DSIO_TEXM3x2DEPTH: + case D3DSIO_TEXDP3: + case D3DSIO_TEXM3x3: + TranslationError(); + break; + case D3DSIO_TEXDEPTH: + V_snprintf( buff, nBufLen, "texdepth" ); + break; + case D3DSIO_CMP: + Assert( !m_bVertexShader ); + V_snprintf( buff, nBufLen, "CMP" ); + break; + case D3DSIO_BEM: + TranslationError(); + break; + case D3DSIO_DP2ADD: + TranslationError(); + break; + case D3DSIO_DSX: + case D3DSIO_DSY: + TranslationError(); + break; + case D3DSIO_TEXLDD: + V_snprintf( buff, nBufLen, "texldd" ); + break; + case D3DSIO_SETP: + TranslationError(); + break; + case D3DSIO_TEXLDL: + V_snprintf( buff, nBufLen, "texldl" ); + break; + case D3DSIO_BREAKP: + case D3DSIO_PHASE: + TranslationError(); + break; + } +} + +CUtlString D3DToGL::GetUsageAndIndexString( uint32 dwToken, int fSemanticFlags ) +{ + char szTemp[1024]; + PrintUsageAndIndexToString( dwToken, szTemp, sizeof( szTemp ), fSemanticFlags ); + return szTemp; +} + +//------------------------------------------------------------------------------ +// Helper function which prints ASCII representation of usage-usageindex pair to string +// +// Strictly used by vertex shaders +// not used any more now that we have attribmap metadata +//------------------------------------------------------------------------------ +void D3DToGL::PrintUsageAndIndexToString( uint32 dwToken, char* strUsageUsageIndexName, int nBufLen, int fSemanticFlags ) +{ + uint32 dwUsage = ( dwToken & D3DSP_DCL_USAGE_MASK ); + uint32 dwUsageIndex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + switch ( dwUsage ) + { + case D3DDECLUSAGE_POSITION: + if ( m_bVertexShader ) + { + if ( fSemanticFlags & SEMANTIC_OUTPUT ) + V_snprintf( strUsageUsageIndexName, nBufLen, "vTempPos" ); // effectively gl_Position + else + V_snprintf( strUsageUsageIndexName, nBufLen, "gl_Vertex" ); + } + else + { + // .xy = position in viewport coordinates + // .z = depth + V_snprintf( strUsageUsageIndexName, nBufLen, "gl_FragCoord" ); + } + + break; + case D3DDECLUSAGE_BLENDWEIGHT: + V_snprintf( strUsageUsageIndexName, nBufLen, "vertex.attrib[1]" ); // "vertex.attrib[12]" ); // or [1] + break; + case D3DDECLUSAGE_BLENDINDICES: + V_snprintf( strUsageUsageIndexName, nBufLen, "vertex.attrib[13]" ); // "vertex.attrib[13]" ); // or [ 7 ] + break; + case D3DDECLUSAGE_NORMAL: + V_snprintf( strUsageUsageIndexName, nBufLen, "vec4( gl_Normal, 0.0 )" ); + break; + case D3DDECLUSAGE_PSIZE: + TranslationError(); + V_snprintf( strUsageUsageIndexName, nBufLen, "_psize" ); // no analog + break; + case D3DDECLUSAGE_TEXCOORD: + V_snprintf( strUsageUsageIndexName, nBufLen, "oT%d", dwUsageIndex ); + break; + case D3DDECLUSAGE_TANGENT: + + NoteTangentInputUsed(); + V_strncpy( strUsageUsageIndexName, g_pTangentAttributeName, nBufLen ); + + break; + case D3DDECLUSAGE_BINORMAL: + V_snprintf( strUsageUsageIndexName, nBufLen, "vertex.attrib[14]" ); // aka texc[6] + break; +// case D3DDECLUSAGE_TESSFACTOR: +// TranslationError(); +// V_snprintf( strUsageUsageIndexName, nBufLen, "_position" ); // no analog +// break; +// case D3DDECLUSAGE_POSITIONT: +// TranslationError(); +// V_snprintf( strUsageUsageIndexName, nBufLen, "_positiont" ); // no analog +// break; + case D3DDECLUSAGE_COLOR: + + Assert( dwUsageIndex <= 1 ); +// if ( fSemanticFlags & SEMANTIC_OUTPUT ) +// V_snprintf( strUsageUsageIndexName, nBufLen, dwUsageIndex != 0 ? "gl_BackColor" : "gl_FrontColor" ); +// else + V_snprintf( strUsageUsageIndexName, nBufLen, dwUsageIndex != 0 ? "gl_SecondaryColor" : "gl_Color" ); + + break; + case D3DDECLUSAGE_FOG: + TranslationError(); + break; + case D3DDECLUSAGE_DEPTH: + TranslationError(); + V_snprintf( strUsageUsageIndexName, nBufLen, "_depth" ); // no analog + break; + case D3DDECLUSAGE_SAMPLE: + TranslationError(); + V_snprintf( strUsageUsageIndexName, nBufLen, "_sample" ); // no analog + break; + default: + Debugger(); + break; + } +} + +uint32 D3DToGL::GetRegType( uint32 dwRegToken ) +{ + return ( ( dwRegToken & D3DSP_REGTYPE_MASK2 ) >> D3DSP_REGTYPE_SHIFT2 ) | ( ( dwRegToken & D3DSP_REGTYPE_MASK ) >> D3DSP_REGTYPE_SHIFT ); +} + +void D3DToGL::PrintIndentation( char *pBuf, int nBufLen ) +{ + for( int i=0; i 5 && V_strcmp( &pRegister[nLen-5], ".xyzw" ) == 0 ) + pRegister[nLen-5] = 0; +} + + +// This returns 0 for x, 1 for y, 2 for z, and 3 for w. +int GetSwizzleComponentVectorIndex( char chMask ) +{ + if ( chMask == 'x' ) + return 0; + else if ( chMask == 'y' ) + return 1; + else if ( chMask == 'z' ) + return 2; + else if ( chMask == 'w' ) + return 3; + + Error( "GetSwizzleComponentVectorIndex( '%c' ) - invalid parameter.\n", chMask ); + return 0; +} + + +// GLSL needs the # of src masks to match the dest write mask. +// +// So this: +// r0.xy = r1 + r2; +// becomes: +// r0.xy = r1.xy + r2.xy; +// +// +// Also, and this is the trickier one: GLSL reads the source registers from their first component on +// whereas D3D reads them as referenced in the dest register mask! +// +// So this code in D3D: +// r0.yz = c0.x + c1.wxyz +// Really means: +// r0.y = c0.x + c1.x +// r0.z = c0.x + c1.y +// So we translate it to this in GLSL: +// r0.yz = c0.xx + c1.wx +// r0.yz = c0.xx + c1.xy +// +CUtlString D3DToGL::FixGLSLSwizzle( const char *pDestRegisterName, const char *pSrcRegisterName ) +{ + bool bAbsWrapper = false; // Parameter wrapped in an abs() + bool bAbsNegative = false; // -abs() + char szSrcRegister[128]; + V_strncpy( szSrcRegister, pSrcRegisterName, sizeof(szSrcRegister) ); + + // Check for abs() or -abs() wrapper and strip it off during the fixup + if ( !V_strncmp( pSrcRegisterName, "abs(", 4 ) || !V_strncmp( pSrcRegisterName, "-abs(", 5 ) ) + { + bAbsWrapper = true; + bAbsNegative = pSrcRegisterName[0] == '-'; + + const char *pOpenParen = strchr( pSrcRegisterName, '(' ); // FIRST opening paren + const char *pClosingParen = strrchr( pSrcRegisterName, ')' ); // LAST closing paren + + Assert ( pOpenParen && pClosingParen ); // If we start with abs( and don't get both parens, something is very wrong + + // Copy out just the register name with no abs() + int nRegNameLength = pClosingParen - pOpenParen - 1; + V_strncpy( szSrcRegister, pOpenParen+1, nRegNameLength + 1 ); // Kind of a weird function...copy more than you need and slam the last char to NULL-terminate + + } + + int nSwizzlesInDest = GetNumSwizzleComponents( pDestRegisterName ); + if ( nSwizzlesInDest == 0 ) + nSwizzlesInDest = 4; + + char szFixedSrcRegister[128]; + GetParamNameWithoutSwizzle( szSrcRegister, szFixedSrcRegister, sizeof( szFixedSrcRegister ) ); + V_strncat( szFixedSrcRegister, ".", sizeof( szFixedSrcRegister ) ); + for ( int i=0; i < nSwizzlesInDest; i++ ) + { + char chDestWriteMask = GetSwizzleComponent( pDestRegisterName, i ); + int nVectorIndex = GetSwizzleComponentVectorIndex( chDestWriteMask ); + + char ch[2]; + ch[0] = GetSwizzleComponent( szSrcRegister, nVectorIndex ); + ch[1] = 0; + V_strncat( szFixedSrcRegister, ch, sizeof( szFixedSrcRegister ) ); + } + + SimplifyFourParamRegister( szFixedSrcRegister ); + + if ( bAbsWrapper ) + { + char szTempSrcRegister[128]; + V_strncpy( szTempSrcRegister, szFixedSrcRegister, sizeof(szTempSrcRegister) ); + V_snprintf( szFixedSrcRegister, sizeof( szFixedSrcRegister ), "%sabs(%s)", bAbsNegative ? "-" : "", szTempSrcRegister ) ; + } + + return szFixedSrcRegister; +} + +// Weird encoding...bits are split apart in the dwToken +inline uint32 GetRegTypeFromToken( uint32 dwToken ) +{ + return ( ( dwToken & D3DSP_REGTYPE_MASK2 ) >> D3DSP_REGTYPE_SHIFT2 ) | ( ( dwToken & D3DSP_REGTYPE_MASK ) >> D3DSP_REGTYPE_SHIFT ); +} + +void D3DToGL::FlagIndirectRegister( uint32 dwToken, int *pARLDestReg ) +{ + if ( !pARLDestReg ) + return; + + switch ( dwToken & D3DVS_SWIZZLE_MASK & D3DVS_X_W ) + { + case D3DVS_X_X: + *pARLDestReg = ARL_DEST_X; + break; + case D3DVS_X_Y: + *pARLDestReg = ARL_DEST_Y; + break; + case D3DVS_X_Z: + *pARLDestReg = ARL_DEST_Z; + break; + case D3DVS_X_W: + *pARLDestReg = ARL_DEST_W; + break; + } +} + + +//------------------------------------------------------------------------------ +// PrintParameterToString() +// +// Helper function which prints ASCII representation of passed Parameter dwToken +// to string. Token defines parameter details. The dwSourceOrDest parameter says +// whether or not this is a source or destination register +//------------------------------------------------------------------------------ +void D3DToGL::PrintParameterToString ( uint32 dwToken, uint32 dwSourceOrDest, char *pRegisterName, int nBufLen, bool bForceScalarSource, int *pARLDestReg ) +{ + char buff[32]; + bool bAllowWriteMask = true; + bool bAllowSwizzle = true; + + uint32 dwRegNum = dwToken & D3DSP_REGNUM_MASK; + + uint32 dwRegType, dwSwizzle; + uint32 dwSrcModifier = D3DSPSM_NONE; + + // Clear string to zero length + pRegisterName[ 0 ] = 0; + + dwRegType = GetRegTypeFromToken( dwToken ); + + // If this is a dest register + if ( dwSourceOrDest == DST_REGISTER ) + { + // Instruction modifiers + if ( dwToken & D3DSPDM_PARTIALPRECISION ) + { +// strcat_s( pRegisterName, nBufLen, "_pp" ); + } + + if ( dwToken & D3DSPDM_MSAMPCENTROID) + { +// strcat_s( pRegisterName, nBufLen, "_centroid" ); + } + } + + // If this is a source register + if ( dwSourceOrDest == SRC_REGISTER ) + { + dwSrcModifier = dwToken & D3DSP_SRCMOD_MASK; + + // If there are any source modifiers, check to see if they're at + // least partially "prefix" and prepend appropriately + if ( dwSrcModifier != D3DSPSM_NONE ) + { + switch ( dwSrcModifier ) + { + // These four start with just minus... (some may result in "postfix" notation as well later on) + case D3DSPSM_NEG: // negate + strcat_s( pRegisterName, nBufLen, "-" ); + break; + case D3DSPSM_BIASNEG: // bias and negate + case D3DSPSM_SIGNNEG: // sign and negate + case D3DSPSM_X2NEG: // *2 and negate + TranslationError(); + strcat_s( pRegisterName, nBufLen, "-" ); + break; + case D3DSPSM_COMP: // complement + TranslationError(); + strcat_s( pRegisterName, nBufLen, "1-" ); + break; + case D3DSPSM_ABS: // abs() + strcat_s( pRegisterName, nBufLen, "abs(" ); + + break; + case D3DSPSM_ABSNEG: // -abs() + strcat_s( pRegisterName, nBufLen, "-abs(" ); + + break; + case D3DSPSM_NOT: // for predicate register: "!p0" + TranslationError(); + strcat_s( pRegisterName, nBufLen, "!" ); + break; + } + } + } + + // Register name (from type and number) + switch ( dwRegType ) + { + case D3DSPR_TEMP: + V_snprintf( buff, sizeof( buff ), "r%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + m_dwTempUsageMask |= 0x00000001 << dwRegNum; // Keep track of the use of this temp + break; + case D3DSPR_INPUT: + if ( !m_bVertexShader && ( dwSourceOrDest == SRC_REGISTER ) ) + { + if ( m_dwMajorVersion == 3 ) + { + V_snprintf( buff, sizeof( buff ), "oTempT%d", dwRegNum ); + } + else + { + V_snprintf( buff, sizeof( buff ), dwRegNum == 0 ? "gl_Color" : "gl_SecondaryColor" ); + } + strcat_s( pRegisterName, nBufLen, buff ); + } + else + { + V_snprintf( buff, sizeof( buff ), "v%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + } + break; + case D3DSPR_CONST: + if ( m_bConstantRegisterDefined[dwRegNum] ) + { + char szConstantRegName[3]; + if ( m_bVertexShader ) + { + V_snprintf( szConstantRegName, 3, "vd" ); + } + else + { + V_snprintf( szConstantRegName, 3, "pd" ); + } + + // Put defined constants into their own namespace "d" + V_snprintf( buff, sizeof( buff ), "%s%d", szConstantRegName, dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + } + else if ( dwToken & D3DSHADER_ADDRESSMODE_MASK ) // Indirect addressing (e.g. skinning in a vertex shader) + { + char szConstantRegName[16]; + if ( m_bVertexShader ) + { + V_snprintf( szConstantRegName, 3, "vc" ); + } + else // No indirect addressing in PS, this shouldn't happen + { + TranslationError(); + V_snprintf( szConstantRegName, 3, "pc" ); + } + + if ( ( m_bGenerateBoneUniformBuffer ) && ( dwRegNum >= DXABSTRACT_VS_FIRST_BONE_SLOT ) ) + { + if( dwRegNum < DXABSTRACT_VS_LAST_BONE_SLOT ) + { + dwRegNum -= DXABSTRACT_VS_FIRST_BONE_SLOT; + V_strcpy( szConstantRegName, "vcbones" ); + + m_nHighestBoneRegister = ( DXABSTRACT_VS_PARAM_SLOTS - 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT; + } + else + { + dwRegNum -= ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT; + m_nHighestRegister = m_bGenerateBoneUniformBuffer ? ( ( DXABSTRACT_VS_PARAM_SLOTS - 1 ) - ( ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT ) ): ( DXABSTRACT_VS_PARAM_SLOTS - 1 ); + } + } + else + { + m_nHighestRegister = m_bGenerateBoneUniformBuffer ? ( ( DXABSTRACT_VS_PARAM_SLOTS - 1 ) - ( ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT ) ): ( DXABSTRACT_VS_PARAM_SLOTS - 1 ); + } + + // Index into single pc/vc[] register array with relative addressing + int nDstReg = -1; + FlagIndirectRegister( GetNextToken(), &nDstReg ); + if ( pARLDestReg ) + *pARLDestReg = nDstReg; + + Assert( nDstReg != ARL_DEST_NONE ); + int nSrcSwizzle = 'x'; + if ( nDstReg == ARL_DEST_Y ) + nSrcSwizzle = 'y'; + else if ( nDstReg == ARL_DEST_Z ) + nSrcSwizzle = 'z'; + else if ( nDstReg == ARL_DEST_W ) + nSrcSwizzle = 'w'; + V_snprintf( buff, sizeof( buff ), "%s[int(va_r.%c) + %d]", szConstantRegName, nSrcSwizzle, dwRegNum ); + + strcat_s( pRegisterName, nBufLen, buff ); + + // Must allow swizzling, otherwise this example doesn't compile right: mad r3.xyz, c27[a0.w].w, r3, r7 + //bAllowSwizzle = false; + } + else // Direct addressing of constant array + { + char szConstantRegName[16]; + V_snprintf( szConstantRegName, 3, m_bVertexShader ? "vc" : "pc" ); + + if ( ( m_bGenerateBoneUniformBuffer ) && ( dwRegNum >= DXABSTRACT_VS_FIRST_BONE_SLOT ) ) + { + if( dwRegNum < DXABSTRACT_VS_LAST_BONE_SLOT ) + { + dwRegNum -= DXABSTRACT_VS_FIRST_BONE_SLOT; + V_strcpy( szConstantRegName, "vcbones" ); + + m_nHighestBoneRegister = MAX( m_nHighestBoneRegister, (int)dwRegNum ); + } + else + { + // handles case where constants after the bones are used (c217 onwards), these are to be concatenated with those before the bones (c0-c57) + // keep track of regnum for concatenated array + dwRegNum -= ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT; + m_nHighestRegister = MAX( m_nHighestRegister, dwRegNum ); + } + } + else + { + //// NOGO if (dwRegNum != 255) // have seen cases where dwRegNum is 0xFF... need to figure out where those opcodes are coming from + { + m_nHighestRegister = MAX( m_nHighestRegister, dwRegNum ); + } + + Assert( m_nHighestRegister < DXABSTRACT_VS_PARAM_SLOTS ); + } + + // Index into single pc/vc[] register array with absolute addressing, same for GLSL and ASM + V_snprintf( buff, sizeof( buff ), "%s[%d]", szConstantRegName, dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + } + break; + case D3DSPR_ADDR: // aliases to D3DSPR_TEXTURE + if ( m_bVertexShader ) + { + Assert( dwRegNum == 0 ); + + V_snprintf( buff, sizeof( buff ), "va_r" ); + } + else // D3DSPR_TEXTURE in the pixel shader + { + // If dest reg, this is an iterator/varying declaration + if ( dwSourceOrDest == DST_REGISTER ) + { + // Is this iterator centroid? + if ( m_nCentroidMask & ( 0x00000001 << dwRegNum ) ) + { + V_snprintf( buff, sizeof( buff ), "centroid varying vec4 oT%d", dwRegNum ); // centroid varying + } + else + { + V_snprintf( buff, sizeof( buff ), "varying vec4 oT%d", dwRegNum ); + } + + bAllowWriteMask = false; + } + else // source register + { + V_snprintf( buff, sizeof( buff ), "oT%d", dwRegNum ); + } + } + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_RASTOUT: // vertex shader oPos + Assert( m_bVertexShader ); + Assert( m_dwMajorVersion == 2 ); + switch( dwRegNum ) + { + case D3DSRO_POSITION: + strcat_s( pRegisterName, nBufLen, "vTempPos" ); // In GLSL, this ends up in gl_Position later on + m_bDeclareVSOPos = true; + break; + + case D3DSRO_FOG: + strcat_s( pRegisterName, nBufLen, "gl_FogFragCoord" ); + m_bDeclareVSOFog = true; + break; + + default: + printf( "\nD3DSPR_RASTOUT: dwRegNum is %08x and token is %08x", dwRegNum, dwToken ); + TranslationError(); + break; + } + break; + case D3DSPR_ATTROUT: + Assert( m_bVertexShader ); + Assert( m_dwMajorVersion == 2 ); + + if ( dwRegNum == 0 ) + { + V_snprintf( buff, sizeof( buff ), "gl_FrontColor" ); + } + else if ( dwRegNum == 1 ) + { + V_snprintf( buff, sizeof( buff ), "gl_FrontSecondaryColor" ); + } + else + { + Error( "Invalid D3DSPR_ATTROUT index" ); + } + + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_TEXCRDOUT: // aliases to D3DSPR_OUTPUT + if ( m_bVertexShader ) + { + if ( m_nVSPositionOutput == (int32) dwRegNum ) + { + V_snprintf( buff, sizeof( buff ), "vTempPos" ); // This output varying is the position + } + else if ( m_dwMajorVersion == 3 ) + { + V_snprintf( buff, sizeof( buff ), "oTempT%d", dwRegNum ); + } + else + { + V_snprintf( buff, sizeof( buff ), "oT%d", dwRegNum ); + } + + m_dwTexCoordOutMask |= ( 0x00000001 << dwRegNum ); + } + else + { + V_snprintf( buff, sizeof( buff ), "oC%d", dwRegNum ); + } + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_CONSTINT: + V_snprintf( buff, sizeof( buff ), "i%d", dwRegNum ); // Loops use these + strcat_s( pRegisterName, nBufLen, buff ); + m_dwConstIntUsageMask |= 0x00000001 << dwRegNum; // Keep track of the use of this integer constant + break; + case D3DSPR_COLOROUT: + V_snprintf( buff, sizeof( buff ), "gl_FragData[%d]", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + m_bOutputColorRegister[dwRegNum] = true; + break; + case D3DSPR_DEPTHOUT: + V_snprintf( buff, sizeof( buff ), "gl_FragDepth" ); + strcat_s( pRegisterName, nBufLen, buff ); + m_bOutputDepthRegister = true; + break; + case D3DSPR_SAMPLER: + V_snprintf( buff, sizeof( buff ), "sampler%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_CONST2: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "c%d", dwRegNum+2048); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_CONST3: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "c%d", dwRegNum+4096); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_CONST4: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "c%d", dwRegNum+6144); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_CONSTBOOL: + V_snprintf( buff, sizeof( buff ), m_bVertexShader ? "b%d" : "fb%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + m_dwConstBoolUsageMask |= 0x00000001 << dwRegNum; // Keep track of the use of this bool constant + break; + case D3DSPR_LOOP: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "aL%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_TEMPFLOAT16: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "temp_float16_xxx%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_MISCTYPE: + Assert( dwRegNum == 0 ); // So far, we know that MISC[0] is gl_FragCoord (aka vPos in DX ASM parlance), but we don't know about any other MISC registers + V_snprintf( buff, sizeof( buff ), "gl_FragCoord" ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_LABEL: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "label%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_PREDICATE: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "p%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + } + + // If this is a dest register + if ( dwSourceOrDest == DST_REGISTER ) + { + // + // Write masks + // + // If some (not all, not none) of the write masks are set, we should include them + // + if ( bAllowWriteMask && ( !((dwToken & D3DSP_WRITEMASK_ALL) == D3DSP_WRITEMASK_ALL) || ((dwToken & D3DSP_WRITEMASK_ALL) == 0x00000000) ) ) + { + // Put the dot on there + strcat_s( pRegisterName, nBufLen, "." ); + + // Optionally put on the x, y, z or w + int nMasksWritten = 0; + if ( dwToken & D3DSP_WRITEMASK_0 ) + { + strcat_s( pRegisterName, nBufLen, "x" ); + ++nMasksWritten; + } + if ( dwToken & D3DSP_WRITEMASK_1 ) + { + strcat_s( pRegisterName, nBufLen, "y" ); + ++nMasksWritten; + } + if ( dwToken & D3DSP_WRITEMASK_2 ) + { + strcat_s( pRegisterName, nBufLen, "z" ); + ++nMasksWritten; + } + if ( dwToken & D3DSP_WRITEMASK_3 ) + { + strcat_s( pRegisterName, nBufLen, "w" ); + ++nMasksWritten; + } + } + } + else // must be a source register + { + if ( bAllowSwizzle ) // relative addressing hard-codes the swizzle on a0.x + { + uint32 dwXSwizzle, dwYSwizzle, dwZSwizzle, dwWSwizzle; + + // Mask out the swizzle modifier + dwSwizzle = dwToken & D3DVS_SWIZZLE_MASK; + + // If there are any swizzles at all, tack on the appropriate notation + if ( dwSwizzle != D3DVS_NOSWIZZLE ) + { + // Separate out the two-bit codes for each component swizzle + dwXSwizzle = dwSwizzle & D3DVS_X_W; + dwYSwizzle = dwSwizzle & D3DVS_Y_W; + dwZSwizzle = dwSwizzle & D3DVS_Z_W; + dwWSwizzle = dwSwizzle & D3DVS_W_W; + + // Put on the dot + strcat_s( pRegisterName, nBufLen, "." ); + + // See where X comes from + switch ( dwXSwizzle ) + { + case D3DVS_X_X: + strcat_s( pRegisterName, nBufLen, "x" ); + break; + case D3DVS_X_Y: + strcat_s( pRegisterName, nBufLen, "y" ); + break; + case D3DVS_X_Z: + strcat_s( pRegisterName, nBufLen, "z" ); + break; + case D3DVS_X_W: + strcat_s( pRegisterName, nBufLen, "w" ); + break; + } + + if ( !bForceScalarSource ) + { + // If the source of the remaining components are aren't + // identical to the source of x, continue with swizzle + if ( ((dwXSwizzle >> D3DVS_SWIZZLE_SHIFT) != (dwYSwizzle >> (D3DVS_SWIZZLE_SHIFT + 2))) || // X and Y sources match? + ((dwXSwizzle >> D3DVS_SWIZZLE_SHIFT) != (dwZSwizzle >> (D3DVS_SWIZZLE_SHIFT + 4))) || // X and Z sources match? + ((dwXSwizzle >> D3DVS_SWIZZLE_SHIFT) != (dwWSwizzle >> (D3DVS_SWIZZLE_SHIFT + 6)))) // X and W sources match? + { + + // OpenGL seems to want us to have either 1 or 4 components in a swizzle, so just plow on through the rest + switch ( dwYSwizzle ) + { + case D3DVS_Y_X: + strcat_s( pRegisterName, nBufLen, "x" ); + break; + case D3DVS_Y_Y: + strcat_s( pRegisterName, nBufLen, "y" ); + break; + case D3DVS_Y_Z: + strcat_s( pRegisterName, nBufLen, "z" ); + break; + case D3DVS_Y_W: + strcat_s( pRegisterName, nBufLen, "w" ); + break; + } + + switch ( dwZSwizzle ) + { + case D3DVS_Z_X: + strcat_s( pRegisterName, nBufLen, "x" ); + break; + case D3DVS_Z_Y: + strcat_s( pRegisterName, nBufLen, "y" ); + break; + case D3DVS_Z_Z: + strcat_s( pRegisterName, nBufLen, "z" ); + break; + case D3DVS_Z_W: + strcat_s( pRegisterName, nBufLen, "w" ); + break; + } + + switch ( dwWSwizzle ) + { + case D3DVS_W_X: + strcat_s( pRegisterName, nBufLen, "x" ); + break; + case D3DVS_W_Y: + strcat_s( pRegisterName, nBufLen, "y" ); + break; + case D3DVS_W_Z: + strcat_s( pRegisterName, nBufLen, "z" ); + break; + case D3DVS_W_W: + strcat_s( pRegisterName, nBufLen, "w" ); + break; + } + + } + + } // end !bForceScalarSource + } + else // dwSwizzle == D3DVS_NOSWIZZLE + { + // If this is a MOVA / ARL, GL on the Mac requires us to tack the .x onto the source register + if ( bForceScalarSource ) + { + strcat_s( pRegisterName, nBufLen, ".x" ); + } + } + } // bAllowSwizzle + + // If there are any source modifiers, check to see if they're at + // least partially "postfix" and tack them on as appropriate + if ( dwSrcModifier != D3DSPSM_NONE ) + { + switch ( dwSrcModifier ) + { + case D3DSPSM_BIAS: // bias + case D3DSPSM_BIASNEG: // bias and negate + TranslationError(); + strcat_s( pRegisterName, nBufLen, "_bx2" ); + break; + case D3DSPSM_SIGN: // sign + case D3DSPSM_SIGNNEG: // sign and negate + TranslationError(); + strcat_s( pRegisterName, nBufLen, "_sgn" ); + break; + case D3DSPSM_X2: // *2 + case D3DSPSM_X2NEG: // *2 and negate + TranslationError(); + strcat_s( pRegisterName, nBufLen, "_x2" ); + break; + case D3DSPSM_ABS: // abs() + case D3DSPSM_ABSNEG: // -abs() + strcat_s( pRegisterName, nBufLen, ")" ); + break; + case D3DSPSM_DZ: // divide through by z component + TranslationError(); + strcat_s( pRegisterName, nBufLen, "_dz" ); + break; + case D3DSPSM_DW: // divide through by w component + TranslationError(); + strcat_s( pRegisterName, nBufLen, "_dw" ); + break; + } + } // end postfix modifiers (really only ps.1.x) + } +} + +void D3DToGL::RecordInputAndOutputPositions() +{ + // Remember where we are in the token stream. + m_pRecordedInputTokenStart = m_pdwNextToken; + + // Remember where our outputs are. + m_nRecordedParamCodeStrlen = V_strlen( (char*)m_pBufParamCode->Base() ); + m_nRecordedALUCodeStrlen = V_strlen( (char*)m_pBufALUCode->Base() ); + m_nRecordedAttribCodeStrlen = V_strlen( (char*)m_pBufAttribCode->Base() ); +} +void D3DToGL::AddTokenHexCodeToBuffer( char *pBuffer, int nSize, int nLastStrlen ) +{ + int nCurStrlen = V_strlen( pBuffer ); + if ( nCurStrlen == nLastStrlen ) + return; + + // Build a string with all the hex codes of the tokens since last time. + char szHex[512]; + szHex[0] = '\n'; + V_snprintf( &szHex[1], sizeof( szHex )-1, HEXCODE_HEADER ); + int nTokens = MIN( 10, m_pdwNextToken - m_pRecordedInputTokenStart ); + for ( int i=0; i < nTokens; i++ ) + { + char szTemp[32]; + V_snprintf( szTemp, sizeof( szTemp ), "0x%x ", m_pRecordedInputTokenStart[i] ); + V_strncat( szHex, szTemp, sizeof( szHex ) ); + } + V_strncat( szHex, "\n", sizeof( szHex ) ); + + // Insert the hex codes into the string. + int nBytesToInsert = V_strlen( szHex ); + if ( nCurStrlen + nBytesToInsert + 1 >= nSize ) + Error( "Buffer overflow writing token hex codes" ); + + if ( m_bPutHexCodesAfterLines ) + { + // Put it at the end of the last line. + if ( pBuffer[nCurStrlen-1] == '\n' ) + pBuffer[nCurStrlen-1] = 0; + + V_strncat( pBuffer, &szHex[1], nSize ); + } + else + { + memmove( pBuffer + nLastStrlen + nBytesToInsert, pBuffer + nLastStrlen, nCurStrlen - nLastStrlen + 1 ); + memcpy( pBuffer + nLastStrlen, szHex, nBytesToInsert ); + } +} + +void D3DToGL::AddTokenHexCode() +{ + if ( m_pdwNextToken > m_pRecordedInputTokenStart ) + { + AddTokenHexCodeToBuffer( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size(), m_nRecordedParamCodeStrlen ); + AddTokenHexCodeToBuffer( (char*)m_pBufALUCode->Base(), m_pBufALUCode->Size(), m_nRecordedALUCodeStrlen ); + AddTokenHexCodeToBuffer( (char*)m_pBufAttribCode->Base(), m_pBufAttribCode->Size(), m_nRecordedAttribCodeStrlen ); + } +} + +uint32 D3DToGL::MaintainAttributeMap( uint32 dwToken, uint32 dwRegToken ) +{ + // Check that this reg index has not been used before - if it has, let Houston know + uint dwRegIndex = dwRegToken & D3DSP_REGNUM_MASK; + if ( m_dwAttribMap[ dwRegIndex ] == 0xFFFFFFFF ) + { + // log it + // semantic/usage in the higher nibble + // usage index in the low nibble + + uint usage = dwToken & D3DSP_DCL_USAGE_MASK; + uint usageindex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + m_dwAttribMap[ dwRegIndex ] = ( usage << 4 ) | usageindex; + + // avoid writing 0xBB since runtime code uses that for an 'unused' marker + if ( m_dwAttribMap[ dwRegIndex ] == 0xBB ) + { + Debugger(); + } + } + else + { + //not OK + Debugger(); + } + + return dwRegIndex; +} + +void D3DToGL::Handle_DCL() +{ + uint32 dwToken = GetNextToken(); // What kind of dcl is this... + uint32 dwRegToken = GetNextToken(); // Look ahead to register token + + uint32 dwUsage = ( dwToken & D3DSP_DCL_USAGE_MASK ); + uint32 dwUsageIndex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + uint32 dwRegNum = dwRegToken & D3DSP_REGNUM_MASK; + uint32 nRegType = GetRegTypeFromToken( dwRegToken ); + + if ( m_bVertexShader ) + { + // If this is an output, remember the index (what the ASM code calls o0, o1, o2..) and the semantic. + // When GetParameterString( DST_REGISTER ) hits this one, we'll return "oN". + // At the end of the main() function, we'll insert a bunch of statements like "gl_Color = o2" based on what we remembered here. + if ( ( m_dwMajorVersion >= 3 ) && ( nRegType == D3DSPR_OUTPUT ) ) + { +// uint32 dwRegComponents = ( dwRegToken & D3DSP_WRITEMASK_ALL ) >> 16; // Components used by the output register (1 means float, 3 means vec2, 7 means vec3, f means vec4) + + if ( dwRegNum >= MAX_DECLARED_OUTPUTS ) + Error( "Output register number (%d) too high (only %d supported).", dwRegNum, MAX_DECLARED_OUTPUTS ); + + if ( m_DeclaredOutputs[dwRegNum] != UNDECLARED_OUTPUT ) + Error( "Output dcl_ hit for register #%d more than once!", dwRegNum ); + + Assert( dwToken != UNDECLARED_OUTPUT ); + m_DeclaredOutputs[dwRegNum] = dwToken; + + //uint32 dwUsage = ( dwToken & D3DSP_DCL_USAGE_MASK ); + //uint32 dwUsageIndex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + // Flag which o# output register maps to gl_Position + if ( dwUsage == D3DDECLUSAGE_POSITION ) + { + m_nVSPositionOutput = dwUsageIndex; + m_bDeclareVSOPos = true; + } + + if ( m_bAddHexCodeComments ) + { + CUtlString sParam2 = GetUsageAndIndexString( dwToken, SEMANTIC_OUTPUT ); + PrintToBuf( *m_pBufHeaderCode, "// [GL remembering that oT%d maps to %s]\n", dwRegNum, sParam2.String() ); + } + + } + else if ( GetRegType( dwRegToken ) == D3DSPR_SAMPLER ) + { + // We can support vertex texturing if necessary, but I can't find a use case in any branch. (HW morphing in L4D2 isn't enabled, and the comments indicate that r_hwmorph isn't compatible with mat_queue_mode anyway, and CS:GO/DoTA don't use vertex shader texturing.) + TranslationError(); + + int nRegNum = dwRegToken & D3DSP_REGNUM_MASK; + switch ( TextureType( dwToken ) ) + { + default: + case D3DSTT_UNKNOWN: + case D3DSTT_2D: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_2D; + break; + case D3DSTT_CUBE: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_CUBE; + break; + case D3DSTT_VOLUME: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_3D; + break; + } + + // Track sampler declarations + m_dwSamplerUsageMask |= 1 << nRegNum; + } + else + { + Assert( GetRegType( dwRegToken ) == D3DSPR_INPUT); + + CUtlString sParam1 = GetParameterString( dwRegToken, DST_REGISTER, false, NULL ); + CUtlString sParam2 = GetUsageAndIndexString( dwToken, SEMANTIC_INPUT ); + + sParam2 = FixGLSLSwizzle( sParam1, sParam2 ); + PrintToBuf( *m_pBufHeaderCode, "attribute vec4 %s; // ", sParam1.String() ); + + MaintainAttributeMap( dwToken, dwRegToken ); + + char temp[128]; + // regnum goes straight into the vertex.attrib[n] index + sprintf( temp, "%08x %08x\n", dwToken, dwRegToken ); + StrcatToHeaderCode( temp ); + } + } + else // Pixel shader + { + // If the register is a sampler, the dcl has a dimension decorator that we have to save for subsequent TEX instructions + uint32 nRegType = GetRegType( dwRegToken ); + if ( nRegType == D3DSPR_SAMPLER ) + { + int nRegNum = dwRegToken & D3DSP_REGNUM_MASK; + switch ( TextureType( dwToken ) ) + { + default: + case D3DSTT_UNKNOWN: + case D3DSTT_2D: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_2D; + break; + case D3DSTT_CUBE: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_CUBE; + break; + case D3DSTT_VOLUME: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_3D; + break; + } + + // Track sampler declarations + m_dwSamplerUsageMask |= 1 << nRegNum; + } + else // Not a sampler, we're going to generate varying declaration code + { + // In pixel shaders we only declare texture coordinate varyings since they may be using centroid + if ( ( m_dwMajorVersion == 3 ) && ( nRegType == D3DSPR_INPUT ) ) + { + Assert( m_DeclaredInputs[dwRegNum] == UNDECLARED_INPUT ); + m_DeclaredInputs[dwRegNum] = dwToken; + + if ( ( dwUsage != D3DDECLUSAGE_COLOR ) && ( dwUsage != D3DDECLUSAGE_TEXCOORD ) ) + { + TranslationError(); // Not supported yet, but can be if we need it. + } + + if ( dwUsage == D3DDECLUSAGE_TEXCOORD ) + { + char buf[256]; + if ( m_nCentroidMask & ( 0x00000001 << dwUsageIndex ) ) + { + V_snprintf( buf, sizeof( buf ), "centroid varying vec4 oT%d;\n", dwUsageIndex ); // centroid varying + } + else + { + V_snprintf( buf, sizeof( buf ), "varying vec4 oT%d;\n", dwUsageIndex ); + } + StrcatToHeaderCode( buf ); + } + } + else if ( nRegType == D3DSPR_TEXTURE ) + { + char buff[256]; + PrintParameterToString( dwRegToken, DST_REGISTER, buff, sizeof( buff ), false, NULL ); + PrintToBuf( *m_pBufHeaderCode, "%s;\n",buff ); + } + else + { + // No need to declare anything (probably D3DSPR_MISCTYPE either VPOS or VFACE) + } + } + } +} + +static bool IsFloatNaN( float f ) +{ + const uint nBits = *reinterpret_cast(&f); + const uint nExponent = ( nBits >> 23 ) & 0xFF; + + return ( nExponent == 255 ); +} + +static inline bool EqualTol( double a, double b, double t ) +{ + return fabs( a - b ) <= ( ( MAX( fabs( a ), fabs( b ) ) + 1.0 ) * t ); +} + +// Originally written by Bruce Dawson, see: +// See http://randomascii.wordpress.com/2012/03/08/float-precisionfrom-zero-to-100-digits-2/ +// This class represents a very limited high-precision number with 'count' 32-bit +// unsigned elements. +template +struct HighPrec +{ + typedef unsigned T; + typedef unsigned long long Product_t; + static const int kWordShift = 32; + HighPrec() + { + memset(m_data, 0, sizeof(m_data)); + m_nLowestNonZeroIndex = ARRAYSIZE(m_data); + } + + // Insert the bits from value into m_data, shifted in from the bottom (least + // significant end) by the specified number of bits. A shift of zero or less + // means that none of the bits will be shifted in. A shift of one means that + // the high bit of value will be in the bottom of the last element of m_data - + // the least significant bit. A shift of kWordShift means that value will be + // in the least significant element of m_data, and so on. + void InsertLowBits(T value, int shiftAmount) + { + if (shiftAmount <= 0) + return; + + int subShift = shiftAmount & (kWordShift - 1); + int bigShift = shiftAmount / kWordShift; + Product_t result = (Product_t)value << subShift; + T resultLow = (T)result; + T resultHigh = result >> kWordShift; + + // Use an unsigned type so that negative numbers will become large, + // which makes the range checking below simpler. + unsigned highIndex = ARRAYSIZE(m_data) - 1 - bigShift; + // Write the results to the data array. If the index is too large + // then that means that the data was shifted off the edge. + if ( (highIndex < ARRAYSIZE(m_data)) && ( resultHigh ) ) + { + m_data[highIndex] |= resultHigh; + m_nLowestNonZeroIndex = MIN( m_nLowestNonZeroIndex, highIndex ); + } + + if ( ( highIndex + 1 < ARRAYSIZE(m_data)) && ( resultLow ) ) + { + m_data[highIndex + 1] |= resultLow; + m_nLowestNonZeroIndex = MIN( m_nLowestNonZeroIndex, highIndex + 1 ); + } + } + + // Insert the bits from value into m_data, shifted in from the top (most + // significant end) by the specified number of bits. A shift of zero or less + // means that none of the bits will be shifted in. A shift of one means that + // the low bit of value will be in the top of the first element of m_data - + // the most significant bit. A shift of kWordShift means that value will be + // in the most significant element of m_data, and so on. + void InsertTopBits(T value, int shiftAmount) + { + InsertLowBits(value, (ARRAYSIZE(m_data) + 1) * kWordShift - shiftAmount); + } + + // Return true if all elements of m_data are zero. + bool IsZero() const + { + bool bIsZero = ( m_nLowestNonZeroIndex == ARRAYSIZE(m_data) ); + +#ifdef DEBUG + for (int i = 0; i < ARRAYSIZE(m_data); ++i) + { + if (m_data[i]) + { + Assert( !bIsZero ); + return false; + } + } + Assert( bIsZero ); +#endif + + return bIsZero; + } + + // Divide by div and return the remainder, from 0 to div-1. + // Standard long-division algorithm. + T DivReturnRemainder(T divisor) + { + T remainder = 0; + +#ifdef DEBUG + for (uint j = 0; j < m_nLowestNonZeroIndex; ++j) + { + Assert( m_data[j] == 0 ); + } +#endif + + int nNewLowestNonZeroIndex = ARRAYSIZE(m_data); + for (int i = m_nLowestNonZeroIndex; i < ARRAYSIZE(m_data); ++i) + { + Product_t dividend = ((Product_t)remainder << kWordShift) + m_data[i]; + Product_t result = dividend / divisor; + remainder = T(dividend % divisor); + + m_data[i] = T(result); + + if ( ( result ) && ( nNewLowestNonZeroIndex == ARRAYSIZE(m_data) ) ) + nNewLowestNonZeroIndex = i; + } + m_nLowestNonZeroIndex = nNewLowestNonZeroIndex; + + return remainder; + } + + // The individual 'digits' (32-bit unsigned integers actually) that + // make up the number. The most-significant digit is in m_data[0]. + T m_data[count]; + + uint m_nLowestNonZeroIndex; +}; + +union Double_t +{ + Double_t(double num = 0.0f) : f(num) {} + // Portable extraction of components. + bool Negative() const { return (i >> 63) != 0; } + int64_t RawMantissa() const { return i & ((1LL << 52) - 1); } + int64_t RawExponent() const { return (i >> 52) & 0x7FF; } + + int64_t i; + double f; +}; + +static uint PrintDoubleInt( char *pBuf, uint nBufSize, double f, uint nMinChars ) +{ + static const char *pDigits = "00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899"; + + Assert( !nMinChars || ( ( nMinChars % 6 ) == 0 ) ); + + char *pLastChar = pBuf + nBufSize - 1; + char *pDst = pLastChar; + *pDst-- = '\0'; + + // Put the double in our magic union so we can grab the components. + union Double_t num(f); + + // Get the character that represents the sign. + // Check for NaNs or infinity. + if (num.RawExponent() == 2047) + { + TranslationError(); + } + + // Adjust for the exponent bias. + int exponentValue = int(num.RawExponent() - 1023); + // Add the implied one to the mantissa. + uint64_t mantissaValue = (1ll << 52) + num.RawMantissa(); + // Special-case for denormals - no special exponent value and + // no implied one. + if (num.RawExponent() == 0) + { + exponentValue = -1022; + mantissaValue = num.RawMantissa(); + } + uint32_t mantissaHigh = mantissaValue >> 32; + uint32_t mantissaLow = mantissaValue & 0xFFFFFFFF; + + // The first bit of the mantissa has an implied value of one and this can + // be shifted 1023 positions to the left, so that's 1024 bits to the left + // of the binary point, or 32 32-bit words for the integer part. + HighPrec<32> intPart; + // When our exponentValue is zero (a number in the 1.0 to 2.0 range) + // we have a 53-bit mantissa and the implied value of the highest bit + // is 1. We need to shift 12 bits in from the bottom to get that 53rd bit + // into the ones spot in the integral portion. + // To complicate it a bit more we have to insert the mantissa as two parts. + intPart.InsertLowBits(mantissaHigh, 12 + exponentValue); + intPart.InsertLowBits(mantissaLow, 12 + exponentValue - 32); + + bool bAnyDigitsLeft; + do + { + uint remainder = intPart.DivReturnRemainder( 1000000 ); // 10^6 + uint origRemainer = remainder; (void)origRemainer; + + bAnyDigitsLeft = !intPart.IsZero(); + + if ( bAnyDigitsLeft ) + { + uint n = remainder % 100U; remainder /= 100U; *reinterpret_cast(pDst - 1) = reinterpret_cast(pDigits)[n]; + n = remainder % 100U; remainder /= 100U; *reinterpret_cast(pDst - 1 - 2) = reinterpret_cast(pDigits)[n]; + Assert( remainder < 100U ); + *reinterpret_cast(pDst - 1 - 4) = reinterpret_cast(pDigits)[remainder]; + pDst -= 6; + } + else + { + uint n = remainder % 100U; remainder /= 100U; *reinterpret_cast(pDst - 1) = reinterpret_cast(pDigits)[n]; --pDst; if ( ( n >= 10 ) || ( remainder ) ) --pDst; + if ( remainder ) + { + n = remainder % 100U; remainder /= 100U; *reinterpret_cast(pDst - 1) = reinterpret_cast(pDigits)[n]; --pDst; if ( ( n >= 10 ) || ( remainder ) ) --pDst; + + if ( remainder ) + { + Assert( remainder < 100U ); + *reinterpret_cast(pDst - 1) = reinterpret_cast(pDigits)[remainder]; --pDst; if ( remainder >= 10 ) --pDst; + } + } + } + + } while ( bAnyDigitsLeft ); + + uint l = pLastChar - pDst; + + while ( ( l - 1 ) < nMinChars ) + { + *pDst-- = '0'; + l++; + } + + Assert( (int)l == ( pLastChar - pDst ) ); + + Assert( l <= nBufSize ); + + memmove( pBuf, pDst + 1, l ); + return l - 1; +} + +// FloatToString is equivalent to sprintf( "%.12f" ), but doesn't have any dependencies on the current locale setting. +// Unfortunately, high accuracy radix conversion is actually pretty tricky to do right. +// Most importantly, this function has the same max roundtrip (IEEE->ASCII->IEEE) error as the MS CRT functions and can reliably handle extremely large inputs. +static void FloatToString( char *pBuf, uint nBufSize, double fConst ) +{ + char *pEnd = pBuf + nBufSize; + char *pDst = pBuf; + + double flVal = fConst; + if ( IsFloatNaN( flVal ) ) + { + flVal = 0; + } + + if ( flVal < 0.0f ) + { + *pDst++ = '-'; + flVal = -flVal; + } + + double flInt; + double flFract = modf( flVal, &flInt ); + + flFract = floor( flFract * 1000000000000.0 + .5 ); + + if ( !flInt ) + { + *pDst++ = '0'; + } + else + { + uint l = PrintDoubleInt( pDst, pEnd - pDst, flInt, 0 ); + pDst += l; + } + + *pDst++ = '.'; + if ( !flFract ) + { + *pDst++ = '0'; + *pDst++ = '\0'; + } + else + { + uint l = PrintDoubleInt( pDst, pEnd - pDst, flFract, 12 ); + pDst += l; + + StripExtraTrailingZeros( pBuf ); // Turn 1.00000 into 1.0 + } +} + +#if 0 +#include "vstdlib/random.h" +static void TestFloatConversion() +{ + for ( ; ; ) + { + double fConst; + switch ( rand() % 4 ) + { + case 0: + fConst = RandomFloat( -1e-30, 1e+30 ); break; + case 1: + fConst = RandomFloat( -1e-10, 1e+10 ); break; + case 2: + fConst = RandomFloat( -1e-5, 1e+5 ); break; + default: + fConst = RandomFloat( -1, 1 ); break; + } + + char szTemp[1024]; + + // FloatToString does not rely on V_snprintf(), so it can't be affected by the current locale setting. + FloatToString( szTemp, sizeof( szTemp ), fConst ); + + static double flMaxErr1; + static double flMaxErr2; + + // Compare FloatToString()'s results vs. V_snprintf()'s, also track maximum error of each. + double flCheck = atof( szTemp ); + double flErr = fabs( flCheck - fConst ); + flMaxErr1 = MAX( flMaxErr1, flErr ); + Assert( EqualTol( flCheck, fConst, .000000125 ) ); + + char szTemp2[256]; + V_snprintf( szTemp2, sizeof( szTemp2 ), "%.12f", fConst ); + StripExtraTrailingZeros( szTemp2 ); + + if ( !strchr( szTemp2, '.' ) ) + { + V_strncat( szTemp2, ".0", sizeof( szTemp2 ) ); + } + double flCheck2 = atof( szTemp2 ); + double flErr2 = fabs( flCheck2 - fConst ); + flMaxErr2 = MAX( flMaxErr2, flErr2 ); + Assert( EqualTol( flCheck2, fConst, .000000125 ) ); + + if ( flMaxErr1 > flMaxErr2 ) + { + GLMDebugPrintf( "!\n" ); + } + } +} +#endif + +void D3DToGL::Handle_DEFIB( uint32 instruction ) +{ + Assert( ( instruction == D3DSIO_DEFI ) || ( instruction == D3DSIO_DEFB ) ); + + // which register is being defined + uint32 dwToken = GetNextToken(); + + uint32 nRegNum = dwToken & D3DSP_REGNUM_MASK; + + uint32 regType = GetRegTypeFromToken( dwToken ); + + + if ( regType == D3DSPR_CONSTINT ) + { + m_dwDefConstIntUsageMask |= ( 1 << nRegNum ); + + uint x = GetNextToken(); + uint y = GetNextToken(); + uint z = GetNextToken(); + uint w = GetNextToken(); + NOTE_UNUSED(y); NOTE_UNUSED(z); NOTE_UNUSED(w); + + Assert( nRegNum < 32 ); + if ( nRegNum < 32 ) + { + m_dwDefConstIntIterCount[nRegNum] = x; + } + } + else + { + TranslationError(); + } + +} + +void D3DToGL::Handle_DEF() +{ + //TestFloatConversion(); + + // + // JasonM TODO: catch D3D's sincos-specific D3DSINCOSCONST1 and D3DSINCOSCONST2 constants and filter them out here + // + + // Which register is being defined + uint32 dwToken = GetNextToken(); + + // Note that this constant was explicitly defined + m_bConstantRegisterDefined[dwToken & D3DSP_REGNUM_MASK] = true; + CUtlString sParamName = GetParameterString( dwToken, DST_REGISTER, false, NULL ); + + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + PrintToBuf( *m_pBufParamCode, "vec4 %s = vec4( ", sParamName.String() ); + + // Run through the 4 floats + for ( int i=0; i < 4; i++ ) + { + float fConst = uint32ToFloat( GetNextToken() ); + + char szTemp[1024]; + + FloatToString( szTemp, sizeof( szTemp ), fConst ); + +#if 0 + static double flMaxErr1; + static double flMaxErr2; + + // Compare FloatToString()'s results vs. V_snprintf()'s, also track maximum error of each. + double flCheck = atof( szTemp ); + double flErr = fabs( flCheck - fConst ); + flMaxErr1 = MAX( flMaxErr1, flErr ); + Assert( EqualTol( flCheck, fConst, .000000125 ) ); + + char szTemp2[256]; + V_snprintf( szTemp2, sizeof( szTemp2 ), "%.12f", fConst ); + StripExtraTrailingZeros( szTemp2 ); + + if ( !strchr( szTemp2, '.' ) ) + { + V_strncat( szTemp2, ".0", sizeof( szTemp2 ) ); + } + double flCheck2 = atof( szTemp2 ); + double flErr2 = fabs( flCheck2 - fConst ); + flMaxErr2 = MAX( flMaxErr2, flErr2 ); + Assert( EqualTol( flCheck2, fConst, .000000125 ) ); + + if ( flMaxErr1 > flMaxErr2 ) + { + GLMDebugPrintf( "!\n" ); + } +#endif + + PrintToBuf( *m_pBufParamCode, i != 3 ? "%s, " : "%s", szTemp ); // end with comma-space + } + + PrintToBuf( *m_pBufParamCode, " );\n" ); +} + +void D3DToGL::Handle_MAD( uint32 nInstruction ) +{ + uint32 nDestToken = GetNextToken(); + CUtlString sParam1 = GetParameterString( nDestToken, DST_REGISTER, false, NULL ); + int nARLComp0 = ARL_DEST_NONE; + CUtlString sParam2 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp0 ); + int nARLComp1 = ARL_DEST_NONE; + CUtlString sParam3 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp1 ); + int nARLComp2 = ARL_DEST_NONE; + CUtlString sParam4 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp2 ); + + // This optionally inserts a move from our dummy address register to the .x component of the real one + InsertMoveFromAddressRegister( m_pBufALUCode, nARLComp0, nARLComp1, nARLComp2 ); + + sParam2 = FixGLSLSwizzle( sParam1, sParam2 ); + sParam3 = FixGLSLSwizzle( sParam1, sParam3 ); + sParam4 = FixGLSLSwizzle( sParam1, sParam4 ); + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s * %s + %s;\n", sParam1.String(), sParam2.String(), sParam3.String(), sParam4.String() ); + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( sParam1.String() ); + if ( nComponents == 0 ) + nComponents = 4; + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", sParam1.String(), sParam1.String(), g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + + +void D3DToGL::Handle_DP2ADD() +{ + char pDestReg[64], pSrc0Reg[64], pSrc1Reg[64], pSrc2Reg[64]; + uint32 nDestToken = GetNextToken(); + PrintParameterToString( nDestToken, DST_REGISTER, pDestReg, sizeof( pDestReg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc0Reg, sizeof( pSrc0Reg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc1Reg, sizeof( pSrc1Reg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc2Reg, sizeof( pSrc2Reg ), false, NULL ); + + // We should only be assigning to a single component of the dest. + Assert( GetNumSwizzleComponents( pDestReg ) == 1 ); + Assert( GetNumSwizzleComponents( pSrc2Reg ) == 1 ); + + // This is a 2D dot product, so we only want two entries from the middle components. + CUtlString sArg0 = EnsureNumSwizzleComponents( pSrc0Reg, 2 ); + CUtlString sArg1 = EnsureNumSwizzleComponents( pSrc1Reg, 2 ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = dot( %s, %s ) + %s;\n", pDestReg, sArg0.String(), sArg1.String(), pSrc2Reg ); + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( pDestReg ); + if ( nComponents == 0 ) + nComponents = 4; + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", pDestReg, pDestReg, g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + + +void D3DToGL::Handle_SINCOS() +{ + char pDestReg[64], pSrc0Reg[64]; + PrintParameterToString( GetNextToken(), DST_REGISTER, pDestReg, sizeof( pDestReg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc0Reg, sizeof( pSrc0Reg ), true, NULL ); + m_bNeedsSinCosDeclarations = true; + + + CUtlString sDest( pDestReg ); + CUtlString sArg0 = EnsureNumSwizzleComponents( pSrc0Reg, 1 );// Ensure input is scalar + CUtlString sResult( "vSinCosTmp.xy" ); // Always going to populate this + sResult = FixGLSLSwizzle( sDest, sResult ); // Make sure we match the desired output reg + + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.z = %s * %s;\n", sArg0.String(), sArg0.String() ); + + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.xy = vSinCosTmp.zz * scA.xy + scA.wz;\n" ); + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.xy = vSinCosTmp.xy * vSinCosTmp.zz + scB.xy;\n" ); + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.xy = vSinCosTmp.xy * vSinCosTmp.zz + scB.wz;\n" ); + + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.x = vSinCosTmp.x * %s;\n", sArg0.String() ); + + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.xy = vSinCosTmp.xy * vSinCosTmp.xx;\n" ); + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.xy = vSinCosTmp.xy + vSinCosTmp.xy;\n" ); + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.x = -vSinCosTmp.x + scB.z;\n" ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s;\n", sDest.String(), sResult.String() ); + + if ( m_dwMajorVersion < 3 ) + { + // Eat two more tokens since D3D defines Taylor series constants that we won't need + // Only valid for pixel and vertex shader version earlier than 3_0 + // (http://msdn.microsoft.com/en-us/library/windows/hardware/ff569710(v=vs.85).aspx) + SkipTokens( 2 ); + } +} + + +void D3DToGL::Handle_LRP( uint32 nInstruction ) +{ + uint32 nDestToken = GetNextToken(); + CUtlString sDest = GetParameterString( nDestToken, DST_REGISTER, false, NULL ); + int nARLComp0 = ARL_DEST_NONE; + CUtlString sParam0 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp0 ); + int nARLComp1 = ARL_DEST_NONE; + CUtlString sParam1 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp1 ); + int nARLComp2 = ARL_DEST_NONE; + CUtlString sParam2 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp2 ); + + // This optionally inserts a move from our dummy address register to the .x component of the real one + InsertMoveFromAddressRegister( m_pBufALUCode, nARLComp0, nARLComp1, nARLComp2 ); + + sParam0 = FixGLSLSwizzle( sDest, sParam0 ); + sParam1 = FixGLSLSwizzle( sDest, sParam1 ); + sParam2 = FixGLSLSwizzle( sDest, sParam2 ); + + // dest = src0 * (src1 - src2) + src2; + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s * ( %s - %s ) + %s;\n", sDest.String(), sParam0.String(), sParam1.String(), sParam2.String(), sParam2.String() ); + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( sDest.String() ); + if ( nComponents == 0 ) + nComponents = 4; + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", sDest.String(), sDest.String(), g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + + +void D3DToGL::Handle_TEX( uint32 dwToken, bool bIsTexLDL ) +{ + char pDestReg[64], pSrc0Reg[64], pSrc1Reg[64]; + PrintParameterToString( GetNextToken(), DST_REGISTER, pDestReg, sizeof( pDestReg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc0Reg, sizeof( pSrc0Reg ), false, NULL ); + + DWORD dwSrc1Token = GetNextToken(); + PrintParameterToString( dwSrc1Token, SRC_REGISTER, pSrc1Reg, sizeof( pSrc1Reg ), false, NULL ); + + Assert( (dwSrc1Token & D3DSP_REGNUM_MASK) < ARRAYSIZE( m_dwSamplerTypes ) ); + uint32 nSamplerType = m_dwSamplerTypes[dwSrc1Token & D3DSP_REGNUM_MASK]; + if ( nSamplerType == SAMPLER_TYPE_2D ) + { + const bool bIsShadowSampler = ( ( 1 << ( (int) ( dwSrc1Token & D3DSP_REGNUM_MASK ) ) ) & m_nShadowDepthSamplerMask ) != 0; + + if ( bIsTexLDL ) + { + CUtlString sCoordVar = EnsureNumSwizzleComponents( pSrc0Reg, bIsShadowSampler ? 3 : 2 ); + + // Strip out the W component of the pSrc0Reg and pass that as the LOD to texture2DLod. + char szLOD[128], szExtra[8]; + GetParamNameWithoutSwizzle( pSrc0Reg, szLOD, sizeof( szLOD ) ); + V_snprintf( szExtra, sizeof( szExtra ), ".%c", GetSwizzleComponent( pSrc0Reg, 3 ) ); + V_strncat( szLOD, szExtra, sizeof( szLOD ) ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s( %s, %s, %s );\n", pDestReg, bIsShadowSampler ? "shadow2DLod" : "texture2DLod", pSrc1Reg, sCoordVar.String(), szLOD ); + } + else if ( bIsShadowSampler ) + { + // .z is meant to contain the object depth, while .xy contains the 2D tex coords + CUtlString sCoordVar3D = EnsureNumSwizzleComponents( pSrc0Reg, 3 ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = shadow2D( %s, %s );\n", pDestReg, pSrc1Reg, sCoordVar3D.String() ); + Assert( m_dwSamplerTypes[dwSrc1Token & D3DSP_REGNUM_MASK] == SAMPLER_TYPE_2D ); + } + else if( ( OpcodeSpecificData( dwToken ) << D3DSP_OPCODESPECIFICCONTROL_SHIFT ) == D3DSI_TEXLD_PROJECT ) + { + // This projective case is after the shadow case intentionally, due to the way that "projective" + // loads are overloaded in our D3D shaders for shadow lookups. + // + // We use the vec4 variant of texture2DProj() intentionally here, since it lines up well with Direct3D. + + CUtlString s4DProjCoords = EnsureNumSwizzleComponents( pSrc0Reg, 4 ); // Ensure vec4 variant + PrintToBufWithIndents( *m_pBufALUCode, "%s = texture2DProj( %s, %s );\n", pDestReg, pSrc1Reg, s4DProjCoords.String() ); + } + else + { + CUtlString sCoordVar = EnsureNumSwizzleComponents( pSrc0Reg, bIsShadowSampler ? 3 : 2 ); + PrintToBufWithIndents( *m_pBufALUCode, "%s = texture2D( %s, %s );\n", pDestReg, pSrc1Reg, sCoordVar.String() ); + } + } + else if ( nSamplerType == SAMPLER_TYPE_3D ) + { + if ( bIsTexLDL ) + { + TranslationError(); + } + + CUtlString sCoordVar = EnsureNumSwizzleComponents( pSrc0Reg, 3 ); + PrintToBufWithIndents( *m_pBufALUCode, "%s = texture3D( %s, %s );\n", pDestReg, pSrc1Reg, sCoordVar.String() ); + } + else if ( nSamplerType == SAMPLER_TYPE_CUBE ) + { + if ( bIsTexLDL ) + { + TranslationError(); + } + + CUtlString sCoordVar = EnsureNumSwizzleComponents( pSrc0Reg, 3 ); + PrintToBufWithIndents( *m_pBufALUCode, "%s = textureCube( %s, %s );\n", pDestReg, pSrc1Reg, sCoordVar.String() ); + } + else + { + Error( "TEX instruction: unsupported sampler type used" ); + } +} + +void D3DToGL::StrcatToHeaderCode( const char *pBuf ) +{ + strcat_s( (char*)m_pBufHeaderCode->Base(), m_pBufHeaderCode->Size(), pBuf ); +} + +void D3DToGL::StrcatToALUCode( const char *pBuf ) +{ + PrintIndentation( (char*)m_pBufALUCode->Base(), m_pBufALUCode->Size() ); + + strcat_s( (char*)m_pBufALUCode->Base(), m_pBufALUCode->Size(), pBuf ); +} + +void D3DToGL::StrcatToParamCode( const char *pBuf ) +{ + strcat_s( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size(), pBuf ); +} + +void D3DToGL::StrcatToAttribCode( const char *pBuf ) +{ + strcat_s( (char*)m_pBufAttribCode->Base(), m_pBufAttribCode->Size(), pBuf ); +} + +void D3DToGL::Handle_TexLDD( uint32 nInstruction ) +{ + TranslationError(); // Not supported yet, but can be if we need it. +} + + +void D3DToGL::Handle_TexCoord() +{ + TranslationError(); + + // If ps_1_4, this is texcrd + if ( (m_dwMajorVersion == 1) && (m_dwMinorVersion == 4) && (!m_bVertexShader) ) + { + StrcatToALUCode( "texcrd" ); + } + else // else it's texcoord + { + TranslationError(); + StrcatToALUCode( "texcoord" ); + } + + char buff[256]; + PrintParameterToString( GetNextToken(), DST_REGISTER, buff, sizeof( buff ), false, NULL ); + StrcatToALUCode( buff ); + + // If ps_1_4, texcrd also has a source parameter + if ((m_dwMajorVersion == 1) && (m_dwMinorVersion == 4) && (!m_bVertexShader)) + { + StrcatToALUCode( ", " ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, buff, sizeof( buff ), false, NULL ); + StrcatToALUCode( buff ); + } + + StrcatToALUCode( ";\n" ); +} + +void D3DToGL::Handle_BREAKC( uint32 dwToken ) +{ + uint nComparison = ( dwToken & D3DSHADER_COMPARISON_MASK ) >> D3DSHADER_COMPARISON_SHIFT; + + const char *pComparison = "?"; + switch ( nComparison ) + { + case D3DSPC_GT: pComparison = ">"; break; + case D3DSPC_EQ: pComparison = "=="; break; + case D3DSPC_GE: pComparison = ">="; break; + case D3DSPC_LT: pComparison = "<"; break; + case D3DSPC_NE: pComparison = "!="; break; + case D3DSPC_LE: pComparison = "<="; break; + default: + TranslationError(); + } + + char src0[256]; + uint32 src0Token = GetNextToken(); + PrintParameterToString( src0Token, SRC_REGISTER, src0, sizeof( src0 ), false, NULL ); + + char src1[256]; + uint32 src1Token = GetNextToken(); + PrintParameterToString( src1Token, SRC_REGISTER, src1, sizeof( src1 ), false, NULL ); + + PrintToBufWithIndents( *m_pBufALUCode, "if (%s %s %s) break;\n", src0, pComparison, src1 ); +} + +void D3DToGL::HandleBinaryOp_GLSL( uint32 nInstruction ) +{ + uint32 nDestToken = GetNextToken(); + CUtlString sParam1 = GetParameterString( nDestToken, DST_REGISTER, false, NULL ); + int nARLComp0 = ARL_DEST_NONE; + CUtlString sParam2 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp0 ); + int nARLComp1 = ARL_DEST_NONE; + CUtlString sParam3 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp1 ); + + // This optionally inserts a move from our dummy address register to the .x component of the real one + InsertMoveFromAddressRegister( m_pBufALUCode, nARLComp0, nARLComp1 ); + + // Since DP3 and DP4 have a scalar as the dest and vectors as the src, don't screw with the swizzle specifications. + if ( nInstruction == D3DSIO_DP3 ) + { + sParam2 = EnsureNumSwizzleComponents( sParam2, 3 ); + sParam3 = EnsureNumSwizzleComponents( sParam3, 3 ); + } + else if ( nInstruction == D3DSIO_DP4 ) + { + sParam2 = EnsureNumSwizzleComponents( sParam2, 4 ); + sParam3 = EnsureNumSwizzleComponents( sParam3, 4 ); + } + else if ( nInstruction == D3DSIO_DST ) + { + m_bUsesDSTInstruction = true; + sParam2 = EnsureNumSwizzleComponents( sParam2, 4 ); + sParam3 = EnsureNumSwizzleComponents( sParam3, 4 ); + } + else + { + sParam2 = FixGLSLSwizzle( sParam1, sParam2 ); + sParam3 = FixGLSLSwizzle( sParam1, sParam3 ); + } + + char buff[256]; + if ( nInstruction == D3DSIO_ADD || nInstruction == D3DSIO_SUB || nInstruction == D3DSIO_MUL ) + { + // These all look like x = y op z + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s %s %s;\n", sParam1.String(), sParam2.String(), GetGLSLOperatorString( nInstruction ), sParam3.String() ); + } + else + { + int nDestComponents = GetNumSwizzleComponents( sParam1.String() ); + int nSrcComponents = GetNumSwizzleComponents( sParam2.String() ); + + // All remaining instructions can use GLSL intrinsics like dot() and cross(). + bool bDoubleClose = OpenIntrinsic( nInstruction, buff, sizeof( buff ), nDestComponents, nSrcComponents ); + + if ( ( nSrcComponents == 1 ) && ( nInstruction == D3DSIO_SGE ) ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s%s >= %s );\n", sParam1.String(), buff, sParam2.String(), sParam3.String() ); + } + else if ( ( nSrcComponents == 1 ) && ( nInstruction == D3DSIO_SLT ) ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s%s < %s );\n", sParam1.String(), buff, sParam2.String(), sParam3.String() ); + } + else + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s%s, %s %s;\n", sParam1.String(), buff, sParam2.String(), sParam3.String(), bDoubleClose ? ") )" : ")" ); + } + } + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( sParam1.String() ); + if ( nComponents == 0 ) + nComponents = 4; + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", sParam1.String(), sParam1.String(), g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + +void D3DToGL::HandleBinaryOp_ASM( uint32 nInstruction ) +{ + CUtlString sParam1 = GetParameterString( GetNextToken(), DST_REGISTER, false, NULL ); + int nARLComp0 = ARL_DEST_NONE; + CUtlString sParam2 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp0 ); + int nARLComp1 = ARL_DEST_NONE; + CUtlString sParam3 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp1 ); + + // This optionally inserts a move from our dummy address register to the .x component of the real one + InsertMoveFromAddressRegister( m_pBufALUCode, nARLComp0, nARLComp1 ); + + char buff[256]; + PrintOpcode( nInstruction, buff, sizeof( buff ) ); + PrintToBufWithIndents( *m_pBufALUCode, "%s%s, %s, %s;\n", buff, sParam1.String(), sParam2.String(), sParam3.String() ); +} + +void D3DToGL::WriteGLSLCmp( const char *pDestReg, const char *pSrc0Reg, const char *pSrc1Reg, const char *pSrc2Reg ) +{ + int nWriteMaskEntries = GetNumWriteMaskEntries( pDestReg ); + for ( int i=0; i < nWriteMaskEntries; i++ ) + { + char params[4][256]; + WriteParamWithSingleMaskEntry( pDestReg, i, params[0], sizeof( params[0] ) ); + WriteParamWithSingleMaskEntry( pSrc0Reg, i, params[1], sizeof( params[1] ) ); + WriteParamWithSingleMaskEntry( pSrc1Reg, i, params[2], sizeof( params[2] ) ); + WriteParamWithSingleMaskEntry( pSrc2Reg, i, params[3], sizeof( params[3] ) ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = ( %s >= 0.0 ) ? %s : %s;\n", params[0], params[1], params[2], params[3] ); + } +} + +void D3DToGL::Handle_CMP() +{ + // In Direct3D, result = (src0 >= 0.0) ? src1 : src2 + // In OpenGL, result = (src0 < 0.0) ? src1 : src2 + // + // As a result, arguments are effectively in a different order than Direct3D! !#$&*!%#$& + char pDestReg[64], pSrc0Reg[64], pSrc1Reg[64], pSrc2Reg[64]; + uint32 nDestToken = GetNextToken(); + PrintParameterToString( nDestToken, DST_REGISTER, pDestReg, sizeof( pDestReg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc0Reg, sizeof( pSrc0Reg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc1Reg, sizeof( pSrc1Reg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc2Reg, sizeof( pSrc2Reg ), false, NULL ); + + // These are a tricky case.. we have to expand it out into multiple statements. + char szDestBase[256]; + GetParamNameWithoutSwizzle( pDestReg, szDestBase, sizeof( szDestBase ) ); + + V_strncpy( pSrc0Reg, FixGLSLSwizzle( pDestReg, pSrc0Reg ), sizeof( pSrc0Reg ) ); + V_strncpy( pSrc1Reg, FixGLSLSwizzle( pDestReg, pSrc1Reg ), sizeof( pSrc1Reg ) ); + V_strncpy( pSrc2Reg, FixGLSLSwizzle( pDestReg, pSrc2Reg ), sizeof( pSrc2Reg ) ); + + // This isn't reliable! + //if ( DoParamNamesMatch( pDestReg, pSrc0Reg ) && GetNumSwizzleComponents( pDestReg ) > 1 ) + if ( 1 ) + { + // So the dest register is the same as the comparand. We're in danger of screwing up our results. + // + // For example, this code: + // CMP r0.xy, r0.xx, r1, r2 + // would generate this: + // r0.x = (r0.x >= 0) ? r1.x : r2.x; + // r0.y = (r0.x >= 0) ? r1.x : r2.x; + // + // But the first lines changes r0.x and thus screws the atomicity of the CMP instruction for the second line. + // So we assign r0 to a temporary first and then write to the temporary. + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s;\n", g_pAtomicTempVarName, szDestBase ); + + char szTempVar[256]; + ReplaceParamName( pDestReg, g_pAtomicTempVarName, szTempVar, sizeof( szTempVar ) ); + WriteGLSLCmp( szTempVar, pSrc0Reg, pSrc1Reg, pSrc2Reg ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s;\n", szDestBase, g_pAtomicTempVarName ); + m_bUsedAtomicTempVar = true; + } + else + { + // Just write out the simple expanded version of the CMP. No need to use atomic_temp_var. + WriteGLSLCmp( pDestReg, pSrc0Reg, pSrc1Reg, pSrc2Reg ); + } + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( pDestReg ); + if ( nComponents == 0 ) + nComponents = 4; + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", pDestReg, pDestReg, g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + +void D3DToGL::Handle_NRM() +{ + char pDestReg[64]; + char pSrc0Reg[64]; + PrintParameterToString( GetNextToken(), DST_REGISTER, pDestReg, sizeof( pDestReg ), false, NULL ); + int nARLSrcComp = ARL_DEST_NONE; + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc0Reg, sizeof( pSrc0Reg ), false, &nARLSrcComp ); + + if ( nARLSrcComp != -1 ) + { + InsertMoveFromAddressRegister( m_pBufALUCode, nARLSrcComp, -1, -1 ); + } + + CUtlString sSrc = EnsureNumSwizzleComponents( pSrc0Reg, 3 ); + PrintToBufWithIndents( *m_pBufALUCode, "%s = normalize( %s );\n", pDestReg, sSrc.String() ); +} + +void D3DToGL::Handle_UnaryOp( uint32 nInstruction ) +{ + uint32 nDestToken = GetNextToken(); + CUtlString sParam1 = GetParameterString( nDestToken, DST_REGISTER, false, NULL ); + CUtlString sParam2 = GetParameterString( GetNextToken(), SRC_REGISTER, false, NULL ); + sParam2 = FixGLSLSwizzle( sParam1, sParam2 ); + + + if ( nInstruction == D3DSIO_MOV ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s;\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_RSQ ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = inversesqrt( %s );\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_RCP ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = 1.0 / %s;\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_EXP ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = exp2( %s );\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_FRC ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = fract( %s );\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_LOG ) // d3d 'log' is log base 2 + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = log2( %s );\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_ABS ) // rbarris did this one, Jason please check + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = abs( %s );\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_MOVA ) + { + m_bDeclareAddressReg = true; + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s;\n", sParam1.String(), sParam2.String() ); + + if ( !m_bGenerateBoneUniformBuffer ) + { + m_nHighestRegister = DXABSTRACT_VS_PARAM_SLOTS - 1; + } + } + else + { + Error( "Unsupported instruction" ); + } + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( sParam1.String() ); + if ( nComponents == 0 ) + { + nComponents = 4; + } + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", sParam1.String(), sParam1.String(), g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + +void D3DToGL::WriteGLSLSamplerDefinitions() +{ + int nSamplersWritten = 0; + for ( int i=0; i < ARRAYSIZE( m_dwSamplerTypes ); i++ ) + { + if ( m_dwSamplerTypes[i] == SAMPLER_TYPE_2D ) + { + if ( ( ( 1 << i ) & m_nShadowDepthSamplerMask ) != 0 ) + { + PrintToBuf( *m_pBufHeaderCode, "uniform sampler2DShadow sampler%d;\n", i ); + } + else + { + PrintToBuf( *m_pBufHeaderCode, "uniform sampler2D sampler%d;\n", i ); + } + ++nSamplersWritten; + } + else if ( m_dwSamplerTypes[i] == SAMPLER_TYPE_3D ) + { + PrintToBuf( *m_pBufHeaderCode, "uniform sampler3D sampler%d;\n", i ); + ++nSamplersWritten; + } + else if ( m_dwSamplerTypes[i] == SAMPLER_TYPE_CUBE ) + { + PrintToBuf( *m_pBufHeaderCode, "uniform samplerCube sampler%d;\n", i ); + ++nSamplersWritten; + } + else if ( m_dwSamplerTypes[i] != SAMPLER_TYPE_UNUSED ) + { + Error( "Unknown sampler type." ); + } + } + + if ( nSamplersWritten > 0 ) + PrintToBuf( *m_pBufHeaderCode, "\n\n" ); +} + +void D3DToGL::WriteGLSLOutputVariableAssignments() +{ + if ( m_bVertexShader ) + { + // Map output "oN" registers back to GLSL output variables. + if ( m_bAddHexCodeComments ) + { + PrintToBuf( *m_pBufAttribCode, "\n// Now we're storing the oN variables from the output dcl_ statements back into their GLSL equivalents.\n" ); + } + + for ( int i=0; i < ARRAYSIZE( m_DeclaredOutputs ); i++ ) + { + if ( m_DeclaredOutputs[i] == UNDECLARED_OUTPUT ) + continue; + + if ( ( m_dwTexCoordOutMask & ( 1 << i ) ) == 0 ) + continue; + + uint32 dwToken = m_DeclaredOutputs[i]; + + uint32 dwUsage = ( dwToken & D3DSP_DCL_USAGE_MASK ); + uint32 dwUsageIndex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + if ( ( dwUsage == D3DDECLUSAGE_FOG ) || ( dwUsage == D3DDECLUSAGE_PSIZE ) ) + { + TranslationError(); // Not supported yet, but can be if we need it. + } + + if ( dwUsage == D3DDECLUSAGE_COLOR ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = oTempT%d;\n", dwUsageIndex ? "gl_FrontSecondaryColor" : "gl_FrontColor", i ); + } + else if ( dwUsage == D3DDECLUSAGE_TEXCOORD ) + { + char buf[256]; + if ( m_nCentroidMask & ( 0x00000001 << dwUsageIndex ) ) + { + V_snprintf( buf, sizeof( buf ), "centroid varying vec4 oT%d;\n", dwUsageIndex ); // centroid varying + } + else + { + V_snprintf( buf, sizeof( buf ), "varying vec4 oT%d;\n", dwUsageIndex ); + } + StrcatToHeaderCode( buf ); + + PrintToBufWithIndents( *m_pBufALUCode, "oT%d = oTempT%d;\n", dwUsageIndex, i ); + } + } + } +} + +void D3DToGL::WriteGLSLInputVariableAssignments() +{ + if ( m_bVertexShader ) + return; + + for ( int i=0; i < ARRAYSIZE( m_DeclaredInputs ); i++ ) + { + if ( m_DeclaredInputs[i] == UNDECLARED_INPUT ) + continue; + + uint32 dwToken = m_DeclaredInputs[i]; + + uint32 dwUsage = ( dwToken & D3DSP_DCL_USAGE_MASK ); + uint32 dwUsageIndex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + if ( dwUsage == D3DDECLUSAGE_COLOR ) + { + PrintToBufWithIndents( *m_pBufAttribCode, "vec4 oTempT%d = %s;\n", i, dwUsageIndex ? "gl_SecondaryColor" : "gl_Color" ); + } + else if ( dwUsage == D3DDECLUSAGE_TEXCOORD ) + { + PrintToBufWithIndents( *m_pBufAttribCode, "vec4 oTempT%d = oT%d;\n", i, dwUsageIndex ); + } + } +} + +void D3DToGL::Handle_DeclarativeNonDclOp( uint32 nInstruction ) +{ + char buff[128]; + uint32 dwToken = GetNextToken(); + PrintParameterToString( dwToken, DST_REGISTER, buff, sizeof( buff ), false, NULL ); + + if ( nInstruction == D3DSIO_TEXKILL ) + { + // TEXKILL is supposed to discard the pixel if any of the src register's X, Y, or Z components are less than zero. + // We have to translate it to something like: + // if ( r0.x < 0.0 || r0.y < 0.0 ) + // discard; + char c[3]; + c[0] = GetSwizzleComponent( buff, 0 ); + c[1] = GetSwizzleComponent( buff, 1 ); + c[2] = GetSwizzleComponent( buff, 2 ); + + // Get the unique components. + char cUnique[3]; + cUnique[0] = c[0]; + + int nUnique = 1; + if ( c[1] != c[0] ) + cUnique[nUnique++] = c[1]; + + if ( c[2] != c[1] && c[2] != c[0] ) + cUnique[nUnique++] = c[2]; + + // Get the src register base name. + char szBase[256]; + GetParamNameWithoutSwizzle( buff, szBase, sizeof( szBase ) ); + + PrintToBufWithIndents( *m_pBufALUCode, "if ( %s.%c < 0.0 ", szBase, cUnique[0] ); + for ( int i=1; i < nUnique; i++ ) + { + PrintToBuf( *m_pBufALUCode, "|| %s.%c < 0.0 ", szBase, cUnique[i] ); + } + PrintToBuf( *m_pBufALUCode, ")\n{\n\tdiscard;\n}\n" ); + } + else + { + char szOpcode[128]; + PrintOpcode( nInstruction, szOpcode, sizeof( szOpcode ) ); + StrcatToALUCode( szOpcode ); + + StrcatToALUCode( buff ); + StrcatToALUCode( ";\n" ); + } +} + + +void D3DToGL::NoteTangentInputUsed() +{ + if ( !m_bTangentInputUsed ) + { + m_bTangentInputUsed = true; +// PrintToBuf( *m_pBufParamCode, "attribute vec4 %s;\n", g_pTangentAttributeName ); + } +} + + +// These are the only ARL instructions that should appear in the instruction stream +void D3DToGL::InsertMoveInstruction( CUtlBuffer *pCode, int nARLComponent ) +{ + PrintIndentation( ( char * )pCode->Base(), pCode->Size() ); + + switch ( nARLComponent ) + { + case ARL_DEST_X: + strcat_s( ( char * )pCode->Base(), pCode->Size(), "a0 = int( va_r.x );\n" ); + break; + case ARL_DEST_Y: + strcat_s( ( char * )pCode->Base(), pCode->Size(), "a0 = int( va_r.y );\n" ); + break; + case ARL_DEST_Z: + strcat_s( ( char * )pCode->Base(), pCode->Size(), "a0 = int( va_r.z );\n" ); + break; + case ARL_DEST_W: + strcat_s( ( char * )pCode->Base(), pCode->Size(), "a0 = int( va_r.w );\n" ); + break; + } +} + +// This optionally inserts a move from our dummy address register to the .x component of the real one +void D3DToGL::InsertMoveFromAddressRegister( CUtlBuffer *pCode, int nARLComp0, int nARLComp1, int nARLComp2 /* = ARL_DEST_NONE */ ) +{ + // We no longer need to do this in GLSL - we put the cast to int from the dummy address register va_r.x, va_r.y, etc. directly into the instruction + return; +} + + +//------------------------------------------------------------------------------ +// TranslateShader() +// +// This is the main function that the outside world sees. A pointer to the +// uint32 stream returned from the D3DX compile routine is parsed and used +// to write human-readable asm code into the character array pointed to by +// pDisassembledCode. An error code is returned. +//------------------------------------------------------------------------------ + + +int D3DToGL::TranslateShader( uint32* code, CUtlBuffer *pBufDisassembledCode, bool *bVertexShader, uint32 options, int32 nShadowDepthSamplerMask, uint32 nCentroidMask, char *debugLabel ) +{ + CUtlString sLine, sParamName; + uint32 i, dwToken, nInstruction, nNumTokensToSkip; + char buff[256]; + + // obey options + m_bUseEnvParams = (options & D3DToGL_OptionUseEnvParams) != 0; + m_bDoFixupZ = (options & D3DToGL_OptionDoFixupZ) != 0; + m_bDoFixupY = (options & D3DToGL_OptionDoFixupY) != 0; + m_bDoUserClipPlanes = (options & D3DToGL_OptionDoUserClipPlanes) != 0; + + m_bAddHexCodeComments = (options & D3DToGL_AddHexComments) != 0; + m_bPutHexCodesAfterLines = (options & D3DToGL_PutHexCommentsAfterLines) != 0; + m_bGeneratingDebugText = (options & D3DToGL_GeneratingDebugText) != 0; + m_bGenerateSRGBWriteSuffix = (options & D3DToGL_OptionSRGBWriteSuffix) != 0; + + m_NumIndentTabs = 1; // start code indented one tab + m_nLoopDepth = 0; + + // debugging + m_bSpew = (options & D3DToGL_OptionSpew) != 0; + + // These are not accessed below in a way that will cause them to glow, so + // we could overflow these and/or the buffer pointed to by pDisassembledCode + m_pBufAttribCode = new CUtlBuffer( 100, 10000, CUtlBuffer::TEXT_BUFFER ); + m_pBufParamCode = new CUtlBuffer( 100, 10000, CUtlBuffer::TEXT_BUFFER ); + m_pBufALUCode = new CUtlBuffer( 100, 60000, CUtlBuffer::TEXT_BUFFER ); + + // Pointers to text buffers for assembling sections of the program + m_pBufHeaderCode = pBufDisassembledCode; + char *pAttribMapStart = NULL; + ((char*)m_pBufHeaderCode->Base())[0] = 0; + ((char*)m_pBufAttribCode->Base())[0] = 0; + ((char*)m_pBufParamCode->Base())[0] = 0; + ((char*)m_pBufALUCode->Base())[0] = 0; + + + for ( i=0; iBase(), m_pBufHeaderCode->Size(), "#version %s\n%s", glslVersionText, glslExtText ); + m_bVertexShader = false; + } + else // vertex shader + { + m_bGenerateSRGBWriteSuffix = false; + + V_snprintf( (char *)m_pBufHeaderCode->Base(), m_pBufHeaderCode->Size(), "#version %s\n%s//ATTRIBMAP-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx\n", glslVersionText, glslExtText ); + + // find that first '-xx' which is where the attrib map will be written later. + pAttribMapStart = strstr( (char *)m_pBufHeaderCode->Base(), "-xx" ) + 1; + + m_bVertexShader = true; + } + + *bVertexShader = m_bVertexShader; + + m_bGenerateBoneUniformBuffer = m_bVertexShader && ((options & D3DToGL_OptionGenerateBoneUniformBuffer) != 0); + + if ( m_bAddHexCodeComments ) + { + RecordInputAndOutputPositions(); + } + + if ( m_bSpew ) + { + printf("\n************* translating shader " ); + } + + int opcounter = 0; + + // Loop until we hit the end dwToken...note that D3DPS_END() == D3DVS_END() so this works for either + while ( dwToken != D3DPS_END() ) + { + if ( m_bAddHexCodeComments ) + { + AddTokenHexCode(); + RecordInputAndOutputPositions(); + } + +#ifdef POSIX + int tokenIndex = m_pdwNextToken - code; +#endif + int aluCodeLength0 = V_strlen( (char *) m_pBufALUCode->Base() ); + + dwToken = GetNextToken(); // Get next dwToken in the stream + nInstruction = Opcode( dwToken ); // Mask out the instruction opcode + + if ( m_bSpew ) + { +#ifdef POSIX + printf("\n** token# %04x inst# %04d opcode %s (%08x)", tokenIndex, opcounter, GLMDecode(eD3D_SIO, nInstruction), dwToken ); +#endif + opcounter++; + } + + switch ( nInstruction ) + { + // -- No arguments at all ----------------------------------------------- + case D3DSIO_NOP: + // D3D compiler outputs NOPs when shader debugging/optimizations are disabled. + break; + + case D3DSIO_PHASE: + case D3DSIO_RET: + case D3DSIO_ENDLOOP: + case D3DSIO_BREAK: + TranslationError(); + PrintOpcode( nInstruction, buff, sizeof( buff ) ); + StrcatToALUCode( buff ); + StrcatToALUCode( ";\n" ); + break; + + // -- "Declarative" non dcl ops ---------------------------------------- + case D3DSIO_TEXDEPTH: + case D3DSIO_TEXKILL: + Handle_DeclarativeNonDclOp( nInstruction ); + break; + + // -- Unary ops ------------------------------------------------- + case D3DSIO_BEM: + case D3DSIO_TEXBEM: + case D3DSIO_TEXBEML: + case D3DSIO_TEXDP3: + case D3DSIO_TEXDP3TEX: + case D3DSIO_TEXM3x2DEPTH: + case D3DSIO_TEXM3x2TEX: + case D3DSIO_TEXM3x3: + case D3DSIO_TEXM3x3PAD: + case D3DSIO_TEXM3x3TEX: + case D3DSIO_TEXM3x3VSPEC: + case D3DSIO_TEXREG2AR: + case D3DSIO_TEXREG2GB: + case D3DSIO_TEXREG2RGB: + case D3DSIO_LABEL: + case D3DSIO_CALL: + case D3DSIO_LOOP: + case D3DSIO_BREAKP: + case D3DSIO_DSX: + case D3DSIO_DSY: + TranslationError(); + break; + + case D3DSIO_IFC: + { + static const char *s_szCompareStrings[ 7 ] = + { + "__INVALID__", + ">", + "==", + ">=", + "<", + "!=", + "<=" + }; + + // Compare mode is encoded in instruction token + uint32 dwCompareMode = OpcodeSpecificData( dwToken ); + + Assert( ( dwCompareMode >= 1 ) && ( dwCompareMode <= 6 ) ); + + // Get left side of compare + dwToken = GetNextToken(); + char szLeftSide[32]; + PrintParameterToString( dwToken, SRC_REGISTER, szLeftSide, sizeof( szLeftSide ), false, NULL ); + + // Get right side of compare + dwToken = GetNextToken(); + char szRightSide[32]; + PrintParameterToString( dwToken, SRC_REGISTER, szRightSide, sizeof( szRightSide ), false, NULL ); + + PrintToBufWithIndents( *m_pBufALUCode, "if ( %s %s %s )\n", szLeftSide, s_szCompareStrings[dwCompareMode], szRightSide ); + StrcatToALUCode( "{\n" ); + m_NumIndentTabs++; + + break; + } + case D3DSIO_IF: + dwToken = GetNextToken(); + PrintParameterToString( dwToken, SRC_REGISTER, buff, sizeof( buff ), false, NULL ); + + PrintToBufWithIndents( *m_pBufALUCode, "if ( %s )\n", buff ); + StrcatToALUCode( "{\n" ); + m_NumIndentTabs++; + + break; + + case D3DSIO_ELSE: + m_NumIndentTabs--; + StrcatToALUCode( "}\n" ); + StrcatToALUCode( "else\n" ); + StrcatToALUCode( "{\n" ); + m_NumIndentTabs++; + + break; + + case D3DSIO_ENDIF: + m_NumIndentTabs--; + StrcatToALUCode( "}\n" ); + + break; + + case D3DSIO_REP: + dwToken = GetNextToken(); + PrintParameterToString( dwToken, SRC_REGISTER, buff, sizeof( buff ), false, NULL ); + + // In practice, this is the only form of for loop that will appear in DX asm + PrintToBufWithIndents( *m_pBufALUCode, "for( int i=0; i < %s; i++ )\n", buff ); + StrcatToALUCode( "{\n" ); + + m_nLoopDepth++; + + // For now, we don't deal with loop nesting + // Easy enough to fix later with an array of loop names i, j, k etc + Assert( m_nLoopDepth <= 1 ); + + m_NumIndentTabs++; + + break; + + case D3DSIO_ENDREP: + m_nLoopDepth--; + m_NumIndentTabs--; + StrcatToALUCode( "}\n" ); + + break; + + case D3DSIO_NRM: + Handle_NRM(); + break; + + case D3DSIO_MOVA: + + Handle_UnaryOp( nInstruction ); + + break; + + // Unary operations + case D3DSIO_MOV: + case D3DSIO_RCP: + case D3DSIO_RSQ: + case D3DSIO_EXP: + case D3DSIO_EXPP: + case D3DSIO_LOG: + case D3DSIO_LOGP: + case D3DSIO_FRC: + case D3DSIO_LIT: + case D3DSIO_ABS: + Handle_UnaryOp( nInstruction ); + break; + + // -- Binary ops ------------------------------------------------- + case D3DSIO_TEXM3x3SPEC: + case D3DSIO_M4x4: + case D3DSIO_M4x3: + case D3DSIO_M3x4: + case D3DSIO_M3x3: + case D3DSIO_M3x2: + case D3DSIO_CALLNZ: + case D3DSIO_SETP: + TranslationError(); + break; + + case D3DSIO_BREAKC: + Handle_BREAKC( dwToken ); + break; + + // Binary Operations + case D3DSIO_ADD: + case D3DSIO_SUB: + case D3DSIO_MUL: + case D3DSIO_DP3: + case D3DSIO_DP4: + case D3DSIO_MIN: + case D3DSIO_MAX: + case D3DSIO_DST: + case D3DSIO_SLT: + case D3DSIO_SGE: + case D3DSIO_CRS: + case D3DSIO_POW: + HandleBinaryOp_GLSL( nInstruction ); + + break; + + // -- Ternary ops ------------------------------------------------- + case D3DSIO_DP2ADD: + Handle_DP2ADD(); + break; + case D3DSIO_LRP: + Handle_LRP( nInstruction ); + break; + case D3DSIO_SGN: + Assert( m_bVertexShader ); + TranslationError(); // TODO emulate with SLT etc + break; + case D3DSIO_CND: + TranslationError(); + break; + case D3DSIO_CMP: + Handle_CMP(); + break; + case D3DSIO_SINCOS: + Handle_SINCOS(); + break; + case D3DSIO_MAD: + Handle_MAD( nInstruction ); + break; + + // -- Quaternary op ------------------------------------------------ + case D3DSIO_TEXLDD: + Handle_TexLDD( nInstruction ); + break; + + // -- Special cases: texcoord vs texcrd and tex vs texld ----------- + case D3DSIO_TEXCOORD: + Handle_TexCoord(); + break; + + case D3DSIO_TEX: + Handle_TEX( dwToken, false ); + break; + + case D3DSIO_TEXLDL: + Handle_TEX( nInstruction, true ); + break; + + case D3DSIO_DCL: + Handle_DCL(); + break; + + case D3DSIO_DEFB: + case D3DSIO_DEFI: + Handle_DEFIB( nInstruction ); + break; + + case D3DSIO_DEF: + Handle_DEF(); + break; + + case D3DSIO_COMMENT: + // Using OpcodeSpecificData() can fail here since the comments can be longer than 0xff dwords + nNumTokensToSkip = ( dwToken & 0x0fff0000 ) >> 16; + SkipTokens( nNumTokensToSkip ); + break; + + case D3DSIO_END: + break; + } + + if ( m_bSpew ) + { + int aluCodeLength1 = V_strlen( (char *) m_pBufALUCode->Base() ); + if ( aluCodeLength1 != aluCodeLength0 ) + { + // code was emitted + printf( "\n > %s", ((char *)m_pBufALUCode->Base()) + aluCodeLength0 ); + + aluCodeLength0 = aluCodeLength1; + } + } + } + + // Note that this constant packing expects .wzyx swizzles in case we ever use the SINCOS code in a ps_2_x shader + // + // The Microsoft documentation on this is all kinds of broken and, strangely, these numbers don't even + // match the D3DSINCOSCONST1 and D3DSINCOSCONST2 constants used by the D3D assembly sincos instruction... + if ( m_bNeedsSinCosDeclarations ) + { + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + StrcatToParamCode( "vec4 scA = vec4( -1.55009923e-6, -2.17013894e-5, 0.00260416674, 0.00026041668 );\n" ); + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + StrcatToParamCode( "vec4 scB = vec4( -0.020833334, -0.125, 1.0, 0.5 );\n" ); + } + + // Stick in the sampler mask in hex + PrintToBuf( *m_pBufHeaderCode, "%sSAMPLERMASK-%x\n", "//", m_dwSamplerUsageMask ); + + uint nSamplerTypes = 0; + for ( int i = 0; i < 16; i++ ) + { + Assert( m_dwSamplerTypes[i] < 4); + nSamplerTypes |= ( m_dwSamplerTypes[i] << ( i * 2 ) ); + } + + PrintToBuf( *m_pBufHeaderCode, "%sSAMPLERTYPES-%x\n", "//", nSamplerTypes ); + + // fragData outputs referenced + uint nFragDataMask = 0; + for ( int i = 0; i < 4; i++ ) + { + nFragDataMask |= m_bOutputColorRegister[ i ] ? ( 1 << i ) : 0; + } + + PrintToBuf( *m_pBufHeaderCode, "%sFRAGDATAMASK-%x\n", "//", nFragDataMask ); + + // Uniforms + + PrintToBuf( *m_pBufHeaderCode, "//HIGHWATER-%d\n", m_nHighestRegister + 1 ); + if ( ( m_bVertexShader ) && ( m_bGenerateBoneUniformBuffer ) ) + { + PrintToBuf( *m_pBufHeaderCode, "//HIGHWATERBONE-%i\n", m_nHighestBoneRegister + 1 ); + } + + PrintToBuf( *m_pBufHeaderCode, "\nuniform vec4 %s[%d];\n", m_bVertexShader ? "vc" : "pc", m_nHighestRegister + 1 ); + + if ( ( m_nHighestBoneRegister >= 0 ) && ( m_bVertexShader ) && ( m_bGenerateBoneUniformBuffer ) ) + { + PrintToBuf( *m_pBufHeaderCode, "\nuniform vec4 %s[%d];\n", "vcbones", m_nHighestBoneRegister + 1 ); + } + + if ( m_bVertexShader ) + { + PrintToBuf( *m_pBufHeaderCode, "\nuniform vec4 vcscreen;\n" ); + } + + for( int i=0; i<32; i++ ) + { + if ( ( m_dwConstIntUsageMask & ( 0x00000001 << i ) ) && + ( !( m_dwDefConstIntUsageMask & ( 0x00000001 << i ) ) ) + ) + { + PrintToBuf( *m_pBufHeaderCode, "uniform int i%d ;\n", i ); + } + } + + for( int i=0; i<32; i++ ) + { + if ( m_dwDefConstIntUsageMask & ( 0x00000001 << i ) ) + { + PrintToBuf( *m_pBufHeaderCode, "const int i%d = %i;\n", i, m_dwDefConstIntIterCount[i] ); + } + } + + for( int i=0; i<32; i++ ) + { + if ( m_dwConstBoolUsageMask & ( 0x00000001 << i ) ) + { + PrintToBuf( *m_pBufHeaderCode, m_bVertexShader ? "uniform bool b%d;\n" : "uniform bool fb%d;\n", i ); + } + } + + // Control bit for sRGB Write suffix + if ( m_bGenerateSRGBWriteSuffix ) + { + // R500 Hookup + // Set this guy to 1 when the sRGBWrite state is true, otherwise 0 + StrcatToHeaderCode( "uniform float flSRGBWrite;\n" ); + } + + PrintToBuf( *m_pBufHeaderCode, "\n" ); + + // Write samplers + WriteGLSLSamplerDefinitions(); + + if ( m_bUsesDSTInstruction ) + { + PrintToBuf( *m_pBufHeaderCode, "vec4 dst(vec4 src0,vec4 src1) { return vec4(1.0f,src0.y*src1.y,src0.z,src1.w); }\n" ); + } + + if ( m_bDeclareAddressReg ) + { + if ( !m_bGenerateBoneUniformBuffer ) + { + m_nHighestRegister = DXABSTRACT_VS_PARAM_SLOTS - 1; + } + + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + StrcatToParamCode( "vec4 va_r;\n" ); + } + + char *pTempVarStr = "TEMP"; + pTempVarStr = "vec4"; + + // Declare temps in Param code buffer + for( int i=0; i<32; i++ ) + { + if ( m_dwTempUsageMask & ( 0x00000001 << i ) ) + { + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + PrintToBuf( *m_pBufParamCode, "%s r%d;\n", pTempVarStr, i ); + } + } + + if ( m_bVertexShader && (m_bDoUserClipPlanes || m_bDoFixupZ || m_bDoFixupY ) ) + { + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + StrcatToParamCode( "vec4 vTempPos;\n" ); + } + + if ( ( m_bVertexShader ) && ( m_dwMajorVersion == 3 ) ) + { + for ( int i = 0; i < 32; i++ ) + { + if ( m_dwTexCoordOutMask & ( 1 << i ) ) + { + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + + char buf[256]; + V_snprintf( buf, sizeof( buf ), "vec4 oTempT%i = vec4( 0, 0, 0, 0 );\n", i ); + StrcatToParamCode( buf ); + } + } + } + + if ( m_bNeedsSinCosDeclarations ) + { + StrcatToParamCode( "vec3 vSinCosTmp;\n" ); // declare temp used by GLSL sin and cos intrinsics + } + + // Optional temps needed to emulate d2add instruction in DX pixel shaders + if ( m_bNeedsD2AddTemp ) + { + PrintToBuf( *m_pBufParamCode, "%s DP2A0;\n%s DP2A1;\n", pTempVarStr, pTempVarStr ); + } + + // Optional temp needed to emulate lerp instruction in DX vertex shaders + if ( m_bNeedsLerpTemp ) + { + PrintToBuf( *m_pBufParamCode, "%s LRP_TEMP;\n", pTempVarStr ); + } + + // Optional temp needed to emulate NRM instruction in DX shaders + if ( m_bNeedsNRMTemp ) + { + PrintToBuf( *m_pBufParamCode, "%s NRM_TEMP;\n", pTempVarStr ); + } + + if ( m_bDeclareVSOPos && m_bVertexShader ) + { + if ( m_bDoUserClipPlanes ) + { + StrcatToALUCode( "gl_ClipVertex = vTempPos;\n" ); // if user clip is enabled, jam clip space position into gl_ClipVertex + } + + if ( m_bDoFixupZ || m_bDoFixupY ) + { + // TODO: insert clip distance computation something like this: + // + // StrcatToALUCode( "DP4 oCLP[0].x, oPos, vc[215]; \n" ); + // + + if ( m_bDoFixupZ ) + { + StrcatToALUCode( "vTempPos.z = vTempPos.z * vc[0].z - vTempPos.w; // z' = (2*z)-w\n" ); + } + + if ( m_bDoFixupY ) + { + // append instructions to flip Y over + // new Y = -(old Y) + StrcatToALUCode( "vTempPos.y = -vTempPos.y; // y' = -y \n" ); + } + + // Apply half pixel offset (0.5f pixel offset D3D) to output vertices to account for the pixel center difference between D3D9 and OpenGL. + // This is the actual work in the shader. This works out to be 0.5 pixels wide because clip space is 2 units wide (-1, 1). + StrcatToALUCode( "vTempPos.xy += vcscreen.xy * vTempPos.w;\n" ); + + StrcatToALUCode( "gl_Position = vTempPos;\n" ); + } + else + { + StrcatToParamCode( "OUTPUT oPos = result.position;\n" ); + + // TODO: insert clip distance computation something like this: + // + // StrcatToALUCode( "DP4 oCLP[0].x, oPos, c[215]; \n" ); + // + } + } + + if ( m_bVertexShader ) + { + if ( m_dwMajorVersion == 3 ) + { + WriteGLSLOutputVariableAssignments(); + } + else + { + for ( int i=0; i<32; i++ ) + { + char outTexCoordBuff[64]; + + // Don't declare a varying for the output that is mapped to the position output + if ( i != m_nVSPositionOutput ) + { + if ( m_dwTexCoordOutMask & ( 0x00000001 << i ) ) + { + if ( m_nCentroidMask & ( 0x00000001 << i ) ) + { + V_snprintf( outTexCoordBuff, sizeof( outTexCoordBuff ), "centroid varying vec4 oT%d;\n", i ); // centroid varying + StrcatToHeaderCode( outTexCoordBuff ); + } + else + { + V_snprintf( outTexCoordBuff, sizeof( outTexCoordBuff ), "varying vec4 oT%d;\n", i ); + StrcatToHeaderCode( outTexCoordBuff ); + } + } + } + } + } + } + else + { + if ( m_dwMajorVersion == 3 ) + { + WriteGLSLInputVariableAssignments(); + } + } + + // do some annotation at the end of the attrib block + { + char temp[1000]; + + if ( m_bVertexShader ) + { + // write attrib map into the text starting at pAttribMapStart - two hex digits per attrib + for( int i=0; i<16; i++ ) + { + if ( m_dwAttribMap[i] != 0xFFFFFFFF ) + { + V_snprintf( temp, sizeof(temp), "%02X", m_dwAttribMap[i] ); + memcpy( pAttribMapStart + (i*3), temp, 2 ); + } + } + } + + PrintIndentation( (char*)m_pBufAttribCode->Base(), m_pBufAttribCode->Size() ); + + // This used to write out a translation counter into the shader as a comment. However, the order that shaders get in here + // is non-deterministic between runs, and the change in this comment would cause shaders to appear different to the GL disk cache, + // significantly increasing app load time. + // Other code looks for trans#%d, so we can't just remove it. Instead, output it as 0. + V_snprintf( temp, sizeof(temp), "%s trans#%d label:%s\n", "//", 0, debugLabel ? debugLabel : "none" ); + StrcatToAttribCode( temp ); + } + + // If we actually sample from a shadow depth sampler, we need to declare the shadow option at the top + if ( m_bDeclareShadowOption ) + { + StrcatToHeaderCode( "OPTION ARB_fragment_program_shadow;\n" ); + } + + StrcatToHeaderCode( "\nvoid main()\n{\n" ); + if ( m_bUsedAtomicTempVar ) + { + PrintToBufWithIndents( *m_pBufHeaderCode, "vec4 %s;\n\n", g_pAtomicTempVarName ); + } + + // sRGB Write suffix + if ( m_bGenerateSRGBWriteSuffix ) + { + StrcatToALUCode( "vec3 sRGBFragData;\n" ); + StrcatToALUCode( "sRGBFragData.xyz = log( gl_FragData[0].xyz );\n" ); + StrcatToALUCode( "sRGBFragData.xyz = sRGBFragData.xyz * vec3( 0.454545f, 0.454545f, 0.454545f );\n" ); + StrcatToALUCode( "sRGBFragData.xyz = exp( sRGBFragData.xyz );\n" ); + StrcatToALUCode( "gl_FragData[0].xyz = mix( gl_FragData[0].xyz, sRGBFragData, flSRGBWrite );\n" ); + } + + strcat_s( (char*)m_pBufALUCode->Base(), m_pBufALUCode->Size(), "}\n" ); + + // Put all of the strings together for final program ( pHeaderCode + pAttribCode + pParamCode + pALUCode ) + StrcatToHeaderCode( (char*)m_pBufAttribCode->Base() ); + StrcatToHeaderCode( (char*)m_pBufParamCode->Base() ); + StrcatToHeaderCode( (char*)m_pBufALUCode->Base() ); + + // Cleanup - don't touch m_pBufHeaderCode, as it is managed by the caller + delete m_pBufAttribCode; + delete m_pBufParamCode; + delete m_pBufALUCode; + m_pBufAttribCode = m_pBufParamCode = m_pBufALUCode = NULL; + + if ( m_bSpew ) + { + printf("\n************* translation complete\n\n " ); + } + + return DISASM_OK; +} diff --git a/togles/linuxwin/dx9asmtogl2.h b/togles/linuxwin/dx9asmtogl2.h new file mode 100644 index 00000000..2ab7e098 --- /dev/null +++ b/togles/linuxwin/dx9asmtogl2.h @@ -0,0 +1,261 @@ +//========= 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. +//------------------------------------------------------------------------------ +// DX9AsmToGL2.h +//------------------------------------------------------------------------------ + +#ifndef DX9_ASM_TO_GL_2_H +#define DX9_ASM_TO_GL_2_H +#include "tier1/utlstring.h" + +#define DISASM_OK 0 +#define DISASM_ERROR 1 + +#define MAX_SHADER_CONSTANTS 512 + +#define MAX_DECLARED_OUTPUTS 32 +#define MAX_DECLARED_INPUTS 32 + +#define HEXCODE_HEADER "// Hex: " + +// Option bits +#define D3DToGL_OptionUseEnvParams 0x0001 +#define D3DToGL_OptionDoFixupZ 0x0002 // Add instructions to put Z in the right interval for GL +#define D3DToGL_OptionDoFixupY 0x0004 // Add instructions to flip the Y over for GL +#define D3DToGL_OptionDoUserClipPlanes 0x0008 // ARB mode: Include OPTION vertex_program_2 and append DP4's to write into oCLP[0] and oCLP[1] + // GLSL mode: generate code to write gl_ClipVertex + +#define D3DToGL_AddHexComments 0x0020 // Include hex comments in the code for debugging +#define D3DToGL_PutHexCommentsAfterLines 0x0040 // If D3DToGL_AddHexComments is set, this puts the codes to the right, rather than on separate lines +#define D3DToGL_GeneratingDebugText 0x0080 // This tells it that we're just getting info for debugging so go easy on asserts and errors +#define D3DToGL_OptionSRGBWriteSuffix 0x0400 // Tack sRGB conversion suffix on to pixel shaders +#define D3DToGL_OptionGenerateBoneUniformBuffer 0x0800 // if enabled, the vertex shader "bone" registers (all regs DXABSTRACT_VS_FIRST_BONE_SLOT and higher) will be separated out into another uniform buffer (vcbone) +#define D3DToGL_OptionUseBindlessTexturing 0x1000 +#define D3DToGL_OptionSpew 0x80000000 + +// Code for which component of the "dummy" address register is needed by an instruction +#define ARL_DEST_NONE -1 +#define ARL_DEST_X 0 +#define ARL_DEST_Y 1 +#define ARL_DEST_Z 2 +#define ARL_DEST_W 3 + +class D3DToGL +{ +private: + // Pointers for dwToken stream management + uint32* m_pdwBaseToken; + uint32* m_pdwNextToken; + + // Vertex shader or pixel shader, and version (necessary because some opcodes alias) + bool m_bVertexShader; + uint32 m_dwMinorVersion; + uint32 m_dwMajorVersion; + + // Option flags + bool m_bUseEnvParams; // set D3DToGL_OptionUseEnvParams in 'options' to use + bool m_bDoFixupZ; // set D3DToGL_OptionDoFixupZ + bool m_bDoFixupY; // set D3DToGL_OptionDoFixupZ + bool m_bDoUserClipPlanes; // set D3DToGL_OptionDoUserClipPlanes + bool m_bSpew; // set D3DToGL_OptionSpew + bool m_bGenerateSRGBWriteSuffix; // set D3DToGL_OptionSRGBWriteSuffix + bool m_bGenerateBoneUniformBuffer; + bool m_bUseBindlessTexturing; + + // Counter for dealing with nested loops + int m_nLoopDepth; + + // Add "// Hex: 0xFFEEF00"-type statements after each instruction is parsed. + bool m_bAddHexCodeComments; // set D3DToGL_AddHexComments + + // Only applicable if m_bAddHexCodeComments is true. + // If this is true, then it puts the hex code comments to the right of the instructions in a comment + // rather than preceding the instructions. + // Defaults to FALSE. + bool m_bPutHexCodesAfterLines; // set D3DToGL_PutHexCommentsAtEnd + + // This tells it that we're just getting info for debugging so go easy on asserts and errors. + // Defaults to FALSE. + bool m_bGeneratingDebugText; + + // Various scratch temps needed to handle mis-matches in instruction sets between D3D and OpenGL + bool m_bNeedsD2AddTemp; + bool m_bNeedsNRMTemp; + bool m_bDeclareAddressReg; + bool m_bNeedsLerpTemp; + bool m_bNeedsSinCosDeclarations; + + // Keep track of which vs outputs are used so we can declare them + bool m_bDeclareVSOPos; + bool m_bDeclareVSOFog; + uint32 m_dwTexCoordOutMask; + + int32 m_nVSPositionOutput; + + // Mask of varyings which need centroid decoration + uint32 m_nCentroidMask; + + // Keep track of which temps are used so they can be declared + uint32 m_dwTempUsageMask; + uint32 m_dwTempBoolUsageMask; + bool m_bOutputColorRegister[4]; + bool m_bOutputDepthRegister; + + // Declaration of integer and bool constants + uint32 m_dwConstIntUsageMask; + uint32 m_dwConstBoolUsageMask; + + uint32 m_dwDefConstIntUsageMask; + uint32 m_dwDefConstIntIterCount[32]; + + // Did we use atomic_temp_var? + bool m_bUsedAtomicTempVar; + + // Track constants so we know how to declare them + bool m_bConstantRegisterDefined[MAX_SHADER_CONSTANTS]; + + // Track sampler types when declared so we can properly decorate TEX instructions + uint32 m_dwSamplerTypes[32]; + + // Track sampler usage + uint32 m_dwSamplerUsageMask; + + // Track shadow sampler usage + int m_nShadowDepthSamplerMask; + bool m_bDeclareShadowOption; + + // Track attribute references + // init to 0xFFFFFFFF (unhit) + // index by (dwRegToken & D3DSP_REGNUM_MASK) in VS DCL insns + // fill with (usage<<4) | (usage index). + uint32 m_dwAttribMap[16]; + + // Register high water mark + uint32 m_nHighestRegister; + int32 m_nHighestBoneRegister; + + // GLSL does indentation for readability + int m_NumIndentTabs; + + // Output buffers. + CUtlBuffer *m_pBufHeaderCode; + CUtlBuffer *m_pBufAttribCode; + CUtlBuffer *m_pBufParamCode; + CUtlBuffer *m_pBufALUCode; + + char *m_pFinalAssignmentsCode; + int m_nFinalAssignmentsBufSize; + + // Recorded positions for debugging. + uint32* m_pRecordedInputTokenStart; + int m_nRecordedParamCodeStrlen; + int m_nRecordedALUCodeStrlen; + int m_nRecordedAttribCodeStrlen; + + // In GLSL mode, these store the semantic attached to each oN register. + // They are the values that you pass to GetUsageIndexAndString. + uint32 m_DeclaredOutputs[MAX_DECLARED_OUTPUTS]; + + uint32 m_DeclaredInputs[MAX_DECLARED_INPUTS]; + + // Have they used the tangent input semantic (i.e. is g_pTangentAttributeName declared)? + bool m_bTangentInputUsed; + + bool m_bUsesDSTInstruction; + +private: + // Utilities to aid in decoding token stream + uint32 GetNextToken( void ); + void SkipTokens( uint32 numToSkip ); + uint32 Opcode( uint32 dwToken ); + uint32 OpcodeSpecificData( uint32 dwToken ); + uint32 TextureType ( uint32 dwToken ); + uint32 GetRegType( uint32 dwRegToken ); + + // Write to the different buffers. + void StrcatToHeaderCode( const char *pBuf ); + void StrcatToALUCode( const char *pBuf ); + void StrcatToParamCode( const char *pBuf ); + void StrcatToAttribCode( const char *pBuf ); + void PrintToBufWithIndents( CUtlBuffer &buf, const char *pFormat, ... ); + + // This helps write the token hex codes into the output stream for debugging. + void AddTokenHexCodeToBuffer( char *pBuffer, int nSize, int nLastStrlen ); + void RecordInputAndOutputPositions(); + void AddTokenHexCode(); + + // Utilities for decoding tokens in to strings according to ASM syntax + void PrintOpcode( uint32 inst, char* buff, int nBufLen ); + + // fSemanticFlags is SEMANTIC_INPUT or SEMANTIC_OUTPUT. + void PrintUsageAndIndexToString( uint32 dwToken, char* strUsageUsageIndexName, int nBufLen, int fSemanticFlags ); + CUtlString GetUsageAndIndexString( uint32 dwToken, int fSemanticFlags ); + CUtlString GetParameterString( uint32 dwToken, uint32 dwSourceOrDest, bool bForceScalarSource, int *pARLDestReg ); + const char* GetGLSLOperatorString( uint32 inst ); + + void PrintParameterToString ( uint32 dwToken, uint32 dwSourceOrDest, char *pRegisterName, int nBufLen, bool bForceScalarSource, int *pARLDestReg ); + + void InsertMoveFromAddressRegister( CUtlBuffer *pCode, int nARLComp0, int nARLComp1, int nARLComp2 = ARL_DEST_NONE ); + void InsertMoveInstruction( CUtlBuffer *pCode, int nARLComponent ); + void FlagIndirectRegister( uint32 dwToken, int *pARLDestReg ); + + // Utilities for decoding tokens in to strings according to GLSL syntax + bool OpenIntrinsic( uint32 inst, char* buff, int nBufLen, uint32 destDimension, uint32 nArgumentDimension ); + void PrintIndentation( char *pBuf, int nBufLen ); + + uint32 MaintainAttributeMap( uint32 dwToken, uint32 dwRegToken ); + + CUtlString FixGLSLSwizzle( const char *pDestRegisterName, const char *pSrcRegisterName ); + void WriteGLSLCmp( const char *pDestReg, const char *pSrc0Reg, const char *pSrc1Reg, const char *pSrc2Reg ); + void WriteGLSLSamplerDefinitions(); + void WriteGLSLOutputVariableAssignments(); + void WriteGLSLInputVariableAssignments(); + void NoteTangentInputUsed(); + + void Handle_DCL(); + void Handle_DEF(); + void Handle_DEFIB( uint32 nInstruction ); + void Handle_MAD( uint32 nInstruction ); + void Handle_DP2ADD(); + void Handle_SINCOS(); + void Handle_LRP( uint32 nInstruction ); + void Handle_TEX( uint32 dwToken, bool bIsTexLDL ); + void Handle_TexLDD( uint32 nInstruction ); + void Handle_TexCoord(); + void Handle_UnaryOp( uint32 nInstruction ); + void Handle_BREAKC( uint32 dwToken ); + void HandleBinaryOp_GLSL( uint32 nInstruction ); + void HandleBinaryOp_ASM( uint32 nInstruction ); + void Handle_CMP(); + void Handle_NRM(); + void Handle_DeclarativeNonDclOp( uint32 nInstruction ); + +public: + D3DToGL(); + + int TranslateShader( uint32* code, CUtlBuffer *pBufDisassembledCode, bool *bVertexShader, uint32 options, int32 nShadowDepthSamplerMask, uint32 nCentroidMask, char *debugLabel ); +}; + + +#endif // DX9_ASM_TO_GL_2_H diff --git a/togles/linuxwin/dxabstract.cpp b/togles/linuxwin/dxabstract.cpp new file mode 100644 index 00000000..f4459e8e --- /dev/null +++ b/togles/linuxwin/dxabstract.cpp @@ -0,0 +1,6845 @@ +//========= 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. +// +// dxabstract.cpp +// +//================================================================================================== +#include "togl/rendermechanism.h" +#include "tier0/vprof_telemetry.h" +#include "tier0/dbg.h" +#include "tier0/threadtools.h" +#include "tier0/vprof.h" +#include "tier1/strtools.h" +#include "tier1/utlbuffer.h" +#include "dx9asmtogl2.h" +#include "mathlib/vmatrix.h" +#include "materialsystem/IShader.h" + +#include "glmgr_flush.inl" + +#if defined(OSX) || defined(LINUX) || (defined (WIN32) && defined( DX_TO_GL_ABSTRACTION )) + #include "appframework/ilaunchermgr.h" + extern ILauncherMgr *g_pLauncherMgr; +#endif + +#include "tier0/icommandline.h" +#include "tier0/memdbgon.h" + +#ifdef USE_ACTUAL_DX + +#pragma comment( lib, "../../dx9sdk/lib/d3d9.lib" ) +#pragma comment( lib, "../../dx9sdk/lib/d3dx9.lib" ) + +#else + +#ifdef POSIX +#define strcat_s( a, b, c) V_strcat( a, c, b ) +#endif + +#define D3D_DEVICE_VALID_MARKER 0x12EBC845 +#define GL_PUBLIC_ENTRYPOINT_CHECKS( dev ) Assert( dev->GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); Assert( dev->m_nValidMarker == D3D_DEVICE_VALID_MARKER ); +// ------------------------------------------------------------------------------------------------------------------------------ // +bool g_bNullD3DDevice; + +static D3DToGL g_D3DToOpenGLTranslatorGLSL; +static IDirect3DDevice9 *g_pD3D_Device; + +#if GL_BATCH_PERF_ANALYSIS + #include "../../thirdparty/miniz/simple_bitmap.h" + #include "../../thirdparty/miniz/miniz.c" + + ConVar gl_batch_vis_abs_scale( "gl_batch_vis_abs_scale", ".050" ); + ConVar gl_present_vis_abs_scale( "gl_present_vis_abs_scale", ".050" ); + //ConVar gl_batch_vis_y_scale( "gl_batch_vis_y_scale", "0.007" ); + ConVar gl_batch_vis_y_scale( "gl_batch_vis_y_scale", "0.0" ); + static double s_rdtsc_to_ms; + + uint64 g_nTotalD3DCycles; + uint g_nTotalD3DCalls; + + class CGLBatchPerfCallTimer + { + public: + inline CGLBatchPerfCallTimer() { g_nTotalD3DCalls++; g_nTotalD3DCycles -= TM_FAST_TIME(); } + inline ~CGLBatchPerfCallTimer() { g_nTotalD3DCycles += TM_FAST_TIME(); } + }; + + #define GL_BATCH_PERF_CALL_TIMER CGLBatchPerfCallTimer scopedCallTimer; +#else + #define GL_BATCH_PERF_CALL_TIMER +#endif // GL_BATCH_PERF_ANALYSIS + +ConVar gl_batch_vis( "gl_batch_vis", "0" ); + +// ------------------------------------------------------------------------------------------------------------------------------ // +// functions that are dependant on g_pLauncherMgr + +inline GLMDisplayDB *GetDisplayDB( void ) +{ + return g_pLauncherMgr->GetDisplayDB(); +} + +inline void RenderedSize( uint &width, uint &height, bool set ) +{ + g_pLauncherMgr->RenderedSize( width, height, set ); +} + +inline void GetStackCrawl( CStackCrawlParams *params ) +{ + g_pLauncherMgr->GetStackCrawl(params); +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#if defined( WIN32 ) + +bool D3DMATRIX::operator == ( CONST D3DMATRIX& src) const +{ + return V_memcmp( (void*)this, (void*)&src, sizeof(this) ) == 0; +} + +D3DMATRIX::operator void* () +{ + return (void*)this; +} + +#endif + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- D3DXMATRIX operators + +#endif + +D3DXMATRIX D3DXMATRIX::operator*( const D3DXMATRIX &o ) const +{ + D3DXMATRIX result; + + D3DXMatrixMultiply( &result, this, &o ); // this = lhs o = rhs result = this * o + + return result; +} + +D3DXMATRIX::operator FLOAT* () +{ + return (float*)this; +} + +float& D3DXMATRIX::operator()( int row, int column ) +{ + return m[row][column]; +} + +const float& D3DXMATRIX::operator()( int row, int column ) const +{ + return m[row][column]; +} + +bool D3DXMATRIX::operator != ( CONST D3DXMATRIX& src ) const +{ + return V_memcmp( (void*)this, (void*)&src, sizeof(this) ) != 0; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- D3DXPLANE operators + +#endif + +float& D3DXPLANE::operator[]( int i ) +{ + return ((float*)this)[i]; +} + +bool D3DXPLANE::operator==( const D3DXPLANE &o ) +{ + return a == o.a && b == o.b && c == o.c && d == o.d; +} + +bool D3DXPLANE::operator!=( const D3DXPLANE &o ) +{ + return !( *this == o ); +} + +D3DXPLANE::operator float*() +{ + return (float*)this; +} + +D3DXPLANE::operator const float*() const +{ + return (const float*)this; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- D3DXVECTOR2 operators + +#endif + +D3DXVECTOR2::operator FLOAT* () +{ + return (float*)this; +} + +D3DXVECTOR2::operator CONST FLOAT* () const +{ + return (const float*)this; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- D3DXVECTOR3 operators + +#endif + +D3DXVECTOR3::D3DXVECTOR3( float a, float b, float c ) +{ + x = a; + y = b; + z = c; +} + +D3DXVECTOR3::operator FLOAT* () +{ + return (float*)this; +} + +D3DXVECTOR3::operator CONST FLOAT* () const +{ + return (const float*)this; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- D3DXVECTOR4 operators + +#endif + +D3DXVECTOR4::D3DXVECTOR4( float a, float b, float c, float d ) +{ + x = a; + y = b; + z = c; + w = d; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +DWORD IDirect3DResource9::SetPriority(DWORD PriorityNew) +{ +// DXABSTRACT_BREAK_ON_ERROR(); +// GLMPRINTF(( "-X- SetPriority" )); + // no-op city + return 0; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DBaseTexture9 + +#endif + +IDirect3DBaseTexture9::~IDirect3DBaseTexture9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DBaseTexture9" )); + + if (m_device) + { + Assert( m_device->m_ObjectStats.m_nTotalTextures >= 1 ); + m_device->m_ObjectStats.m_nTotalTextures--; + + GLMPRINTF(( "-A- ~IDirect3DBaseTexture9 taking normal delete path on %08x, device is %08x ", this, m_device )); + m_device->ReleasedTexture( this ); + + if (m_tex) + { + GLMPRINTF(("-A- ~IDirect3DBaseTexture9 deleted '%s' @ %08x (GLM %08x) %s",m_tex->m_layout->m_layoutSummary, this, m_tex, m_tex->m_debugLabel ? m_tex->m_debugLabel : "" )); + + m_device->ReleasedCGLMTex( m_tex ); + + m_tex->m_ctx->DelTex( m_tex ); + m_tex = NULL; + } + else + { + GLMPRINTF(( "-A- ~IDirect3DBaseTexture9 : whoops, no tex to delete here ?" )); + } + m_device = NULL; // ** THIS ** is the only place to scrub this. Don't do it in the subclass destructors. + } + else + { + GLMPRINTF(( "-A- ~IDirect3DBaseTexture9 taking strange delete path on %08x, device is %08x ", this, m_device )); + } + + GLMPRINTF(( "<-A- ~IDirect3DBaseTexture9" )); +} + +D3DRESOURCETYPE IDirect3DBaseTexture9::GetType() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + return m_restype; //D3DRTYPE_TEXTURE; +} + +DWORD IDirect3DBaseTexture9::GetLevelCount() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + return m_tex->m_layout->m_mipCount; +} + +HRESULT IDirect3DBaseTexture9::GetLevelDesc(UINT Level,D3DSURFACE_DESC *pDesc) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + Assert (Level < static_cast(m_tex->m_layout->m_mipCount)); + + D3DSURFACE_DESC result = m_descZero; + // then mutate it for the level of interest + + GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; + + result.Width = slice->m_xSize; + result.Height = slice->m_ySize; + + *pDesc = result; + + return S_OK; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DTexture9 + +#endif + +HRESULT IDirect3DDevice9::CreateTexture(UINT Width,UINT Height,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DTexture9** ppTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + m_ObjectStats.m_nTotalTextures++; + + GLMPRINTF((">-A-IDirect3DDevice9::CreateTexture")); + IDirect3DTexture9 *dxtex = new IDirect3DTexture9; + dxtex->m_restype = D3DRTYPE_TEXTURE; + + dxtex->m_device = this; + + dxtex->m_descZero.Format = Format; + dxtex->m_descZero.Type = D3DRTYPE_TEXTURE; + dxtex->m_descZero.Usage = Usage; + dxtex->m_descZero.Pool = Pool; + + dxtex->m_descZero.MultiSampleType = D3DMULTISAMPLE_NONE; + dxtex->m_descZero.MultiSampleQuality = 0; + dxtex->m_descZero.Width = Width; + dxtex->m_descZero.Height = Height; + + GLMTexLayoutKey key; + memset( &key, 0, sizeof(key) ); + + key.m_texGLTarget = GL_TEXTURE_2D; + key.m_texFormat = Format; + + if (Levels>1) + { + key.m_texFlags |= kGLMTexMipped; + } + + // http://msdn.microsoft.com/en-us/library/bb172625(VS.85).aspx + + // complain if any usage bits come down that I don't know. + uint knownUsageBits = (D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC | D3DUSAGE_TEXTURE_SRGB | D3DUSAGE_DEPTHSTENCIL); + if ( (Usage & knownUsageBits) != Usage ) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + if (Usage & D3DUSAGE_AUTOGENMIPMAP) + { + key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; + } + + if (Usage & D3DUSAGE_DYNAMIC) + { + // GLMPRINTF(("-X- DYNAMIC tex usage ignored..")); //FIXME + } + + if (Usage & D3DUSAGE_TEXTURE_SRGB) + { + key.m_texFlags |= kGLMTexSRGB; + } + + if (Usage & D3DUSAGE_RENDERTARGET) + { + Assert( !(Usage & D3DUSAGE_DEPTHSTENCIL) ); + + m_ObjectStats.m_nTotalRenderTargets++; + + key.m_texFlags |= kGLMTexRenderable; + + const GLMTexFormatDesc *pFmtDesc = GetFormatDesc( key.m_texFormat ); + if ( pFmtDesc->m_glIntFormatSRGB != 0 ) + { + key.m_texFlags |= kGLMTexSRGB; // this catches callers of CreateTexture who set the "renderable" option - they get an SRGB tex + } + + if (m_ctx->Caps().m_cantAttachSRGB) + { + // this config can't support SRGB render targets. quietly turn off the sRGB bit. + key.m_texFlags &= ~kGLMTexSRGB; + } + } + + if ( ( Format == D3DFMT_D16 ) || ( Format == D3DFMT_D24X8 ) || ( Format == D3DFMT_D24S8 ) ) + { + key.m_texFlags |= kGLMTexIsDepth; + } + if ( Format == D3DFMT_D24S8 ) + { + key.m_texFlags |= kGLMTexIsStencil; + } + + key.m_xSize = Width; + key.m_ySize = Height; + key.m_zSize = 1; + + CGLMTex *tex = m_ctx->NewTex( &key, Levels, pDebugLabel ); + if (!tex) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + dxtex->m_tex = tex; + + dxtex->m_tex->m_srgbFlipCount = 0; + + m_ObjectStats.m_nTotalSurfaces++; + + dxtex->m_surfZero = new IDirect3DSurface9; + dxtex->m_surfZero->m_restype = (D3DRESOURCETYPE)0; // this is a ref to a tex, not the owner... + + // do not do an AddRef here. + + dxtex->m_surfZero->m_device = this; + + dxtex->m_surfZero->m_desc = dxtex->m_descZero; + dxtex->m_surfZero->m_tex = tex; + dxtex->m_surfZero->m_face = 0; + dxtex->m_surfZero->m_mip = 0; + + GLMPRINTF(("-A- IDirect3DDevice9::CreateTexture created '%s' @ %08x (GLM %08x) %s",tex->m_layout->m_layoutSummary, dxtex, tex, pDebugLabel ? pDebugLabel : "" )); + + *ppTexture = dxtex; + + GLMPRINTF(("<-A-IDirect3DDevice9::CreateTexture")); + return S_OK; +} + + +IDirect3DTexture9::~IDirect3DTexture9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + GLMPRINTF(( ">-A- IDirect3DTexture9" )); + + // IDirect3DBaseTexture9::~IDirect3DBaseTexture9 frees up m_tex + // we take care of surfZero + + if (m_device) + { + m_device->ReleasedTexture( this ); + + if (m_surfZero) + { + ULONG refc = m_surfZero->Release( 0, "~IDirect3DTexture9 public release (surfZero)" ); (void)refc; + Assert( !refc ); + m_surfZero = NULL; + } + // leave m_device alone! + } + + GLMPRINTF(( "<-A- IDirect3DTexture9" )); +} + +HRESULT IDirect3DTexture9::LockRect(UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DTexture9::UnlockRect(UINT Level) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DTexture9::GetSurfaceLevel(UINT Level,IDirect3DSurface9** ppSurfaceLevel) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + // we create and pass back a surface, and the client is on the hook to release it. tidy. + + m_device->m_ObjectStats.m_nTotalSurfaces++; + + IDirect3DSurface9 *surf = new IDirect3DSurface9; + surf->m_restype = (D3DRESOURCETYPE)0; // 0 is special and means this 'surface' does not own its m_tex + + // Dicey...higher level code seems to want this and not want this. Are we missing some AddRef/Release behavior elsewhere? + // trying to turn this off - experimental - 26Oct2010 surf->AddRef(); + + surf->m_device = this->m_device; + + GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; + + surf->m_desc = m_descZero; + surf->m_desc.Width = slice->m_xSize; + surf->m_desc.Height = slice->m_ySize; + + surf->m_tex = m_tex; + surf->m_face = 0; + surf->m_mip = Level; + + *ppSurfaceLevel = surf; + + return S_OK; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DCubeTexture9 + +#endif + +HRESULT IDirect3DDevice9::CreateCubeTexture(UINT EdgeLength,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DCubeTexture9** ppCubeTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel) +{ + GL_BATCH_PERF_CALL_TIMER; + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + GLMPRINTF((">-A- IDirect3DDevice9::CreateCubeTexture")); + + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + m_ObjectStats.m_nTotalTextures++; + + IDirect3DCubeTexture9 *dxtex = new IDirect3DCubeTexture9; + dxtex->m_restype = D3DRTYPE_CUBETEXTURE; + + dxtex->m_device = this; + + dxtex->m_descZero.Format = Format; + dxtex->m_descZero.Type = D3DRTYPE_CUBETEXTURE; + dxtex->m_descZero.Usage = Usage; + dxtex->m_descZero.Pool = Pool; + + dxtex->m_descZero.MultiSampleType = D3DMULTISAMPLE_NONE; + dxtex->m_descZero.MultiSampleQuality = 0; + dxtex->m_descZero.Width = EdgeLength; + dxtex->m_descZero.Height = EdgeLength; + + GLMTexLayoutKey key; + memset( &key, 0, sizeof(key) ); + + key.m_texGLTarget = GL_TEXTURE_CUBE_MAP; + key.m_texFormat = Format; + + if (Levels>1) + { + key.m_texFlags |= kGLMTexMipped; + } + + // http://msdn.microsoft.com/en-us/library/bb172625(VS.85).aspx + // complain if any usage bits come down that I don't know. + uint knownUsageBits = (D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC | D3DUSAGE_TEXTURE_SRGB); + if ( (Usage & knownUsageBits) != Usage ) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + if (Usage & D3DUSAGE_AUTOGENMIPMAP) + { + key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; + } + + if (Usage & D3DUSAGE_RENDERTARGET) + { + key.m_texFlags |= kGLMTexRenderable; + + m_ObjectStats.m_nTotalRenderTargets++; + } + + if (Usage & D3DUSAGE_DYNAMIC) + { + //GLMPRINTF(("-X- DYNAMIC tex usage ignored..")); //FIXME + } + + if (Usage & D3DUSAGE_TEXTURE_SRGB) + { + key.m_texFlags |= kGLMTexSRGB; + } + + key.m_xSize = EdgeLength; + key.m_ySize = EdgeLength; + key.m_zSize = 1; + + CGLMTex *tex = m_ctx->NewTex( &key, Levels, pDebugLabel ); + if (!tex) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + dxtex->m_tex = tex; + + dxtex->m_tex->m_srgbFlipCount = 0; + + for( int face = 0; face < 6; face ++) + { + m_ObjectStats.m_nTotalSurfaces++; + + dxtex->m_surfZero[face] = new IDirect3DSurface9; + dxtex->m_surfZero[face]->m_restype = (D3DRESOURCETYPE)0; // 0 is special and means this 'surface' does not own its m_tex + // do not do an AddRef here. + + dxtex->m_surfZero[face]->m_device = this; + + dxtex->m_surfZero[face]->m_desc = dxtex->m_descZero; + dxtex->m_surfZero[face]->m_tex = tex; + dxtex->m_surfZero[face]->m_face = face; + dxtex->m_surfZero[face]->m_mip = 0; + } + + GLMPRINTF(("-A- IDirect3DDevice9::CreateCubeTexture created '%s' @ %08x (GLM %08x)",tex->m_layout->m_layoutSummary, dxtex, tex )); + + *ppCubeTexture = dxtex; + + GLMPRINTF(("<-A- IDirect3DDevice9::CreateCubeTexture")); + + return S_OK; +} + +IDirect3DCubeTexture9::~IDirect3DCubeTexture9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DCubeTexture9" )); + + if (m_device) + { + GLMPRINTF(( "-A- ~IDirect3DCubeTexture9 taking normal delete path on %08x, device is %08x, surfzero[0] is %08x ", this, m_device, m_surfZero[0] )); + m_device->ReleasedTexture( this ); + + // let IDirect3DBaseTexture9::~IDirect3DBaseTexture9 free up m_tex + // we handle the surfZero array for the faces + + for( int face = 0; face < 6; face ++) + { + if (m_surfZero[face]) + { + Assert( m_surfZero[face]->m_device == m_device ); + ULONG refc = m_surfZero[face]->Release( 0, "~IDirect3DCubeTexture9 public release (surfZero)"); + if ( refc!=0 ) + { + GLMPRINTF(( "-A- ~IDirect3DCubeTexture9 seeing non zero refcount on surfzero[%d] => %d ", face, refc )); + } + m_surfZero[face] = NULL; + } + } + // leave m_device alone! + } + else + { + GLMPRINTF(( "-A- ~IDirect3DCubeTexture9 taking strange delete path on %08x, device is %08x, surfzero[0] is %08x ", this, m_device, m_surfZero[0] )); + } + + GLMPRINTF(( "<-A- ~IDirect3DCubeTexture9" )); +} + +HRESULT IDirect3DCubeTexture9::GetCubeMapSurface(D3DCUBEMAP_FACES FaceType,UINT Level,IDirect3DSurface9** ppCubeMapSurface) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + // we create and pass back a surface, and the client is on the hook to release it... + + m_device->m_ObjectStats.m_nTotalSurfaces++; + + IDirect3DSurface9 *surf = new IDirect3DSurface9; + surf->m_restype = (D3DRESOURCETYPE)0; // 0 is special and means this 'surface' does not own its m_tex + + GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( FaceType, Level ) ]; + + surf->m_device = this->m_device; + + surf->m_desc = m_descZero; + surf->m_desc.Width = slice->m_xSize; + surf->m_desc.Height = slice->m_ySize; + + surf->m_tex = m_tex; + surf->m_face = FaceType; + surf->m_mip = Level; + + *ppCubeMapSurface = surf; + + return S_OK; +} + +HRESULT IDirect3DCubeTexture9::GetLevelDesc(UINT Level,D3DSURFACE_DESC *pDesc) +{ + GL_BATCH_PERF_CALL_TIMER; + Assert (Level < static_cast(m_tex->m_layout->m_mipCount)); + + D3DSURFACE_DESC result = m_descZero; + // then mutate it for the level of interest + + GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; + + result.Width = slice->m_xSize; + result.Height = slice->m_ySize; + + *pDesc = result; + + return S_OK; +} + + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DVolumeTexture9 + +#endif + +HRESULT IDirect3DDevice9::CreateVolumeTexture(UINT Width,UINT Height,UINT Depth,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DVolumeTexture9** ppVolumeTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF((">-A- IDirect3DDevice9::CreateVolumeTexture")); + // set dxtex->m_restype to D3DRTYPE_VOLUMETEXTURE... + + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + m_ObjectStats.m_nTotalTextures++; + + IDirect3DVolumeTexture9 *dxtex = new IDirect3DVolumeTexture9; + dxtex->m_restype = D3DRTYPE_VOLUMETEXTURE; + + dxtex->m_device = this; + + dxtex->m_descZero.Format = Format; + dxtex->m_descZero.Type = D3DRTYPE_VOLUMETEXTURE; + dxtex->m_descZero.Usage = Usage; + dxtex->m_descZero.Pool = Pool; + + dxtex->m_descZero.MultiSampleType = D3DMULTISAMPLE_NONE; + dxtex->m_descZero.MultiSampleQuality = 0; + dxtex->m_descZero.Width = Width; + dxtex->m_descZero.Height = Height; + + // also a volume specific desc + dxtex->m_volDescZero.Format = Format; + dxtex->m_volDescZero.Type = D3DRTYPE_VOLUMETEXTURE; + dxtex->m_volDescZero.Usage = Usage; + dxtex->m_volDescZero.Pool = Pool; + + dxtex->m_volDescZero.Width = Width; + dxtex->m_volDescZero.Height = Height; + dxtex->m_volDescZero.Depth = Depth; + + GLMTexLayoutKey key; + memset( &key, 0, sizeof(key) ); + + key.m_texGLTarget = GL_TEXTURE_3D; + key.m_texFormat = Format; + + if (Levels>1) + { + key.m_texFlags |= kGLMTexMipped; + } + + // http://msdn.microsoft.com/en-us/library/bb172625(VS.85).aspx + // complain if any usage bits come down that I don't know. + uint knownUsageBits = (D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC | D3DUSAGE_TEXTURE_SRGB); + if ( (Usage & knownUsageBits) != Usage ) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + if (Usage & D3DUSAGE_AUTOGENMIPMAP) + { + key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; + } + + if (Usage & D3DUSAGE_RENDERTARGET) + { + key.m_texFlags |= kGLMTexRenderable; + + m_ObjectStats.m_nTotalRenderTargets++; + } + + if (Usage & D3DUSAGE_DYNAMIC) + { + GLMPRINTF(("-X- DYNAMIC tex usage ignored..")); //FIXME + } + + if (Usage & D3DUSAGE_TEXTURE_SRGB) + { + key.m_texFlags |= kGLMTexSRGB; + } + + key.m_xSize = Width; + key.m_ySize = Height; + key.m_zSize = Depth; + + CGLMTex *tex = m_ctx->NewTex( &key, Levels, pDebugLabel ); + if (!tex) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + dxtex->m_tex = tex; + + dxtex->m_tex->m_srgbFlipCount = 0; + + m_ObjectStats.m_nTotalSurfaces++; + + dxtex->m_surfZero = new IDirect3DSurface9; + dxtex->m_surfZero->m_restype = (D3DRESOURCETYPE)0; // this is a ref to a tex, not the owner... + // do not do an AddRef here. + + dxtex->m_surfZero->m_device = this; + + dxtex->m_surfZero->m_desc = dxtex->m_descZero; + dxtex->m_surfZero->m_tex = tex; + dxtex->m_surfZero->m_face = 0; + dxtex->m_surfZero->m_mip = 0; + + GLMPRINTF(("-A- IDirect3DDevice9::CreateVolumeTexture created '%s' @ %08x (GLM %08x)",tex->m_layout->m_layoutSummary, dxtex, tex )); + + *ppVolumeTexture = dxtex; + + GLMPRINTF(("<-A- IDirect3DDevice9::CreateVolumeTexture")); + + return S_OK; +} + +IDirect3DVolumeTexture9::~IDirect3DVolumeTexture9() +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF((">-A- ~IDirect3DVolumeTexture9")); + + if (m_device) + { + m_device->ReleasedTexture( this ); + + // let IDirect3DBaseTexture9::~IDirect3DBaseTexture9 free up m_tex + // we handle m_surfZero + + if (m_surfZero) + { + ULONG refc = m_surfZero->Release( 0, "~IDirect3DVolumeTexture9 public release (surfZero)" ); (void)refc; + Assert( !refc ); + m_surfZero = NULL; + } + // leave m_device alone! + } + + GLMPRINTF(("<-A- ~IDirect3DVolumeTexture9")); +} + +HRESULT IDirect3DVolumeTexture9::LockBox(UINT Level,D3DLOCKED_BOX* pLockedVolume,CONST D3DBOX* pBox,DWORD Flags) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + GLMTexLockParams lockreq; + memset( &lockreq, 0, sizeof(lockreq) ); + + lockreq.m_tex = this->m_tex; + lockreq.m_face = 0; + lockreq.m_mip = Level; + + lockreq.m_region.xmin = pBox->Left; + lockreq.m_region.ymin = pBox->Top; + lockreq.m_region.zmin = pBox->Front; + lockreq.m_region.xmax = pBox->Right; + lockreq.m_region.ymax = pBox->Bottom; + lockreq.m_region.zmax = pBox->Back; + + char *lockAddress; + int yStride; + int zStride; + + lockreq.m_tex->Lock( &lockreq, &lockAddress, &yStride, &zStride ); + + pLockedVolume->RowPitch = yStride; + pLockedVolume->SlicePitch = yStride; + pLockedVolume->pBits = lockAddress; + + return S_OK; +} + +HRESULT IDirect3DVolumeTexture9::UnlockBox(UINT Level) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + GLMTexLockParams lockreq; + memset( &lockreq, 0, sizeof(lockreq) ); + + lockreq.m_tex = this->m_tex; + lockreq.m_face = 0; + lockreq.m_mip = Level; + + this->m_tex->Unlock( &lockreq ); + + return S_OK; +} + +HRESULT IDirect3DVolumeTexture9::GetLevelDesc( UINT Level, D3DVOLUME_DESC *pDesc ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + if (Level > static_cast(m_tex->m_layout->m_mipCount)) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + D3DVOLUME_DESC result = m_volDescZero; + // then mutate it for the level of interest + + GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; + + result.Width = slice->m_xSize; + result.Height = slice->m_ySize; + result.Depth = slice->m_zSize; + + *pDesc = result; + + return S_OK; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DSurface9 + +#endif + +IDirect3DSurface9::~IDirect3DSurface9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + // not much to do here, but good to verify that these things are being freed (and they are) + //GLMPRINTF(("-A- ~IDirect3DSurface9 - signpost")); + + if (m_device) + { + GLMPRINTF(("-A- ~IDirect3DSurface9 - taking real delete path on %08x device %08x", this, m_device)); + m_device->ReleasedSurface( this ); + + memset( &m_desc, 0, sizeof(m_desc) ); + + if (m_restype != 0) // signal that we are a surface that owns this tex (render target) + { + if (m_tex) + { + GLMPRINTF(("-A- ~IDirect3DSurface9 deleted '%s' @ %08x (GLM %08x) %s",m_tex->m_layout->m_layoutSummary, this, m_tex, m_tex->m_debugLabel ? m_tex->m_debugLabel : "" )); + + m_device->ReleasedCGLMTex( m_tex ); + + m_tex->m_ctx->DelTex( m_tex ); + m_tex = NULL; + } + else + { + GLMPRINTF(( "-A- ~IDirect3DSurface9 : whoops, no tex to delete here ?" )); + } + } + else + { + m_tex = NULL; // we are just a view on the tex, we don't own the tex, do not delete it + } + + m_face = m_mip = 0; + + m_device = NULL; + } + else + { + GLMPRINTF(("-A- ~IDirect3DSurface9 - taking strange delete path on %08x device %08x", this, m_device)); + } +} + +HRESULT IDirect3DSurface9::LockRect(D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMTexLockParams lockreq; + memset( &lockreq, 0, sizeof(lockreq) ); + + lockreq.m_tex = this->m_tex; + lockreq.m_face = this->m_face; + lockreq.m_mip = this->m_mip; + + lockreq.m_region.xmin = pRect->left; + lockreq.m_region.ymin = pRect->top; + lockreq.m_region.zmin = 0; + lockreq.m_region.xmax = pRect->right; + lockreq.m_region.ymax = pRect->bottom; + lockreq.m_region.zmax = 1; + + if ((Flags & (D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK)) == (D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK) ) + { + // smells like readback, force texel readout + lockreq.m_readback = true; + } + + char *lockAddress; + int yStride; + int zStride; + + lockreq.m_tex->Lock( &lockreq, &lockAddress, &yStride, &zStride ); + + pLockedRect->Pitch = yStride; + pLockedRect->pBits = lockAddress; + + return S_OK; +} + +HRESULT IDirect3DSurface9::UnlockRect() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMTexLockParams lockreq; + memset( &lockreq, 0, sizeof(lockreq) ); + + lockreq.m_tex = this->m_tex; + lockreq.m_face = this->m_face; + lockreq.m_mip = this->m_mip; + + lockreq.m_tex->Unlock( &lockreq ); + + return S_OK; +} + +HRESULT IDirect3DSurface9::GetDesc(D3DSURFACE_DESC *pDesc) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + *pDesc = m_desc; + return S_OK; +} + + +// ------------------------------------------------------------------------------------------------------------------------------ // + + +#ifdef OSX + +#pragma mark ----- IDirect3D9 ------------------------------------------------------- + +#endif + +IDirect3D9::~IDirect3D9() +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF(("-A- ~IDirect3D9 - signpost")); +} + +UINT IDirect3D9::GetAdapterCount() +{ + GL_BATCH_PERF_CALL_TIMER; + GLMgr::NewGLMgr(); // init GL manager + + GLMDisplayDB *db = GetDisplayDB(); + int dxAdapterCount = db->GetFakeAdapterCount(); + + return dxAdapterCount; +} + +static void FillD3DCaps9( const GLMRendererInfoFields &glmRendererInfo, D3DCAPS9* pCaps ) +{ + // fill in the pCaps record for adapter... we zero most of it and just fill in the fields that we think the caller wants. + Q_memset( pCaps, 0, sizeof(*pCaps) ); + + + /* Device Info */ + pCaps->DeviceType = D3DDEVTYPE_HAL; + + /* Caps from DX7 Draw */ + pCaps->Caps = 0; // does anyone look at this ? + + pCaps->Caps2 = D3DCAPS2_DYNAMICTEXTURES; + /* Cursor Caps */ + pCaps->CursorCaps = 0; // nobody looks at this + + /* 3D Device Caps */ + pCaps->DevCaps = D3DDEVCAPS_HWTRANSFORMANDLIGHT; + + pCaps->TextureCaps = D3DPTEXTURECAPS_CUBEMAP | D3DPTEXTURECAPS_MIPCUBEMAP | D3DPTEXTURECAPS_NONPOW2CONDITIONAL | D3DPTEXTURECAPS_PROJECTED; + // D3DPTEXTURECAPS_NOPROJECTEDBUMPENV ? + // D3DPTEXTURECAPS_POW2 ? + // caller looks at POT support like this: + // pCaps->m_SupportsNonPow2Textures = + // ( !( caps.TextureCaps & D3DPTEXTURECAPS_POW2 ) || + // ( caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL ) ); + // so we should set D3DPTEXTURECAPS_NONPOW2CONDITIONAL bit ? + + + pCaps->PrimitiveMiscCaps = 0; //only the HDR setup looks at this for D3DPMISCCAPS_SEPARATEALPHABLEND. + // ? D3DPMISCCAPS_SEPARATEALPHABLEND + // ? D3DPMISCCAPS_BLENDOP + // ? D3DPMISCCAPS_CLIPPLANESCALEDPOINTS + // ? D3DPMISCCAPS_CLIPTLVERTS D3DPMISCCAPS_COLORWRITEENABLE D3DPMISCCAPS_MASKZ D3DPMISCCAPS_TSSARGTEMP + + + pCaps->RasterCaps = D3DPRASTERCAPS_SCISSORTEST + | D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS // ref'd in CShaderDeviceMgrDx8::ComputeCapsFromD3D + | D3DPRASTERCAPS_DEPTHBIAS // ref'd in CShaderDeviceMgrDx8::ComputeCapsFromD3D + ; + + pCaps->TextureFilterCaps = D3DPTFILTERCAPS_MINFANISOTROPIC | D3DPTFILTERCAPS_MAGFANISOTROPIC; + + pCaps->MaxTextureWidth = 4096; + pCaps->MaxTextureHeight = 4096; + pCaps->MaxVolumeExtent = 1024; //guesses + + pCaps->MaxTextureAspectRatio = 0; // imply no limit on AR + + pCaps->MaxAnisotropy = glmRendererInfo.m_maxAniso; + + pCaps->TextureOpCaps = D3DTEXOPCAPS_ADD | D3DTEXOPCAPS_MODULATE2X; //guess + //DWORD MaxTextureBlendStages; + //DWORD MaxSimultaneousTextures; + + pCaps->VertexProcessingCaps = D3DVTXPCAPS_TEXGEN_SPHEREMAP; + + pCaps->MaxActiveLights = 8; // guess + + + // MaxUserClipPlanes. A bit complicated.. + // it's difficult to make this fluid without teaching the engine about a cap that could change during run. + + // start it out set to '2'. + // turn it off, if we're in GLSL mode but do not have native clip plane capability. + pCaps->MaxUserClipPlanes = 2; // assume good news + + // is user asking for it to be off ? + if ( CommandLine()->CheckParm( "-nouserclip" ) ) + { + pCaps->MaxUserClipPlanes = 0; + } + + pCaps->MaxVertexBlendMatrices = 0; // see if anyone cares + pCaps->MaxVertexBlendMatrixIndex = 0; // see if anyone cares + + pCaps->MaxPrimitiveCount = 32768; // guess + pCaps->MaxStreams = D3D_MAX_STREAMS; // guess + + pCaps->VertexShaderVersion = 0x300; // model 3.0 + pCaps->MaxVertexShaderConst = DXABSTRACT_VS_PARAM_SLOTS; // number of vertex shader constant registers + + pCaps->PixelShaderVersion = 0x300; // model 3.0 + + // Here are the DX9 specific ones + pCaps->DevCaps2 = D3DDEVCAPS2_STREAMOFFSET; + + pCaps->PS20Caps.NumInstructionSlots = 512; // guess + // only examined once: + // pCaps->m_SupportsPixelShaders_2_b = ( ( caps.PixelShaderVersion & 0xffff ) >= 0x0200) && (caps.PS20Caps.NumInstructionSlots >= 512); + //pCaps->m_SupportsPixelShaders_2_b = 1; + + pCaps->NumSimultaneousRTs = 1; // Will be at least 1 + pCaps->MaxVertexShader30InstructionSlots = 0; + pCaps->MaxPixelShader30InstructionSlots = 0; + +#if DX_TO_GL_ABSTRACTION + pCaps->FakeSRGBWrite = !glmRendererInfo.m_hasGammaWrites; + pCaps->CanDoSRGBReadFromRTs = !glmRendererInfo.m_cantAttachSRGB; + pCaps->MixedSizeTargets = glmRendererInfo.m_hasMixedAttachmentSizes; +#endif +} + +HRESULT IDirect3D9::GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) +{ + GL_BATCH_PERF_CALL_TIMER; + // Generally called from "CShaderDeviceMgrDx8::ComputeCapsFromD3D" in ShaderDeviceDX8.cpp + + // "Adapter" is used to index amongst the set of fake-adapters maintained in the display DB + GLMDisplayDB *db = GetDisplayDB(); + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; + Assert (!result); + // just leave glmRendererInfo filled out for subsequent code to look at as needed. + + FillD3DCaps9( glmRendererInfo, pCaps ); + + return S_OK; +} + +HRESULT IDirect3D9::GetAdapterIdentifier( UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier ) +{ + GL_BATCH_PERF_CALL_TIMER; + // Generally called from "CShaderDeviceMgrDx8::ComputeCapsFromD3D" in ShaderDeviceDX8.cpp + + Assert( Flags == D3DENUM_WHQL_LEVEL ); // we're not handling any other queries than this yet + + Q_memset( pIdentifier, 0, sizeof(*pIdentifier) ); + + GLMDisplayDB *db = GetDisplayDB(); + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; + Assert (!result); + +#ifndef OSX + if( glmRendererInfo.m_rendererID ) +#endif + { + const char *pRenderer = GLMDecode( eGL_RENDERER, glmRendererInfo.m_rendererID & 0x00FFFF00 ); + + Q_snprintf( pIdentifier->Driver, sizeof(pIdentifier->Driver), "OpenGL %s (%08x)", + pRenderer, glmRendererInfo.m_rendererID ); + + Q_snprintf( pIdentifier->Description, sizeof(pIdentifier->Description), "%s - %dx%d - %dMB VRAM", + pRenderer, + glmDisplayInfo.m_displayPixelWidth, glmDisplayInfo.m_displayPixelHeight, + glmRendererInfo.m_vidMemory >> 20 ); + } +#ifndef OSX + else + { + static CDynamicFunctionOpenGL< true, const GLubyte *( APIENTRY *)(GLenum name), const GLubyte * > glGetString("glGetString"); + + const char *pszStringVendor = ( const char * )glGetString( GL_VENDOR ); // NVIDIA Corporation + const char *pszStringRenderer = ( const char * )glGetString( GL_RENDERER ); // GeForce GTX 680/PCIe/SSE2 + const char *pszStringVersion = ( const char * )glGetString( GL_VERSION ); // 4.2.0 NVIDIA 304.22 + + Q_snprintf( pIdentifier->Driver, sizeof( pIdentifier->Driver ), "OpenGL %s (%s)", + pszStringVendor, pszStringRenderer ); + Q_snprintf( pIdentifier->Description, sizeof( pIdentifier->Description ), "%s (%s) %s - %dx%d", + pszStringVendor, pszStringRenderer, pszStringVersion, + glmDisplayInfo.m_displayPixelWidth, glmDisplayInfo.m_displayPixelHeight ); + } +#endif // !OSX + + pIdentifier->VendorId = glmRendererInfo.m_pciVendorID; // 4318; + pIdentifier->DeviceId = glmRendererInfo.m_pciDeviceID; // 401; + pIdentifier->SubSysId = 0; // 3358668866; + pIdentifier->Revision = 0; // 162; + pIdentifier->VideoMemory = glmRendererInfo.m_vidMemory; // amount of video memory in bytes + + #if 0 + // this came from the shaderapigl effort + Q_strncpy( pIdentifier->Driver, "Fake-Video-Card", MAX_DEVICE_IDENTIFIER_STRING ); + Q_strncpy( pIdentifier->Description, "Fake-Video-Card", MAX_DEVICE_IDENTIFIER_STRING ); + pIdentifier->VendorId = 4318; + pIdentifier->DeviceId = 401; + pIdentifier->SubSysId = 3358668866; + pIdentifier->Revision = 162; + #endif + + return S_OK; +} + +HRESULT IDirect3D9::CheckDeviceFormat(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) +{ + GL_BATCH_PERF_CALL_TIMER; + if (0) // hush for now, less spew + { + GLMPRINTF(("-X- ** IDirect3D9::CheckDeviceFormat: \n -- Adapter=%d || DeviceType=%4x:%s || AdapterFormat=%8x:%s\n -- RType %8x: %s\n -- CheckFormat %8x: %s\n -- Usage %8x: %s", + Adapter, + DeviceType, GLMDecode(eD3D_DEVTYPE, DeviceType), + AdapterFormat, GLMDecode(eD3D_FORMAT, AdapterFormat), + RType, GLMDecode(eD3D_RTYPE, RType), + CheckFormat, GLMDecode(eD3D_FORMAT, CheckFormat), + Usage, GLMDecodeMask( eD3D_USAGE, Usage ) )); + } + + HRESULT result = D3DERR_NOTAVAILABLE; // failure + + DWORD knownUsageMask = D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL | D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP + | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING + | D3DUSAGE_QUERY_VERTEXTEXTURE; + (void)knownUsageMask; + + // FramebufferSRGB stuff. + // basically a format is only allowed to have SRGB usage for writing, if you have the framebuffer SRGB extension. + // so, check for that capability with GLM adapter db, and if it's not there, don't mark that bit as usable in any of our formats. + GLMDisplayDB *db = GetDisplayDB(); + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + bool dbresult = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)dbresult; + Assert (!dbresult); + + Assert ((Usage & knownUsageMask) == Usage); + + DWORD legalUsage = 0; + switch( AdapterFormat ) + { + case D3DFMT_X8R8G8B8: + switch( RType ) + { + case D3DRTYPE_TEXTURE: + switch( CheckFormat ) + { + case D3DFMT_DXT1: + case D3DFMT_DXT3: + case D3DFMT_DXT5: + legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_QUERY_SRGBREAD; + + //open question: is auto gen of mipmaps is allowed or attempted on any DXT textures. + break; + + case D3DFMT_A8R8G8B8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + break; + +//$ TODO: Need to merge bitmap changes over from Dota to get these formats. +#if 0 + case D3DFMT_A2R10G10B10: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + break; + + case D3DFMT_A2B10G10R10: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + break; +#endif + + case D3DFMT_R32F: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + break; + + case D3DFMT_A16B16G16R16: + legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + break; + + case D3DFMT_A16B16G16R16F: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE; + + if ( !glmRendererInfo.m_atiR5xx ) + { + legalUsage |= D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + } + break; + + case D3DFMT_A32B32G32R32F: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE; + + if ( !glmRendererInfo.m_atiR5xx && !glmRendererInfo.m_nvG7x ) + { + legalUsage |= D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + } + break; + + case D3DFMT_R5G6B5: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + //----------------------------------------------------------- + // these come in from TestTextureFormat in ColorFormatDX8.cpp which is being driven by InitializeColorInformation... + // which is going to try all 8 combinations of (vertex texturable / render targetable / filterable ) on every image format it knows. + + case D3DFMT_R8G8B8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_QUERY_SRGBREAD; + break; + + case D3DFMT_X8R8G8B8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE; + break; + + // one and two channel textures... we'll have to fake these as four channel tex if we want to support them + case D3DFMT_L8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_A8L8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_A8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + // going to need to go back and double check all of these.. + case D3DFMT_X1R5G5B5: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_A4R4G4B4: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_A1R5G5B5: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_V8U8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_Q8W8V8U8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + // what the heck is QWVU8 ... ? + break; + + case D3DFMT_X8L8V8U8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + // what the heck is XLVU8 ... ? + break; + + // formats with depth... + + case D3DFMT_D16: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL; + // just a guess on the legal usages + break; + + case D3DFMT_D24S8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL; + // just a guess on the legal usages + break; + + // vendor formats... try marking these all invalid for now + case D3DFMT_NV_INTZ: + case D3DFMT_NV_RAWZ: + case D3DFMT_NV_NULL: + case D3DFMT_ATI_D16: + case D3DFMT_ATI_D24S8: + case D3DFMT_ATI_2N: + case D3DFMT_ATI_1N: + legalUsage = 0; + break; + + //----------------------------------------------------------- + + default: + Assert(!"Unknown check format"); + result = D3DERR_NOTAVAILABLE; + break; + } + + if ((Usage & legalUsage) == Usage) + { + result = S_OK; + } + else + { + DWORD unsatBits = Usage & (~legalUsage); // clear the bits of the req that were legal, leaving the illegal ones + unsatBits; + GLMPRINTF(( "-X- --> NOT OK: flags %8x:%s", unsatBits,GLMDecodeMask( eD3D_USAGE, unsatBits ) )); + result = D3DERR_NOTAVAILABLE; + } + break; + + case D3DRTYPE_SURFACE: + switch( static_cast(CheckFormat) ) + { + case 0x434f5441: + case 0x41415353: + result = D3DERR_NOTAVAILABLE; + break; + + case D3DFMT_D24S8: + result = S_OK; + break; + //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=434f5441:UNKNOWN + //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=41415353:UNKNOWN + //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=434f5441:UNKNOWN + //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=41415353:UNKNOWN + } + break; + + default: + Assert(!"Unknown resource type"); + result = D3DERR_NOTAVAILABLE; + break; + } + break; + + default: + Assert(!"Unknown adapter format"); + result = D3DERR_NOTAVAILABLE; + break; + } + + return result; +} + +UINT IDirect3D9::GetAdapterModeCount(UINT Adapter,D3DFORMAT Format) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF(( "-X- IDirect3D9::GetAdapterModeCount: Adapter=%d || Format=%8x:%s", Adapter, Format, GLMDecode(eD3D_FORMAT, Format) )); + + uint modeCount=0; + + GLMDisplayDB *db = GetDisplayDB(); + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; + Assert (!result); + + modeCount = db->GetModeCount( glmRendererIndex, glmDisplayIndex ); + GLMPRINTF(( "-X- --> result is %d", modeCount )); + + return modeCount; +} + +HRESULT IDirect3D9::EnumAdapterModes(UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF(( "-X- IDirect3D9::EnumAdapterModes: Adapter=%d || Format=%8x:%s || Mode=%d", Adapter, Format, GLMDecode(eD3D_FORMAT, Format), Mode )); + + Assert(Format==D3DFMT_X8R8G8B8); + + GLMDisplayDB *db = GetDisplayDB(); + + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + GLMDisplayModeInfoFields glmModeInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); + Assert (!result); + if (result) return D3DERR_NOTAVAILABLE; + + bool result2 = db->GetModeInfo( glmRendererIndex, glmDisplayIndex, Mode, &glmModeInfo ); + Assert( !result2 ); + if (result2) return D3DERR_NOTAVAILABLE; + + pMode->Width = glmModeInfo.m_modePixelWidth; + pMode->Height = glmModeInfo.m_modePixelHeight; + pMode->RefreshRate = glmModeInfo.m_modeRefreshHz; // "adapter default" + pMode->Format = Format; // whatever you asked for ? + + GLMPRINTF(( "-X- IDirect3D9::EnumAdapterModes returning mode size (%d,%d) and D3DFMT_X8R8G8B8",pMode->Width,pMode->Height )); + return S_OK; +} + +HRESULT IDirect3D9::CheckDeviceType(UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) +{ + GL_BATCH_PERF_CALL_TIMER; + //FIXME: we just say "OK" on any query + + GLMPRINTF(( "-X- IDirect3D9::CheckDeviceType: Adapter=%d || DevType=%d:%s || AdapterFormat=%d:%s || BackBufferFormat=%d:%s || bWindowed=%d", + Adapter, + DevType, GLMDecode(eD3D_DEVTYPE,DevType), + AdapterFormat, GLMDecode(eD3D_FORMAT, AdapterFormat), + BackBufferFormat, GLMDecode(eD3D_FORMAT, BackBufferFormat), + (int) bWindowed )); + + return S_OK; +} + +HRESULT IDirect3D9::GetAdapterDisplayMode(UINT Adapter,D3DDISPLAYMODE* pMode) +{ + GL_BATCH_PERF_CALL_TIMER; + // asking what the current mode is + GLMPRINTF(("-X- IDirect3D9::GetAdapterDisplayMode: Adapter=%d", Adapter )); + + GLMDisplayDB *db = GetDisplayDB(); + + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + GLMDisplayModeInfoFields glmModeInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); + Assert(!result); + if (result) return D3DERR_INVALIDCALL; + + int modeIndex = -1; // pass -1 as a mode index to find out about whatever the current mode is on the selected display + + bool modeResult = db->GetModeInfo( glmRendererIndex, glmDisplayIndex, modeIndex, &glmModeInfo ); + Assert (!modeResult); + if (modeResult) return D3DERR_INVALIDCALL; + + pMode->Width = glmModeInfo.m_modePixelWidth; + pMode->Height = glmModeInfo.m_modePixelHeight; + pMode->RefreshRate = glmModeInfo.m_modeRefreshHz; // "adapter default" + pMode->Format = D3DFMT_X8R8G8B8; //FIXME, this is a SWAG + + return S_OK; +} + +HRESULT IDirect3D9::CheckDepthStencilMatch(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF(("-X- IDirect3D9::CheckDepthStencilMatch: Adapter=%d || DevType=%d:%s || AdapterFormat=%d:%s || RenderTargetFormat=%d:%s || DepthStencilFormat=%d:%s", + Adapter, + DeviceType, GLMDecode(eD3D_DEVTYPE,DeviceType), + AdapterFormat, GLMDecode(eD3D_FORMAT, AdapterFormat), + RenderTargetFormat, GLMDecode(eD3D_FORMAT, RenderTargetFormat), + DepthStencilFormat, GLMDecode(eD3D_FORMAT, DepthStencilFormat) )); + + // one known request looks like this: + // AdapterFormat=5:D3DFMT_X8R8G8B8 || RenderTargetFormat=3:D3DFMT_A8R8G8B8 || DepthStencilFormat=2:D3DFMT_D24S8 + + // return S_OK for that one combo, DXABSTRACT_BREAK_ON_ERROR() on anything else + HRESULT result = D3DERR_NOTAVAILABLE; // failure + + switch( AdapterFormat ) + { + case D3DFMT_X8R8G8B8: + { + if ( (RenderTargetFormat == D3DFMT_A8R8G8B8) && (DepthStencilFormat == D3DFMT_D24S8) ) + { + result = S_OK; + } + } + break; + } + + Assert( result == S_OK ); + + return result; +} + +HRESULT IDirect3D9::CheckDeviceMultiSampleType( UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels ) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMDisplayDB *db = GetDisplayDB(); + + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + //GLMDisplayModeInfoFields glmModeInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); + Assert( !result ); + if ( result ) + return D3DERR_INVALIDCALL; + + + if ( !CommandLine()->FindParm("-glmenabletrustmsaa") ) + { + // These ghetto drivers don't get MSAA + if ( ( glmRendererInfo.m_nvG7x || glmRendererInfo.m_atiR5xx ) && ( MultiSampleType > D3DMULTISAMPLE_NONE ) ) + { + if ( pQualityLevels ) + { + *pQualityLevels = 0; + } + return D3DERR_NOTAVAILABLE; + } + } + + switch ( MultiSampleType ) + { + case D3DMULTISAMPLE_NONE: // always return true + if ( pQualityLevels ) + { + *pQualityLevels = 1; + } + return S_OK; + break; + + case D3DMULTISAMPLE_2_SAMPLES: + case D3DMULTISAMPLE_4_SAMPLES: + case D3DMULTISAMPLE_6_SAMPLES: + case D3DMULTISAMPLE_8_SAMPLES: + // note the fact that the d3d enums for 2, 4, 6, 8 samples are equal to 2,4,6,8... + if (glmRendererInfo.m_maxSamples >= (int)MultiSampleType ) + { + if ( pQualityLevels ) + { + *pQualityLevels = 1; + } + return S_OK; + } + else + { + return D3DERR_NOTAVAILABLE; + } + break; + + default: + if ( pQualityLevels ) + { + *pQualityLevels = 0; + } + return D3DERR_NOTAVAILABLE; + break; + } + return D3DERR_NOTAVAILABLE; +} + +HRESULT IDirect3D9::CreateDevice(UINT Adapter,D3DDEVTYPE DeviceType,VD3DHWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) +{ + GL_BATCH_PERF_CALL_TIMER; + +#if GLMDEBUG + GLMDebugPrintf( "WARNING: GLMEBUG is 1, perf. is going to be low!" ); + Warning( "WARNING: GLMEBUG is 1, perf. is going to be low!" ); +#endif +#if !TOGL_SUPPORT_NULL_DEVICE + if (DeviceType == D3DDEVTYPE_NULLREF) + { + Error( "Must define TOGL_SUPPORT_NULL_DEVICE to use the NULL device" ); + DebuggerBreak(); + return E_FAIL; + } +#endif + + // constrain these inputs for the time being + // BackBufferFormat -> A8R8G8B8 + // BackBufferCount -> 1; + // MultiSampleType -> D3DMULTISAMPLE_NONE + // AutoDepthStencilFormat -> D3DFMT_D24S8 + + // NULL out the return pointer so if we exit early it is not set + *ppReturnedDeviceInterface = NULL; + + // assume success unless something is sour + HRESULT result = S_OK; + + // relax this check for now + //if (pPresentationParameters->BackBufferFormat != D3DFMT_A8R8G8B8) + //{ + // DXABSTRACT_BREAK_ON_ERROR(); + // result = -1; + //} + + //rbarris 24Aug10 - relaxing this check - we don't care if the game asks for two backbuffers, it's moot + //if ( pPresentationParameters->BackBufferCount != 1 ) + //{ + // DXABSTRACT_BREAK_ON_ERROR(); + // result = D3DERR_NOTAVAILABLE; + //} + + if ( pPresentationParameters->AutoDepthStencilFormat != D3DFMT_D24S8 ) + { + DXABSTRACT_BREAK_ON_ERROR(); + result = D3DERR_NOTAVAILABLE; + } + + if ( result == S_OK ) + { + // create an IDirect3DDevice9 + // it will make a GLMContext and set up some drawables + + IDirect3DDevice9Params devparams; + memset( &devparams, 0, sizeof(devparams) ); + + devparams.m_adapter = Adapter; + devparams.m_deviceType = DeviceType; + devparams.m_focusWindow = hFocusWindow; // is this meaningful? is this a WindowRef ? follow it up the chain.. + devparams.m_behaviorFlags = BehaviorFlags; + devparams.m_presentationParameters = *pPresentationParameters; + + IDirect3DDevice9 *dev = new IDirect3DDevice9; + + result = dev->Create( &devparams ); + + if ( result == S_OK ) + { + *ppReturnedDeviceInterface = dev; + } + + g_bNullD3DDevice = ( DeviceType == D3DDEVTYPE_NULLREF ); + } + return result; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DQuery9 + +#endif + +HRESULT IDirect3DQuery9::Issue(DWORD dwIssueFlags) +{ + GL_BATCH_PERF_CALL_TIMER; + Assert( m_device->m_nValidMarker == D3D_DEVICE_VALID_MARKER ); + // Flags field for Issue + // #define D3DISSUE_END (1 << 0) // Tells the runtime to issue the end of a query, changing it's state to "non-signaled". + // #define D3DISSUE_BEGIN (1 << 1) // Tells the runtime to issue the beginng of a query. + + // Make sure calling thread owns the GL context. + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + if (dwIssueFlags & D3DISSUE_BEGIN) + { + m_nIssueStartThreadID = ThreadGetCurrentId(); + m_nIssueStartDrawCallIndex = g_nTotalDrawsOrClears; + m_nIssueStartFrameIndex = m_ctx->m_nCurFrame; + m_nIssueStartQueryCreationCounter = CGLMQuery::s_nTotalOcclusionQueryCreatesOrDeletes; + + switch( m_type ) + { + case D3DQUERYTYPE_OCCLUSION: + m_query->Start(); // drop "start counter" call into stream + break; + + default: + Assert(!"Can't use D3DISSUE_BEGIN on this query"); + break; + } + } + + if (dwIssueFlags & D3DISSUE_END) + { + m_nIssueEndThreadID = ThreadGetCurrentId(); + m_nIssueEndDrawCallIndex = g_nTotalDrawsOrClears; + m_nIssueEndFrameIndex = m_ctx->m_nCurFrame; + m_nIssueEndQueryCreationCounter = CGLMQuery::s_nTotalOcclusionQueryCreatesOrDeletes; + + switch( m_type ) + { + case D3DQUERYTYPE_OCCLUSION: + m_query->Stop(); // drop "end counter" call into stream + break; + + case D3DQUERYTYPE_EVENT: + m_nIssueStartThreadID = m_nIssueEndThreadID; + m_nIssueStartDrawCallIndex = m_nIssueEndDrawCallIndex; + m_nIssueStartFrameIndex = m_nIssueEndFrameIndex; + m_nIssueStartQueryCreationCounter = m_nIssueEndQueryCreationCounter; + + // End is very weird with respect to Events (fences). + // DX9 docs say to use End to put the fence in the stream. So we map End to GLM's Start. + // http://msdn.microsoft.com/en-us/library/ee422167(VS.85).aspx + m_query->Start(); // drop "set fence" into stream + break; + } + } + return S_OK; +} + +HRESULT IDirect3DQuery9::GetData(void* pData,DWORD dwSize,DWORD dwGetDataFlags) +{ + GL_BATCH_PERF_CALL_TIMER; + Assert( m_device->m_nValidMarker == D3D_DEVICE_VALID_MARKER ); + HRESULT result = S_FALSE ; + DWORD nCurThreadId = ThreadGetCurrentId(); + + // Make sure calling thread owns the GL context. + Assert( m_ctx->m_nCurOwnerThreadId == nCurThreadId ); + if ( pData ) + { + *(uint*)pData = 0; + } + + if ( !m_query->IsStarted() || !m_query->IsStopped() ) + { + Assert(!"Can't GetData before issue/start/stop"); + printf("\n** IDirect3DQuery9::GetData: can't GetData before issue/start/stop"); + return S_FALSE; + } + + // GetData is not always called with the flush bit. + + // if an answer is not yet available - return S_FALSE. + // if an answer is available - return S_OK and write the answer into *pData. + bool done = false; + bool flush = (dwGetDataFlags & D3DGETDATA_FLUSH) != 0; // aka spin until done + + // hmmm both of these paths are the same, maybe we could fold them up + if ( m_type == D3DQUERYTYPE_OCCLUSION ) + { + // Detect cases that are actually just not supported with the way we're using GL queries. (For example, beginning a query, then creating/deleting any query, the ending the same query is not supported.) + // Also extra paranoid to detect/work around various NV/AMD driver issues. + if ( ( ( m_nIssueStartThreadID != nCurThreadId ) || ( m_nIssueEndThreadID != nCurThreadId ) ) || + ( m_nIssueStartDrawCallIndex == m_nIssueEndDrawCallIndex ) || ( m_nIssueStartFrameIndex != m_nIssueEndFrameIndex ) || + ( m_nIssueStartQueryCreationCounter != m_nIssueEndQueryCreationCounter ) ) + { + // The thread Issue() was called on differs from GetData() - NV's driver doesn't like this, not sure about AMD. Just fake the results if a flush is requested. + // There are various ways to properly handle this scenario, but in practice it only seems to occur in non-critical times (during shutdown or when mat_queue_mode is changed in L4D2). + if ( flush ) + { + gGL->glFlush(); + } + +#if 0 + if ( ( m_nIssueStartThreadID != nCurThreadId ) || ( m_nIssueEndThreadID != nCurThreadId ) ) + { + GLMDebugPrintf( "IDirect3DQuery9::GetData: GetData() called from different thread verses the issueing thread()!\n" ); + } +#endif + if ( m_nIssueStartQueryCreationCounter != m_nIssueEndQueryCreationCounter ) + { + GLMDebugPrintf( "IDirect3DQuery9::GetData: One or more queries have been created or released while this query was still issued! This scenario is not supported in GL.\n"); + } + + // Return with a non-standard error code, so the caller has a chance to do something halfway intelligent. + return D3DERR_NOTAVAILABLE; + } + } + + switch( m_type ) + { + case D3DQUERYTYPE_OCCLUSION: + { + + if ( flush ) + { + uint oqValue = 0; + CFastTimer tm; + tm.Start(); + + + // Is this flush actually necessary? According to the extension it's not. + // It doesn't seem to matter if this is a glFlush() or glFinish() with NVidia's driver (tested in MT mode - not sure if it matters), it still can take several calls to IsDone() before we can stop waiting for the query results. + // On AMD, this flush logic fails during shutdown (the query results never become available) - tried a bunch of experiments and checks with no luck. + + m_query->Complete(&oqValue); + + + double flTotalTime = tm.GetDurationInProgress().GetSeconds() * 1000.0f; + if ( flTotalTime > .5f ) + { + // Give up - something silly has obviously gone wrong in the driver, lying is better than stalling potentially forever. + // This occurs on AMD (single threaded driver) during shutdown, not sure why yet. It has nothing to do with threading. It may have to do with releasing queries or other objects. + // We must return a result otherwise the app itself could hang, waiting infinitely. + //Assert( 0 ); + Warning( "IDirect3DQuery9::GetData(): Occlusion query flush took %3.3fms!\n", flTotalTime ); + } + if (pData) + { + *(uint*)pData = oqValue; + } + result = S_OK; + } + else + { + done = m_query->IsDone(); + if (done) + { + uint oqValue = 0; // or we could just pass pData directly to Complete... + m_query->Complete(&oqValue); + if (pData) + { + *(uint*)pData = oqValue; + } + result = S_OK; + } + else + { + result = S_FALSE; + Assert( !flush ); + } + } + } + break; + + case D3DQUERYTYPE_EVENT: + { + done = m_query->IsDone(); + if ( ( done ) || ( flush ) ) + { + m_query->Complete(NULL); // this will block on pre-SLGU + + result = S_OK; + } + else + { + result = S_FALSE; + Assert( !flush ); + } + } + break; + } + + return result; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DVertexBuffer9 + +#endif + +HRESULT IDirect3DDevice9::CreateVertexBuffer(UINT Length,DWORD Usage,DWORD FVF,D3DPOOL Pool,IDirect3DVertexBuffer9** ppVertexBuffer,VD3DHANDLE* pSharedHandle) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF(( ">-A- IDirect3DDevice9::CreateVertexBuffer" )); + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + m_ObjectStats.m_nTotalVertexBuffers++; + + IDirect3DVertexBuffer9 *newbuff = new IDirect3DVertexBuffer9; + + newbuff->m_device = this; + + newbuff->m_ctx = m_ctx; + + // FIXME need to find home or use for the Usage, FVF, Pool values passed in + uint options = 0; + + if (Usage&D3DUSAGE_DYNAMIC) + { + options |= GLMBufferOptionDynamic; + } + + newbuff->m_vtxBuffer = m_ctx->NewBuffer( kGLMVertexBuffer, Length, options ) ; + + newbuff->m_vtxDesc.Type = D3DRTYPE_VERTEXBUFFER; + newbuff->m_vtxDesc.Usage = Usage; + newbuff->m_vtxDesc.Pool = Pool; + newbuff->m_vtxDesc.Size = Length; + + *ppVertexBuffer = newbuff; + + GLMPRINTF(( "<-A- IDirect3DDevice9::CreateVertexBuffer" )); + + return S_OK; +} + +IDirect3DVertexBuffer9::~IDirect3DVertexBuffer9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DVertexBuffer9" )); + + if (m_device) + { + m_device->ReleasedVertexBuffer( this ); + + if (m_ctx && m_vtxBuffer) + { + GLMPRINTF(( ">-A- ~IDirect3DVertexBuffer9 deleting m_vtxBuffer" )); + m_ctx->DelBuffer( m_vtxBuffer ); + m_vtxBuffer = NULL; + GLMPRINTF(( "<-A- ~IDirect3DVertexBuffer9 deleting m_vtxBuffer - done" )); + } + m_device = NULL; + } + + GLMPRINTF(( "<-A- ~IDirect3DVertexBuffer9" )); +} + +HRESULT IDirect3DVertexBuffer9::Lock(UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "VB Lock" ); + + // FIXME would be good to have "can't lock twice" logic + + Assert( !(Flags & D3DLOCK_READONLY) ); // not impl'd +// Assert( !(Flags & D3DLOCK_NOSYSLOCK) ); // not impl'd - it triggers though + + GLMBuffLockParams lockreq; + lockreq.m_nOffset = OffsetToLock; + lockreq.m_nSize = SizeToLock; + lockreq.m_bNoOverwrite = (Flags & D3DLOCK_NOOVERWRITE) != 0; + lockreq.m_bDiscard = (Flags & D3DLOCK_DISCARD) != 0; + + m_vtxBuffer->Lock( &lockreq, (char**)ppbData ); + + GLMPRINTF(("-X- IDirect3DDevice9::Lock on D3D buf %p (GL name %d) offset %d, size %d => address %p", this, this->m_vtxBuffer->m_nHandle, OffsetToLock, SizeToLock, *ppbData)); + return S_OK; +} + +HRESULT IDirect3DVertexBuffer9::Unlock() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "VB Unlock" ); + + m_vtxBuffer->Unlock(); + return S_OK; +} + +void IDirect3DVertexBuffer9::UnlockActualSize( uint nActualSize, const void *pActualData ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "VB UnlockActualSize" ); + + m_vtxBuffer->Unlock( nActualSize, pActualData ); +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + + +#ifdef OSX + +#pragma mark ----- IDirect3DIndexBuffer9 + +#endif + +HRESULT IDirect3DDevice9::CreateIndexBuffer(UINT Length,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DIndexBuffer9** ppIndexBuffer,VD3DHANDLE* pSharedHandle) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + GLMPRINTF(( ">-A- IDirect3DDevice9::CreateIndexBuffer" )); + + // it is important to save all the create info, since GetDesc could get called later to query it + + m_ObjectStats.m_nTotalIndexBuffers++; + + IDirect3DIndexBuffer9 *newbuff = new IDirect3DIndexBuffer9; + + newbuff->m_device = this; + + newbuff->m_restype = D3DRTYPE_INDEXBUFFER; // hmmmmmmm why are we not derived from d3dresource.. + + newbuff->m_ctx = m_ctx; + + // FIXME need to find home or use for the Usage, Format, Pool values passed in + uint options = 0; + + if (Usage&D3DUSAGE_DYNAMIC) + { + options |= GLMBufferOptionDynamic; + } + + newbuff->m_idxBuffer = m_ctx->NewBuffer( kGLMIndexBuffer, Length, options ) ; + + newbuff->m_idxDesc.Format = Format; + newbuff->m_idxDesc.Type = D3DRTYPE_INDEXBUFFER; + newbuff->m_idxDesc.Usage = Usage; + newbuff->m_idxDesc.Pool = Pool; + newbuff->m_idxDesc.Size = Length; + + *ppIndexBuffer = newbuff; + + GLMPRINTF(( "<-A- IDirect3DDevice9::CreateIndexBuffer" )); + + return S_OK; +} + +IDirect3DIndexBuffer9::~IDirect3DIndexBuffer9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DIndexBuffer9" )); + + if (m_device) + { + m_device->ReleasedIndexBuffer( this ); + + if (m_ctx && m_idxBuffer) + { + GLMPRINTF(( ">-A- ~IDirect3DIndexBuffer9 deleting m_idxBuffer" )); + m_ctx->DelBuffer( m_idxBuffer ); + GLMPRINTF(( "<-A- ~IDirect3DIndexBuffer9 deleting m_idxBuffer - done" )); + } + m_device = NULL; + } + else + { + } + + GLMPRINTF(( "<-A- ~IDirect3DIndexBuffer9" )); +} + + +HRESULT IDirect3DIndexBuffer9::Lock(UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + // FIXME would be good to have "can't lock twice" logic + + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "IB Lock" ); + + GLMBuffLockParams lockreq; + lockreq.m_nOffset = OffsetToLock; + lockreq.m_nSize = SizeToLock; + lockreq.m_bNoOverwrite = ( Flags & D3DLOCK_NOOVERWRITE ) != 0; + lockreq.m_bDiscard = ( Flags & D3DLOCK_DISCARD ) != 0; + + m_idxBuffer->Lock( &lockreq, (char**)ppbData ); + + return S_OK; +} + +HRESULT IDirect3DIndexBuffer9::Unlock() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "IB Unlock" ); + + m_idxBuffer->Unlock(); + + return S_OK; +} + +void IDirect3DIndexBuffer9::UnlockActualSize( uint nActualSize, const void *pActualData ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "IB UnlockActualSize" ); + + m_idxBuffer->Unlock( nActualSize, pActualData ); +} + +HRESULT IDirect3DIndexBuffer9::GetDesc(D3DINDEXBUFFER_DESC *pDesc) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + *pDesc = m_idxDesc; + return S_OK; +} + + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DDevice9 ------------------------------------------------- + +#endif + +void ConvertPresentationParamsToGLMDisplayParams( D3DPRESENT_PARAMETERS *d3dp, GLMDisplayParams *gldp ) +{ + memset( gldp, 0, sizeof(*gldp) ); + + gldp->m_fsEnable = !d3dp->Windowed; + + // see http://msdn.microsoft.com/en-us/library/ee416515(VS.85).aspx + // note that the values below are the only ones mentioned by Source engine; there are many others + switch(d3dp->PresentationInterval) + { + case D3DPRESENT_INTERVAL_ONE: + gldp->m_vsyncEnable = true; // "The driver will wait for the vertical retrace period (the runtime will beam-follow to prevent tearing)." + break; + + case D3DPRESENT_INTERVAL_IMMEDIATE: + gldp->m_vsyncEnable = false; // "The runtime updates the window client area immediately and might do so more than once during the adapter refresh period." + break; + + default: + gldp->m_vsyncEnable = true; // if I don't know it, you're getting vsync enabled. + break; + } + + gldp->m_backBufferWidth = d3dp->BackBufferWidth; + gldp->m_backBufferHeight = d3dp->BackBufferHeight; + gldp->m_backBufferFormat = d3dp->BackBufferFormat; + gldp->m_multiSampleCount = d3dp->MultiSampleType; // it's a count really + + gldp->m_enableAutoDepthStencil = d3dp->EnableAutoDepthStencil != 0; + gldp->m_autoDepthStencilFormat = d3dp->AutoDepthStencilFormat; + + gldp->m_fsRefreshHz = d3dp->FullScreen_RefreshRateInHz; + + // some fields in d3d PB we're not acting on yet... + // UINT BackBufferCount; + // DWORD MultiSampleQuality; + // D3DSWAPEFFECT SwapEffect; + // VD3DHWND hDeviceWindow; + // DWORD Flags; +} + +void UnpackD3DRSITable( void ); + +HRESULT IDirect3DDevice9::Create( IDirect3DDevice9Params *params ) +{ + g_pD3D_Device = this; + + GLMDebugPrintf( "IDirect3DDevice9::Create: BackBufWidth: %u, BackBufHeight: %u, D3DFMT: %u, BackBufCount: %u, MultisampleType: %u, MultisampleQuality: %u\n", + params->m_presentationParameters.BackBufferWidth, + params->m_presentationParameters.BackBufferHeight, + params->m_presentationParameters.BackBufferFormat, + params->m_presentationParameters.BackBufferCount, + params->m_presentationParameters.MultiSampleType, + params->m_presentationParameters.MultiSampleQuality ); + + UnpackD3DRSITable(); + + m_ObjectStats.clear(); + m_PrevObjectStats.clear(); + +#if GL_BATCH_PERF_ANALYSIS && GL_BATCH_PERF_ANALYSIS_WRITE_PNGS + m_pBatch_vis_bitmap = NULL; +#endif + + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF((">-X-IDirect3DDevice9::Create")); + HRESULT result = S_OK; + + // create an IDirect3DDevice9 + // make a GLMContext and set up some drawables + m_params = *params; + + m_ctx = NULL; + + V_memset( m_pRenderTargets, 0, sizeof( m_pRenderTargets ) ); + m_pDepthStencil = NULL; + + m_pDefaultColorSurface = NULL; + m_pDefaultDepthStencilSurface = NULL; + + memset( m_streams, 0, sizeof(m_streams) ); + memset( m_vtx_buffers, 0, sizeof( m_vtx_buffers ) ); + memset( m_textures, 0, sizeof(m_textures) ); + //memset( m_samplers, 0, sizeof(m_samplers) ); + + m_indices.m_idxBuffer = NULL; + + m_vertexShader = NULL; + m_pixelShader = NULL; + + m_pVertDecl = NULL; + + //============================================================================ + // param block for GLM context create + GLMDisplayParams glmParams; + ConvertPresentationParamsToGLMDisplayParams( ¶ms->m_presentationParameters, &glmParams ); + + glmParams.m_mtgl = true; // forget this idea -> (params->m_behaviorFlags & D3DCREATE_MULTITHREADED) != 0; + // the call above fills in a bunch of things, but doesn't know about anything outside of the presentation params. + // those tend to be the things that do not change after create, so we do those here in Create. + + glmParams.m_focusWindow = params->m_focusWindow; + + #if 0 //FIXME-HACK + // map the D3D "adapter" to a renderer/display pair + // (that GPU will have to stay set as-is for any subsequent mode changes) + + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool adaptResult = GLMgr::aGLMgr()->GetDisplayDB()->GetFakeAdapterInfo( params->m_adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); + Assert(!adaptResult); + + glmParams.m_rendererIndex = glmRendererIndex; + glmParams.m_displayIndex = glmDisplayIndex; + // glmParams.m_modeIndex hmmmmm, client doesn't give us a mode number, just a resolution.. + #endif + + m_ctx = GLMgr::aGLMgr()->NewContext( this, &glmParams ); + if (!m_ctx) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); + return (HRESULT) -1; + } + + // make an FBO to draw into and activate it. + m_ctx->m_drawingFBO = m_ctx->NewFBO(); + + // bind it to context. will receive attachments shortly. + m_ctx->BindFBOToCtx( m_ctx->m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + m_bFBODirty = false; + + m_pFBOs = new CGLMFBOMap(); + m_pFBOs->SetLessFunc( RenderTargetState_t::LessFunc ); + + // we create two IDirect3DSurface9's. These will be known as the internal render target 0 and the depthstencil. + + GLMPRINTF(("-X- IDirect3DDevice9::Create making color render target...")); + // color surface + result = this->CreateRenderTarget( + m_params.m_presentationParameters.BackBufferWidth, // width + m_params.m_presentationParameters.BackBufferHeight, // height + m_params.m_presentationParameters.BackBufferFormat, // format + m_params.m_presentationParameters.MultiSampleType, // MSAA depth + m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality + true, // lockable + &m_pDefaultColorSurface, // ppSurface + NULL, // shared handle + "InternalRT0" + ); + + if (result != S_OK) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); + return result; + } + // do not do an AddRef.. + + GLMPRINTF(("-X- IDirect3DDevice9::Create making color render target complete -> %08x", m_pDefaultColorSurface )); + + GLMPRINTF(("-X- IDirect3DDevice9::Create setting color render target...")); + result = this->SetRenderTarget(0, m_pDefaultColorSurface); + if (result != S_OK) + { + GLMPRINTF(("< IDirect3DDevice9::Create (error out)")); + return result; + } + GLMPRINTF(("-X- IDirect3DDevice9::Create setting color render target complete.")); + + Assert (m_params.m_presentationParameters.EnableAutoDepthStencil); + + GLMPRINTF(("-X- IDirect3DDevice9::Create making depth-stencil...")); + result = CreateDepthStencilSurface( + m_params.m_presentationParameters.BackBufferWidth, // width + m_params.m_presentationParameters.BackBufferHeight, // height + m_params.m_presentationParameters.AutoDepthStencilFormat, // format + m_params.m_presentationParameters.MultiSampleType, // MSAA depth + m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality + TRUE, // enable z-buffer discard ???? + &m_pDefaultDepthStencilSurface, // ppSurface + NULL // shared handle + ); + if (result != S_OK) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); + return result; + } + // do not do an AddRef here.. + + GLMPRINTF(("-X- IDirect3DDevice9::Create making depth-stencil complete -> %08x", m_pDefaultDepthStencilSurface)); + GLMPRINTF(("-X- Direct3DDevice9::Create setting depth-stencil render target...")); + result = this->SetDepthStencilSurface(m_pDefaultDepthStencilSurface); + if (result != S_OK) + { + DXABSTRACT_BREAK_ON_ERROR(); + GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); + return result; + } + GLMPRINTF(("-X- IDirect3DDevice9::Create setting depth-stencil render target complete.")); + + UpdateBoundFBO(); + + bool ready = m_ctx->m_drawingFBO->IsReady(); + if (!ready) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); + return (HRESULT)-1; + } + + // this next part really needs to be inside GLMContext.. or replaced with D3D style viewport setup calls. + m_ctx->GenDebugFontTex(); + + // blast the gl state mirror... + memset( &this->gl, 0, sizeof( this->gl ) ); + + InitStates(); + + GLScissorEnable_t defScissorEnable = { true }; + GLScissorBox_t defScissorBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight }; + GLViewportBox_t defViewportBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight, m_params.m_presentationParameters.BackBufferWidth | ( m_params.m_presentationParameters.BackBufferHeight << 16 ) }; + GLViewportDepthRange_t defViewportDepthRange = { 0.1, 1000.0 }; + GLCullFaceEnable_t defCullFaceEnable = { true }; + GLCullFrontFace_t defCullFrontFace = { GL_CCW }; + + gl.m_ScissorEnable = defScissorEnable; + gl.m_ScissorBox = defScissorBox; + gl.m_ViewportBox = defViewportBox; + gl.m_ViewportDepthRange = defViewportDepthRange; + gl.m_CullFaceEnable = defCullFaceEnable; + gl.m_CullFrontFace = defCullFrontFace; + + FullFlushStates(); + + GLMPRINTF(("<-X- IDirect3DDevice9::Create complete")); + + // so GetClientRect can return sane answers + //uint width, height; + RenderedSize( m_params.m_presentationParameters.BackBufferWidth, m_params.m_presentationParameters.BackBufferHeight, true ); // true = set + +#if GL_TELEMETRY_GPU_ZONES + g_TelemetryGPUStats.Clear(); +#endif + + GL_BATCH_PERF( + g_nTotalD3DCalls = 0, g_nTotalD3DCycles = 0, m_nBatchVisY = 0, m_nBatchVisFrameIndex = 0, m_nBatchVisFileIdx = 0, m_nNumProgramChanges = 0, m_flTotalD3DTime = 0, m_nTotalD3DCalls = 0, + m_flTotalD3DTime = 0, m_nTotalGLCalls = 0, m_flTotalGLTime = 0, m_nOverallDraws = 0, m_nOverallPrims = 0, m_nOverallD3DCalls = 0, m_flOverallD3DTime = 0, m_nOverallGLCalls = 0, m_flOverallGLTime = 0, m_nOverallProgramChanges = 0, + m_flOverallPresentTime = 0, m_flOverallPresentTimeSquared = 0, m_nOverallPresents = 0, m_flOverallSwapWindowTime = 0, m_flOverallSwapWindowTimeSquared = 0, m_nTotalPrims = 0; ); + + g_nTotalDrawsOrClears = 0; + + gGL->m_nTotalGLCycles = 0; + gGL->m_nTotalGLCalls = 0; + + m_pDummy_vtx_buffer = new CGLMBuffer( m_ctx, kGLMVertexBuffer, 4096, 0 ); + m_vtx_buffers[0] = m_pDummy_vtx_buffer; + m_vtx_buffers[1] = m_pDummy_vtx_buffer; + m_vtx_buffers[2] = m_pDummy_vtx_buffer; + m_vtx_buffers[3] = m_pDummy_vtx_buffer; + + return result; +} + +IDirect3DDevice9::IDirect3DDevice9() : + m_nValidMarker( D3D_DEVICE_VALID_MARKER ) +{ +} +IDirect3DDevice9::~IDirect3DDevice9() +{ + Assert( m_nValidMarker == D3D_DEVICE_VALID_MARKER ); +#if GL_BATCH_PERF_ANALYSIS && GL_BATCH_PERF_ANALYSIS_WRITE_PNGS + delete m_pBatch_vis_bitmap; +#endif + + delete m_pDummy_vtx_buffer; + for ( int i = 0; i < 4; i++ ) + SetRenderTarget( i, NULL ); + SetDepthStencilSurface( NULL ); + if ( m_pDefaultColorSurface ) + { + m_pDefaultColorSurface->Release( 0, "IDirect3DDevice9::~IDirect3DDevice9 release color surface" ); + m_pDefaultColorSurface = NULL; + } + if ( m_pDefaultDepthStencilSurface ) + { + m_pDefaultDepthStencilSurface->Release( 0, "IDirect3DDevice9::~IDirect3DDevice9 release depth surface" ); + m_pDefaultDepthStencilSurface = NULL; + } + + if ( m_pFBOs ) + { + ResetFBOMap(); + } + + GLMPRINTF(( "-D- IDirect3DDevice9::~IDirect3DDevice9 signpost" )); // want to know when this is called, if ever + + g_pD3D_Device = NULL; + if ( m_ObjectStats.m_nTotalFBOs ) GLMDebugPrintf( "Leaking %i FBOs\n", m_ObjectStats.m_nTotalFBOs ); + if ( m_ObjectStats.m_nTotalVertexShaders ) ConMsg( "Leaking %i vertex shaders\n", m_ObjectStats.m_nTotalVertexShaders ); + if ( m_ObjectStats.m_nTotalPixelShaders ) ConMsg( "Leaking %i pixel shaders\n", m_ObjectStats.m_nTotalPixelShaders ); + if ( m_ObjectStats.m_nTotalVertexDecls ) ConMsg( "Leaking %i vertex decls\n", m_ObjectStats.m_nTotalVertexDecls ); + if ( m_ObjectStats.m_nTotalIndexBuffers ) ConMsg( "Leaking %i index buffers\n", m_ObjectStats.m_nTotalIndexBuffers ); + if ( m_ObjectStats.m_nTotalVertexBuffers ) ConMsg( "Leaking %i vertex buffers\n", m_ObjectStats.m_nTotalVertexBuffers ); + if ( m_ObjectStats.m_nTotalTextures ) ConMsg( "Leaking %i textures\n", m_ObjectStats.m_nTotalTextures ); + if ( m_ObjectStats.m_nTotalSurfaces ) ConMsg( "Leaking %i surfaces\n", m_ObjectStats.m_nTotalSurfaces ); + if ( m_ObjectStats.m_nTotalQueries ) ConMsg( "Leaking %i queries\n", m_ObjectStats.m_nTotalQueries ); + if ( m_ObjectStats.m_nTotalRenderTargets ) ConMsg( "Leaking %i render targets\n", m_ObjectStats.m_nTotalRenderTargets ); + GLMgr::aGLMgr()->DelContext( m_ctx ); + m_ctx = NULL; + m_nValidMarker = 0xDEADBEEF; +} + +#ifdef OSX + +#pragma mark ----- Basics - (IDirect3DDevice9) + +#endif + + +HRESULT IDirect3DDevice9::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + Assert( m_nValidMarker == D3D_DEVICE_VALID_MARKER ); + HRESULT result = S_OK; + + // define the task of reset as: + // provide new drawable RT's for the backbuffer (color and depthstencil). + // fix up viewport / scissor.. + // then pass the new presentation parameters through to GLM. + // (it will in turn notify appframework on the next present... which may be very soon, as mode changes are usually spotted inside Present() ). + + // so some of this looks a lot like Create - we're just a subset of what it does. + // with a little work you could refactor this to be common code. + + GLMDebugPrintf( "IDirect3DDevice9::Reset: BackBufWidth: %u, BackBufHeight: %u, D3DFMT: %u, BackBufCount: %u, MultisampleType: %u, MultisampleQuality: %u\n", + pPresentationParameters->BackBufferWidth, + pPresentationParameters->BackBufferHeight, + pPresentationParameters->BackBufferFormat, + pPresentationParameters->BackBufferCount, + pPresentationParameters->MultiSampleType, + pPresentationParameters->MultiSampleQuality ); + + //------------------------------------------------------------------------------- absorb new presentation params.. + + m_params.m_presentationParameters = *pPresentationParameters; + + //------------------------------------------------------------------------------- color buffer.. + // release old color surface if it's there.. + if ( m_pDefaultColorSurface ) + { + ULONG refc = m_pDefaultColorSurface->Release( 0, "IDirect3DDevice9::Reset public release color surface" ); (void)refc; + Assert( !refc ); + m_pDefaultColorSurface = NULL; + } + + GLMPRINTF(("-X- IDirect3DDevice9::Reset making new color render target...")); + + // color surface + result = this->CreateRenderTarget( + m_params.m_presentationParameters.BackBufferWidth, // width + m_params.m_presentationParameters.BackBufferHeight, // height + m_params.m_presentationParameters.BackBufferFormat, // format + m_params.m_presentationParameters.MultiSampleType, // MSAA depth + m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality + true, // lockable + &m_pDefaultColorSurface, // ppSurface + NULL // shared handle + ); + + if ( result != S_OK ) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); + return result; + } + // do not do an AddRef here.. + + GLMPRINTF(("-X- IDirect3DDevice9::Reset making color render target complete -> %08x", m_pDefaultColorSurface )); + + GLMPRINTF(("-X- IDirect3DDevice9::Reset setting color render target...")); + + result = this->SetDepthStencilSurface( NULL ); + + result = this->SetRenderTarget( 0, m_pDefaultColorSurface ); + if (result != S_OK) + { + GLMPRINTF(("< IDirect3DDevice9::Reset (error out)")); + return result; + } + GLMPRINTF(("-X- IDirect3DDevice9::Reset setting color render target complete.")); + + + //-------------------------------------------------------------------------------depth stencil buffer + // release old depthstencil surface if it's there.. + if ( m_pDefaultDepthStencilSurface ) + { + ULONG refc = m_pDefaultDepthStencilSurface->Release( 0, "IDirect3DDevice9::Reset public release depthstencil surface" ); (void)refc; + Assert(!refc); + m_pDefaultDepthStencilSurface = NULL; + } + + Assert (m_params.m_presentationParameters.EnableAutoDepthStencil); + + GLMPRINTF(("-X- IDirect3DDevice9::Reset making depth-stencil...")); + result = CreateDepthStencilSurface( + m_params.m_presentationParameters.BackBufferWidth, // width + m_params.m_presentationParameters.BackBufferHeight, // height + m_params.m_presentationParameters.AutoDepthStencilFormat, // format + m_params.m_presentationParameters.MultiSampleType, // MSAA depth + m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality + TRUE, // enable z-buffer discard ???? + &m_pDefaultDepthStencilSurface, // ppSurface + NULL // shared handle + ); + if (result != S_OK) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); + return result; + } + // do not do an AddRef here.. + + GLMPRINTF(("-X- IDirect3DDevice9::Reset making depth-stencil complete -> %08x", m_pDefaultDepthStencilSurface)); + + GLMPRINTF(("-X- IDirect3DDevice9::Reset setting depth-stencil render target...")); + result = this->SetDepthStencilSurface(m_pDefaultDepthStencilSurface); + if (result != S_OK) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); + return result; + } + GLMPRINTF(("-X- IDirect3DDevice9::Reset setting depth-stencil render target complete.")); + + UpdateBoundFBO(); + + bool ready = m_ctx->m_drawingFBO->IsReady(); + if (!ready) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); + return D3DERR_DEVICELOST; + } + + //-------------------------------------------------------------------------------zap viewport and scissor to new backbuffer size + + InitStates(); + + GLScissorEnable_t defScissorEnable = { true }; + GLScissorBox_t defScissorBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight }; + GLViewportBox_t defViewportBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight, m_params.m_presentationParameters.BackBufferWidth | ( m_params.m_presentationParameters.BackBufferHeight << 16 ) }; + GLViewportDepthRange_t defViewportDepthRange = { 0.1, 1000.0 }; + GLCullFaceEnable_t defCullFaceEnable = { true }; + GLCullFrontFace_t defCullFrontFace = { GL_CCW }; + + gl.m_ScissorEnable = defScissorEnable; + gl.m_ScissorBox = defScissorBox; + gl.m_ViewportBox = defViewportBox; + gl.m_ViewportDepthRange = defViewportDepthRange; + gl.m_CullFaceEnable = defCullFaceEnable; + gl.m_CullFrontFace = defCullFrontFace; + + FullFlushStates(); + + //-------------------------------------------------------------------------------finally, propagate new display params to GLM context + GLMDisplayParams glmParams; + ConvertPresentationParamsToGLMDisplayParams( pPresentationParameters, &glmParams ); + + // steal back previously sent focus window... + glmParams.m_focusWindow = m_ctx->m_displayParams.m_focusWindow; + Assert( glmParams.m_focusWindow != NULL ); + + // so GetClientRect can return sane answers + //uint width, height; + RenderedSize( pPresentationParameters->BackBufferWidth, pPresentationParameters->BackBufferHeight, true ); // true = set + + m_ctx->Reset(); + + m_ctx->SetDisplayParams( &glmParams ); + + return S_OK; +} + +HRESULT IDirect3DDevice9::SetViewport(CONST D3DVIEWPORT9* pViewport) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + GLMPRINTF(("-X- IDirect3DDevice9::SetViewport : minZ %f, maxZ %f",pViewport->MinZ, pViewport->MaxZ )); + + gl.m_ViewportBox.x = pViewport->X; + gl.m_ViewportBox.width = pViewport->Width; + + gl.m_ViewportBox.y = pViewport->Y; + gl.m_ViewportBox.height = pViewport->Height; + + gl.m_ViewportBox.widthheight = pViewport->Width | ( pViewport->Height << 16 ); + + m_ctx->WriteViewportBox( &gl.m_ViewportBox ); + + gl.m_ViewportDepthRange.flNear = pViewport->MinZ; + gl.m_ViewportDepthRange.flFar = pViewport->MaxZ; + m_ctx->WriteViewportDepthRange( &gl.m_ViewportDepthRange ); + + return S_OK; +} + +HRESULT IDirect3DDevice9::GetViewport( D3DVIEWPORT9* pViewport ) +{ + // 7LS - unfinished, used in scaleformuirenderimpl.cpp (only width and height required) + GL_BATCH_PERF_CALL_TIMER; + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + GLMPRINTF(("-X- IDirect3DDevice9::GetViewport " )); + + pViewport->X = gl.m_ViewportBox.x; + pViewport->Width = gl.m_ViewportBox.width; + + pViewport->Y = gl.m_ViewportBox.y; + pViewport->Height = gl.m_ViewportBox.height; + + pViewport->MinZ = gl.m_ViewportDepthRange.flNear; + pViewport->MaxZ = gl.m_ViewportDepthRange.flFar; + + return S_OK; +} + +HRESULT IDirect3DDevice9::BeginScene() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + m_ctx->BeginFrame(); + + return S_OK; +} + +HRESULT IDirect3DDevice9::EndScene() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + m_ctx->EndFrame(); + return S_OK; +} + + +// stolen from glmgrbasics.cpp + +enum ECarbonModKeyIndex +{ + EcmdKeyBit = 8, /* command key down?*/ + EshiftKeyBit = 9, /* shift key down?*/ + EalphaLockBit = 10, /* alpha lock down?*/ + EoptionKeyBit = 11, /* option key down?*/ + EcontrolKeyBit = 12 /* control key down?*/ +}; + +enum ECarbonModKeyMask +{ + EcmdKey = 1 << EcmdKeyBit, + EshiftKey = 1 << EshiftKeyBit, + EalphaLock = 1 << EalphaLockBit, + EoptionKey = 1 << EoptionKeyBit, + EcontrolKey = 1 << EcontrolKeyBit +}; + +void IDirect3DDevice9::PrintObjectStats( const ObjectStats_t &stats ) +{ + ConMsg( "Total FBOs: %i\n", stats.m_nTotalFBOs ); + ConMsg( "Total vertex shaders: %i\n", stats.m_nTotalVertexShaders ); + ConMsg( "Total pixel shaders: %i\n", stats.m_nTotalPixelShaders ); + ConMsg( "Total vertex decls: %i\n", stats.m_nTotalVertexDecls ); + ConMsg( "Total index buffers: %i\n", stats.m_nTotalIndexBuffers ); + ConMsg( "Total vertex buffers: %i\n", stats.m_nTotalVertexBuffers ); + ConMsg( "Total textures: %i\n", stats.m_nTotalTextures ); + ConMsg( "Total surfaces: %i\n", stats.m_nTotalSurfaces ); + ConMsg( "Total queries: %i\n", stats.m_nTotalQueries ); + ConMsg( "Total render targets: %i\n", stats.m_nTotalRenderTargets ); +} + +void IDirect3DDevice9::DumpStatsToConsole( const CCommand *pArgs ) +{ +#if GL_BATCH_PERF_ANALYSIS + ConMsg( "Overall: Batches: %u, Prims: %u, Program Changes: %u\n", m_nOverallDraws, m_nOverallPrims, m_nOverallProgramChanges ); + ConMsg( "Overall: D3D Calls: %u D3D Time: %4.3fms, Avg D3D Time Per Call: %4.9fms\n", m_nOverallD3DCalls, m_flOverallD3DTime, m_nOverallD3DCalls ? ( m_flOverallD3DTime / m_nOverallD3DCalls ) : 0.0f ); + ConMsg( "Overall: GL Calls: %u GL Time: %4.3fms, Avg GL Time Per Call: %4.9fms\n", m_nOverallGLCalls, m_flOverallGLTime, m_nOverallGLCalls ? ( m_flOverallGLTime / m_nOverallGLCalls ) : 0.0f ); + + ConMsg( "D3DPresent: %u, Overall Time: %4.3fms, Avg: %4.6fms, Std Dev: %4.6fms\n", + m_nOverallPresents, + m_flOverallPresentTime, m_nOverallPresents ? ( m_flOverallPresentTime / m_nOverallPresents ) : 0.0f, + m_nOverallPresents ? ( sqrt( ( m_flOverallPresentTimeSquared / m_nOverallPresents ) - ( m_flOverallPresentTime / m_nOverallPresents ) * ( m_flOverallPresentTime / m_nOverallPresents ) ) ) : 0.0f ); + + ConMsg( "GL SwapWindow(): Overall Time: %4.3fms, Avg: %4.6fms, Std Dev: %4.6fms\n", + m_flOverallSwapWindowTime, m_nOverallPresents ? ( m_flOverallSwapWindowTime / m_nOverallPresents ) : 0.0f, + m_nOverallPresents ? ( sqrt( ( m_flOverallSwapWindowTimeSquared / m_nOverallPresents ) - ( m_flOverallSwapWindowTime / m_nOverallPresents ) * ( m_flOverallSwapWindowTime / m_nOverallPresents ) ) ) : 0.0f ); + + if ( ( pArgs ) && ( pArgs->ArgC() == 2 ) && (pArgs->Arg(1)[0] != '0') ) + { + m_nOverallDraws = 0; + m_nOverallPrims = 0; + m_nOverallProgramChanges = 0; + m_nOverallD3DCalls = 0; + m_flOverallD3DTime = 0; + m_nOverallGLCalls = 0; + m_flOverallGLTime = 0; + m_flOverallPresentTime = 0; + m_flOverallPresentTimeSquared = 0; + m_flOverallSwapWindowTime = 0; + m_flOverallSwapWindowTimeSquared = 0; + m_nOverallPresents = 0; + } +#endif + ConMsg( "Totals:\n" ); + m_ObjectStats.m_nTotalFBOs = m_pFBOs->Count(); + PrintObjectStats( m_ObjectStats ); + ObjectStats_t delta( m_ObjectStats ); + delta -= m_PrevObjectStats; + ConMsg( "Delta:\n" ); + PrintObjectStats( delta ); + m_PrevObjectStats = m_ObjectStats; +} + +static void gl_dump_stats_func( const CCommand &args ) +{ + if ( g_pD3D_Device ) + { + g_pD3D_Device->DumpStatsToConsole( &args ); + } +} + +static ConCommand gl_dump_stats( "gl_dump_stats", gl_dump_stats_func ); +#if GLMDEBUG +void IDirect3DDevice9::DumpTextures( const CCommand *pArgs ) +{ + Assert( m_nValidMarker == D3D_DEVICE_VALID_MARKER ); + (void)pArgs; + CGLMTex *pCurTex = g_pFirstCGMLTex; + if ( pCurTex ) + { + Assert( pCurTex->m_pPrevTex == NULL ); + } + ConMsg( "--- Internal CGLMTex's:\n" ); + uint nNumFound = 0; + while ( pCurTex ) + { + nNumFound++; + ConMsg( "Tex \"%s\", Layout: \"%s\", Size: %u, RT: %u, Depth: %u, Stencil: %u, MSAA: %u\n", + pCurTex->m_debugLabel ? pCurTex->m_debugLabel : "?", + pCurTex->m_layout->m_layoutSummary, + pCurTex->m_layout->m_storageTotalSize, + ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexRenderable ) ? 1 : 0, + ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexIsDepth ) ? 1 : 0, + ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexIsStencil ) ? 1 : 0, + ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexMultisampled ) ? 1 : 0 ); + CGLMTex *pNextTex = pCurTex->m_pNextTex; + if ( pNextTex ) + { + Assert( pNextTex->m_pPrevTex == pCurTex ); + } + pCurTex = pNextTex; + } + ConMsg( "--- Found %u total CGLMTex's\n", nNumFound ); +} +static void gl_dump_textures_func( const CCommand &args ) +{ + if ( g_pD3D_Device ) + { + g_pD3D_Device->DumpTextures( &args ); + } +} +static ConCommand gl_dump_textures( "gl_dump_textures", gl_dump_textures_func ); + +#endif + +ConVar gl_blitmode( "gl_blitmode", "1" ); +ConVar dxa_nullrefresh_capslock( "dxa_nullrefresh_capslock", "0" ); + +HRESULT IDirect3DDevice9::Present(CONST RECT* pSourceRect,CONST RECT* pDestRect,VD3DHWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) +{ + GL_BATCH_PERF( g_nTotalD3DCalls++; ) + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + TOGL_NULL_DEVICE_CHECK; + + if ( m_bFBODirty ) + { + UpdateBoundFBO(); + } + + // before attempting to present a tex, make sure it's been resolved if it was MSAA. + // if we push that responsibility down to m_ctx->Present, it could probably do it without an extra copy. + // i.e. anticipate the blit from the resolvedtex to GL_BACK, and just do that instead. + + // no explicit ResolveTex call first - that got pushed down into GLMContext::Present + +#if GL_BATCH_PERF_ANALYSIS + uint64 nStartGLCycles = gGL->m_nTotalGLCycles; + nStartGLCycles; + + CFastTimer tm; + tm.Start(); +#endif + + m_ctx->Present( m_pDefaultColorSurface->m_tex ); + +#if GL_BATCH_PERF_ANALYSIS + double flPresentTime = tm.GetDurationInProgress().GetMillisecondsF(); + double flGLSwapWindowTime = g_pLauncherMgr->GetPrevGLSwapWindowTime(); + + m_flOverallPresentTime += flPresentTime; + m_flOverallPresentTimeSquared += flPresentTime * flPresentTime; + m_flOverallSwapWindowTime += flGLSwapWindowTime; + m_flOverallSwapWindowTimeSquared += flGLSwapWindowTime * flGLSwapWindowTime; + m_nOverallPresents++; + + uint64 nEndGLCycles = gGL->m_nTotalGLCycles; + nEndGLCycles; + + m_flTotalD3DTime += flPresentTime + g_nTotalD3DCycles * s_rdtsc_to_ms; + m_nTotalD3DCalls += g_nTotalD3DCalls; + + m_flTotalGLTime += gGL->m_nTotalGLCycles * s_rdtsc_to_ms; + m_nTotalGLCalls += gGL->m_nTotalGLCalls; + + m_nOverallProgramChanges += m_nNumProgramChanges; + m_nOverallDraws += g_nTotalDrawsOrClears; + m_nOverallPrims += m_nTotalPrims; + m_nOverallD3DCalls += m_nTotalD3DCalls; + m_flOverallD3DTime += m_flTotalD3DTime; + m_nOverallGLCalls += m_nTotalGLCalls; + m_flOverallGLTime += m_flTotalGLTime; + + static int nPrevBatchVis = -1; + +#if GL_BATCH_PERF_ANALYSIS_WRITE_PNGS + if ((nPrevBatchVis == 1) && m_pBatch_vis_bitmap && m_pBatch_vis_bitmap->is_valid()) + { + double flTotalGLPresentTime = ( nEndGLCycles - nStartGLCycles ) * s_rdtsc_to_ms; + + m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, (uint)(.5f + flPresentTime / gl_present_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), 10, 255, 16, 128); + m_pBatch_vis_bitmap->additive_fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalGLPresentTime / gl_present_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), 10, 0, 255, 128); + m_nBatchVisY += 10; + + uint y = MAX(m_nBatchVisY + 20, 600), l = 0; + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "OpenGL Frame: %u, Batches+Clears: %u, Prims: %u, Program Changes: %u", m_nOverallPresents, g_nTotalDrawsOrClears, m_nTotalPrims, m_nNumProgramChanges ); + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Frame: D3D Calls: %u, D3D Time: %3.3fms", m_nTotalD3DCalls, m_flTotalD3DTime); + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Frame: GL Calls: %u, GL Time: %3.3fms", m_nTotalGLCalls, m_flTotalGLTime); + l++; + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: Batches: %u, Prims: %u, Program Changes: %u", m_nOverallDraws, m_nOverallPrims, m_nOverallProgramChanges ); + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: D3D Calls: %u D3D Time: %4.3fms", m_nOverallD3DCalls, m_flOverallD3DTime ); + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: GL Calls: %u GL Time: %4.3fms", m_nOverallGLCalls, m_flOverallGLTime ); + + size_t png_size = 0; + void *pPNG_data = tdefl_write_image_to_png_file_in_memory(m_pBatch_vis_bitmap->get_ptr(), m_pBatch_vis_bitmap->width(), m_pBatch_vis_bitmap->height(), 3, &png_size, true); + if (pPNG_data) + { + char filename[256]; + V_snprintf(filename, sizeof(filename), "left4dead2/batchvis_%u_%u.png", m_nBatchVisFileIdx, m_nBatchVisFrameIndex); + FILE* pFile = fopen(filename, "wb"); + if (pFile) + { + fwrite(pPNG_data, png_size, 1, pFile); + fclose(pFile); + } + free(pPNG_data); + } + m_nBatchVisFrameIndex++; + m_nBatchVisY = 0; + m_pBatch_vis_bitmap->cls(); + } +#endif + + if (nPrevBatchVis != (int)gl_batch_vis.GetBool()) + { + if ( !m_pBatch_vis_bitmap ) + m_pBatch_vis_bitmap = new simple_bitmap; + + nPrevBatchVis = gl_batch_vis.GetBool(); + if (!nPrevBatchVis) + { + DumpStatsToConsole( NULL ); + m_pBatch_vis_bitmap->clear(); + } + else + { + m_pBatch_vis_bitmap->init(768, 1024); + } + m_nBatchVisY = 0; + m_nBatchVisFrameIndex = 0; + m_nBatchVisFileIdx = (uint)time(NULL); //rand(); + + m_nOverallProgramChanges = 0; + m_nOverallDraws = 0; + m_nOverallD3DCalls = 0; + m_flOverallD3DTime = 0; + m_nOverallGLCalls = 0; + m_flOverallGLTime = 0; + m_flOverallPresentTime = 0; + m_flOverallPresentTimeSquared = 0; + m_flOverallSwapWindowTime = 0; + m_flOverallSwapWindowTimeSquared = 0; + m_nOverallPresents = 0; + } + + g_nTotalD3DCycles = 0; + g_nTotalD3DCalls = 0; + gGL->m_nTotalGLCycles = 0; + gGL->m_nTotalGLCalls = 0; + + m_nNumProgramChanges = 0; + m_flTotalD3DTime = 0; + m_nTotalD3DCalls = 0; + m_flTotalGLTime = 0; + m_nTotalGLCalls = 0; + m_nTotalPrims = 0; +#else + if ( gl_batch_vis.GetBool() ) + { + gl_batch_vis.SetValue( false ); + + ConMsg( "Must define GL_BATCH_PERF_ANALYSIS to use this feature" ); + } +#endif + + g_nTotalDrawsOrClears = 0; + +#if GL_TELEMETRY_GPU_ZONES + g_TelemetryGPUStats.Clear(); +#endif + + return S_OK; +} + +#ifdef OSX + +#pragma mark ----- Textures - (IDirect3DDevice9) +#pragma mark ( create functions for each texture are now adjacent to the rest of the methods for each texture class) + +#endif + +HRESULT IDirect3DDevice9::GetTexture(DWORD Stage,IDirect3DBaseTexture9** ppTexture) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // if implemented, should it increase the ref count ?? + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + + +#ifdef OSX + +#pragma mark ----- RTs and Surfaces - (IDirect3DDevice9) + +#endif + +HRESULT IDirect3DDevice9::CreateRenderTarget(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultisampleQuality,BOOL Lockable,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle, char *pDebugLabel) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + HRESULT result = S_OK; + + m_ObjectStats.m_nTotalSurfaces++; + m_ObjectStats.m_nTotalRenderTargets++; + + IDirect3DSurface9 *surf = new IDirect3DSurface9; + surf->m_restype = D3DRTYPE_SURFACE; + + surf->m_device = this; // always set device on creations! + + GLMTexLayoutKey rtkey; + memset( &rtkey, 0, sizeof(rtkey) ); + + rtkey.m_texGLTarget = GL_TEXTURE_2D; + rtkey.m_xSize = Width; + rtkey.m_ySize = Height; + rtkey.m_zSize = 1; + + rtkey.m_texFormat = Format; + rtkey.m_texFlags = kGLMTexRenderable; + + rtkey.m_texFlags |= kGLMTexSRGB; // all render target tex are SRGB mode + if (m_ctx->Caps().m_cantAttachSRGB) + { + // this config can't support SRGB render targets. quietly turn off the sRGB bit. + rtkey.m_texFlags &= ~kGLMTexSRGB; + } + + if ( (MultiSample !=0) && (!m_ctx->Caps().m_nvG7x) ) + { + rtkey.m_texFlags |= kGLMTexMultisampled; + rtkey.m_texSamples = MultiSample; + // FIXME no support for "MS quality" yet + } + + surf->m_tex = m_ctx->NewTex( &rtkey, 1, pDebugLabel ); + surf->m_face = 0; + surf->m_mip = 0; + + //desc + surf->m_desc.Format = Format; + surf->m_desc.Type = D3DRTYPE_SURFACE; + surf->m_desc.Usage = 0; //FIXME ??????????? + surf->m_desc.Pool = D3DPOOL_DEFAULT; //FIXME ??????????? + surf->m_desc.MultiSampleType = MultiSample; + surf->m_desc.MultiSampleQuality = MultisampleQuality; + surf->m_desc.Width = Width; + surf->m_desc.Height = Height; + + *ppSurface = (result==S_OK) ? surf : NULL; + + #if IUNKNOWN_ALLOC_SPEW + char scratch[1024]; + sprintf(scratch,"RT %s", surf->m_tex->m_layout->m_layoutSummary ); + surf->SetMark( true, scratch ); + #endif + + + return result; +} + +void IDirect3DDevice9::UpdateBoundFBO() +{ + RenderTargetState_t renderTargetState; + for ( uint i = 0; i < 4; i++ ) + { + renderTargetState.m_pRenderTargets[i] = m_pRenderTargets[i] ? m_pRenderTargets[i]->m_tex : NULL; + } + renderTargetState.m_pDepthStencil = m_pDepthStencil ? m_pDepthStencil->m_tex : NULL; + CUtlMap < RenderTargetState_t, CGLMFBO * >::IndexType_t index = m_pFBOs->Find( renderTargetState ); + + if ( m_pFBOs->IsValidIndex( index ) ) + { + Assert( (*m_pFBOs)[index] ); + + m_ctx->m_drawingFBO = (*m_pFBOs)[index]; + } + else + { + CGLMFBO *newFBO = m_ctx->NewFBO(); + + m_pFBOs->Insert( renderTargetState, newFBO ); + + uint nNumBound = 0; + + for ( uint i = 0; i < 4; i++ ) + { + if ( !m_pRenderTargets[i] ) + continue; + + GLMFBOTexAttachParams rtParams; + memset( &rtParams, 0, sizeof(rtParams) ); + + rtParams.m_tex = m_pRenderTargets[i]->m_tex; + rtParams.m_face = m_pRenderTargets[i]->m_face; + rtParams.m_mip = m_pRenderTargets[i]->m_mip; + rtParams.m_zslice = 0; + + newFBO->TexAttach( &rtParams, (EGLMFBOAttachment)(kAttColor0 + i) ); + nNumBound++; + } + + if ( m_pDepthStencil ) + { + GLMFBOTexAttachParams depthParams; + memset( &depthParams, 0, sizeof(depthParams) ); + + depthParams.m_tex = m_pDepthStencil->m_tex; + + EGLMFBOAttachment destAttach = (depthParams.m_tex->m_layout->m_format->m_glDataFormat != 34041) ? kAttDepth : kAttDepthStencil; + + newFBO->TexAttach( &depthParams, destAttach ); + nNumBound++; + } + + (void)nNumBound; + + Assert( nNumBound ); + +#if GLMDEBUG + Assert( newFBO->IsReady() ); +#endif + + m_ctx->m_drawingFBO = newFBO; + } + + m_ctx->BindFBOToCtx( m_ctx->m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + m_bFBODirty = false; +} + +void IDirect3DDevice9::ResetFBOMap() +{ + if ( !m_pFBOs ) + return; + + FOR_EACH_MAP_FAST( (*m_pFBOs), i ) + { + const RenderTargetState_t &rtState = m_pFBOs->Key( i ); (void)rtState; + CGLMFBO *pFBO = (*m_pFBOs)[i]; + + m_ctx->DelFBO( pFBO ); + } + + m_pFBOs->Purge(); + + m_bFBODirty = true; +} + +void IDirect3DDevice9::ScrubFBOMap( CGLMTex *pTex ) +{ + Assert( pTex ); + + if ( !m_pFBOs ) + return; + + CUtlVectorFixed< RenderTargetState_t, 128 > fbosToRemove; + + FOR_EACH_MAP_FAST( (*m_pFBOs), i ) + { + const RenderTargetState_t &rtState = m_pFBOs->Key( i ); + CGLMFBO *pFBO = (*m_pFBOs)[i]; (void)pFBO; + + if ( rtState.RefersTo( pTex ) ) + { + fbosToRemove.AddToTail( rtState ); + } + } + + for ( int i = 0; i < fbosToRemove.Count(); ++i ) + { + const RenderTargetState_t &rtState = fbosToRemove[i]; + + CUtlMap < RenderTargetState_t, CGLMFBO * >::IndexType_t index = m_pFBOs->Find( rtState ); + + if ( !m_pFBOs->IsValidIndex( index ) ) + { + Assert( 0 ); + continue; + } + + CGLMFBO *pFBO = (*m_pFBOs)[index]; + + m_ctx->DelFBO( pFBO ); + + m_pFBOs->RemoveAt( index ); + + m_bFBODirty = true; + } + + //GLMDebugPrintf( "IDirect3DDevice9::ScrubFBOMap: Removed %u entries\n", fbosToRemove.Count() ); +} +HRESULT IDirect3DDevice9::SetRenderTarget(DWORD RenderTargetIndex,IDirect3DSurface9* pRenderTarget) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); + + Assert( RenderTargetIndex < 4 ); + + HRESULT result = S_OK; + + GLMPRINTF(("-F- SetRenderTarget index=%d, surface=%8x (tex=%8x %s)", + RenderTargetIndex, + pRenderTarget, + pRenderTarget ? pRenderTarget->m_tex : NULL, + pRenderTarget ? pRenderTarget->m_tex->m_layout->m_layoutSummary : "" + )); + + // note that it is OK to pass NULL for pRenderTarget, it implies that you would like to detach any color buffer from that target index + + // behaviors... + // if new surf is same as old surf, no change in refcount, in fact, it's early exit + IDirect3DSurface9 *oldTarget = m_pRenderTargets[RenderTargetIndex]; + + if (pRenderTarget == oldTarget) + { + GLMPRINTF(("-F- --> no change",RenderTargetIndex)); + return S_OK; + } + + + // Fix this if porting to x86_64 + if ( m_pRenderTargets[RenderTargetIndex] ) + + + { + + + + + + + + + + + + // we now know that the new surf is not the same as the old surf. + // you can't assume either one is non NULL here though. + + m_pRenderTargets[RenderTargetIndex]->Release( 1, "-A SetRenderTarget private release" ); + } + + if (pRenderTarget) + { + pRenderTarget->AddRef( 1, "+A SetRenderTarget private addref" ); // again, private refcount being raised + } + m_pRenderTargets[RenderTargetIndex] = pRenderTarget; + + m_bFBODirty = true; + +/* + if (!pRenderTarget) + { + GLMPRINTF(("-F- --> Setting NULL render target on index=%d ",RenderTargetIndex)); + } + else + { + GLMPRINTF(("-F- --> attaching index=%d on drawing FBO (%8x)",RenderTargetIndex, m_drawableFBO)); + // attach color to FBO + GLMFBOTexAttachParams rtParams; + memset( &rtParams, 0, sizeof(rtParams) ); + + rtParams.m_tex = pRenderTarget->m_tex; + rtParams.m_face = pRenderTarget->m_face; + rtParams.m_mip = pRenderTarget->m_mip; + rtParams.m_zslice = 0; // FIXME if you ever want to be able to render to slices of a 3D tex.. + + m_drawableFBO->TexAttach( &rtParams, (EGLMFBOAttachment)(kAttColor0 + RenderTargetIndex) ); + } +*/ + +#if GL_BATCH_PERF_ANALYSIS && GL_BATCH_PERF_ANALYSIS_WRITE_PNGS + if ( m_pBatch_vis_bitmap && m_pBatch_vis_bitmap->is_valid() && !RenderTargetIndex ) + { + m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, m_pBatch_vis_bitmap->width(), 1, 30, 20, 20); + m_nBatchVisY += 1; + } +#endif + + return result; +} + + +HRESULT IDirect3DDevice9::GetRenderTarget(DWORD RenderTargetIndex,IDirect3DSurface9** ppRenderTarget) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + if ( !m_pRenderTargets[ RenderTargetIndex ] ) + return D3DERR_NOTFOUND; + + if ( ( RenderTargetIndex > 4 ) || !ppRenderTarget ) + return D3DERR_INVALIDCALL; + + // safe because of early exit on NULL above + m_pRenderTargets[ RenderTargetIndex ]->AddRef(0, "+B GetRenderTarget public addref"); // per http://msdn.microsoft.com/en-us/library/bb174404(VS.85).aspx + + *ppRenderTarget = m_pRenderTargets[ RenderTargetIndex ]; + + return S_OK; +} + +HRESULT IDirect3DDevice9::CreateOffscreenPlainSurface( UINT Width,UINT Height,D3DFORMAT Format,D3DPOOL Pool,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // set surf->m_restype to D3DRTYPE_SURFACE... + + // this is almost identical to CreateRenderTarget.. + + HRESULT result = S_OK; + + m_ObjectStats.m_nTotalSurfaces++; + m_ObjectStats.m_nTotalRenderTargets++; + + IDirect3DSurface9 *surf = new IDirect3DSurface9; + surf->m_restype = D3DRTYPE_SURFACE; + + surf->m_device = this; // always set device on creations! + + GLMTexLayoutKey rtkey; + memset( &rtkey, 0, sizeof(rtkey) ); + + rtkey.m_texGLTarget = GL_TEXTURE_2D; + rtkey.m_xSize = Width; + rtkey.m_ySize = Height; + rtkey.m_zSize = 1; + + rtkey.m_texFormat = Format; + rtkey.m_texFlags = kGLMTexRenderable; + + surf->m_tex = m_ctx->NewTex( &rtkey, 1, "offscreen plain surface" ); + surf->m_face = 0; + surf->m_mip = 0; + + //desc + surf->m_desc.Format = Format; + surf->m_desc.Type = D3DRTYPE_SURFACE; + surf->m_desc.Usage = 0; + surf->m_desc.Pool = D3DPOOL_DEFAULT; + surf->m_desc.MultiSampleType = D3DMULTISAMPLE_NONE; + surf->m_desc.MultiSampleQuality = 0; + surf->m_desc.Width = Width; + surf->m_desc.Height = Height; + + *ppSurface = (result==S_OK) ? surf : NULL; + + return result; +} + +HRESULT IDirect3DDevice9::CreateDepthStencilSurface(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultisampleQuality,BOOL Discard,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + Assert( ( Format == D3DFMT_D16 ) || ( Format == D3DFMT_D24X8 ) || ( Format == D3DFMT_D24S8 ) ); + HRESULT result = S_OK; + + m_ObjectStats.m_nTotalSurfaces++; + m_ObjectStats.m_nTotalRenderTargets++; + + IDirect3DSurface9 *surf = new IDirect3DSurface9; + surf->m_restype = D3DRTYPE_SURFACE; + + surf->m_device = this; // always set device on creations! + + GLMTexLayoutKey depthkey; + memset( &depthkey, 0, sizeof(depthkey) ); + + depthkey.m_texGLTarget = GL_TEXTURE_2D; + depthkey.m_xSize = Width; + depthkey.m_ySize = Height; + depthkey.m_zSize = 1; + + depthkey.m_texFormat = Format; + depthkey.m_texFlags = kGLMTexRenderable | kGLMTexIsDepth; + + if ( Format == D3DFMT_D24S8 ) + { + depthkey.m_texFlags |= kGLMTexIsStencil; + } + + if ( (MultiSample !=0) && (!m_ctx->Caps().m_nvG7x) ) + { + depthkey.m_texFlags |= kGLMTexMultisampled; + depthkey.m_texSamples = MultiSample; + // FIXME no support for "MS quality" yet + } + + surf->m_tex = m_ctx->NewTex( &depthkey, 1, "depth-stencil surface" ); + surf->m_face = 0; + surf->m_mip = 0; + + //desc + + surf->m_desc.Format = Format; + surf->m_desc.Type = D3DRTYPE_SURFACE; + surf->m_desc.Usage = 0; //FIXME ??????????? + surf->m_desc.Pool = D3DPOOL_DEFAULT; //FIXME ??????????? + surf->m_desc.MultiSampleType = MultiSample; + surf->m_desc.MultiSampleQuality = MultisampleQuality; + surf->m_desc.Width = Width; + surf->m_desc.Height = Height; + + *ppSurface = (result==S_OK) ? surf : NULL; + + return result; +} + +HRESULT IDirect3DDevice9::SetDepthStencilSurface( IDirect3DSurface9* pNewZStencil ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + HRESULT result = S_OK; + + GLMPRINTF(("-F- SetDepthStencilSurface, surface=%8x (tex=%8x %s)", + pNewZStencil, + pNewZStencil ? pNewZStencil->m_tex : NULL, + pNewZStencil ? pNewZStencil->m_tex->m_layout->m_layoutSummary : "" + )); + + if ( pNewZStencil == m_pDepthStencil ) + { + GLMPRINTF(("-F- --> no change")); + return S_OK; + } + + if ( pNewZStencil ) + { + pNewZStencil->AddRef(1, "+A SetDepthStencilSurface private addref"); + } + + if ( m_pDepthStencil ) + { + // Note this Release() could cause the surface to be deleted! + m_pDepthStencil->Release(1, "-A SetDepthStencilSurface private release"); + } + + m_pDepthStencil = pNewZStencil; + + m_bFBODirty = true; + + return result; +} + +HRESULT IDirect3DDevice9::GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + if ( !ppZStencilSurface ) + { + return D3DERR_INVALIDCALL; + } + + if ( !m_pDepthStencil ) + { + *ppZStencilSurface = NULL; + return D3DERR_NOTFOUND; + } + + m_pDepthStencil->AddRef(0, "+B GetDepthStencilSurface public addref"); // per http://msdn.microsoft.com/en-us/library/bb174384(VS.85).aspx + + *ppZStencilSurface = m_pDepthStencil; + + return S_OK; +} + +HRESULT IDirect3DDevice9::GetRenderTargetData(IDirect3DSurface9* pRenderTarget,IDirect3DSurface9* pDestSurface) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // is it just a blit ? + + this->StretchRect( pRenderTarget, NULL, pDestSurface, NULL, D3DTEXF_NONE ); // is this good enough ??? + + return S_OK; +} + +HRESULT IDirect3DDevice9::GetFrontBufferData(UINT iSwapChain,IDirect3DSurface9* pDestSurface) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::StretchRect(IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect,IDirect3DSurface9* pDestSurface,CONST RECT* pDestRect,D3DTEXTUREFILTERTYPE Filter) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // find relevant slices in GLM tex + + if ( m_bFBODirty ) + { + UpdateBoundFBO(); + } + + CGLMTex *srcTex = pSourceSurface->m_tex; + int srcSliceIndex = srcTex->CalcSliceIndex( pSourceSurface->m_face, pSourceSurface->m_mip ); + GLMTexLayoutSlice *srcSlice = &srcTex->m_layout->m_slices[ srcSliceIndex ]; + + CGLMTex *dstTex = pDestSurface->m_tex; + int dstSliceIndex = dstTex->CalcSliceIndex( pDestSurface->m_face, pDestSurface->m_mip ); + GLMTexLayoutSlice *dstSlice = &dstTex->m_layout->m_slices[ dstSliceIndex ]; + + if ( dstTex->m_rboName != 0 ) + { + Assert(!"No path yet for blitting into an MSAA tex"); + return S_OK; + } + + bool useFastBlit = (gl_blitmode.GetInt() != 0); + + if ( !useFastBlit && (srcTex->m_rboName !=0)) // old way, we do a resolve to scratch tex first (necessitating two step blit) + { + m_ctx->ResolveTex( srcTex, true ); + } + + // set up source/dest rect in GLM form + GLMRect srcRect, dstRect; + + // d3d nomenclature: + // Y=0 is the visual top and also aligned with V=0. + + srcRect.xmin = pSourceRect ? pSourceRect->left : 0; + srcRect.xmax = pSourceRect ? pSourceRect->right : srcSlice->m_xSize; + srcRect.ymin = pSourceRect ? pSourceRect->top : 0; + srcRect.ymax = pSourceRect ? pSourceRect->bottom : srcSlice->m_ySize; + + dstRect.xmin = pDestRect ? pDestRect->left : 0; + dstRect.xmax = pDestRect ? pDestRect->right : dstSlice->m_xSize; + dstRect.ymin = pDestRect ? pDestRect->top : 0; + dstRect.ymax = pDestRect ? pDestRect->bottom : dstSlice->m_ySize; + + GLenum filterGL = 0; + switch(Filter) + { + case D3DTEXF_NONE: + case D3DTEXF_POINT: + filterGL = GL_NEAREST; + break; + + case D3DTEXF_LINEAR: + filterGL = GL_LINEAR; + break; + + default: // D3DTEXF_ANISOTROPIC + Assert(!"Impl aniso stretch"); + break; + } + + if (useFastBlit) + { + m_ctx->Blit2( srcTex, &srcRect, pSourceSurface->m_face, pSourceSurface->m_mip, + dstTex, &dstRect, pDestSurface->m_face, pDestSurface->m_mip, + filterGL + ); + } + else + { + m_ctx->BlitTex( srcTex, &srcRect, pSourceSurface->m_face, pSourceSurface->m_mip, + dstTex, &dstRect, pDestSurface->m_face, pDestSurface->m_mip, + filterGL + ); + } + + return S_OK; +} + + +// This totally sucks, but this information can't be gleaned any +// other way when translating from D3D to GL at this level +// +// This returns a mask, since multiple GLSL "varyings" can be tagged with centroid +static uint32 CentroidMaskFromName( bool bPixelShader, const char *pName ) +{ + // Important note: This code has been customized for TF2 - don't blindly merge it into other branches! + if ( !pName ) + return 0; + + // Important: The centroid bitflags must match between all linked vertex/pixel shaders! + if ( bPixelShader ) + { + if ( V_stristr( pName, "lightmappedgeneric_ps" ) || V_strstr( pName, "worldtwotextureblend_ps" ) ) + { + return (0x01 << 2) | (0x01 << 3); // iterators 2 and 3 + } + else if ( V_stristr( pName, "lightmappedreflective_ps" ) ) + { + return (0x01 << 6) | (0x01 << 7); // iterators 6 and 7 + } + else if ( V_stristr( pName, "water_ps" ) ) + { + return 0xC0; + } + else if ( V_stristr( pName, "shadow_ps" ) ) + { + return 0x1F; + } + else if ( V_stristr( pName, "ShatteredGlass_ps" ) ) + { + return 0xC; + } + else if ( V_stristr( pName, "WorldVertexAlpha_ps" ) || V_stristr( pName, "WorldVertexTransition_ps" ) ) + { + // These pixel shaders want centroid but shouldn't be used + Assert(0); + return 0; + } + else if ( V_stristr( pName, "flashlight_ps" ) ) + { + return 0xC; + } + } + else // vertex shader + { + // Vertex shaders also + if ( V_stristr( pName, "lightmappedgeneric_vs" ) ) + { + return (0x01 << 2) | (0x01 << 3); // iterators 2 and 3 + } + else if ( V_stristr( pName, "lightmappedreflective_vs" ) ) + { + return (0x01 << 6) | (0x01 << 7); // iterators 6 and 7 + } + else if ( V_stristr( pName, "water_vs" ) ) + { + return 0xC0; + } + else if ( V_stristr( pName, "shadow_vs" ) ) + { + return 0x1F; + } + else if ( V_stristr( pName, "ShatteredGlass_vs" ) ) + { + return 0xC; + } + else if ( V_stristr( pName, "flashlight_vs" ) ) + { + return 0xC; + } + } + + // This shader doesn't have any centroid iterators + return 0; +} + + +// This totally sucks, but this information can't be gleaned any +// other way when translating from D3D to GL at this level +static int ShadowDepthSamplerMaskFromName( const char *pName ) +{ + if ( !pName ) + return 0; + + if ( V_stristr( pName, "water_ps" ) ) + { + return (1<<7); + } + else if ( V_stristr( pName, "infected_ps" ) ) + { + return (1<<1); + } + else if ( V_stristr( pName, "phong_ps" ) ) + { + return (1<<4) | (1<<15); + } + else if ( V_stristr( pName, "vertexlit_and_unlit_generic_bump_ps" ) ) + { + return (1<<8) | (1<<15); + } + else if ( V_stristr( pName, "vertexlit_and_unlit_generic_ps" ) ) + { + return (1<<8) | (1<<15); + } + else if ( V_stristr( pName, "eye_refract_ps" ) ) + { + return (1<<6); + } + else if ( V_stristr( pName, "eyes_flashlight_ps" ) ) + { + return (1<<4); + } + else if ( V_stristr( pName, "worldtwotextureblend_ps" ) ) + { + return (1<<7); + } + else if ( V_stristr( pName, "teeth_flashlight_ps" ) ) + { + return (1<<2); + } + else if ( V_stristr( pName, "flashlight_ps" ) ) // substring of above, make sure this comes last!! + { + return (1<<7); + } + else if ( V_stristr( pName, "lightmappedgeneric_ps" ) ) + { + return (1<<15); + } + else if ( V_stristr( pName, "deferred_global_light_ps" ) ) + { + return (1<<14); + } + else if ( V_stristr( pName, "global_lit_simple_ps" ) ) + { + return (1<<14); + } + else if ( V_stristr( pName, "lightshafts_ps" ) ) + { + return (1<<1); + } + else if ( V_stristr( pName, "multiblend_combined_ps" ) ) + { + return (1<<14); + } + else if ( V_stristr( pName, "multiblend_ps" ) ) + { + return (1<<14); + } + else if ( V_stristr( pName, "customhero_ps" ) ) + { + return (1<<14); + } + + // This shader doesn't have a shadow depth map sampler + return 0; +} + +#ifdef OSX + +#pragma mark ----- Pixel Shaders - (IDirect3DDevice9) + +#endif + +HRESULT IDirect3DDevice9::CreatePixelShader(CONST DWORD* pFunction,IDirect3DPixelShader9** ppShader, const char *pShaderName, char *pDebugLabel, const uint32 *pCentroidMask ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + HRESULT result = D3DERR_INVALIDCALL; + *ppShader = NULL; + + int nShadowDepthSamplerMask = ShadowDepthSamplerMaskFromName( pShaderName ); + uint nCentroidMask = CentroidMaskFromName( true, pShaderName ); + + if ( pCentroidMask ) + { + if ( *pCentroidMask != nCentroidMask ) + { + GLMDebugPrintf( "IDirect3DDevice9::CreatePixelShader: shaderapi's centroid mask (0x%08X) differs from mask derived from shader name (0x%08X) for shader %s\n", *pCentroidMask, nCentroidMask, pDebugLabel ); + } + // It would be great if we could use these centroid masks passed in from shaderapi - but unfortunately they're only available for pixel shaders, and we also need to compute matching masks for vertex shaders! + //nCentroidMask = *pCentroidMask; + } + + { + int numTranslations = 1; + + bool bVertexShader = false; + + // we can do one or two translated forms. they go together in a single buffer with some markers to allow GLM to break it up. + // this also lets us mirror each set of translations to disk with a single file making it easier to view and edit side by side. + + int maxTranslationSize = 50000; // size of any one translation + + CUtlBuffer transbuf( 3000, numTranslations * maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); + CUtlBuffer tempbuf( 3000, maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); + + transbuf.PutString( "//GLSLfp\n" ); // this is required so GLM can crack the text apart + + // note the GLSL translator wants its own buffer + tempbuf.EnsureCapacity( maxTranslationSize ); + + uint glslPixelShaderOptions = D3DToGL_OptionUseEnvParams;// | D3DToGL_OptionAllowStaticControlFlow; + + + // Fake SRGB mode - needed on R500, probably indefinitely. + // Do this stuff if caps show m_needsFakeSRGB=true and the sRGBWrite state is true + // (but not if it's engine_post which is special) + + if (!m_ctx->Caps().m_hasGammaWrites) + { + if ( pShaderName ) + { + if ( !V_stristr( pShaderName, "engine_post" ) ) + { + glslPixelShaderOptions |= D3DToGL_OptionSRGBWriteSuffix; + } + } + } + + g_D3DToOpenGLTranslatorGLSL.TranslateShader( (uint32 *) pFunction, &tempbuf, &bVertexShader, glslPixelShaderOptions, nShadowDepthSamplerMask, nCentroidMask, pDebugLabel ); + + transbuf.PutString( (char*)tempbuf.Base() ); + transbuf.PutString( "\n\n" ); // whitespace + + if ( bVertexShader ) + { + // don't cross the streams + Assert(!"Can't accept vertex shader in CreatePixelShader"); + result = D3DERR_INVALIDCALL; + } + else + { + m_ObjectStats.m_nTotalPixelShaders++; + + IDirect3DPixelShader9 *newprog = new IDirect3DPixelShader9; + + newprog->m_pixHighWater = 0; + newprog->m_pixSamplerMask = 0; + newprog->m_pixSamplerTypes = 0; + + newprog->m_pixProgram = m_ctx->NewProgram( kGLMFragmentProgram, (char *)transbuf.Base(), pShaderName ? pShaderName : "?" ) ; + newprog->m_pixProgram->m_nCentroidMask = nCentroidMask; + newprog->m_pixProgram->m_nShadowDepthSamplerMask = nShadowDepthSamplerMask; + + newprog->m_pixProgram->m_bTranslatedProgram = true; + newprog->m_pixProgram->m_maxVertexAttrs = 0; + + newprog->m_device = this; + + //------ find the frag program metadata and extract it.. + + + { + // find the highwater mark + char *highWaterPrefix = "//HIGHWATER-"; // try to arrange this so it can work with pure GLSL if needed + char *highWaterStr = strstr( (char *)transbuf.Base(), highWaterPrefix ); + if (highWaterStr) + { + char *highWaterActualData = highWaterStr + strlen( highWaterPrefix ); + + int value = -1; + sscanf( highWaterActualData, "%d", &value ); + + newprog->m_pixHighWater = value; + newprog->m_pixProgram->m_descs[kGLMGLSL].m_highWater = value; + } + else + { + Assert(!"couldn't find sampler map in pixel shader"); + } + } + + { + // find the sampler map + char *samplerMaskPrefix = "//SAMPLERMASK-"; // try to arrange this so it can work with pure GLSL if needed + + char *samplerMaskStr = strstr( (char *)transbuf.Base(), samplerMaskPrefix ); + if (samplerMaskStr) + { + char *samplerMaskActualData = samplerMaskStr + strlen( samplerMaskPrefix ); + + int value = -1; + sscanf( samplerMaskActualData, "%04x", &value ); + + newprog->m_pixSamplerMask = value; + newprog->m_pixProgram->m_samplerMask = value; // helps GLM maintain a better linked pair cache even when SRGB sampler state changes + + int nMaxReg; + for ( nMaxReg = 31; nMaxReg >= 0; --nMaxReg ) + if ( value & ( 1 << nMaxReg ) ) + break; + + newprog->m_pixProgram->m_maxSamplers = nMaxReg + 1; + + int nNumUsedSamplers = 0; + for ( int i = 31; i >= 0; --i) + if ( value & ( 1 << i ) ) + nNumUsedSamplers++; + newprog->m_pixProgram->m_nNumUsedSamplers = nNumUsedSamplers; + } + else + { + Assert(!"couldn't find sampler map in pixel shader"); + } + } + + { + // find the sampler map + char *samplerTypesPrefix = "//SAMPLERTYPES-"; // try to arrange this so it can work with pure GLSL if needed + + char *samplerTypesStr = strstr( (char *)transbuf.Base(), samplerTypesPrefix ); + if (samplerTypesStr) + { + char *samplerTypesActualData = samplerTypesStr + strlen( samplerTypesPrefix ); + + int value = -1; + sscanf( samplerTypesActualData, "%08x", &value ); + + newprog->m_pixSamplerTypes = value; + newprog->m_pixProgram->m_samplerTypes = value; // helps GLM maintain a better linked pair cache even when SRGB sampler state changes + } + else + { + Assert(!"couldn't find sampler types in pixel shader"); + } + } + + { + // find the fb outputs used by this shader/combo + const GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT }; + + char *fragDataMaskPrefix = "//FRAGDATAMASK-"; + + char *fragDataMaskStr = strstr( (char *)transbuf.Base(), fragDataMaskPrefix ); + if ( fragDataMaskStr ) + { + char *fragDataActualData = fragDataMaskStr + strlen( fragDataMaskPrefix ); + + int value = -1; + sscanf( fragDataActualData, "%04x", &value ); + + newprog->m_pixFragDataMask = value; + newprog->m_pixProgram->m_fragDataMask = value; + + newprog->m_pixProgram->m_numDrawBuffers = 0; + for( int i = 0; i < 4; i++ ) + { + if( newprog->m_pixProgram->m_fragDataMask & ( 1 << i ) ) + { + newprog->m_pixProgram->m_drawBuffers[ newprog->m_pixProgram->m_numDrawBuffers ] = buffers[ i ]; + newprog->m_pixProgram->m_numDrawBuffers++; + } + } + + if( newprog->m_pixProgram->m_numDrawBuffers == 0 ) + { + Assert(!"couldn't find fragment output in pixel shader"); + newprog->m_pixProgram->m_drawBuffers[ 0 ] = buffers[ 0 ]; + newprog->m_pixProgram->m_numDrawBuffers = 1; + } + } + else + { + newprog->m_pixFragDataMask = 0; + newprog->m_pixProgram->m_fragDataMask = 0; + } + + } + + *ppShader = newprog; + + result = S_OK; + } + } + + + return result; +} + +IDirect3DPixelShader9::~IDirect3DPixelShader9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DPixelShader9" )); + + if (m_device) + { + m_device->ReleasedPixelShader( this ); + + if (m_pixProgram) + { + m_pixProgram->m_ctx->DelProgram( m_pixProgram ); + m_pixProgram = NULL; + } + m_device = NULL; + } + + GLMPRINTF(( "<-A- ~IDirect3DPixelShader9" )); +} + + +HRESULT IDirect3DDevice9::SetPixelShaderNonInline(IDirect3DPixelShader9* pShader) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + m_ctx->SetFragmentProgram( pShader ? pShader->m_pixProgram : NULL ); + m_pixelShader = pShader; + + return S_OK; +} + +HRESULT IDirect3DDevice9::SetPixelShaderConstantFNonInline(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; +#if 0 + const uint nRegToWatch = 3; + if ( ( ( StartRegister + Vector4fCount ) > nRegToWatch ) && ( StartRegister <= nRegToWatch ) ) + { + GLMDebugPrintf( "-- %f %f %f %f\n", pConstantData[(nRegToWatch - StartRegister)*4+0], pConstantData[(nRegToWatch - StartRegister)*4+1], pConstantData[(nRegToWatch - StartRegister)*4+2], pConstantData[(nRegToWatch - StartRegister)*4+3] ); + } +#endif + m_ctx->SetProgramParametersF( kGLMFragmentProgram, StartRegister, (float *)pConstantData, Vector4fCount ); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetPixelShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + m_ctx->SetProgramParametersB( kGLMFragmentProgram, StartRegister, (int *)pConstantData, BoolCount ); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetPixelShaderConstantI(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + GLMPRINTF(("-X- Ignoring IDirect3DDevice9::SetPixelShaderConstantI call, count was %d", Vector4iCount )); +// m_ctx->SetProgramParametersI( kGLMFragmentProgram, StartRegister, pConstantData, Vector4iCount ); + return S_OK; +} + + +#ifdef OSX + +#pragma mark ----- Vertex Shaders - (IDirect3DDevice9) + +#endif + +HRESULT IDirect3DDevice9::CreateVertexShader(CONST DWORD* pFunction, IDirect3DVertexShader9** ppShader, const char *pShaderName, char *pDebugLabel) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + HRESULT result = D3DERR_INVALIDCALL; + *ppShader = NULL; + + uint32 nCentroidMask = CentroidMaskFromName( false, pShaderName ); + + { + int numTranslations = 1; + + bool bVertexShader = false; + + // we can do one or two translated forms. they go together in a single buffer with some markers to allow GLM to break it up. + // this also lets us mirror each set of translations to disk with a single file making it easier to view and edit side by side. + + int maxTranslationSize = 500000; // size of any one translation + + CUtlBuffer transbuf( 1000, numTranslations * maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); + CUtlBuffer tempbuf( 1000, maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); + + transbuf.PutString( "//GLSLvp\n" ); // this is required so GLM can crack the text apart + + // note the GLSL translator wants its own buffer + tempbuf.EnsureCapacity( maxTranslationSize ); + + uint glslVertexShaderOptions = D3DToGL_OptionUseEnvParams | D3DToGL_OptionDoFixupZ | D3DToGL_OptionDoFixupY; + + if ( m_ctx->Caps().m_hasNativeClipVertexMode ) + { + // note the matched trickery over in IDirect3DDevice9::FlushStates - + // if on a chipset that does no have native gl_ClipVertex support, then + // omit writes to gl_ClipVertex, and instead submit plane equations that have been altered, + // and clipping will take place in GL space using gl_Position instead of gl_ClipVertex. + + // note that this is very much a hack to mate up with ATI R5xx hardware constraints, and with older + // drivers even for later ATI parts like r6xx/r7xx. And it doesn't work on NV parts, so you really + // do have to choose the right way to go. + + glslVertexShaderOptions |= D3DToGL_OptionDoUserClipPlanes; + } + + if ( !CommandLine()->CheckParm("-disableboneuniformbuffers") ) + { + // If using GLSL, enabling a uniform buffer specifically for bone registers. (Not currently supported with ARB shaders, which are not optimized at all anyway.) + glslVertexShaderOptions |= D3DToGL_OptionGenerateBoneUniformBuffer; + } + + g_D3DToOpenGLTranslatorGLSL.TranslateShader( (uint32 *) pFunction, &tempbuf, &bVertexShader, glslVertexShaderOptions, -1, nCentroidMask, pDebugLabel ); + + transbuf.PutString( (char*)tempbuf.Base() ); + transbuf.PutString( "\n\n" ); // whitespace + + if ( !bVertexShader ) + { + // don't cross the streams + Assert(!"Can't accept pixel shader in CreateVertexShader"); + result = D3DERR_INVALIDCALL; + } + else + { + m_ObjectStats.m_nTotalVertexShaders++; + + IDirect3DVertexShader9 *newprog = new IDirect3DVertexShader9; + + newprog->m_device = this; + + newprog->m_vtxProgram = m_ctx->NewProgram( kGLMVertexProgram, (char *)transbuf.Base(), pShaderName ? pShaderName : "?" ) ; + newprog->m_vtxProgram->m_nCentroidMask = nCentroidMask; + + newprog->m_vtxProgram->m_bTranslatedProgram = true; + newprog->m_vtxProgram->m_maxVertexAttrs = 0; + newprog->m_maxVertexAttrs = 0; + + // find the highwater mark.. + + char *highWaterPrefix = "//HIGHWATER-"; // try to arrange this so it can work with pure GLSL if needed + char *highWaterStr = strstr( (char *)transbuf.Base(), highWaterPrefix ); + if (highWaterStr) + { + char *highWaterActualData = highWaterStr + strlen( highWaterPrefix ); + + int value = -1; + sscanf( highWaterActualData, "%d", &value ); + + newprog->m_vtxHighWater = value; + newprog->m_vtxProgram->m_descs[kGLMGLSL].m_highWater = value; + } + else + { + Assert(!"couldn't find highwater mark in vertex shader"); + } + + char *highWaterBonePrefix = "//HIGHWATERBONE-"; // try to arrange this so it can work with pure GLSL if needed + char *highWaterBoneStr = strstr( (char *)transbuf.Base(), highWaterBonePrefix ); + if (highWaterBoneStr) + { + char *highWaterActualData = highWaterBoneStr + strlen( highWaterBonePrefix ); + + int value = -1; + sscanf( highWaterActualData, "%d", &value ); + + newprog->m_vtxHighWaterBone = value; + newprog->m_vtxProgram->m_descs[kGLMGLSL].m_VSHighWaterBone = value; + } + else + { + newprog->m_vtxHighWaterBone = 0; + newprog->m_vtxProgram->m_descs[kGLMGLSL].m_VSHighWaterBone = 0; + } + + // find the attrib map.. + char *attribMapPrefix = "//ATTRIBMAP-"; // try to arrange this so it can work with pure GLSL if needed + char *attribMapStr = strstr( (char *)transbuf.Base(), attribMapPrefix ); + + if (attribMapStr) + { + char *attribMapActualData = attribMapStr + strlen( attribMapPrefix ); + uint nMaxVertexAttribs = 0; + for( int i=0; i<16; i++) + { + int value = -1; + char *dataItem = attribMapActualData + (i*3); + sscanf( dataItem, "%02x", &value ); + if (value >=0) + { + // make sure it's not a terminator + if (value == 0xBB) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + } + else + { + // probably an 'xx'... check + if ( (dataItem[0] != 'x') || (dataItem[1] != 'x') ) + { + DXABSTRACT_BREAK_ON_ERROR(); // bad news + } + else + { + value = 0xBB; // not likely to see one of these... "fog with usage index 11" + } + } + + if ( value != 0xBB ) + nMaxVertexAttribs = i; + + newprog->m_vtxAttribMap[i] = value; + } + + newprog->m_vtxProgram->m_maxVertexAttrs = nMaxVertexAttribs + 1; + newprog->m_maxVertexAttrs = nMaxVertexAttribs + 1; + } + else + { + DXABSTRACT_BREAK_ON_ERROR(); // that's bad... + } + + *ppShader = newprog; + + result = S_OK; + } + } + + return result; +} + +IDirect3DVertexShader9::~IDirect3DVertexShader9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DVertexShader9" )); + + if (m_device) + { + m_device->ReleasedVertexShader( this ); + + if (m_vtxProgram) + { + m_vtxProgram->m_ctx->DelProgram( m_vtxProgram ); + m_vtxProgram = NULL; + } + m_device = NULL; + } + else + { + } + + + GLMPRINTF(( "<-A- ~IDirect3DVertexShader9" )); +} + +HRESULT IDirect3DDevice9::SetVertexShaderNonInline(IDirect3DVertexShader9* pShader) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + m_ctx->SetVertexProgram( pShader ? pShader->m_vtxProgram : NULL ); + m_vertexShader = pShader; + return S_OK; +} + +HRESULT IDirect3DDevice9::SetVertexShaderConstantFNonInline(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) // groups of 4 floats! +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + m_ctx->SetProgramParametersF( kGLMVertexProgram, StartRegister, (float *)pConstantData, Vector4fCount ); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetVertexShaderConstantBNonInline(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) // individual bool count! +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + m_ctx->SetProgramParametersB( kGLMVertexProgram, StartRegister, (int *)pConstantData, BoolCount ); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetVertexShaderConstantINonInline(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) // groups of 4 ints! +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + m_ctx->SetProgramParametersI( kGLMVertexProgram, StartRegister, (int *)pConstantData, Vector4iCount ); + return S_OK; +} + +#ifdef OSX + +#pragma mark ----- Shader Pairs - (IDirect3DDevice9) + +#endif + +// callers need to ifdef POSIX this, because this method does not exist on real DX9 +HRESULT IDirect3DDevice9::LinkShaderPair( IDirect3DVertexShader9* vs, IDirect3DPixelShader9* ps ) +{ + GL_BATCH_PERF_CALL_TIMER; + // these are really GLSL "shaders" not "programs" but the old reference to "program" persists due to the assembler heritage + if (vs->m_vtxProgram && ps->m_pixProgram) + { + m_ctx->LinkShaderPair( vs->m_vtxProgram, ps->m_pixProgram ); + } + return S_OK; +} + +HRESULT IDirect3DDevice9::ValidateShaderPair( IDirect3DVertexShader9* vs, IDirect3DPixelShader9* ps ) +{ + GL_BATCH_PERF_CALL_TIMER; + // these are really GLSL "shaders" not "programs" but the old reference to "program" persists due to the assembler heritage + if (vs->m_vtxProgram && ps->m_pixProgram) + { + m_ctx->ValidateShaderPair( vs->m_vtxProgram, ps->m_pixProgram ); + } + return S_OK; +} + +// callers need to ifdef POSIX this, because this method does not exist on real DX9 +// +HRESULT IDirect3DDevice9::QueryShaderPair( int index, GLMShaderPairInfo *infoOut ) +{ + GL_BATCH_PERF_CALL_TIMER; + // these are really GLSL "shaders" not "programs" ... + + m_ctx->QueryShaderPair( index, infoOut ); + + return S_OK; +} + + +#ifdef OSX + +#pragma mark ----- Vertex Buffers and Vertex Declarations - (IDirect3DDevice9) + +#endif + +HRESULT IDirect3DDevice9::CreateVertexDeclaration(CONST D3DVERTEXELEMENT9* pVertexElements,IDirect3DVertexDeclaration9** ppDecl) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + *ppDecl = NULL; + + // the goal here is to arrive at something which lets us quickly generate GLMVertexSetups. + + // the information we don't have, that must be inferred from the decls, is: + // -> how many unique streams (buffers) are used - pure curiosity + // -> what the stride and offset is for each decl. Size you can figure out on the spot, stride requires surveying all the components in each stream first. + // so init an array of per-stream offsets to 0. + // each one is a cursor that gets bumped by decls. + uint streamOffsets[ D3D_MAX_STREAMS ]; + uint streamCount = 0; + + uint attribMap[16]; + uint attribMapIndex = 0; + memset( attribMap, 0xFF, sizeof( attribMap ) ); + + memset( streamOffsets, 0, sizeof( streamOffsets ) ); + + m_ObjectStats.m_nTotalVertexDecls++; + + IDirect3DVertexDeclaration9 *decl9 = new IDirect3DVertexDeclaration9; + decl9->m_device = this; + + decl9->m_elemCount = 0; + + for (const D3DVERTEXELEMENT9 *src = pVertexElements; (src->Stream != 0xFF); src++) + { + // element + D3DVERTEXELEMENT9_GL *elem = &decl9->m_elements[ decl9->m_elemCount++ ]; + + // copy the D3D decl wholesale. + elem->m_dxdecl = *src; + + // latch current offset in this stream. + elem->m_gldecl.m_offset = streamOffsets[ elem->m_dxdecl.Stream ]; + + // figure out size of this attr and move the cursor + // if cursor was on zero, bump the active stream count + + if (!streamOffsets[ elem->m_dxdecl.Stream ]) + streamCount++; + + int bytes = 0; + switch( elem->m_dxdecl.Type ) + { + case D3DDECLTYPE_FLOAT1: elem->m_gldecl.m_nCompCount = 1; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 4; break; + case D3DDECLTYPE_FLOAT2: elem->m_gldecl.m_nCompCount = 2; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 8; break; + + //case D3DVSDT_FLOAT3: + case D3DDECLTYPE_FLOAT3: elem->m_gldecl.m_nCompCount = 3; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 12; break; + + //case D3DVSDT_FLOAT4: + case D3DDECLTYPE_FLOAT4: elem->m_gldecl.m_nCompCount = 4; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 16; break; + + // case D3DVSDT_UBYTE4: + case D3DDECLTYPE_D3DCOLOR: + case D3DDECLTYPE_UBYTE4: + case D3DDECLTYPE_UBYTE4N: + + // Force this path since we're on 10.6.2 and can't rely on EXT_vertex_array_bgra + if ( 1 ) + { + // pass 4 UB's but we know this is out of order compared to D3DCOLOR data + elem->m_gldecl.m_nCompCount = 4; elem->m_gldecl.m_datatype = GL_UNSIGNED_BYTE; + } + else + { + // pass a GL BGRA color courtesy of http://www.opengl.org/registry/specs/ARB/vertex_array_bgra.txt + elem->m_gldecl.m_nCompCount = GL_BGRA; elem->m_gldecl.m_datatype = GL_UNSIGNED_BYTE; + } + + elem->m_gldecl.m_normalized = ( (elem->m_dxdecl.Type == D3DDECLTYPE_D3DCOLOR) || + (elem->m_dxdecl.Type == D3DDECLTYPE_UBYTE4N) ); + + bytes = 4; + break; + + case D3DDECLTYPE_SHORT2: + // pass 2 US's but we know this is out of order compared to D3DCOLOR data + elem->m_gldecl.m_nCompCount = 2; elem->m_gldecl.m_datatype = GL_SHORT; + + elem->m_gldecl.m_normalized = 0; + bytes = 4; + break; + + default: DXABSTRACT_BREAK_ON_ERROR(); return D3DERR_INVALIDCALL; break; + + /* + typedef enum _D3DDECLTYPE + { + D3DDECLTYPE_FLOAT1 = 0, // 1D float expanded to (value, 0., 0., 1.) + D3DDECLTYPE_FLOAT2 = 1, // 2D float expanded to (value, value, 0., 1.) + D3DDECLTYPE_FLOAT3 = 2, // 3D float expanded to (value, value, value, 1.) + D3DDECLTYPE_FLOAT4 = 3, // 4D float + D3DDECLTYPE_D3DCOLOR = 4, // 4D packed unsigned bytes mapped to 0. to 1. range + // Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A) + D3DDECLTYPE_UBYTE4 = 5, // 4D unsigned byte + D3DDECLTYPE_SHORT2 = 6, // 2D signed short expanded to (value, value, 0., 1.) + D3DDECLTYPE_SHORT4 = 7, // 4D signed short + + // The following types are valid only with vertex shaders >= 2.0 + + + D3DDECLTYPE_UBYTE4N = 8, // Each of 4 bytes is normalized by dividing to 255.0 + D3DDECLTYPE_SHORT2N = 9, // 2D signed short normalized (v[0]/32767.0,v[1]/32767.0,0,1) + D3DDECLTYPE_SHORT4N = 10, // 4D signed short normalized (v[0]/32767.0,v[1]/32767.0,v[2]/32767.0,v[3]/32767.0) + D3DDECLTYPE_USHORT2N = 11, // 2D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,0,1) + D3DDECLTYPE_USHORT4N = 12, // 4D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,v[2]/65535.0,v[3]/65535.0) + D3DDECLTYPE_UDEC3 = 13, // 3D unsigned 10 10 10 format expanded to (value, value, value, 1) + D3DDECLTYPE_DEC3N = 14, // 3D signed 10 10 10 format normalized and expanded to (v[0]/511.0, v[1]/511.0, v[2]/511.0, 1) + D3DDECLTYPE_FLOAT16_2 = 15, // Two 16-bit floating point values, expanded to (value, value, 0, 1) + D3DDECLTYPE_FLOAT16_4 = 16, // Four 16-bit floating point values + D3DDECLTYPE_UNUSED = 17, // When the type field in a decl is unused. + } D3DDECLTYPE; + */ + } + + // write the offset and move the cursor + elem->m_gldecl.m_offset = streamOffsets[elem->m_dxdecl.Stream]; + streamOffsets[ elem->m_dxdecl.Stream ] += bytes; + + // cannot write m_stride yet, so zero it + elem->m_gldecl.m_stride = 0; + + elem->m_gldecl.m_pBuffer = NULL; // must be filled in at draw time.. + + // elem count was already bumped. + + // update attrib map + attribMap[ attribMapIndex++ ] = (elem->m_dxdecl.Usage << 4) | (elem->m_dxdecl.UsageIndex); + } + // the loop is done, we now know how many active streams there are, how many atribs are active in the declaration, + // and how big each one is in terms of stride. + + // all that is left is to go back and write the strides - the stride comes from the stream offset cursors accumulated earlier. + for( uint j=0; j< decl9->m_elemCount; j++) + { + D3DVERTEXELEMENT9_GL *elem = &decl9->m_elements[ j ]; + + elem->m_gldecl.m_stride = streamOffsets[ elem->m_dxdecl.Stream ]; + } + + memset( decl9->m_VertexAttribDescToStreamIndex, 0xFF, sizeof( decl9->m_VertexAttribDescToStreamIndex ) ); + D3DVERTEXELEMENT9_GL *pDeclElem = decl9->m_elements; + for( uint j = 0; j < decl9->m_elemCount; j++, pDeclElem++) + { + uint nPackedVertexAttribDesc = ( pDeclElem->m_dxdecl.Usage << 4 ) | pDeclElem->m_dxdecl.UsageIndex; + if ( nPackedVertexAttribDesc == 0xBB ) + { + // 0xBB is a reserved packed vertex attrib value - shouldn't encounter in practice + DXABSTRACT_BREAK_ON_ERROR(); + } + decl9->m_VertexAttribDescToStreamIndex[ nPackedVertexAttribDesc ] = j; + } + + *ppDecl = decl9; + + return S_OK; +} + +IDirect3DVertexDeclaration9::~IDirect3DVertexDeclaration9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(("-A- ~IDirect3DVertexDeclaration9 signpost")); + + m_device->ReleasedVertexDeclaration( this ); +} + +HRESULT IDirect3DDevice9::SetVertexDeclarationNonInline(IDirect3DVertexDeclaration9* pDecl) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + m_pVertDecl = pDecl; + return S_OK; +} + +HRESULT IDirect3DDevice9::SetFVF(DWORD FVF) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::GetFVF(DWORD* pFVF) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + + +#ifdef OSX + +#pragma mark ----- Vertex Buffers and Streams - (IDirect3DDevice9) + +#pragma mark ----- Create function moved to be adjacent to other buffer methods + +#endif + +HRESULT IDirect3DDevice9::SetStreamSourceNonInline(UINT StreamNumber,IDirect3DVertexBuffer9* pStreamData,UINT OffsetInBytes,UINT Stride) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + Assert( StreamNumber < D3D_MAX_STREAMS ); + Assert( ( Stride & 3 ) == 0 ); // we support non-DWORD aligned strides, but on some drivers (like AMD's) perf goes off a cliff + + // perfectly legal to see a vertex buffer of NULL get passed in here. + // so we need an array to track these. + // OK, we are being given the stride, we don't need to calc it.. + + GLMPRINTF(("-X- IDirect3DDevice9::SetStreamSource setting stream #%d to D3D buf %p (GL name %d); offset %d, stride %d", StreamNumber, pStreamData, (pStreamData) ? pStreamData->m_vtxBuffer->m_nHandle: -1, OffsetInBytes, Stride)); + + if ( !pStreamData ) + { + OffsetInBytes = 0; + Stride = 0; + + m_vtx_buffers[ StreamNumber ] = m_pDummy_vtx_buffer; + } + else + { + // We do not support strides of 0 + Assert( Stride > 0 ); + m_vtx_buffers[ StreamNumber ] = pStreamData->m_vtxBuffer; + } + + m_streams[ StreamNumber ].m_vtxBuffer = pStreamData; + m_streams[ StreamNumber ].m_offset = OffsetInBytes; + m_streams[ StreamNumber ].m_stride = Stride; + + return S_OK; +} + +#ifdef OSX + +#pragma mark ----- Index Buffers - (IDirect3DDevice9) +#pragma mark ----- Creatue function relocated to be adjacent to the rest of the index buffer methods + +#endif + +HRESULT IDirect3DDevice9::SetIndicesNonInline(IDirect3DIndexBuffer9* pIndexData) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // just latch it. + m_indices.m_idxBuffer = pIndexData; + + return S_OK; +} + + +#ifdef OSX + +#pragma mark ----- Release Handlers - (IDirect3DDevice9) + +#endif + +void IDirect3DDevice9::ReleasedVertexDeclaration( IDirect3DVertexDeclaration9 *pDecl ) +{ + m_ctx->ClearCurAttribs(); + + Assert( m_ObjectStats.m_nTotalVertexDecls >= 1 ); + m_ObjectStats.m_nTotalVertexDecls--; +} + +void IDirect3DDevice9::ReleasedTexture( IDirect3DBaseTexture9 *baseTex ) +{ + GL_BATCH_PERF_CALL_TIMER; + TOGL_NULL_DEVICE_CHECK_RET_VOID; + + // see if this texture is referenced in any of the texture units and scrub it if so. + for( int i=0; i< GLM_SAMPLER_COUNT; i++) + { + if (m_textures[i] == baseTex) + { + m_textures[i] = NULL; + m_ctx->SetSamplerTex( i, NULL ); // texture sets go straight through to GLM, no dirty bit + } + } +} + +void IDirect3DDevice9::ReleasedCGLMTex( CGLMTex *pTex) +{ + GL_BATCH_PERF_CALL_TIMER; + TOGL_NULL_DEVICE_CHECK_RET_VOID; + + ScrubFBOMap( pTex ); + if ( pTex->m_layout ) + { + if ( pTex->m_layout->m_key.m_texFlags & kGLMTexRenderable ) + { + Assert( m_ObjectStats.m_nTotalRenderTargets >= 1 ); + m_ObjectStats.m_nTotalRenderTargets--; + } + } +} +void IDirect3DDevice9::ReleasedSurface( IDirect3DSurface9 *pSurface ) +{ + for( int i = 0; i < 4; i++ ) + { + if ( m_pRenderTargets[i] == pSurface ) + { + // this was a surprise release... scrub it + m_pRenderTargets[i] = NULL; + m_bFBODirty = true; + GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pRenderTargets[%d]", pSurface, i )); + } + } + + if ( m_pDepthStencil == pSurface ) + { + m_pDepthStencil = NULL; + m_bFBODirty = true; + GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pDepthStencil", pSurface )); + } + + if ( m_pDefaultColorSurface == pSurface ) + { + m_pDefaultColorSurface = NULL; + GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pDefaultColorSurface", pSurface )); + } + + if ( m_pDefaultDepthStencilSurface == pSurface ) + { + m_pDefaultDepthStencilSurface = NULL; + GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pDefaultDepthStencilSurface", pSurface )); + } + + Assert( m_ObjectStats.m_nTotalSurfaces >= 1 ); + m_ObjectStats.m_nTotalSurfaces--; +} + +void IDirect3DDevice9::ReleasedPixelShader( IDirect3DPixelShader9 *pixelShader ) +{ + if ( m_pixelShader == pixelShader ) + { + m_pixelShader = NULL; + GLMPRINTF(( "-A- Scrubbed pixel shader %08x from m_pixelShader", pixelShader )); + } + m_ctx->ReleasedShader(); + + Assert( m_ObjectStats.m_nTotalPixelShaders >= 1 ); + m_ObjectStats.m_nTotalPixelShaders--; +} + +void IDirect3DDevice9::ReleasedVertexShader( IDirect3DVertexShader9 *vertexShader ) +{ + if ( m_vertexShader == vertexShader ) + { + m_vertexShader = NULL; + GLMPRINTF(( "-A- Scrubbed vertex shader %08x from m_vertexShader", vertexShader )); + } + m_ctx->ClearCurAttribs(); + m_ctx->ReleasedShader(); + + Assert( m_ObjectStats.m_nTotalVertexShaders >= 1 ); + m_ObjectStats.m_nTotalVertexShaders--; +} + +void IDirect3DDevice9::ReleasedVertexBuffer( IDirect3DVertexBuffer9 *vertexBuffer ) +{ + for (int i=0; i< D3D_MAX_STREAMS; i++) + { + if ( m_streams[i].m_vtxBuffer == vertexBuffer ) + { + m_streams[i].m_vtxBuffer = NULL; + m_vtx_buffers[i] = m_pDummy_vtx_buffer; + + GLMPRINTF(( "-A- Scrubbed vertex buffer %08x from m_streams[%d]", vertexBuffer, i )); + } + } + m_ctx->ClearCurAttribs(); + + Assert( m_ObjectStats.m_nTotalVertexBuffers >= 1 ); + m_ObjectStats.m_nTotalVertexBuffers--; +} + +void IDirect3DDevice9::ReleasedIndexBuffer( IDirect3DIndexBuffer9 *indexBuffer ) +{ + if ( m_indices.m_idxBuffer == indexBuffer ) + { + m_indices.m_idxBuffer = NULL; + GLMPRINTF(( "-A- Scrubbed index buffer %08x from m_indices", indexBuffer )); + } + + Assert( m_ObjectStats.m_nTotalIndexBuffers >= 1 ); + m_ObjectStats.m_nTotalIndexBuffers--; +} + + +void IDirect3DDevice9::ReleasedQuery( IDirect3DQuery9 *query ) +{ + Assert( m_ObjectStats.m_nTotalQueries >= 1 ); + m_ObjectStats.m_nTotalQueries--; +} + +#ifdef OSX + +#pragma mark ----- Queries - (IDirect3DDevice9) + +#endif + +// note that detection of whether queries are supported is done by trying to create one. +// so for GL, be observant here of whether we have that capability or not. +// pretty much have this everywhere but i950. + +HRESULT IDirect3DDevice9::CreateQuery(D3DQUERYTYPE Type,IDirect3DQuery9** ppQuery) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + if (m_ctx->Caps().m_hasOcclusionQuery) + { + m_ObjectStats.m_nTotalQueries++; + + IDirect3DQuery9 *newquery = new IDirect3DQuery9; + + newquery->m_device = this; + + newquery->m_type = Type; + newquery->m_ctx = m_ctx; + newquery->m_nIssueStartThreadID = 0; + newquery->m_nIssueEndThreadID = 0; + newquery->m_nIssueStartDrawCallIndex = 0; + newquery->m_nIssueEndDrawCallIndex = 0; + + GLMQueryParams params; + memset( ¶ms, 0, sizeof(params) ); + + //bool known = false; + switch(newquery->m_type) + { + case D3DQUERYTYPE_OCCLUSION: /* D3DISSUE_BEGIN, D3DISSUE_END */ + // create an occlusion query + params.m_type = EOcclusion; + break; + + case D3DQUERYTYPE_EVENT: /* D3DISSUE_END */ + params.m_type = EFence; + break; + + case D3DQUERYTYPE_RESOURCEMANAGER: /* D3DISSUE_END */ + case D3DQUERYTYPE_TIMESTAMP: /* D3DISSUE_END */ + case D3DQUERYTYPE_TIMESTAMPFREQ: /* D3DISSUE_END */ + case D3DQUERYTYPE_INTERFACETIMINGS: /* D3DISSUE_BEGIN, D3DISSUE_END */ + case D3DQUERYTYPE_PIXELTIMINGS: /* D3DISSUE_BEGIN, D3DISSUE_END */ + case D3DQUERYTYPE_CACHEUTILIZATION: /* D3DISSUE_BEGIN, D3DISSUE_END */ + Assert( !"Un-implemented query type" ); + break; + + default: + Assert( !"Unknown query type" ); + break; + } + newquery->m_query = m_ctx->NewQuery( ¶ms ); + + *ppQuery = newquery; + return S_OK; + } + else + { + *ppQuery = NULL; + return -1; // failed + } + +} + +IDirect3DQuery9::~IDirect3DQuery9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF((">-A- ~IDirect3DQuery9")); + + if (m_device) + { + m_device->ReleasedQuery( this ); + + if (m_query) + { + GLMPRINTF((">-A- ~IDirect3DQuery9 freeing m_query")); + + m_query->m_ctx->DelQuery( m_query ); + m_query = NULL; + + GLMPRINTF(("<-A- ~IDirect3DQuery9 freeing m_query done")); + } + m_device = NULL; + } + + GLMPRINTF(("<-A- ~IDirect3DQuery9")); +} + +#ifdef OSX + +#pragma mark ----- Render States - (IDirect3DDevice9) + +#endif + +#define D3DRS_VALUE_LIMIT 210 + +struct D3D_RSINFO +{ + int m_class; + D3DRENDERSTATETYPE m_state; + DWORD m_defval; + // m_class runs 0-3. + // 3 = must implement - fully general - "obey" + // 2 = implement setup to the default value (it has a GL effect but does not change later) "obey once" + // 1 = "fake implement" setup to the default value no GL effect, debug break if anything but default value comes through - "ignore" + // 0 = game never ever sets this one, break if someone even tries. "complain" +}; + +D3D_RSINFO g_D3DRS_INFO_unpacked[ D3DRS_VALUE_LIMIT+1 ]; + +#ifdef D3D_RSI + #error macro collision... rename this +#else + #define D3D_RSI(nclass,nstate,ndefval) { nclass, nstate, ndefval } +#endif + +// FP conversions to hex courtesy of http://babbage.cs.qc.cuny.edu/IEEE-754/Decimal.html +#define CONST_DZERO 0x00000000 +#define CONST_DONE 0x3F800000 +#define CONST_D64 0x42800000 +#define DONT_KNOW_YET 0x31415926 + + +// see http://www.toymaker.info/Games/html/render_states.html + +D3D_RSINFO g_D3DRS_INFO_packed[] = +{ + // these do not have to be in any particular order. they get unpacked into the empty array above for direct indexing. + + D3D_RSI( 3, D3DRS_ZENABLE, DONT_KNOW_YET ), // enable Z test (or W buffering) + D3D_RSI( 3, D3DRS_ZWRITEENABLE, DONT_KNOW_YET ), // enable Z write + D3D_RSI( 3, D3DRS_ZFUNC, DONT_KNOW_YET ), // select Z func + + D3D_RSI( 3, D3DRS_COLORWRITEENABLE, TRUE ), // see transitiontable.cpp "APPLY_RENDER_STATE_FUNC( D3DRS_COLORWRITEENABLE, ColorWriteEnable )" + + D3D_RSI( 3, D3DRS_CULLMODE, D3DCULL_CCW ), // backface cull control + + D3D_RSI( 3, D3DRS_ALPHABLENDENABLE, DONT_KNOW_YET ), // ->CTransitionTable::ApplySeparateAlphaBlend and ApplyAlphaBlend + D3D_RSI( 3, D3DRS_BLENDOP, D3DBLENDOP_ADD ), + D3D_RSI( 3, D3DRS_SRCBLEND, DONT_KNOW_YET ), + D3D_RSI( 3, D3DRS_DESTBLEND, DONT_KNOW_YET ), + + D3D_RSI( 1, D3DRS_SEPARATEALPHABLENDENABLE, FALSE ), // hit in CTransitionTable::ApplySeparateAlphaBlend + D3D_RSI( 1, D3DRS_SRCBLENDALPHA, D3DBLEND_ONE ), // going to demote these to class 1 until I figure out if they are implementable + D3D_RSI( 1, D3DRS_DESTBLENDALPHA, D3DBLEND_ZERO ), + D3D_RSI( 1, D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD ), + + // what is the deal with alpha test... looks like it is inited to off. + D3D_RSI( 3, D3DRS_ALPHATESTENABLE, 0 ), + D3D_RSI( 3, D3DRS_ALPHAREF, 0 ), + D3D_RSI( 3, D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL ), + + D3D_RSI( 3, D3DRS_STENCILENABLE, FALSE ), + D3D_RSI( 3, D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_STENCILPASS, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_STENCILFUNC, D3DCMP_ALWAYS ), + D3D_RSI( 3, D3DRS_STENCILREF, 0 ), + D3D_RSI( 3, D3DRS_STENCILMASK, 0xFFFFFFFF ), + D3D_RSI( 3, D3DRS_STENCILWRITEMASK, 0xFFFFFFFF ), + + D3D_RSI( 3, D3DRS_TWOSIDEDSTENCILMODE, FALSE ), + D3D_RSI( 3, D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_CCW_STENCILPASS, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS ), + + D3D_RSI( 3, D3DRS_FOGENABLE, FALSE ), // see CShaderAPIDx8::FogMode and friends - be ready to do the ARB fog linear option madness + D3D_RSI( 3, D3DRS_FOGCOLOR, 0 ), + D3D_RSI( 3, D3DRS_FOGTABLEMODE, D3DFOG_NONE ), + D3D_RSI( 3, D3DRS_FOGSTART, CONST_DZERO ), + D3D_RSI( 3, D3DRS_FOGEND, CONST_DONE ), + D3D_RSI( 3, D3DRS_FOGDENSITY, CONST_DZERO ), + D3D_RSI( 3, D3DRS_RANGEFOGENABLE, FALSE ), + D3D_RSI( 3, D3DRS_FOGVERTEXMODE, D3DFOG_NONE ), // watch out for CShaderAPIDx8::CommitPerPassFogMode.... + + D3D_RSI( 3, D3DRS_MULTISAMPLEANTIALIAS, TRUE ), + D3D_RSI( 3, D3DRS_MULTISAMPLEMASK, 0xFFFFFFFF ), + + D3D_RSI( 3, D3DRS_SCISSORTESTENABLE, FALSE ), // heed IDirect3DDevice9::SetScissorRect + + D3D_RSI( 3, D3DRS_DEPTHBIAS, CONST_DZERO ), + D3D_RSI( 3, D3DRS_SLOPESCALEDEPTHBIAS, CONST_DZERO ), + + D3D_RSI( 3, D3DRS_COLORWRITEENABLE1, 0x0000000f ), + D3D_RSI( 3, D3DRS_COLORWRITEENABLE2, 0x0000000f ), + D3D_RSI( 3, D3DRS_COLORWRITEENABLE3, 0x0000000f ), + + D3D_RSI( 3, D3DRS_SRGBWRITEENABLE, 0 ), // heeded but ignored.. + + D3D_RSI( 2, D3DRS_CLIPPING, TRUE ), // um, yeah, clipping is enabled (?) + D3D_RSI( 3, D3DRS_CLIPPLANEENABLE, 0 ), // mask 1<m_class >= 0; packed++ ) + { + if ( (packed->m_state <0) || (packed->m_state >= D3DRS_VALUE_LIMIT) ) + { + // bad + DXABSTRACT_BREAK_ON_ERROR(); + } + else + { + // dispatch it to the unpacked array + g_D3DRS_INFO_unpacked[ packed->m_state ] = *packed; + } + } +} + +// convenience functions + +#ifdef OSX + +#pragma mark ----- Sampler States - (IDirect3DDevice9) + +#endif + +void IDirect3DDevice9::FlushClipPlaneEquation() +{ + for( int x=0; xCaps().m_hasNativeClipVertexMode ) + { + // hacked coeffs = { src->x, -src->y, 0.5f * src->z, src->w + (0.5f * src->z) }; + // Antonio's trick - so we can use gl_Position as the clippee, not gl_ClipVertex. + + GLClipPlaneEquation_t *equ = &gl.m_ClipPlaneEquation[x]; + + ///////////////// temp1 + temp1.x = equ->x; + temp1.y = equ->y * -1.0; + temp1.z = equ->z * 0.5; + temp1.w = equ->w + (equ->z * 0.5); + + + //////////////// temp2 + VMatrix mat1( 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 2, -1, + 0, 0, 0, 1 + ); + //mat1 = mat1.Transpose(); + + VMatrix mat2; + bool success = mat1.InverseGeneral( mat2 ); + + if (success) + { + VMatrix mat3; + mat3 = mat2.Transpose(); + + VPlane origPlane( Vector( equ->x, equ->y, equ->z ), equ->w ); + VPlane newPlane; + + newPlane = mat3 * origPlane /* * mat3 */; + + VPlane finalPlane = newPlane; + + temp2.x = newPlane.m_Normal.x; + temp2.y = newPlane.m_Normal.y; + temp2.z = newPlane.m_Normal.z; + temp2.w = newPlane.m_Dist; + } + else + { + temp2.x = 0; + temp2.y = 0; + temp2.z = 0; + temp2.w = 0; + } + } + else + { + temp1 = temp2 = gl.m_ClipPlaneEquation[x]; + } + + if (1) //GLMKnob("caps-key",NULL)==0.0) + { + m_ctx->WriteClipPlaneEquation( &temp1, x ); // no caps lock = Antonio or classic + + /* + if (x<1) + { + GLMPRINTF(( " plane %d √vers1[ %5.2f %5.2f %5.2f %5.2f ] vers2[ %5.2f %5.2f %5.2f %5.2f ]", + x, + temp1.x,temp1.y,temp1.z,temp1.w, + temp2.x,temp2.y,temp2.z,temp2.w + )); + } + */ + } + else + { + m_ctx->WriteClipPlaneEquation( &temp2, x ); // caps = our way or classic + + /* + if (x<1) + { + GLMPRINTF(( " plane %d vers1[ %5.2f %5.2f %5.2f %5.2f ] √vers2[ %5.2f %5.2f %5.2f %5.2f ]", + x, + temp1.x,temp1.y,temp1.z,temp1.w, + temp2.x,temp2.y,temp2.z,temp2.w + )); + } + */ + } + } +} + +void IDirect3DDevice9::InitStates() +{ + m_ctx->m_AlphaTestEnable.Read( &gl.m_AlphaTestEnable, 0 ); + m_ctx->m_AlphaTestFunc.Read( &gl.m_AlphaTestFunc, 0 ); + m_ctx->m_CullFaceEnable.Read( &gl.m_CullFaceEnable, 0 ); + m_ctx->m_DepthBias.Read( &gl.m_DepthBias, 0 ); + m_ctx->m_ScissorEnable.Read( &gl.m_ScissorEnable, 0 ); + m_ctx->m_ScissorBox.Read( &gl.m_ScissorBox, 0 ); + m_ctx->m_ViewportBox.Read( &gl.m_ViewportBox, 0 ); + m_ctx->m_ViewportDepthRange.Read( &gl.m_ViewportDepthRange, 0 ); + + for( int x=0; xm_ClipPlaneEnable.ReadIndex( &gl.m_ClipPlaneEnable[x], x, 0 ); + + m_ctx->m_PolygonMode.Read( &gl.m_PolygonMode, 0 ); + m_ctx->m_CullFrontFace.Read( &gl.m_CullFrontFace, 0 ); + m_ctx->m_AlphaToCoverageEnable.Read( &gl.m_AlphaToCoverageEnable, 0 ); + m_ctx->m_BlendEquation.Read( &gl.m_BlendEquation, 0 ); + m_ctx->m_BlendColor.Read( &gl.m_BlendColor, 0 ); + + for( int x=0; xm_ClipPlaneEquation.ReadIndex( &gl.m_ClipPlaneEquation[x], x, 0 ); + + m_ctx->m_ColorMaskSingle.Read( &gl.m_ColorMaskSingle, 0 ); + + m_ctx->m_BlendEnable.Read( &gl.m_BlendEnable, 0 ); + m_ctx->m_BlendFactor.Read( &gl.m_BlendFactor, 0 ); + m_ctx->m_BlendEnableSRGB.Read( &gl.m_BlendEnableSRGB, 0 ); + m_ctx->m_DepthTestEnable.Read( &gl.m_DepthTestEnable, 0 ); + m_ctx->m_DepthFunc.Read( &gl.m_DepthFunc, 0 ); + m_ctx->m_DepthMask.Read( &gl.m_DepthMask, 0 ); + m_ctx->m_StencilTestEnable.Read( &gl.m_StencilTestEnable, 0 ); + m_ctx->m_StencilFunc.Read( &gl.m_StencilFunc, 0 ); + + m_ctx->m_StencilOp.ReadIndex( &gl.m_StencilOp, 0, 0 ); + m_ctx->m_StencilOp.ReadIndex( &gl.m_StencilOp, 1, 0 ); + + m_ctx->m_StencilWriteMask.Read( &gl.m_StencilWriteMask, 0 ); + m_ctx->m_ClearColor.Read( &gl.m_ClearColor, 0 ); + m_ctx->m_ClearDepth.Read( &gl.m_ClearDepth, 0 ); + m_ctx->m_ClearStencil.Read( &gl.m_ClearStencil, 0 ); +} + +void IDirect3DDevice9::FullFlushStates() +{ + m_ctx->WriteAlphaTestEnable( &gl.m_AlphaTestEnable ); + m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteDepthBias( &gl.m_DepthBias ); + m_ctx->WriteScissorEnable( &gl.m_ScissorEnable ); + m_ctx->WriteScissorBox( &gl.m_ScissorBox ); + m_ctx->WriteViewportBox( &gl.m_ViewportBox ); + m_ctx->WriteViewportDepthRange( &gl.m_ViewportDepthRange ); + + for( int x=0; xWriteClipPlaneEnable( &gl.m_ClipPlaneEnable[x], x ); + + m_ctx->WritePolygonMode( &gl.m_PolygonMode ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + m_ctx->WriteAlphaToCoverageEnable( &gl.m_AlphaToCoverageEnable ); + m_ctx->WriteBlendEquation( &gl.m_BlendEquation ); + m_ctx->WriteBlendColor( &gl.m_BlendColor ); + FlushClipPlaneEquation(); + m_ctx->WriteColorMaskSingle( &gl.m_ColorMaskSingle ); + + m_ctx->WriteBlendEnable( &gl.m_BlendEnable ); + m_ctx->WriteBlendFactor( &gl.m_BlendFactor ); + m_ctx->WriteBlendEnableSRGB( &gl.m_BlendEnableSRGB ); + m_ctx->WriteDepthTestEnable( &gl.m_DepthTestEnable ); + m_ctx->WriteDepthFunc( &gl.m_DepthFunc ); + m_ctx->WriteDepthMask( &gl.m_DepthMask ); + m_ctx->WriteStencilTestEnable( &gl.m_StencilTestEnable ); + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + + m_ctx->WriteStencilWriteMask( &gl.m_StencilWriteMask ); + m_ctx->WriteClearColor( &gl.m_ClearColor ); + m_ctx->WriteClearDepth( &gl.m_ClearDepth ); + m_ctx->WriteClearStencil( &gl.m_ClearStencil ); +} + +HRESULT IDirect3DDevice9::DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType,UINT StartVertex,UINT PrimitiveCount) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +// 7LS - TODO +#ifndef DX_TO_GL_ABSTRACTION +HRESULT IDirect3DDevice9::DrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType,UINT PrimitiveCountx,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) +{ +// 7LS + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} +#endif + +// Type +// [in] Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method. See Remarks. + +// BaseVertexIndex +// [in] Offset from the start of the vertex buffer to the first vertex. See Scenario 4. + +// MinIndex +// [in] Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex. + +// NumVertices +// [in] Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex. + +// StartIndex +// [in] Index of the first index to use when accessing the index buffer. + +// PrimitiveCount +// [in] Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type. The maximum number of primitives allowed is determined by checking the MaxPrimitiveCount member of the D3DCAPS9 structure. + +// BE VERY CAREFUL what you do in this function. It's extremely hot, and calling the wrong GL API's in here will crush perf. on NVidia threaded drivers. +#ifndef OSX + +HRESULT IDirect3DDevice9::DrawIndexedPrimitive( D3DPRIMITIVETYPE Type, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount ) +{ + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + TOGL_NULL_DEVICE_CHECK; + if ( m_bFBODirty ) + { + UpdateBoundFBO(); + } + + g_nTotalDrawsOrClears++; + +#if GL_BATCH_PERF_ANALYSIS + m_nTotalPrims += primCount; + CFastTimer tm; + CFlushDrawStatesStats& flushStats = m_ctx->m_FlushStats; + tm.Start(); + flushStats.Clear(); +#endif + +#if GLMDEBUG + if ( gl.m_FogEnable ) + { + GLMPRINTF(("-D- IDirect3DDevice9::DrawIndexedPrimitive is seeing enabled fog...")); + } +#endif + + if ( ( !m_indices.m_idxBuffer ) || ( !m_vertexShader ) ) + goto draw_failed; + + { + GL_BATCH_PERF_CALL_TIMER; + + m_ctx->FlushDrawStates( MinVertexIndex, MinVertexIndex + NumVertices - 1, BaseVertexIndex ); + + { +#if !GL_TELEMETRY_ZONES && GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "glDrawRangeElements %u", primCount ); +#endif + Assert( ( D3DPT_LINELIST == 2 ) && ( D3DPT_TRIANGLELIST == 4 ) && ( D3DPT_TRIANGLESTRIP == 5 ) ); + + static const struct prim_t + { + GLenum m_nType; + uint m_nPrimMul; + uint m_nPrimAdd; + } s_primTypes[6] = + { + { 0, 0, 0 }, // 0 + { 0, 0, 0 }, // 1 + { GL_LINES, 2, 0 }, // 2 D3DPT_LINELIST + { 0, 0, 0 }, // 3 + { GL_TRIANGLES, 3, 0 }, // 4 D3DPT_TRIANGLELIST + { GL_TRIANGLE_STRIP, 1, 2 } // 5 D3DPT_TRIANGLESTRIP + }; + + if ( Type <= D3DPT_TRIANGLESTRIP ) + { + const prim_t& p = s_primTypes[Type]; + Assert( p.m_nType ); + Assert( NumVertices >= 1 ); + + m_ctx->DrawRangeElements( p.m_nType, (GLuint)MinVertexIndex, (GLuint)( MinVertexIndex + NumVertices - 1 ), (GLsizei)p.m_nPrimAdd + primCount * p.m_nPrimMul, (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)( startIndex * sizeof(short) ), BaseVertexIndex, m_indices.m_idxBuffer->m_idxBuffer ); + } + } + } + +#if GL_BATCH_PERF_ANALYSIS + if ( s_rdtsc_to_ms == 0.0f ) + { + TmU64 t0 = Plat_Rdtsc(); + double d0 = Plat_FloatTime(); + + ThreadSleep( 1000 ); + + TmU64 t1 = Plat_Rdtsc(); + double d1 = Plat_FloatTime(); + + s_rdtsc_to_ms = ( 1000.0f * ( d1 - d0 ) ) / ( t1 - t0 ); + } + +#if GL_BATCH_PERF_ANALYSIS_WRITE_PNGS + if ( m_pBatch_vis_bitmap && m_pBatch_vis_bitmap->is_valid() ) + { + double t = tm.GetDurationInProgress().GetMillisecondsF(); + + uint h = 1; + if ( gl_batch_vis_y_scale.GetFloat() > 0.0f) + { + h = ceil( t / gl_batch_vis_y_scale.GetFloat() ); + h = MAX(h, 1); + } + + // Total time spent inside any and all our "D3D9" calls + double flTotalD3DTime = g_nTotalD3DCycles * s_rdtsc_to_ms; + m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalD3DTime / gl_batch_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), h, 150, 150, 150); + + // Total total spent processing just DrawIndexedPrimitive() for this batch. + m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, (uint)(.5f + t / gl_batch_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), h, 70, 70, 70); + + double flTotalGLMS = gGL->m_nTotalGLCycles * s_rdtsc_to_ms; + + // Total time spent inside of all OpenGL calls + m_pBatch_vis_bitmap->additive_fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalGLMS / gl_batch_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), h, 0, 0, 64); + + if (flushStats.m_nNewVS) m_pBatch_vis_bitmap->additive_fill_box(80-16, m_nBatchVisY, 8, h, 0, 110, 0); + if (flushStats.m_nNewPS) m_pBatch_vis_bitmap->additive_fill_box(80-8, m_nBatchVisY, 8, h, 110, 0, 110); + + int lm = 80; + m_pBatch_vis_bitmap->fill_box(lm+0+flushStats.m_nFirstVSConstant, m_nBatchVisY, flushStats.m_nNumVSConstants, h, 64, 255, 255); + m_pBatch_vis_bitmap->fill_box(lm+64, m_nBatchVisY, flushStats.m_nNumVSBoneConstants, h, 255, 64, 64); + m_pBatch_vis_bitmap->fill_box(lm+64+256+flushStats.m_nFirstPSConstant, m_nBatchVisY, flushStats.m_nNumPSConstants, h, 64, 64, 255); + + m_pBatch_vis_bitmap->fill_box(lm+64+256+32, m_nBatchVisY, flushStats.m_nNumChangedSamplers, h, 255, 255, 255); + m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16, m_nBatchVisY, flushStats.m_nNumSamplingParamsChanged, h, 92, 128, 255); + + if ( flushStats.m_nVertexBufferChanged ) m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16+64, m_nBatchVisY, 16, h, 128, 128, 128); + if ( flushStats.m_nIndexBufferChanged ) m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16+64+16, m_nBatchVisY, 16, h, 128, 128, 255); + + m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16+64+16+16, m_nBatchVisY, ( ( g_nTotalVBLockBytes + g_nTotalIBLockBytes ) * 64 + 2047 ) / 2048, h, 120, 120, 120 ); + m_pBatch_vis_bitmap->additive_fill_box(lm+64+256+32+16+64+16+16, m_nBatchVisY, ( g_nTotalVBLockBytes * 64 + 2047 ) / 2048, h, 120, 0, 0); + + m_nBatchVisY += h; + } +#endif + + m_nNumProgramChanges += ((flushStats.m_nNewVS + flushStats.m_nNewPS) != 0); + + m_flTotalD3DTime += g_nTotalD3DCycles * s_rdtsc_to_ms; + m_nTotalD3DCalls += g_nTotalD3DCalls; + g_nTotalD3DCycles = 0; + g_nTotalD3DCalls = 0; + + m_flTotalGLTime += gGL->m_nTotalGLCycles * s_rdtsc_to_ms; + m_nTotalGLCalls += gGL->m_nTotalGLCalls; + gGL->m_nTotalGLCycles = 0; + gGL->m_nTotalGLCalls = 0; + + g_nTotalVBLockBytes = 0; + g_nTotalIBLockBytes = 0; +#endif + + return S_OK; + +draw_failed: + Assert( 0 ); + return E_FAIL; +} + +#else + +// OSX 10.6 support + +HRESULT IDirect3DDevice9::FlushIndexBindings( void ) +{ + // push index buffer state + m_ctx->SetIndexBuffer( m_indices.m_idxBuffer->m_idxBuffer ); + return S_OK; +} + +HRESULT IDirect3DDevice9::FlushVertexBindings( uint baseVertexIndex ) +{ + // push vertex buffer state for the current vertex decl + // in this variant we just walk the attrib map in the VS and do a pull for each one. + // if we can't find a match in the vertex decl, we may fall back to the secret 'dummy' VBO that GLM maintains + + GLMVertexSetup setup; + memset( &setup, 0, sizeof( setup ) ); + + IDirect3DVertexDeclaration9 *vxdecl = m_pVertDecl; + unsigned char *vshAttribMap = m_vertexShader->m_vtxAttribMap; + + // this loop could be tightened if we knew the number of live entries in the shader attrib map. + // which of course would be easy to do in the create shader function or even in the translator. + + GLMVertexAttributeDesc *dstAttr = setup.m_attrs; + for( int i=0; i<16; i++,dstAttr++ ) + { + unsigned char vshattrib = vshAttribMap[ i ]; + if (vshattrib != 0xBB) + { + // try to find the match in the decl. + // idea: put some inverse table in the decl which could accelerate this search. + + D3DVERTEXELEMENT9_GL *elem = m_pVertDecl->m_elements; + for( int j=0; j< m_pVertDecl->m_elemCount; j++,elem++) + { + // if it matches, install it, change vshattrib so the code below does not trigger, then end the loop + if ( ((vshattrib>>4) == elem->m_dxdecl.Usage) && ((vshattrib & 0x0F) == elem->m_dxdecl.UsageIndex) ) + { + // targeting attribute #i in the setup with element data #j from the decl + + *dstAttr = elem->m_gldecl; + + // then fix buffer, stride, offset - note that we honor the base vertex index here by fiddling the offset + int streamIndex = elem->m_dxdecl.Stream; + dstAttr->m_pBuffer = m_streams[ streamIndex ].m_vtxBuffer->m_vtxBuffer; + dstAttr->m_stride = m_streams[ streamIndex ].m_stride; + dstAttr->m_offset += m_streams[ streamIndex ].m_offset + (baseVertexIndex * dstAttr->m_stride); + + // set mask + setup.m_attrMask |= (1 << i); + + // end loop + vshattrib = 0xBB; + j = 999; + } + } + + // if vshattrib is not 0xBB here, that means we could not find a source in the decl for it + if (vshattrib != 0xBB) + { + // fill out attr the same way as usual, we just pass NULL for the buffer and ask GLM to have mercy on us + + dstAttr->m_pBuffer = NULL; + dstAttr->m_stride = 0; + dstAttr->m_offset = 0; + + // only implement certain usages... if we haven't seen it before, stop. + switch (vshattrib >> 4) // aka usage + { + case D3DDECLUSAGE_POSITION: + case D3DDECLUSAGE_BLENDWEIGHT: + case D3DDECLUSAGE_BLENDINDICES: + Debugger(); + break; + + case D3DDECLUSAGE_NORMAL: + dstAttr->m_nCompCount = 3; + dstAttr->m_datatype = GL_FLOAT; + dstAttr->m_normalized = false; + break; + + case D3DDECLUSAGE_PSIZE: + Debugger(); + break; + + case D3DDECLUSAGE_TEXCOORD: + dstAttr->m_nCompCount = 3; + dstAttr->m_datatype = GL_FLOAT; + dstAttr->m_normalized = false; + break; + + case D3DDECLUSAGE_TANGENT: + case D3DDECLUSAGE_BINORMAL: + case D3DDECLUSAGE_TESSFACTOR: + case D3DDECLUSAGE_PLUGH: + Debugger(); + break; + + case D3DDECLUSAGE_COLOR: + dstAttr->m_nCompCount = 4; + dstAttr->m_datatype = GL_UNSIGNED_BYTE; + dstAttr->m_normalized = true; + break; + + case D3DDECLUSAGE_FOG: + case D3DDECLUSAGE_DEPTH: + case D3DDECLUSAGE_SAMPLE: + Debugger(); + break; + } + } + } + } + + // copy active program's vertex attrib map into the vert setup info + memcpy(&setup.m_vtxAttribMap, m_vertexShader->m_vtxAttribMap, sizeof(m_vertexShader->m_vtxAttribMap)); + + m_ctx->SetVertexAttributes(&setup); + return S_OK; +} + + +// OSX path offering support for 10.6 (we do not have support for glDrawRangeElementsBaseVertex) +HRESULT IDirect3DDevice9::DrawIndexedPrimitive( D3DPRIMITIVETYPE Type,INT BaseVertexIndex,UINT MinVertexIndex,UINT NumVertices,UINT startIndex,UINT primCount ) +{ + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + TOGL_NULL_DEVICE_CHECK; + if ( m_bFBODirty ) + { + UpdateBoundFBO(); + } + + g_nTotalDrawsOrClears++; + +#if GL_BATCH_PERF_ANALYSIS + m_nTotalPrims += primCount; + CFastTimer tm; + CFlushDrawStatesStats& flushStats = m_ctx->m_FlushStats; + tm.Start(); + flushStats.Clear(); +#endif + +#if GLMDEBUG + if ( gl.m_FogEnable ) + { + GLMPRINTF(("-D- IDirect3DDevice9::DrawIndexedPrimitive is seeing enabled fog...")); + } +#endif + + if ( ( !m_indices.m_idxBuffer ) || ( !m_vertexShader ) ) + goto draw_failed; + + this->FlushIndexBindings( ); + this->FlushVertexBindings( BaseVertexIndex ); + m_ctx->FlushDrawStates( MinVertexIndex, MinVertexIndex + NumVertices - 1, 0 ); + + if (gl.m_FogEnable) + { + GLMPRINTF(("-D- IDirect3DDevice9::DrawIndexedPrimitive is seeing enabled fog...")); + } + + switch(Type) + { + case D3DPT_POINTLIST: + Debugger(); + break; + + case D3DPT_LINELIST: + GLMPRINTF(("-X- IDirect3DDevice9::DrawIndexedPrimitive( D3DPT_LINELIST ) - ignored.")); + // Debugger(); + m_ctx->DrawRangeElements( (GLenum)GL_LINES, (GLuint)MinVertexIndex, (GLuint)(MinVertexIndex + NumVertices), (GLsizei)primCount*2, (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)(startIndex * sizeof(short)), m_indices.m_idxBuffer->m_idxBuffer ); + break; + + case D3DPT_TRIANGLELIST: + m_ctx->DrawRangeElements(GL_TRIANGLES, (GLuint)MinVertexIndex, (GLuint)(MinVertexIndex + NumVertices), (GLsizei)primCount*3, (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)(startIndex * sizeof(short)), m_indices.m_idxBuffer->m_idxBuffer ); + break; + + case D3DPT_TRIANGLESTRIP: + // enabled... Debugger(); + m_ctx->DrawRangeElements(GL_TRIANGLE_STRIP, (GLuint)MinVertexIndex, (GLuint)(MinVertexIndex + NumVertices), (GLsizei)(2+primCount), (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)(startIndex * sizeof(short)), m_indices.m_idxBuffer->m_idxBuffer ); + break; + } + + return S_OK; + +draw_failed: + Assert( 0 ); + return E_FAIL; +} + +#endif // #ifndef OSX + +HRESULT IDirect3DDevice9::DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType,UINT MinVertexIndex,UINT NumVertices,UINT PrimitiveCount,CONST void* pIndexData,D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +BOOL IDirect3DDevice9::ShowCursor(BOOL bShow) +{ + // FIXME NOP + //DXABSTRACT_BREAK_ON_ERROR(); + return TRUE; +} + +void d3drect_to_glmbox( D3DRECT *src, GLScissorBox_t *dst ) +{ + // to convert from a d3d rect to a GL rect you have to fix up the vertical axis, since D3D Y=0 is the top, but GL Y=0 is the bottom. + // you can't fix it without knowing the height. + + dst->width = src->x2 - src->x1; + dst->x = src->x1; // left edge + + dst->height = src->y2 - src->y1; + dst->y = src->y1; // bottom edge - take large Y from d3d and subtract from surf height. +} + +HRESULT IDirect3DDevice9::Clear(DWORD Count,CONST D3DRECT* pRects,DWORD Flags,D3DCOLOR Color,float Z,DWORD Stencil) +{ + GL_BATCH_PERF_CALL_TIMER; + + if ( m_bFBODirty ) + { + UpdateBoundFBO(); + } + + g_nTotalDrawsOrClears++; + + m_ctx->FlushDrawStatesNoShaders(); + + + //debugging Color = (rand() | 0xFF0000FF) & 0xFF3F3FFF; + if (!Count) + { + // run clear with no added rectangle + m_ctx->Clear( (Flags&D3DCLEAR_TARGET)!=0, Color, + (Flags&D3DCLEAR_ZBUFFER)!=0, Z, + (Flags&D3DCLEAR_STENCIL)!=0, Stencil, + NULL + ); + } + else + { + GLScissorBox_t tempbox; + + // do the rects one by one and convert each one to GL form + for( uint i=0; iClear( (Flags&D3DCLEAR_TARGET)!=0, Color, + (Flags&D3DCLEAR_ZBUFFER)!=0, Z, + (Flags&D3DCLEAR_STENCIL)!=0, Stencil, + &tempbox + ); + } + } + + return S_OK; +} + +HRESULT IDirect3DDevice9::SetTransform(D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetTextureStageState(DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::ValidateDevice(DWORD* pNumPasses) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetMaterial(CONST D3DMATERIAL9* pMaterial) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + GLMPRINTF(("-X- IDirect3DDevice9::SetMaterial - ignored.")); +// DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + + +HRESULT IDirect3DDevice9::LightEnable(DWORD Index,BOOL Enable) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetScissorRect(CONST RECT* pRect) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + //int nSurfaceHeight = m_ctx->m_drawingFBO->m_attach[ kAttColor0 ].m_tex->m_layout->m_key.m_ySize; + + GLScissorBox_t newScissorBox = { pRect->left, pRect->top, pRect->right - pRect->left, pRect->bottom - pRect->top }; + gl.m_ScissorBox = newScissorBox; + m_ctx->WriteScissorBox( &gl.m_ScissorBox ); + return S_OK; +} + +HRESULT IDirect3DDevice9::GetDeviceCaps(D3DCAPS9* pCaps) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + // "Adapter" is used to index amongst the set of fake-adapters maintained in the display DB + GLMDisplayDB *db = GetDisplayDB(); + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + bool result = db->GetFakeAdapterInfo( m_params.m_adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; + Assert (!result); + // just leave glmRendererInfo filled out for subsequent code to look at as needed. + + FillD3DCaps9( glmRendererInfo, pCaps ); + + return S_OK; +} + + +HRESULT IDirect3DDevice9::TestCooperativeLevel() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // game calls this to see if device was lost. + // last I checked the device was still attached to the computer. + // so, return OK. + + return S_OK; +} + +HRESULT IDirect3DDevice9::SetClipPlane(DWORD Index,CONST float* pPlane) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + Assert(Index<2); + + // We actually push the clip plane coeffs to two places + // - into a shader param for ARB mode + // - and into the API defined clip plane slots for GLSL mode. + + // if ARB mode... THIS NEEDS TO GO... it's messing up the dirty ranges.. + { + // this->SetVertexShaderConstantF( DXABSTRACT_VS_CLIP_PLANE_BASE+Index, pPlane, 1 ); // stash the clip plane values into shader param - translator knows where to look + } + + // if GLSL mode... latch it and let FlushStates push it out + { + GLClipPlaneEquation_t peq; + peq.x = pPlane[0]; + peq.y = pPlane[1]; + peq.z = pPlane[2]; + peq.w = pPlane[3]; + + gl.m_ClipPlaneEquation[ Index ] = peq; + FlushClipPlaneEquation(); + + // m_ctx->WriteClipPlaneEquation( &peq, Index ); + } + + return S_OK; +} + +HRESULT IDirect3DDevice9::EvictManagedResources() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + GLMPRINTF(("-X- IDirect3DDevice9::EvictManagedResources --> IGNORED")); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetLight(DWORD Index,CONST D3DLIGHT9*) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +void IDirect3DDevice9::SetGammaRamp(UINT iSwapChain,DWORD Flags,CONST D3DGAMMARAMP* pRamp) +{ + GL_BATCH_PERF_CALL_TIMER; + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + + if ( g_pLauncherMgr ) + { + g_pLauncherMgr->SetGammaRamp( pRamp->red, pRamp->green, pRamp->blue ); + } +} + +void TOGLMETHODCALLTYPE IDirect3DDevice9::SaveGLState() +{ +} + +void TOGLMETHODCALLTYPE IDirect3DDevice9::RestoreGLState() +{ + m_ctx->ForceFlushStates(); + + m_bFBODirty = true; +} + +void IDirect3DDevice9::AcquireThreadOwnership( ) +{ + GL_BATCH_PERF_CALL_TIMER; + m_ctx->MakeCurrent( true ); +} + + +void IDirect3DDevice9::ReleaseThreadOwnership( ) +{ + GL_BATCH_PERF_CALL_TIMER; + m_ctx->ReleaseCurrent( true ); +} + +void IDirect3DDevice9::SetMaxUsedVertexShaderConstantsHintNonInline( uint nMaxReg ) +{ + GL_BATCH_PERF_CALL_TIMER; + m_ctx->SetMaxUsedVertexShaderConstantsHint( nMaxReg ); +} + +HRESULT IDirect3DDevice9::SetRenderState( D3DRENDERSTATETYPE State, DWORD Value ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + +#if GLMDEBUG + // None of this is needed normally. + char rsSpew = 1; + char ignored = 0; + + if (State >= D3DRS_VALUE_LIMIT) + { + DXABSTRACT_BREAK_ON_ERROR(); // bad + return S_OK; + } + + const D3D_RSINFO *info = &g_D3DRS_INFO_unpacked[ State ]; + if (info->m_state != State) + { + DXABSTRACT_BREAK_ON_ERROR(); // bad - we never set up that state in our list + return S_OK; + } + + if (rsSpew) + { + GLMPRINTF(("-X- IDirect3DDevice9::SetRenderState: set %s(%d) to %d(0x%08x) ( class %d, defval is %d(0x%08x) )", GLMDecode( eD3D_RSTATE,State),State, Value,Value, info->m_class, info->m_defval,info->m_defval )); + } + + switch( info->m_class ) + { + case 0: // just ignore quietly. example: D3DRS_LIGHTING + ignored = 1; + break; + + case 1: + { + // no GL response - and no error as long as the write value matches the default + if (Value != info->m_defval) + { + static char stop_here_1 = 0; + if (stop_here_1) + DXABSTRACT_BREAK_ON_ERROR(); + } + break; + } + + case 2: + { + // provide GL response, but only support known default value + if (Value != info->m_defval) + { + static char stop_here_2 = 0; + if (stop_here_2) + DXABSTRACT_BREAK_ON_ERROR(); + } + // fall through to mode 3 + } + case 3: + // normal case - the switch statement below will handle this + break; + } +#endif + + switch (State) + { + case D3DRS_ZENABLE: // kGLDepthTestEnable + { + gl.m_DepthTestEnable.enable = Value; + m_ctx->WriteDepthTestEnable( &gl.m_DepthTestEnable ); + break; + } + case D3DRS_ZWRITEENABLE: // kGLDepthMask + { + gl.m_DepthMask.mask = Value; + m_ctx->WriteDepthMask( &gl.m_DepthMask ); + break; + } + case D3DRS_ZFUNC: + { + // kGLDepthFunc + GLenum func = D3DCompareFuncToGL( Value ); + gl.m_DepthFunc.func = func; + m_ctx->WriteDepthFunc( &gl.m_DepthFunc ); + break; + } + + case D3DRS_COLORWRITEENABLE: // kGLColorMaskSingle + { + gl.m_ColorMaskSingle.r = ((Value & D3DCOLORWRITEENABLE_RED) != 0) ? 0xFF : 0x00; + gl.m_ColorMaskSingle.g = ((Value & D3DCOLORWRITEENABLE_GREEN)!= 0) ? 0xFF : 0x00; + gl.m_ColorMaskSingle.b = ((Value & D3DCOLORWRITEENABLE_BLUE) != 0) ? 0xFF : 0x00; + gl.m_ColorMaskSingle.a = ((Value & D3DCOLORWRITEENABLE_ALPHA)!= 0) ? 0xFF : 0x00; + m_ctx->WriteColorMaskSingle( &gl.m_ColorMaskSingle ); + break; + } + + case D3DRS_CULLMODE: // kGLCullFaceEnable / kGLCullFrontFace + { + switch (Value) + { + case D3DCULL_NONE: + { + gl.m_CullFaceEnable.enable = false; + gl.m_CullFrontFace.value = GL_CCW; //doesn't matter + + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + break; + } + + case D3DCULL_CW: + { + gl.m_CullFaceEnable.enable = true; + gl.m_CullFrontFace.value = GL_CW; //origGL_CCW; + + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + break; + } + case D3DCULL_CCW: + { + gl.m_CullFaceEnable.enable = true; + gl.m_CullFrontFace.value = GL_CCW; //origGL_CW; + + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + break; + } + default: + { + DXABSTRACT_BREAK_ON_ERROR(); + break; + } + } + break; + } + + //-------------------------------------------------------------------------------------------- alphablend stuff + + case D3DRS_ALPHABLENDENABLE: // kGLBlendEnable + { + gl.m_BlendEnable.enable = Value; + m_ctx->WriteBlendEnable( &gl.m_BlendEnable ); + break; + } + + case D3DRS_BLENDOP: // kGLBlendEquation // D3D blend-op ==> GL blend equation + { + GLenum equation = D3DBlendOperationToGL( Value ); + gl.m_BlendEquation.equation = equation; + m_ctx->WriteBlendEquation( &gl.m_BlendEquation ); + break; + } + + case D3DRS_SRCBLEND: // kGLBlendFactor // D3D blend-factor ==> GL blend factor + case D3DRS_DESTBLEND: // kGLBlendFactor + { + GLenum factor = D3DBlendFactorToGL( Value ); + + if (State==D3DRS_SRCBLEND) + { + gl.m_BlendFactor.srcfactor = factor; + } + else + { + gl.m_BlendFactor.dstfactor = factor; + } + m_ctx->WriteBlendFactor( &gl.m_BlendFactor ); + break; + } + + case D3DRS_SRGBWRITEENABLE: // kGLBlendEnableSRGB + { + gl.m_BlendEnableSRGB.enable = Value; + m_ctx->WriteBlendEnableSRGB( &gl.m_BlendEnableSRGB ); + break; + } + + //-------------------------------------------------------------------------------------------- alphatest stuff + + case D3DRS_ALPHATESTENABLE: + { + gl.m_AlphaTestEnable.enable = Value; + m_ctx->WriteAlphaTestEnable( &gl.m_AlphaTestEnable ); + break; + } + + case D3DRS_ALPHAREF: + { + gl.m_AlphaTestFunc.ref = Value / 255.0f; + m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); + break; + } + + case D3DRS_ALPHAFUNC: + { + GLenum func = D3DCompareFuncToGL( Value );; + gl.m_AlphaTestFunc.func = func; + m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); + break; + } + + //-------------------------------------------------------------------------------------------- stencil stuff + + case D3DRS_STENCILENABLE: // GLStencilTestEnable_t + { + gl.m_StencilTestEnable.enable = Value; + m_ctx->WriteStencilTestEnable( &gl.m_StencilTestEnable ); + break; + } + + case D3DRS_STENCILFAIL: // GLStencilOp_t "what do you do if stencil test fails" + { + GLenum stencilop = D3DStencilOpToGL( Value ); + gl.m_StencilOp.sfail = stencilop; + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + break; + } + + case D3DRS_STENCILZFAIL: // GLStencilOp_t "what do you do if stencil test passes *but* depth test fails, if depth test happened" + { + GLenum stencilop = D3DStencilOpToGL( Value ); + gl.m_StencilOp.dpfail = stencilop; + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + break; + } + + case D3DRS_STENCILPASS: // GLStencilOp_t "what do you do if stencil test and depth test both pass" + { + GLenum stencilop = D3DStencilOpToGL( Value ); + gl.m_StencilOp.dppass = stencilop; + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + break; + } + + case D3DRS_STENCILFUNC: // GLStencilFunc_t + { + GLenum stencilfunc = D3DCompareFuncToGL( Value ); + gl.m_StencilFunc.frontfunc = gl.m_StencilFunc.backfunc = stencilfunc; + + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + break; + } + + case D3DRS_STENCILREF: // GLStencilFunc_t + { + gl.m_StencilFunc.ref = Value; + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + break; + } + + case D3DRS_STENCILMASK: // GLStencilFunc_t + { + gl.m_StencilFunc.mask = Value; + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + break; + } + + case D3DRS_STENCILWRITEMASK: // GLStencilWriteMask_t + { + gl.m_StencilWriteMask.mask = Value; + m_ctx->WriteStencilWriteMask( &gl.m_StencilWriteMask ); + break; + } + + case D3DRS_FOGENABLE: // none of these are implemented yet... erk + { + gl.m_FogEnable = (Value != 0); + GLMPRINTF(("-D- fogenable = %d",Value )); + break; + } + + case D3DRS_SCISSORTESTENABLE: // kGLScissorEnable + { + gl.m_ScissorEnable.enable = Value; + m_ctx->WriteScissorEnable( &gl.m_ScissorEnable ); + break; + } + + case D3DRS_DEPTHBIAS: // kGLDepthBias + { + // the value in the dword is actually a float + float fvalue = *(float*)&Value; + gl.m_DepthBias.units = fvalue; + + m_ctx->WriteDepthBias( &gl.m_DepthBias ); + break; + } + + // good ref on these: http://aras-p.info/blog/2008/06/12/depth-bias-and-the-power-of-deceiving-yourself/ + case D3DRS_SLOPESCALEDEPTHBIAS: + { + // the value in the dword is actually a float + float fvalue = *(float*)&Value; + gl.m_DepthBias.factor = fvalue; + + m_ctx->WriteDepthBias( &gl.m_DepthBias ); + break; + } + + // Alpha to coverage + case D3DRS_ADAPTIVETESS_Y: + { + gl.m_AlphaToCoverageEnable.enable = Value; + m_ctx->WriteAlphaToCoverageEnable( &gl.m_AlphaToCoverageEnable ); + break; + } + + case D3DRS_CLIPPLANEENABLE: // kGLClipPlaneEnable + { + // d3d packs all the enables into one word. + // we break that out so we don't do N glEnable calls to sync - + // GLM is tracking one unique enable per plane. + for( int i=0; iWriteClipPlaneEnable( &gl.m_ClipPlaneEnable[x], x ); + break; + } + + //-------------------------------------------------------------------------------------------- polygon/fill mode + + case D3DRS_FILLMODE: + { + GLuint mode = 0; + switch(Value) + { + case D3DFILL_POINT: mode = GL_POINT; break; + case D3DFILL_WIREFRAME: mode = GL_LINE; break; + case D3DFILL_SOLID: mode = GL_FILL; break; + default: DXABSTRACT_BREAK_ON_ERROR(); break; + } + gl.m_PolygonMode.values[0] = gl.m_PolygonMode.values[1] = mode; + m_ctx->WritePolygonMode( &gl.m_PolygonMode ); + break; + } + +#if GLMDEBUG + case D3DRS_MULTISAMPLEANTIALIAS: + case D3DRS_MULTISAMPLEMASK: + case D3DRS_FOGCOLOR: + case D3DRS_FOGTABLEMODE: + case D3DRS_FOGSTART: + case D3DRS_FOGEND: + case D3DRS_FOGDENSITY: + case D3DRS_RANGEFOGENABLE: + case D3DRS_FOGVERTEXMODE: + case D3DRS_COLORWRITEENABLE1: // kGLColorMaskMultiple + case D3DRS_COLORWRITEENABLE2: // kGLColorMaskMultiple + case D3DRS_COLORWRITEENABLE3: // kGLColorMaskMultiple + case D3DRS_SEPARATEALPHABLENDENABLE: + case D3DRS_BLENDOPALPHA: + case D3DRS_SRCBLENDALPHA: + case D3DRS_DESTBLENDALPHA: + case D3DRS_TWOSIDEDSTENCILMODE: // -> GL_STENCIL_TEST_TWO_SIDE_EXT... not yet implemented ? + case D3DRS_CCW_STENCILFAIL: // GLStencilOp_t + case D3DRS_CCW_STENCILZFAIL: // GLStencilOp_t + case D3DRS_CCW_STENCILPASS: // GLStencilOp_t + case D3DRS_CCW_STENCILFUNC: // GLStencilFunc_t + case D3DRS_CLIPPING: // ???? is clipping ever turned off ?? + case D3DRS_LASTPIXEL: + case D3DRS_DITHERENABLE: + case D3DRS_SHADEMODE: + default: + ignored = 1; + break; +#endif + } + +#if GLMDEBUG + if (rsSpew && ignored) + { + GLMPRINTF(("-X- (ignored)")); + } +#endif + + return S_OK; +} + +HRESULT IDirect3DDevice9::SetSamplerStateNonInline( DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + Assert( Sampler < GLM_SAMPLER_COUNT ); + + m_ctx->SetSamplerDirty( Sampler ); + + switch( Type ) + { + case D3DSAMP_ADDRESSU: + m_ctx->SetSamplerAddressU( Sampler, Value ); + break; + case D3DSAMP_ADDRESSV: + m_ctx->SetSamplerAddressV( Sampler, Value ); + break; + case D3DSAMP_ADDRESSW: + m_ctx->SetSamplerAddressW( Sampler, Value ); + break; + case D3DSAMP_BORDERCOLOR: + m_ctx->SetSamplerBorderColor( Sampler, Value ); + break; + case D3DSAMP_MAGFILTER: + m_ctx->SetSamplerMagFilter( Sampler, Value ); + break; + case D3DSAMP_MIPFILTER: + m_ctx->SetSamplerMipFilter( Sampler, Value ); + break; + case D3DSAMP_MINFILTER: + m_ctx->SetSamplerMinFilter( Sampler, Value ); + break; + case D3DSAMP_MIPMAPLODBIAS: + m_ctx->SetSamplerMipMapLODBias( Sampler, Value ); + break; + case D3DSAMP_MAXMIPLEVEL: + m_ctx->SetSamplerMaxMipLevel( Sampler, Value); + break; + case D3DSAMP_MAXANISOTROPY: + m_ctx->SetSamplerMaxAnisotropy( Sampler, Value); + break; + case D3DSAMP_SRGBTEXTURE: + //m_samplers[ Sampler ].m_srgb = Value; + m_ctx->SetSamplerSRGBTexture(Sampler, Value); + break; + case D3DSAMP_SHADOWFILTER: + m_ctx->SetShadowFilter(Sampler, Value); + break; + + default: DXABSTRACT_BREAK_ON_ERROR(); break; + } + + return S_OK; +} + +void IDirect3DDevice9::SetSamplerStatesNonInline( + DWORD Sampler, DWORD AddressU, DWORD AddressV, DWORD AddressW, + DWORD MinFilter, DWORD MagFilter, DWORD MipFilter, + DWORD MinLod, float LodBias) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + Assert( Sampler < GLM_SAMPLER_COUNT); + + m_ctx->SetSamplerDirty( Sampler ); + + m_ctx->SetSamplerStates( Sampler, AddressU, AddressV, AddressW, MinFilter, MagFilter, MipFilter, MinLod, LodBias ); +} + +HRESULT IDirect3DDevice9::SetTextureNonInline(DWORD Stage,IDirect3DBaseTexture9* pTexture) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + Assert( Stage < GLM_SAMPLER_COUNT ); + m_textures[Stage] = pTexture; + m_ctx->SetSamplerTex( Stage, pTexture ? pTexture->m_tex : NULL ); + return S_OK; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +void* ID3DXBuffer::GetBufferPointer() +{ + GL_BATCH_PERF_CALL_TIMER; + DXABSTRACT_BREAK_ON_ERROR(); + return NULL; +} + +DWORD ID3DXBuffer::GetBufferSize() +{ + GL_BATCH_PERF_CALL_TIMER; + DXABSTRACT_BREAK_ON_ERROR(); + return 0; +} + + + +#ifndef _MSC_VER +#pragma mark ----- More D3DX stuff +#endif + +// ------------------------------------------------------------------------------------------------------------------------------ // +// D3DX stuff. +// ------------------------------------------------------------------------------------------------------------------------------ // + +// matrix stack... + +HRESULT D3DXCreateMatrixStack( DWORD Flags, LPD3DXMATRIXSTACK* ppStack) +{ + + *ppStack = new ID3DXMatrixStack; + + (*ppStack)->Create(); + + return S_OK; +} + +ID3DXMatrixStack::ID3DXMatrixStack( void ) +{ + m_refcount[0] = 1; + m_refcount[1] = 0; +}; + +void ID3DXMatrixStack::AddRef( int which, char *comment ) +{ + Assert( which >= 0 ); + Assert( which < 2 ); + m_refcount[which]++; +}; + +ULONG ID3DXMatrixStack::Release( int which, char *comment ) +{ + Assert( which >= 0 ); + Assert( which < 2 ); + + bool deleting = false; + + m_refcount[which]--; + if ( (!m_refcount[0]) && (!m_refcount[1]) ) + { + deleting = true; + } + + if (deleting) + { + if (m_mark) + { + GLMPRINTF(("")) ; // place to hang a breakpoint + } + delete this; + return 0; + } + else + { + return m_refcount[0]; + } +}; + + +HRESULT ID3DXMatrixStack::Create() +{ + m_stack.EnsureCapacity( 16 ); // 1KB ish + m_stack.AddToTail(); + m_stackTop = 0; // top of stack is at index 0 currently + + LoadIdentity(); + + return S_OK; +} + +D3DXMATRIX* ID3DXMatrixStack::GetTop() +{ + return (D3DXMATRIX*)&m_stack[ m_stackTop ]; +} + +void ID3DXMatrixStack::Push() +{ + D3DMATRIX temp = m_stack[ m_stackTop ]; + m_stack.AddToTail( temp ); + m_stackTop ++; +} + +void ID3DXMatrixStack::Pop() +{ + int elem = m_stackTop--; + m_stack.Remove( elem ); +} + +void ID3DXMatrixStack::LoadIdentity() +{ + D3DXMATRIX *mat = GetTop(); + + D3DXMatrixIdentity( mat ); +} + +void ID3DXMatrixStack::LoadMatrix( const D3DXMATRIX *pMat ) +{ + *(GetTop()) = *pMat; +} + + +void ID3DXMatrixStack::MultMatrix( const D3DXMATRIX *pMat ) +{ + + // http://msdn.microsoft.com/en-us/library/bb174057(VS.85).aspx + // This method right-multiplies the given matrix to the current matrix + // (transformation is about the current world origin). + // m_pstack[m_currentPos] = m_pstack[m_currentPos] * (*pMat); + // This method does not add an item to the stack, it replaces the current + // matrix with the product of the current matrix and the given matrix. + + + DXABSTRACT_BREAK_ON_ERROR(); +} + +void ID3DXMatrixStack::MultMatrixLocal( const D3DXMATRIX *pMat ) +{ + // http://msdn.microsoft.com/en-us/library/bb174058(VS.85).aspx + // This method left-multiplies the given matrix to the current matrix + // (transformation is about the local origin of the object). + // m_pstack[m_currentPos] = (*pMat) * m_pstack[m_currentPos]; + // This method does not add an item to the stack, it replaces the current + // matrix with the product of the given matrix and the current matrix. + + + DXABSTRACT_BREAK_ON_ERROR(); +} + +HRESULT ID3DXMatrixStack::ScaleLocal(FLOAT x, FLOAT y, FLOAT z) +{ + // http://msdn.microsoft.com/en-us/library/bb174066(VS.85).aspx + // Scale the current matrix about the object origin. + // This method left-multiplies the current matrix with the computed + // scale matrix. The transformation is about the local origin of the object. + // + // D3DXMATRIX tmp; + // D3DXMatrixScaling(&tmp, x, y, z); + // m_stack[m_currentPos] = tmp * m_stack[m_currentPos]; + + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + + +HRESULT ID3DXMatrixStack::RotateAxisLocal(CONST D3DXVECTOR3* pV, FLOAT Angle) +{ + // http://msdn.microsoft.com/en-us/library/bb174062(VS.85).aspx + // Left multiply the current matrix with the computed rotation + // matrix, counterclockwise about the given axis with the given angle. + // (rotation is about the local origin of the object) + + // D3DXMATRIX tmp; + // D3DXMatrixRotationAxis( &tmp, pV, angle ); + // m_stack[m_currentPos] = tmp * m_stack[m_currentPos]; + // Because the rotation is left-multiplied to the matrix stack, the rotation + // is relative to the object's local coordinate space. + + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT ID3DXMatrixStack::TranslateLocal(FLOAT x, FLOAT y, FLOAT z) +{ + // http://msdn.microsoft.com/en-us/library/bb174068(VS.85).aspx + // Left multiply the current matrix with the computed translation + // matrix. (transformation is about the local origin of the object) + + // D3DXMATRIX tmp; + // D3DXMatrixTranslation( &tmp, x, y, z ); + // m_stack[m_currentPos] = tmp * m_stack[m_currentPos]; + + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + + + + +const char* D3DXGetPixelShaderProfile( IDirect3DDevice9 *pDevice ) +{ + DXABSTRACT_BREAK_ON_ERROR(); + return ""; +} + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable:4701) // potentially uninitialized local variable 'temp' used +#endif +D3DXMATRIX* D3DXMatrixMultiply( D3DXMATRIX *pOut, CONST D3DXMATRIX *pM1, CONST D3DXMATRIX *pM2 ) +{ + D3DXMATRIX temp; + + for( int i=0; i<4; i++) + { + for( int j=0; j<4; j++) + { + temp.m[i][j] = (pM1->m[ i ][ 0 ] * pM2->m[ 0 ][ j ]) + + (pM1->m[ i ][ 1 ] * pM2->m[ 1 ][ j ]) + + (pM1->m[ i ][ 2 ] * pM2->m[ 2 ][ j ]) + + (pM1->m[ i ][ 3 ] * pM2->m[ 3 ][ j ]); + } + } + *pOut = temp; + return pOut; +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// Transform a 3D vector by a given matrix, projecting the result back into w = 1 +// http://msdn.microsoft.com/en-us/library/ee417622(VS.85).aspx +D3DXVECTOR3* D3DXVec3TransformCoord(D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM) +{ + D3DXVECTOR3 vOut; + + float norm = (pM->m[0][3] * pV->x) + (pM->m[1][3] * pV->y) + (pM->m[2][3] *pV->z) + pM->m[3][3]; + if ( norm ) + { + float norm_inv = 1.0f / norm; + vOut.x = (pM->m[0][0] * pV->x + pM->m[1][0] * pV->y + pM->m[2][0] * pV->z + pM->m[3][0]) * norm_inv; + vOut.y = (pM->m[0][1] * pV->x + pM->m[1][1] * pV->y + pM->m[2][1] * pV->z + pM->m[3][1]) * norm_inv; + vOut.z = (pM->m[0][2] * pV->x + pM->m[1][2] * pV->y + pM->m[2][2] * pV->z + pM->m[3][2]) * norm_inv; + } + else + { + vOut.x = vOut.y = vOut.z = 0.0f; + } + + *pOut = vOut; + + return pOut; +} + + +void D3DXMatrixIdentity( D3DXMATRIX *mat ) +{ + for( int i=0; i<4; i++) + { + for( int j=0; j<4; j++) + { + mat->m[i][j] = (i==j) ? 1.0f : 0.0f; // 1's on the diagonal. + } + } +} + +D3DXMATRIX* D3DXMatrixTranslation( D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z ) +{ + D3DXMatrixIdentity( pOut ); + pOut->m[3][0] = x; + pOut->m[3][1] = y; + pOut->m[3][2] = z; + return pOut; +} + +D3DXMATRIX* D3DXMatrixInverse( D3DXMATRIX *pOut, FLOAT *pDeterminant, CONST D3DXMATRIX *pM ) +{ + Assert( sizeof( D3DXMATRIX ) == (16 * sizeof(float) ) ); + Assert( sizeof( VMatrix ) == (16 * sizeof(float) ) ); + Assert( pDeterminant == NULL ); // homey don't play that + + VMatrix *origM = (VMatrix*)pM; + VMatrix *destM = (VMatrix*)pOut; + + bool success = MatrixInverseGeneral( *origM, *destM ); (void)success; + Assert( success ); + + return pOut; +} + + +D3DXMATRIX* D3DXMatrixTranspose( D3DXMATRIX *pOut, CONST D3DXMATRIX *pM ) +{ + if (pOut != pM) + { + for( int i=0; i<4; i++) + { + for( int j=0; j<4; j++) + { + pOut->m[i][j] = pM->m[j][i]; + } + } + } + else + { + D3DXMATRIX temp = *pM; + D3DXMatrixTranspose( pOut, &temp ); + } + + return NULL; +} + + +D3DXPLANE* D3DXPlaneNormalize( D3DXPLANE *pOut, CONST D3DXPLANE *pP) +{ + // not very different from normalizing a vector. + // figure out the square root of the sum-of-squares of the x,y,z components + // make sure that's non zero + // then divide all four components by that value + // or return some dummy plane like 0,0,1,0 if it fails + + float len = sqrt( (pP->a * pP->a) + (pP->b * pP->b) + (pP->c * pP->c) ); + if (len > 1e-10) //FIXME need a real epsilon here ? + { + pOut->a = pP->a / len; pOut->b = pP->b / len; pOut->c = pP->c / len; pOut->d = pP->d / len; + } + else + { + pOut->a = 0.0f; pOut->b = 0.0f; pOut->c = 1.0f; pOut->d = 0.0f; + } + return pOut; +} + + +D3DXVECTOR4* D3DXVec4Transform( D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pV, CONST D3DXMATRIX *pM ) +{ + VMatrix *mat = (VMatrix*)pM; + Vector4D *vIn = (Vector4D*)pV; + Vector4D *vOut = (Vector4D*)pOut; + + Vector4DMultiplyTranspose( *mat, *vIn, *vOut ); + + return pOut; +} + + + +D3DXVECTOR4* D3DXVec4Normalize( D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pV ) +{ + Vector4D *vIn = (Vector4D*) pV; + Vector4D *vOut = (Vector4D*) pOut; + + *vOut = *vIn; + Vector4DNormalize( *vOut ); + + return pOut; +} + + +D3DXMATRIX* D3DXMatrixOrthoOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn,FLOAT zf ) +{ + DXABSTRACT_BREAK_ON_ERROR(); + return NULL; +} + + +D3DXMATRIX* D3DXMatrixPerspectiveRH( D3DXMATRIX *pOut, FLOAT w, FLOAT h, FLOAT zn, FLOAT zf ) +{ + DXABSTRACT_BREAK_ON_ERROR(); + return NULL; +} + + +D3DXMATRIX* D3DXMatrixPerspectiveOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn, FLOAT zf ) +{ + DXABSTRACT_BREAK_ON_ERROR(); + return NULL; +} + + +D3DXPLANE* D3DXPlaneTransform( D3DXPLANE *pOut, CONST D3DXPLANE *pP, CONST D3DXMATRIX *pM ) +{ + float *out = &pOut->a; + + // dot dot dot + for( int x=0; x<4; x++ ) + { + out[x] = (pM->m[0][x] * pP->a) + + (pM->m[1][x] * pP->b) + + (pM->m[2][x] * pP->c) + + (pM->m[3][x] * pP->d); + } + + return pOut; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +IDirect3D9 *Direct3DCreate9(UINT SDKVersion) +{ + GLMPRINTF(( "-X- Direct3DCreate9: %d", SDKVersion )); + + return new IDirect3D9; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +void D3DPERF_SetOptions( DWORD dwOptions ) +{ +} + + +HRESULT D3DXCompileShader( + LPCSTR pSrcData, + UINT SrcDataLen, + CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, + LPCSTR pFunctionName, + LPCSTR pProfile, + DWORD Flags, + LPD3DXBUFFER* ppShader, + LPD3DXBUFFER* ppErrorMsgs, + LPD3DXCONSTANTTABLE* ppConstantTable) +{ + DXABSTRACT_BREAK_ON_ERROR(); // is anyone calling this ? + return S_OK; +} + +#if defined(DX_TO_GL_ABSTRACTION) +void toglGetClientRect( void *hWnd, RECT *destRect ) +{ + // the only useful answer this call can offer, is the size of the canvas. + // actually getting the window bounds is not useful. + // so, see if a D3D device is up and running, and if so, + // dig in and find out its backbuffer size and use that. + + uint width, height; + g_pLauncherMgr->RenderedSize( width, height, false ); // false = get them, don't set them + Assert( width!=0 && height!=0 ); + + destRect->left = 0; + destRect->top = 0; + destRect->right = width; + destRect->bottom = height; + + //GLMPRINTF(( "-D- GetClientRect returning rect of (0,0, %d,%d)",width,height )); + + return; +} + +#endif + +#endif diff --git a/togles/linuxwin/glentrypoints.cpp b/togles/linuxwin/glentrypoints.cpp new file mode 100644 index 00000000..ef1f01ff --- /dev/null +++ b/togles/linuxwin/glentrypoints.cpp @@ -0,0 +1,512 @@ +//========= 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. +// +// glentrypoints.cpp +// +//=============================================================================// +// Immediately include gl.h, etc. here to avoid compilation warnings. + +#include "togl/rendermechanism.h" + +#include "appframework/AppFramework.h" +#include "appframework/IAppSystemGroup.h" +#include "tier0/dbg.h" +#include "tier0/icommandline.h" +#include "tier0/platform.h" +#include "interface.h" +#include "filesystem.h" +#include "filesystem_init.h" +#include "tier1/convar.h" +#include "vstdlib/cvar.h" +#include "inputsystem/ButtonCode.h" +#include "tier1.h" +#include "tier2/tier2.h" + +#if defined(_LINUX) && !defined(__ANDROID__) +#include +#endif + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +#if !defined(DX_TO_GL_ABSTRACTION) +#error +#endif + +#if defined(OSX) || defined(LINUX) || (defined (WIN32) && defined( DX_TO_GL_ABSTRACTION )) + #include "appframework/ilaunchermgr.h" + ILauncherMgr *g_pLauncherMgr = NULL; +#endif + +#define DEBUG_ALL_GLCALLS 0 + +#if DEBUG_ALL_GLCALLS +bool g_bDebugOpenGLCalls = true; +bool g_bPrintOpenGLCalls = false; + +#define GL_EXT(x,glmajor,glminor) +#define GL_FUNC(ext,req,ret,fn,arg,call) \ + static ret (*fn##_gldebugptr) arg = NULL; \ + static ret fn##_gldebug arg { \ + if (!g_bDebugOpenGLCalls) { return fn##_gldebugptr call; } \ + if (g_bPrintOpenGLCalls) { \ + printf("Calling %s ... ", #fn); \ + fflush(stdout); \ + } \ + ret retval = fn##_gldebugptr call; \ + if (g_bPrintOpenGLCalls) { \ + printf("%s returned!\n", #fn); \ + fflush(stdout); \ + } \ + const GLenum err = glGetError_gldebugptr(); \ + if ( err == GL_INVALID_FRAMEBUFFER_OPERATION_EXT ) { \ + const GLenum fberr = gGL->glCheckFramebufferStatus( GL_FRAMEBUFFER_EXT ); \ + printf("%s triggered error GL_INVALID_FRAMEBUFFER_OPERATION_EXT! (0x%X)\n\n\n", #fn, (int) fberr); \ + fflush(stdout); \ + __asm__ __volatile__ ( "int $3\n\t" ); \ + } else if (err != GL_NO_ERROR) { \ + printf("%s triggered error 0x%X!\n\n\n", #fn, (int) err); \ + fflush(stdout); \ + __asm__ __volatile__ ( "int $3\n\t" ); \ + } \ + return retval; \ +} + +#define GL_FUNC_VOID(ext,req,fn,arg,call) \ + static void (*fn##_gldebugptr) arg = NULL; \ + static void fn##_gldebug arg { \ + if (!g_bDebugOpenGLCalls) { fn##_gldebugptr call; return; } \ + if (g_bPrintOpenGLCalls) { \ + printf("Calling %s ... ", #fn); \ + fflush(stdout); \ + } \ + fn##_gldebugptr call; \ + if (g_bPrintOpenGLCalls) { \ + printf("%s returned!\n", #fn); \ + fflush(stdout); \ + } \ + const GLenum err = glGetError_gldebugptr(); \ + if ( err == GL_INVALID_FRAMEBUFFER_OPERATION_EXT ) { \ + const GLenum fberr = gGL->glCheckFramebufferStatus( GL_FRAMEBUFFER_EXT ); \ + printf("%s triggered error GL_INVALID_FRAMEBUFFER_OPERATION_EXT! (0x%X)\n\n\n", #fn, (int) fberr); \ + fflush(stdout); \ + __asm__ __volatile__ ( "int $3\n\t" ); \ + } else if (err != GL_NO_ERROR) { \ + printf("%s triggered error 0x%X!\n\n\n", #fn, (int) err); \ + fflush(stdout); \ + __asm__ __volatile__ ( "int $3\n\t" ); \ + } \ +} + +#include "togl/glfuncs.inl" +#undef GL_FUNC_VOID +#undef GL_FUNC +#undef GL_EXT +#endif + +COpenGLEntryPoints *gGL = NULL; +GL_GetProcAddressCallbackFunc_t gGL_GetProcAddressCallback = NULL; + +void *VoidFnPtrLookup_GlMgr(const char *fn, bool &okay, const bool bRequired, void *fallback) +{ + void *retval = NULL; + if ((!okay) && (!bRequired)) // always look up if required (so we get a complete list of crucial missing symbols). + return NULL; + + // SDL does the right thing, so we never need to use tier0 in this case. + retval = (*gGL_GetProcAddressCallback)(fn, okay, bRequired, fallback); + //printf("CDynamicFunctionOpenGL: SDL_GL_GetProcAddress(\"%s\") returned %p\n", fn, retval); + if ((retval == NULL) && (fallback != NULL)) + { + //printf("CDynamicFunctionOpenGL: Using fallback %p for \"%s\"\n", fallback, fn); + retval = fallback; + } + + // Note that a non-NULL response doesn't mean it's safe to call the function! + // You always have to check that the extension is supported; + // an implementation MAY return NULL in this case, but it doesn't have to (and doesn't, with the DRI drivers). + okay = (okay && (retval != NULL)); + if (bRequired && !okay) + fprintf(stderr, "Could not find required OpenGL entry point '%s'!\n", fn); + + return retval; +} + +COpenGLEntryPoints *GetOpenGLEntryPoints(GL_GetProcAddressCallbackFunc_t callback) +{ + if (gGL == NULL) + { + gGL_GetProcAddressCallback = callback; + gGL = new COpenGLEntryPoints(); + if (!gGL->m_bHave_OpenGL) + Error( "Missing basic required OpenGL functionality." ); + + } + return gGL; +} + +void ClearOpenGLEntryPoints() +{ + if ( gGL ) + { + gGL->ClearEntryPoints(); + } +} +COpenGLEntryPoints *ToGLConnectLibraries( CreateInterfaceFn factory ) +{ + ConnectTier1Libraries( &factory, 1 ); + ConVar_Register(); + ConnectTier2Libraries( &factory, 1 ); + + if ( !g_pFullFileSystem ) + { + Warning( "ToGL was unable to access the required interfaces!\n" ); + } + + // NOTE! : Overbright is 1.0 so that Hammer will work properly with the white bumped and unbumped lightmaps. + MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f ); + + #if defined( USE_SDL ) + g_pLauncherMgr = (ILauncherMgr *)factory( SDLMGR_INTERFACE_VERSION, NULL ); + #endif + + return gGL; +} + +void ToGLDisconnectLibraries() +{ + DisconnectTier2Libraries(); + ConVar_Unregister(); + DisconnectTier1Libraries(); +} + +#define GLVERNUM(Major, Minor, Patch) (((Major) * 100000) + ((Minor) * 1000) + (Patch)) + +static void GetOpenGLVersion(int *major, int *minor, int *patch) +{ + *major = *minor = *patch = 0; + static CDynamicFunctionOpenGL< true, const GLubyte *( APIENTRY *)(GLenum name), const GLubyte * > glGetString("glGetString"); + if (glGetString) + { + const char *version = (const char *) glGetString(GL_VERSION); + if (version) + { + sscanf( version, "%d.%d.%d", major, minor, patch ); + } + } +} + +static int GetOpenGLVersionMajor() +{ + int major, minor, patch; + GetOpenGLVersion(&major, &minor, &patch); + return major; +} + +static int GetOpenGLVersionMinor() +{ + int major, minor, patch; + GetOpenGLVersion(&major, &minor, &patch); + return minor; +} + +static int GetOpenGLVersionPatch() +{ + int major, minor, patch; + GetOpenGLVersion(&major, &minor, &patch); + return patch; +} + +static bool CheckBaseOpenGLVersion() +{ + const int NEED_MAJOR = 2; + const int NEED_MINOR = 0; + const int NEED_PATCH = 0; + + int major, minor, patch; + GetOpenGLVersion(&major, &minor, &patch); + + const int need = GLVERNUM(NEED_MAJOR, NEED_MINOR, NEED_PATCH); + const int have = GLVERNUM(major, minor, patch); + if (have < need) + { + fprintf(stderr, "PROBLEM: You appear to have OpenGL %d.%d.%d, but we need at least %d.%d.%d!\n", + major, minor, patch, NEED_MAJOR, NEED_MINOR, NEED_PATCH); + return false; + } + return true; +} + +static bool CheckOpenGLExtension_internal(const char *ext, const int coremajor, const int coreminor) +{ + if ((coremajor >= 0) && (coreminor >= 0)) // we know that this extension is part of the base spec as of GL_VERSION coremajor.coreminor. + { + int major, minor, patch; + GetOpenGLVersion(&major, &minor, &patch); + const int need = GLVERNUM(coremajor, coreminor, 0); + const int have = GLVERNUM(major, minor, patch); + if (have >= need) + return true; // we definitely have access to this "extension," as it is part of this version of the GL's core functionality. + } + + // okay, see if the GL_EXTENSIONS string reports it. + static CDynamicFunctionOpenGL< true, const GLubyte *( APIENTRY *)(GLenum name), const GLubyte * > glGetString("glGetString"); + if (!glGetString) + return false; + + // hacky scanning of this string, because I don't want to spend time breaking it into a vector like I should have. + const char *extensions = (const char *) glGetString(GL_EXTENSIONS); + const size_t extlen = strlen(ext); + while ((extensions) && (*extensions)) + { + const char *ptr = strstr(extensions, ext); +#if _WIN32 + if (!ptr) + { + static CDynamicFunctionOpenGL< true, const char *( APIENTRY *)( ), const char * > wglGetExtensionsStringEXT("wglGetExtensionsStringEXT"); + if (wglGetExtensionsStringEXT) + { + extensions = wglGetExtensionsStringEXT(); + ptr = strstr(extensions, ext); + } + + if (!ptr) + { + return false; + } + } +#elif !defined ( OSX ) && !defined( __ANDROID__ ) + if (!ptr) + { + static CDynamicFunctionOpenGL< true, Display *( APIENTRY *)( ), Display* > glXGetCurrentDisplay("glXGetCurrentDisplay"); + static CDynamicFunctionOpenGL< true, const char *( APIENTRY *)( Display*, int ), const char * > glXQueryExtensionsString("glXQueryExtensionsString"); + if (glXQueryExtensionsString && glXGetCurrentDisplay) + { + extensions = glXQueryExtensionsString(glXGetCurrentDisplay(), 0); + ptr = strstr(extensions, ext); + } + } +#endif + + if (!ptr) + return false; + + // make sure this matches the entire string, and isn't a substring match of some other extension. + // if ( ( (string is at start of extension list) or (the char before the string is a space) ) and + // (the next char after the string is a space or a null terminator) ) + if ( ((ptr == extensions) || (ptr[-1] == ' ')) && + ((ptr[extlen] == ' ') || (ptr[extlen] == '\0')) ) + return true; // found it! + + extensions = ptr + extlen; // skip ahead, search again. + } + return false; +} + +static bool CheckOpenGLExtension(const char *ext, const int coremajor, const int coreminor) +{ + const bool retval = CheckOpenGLExtension_internal(ext, coremajor, coreminor); + printf("This system %s the OpenGL extension %s.\n", retval ? "supports" : "DOES NOT support", ext); + return retval; +} + +// The GL context you want entry points for must be current when you hit this constructor! +COpenGLEntryPoints::COpenGLEntryPoints() + : m_nTotalGLCycles(0) + , m_nTotalGLCalls(0) + , m_nOpenGLVersionMajor(GetOpenGLVersionMajor()) + , m_nOpenGLVersionMinor(GetOpenGLVersionMinor()) + , m_nOpenGLVersionPatch(GetOpenGLVersionPatch()) + , m_bHave_OpenGL(CheckBaseOpenGLVersion()) // may reset to false as these lookups happen. +#define GL_EXT(x,glmajor,glminor) , m_bHave_##x(CheckOpenGLExtension(#x, glmajor, glminor)) +#define GL_FUNC(ext,req,ret,fn,arg,call) , fn(#fn, m_bHave_##ext) +#define GL_FUNC_VOID(ext,req,fn,arg,call) , fn(#fn, m_bHave_##ext) +#include "togl/glfuncs.inl" +#undef GL_FUNC_VOID +#undef GL_FUNC +#undef GL_EXT +{ + // Locally cache the copy of the GL device strings, to avoid needing to call these glGet's (which can be extremely slow) more than once. + const char *pszString = ( const char * )glGetString(GL_VENDOR); + m_pGLDriverStrings[cGLVendorString] = strdup( pszString ? pszString : "" ); + + m_nDriverProvider = cGLDriverProviderUnknown; + if ( V_stristr( m_pGLDriverStrings[cGLVendorString], "nvidia" ) ) + m_nDriverProvider = cGLDriverProviderNVIDIA; + else if ( V_stristr( m_pGLDriverStrings[cGLVendorString], "amd" ) || V_stristr( m_pGLDriverStrings[cGLVendorString], "ati" ) ) + m_nDriverProvider = cGLDriverProviderAMD; + else if ( V_stristr( m_pGLDriverStrings[cGLVendorString], "intel" ) ) + m_nDriverProvider = cGLDriverProviderIntelOpenSource; + else if ( V_stristr( m_pGLDriverStrings[cGLVendorString], "apple" ) ) + m_nDriverProvider = cGLDriverProviderApple; + + pszString = ( const char * )glGetString(GL_RENDERER); + m_pGLDriverStrings[cGLRendererString] = strdup( pszString ? pszString : "" ); + + pszString = ( const char * )glGetString(GL_VERSION); + m_pGLDriverStrings[cGLVersionString] = strdup( pszString ? pszString : "" ); + + pszString = ( const char * )glGetString(GL_EXTENSIONS); + m_pGLDriverStrings[cGLExtensionsString] = strdup( pszString ? pszString : "" ); + + printf( "OpenGL: %s %s (%d.%d.%d)\n", m_pGLDriverStrings[ cGLRendererString ], m_pGLDriverStrings[ cGLVersionString ], + m_nOpenGLVersionMajor, m_nOpenGLVersionMinor, m_nOpenGLVersionPatch ); + + // !!! FIXME: Alfred says the original GL_APPLE_fence code only exists to + // !!! FIXME: hint Apple's drivers and not because we rely on the + // !!! FIXME: functionality. If so, just remove this check (and the + // !!! FIXME: GL_NV_fence code entirely). +#ifndef ANDROID // HACK + if ((m_bHave_OpenGL) && ((!m_bHave_GL_NV_fence) && (!m_bHave_GL_ARB_sync) && (!m_bHave_GL_APPLE_fence))) + { + Error( "Required OpenGL extension \"GL_NV_fence\", \"GL_ARB_sync\", or \"GL_APPLE_fence\" is not supported. Please upgrade your OpenGL driver." ); + } +#endif + + // same extension, different name. + if (m_bHave_GL_EXT_vertex_array_bgra || m_bHave_GL_ARB_vertex_array_bgra) + { + m_bHave_GL_EXT_vertex_array_bgra = m_bHave_GL_ARB_vertex_array_bgra = true; + } + + // GL_ARB_framebuffer_object is a superset of GL_EXT_framebuffer_object, + // (etc) but if you don't call in through the ARB entry points, you won't + // get the relaxed restrictions on mismatched attachment dimensions. + if (m_bHave_GL_ARB_framebuffer_object) + { + m_bHave_GL_EXT_framebuffer_object = true; + m_bHave_GL_EXT_framebuffer_blit = true; + m_bHave_GL_EXT_framebuffer_multisample = true; + glBindFramebufferEXT.Force(glBindFramebuffer.Pointer()); + glBindRenderbufferEXT.Force(glBindRenderbuffer.Pointer()); + glCheckFramebufferStatusEXT.Force(glCheckFramebufferStatus.Pointer()); + glDeleteRenderbuffersEXT.Force(glDeleteRenderbuffers.Pointer()); + glFramebufferRenderbufferEXT.Force(glFramebufferRenderbuffer.Pointer()); + glFramebufferTexture2DEXT.Force(glFramebufferTexture2D.Pointer()); + glFramebufferTexture3DEXT.Force(glFramebufferTexture3D.Pointer()); + glGenFramebuffersEXT.Force(glGenFramebuffers.Pointer()); + glGenRenderbuffersEXT.Force(glGenRenderbuffers.Pointer()); + glDeleteFramebuffersEXT.Force(glDeleteFramebuffers.Pointer()); + glBlitFramebufferEXT.Force(glBlitFramebuffer.Pointer()); + glRenderbufferStorageMultisampleEXT.Force(glRenderbufferStorageMultisample.Pointer()); + } + +#if DEBUG_ALL_GLCALLS + // push all GL calls through the debug wrappers. +#define GL_EXT(x,glmajor,glminor) +#define GL_FUNC(ext,req,ret,fn,arg,call) \ + fn##_gldebugptr = this->fn; \ + this->fn.Force(fn##_gldebug); +#define GL_FUNC_VOID(ext,req,fn,arg,call) \ + fn##_gldebugptr = this->fn; \ + this->fn.Force(fn##_gldebug); +#include "togl/glfuncs.inl" +#undef GL_FUNC_VOID +#undef GL_FUNC +#undef GL_EXT +#endif + +#ifdef OSX + m_bHave_GL_NV_bindless_texture = false; + m_bHave_GL_AMD_pinned_memory = false; +#else + if ( ( m_bHave_GL_NV_bindless_texture ) && ( !CommandLine()->CheckParm( "-gl_nv_bindless_texturing" ) ) ) + { + m_bHave_GL_NV_bindless_texture = false; + glGetTextureHandleNV.Force( NULL ); + glGetTextureSamplerHandleNV.Force( NULL ); + glMakeTextureHandleResidentNV.Force( NULL ); + glMakeTextureHandleNonResidentNV.Force( NULL ); + glUniformHandleui64NV.Force( NULL ); + glUniformHandleui64vNV.Force( NULL ); + glProgramUniformHandleui64NV.Force( NULL ); + glProgramUniformHandleui64vNV.Force( NULL ); + glIsTextureHandleResidentNV.Force( NULL ); + } + + if ( !CommandLine()->CheckParm( "-gl_amd_pinned_memory" ) ) + { + m_bHave_GL_AMD_pinned_memory = false; + } +#endif // !OSX + + // Getting reports of black screens, etc. with ARB_buffer_storage and AMD drivers. This type of thing: + // http://forums.steampowered.com/forums/showthread.php?t=3266806 + // So disable it for now. + if ( ( m_nDriverProvider == cGLDriverProviderAMD ) || CommandLine()->CheckParm( "-gl_disable_arb_buffer_storage" ) ) + { + m_bHave_GL_ARB_buffer_storage = false; + } + + printf( "GL_NV_bindless_texture: %s\n", m_bHave_GL_NV_bindless_texture ? "ENABLED" : "DISABLED" ); + printf( "GL_AMD_pinned_memory: %s\n", m_bHave_GL_AMD_pinned_memory ? "ENABLED" : "DISABLED" ); + printf( "GL_ARB_buffer_storage: %s\n", m_bHave_GL_ARB_buffer_storage ? "AVAILABLE" : "NOT AVAILABLE" ); + printf( "GL_EXT_texture_sRGB_decode: %s\n", m_bHave_GL_EXT_texture_sRGB_decode ? "AVAILABLE" : "NOT AVAILABLE" ); + + bool bGLCanDecodeS3TCTextures = m_bHave_GL_EXT_texture_compression_s3tc || ( m_bHave_GL_EXT_texture_compression_dxt1 && m_bHave_GL_ANGLE_texture_compression_dxt3 && m_bHave_GL_ANGLE_texture_compression_dxt5 ); + if ( !bGLCanDecodeS3TCTextures ) + { + Error( "This application requires either the GL_EXT_texture_compression_s3tc, or the GL_EXT_texture_compression_dxt1 + GL_ANGLE_texture_compression_dxt3 + GL_ANGLE_texture_compression_dxt5 OpenGL extensions. Please install S3TC texture support.\n" ); + } + +#ifdef OSX + if ( CommandLine()->FindParm( "-glmnosrgbdecode" ) ) + { + Msg( "Forcing m_bHave_GL_EXT_texture_sRGB_decode off.\n" ); + m_bHave_GL_EXT_texture_sRGB_decode = false; + } +#endif + +#ifndef OSX + if ( !m_bHave_GL_EXT_texture_sRGB_decode ) + { + Error( "Required OpenGL extension \"GL_EXT_texture_sRGB_decode\" is not supported. Please update your OpenGL driver.\n" ); + } +#endif +} + +COpenGLEntryPoints::~COpenGLEntryPoints() +{ + for ( uint i = 0; i < cGLTotalDriverProviders; ++i ) + { + free( m_pGLDriverStrings[i] ); + m_pGLDriverStrings[i] = NULL; + } +} + +void COpenGLEntryPoints::ClearEntryPoints() +{ + #define GL_EXT(x,glmajor,glminor) + #define GL_FUNC(ext,req,ret,fn,arg,call) fn.Force( NULL ); + #define GL_FUNC_VOID(ext,req,fn,arg,call) fn.Force( NULL ); + #include "togl/glfuncs.inl" + #undef GL_FUNC_VOID + #undef GL_FUNC + #undef GL_EXT +} +// Turn off memdbg macros (turned on up top) since this is included like a header +#include "tier0/memdbgoff.h" + + + diff --git a/togles/linuxwin/glmgr.cpp b/togles/linuxwin/glmgr.cpp new file mode 100644 index 00000000..1c18720f --- /dev/null +++ b/togles/linuxwin/glmgr.cpp @@ -0,0 +1,6092 @@ +//========= 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.cpp +// +//=============================================================================== +#include "togl/rendermechanism.h" + +#include "tier0/icommandline.h" + +#include "tier0/vprof.h" +#include "glmtexinlines.h" + +#include "materialsystem/IShader.h" +#include "appframework/ilaunchermgr.h" + +#include "convar.h" + +#include "glmgr_flush.inl" + +#ifdef OSX +#include +#include "intelglmallocworkaround.h" +#endif + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + + +// Whether the code should use gl_arb_debug_output. This causes error messages to be streamed, via callback, to the application. +// It is much friendlier to the MTGL driver. +// NOTE: This can be turned off after launch, but it cannot be turned on after launch--it implies a context-creation-time +// behavior. +ConVar gl_debug_output( "gl_debug_output", "1" ); + + +// Whether or not we should batch up our creation and deletion behavior. +ConVar gl_batch_tex_creates( "gl_batch_tex_creates", "0" ); +ConVar gl_batch_tex_destroys( "gl_batch_tex_destroys", "0" ); + +//=============================================================================== + +// g_nTotalDrawsOrClears is reset to 0 in Present() +uint g_nTotalDrawsOrClears, g_nTotalVBLockBytes, g_nTotalIBLockBytes; + +#if GL_TELEMETRY_GPU_ZONES +TelemetryGPUStats_t g_TelemetryGPUStats; +#endif + +const int kGLMInitialTexCount = 4096; +const int kGLMReUpTexCount = 1024; +const int kGLMHighWaterUndeleted = 2048; +const int kDeletedTextureDim = 4; +const uint32 g_garbageTextureBits[ 4 * kDeletedTextureDim * kDeletedTextureDim ] = { 0 }; + +char g_nullFragmentProgramText [] = +{ + "!!ARBfp1.0 \n" + "PARAM black = { 0.0, 0.0, 0.0, 1.0 }; \n" // opaque black + "MOV result.color, black; \n" + "END \n\n\n" + "//GLSLfp\n" + "void main()\n" + "{\n" + "gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n" + "}\n" + +}; + +// make dummy programs for doing texture preload via dummy draw +char g_preloadTexVertexProgramText[] = +{ + "//GLSLvp \n" + "#version 120 \n" + "varying vec4 otex; \n" + "void main() \n" + "{ \n" + "vec4 pos = ftransform(); // vec4( 0.1, 0.1, 0.1, 0.1 ); \n" + "vec4 tex = vec4( 0.0, 0.0, 0.0, 0.0 ); \n" + " \n" + "gl_Position = pos; \n" + "otex = tex; \n" + "} \n" +}; + +char g_preload2DTexFragmentProgramText[] = +{ + "//GLSLfp \n" + "#version 120 \n" + "varying vec4 otex; \n" + "//SAMPLERMASK-8000 // may not be needed \n" + "//HIGHWATER-30 // may not be needed \n" + " \n" + "uniform vec4 pc[31]; \n" + "uniform sampler2D sampler15; \n" + " \n" + "void main() \n" + "{ \n" + "vec4 r0; \n" + "r0 = texture2D( sampler15, otex.xy ); \n" + "gl_FragColor = r0; //discard; \n" + "} \n" +}; + +char g_preload3DTexFragmentProgramText[] = +{ + "//GLSLfp \n" + "#version 120 \n" + "varying vec4 otex; \n" + "//SAMPLERMASK-8000 // may not be needed \n" + "//HIGHWATER-30 // may not be needed \n" + " \n" + "uniform vec4 pc[31]; \n" + "uniform sampler3D sampler15; \n" + " \n" + "void main() \n" + "{ \n" + "vec4 r0; \n" + "r0 = texture3D( sampler15, otex.xyz ); \n" + "gl_FragColor = r0; //discard; \n" + "} \n" +}; + +char g_preloadCubeTexFragmentProgramText[] = +{ + "//GLSLfp \n" + "#version 120 \n" + "varying vec4 otex; \n" + "//SAMPLERMASK-8000 // may not be needed \n" + "//HIGHWATER-30 // may not be needed \n" + " \n" + "uniform vec4 pc[31]; \n" + "uniform samplerCube sampler15; \n" + " \n" + "void main() \n" + "{ \n" + "vec4 r0; \n" + "r0 = textureCube( sampler15, otex.xyz ); \n" + "gl_FragColor = r0; //discard; \n" + "} \n" +}; + +const char* glSourceToString(GLenum source) +{ + switch (source) + { + case GL_DEBUG_SOURCE_API_ARB: return "API"; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: return "WINDOW_SYSTEM"; + case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: return "SHADER_COMPILER"; + case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: return "THIRD_PARTY"; + case GL_DEBUG_SOURCE_APPLICATION_ARB: return "APPLICATION"; + case GL_DEBUG_SOURCE_OTHER_ARB: return "OTHER"; + default: break; + } + return "UNKNOWN"; +} + +const char* glTypeToString(GLenum type) +{ + switch (type) + { + case GL_DEBUG_TYPE_ERROR_ARB: return "ERROR"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: return "DEPRECATION"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: return "UNDEFINED_BEHAVIOR"; + case GL_DEBUG_TYPE_PORTABILITY_ARB: return "PORTABILITY"; + case GL_DEBUG_TYPE_PERFORMANCE_ARB: return "PERFORMANCE"; + case GL_DEBUG_TYPE_OTHER_ARB: return "OTHER"; + default: break; + } + return "UNKNOWN"; +} + +const char* glSeverityToString(GLenum severity) +{ + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH_ARB: return "HIGH"; + case GL_DEBUG_SEVERITY_MEDIUM_ARB: return "MEDIUM"; + case GL_DEBUG_SEVERITY_LOW_ARB: return "LOW"; + default: break; + } + return "UNKNOWN"; +} + +bool g_bDebugOutputBreakpoints = true; + +void APIENTRY GL_Debug_Output_Callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) +{ + const char *sSource = glSourceToString(source), + *sType = glTypeToString(type), + *sSeverity = glSeverityToString(severity); + + // According to NVidia, this error is a bug in the driver and not really an error (it's a warning in newer drivers): "Texture X is base level inconsistent. Check texture size" + if ( ( type == GL_DEBUG_TYPE_ERROR_ARB ) && strstr( message, "base level inconsistent" ) ) + { + return; + } + + if ( gl_debug_output.GetBool() || type == GL_DEBUG_TYPE_ERROR_ARB ) + { + Msg( "GL: [%s][%s][%s][%d]: %s\n", sSource, sType, sSeverity, id, message ); + } + +#ifdef WIN32 + OutputDebugStringA( message ); +#endif + + if ( ( type == GL_DEBUG_TYPE_ERROR_ARB ) && ( g_bDebugOutputBreakpoints ) ) + { + DebuggerBreak(); + } +} + +void GLMDebugPrintf( const char *pMsg, ... ) +{ + //$ TODO: Should this call Warning()? + //$ TODO: Should replace call these calls with Warning() / Msg() / DevMsg()? + + va_list args; + va_start( args, pMsg ); + vprintf( pMsg, args ); + va_end( args ); +} + +//=============================================================================== +// functions that are dependant on g_pLauncherMgr + +inline bool MakeContextCurrent( PseudoGLContextPtr hContext ) +{ + return g_pLauncherMgr->MakeContextCurrent( hContext ); +} + +inline PseudoGLContextPtr GetMainContext() +{ + return g_pLauncherMgr->GetMainContext(); +} + +inline PseudoGLContextPtr GetGLContextForWindow( void* windowref ) +{ + return g_pLauncherMgr->GetGLContextForWindow( windowref ); +} + +inline void IncrementWindowRefCount() +{ + g_pLauncherMgr->IncWindowRefCount(); +} +inline void DecrementWindowRefCount() +{ + g_pLauncherMgr->DecWindowRefCount(); +} +inline void ShowPixels( CShowPixelsParams *params ) +{ + g_pLauncherMgr->ShowPixels(params); +} + +inline void DisplayedSize( uint &width, uint &height ) +{ + g_pLauncherMgr->DisplayedSize( width, height ); +} + +inline void GetDesiredPixelFormatAttribsAndRendererInfo( uint **ptrOut, uint *countOut, GLMRendererInfoFields *rendInfoOut ) +{ + g_pLauncherMgr->GetDesiredPixelFormatAttribsAndRendererInfo( ptrOut, countOut, rendInfoOut ); +} + +inline void GetStackCrawl( CStackCrawlParams *params ) +{ + g_pLauncherMgr->GetStackCrawl(params); +} + +#if GLMDEBUG +inline void PumpWindowsMessageLoop() +{ + g_pLauncherMgr->PumpWindowsMessageLoop(); +} +inline int GetEvents( CCocoaEvent *pEvents, int nMaxEventsToReturn, bool debugEvents = false ) +{ + return g_pLauncherMgr->GetEvents( pEvents, nMaxEventsToReturn, debugEvents ); +} +#endif + +//=============================================================================== +// helper routines for debug + +static bool hasnonzeros( float *values, int count ) +{ + for( int i=0; i [ %10.5f %10.5f %10.5f %10.5f ]", + baseSlotNumber+islot, + row[0],row[1],row[2],row[3], + col[0],col[1],col[2],col[3] + )); + } + else + { + if (islot<3) + { + GLMPRINTF(( "-D- %03d: [ %10.5f %10.5f %10.5f %10.5f ] T=> [ %10.5f %10.5f %10.5f ]", + baseSlotNumber+islot, + row[0],row[1],row[2],row[3], + col[0],col[1],col[2] + )); + } + else + { + GLMPRINTF(( "-D- %03d: T=> [ %10.5f %10.5f %10.5f ]", + baseSlotNumber+islot, + col[0],col[1],col[2] + )); + } + } + } + GLMPRINTSTR(("-D-")); + } + else + { + GLMPRINTF(("-D- %s - (all 0.0)", label )); + } + +} + + +static void transform_dp4( float *in4, float *m00, int slots, float *out4 ) +{ + // m00 points to a column. + // each DP is one column of the matrix ( m00[4*n] + // if we are passed a three slot matrix, this is three columns, the source W plays into all three columns, but we must set the final output W to 1 ? + for( int n=0; nm_nCurOwnerThreadId = ThreadGetCurrentId(); + if ( !MakeContextCurrent( context->m_ctx ) ) + { + // give up + GLMStop(); + } + Assert( 0 ); +#endif +} + +GLMContext *GLMgr::GetCurrentContext( void ) +{ +#if defined( USE_SDL ) + PseudoGLContextPtr context = GetMainContext(); + return (GLMContext*) context; +#else + Assert( 0 ); + return NULL; +#endif +} + + +// #define CHECK_THREAD_USAGE 1 + + +//=============================================================================== +// GLMContext public methods +void GLMContext::MakeCurrent( bool bRenderThread ) +{ + tmZone( TELEMETRY_LEVEL0, 0, "GLMContext::MakeCurrent" ); + Assert( m_nCurOwnerThreadId == 0 || m_nCurOwnerThreadId == ThreadGetCurrentId() ); + +#if defined( USE_SDL ) + +#ifndef CHECK_THREAD_USAGE + if ( bRenderThread ) + { +// Msg( "******************************************** %08x Acquiring Context\n", ThreadGetCurrentId() ); + m_nCurOwnerThreadId = ThreadGetCurrentId(); + bool bSuccess = MakeContextCurrent( m_ctx ); + if ( !bSuccess ) + { + Assert( 0 ); + } + } +#else + uint32 dwThreadId = ThreadGetCurrentId(); + + if ( bRenderThread || dwThreadId == m_dwRenderThreadId ) + { + m_nCurOwnerThreadId = ThreadGetCurrentId(); + m_dwRenderThreadId = dwThreadId; + MakeContextCurrent( m_ctx ); + m_bIsThreading = true; + } + else if ( !m_bIsThreading ) + { + m_nCurOwnerThreadId = ThreadGetCurrentId(); + MakeContextCurrent( m_ctx ); + } + else + { + Assert( 0 ); + } +#endif + +#else + Assert( 0 ); +#endif +} + + +void GLMContext::ReleaseCurrent( bool bRenderThread ) +{ + tmZone( TELEMETRY_LEVEL0, 0, "GLMContext::ReleaseCurrent" ); + Assert( m_nCurOwnerThreadId == ThreadGetCurrentId() ); + +#if defined( USE_SDL ) + +#ifndef CHECK_THREAD_USAGE + if ( bRenderThread ) + { +// Msg( "******************************************** %08x Releasing Context\n", ThreadGetCurrentId() ); + m_nCurOwnerThreadId = 0; + m_nThreadOwnershipReleaseCounter++; + MakeContextCurrent( NULL ); + } +#else + m_nCurOwnerThreadId = 0; + m_nThreadOwnershipReleaseCounter++; + MakeContextCurrent( NULL ); + if ( bRenderThread ) + { + m_bIsThreading = false; + } +#endif + +#else + Assert( 0 ); +#endif +} + + +// This function forces all GL state to be re-sent to the context. Some state will only be set on the next batch flush. +void GLMContext::ForceFlushStates() +{ + // Flush various render states + m_AlphaTestEnable.Flush(); + m_AlphaTestFunc.Flush(); + + m_DepthBias.Flush(); + + m_ScissorEnable.Flush(); + m_ScissorBox.Flush(); + + m_ViewportBox.Flush(); + m_ViewportDepthRange.Flush(); + + m_ColorMaskSingle.Flush(); + + m_BlendEnable.Flush(); + m_BlendFactor.Flush(); + + m_BlendEnableSRGB.Flush(); + + m_DepthTestEnable.Flush(); + m_DepthFunc.Flush(); + m_DepthMask.Flush(); + + m_StencilTestEnable.Flush(); + m_StencilFunc.Flush(); + m_StencilOp.Flush(); + m_StencilWriteMask.Flush(); + + m_ClearColor.Flush(); + m_ClearDepth.Flush(); + m_ClearStencil.Flush(); + + m_ClipPlaneEnable.Flush(); // always push clip state + m_ClipPlaneEquation.Flush(); + + m_CullFaceEnable.Flush(); + + m_CullFrontFace.Flush(); + + m_PolygonMode.Flush(); + + m_AlphaToCoverageEnable.Flush(); + m_ColorMaskMultiple.Flush(); + m_BlendEquation.Flush(); + m_BlendColor.Flush(); + // Reset various things so they get reset on the next batch flush + m_activeTexture = -1; + + for ( int i = 0; i < GLM_SAMPLER_COUNT; i++ ) + { + SetSamplerTex( i, m_samplers[i].m_pBoundTex ); + SetSamplerDirty( i ); + } + + // Attributes/vertex attribs + ClearCurAttribs(); + + m_lastKnownVertexAttribMask = 0; + m_nNumSetVertexAttributes = 16; + memset( &m_boundVertexAttribs[0], 0xFF, sizeof( m_boundVertexAttribs ) ); + for( int index=0; index < kGLMVertexAttributeIndexMax; index++ ) + gGL->glDisableVertexAttribArray( index ); + + // Program + NullProgram(); + + // FBO + BindFBOToCtx( m_boundReadFBO, GL_READ_FRAMEBUFFER_EXT ); + BindFBOToCtx( m_boundDrawFBO, GL_DRAW_FRAMEBUFFER_EXT ); + + // Current VB/IB/pinned memory buffers + gGL->glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, m_nBoundGLBuffer[ kGLMIndexBuffer] ); + gGL->glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nBoundGLBuffer[ kGLMVertexBuffer] ); + +#ifndef OSX + if ( gGL->m_bHave_GL_AMD_pinned_memory ) + { + gGL->glBindBufferARB( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer].GetHandle() ); + } +#endif +} + +const GLMRendererInfoFields& GLMContext::Caps( void ) +{ + return m_caps; +} + +void GLMContext::DumpCaps( void ) +{ + /* + #define dumpfield( fff ) printf( "\n "#fff" : %d", (int) m_caps.fff ) + #define dumpfield_hex( fff ) printf( "\n "#fff" : 0x%08x", (int) m_caps.fff ) + #define dumpfield_str( fff ) printf( "\n "#fff" : %s", m_caps.fff ) + */ + + #define dumpfield( fff ) printf( "\n %-30s : %d", #fff, (int) m_caps.fff ) + #define dumpfield_hex( fff ) printf( "\n %-30s : 0x%08x", #fff, (int) m_caps.fff ) + #define dumpfield_str( fff ) printf( "\n %-30s : %s", #fff, m_caps.fff ) + + printf("\n-------------------------------- context caps for context %08x", (uint)this); + + dumpfield( m_fullscreen ); + dumpfield( m_accelerated ); + dumpfield( m_windowed ); + dumpfield_hex( m_rendererID ); + dumpfield( m_displayMask ); + dumpfield( m_bufferModes ); + dumpfield( m_colorModes ); + dumpfield( m_accumModes ); + dumpfield( m_depthModes ); + dumpfield( m_stencilModes ); + dumpfield( m_maxAuxBuffers ); + dumpfield( m_maxSampleBuffers ); + dumpfield( m_maxSamples ); + dumpfield( m_sampleModes ); + dumpfield( m_sampleAlpha ); + dumpfield_hex( m_vidMemory ); + dumpfield_hex( m_texMemory ); + + dumpfield_hex( m_pciVendorID ); + dumpfield_hex( m_pciDeviceID ); + dumpfield_str( m_pciModelString ); + dumpfield_str( m_driverInfoString ); + + printf( "\n m_osComboVersion: 0x%08x (%d.%d.%d)", m_caps.m_osComboVersion, (m_caps.m_osComboVersion>>16)&0xFF, (m_caps.m_osComboVersion>>8)&0xFF, (m_caps.m_osComboVersion)&0xFF ); + + dumpfield( m_ati ); + if (m_caps.m_ati) + { + dumpfield( m_atiR5xx ); + dumpfield( m_atiR6xx ); + dumpfield( m_atiR7xx ); + dumpfield( m_atiR8xx ); + dumpfield( m_atiNewer ); + } + + dumpfield( m_intel ); + if (m_caps.m_intel) + { + dumpfield( m_intel95x ); + dumpfield( m_intel3100 ); + dumpfield( m_intelHD4000 ); + } + + dumpfield( m_nv ); + if (m_caps.m_nv) + { + //dumpfield( m_nvG7x ); + dumpfield( m_nvG8x ); + dumpfield( m_nvNewer ); + } + + dumpfield( m_hasGammaWrites ); + dumpfield( m_hasMixedAttachmentSizes ); + dumpfield( m_hasBGRA ); + dumpfield( m_hasNewFullscreenMode ); + dumpfield( m_hasNativeClipVertexMode ); + dumpfield( m_maxAniso ); + + dumpfield( m_hasBindableUniforms ); + dumpfield( m_maxVertexBindableUniforms ); + dumpfield( m_maxFragmentBindableUniforms ); + dumpfield( m_maxBindableUniformSize ); + + dumpfield( m_hasUniformBuffers ); + dumpfield( m_hasPerfPackage1 ); + + dumpfield( m_cantBlitReliably ); + dumpfield( m_cantAttachSRGB ); + dumpfield( m_cantResolveFlipped ); + dumpfield( m_cantResolveScaled ); + dumpfield( m_costlyGammaFlips ); + dumpfield( m_badDriver1064NV ); + dumpfield( m_badDriver108Intel ); + + printf("\n--------------------------------"); + + #undef dumpfield + #undef dumpfield_hex + #undef dumpfield_str +} + +CGLMTex *GLMContext::NewTex( GLMTexLayoutKey *key, uint levels, const char *debugLabel ) +{ + // get a layout based on the key + GLMTexLayout *layout = m_texLayoutTable->NewLayoutRef( key ); + + CGLMTex *tex = new CGLMTex( this, layout, levels, debugLabel ); + + return tex; +} + +void GLMContext::DelTex( CGLMTex * tex ) +{ + //Queue the texture for deletion in ProcessTextureDeletes + //when we are sure we will hold the context. + m_DeleteTextureQueue.PushItem(tex); +} + +void GLMContext::ProcessTextureDeletes() +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmEvent( "GLMContext::ProcessTextureDeletes" ); +#endif + + CGLMTex* tex = nullptr; + while ( m_DeleteTextureQueue.PopItem( &tex ) ) + { + for( int i = 0; i < GLM_SAMPLER_COUNT; i++) + { + if ( m_samplers[i].m_pBoundTex == tex ) + { + BindTexToTMU( NULL, i ); + } + } + + if ( tex->m_rtAttachCount != 0 ) + { + // RG - huh? wtf? TODO: fix this code which seems to be purposely leaking + // leak it and complain - we may have to implement a deferred-delete system for tex like these + + GLMDebugPrintf("GLMContext::DelTex: Leaking tex %08x [ %s ] - was attached for drawing at time of delete",tex, tex->m_layout->m_layoutSummary ); + + #if 0 + // can't actually do this yet as the draw calls will tank + FOR_EACH_VEC( m_fboTable, i ) + { + CGLMFBO *fbo = m_fboTable[i]; + fbo->TexScrub( tex ); + } + tex->m_rtAttachCount = 0; + #endif + } + else + { + delete tex; + } + } +} + +// push and pop attrib when blit has mixed srgb source and dest? +ConVar gl_radar7954721_workaround_mixed ( "gl_radar7954721_workaround_mixed", "1" ); + +// push and pop attrib on any blit? +ConVar gl_radar7954721_workaround_all ( "gl_radar7954721_workaround_all", "0" ); + +// what attrib mask to use ? +ConVar gl_radar7954721_workaround_maskval ( "gl_radar7954721_workaround_maskval", "0" ); + +enum eBlitFormatClass +{ + eColor, + eDepth, // may not get used. not sure.. + eDepthStencil +}; + +uint glAttachFromClass[ 3 ] = { GL_COLOR_ATTACHMENT0_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_DEPTH_STENCIL_ATTACHMENT_EXT }; + +void glScrubFBO ( GLenum target ) +{ + gGL->glFramebufferRenderbufferEXT ( target, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, 0); + gGL->glFramebufferRenderbufferEXT ( target, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + gGL->glFramebufferRenderbufferEXT ( target, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + + gGL->glFramebufferTexture2DEXT ( target, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0 ); + gGL->glFramebufferTexture2DEXT ( target, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0 ); + gGL->glFramebufferTexture2DEXT ( target, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0 ); +} + +void glAttachRBOtoFBO ( GLenum target, eBlitFormatClass formatClass, uint rboName ) +{ + switch( formatClass ) + { + case eColor: + gGL->glFramebufferRenderbufferEXT ( target, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, rboName); + break; + + case eDepth: + gGL->glFramebufferRenderbufferEXT ( target, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboName); + break; + + case eDepthStencil: + gGL->glFramebufferRenderbufferEXT ( target, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboName); + gGL->glFramebufferRenderbufferEXT ( target, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboName); + break; + } +} + +void glAttachTex2DtoFBO ( GLenum target, eBlitFormatClass formatClass, uint texName, uint texMip ) +{ + switch( formatClass ) + { + case eColor: + gGL->glFramebufferTexture2DEXT ( target, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texName, texMip ); + break; + + case eDepth: + gGL->glFramebufferTexture2DEXT ( target, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texName, texMip ); + break; + + case eDepthStencil: + gGL->glFramebufferTexture2DEXT ( target, GL_DEPTH_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texName, texMip ); + break; + } +} + +ConVar gl_can_resolve_flipped("gl_can_resolve_flipped", "0" ); +ConVar gl_cannot_resolve_flipped("gl_cannot_resolve_flipped", "0" ); + +// these are only consulted if the m_cant_resolve_scaled cap bool is false. + +ConVar gl_minify_resolve_mode("gl_minify_resolve_mode", "1" ); // if scaled resolve available, for downscaled resolve blits only (i.e. internal blits) +ConVar gl_magnify_resolve_mode("gl_magnify_resolve_mode", "2" ); // if scaled resolve available, for upscaled resolve blits only + + // 0 == old style, two steps + // 1 == faster, one step blit aka XGL_SCALED_RESOLVE_FASTEST_EXT - if available. + // 2 == faster, one step blit aka XGL_SCALED_RESOLVE_NICEST_EXT - if available. + +void GLMContext::SaveColorMaskAndSetToDefault() +{ + // NVidia's driver doesn't ignore the colormask during blitframebuffer calls, so we need to save/restore it: + // “The bug here is that our driver fails to ignore colormask for BlitFramebuffer calls. This was unclear in the original spec, but we resolved it in Khronos last year (https://cvs.khronos.org/bugzilla/show_bug.cgi?id=7969).” + m_ColorMaskSingle.Read( &m_SavedColorMask, 0 ); + + GLColorMaskSingle_t newColorMask; + newColorMask.r = newColorMask.g = newColorMask.b = newColorMask.a = -1; + m_ColorMaskSingle.Write( &newColorMask ); +} + +void GLMContext::RestoreSavedColorMask() +{ + m_ColorMaskSingle.Write( &m_SavedColorMask ); +} + +void GLMContext::Blit2( CGLMTex *srcTex, GLMRect *srcRect, int srcFace, int srcMip, CGLMTex *dstTex, GLMRect *dstRect, int dstFace, int dstMip, uint filter ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "Blit2" ); + g_TelemetryGPUStats.m_nTotalBlit2++; +#endif + + SaveColorMaskAndSetToDefault(); + + Assert( srcFace == 0 ); + Assert( dstFace == 0 ); + + //----------------------------------------------------------------- format assessment + + eBlitFormatClass formatClass = eColor; + uint blitMask= 0; + + switch( srcTex->m_layout->m_format->m_glDataFormat ) + { + case GL_RED: case GL_BGRA: case GL_RGB: case GL_RGBA: case GL_ALPHA: case GL_LUMINANCE: case GL_LUMINANCE_ALPHA: + formatClass = eColor; + blitMask = GL_COLOR_BUFFER_BIT; + break; + + case GL_DEPTH_COMPONENT: + formatClass = eDepth; + blitMask = GL_DEPTH_BUFFER_BIT; + break; + + case GL_DEPTH_STENCIL_EXT: + formatClass = eDepthStencil; + blitMask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + break; + + default: + Assert(!"Unsupported format for blit" ); + GLMStop(); + break; + } + + //----------------------------------------------------------------- blit assessment + + + bool blitResolves = srcTex->m_rboName != 0; + bool blitScales = ((srcRect->xmax - srcRect->xmin) != (dstRect->xmax - dstRect->xmin)) || ((srcRect->ymax - srcRect->ymin) != (dstRect->ymax - dstRect->ymin)); + + bool blitToBack = (dstTex == NULL); + bool blitFlips = blitToBack; // implicit y-flip upon blit to GL_BACK supplied + + //should we support blitFromBack ? + + bool srcGamma = srcTex && ((srcTex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0); + bool dstGamma = dstTex && ((dstTex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0); + + bool doPushPop = (srcGamma != dstGamma) && gl_radar7954721_workaround_mixed.GetInt() && m_caps.m_nv; // workaround for cross gamma blit problems on NV + // ^^ need to re-check this on some post-10.6.3 build on NV to see if it was fixed + + if (doPushPop) + { + gGL->glPushAttrib( 0 ); + } + + //----------------------------------------------------------------- figure out the plan + + bool blitTwoStep = false; // think positive + + // each subsequent segment here can only set blitTwoStep, not clear it. + // the common case where these get hit is resolve out to presentation + // there may be GL extensions or driver revisions which start doing these safely. + // ideally many blits internally resolve without scaling and can thus go direct without using the scratch tex. + + if (blitResolves && (blitFlips||blitToBack)) // flips, blit to back, same thing (for now) + { + if( gl_cannot_resolve_flipped.GetInt() ) + { + blitTwoStep = true; + } + else if (!gl_can_resolve_flipped.GetInt()) + { + blitTwoStep = blitTwoStep || m_caps.m_cantResolveFlipped; // if neither convar renders an opinion, fall back to the caps to decide if we have to two-step. + } + } + + // only consider trying to use the scaling resolve filter, + // if we are confident we are not headed for two step mode already. + if (!blitTwoStep) + { + if (blitResolves && blitScales) + { + if (m_caps.m_cantResolveScaled) + { + // filter is unchanged, two step mode switches on + blitTwoStep = true; + } + else + { + bool blitScalesDown = ((srcRect->xmax - srcRect->xmin) > (dstRect->xmax - dstRect->xmin)) || ((srcRect->ymax - srcRect->ymin) > (dstRect->ymax - dstRect->ymin)); + int mode = (blitScalesDown) ? gl_minify_resolve_mode.GetInt() : gl_magnify_resolve_mode.GetInt(); + + // roughly speaking, resolve blits that minify represent setup for special effects ("copy framebuffer to me") + // resolve blits that magnify are almost always on the final present in the case where remder size < display size + + switch( mode ) + { + case 0: + default: + // filter is unchanged, two step mode + blitTwoStep = true; + break; + + case 1: + // filter goes to fastest, one step mode + blitTwoStep = false; + filter = XGL_SCALED_RESOLVE_FASTEST_EXT; + break; + + case 2: + // filter goes to nicest, one step mode + blitTwoStep = false; + filter = XGL_SCALED_RESOLVE_NICEST_EXT; + break; + } + } + } + } + + //----------------------------------------------------------------- save old scissor state and disable scissor + GLScissorEnable_t oldsciss,newsciss; + m_ScissorEnable.Read( &oldsciss, 0 ); + + if (oldsciss.enable) + { + // turn off scissor + newsciss.enable = false; + m_ScissorEnable.Write( &newsciss ); + } + + //----------------------------------------------------------------- fork in the road, depending on two-step or not + if (blitTwoStep) + { + // a resolve that can't be done directly due to constraints on scaling or flipping. + + // bind scratch FBO0 to read, scrub it, attach RBO + BindFBOToCtx ( m_scratchFBO[0], GL_READ_FRAMEBUFFER_EXT ); + glScrubFBO ( GL_READ_FRAMEBUFFER_EXT ); + glAttachRBOtoFBO ( GL_READ_FRAMEBUFFER_EXT, formatClass, srcTex->m_rboName ); + + // bind scratch FBO1 to write, scrub it, attach scratch tex + BindFBOToCtx ( m_scratchFBO[1], GL_DRAW_FRAMEBUFFER_EXT ); + glScrubFBO ( GL_DRAW_FRAMEBUFFER_EXT ); + glAttachTex2DtoFBO ( GL_DRAW_FRAMEBUFFER_EXT, formatClass, srcTex->m_texName, 0 ); + + // set read and draw buffers appropriately + gGL->glReadBuffer ( glAttachFromClass[formatClass] ); + gGL->glDrawBuffer ( glAttachFromClass[formatClass] ); + + // blit#1 - to resolve to scratch + // implicitly means no scaling, thus will be done with NEAREST sampling + + GLenum resolveFilter = GL_NEAREST; + + gGL->glBlitFramebufferEXT( 0, 0, srcTex->m_layout->m_key.m_xSize, srcTex->m_layout->m_key.m_ySize, + 0, 0, srcTex->m_layout->m_key.m_xSize, srcTex->m_layout->m_key.m_ySize, // same source and dest rect, whole surface + blitMask, resolveFilter ); + + // FBO1 now holds the interesting content. + // scrub FBO0, bind FBO1 to READ, fall through to next stage of blit where 1 goes onto 0 (or BACK) + + glScrubFBO ( GL_READ_FRAMEBUFFER_EXT ); // zap FBO0 + BindFBOToCtx ( m_scratchFBO[1], GL_READ_FRAMEBUFFER_EXT ); + + srcTex->ForceRBONonDirty(); + } + else + { +#if 1 + if (srcTex->m_pBlitSrcFBO == NULL) + { + srcTex->m_pBlitSrcFBO = NewFBO(); + BindFBOToCtx( srcTex->m_pBlitSrcFBO, GL_READ_FRAMEBUFFER_EXT ); + if (blitResolves) + { + glAttachRBOtoFBO( GL_READ_FRAMEBUFFER_EXT, formatClass, srcTex->m_rboName ); + } + else + { + glAttachTex2DtoFBO( GL_READ_FRAMEBUFFER_EXT, formatClass, srcTex->m_texName, srcMip ); + } + } + else + { + BindFBOToCtx ( srcTex->m_pBlitSrcFBO, GL_READ_FRAMEBUFFER_EXT ); + // GLMCheckError(); + } +#else + // arrange source surface on FBO1 for blit directly to dest (which could be FBO0 or BACK) + BindFBOToCtx( m_scratchFBO[1], GL_READ_FRAMEBUFFER_EXT ); + glScrubFBO( GL_READ_FRAMEBUFFER_EXT ); + GLMCheckError(); + if (blitResolves) + { + glAttachRBOtoFBO( GL_READ_FRAMEBUFFER_EXT, formatClass, srcTex->m_rboName ); + } + else + { + glAttachTex2DtoFBO( GL_READ_FRAMEBUFFER_EXT, formatClass, srcTex->m_texName, srcMip ); + } +#endif + + gGL->glReadBuffer( glAttachFromClass[formatClass] ); + } + + //----------------------------------------------------------------- zero or one blits may have happened above, whichever took place, FBO1 is now on read + + bool yflip = false; + if (blitToBack) + { + // backbuffer is special - FBO0 is left out (either scrubbed already, or not used) + + BindFBOToCtx ( NULL, GL_DRAW_FRAMEBUFFER_EXT ); + gGL->glDrawBuffer ( GL_BACK ); + + yflip = true; + } + else + { + // not going to GL_BACK - use FBO0. set up dest tex or RBO on it. i.e. it's OK to blit from MSAA to MSAA if needed, though unlikely. + Assert( dstTex != NULL ); +#if 1 + if (dstTex->m_pBlitDstFBO == NULL) + { + dstTex->m_pBlitDstFBO = NewFBO(); + BindFBOToCtx( dstTex->m_pBlitDstFBO, GL_DRAW_FRAMEBUFFER_EXT ); + if (dstTex->m_rboName) + { + glAttachRBOtoFBO( GL_DRAW_FRAMEBUFFER_EXT, formatClass, dstTex->m_rboName ); + } + else + { + glAttachTex2DtoFBO( GL_DRAW_FRAMEBUFFER_EXT, formatClass, dstTex->m_texName, dstMip ); + } + } + else + { + BindFBOToCtx( dstTex->m_pBlitDstFBO, GL_DRAW_FRAMEBUFFER_EXT ); + } +#else + BindFBOToCtx( m_scratchFBO[0], GL_DRAW_FRAMEBUFFER_EXT ); GLMCheckError(); + glScrubFBO( GL_DRAW_FRAMEBUFFER_EXT ); + + if (dstTex->m_rboName) + { + glAttachRBOtoFBO( GL_DRAW_FRAMEBUFFER_EXT, formatClass, dstTex->m_rboName ); + } + else + { + glAttachTex2DtoFBO( GL_DRAW_FRAMEBUFFER_EXT, formatClass, dstTex->m_texName, dstMip ); + } + + gGL->glDrawBuffer ( glAttachFromClass[formatClass] ); GLMCheckError(); +#endif + } + + // final blit + + // i think in general, if we are blitting same size, gl_nearest is the right filter to pass. + // this re-steering won't kick in if there is scaling or a special scaled resolve going on. + if (!blitScales) + { + // steer it + filter = GL_NEAREST; + } + + // this is blit #1 or #2 depending on what took place above. + if (yflip) + { + gGL->glBlitFramebufferEXT( srcRect->xmin, srcRect->ymin, srcRect->xmax, srcRect->ymax, + dstRect->xmin, dstRect->ymax, dstRect->xmax, dstRect->ymin, // note dest Y's are flipped + blitMask, filter ); + } + else + { + gGL->glBlitFramebufferEXT( srcRect->xmin, srcRect->ymin, srcRect->xmax, srcRect->ymax, + dstRect->xmin, dstRect->ymin, dstRect->xmax, dstRect->ymax, + blitMask, filter ); + } + + //----------------------------------------------------------------- scrub READ and maybe DRAW FBO, and unbind + +// glScrubFBO ( GL_READ_FRAMEBUFFER_EXT ); + BindFBOToCtx ( NULL, GL_READ_FRAMEBUFFER_EXT ); + if (!blitToBack) + { +// glScrubFBO ( GL_DRAW_FRAMEBUFFER_EXT ); + BindFBOToCtx ( NULL, GL_DRAW_FRAMEBUFFER_EXT ); + } + + //----------------------------------------------------------------- restore GLM's drawing FBO + + // restore GLM drawing FBO + BindFBOToCtx( m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + if (doPushPop) + { + gGL->glPopAttrib( ); + } + + + //----------------------------------------------------------------- restore old scissor state + if (oldsciss.enable) + { + m_ScissorEnable.Write( &oldsciss ); + } + + RestoreSavedColorMask(); +} + + +void GLMContext::BlitTex( CGLMTex *srcTex, GLMRect *srcRect, int srcFace, int srcMip, CGLMTex *dstTex, GLMRect *dstRect, int dstFace, int dstMip, GLenum filter, bool useBlitFB ) +{ + // This path doesn't work anymore (or did it ever work in the L4D2 Linux branch?) + DXABSTRACT_BREAK_ON_ERROR(); + return; + + SaveColorMaskAndSetToDefault(); + + switch( srcTex->m_layout->m_format->m_glDataFormat ) + { + case GL_BGRA: + case GL_RGB: + case GL_RGBA: + case GL_ALPHA: + case GL_LUMINANCE: + case GL_LUMINANCE_ALPHA: + #if 0 + if (GLMKnob("caps-key",NULL) > 0.0) + { + useBlitFB = false; + } + #endif + + if ( m_caps.m_cantBlitReliably ) // this is referring to a problem with the x3100.. + { + useBlitFB = false; + } + break; + } + + if (0) + { + GLMPRINTF(("-D- Blit from %d %d %d %d to %d %d %d %d", + srcRect->xmin, srcRect->ymin, srcRect->xmax, srcRect->ymax, + dstRect->xmin, dstRect->ymin, dstRect->xmax, dstRect->ymax + )); + + GLMPRINTF(( "-D- src tex layout is %s", srcTex->m_layout->m_layoutSummary )); + GLMPRINTF(( "-D- dst tex layout is %s", dstTex->m_layout->m_layoutSummary )); + } + + int pushed = 0; + uint pushmask = gl_radar7954721_workaround_maskval.GetInt(); + //GL_COLOR_BUFFER_BIT + //| GL_CURRENT_BIT + //| GL_ENABLE_BIT + //| GL_FOG_BIT + //| GL_PIXEL_MODE_BIT + //| GL_SCISSOR_BIT + //| GL_STENCIL_BUFFER_BIT + //| GL_TEXTURE_BIT + //GL_VIEWPORT_BIT + //; + + if (gl_radar7954721_workaround_all.GetInt()!=0) + { + gGL->glPushAttrib( pushmask ); + pushed++; + } + else + { + bool srcGamma = (srcTex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0; + bool dstGamma = (dstTex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0; + + if (srcGamma != dstGamma) + { + if (gl_radar7954721_workaround_mixed.GetInt()) + { + gGL->glPushAttrib( pushmask ); + pushed++; + } + } + } + + if (useBlitFB) + { + // state we need to save + // current setting of scissor + // current setting of the drawing fbo (no explicit save, it's in the context) + GLScissorEnable_t oldsciss,newsciss; + m_ScissorEnable.Read( &oldsciss, 0 ); + + // remember to restore m_drawingFBO at end of effort + + // setup + // turn off scissor + newsciss.enable = false; + m_ScissorEnable.Write( &newsciss ); + + // select which attachment enum we're going to use for the blit + // default to color0, unless it's a depth or stencil flava + + Assert( srcTex->m_layout->m_format->m_glDataFormat == dstTex->m_layout->m_format->m_glDataFormat ); + + EGLMFBOAttachment attachIndex = (EGLMFBOAttachment)0; + GLenum attachIndexGL = 0; + GLuint blitMask = 0; + switch( srcTex->m_layout->m_format->m_glDataFormat ) + { + case GL_BGRA: + case GL_RGB: + case GL_RGBA: + case GL_ALPHA: + case GL_LUMINANCE: + case GL_LUMINANCE_ALPHA: + attachIndex = kAttColor0; + attachIndexGL = GL_COLOR_ATTACHMENT0_EXT; + blitMask = GL_COLOR_BUFFER_BIT; + break; + + case GL_DEPTH_COMPONENT: + attachIndex = kAttDepth; + attachIndexGL = GL_DEPTH_ATTACHMENT_EXT; + blitMask = GL_DEPTH_BUFFER_BIT; + break; + + case GL_DEPTH_STENCIL_EXT: + attachIndex = kAttDepthStencil; + attachIndexGL = GL_DEPTH_STENCIL_ATTACHMENT_EXT; + blitMask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + break; + + default: + Assert(0); + break; + } + + // set the read fb, attach read tex at appropriate attach point, set read buffer + BindFBOToCtx( m_blitReadFBO, GL_READ_FRAMEBUFFER_EXT ); + + GLMFBOTexAttachParams attparams; + attparams.m_tex = srcTex; + attparams.m_face = srcFace; + attparams.m_mip = srcMip; + attparams.m_zslice = 0; + m_blitReadFBO->TexAttach( &attparams, attachIndex, GL_READ_FRAMEBUFFER_EXT ); + + gGL->glReadBuffer( attachIndexGL ); + + + // set the write fb and buffer, and attach write tex + BindFBOToCtx( m_blitDrawFBO, GL_DRAW_FRAMEBUFFER_EXT ); + + attparams.m_tex = dstTex; + attparams.m_face = dstFace; + attparams.m_mip = dstMip; + attparams.m_zslice = 0; + m_blitDrawFBO->TexAttach( &attparams, attachIndex, GL_DRAW_FRAMEBUFFER_EXT ); + + gGL->glDrawBuffer( attachIndexGL ); + + // do the blit + gGL->glBlitFramebufferEXT( srcRect->xmin, srcRect->ymin, srcRect->xmax, srcRect->ymax, + dstRect->xmin, dstRect->ymin, dstRect->xmax, dstRect->ymax, + blitMask, filter ); + + // cleanup + // unset the read fb and buffer, detach read tex + // unset the write fb and buffer, detach write tex + + m_blitReadFBO->TexDetach( attachIndex, GL_READ_FRAMEBUFFER_EXT ); + + m_blitDrawFBO->TexDetach( attachIndex, GL_DRAW_FRAMEBUFFER_EXT ); + + // put the original FB back in place (both read and draw) + // this bind will hit both read and draw bindings + BindFBOToCtx( m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + // set the read and write buffers back to... what ? does it matter for anything but copies ? don't worry about it + + // restore the scissor state + m_ScissorEnable.Write( &oldsciss ); + } + else + { + // textured quad style + + // we must attach the dest tex as the color buffer on the blit draw FBO + // so that means we need to re-set the drawing FBO on exit + + EGLMFBOAttachment attachIndex = (EGLMFBOAttachment)0; + GLenum attachIndexGL = 0; + switch( srcTex->m_layout->m_format->m_glDataFormat ) + { + case GL_BGRA: + case GL_RGB: + case GL_RGBA: + case GL_ALPHA: + case GL_LUMINANCE: + case GL_LUMINANCE_ALPHA: + attachIndex = kAttColor0; + attachIndexGL = GL_COLOR_ATTACHMENT0_EXT; + break; + + default: + Assert(!"Can't blit that format"); + break; + } + + BindFBOToCtx( m_blitDrawFBO, GL_DRAW_FRAMEBUFFER_EXT ); + + GLMFBOTexAttachParams attparams; + attparams.m_tex = dstTex; + attparams.m_face = dstFace; + attparams.m_mip = dstMip; + attparams.m_zslice = 0; + m_blitDrawFBO->TexAttach( &attparams, attachIndex, GL_DRAW_FRAMEBUFFER_EXT ); + + gGL->glDrawBuffer( attachIndexGL ); + + // attempt to just set states directly the way we want them, then use the latched states to repair them afterward. + NullProgram(); // out of program mode + + gGL->glDisable ( GL_ALPHA_TEST ); + gGL->glDisable ( GL_CULL_FACE ); + gGL->glDisable ( GL_POLYGON_OFFSET_FILL ); + gGL->glDisable ( GL_SCISSOR_TEST ); + + gGL->glDisable ( GL_CLIP_PLANE0 ); + gGL->glDisable ( GL_CLIP_PLANE1 ); + + gGL->glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + gGL->glDisable ( GL_BLEND ); + + gGL->glDepthMask ( GL_FALSE ); + gGL->glDisable ( GL_DEPTH_TEST ); + + gGL->glDisable ( GL_STENCIL_TEST ); + gGL->glStencilMask ( GL_FALSE ); + + + // now do the unlit textured quad... + gGL->glActiveTexture( GL_TEXTURE0 ); + gGL->glBindTexture( GL_TEXTURE_2D, srcTex->m_texName ); + + gGL->glEnable(GL_TEXTURE_2D); + + // immediate mode is fine + + float topv = 1.0; + float botv = 0.0; + + gGL->glBegin(GL_QUADS); + gGL->glTexCoord2f ( 0.0, botv ); + gGL->glVertex3f ( -1.0, -1.0, 0.0 ); + + gGL->glTexCoord2f ( 1.0, botv ); + gGL->glVertex3f ( 1.0, -1.0, 0.0 ); + + gGL->glTexCoord2f ( 1.0, topv ); + gGL->glVertex3f ( 1.0, 1.0, 0.0 ); + + gGL->glTexCoord2f ( 0.0, topv ); + gGL->glVertex3f ( -1.0, 1.0, 0.0 ); + gGL->glEnd(); + + gGL->glBindTexture( GL_TEXTURE_2D, 0 ); + + gGL->glDisable(GL_TEXTURE_2D); + + BindTexToTMU( m_samplers[0].m_pBoundTex, 0 ); + + // leave active program empty - flush draw states will fix + + // then restore states using the scoreboard + + m_AlphaTestEnable.Flush(); + m_AlphaToCoverageEnable.Flush(); + m_CullFaceEnable.Flush(); + m_DepthBias.Flush(); + m_ScissorEnable.Flush(); + + m_ClipPlaneEnable.FlushIndex( 0 ); + m_ClipPlaneEnable.FlushIndex( 1 ); + + m_ColorMaskSingle.Flush(); + m_BlendEnable.Flush(); + + m_DepthMask.Flush(); + m_DepthTestEnable.Flush(); + + m_StencilWriteMask.Flush(); + m_StencilTestEnable.Flush(); + + // unset the write fb and buffer, detach write tex + + m_blitDrawFBO->TexDetach( attachIndex, GL_DRAW_FRAMEBUFFER_EXT ); + + // put the original FB back in place (both read and draw) + BindFBOToCtx( m_drawingFBO, GL_FRAMEBUFFER_EXT ); + } + + while(pushed) + { + gGL->glPopAttrib(); + pushed--; + } + + RestoreSavedColorMask(); +} + +void GLMContext::ResolveTex( CGLMTex *tex, bool forceDirty ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "ResolveTex" ); + g_TelemetryGPUStats.m_nTotalResolveTex++; +#endif + + // only run resolve if it's (a) possible and (b) dirty or force-dirtied + if ( ( tex->m_rboName ) && ( tex->IsRBODirty() || forceDirty ) ) + { + // state we need to save + // current setting of scissor + // current setting of the drawing fbo (no explicit save, it's in the context) + GLScissorEnable_t oldsciss,newsciss; + m_ScissorEnable.Read( &oldsciss, 0 ); + + // remember to restore m_drawingFBO at end of effort + + // setup + // turn off scissor + newsciss.enable = false; + m_ScissorEnable.Write( &newsciss ); + + // select which attachment enum we're going to use for the blit + // default to color0, unless it's a depth or stencil flava + + // for resolve, only handle a modest subset of the possible formats + EGLMFBOAttachment attachIndex = (EGLMFBOAttachment)0; + GLenum attachIndexGL = 0; + GLuint blitMask = 0; + switch( tex->m_layout->m_format->m_glDataFormat ) + { + case GL_BGRA: + case GL_RGB: + case GL_RGBA: + // case GL_ALPHA: + // case GL_LUMINANCE: + // case GL_LUMINANCE_ALPHA: + attachIndex = kAttColor0; + attachIndexGL = GL_COLOR_ATTACHMENT0_EXT; + blitMask = GL_COLOR_BUFFER_BIT; + break; + + // case GL_DEPTH_COMPONENT: + // attachIndex = kAttDepth; + // attachIndexGL = GL_DEPTH_ATTACHMENT_EXT; + // blitMask = GL_DEPTH_BUFFER_BIT; + // break; + + case GL_DEPTH_STENCIL_EXT: + attachIndex = kAttDepthStencil; + attachIndexGL = GL_DEPTH_STENCIL_ATTACHMENT_EXT; + blitMask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + break; + + default: + Assert(!"Unsupported format for MSAA resolve" ); + break; + } + + + // set the read fb, attach read RBO at appropriate attach point, set read buffer + BindFBOToCtx( m_blitReadFBO, GL_READ_FRAMEBUFFER_EXT ); + + // going to avoid the TexAttach / TexDetach calls due to potential confusion, implement it directly here + + //----------------------------------------------------------------------------------- + // put tex->m_rboName on the read FB's attachment + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // you have to attach it both places... + // http://www.opengl.org/wiki/GL_EXT_framebuffer_object + + // bind the RBO to the GL_RENDERBUFFER_EXT target - is this extraneous ? + //glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, tex->m_rboName ); + + // attach the GL_RENDERBUFFER_EXT target to the depth and stencil attach points + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, tex->m_rboName); + + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, tex->m_rboName); + + // no need to leave the RBO hanging on + //glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + } + else + { + //glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, tex->m_rboName ); + + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, attachIndexGL, GL_RENDERBUFFER_EXT, tex->m_rboName); + + //glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + } + + gGL->glReadBuffer( attachIndexGL ); + + //----------------------------------------------------------------------------------- + // put tex->m_texName on the draw FBO attachment + + // set the write fb and buffer, and attach write tex + BindFBOToCtx( m_blitDrawFBO, GL_DRAW_FRAMEBUFFER_EXT ); + + // regular path - attaching a texture2d + + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, tex->m_texName, 0 ); + + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, tex->m_texName, 0 ); + } + else + { + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, attachIndexGL, GL_TEXTURE_2D, tex->m_texName, 0 ); + } + + gGL->glDrawBuffer( attachIndexGL ); + + //----------------------------------------------------------------------------------- + + // blit + gGL->glBlitFramebufferEXT( 0, 0, tex->m_layout->m_key.m_xSize, tex->m_layout->m_key.m_ySize, + 0, 0, tex->m_layout->m_key.m_xSize, tex->m_layout->m_key.m_ySize, + blitMask, GL_NEAREST ); + // or should it be GL_LINEAR? does it matter ? + + //----------------------------------------------------------------------------------- + // cleanup + //----------------------------------------------------------------------------------- + + + // unset the read fb and buffer, detach read RBO + //glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // detach the GL_RENDERBUFFER_EXT target from the depth and stencil attach points + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + } + else + { + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, attachIndexGL, GL_RENDERBUFFER_EXT, 0); + } + + //----------------------------------------------------------------------------------- + // unset the write fb and buffer, detach write tex + + + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0 ); + + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0 ); + } + else + { + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, attachIndexGL, GL_TEXTURE_2D, 0, 0 ); + } + + // put the original FB back in place (both read and draw) + // this bind will hit both read and draw bindings + BindFBOToCtx( m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + // set the read and write buffers back to... what ? does it matter for anything but copies ? don't worry about it + + // restore the scissor state + m_ScissorEnable.Write( &oldsciss ); + + // mark the RBO clean on the resolved tex + tex->ForceRBONonDirty(); + } +} + +void GLMContext::PreloadTex( CGLMTex *tex, bool force ) +{ + // if conditions allow (i.e. a drawing surface is active) + // bind the texture on TMU 15 + // set up a dummy program to sample it but not write (use 'discard') + // draw a teeny little triangle that won't generate a lot of fragments + if (!m_pairCache) + return; + + if (!m_drawingFBO) + return; + + if (tex->m_texPreloaded && !force) // only do one preload unless forced to re-do + { + //printf("\nnot-preloading %s", tex->m_debugLabel ? tex->m_debugLabel : "(unknown)"); + return; + } + + //printf("\npreloading %s", tex->m_debugLabel ? tex->m_debugLabel : "(unknown)"); + + CGLMProgram *vp = m_preloadTexVertexProgram; + CGLMProgram *fp = NULL; + switch(tex->m_layout->m_key.m_texGLTarget) + { + case GL_TEXTURE_2D: fp = m_preload2DTexFragmentProgram; + break; + + case GL_TEXTURE_3D: fp = m_preload3DTexFragmentProgram; + break; + + case GL_TEXTURE_CUBE_MAP: fp = m_preloadCubeTexFragmentProgram; + break; + } + if (!fp) + return; + + CGLMShaderPair *preloadPair = m_pairCache->SelectShaderPair( vp, fp, 0 ); + if (!preloadPair) + return; + + if ( !preloadPair->m_valid ) + { + if ( !preloadPair->ValidateProgramPair() ) + { + return; + } + } + + gGL->glUseProgram( (GLuint)preloadPair->m_program ); + + m_pBoundPair = preloadPair; + m_bDirtyPrograms = true; + + // almost ready to draw... + + //int tmuForPreload = 15; + + // shut down all the generic attribute arrays on the detention level - next real draw will activate them again + m_lastKnownVertexAttribMask = 0; + m_nNumSetVertexAttributes = 16; + memset( &m_boundVertexAttribs[0], 0xFF, sizeof( m_boundVertexAttribs ) ); + + // Force the next flush to reset the attributes. + ClearCurAttribs(); + + for( int index=0; index < kGLMVertexAttributeIndexMax; index++ ) + { + gGL->glDisableVertexAttribArray( index ); + } + + // bind texture and sampling params + CGLMTex *pPrevTex = m_samplers[15].m_pBoundTex; + +#ifndef OSX // 10.6 + if ( m_bUseSamplerObjects ) + { + gGL->glBindSampler( 15, 0 ); + } +#endif // !OSX + + BindTexToTMU( tex, 15 ); + + // unbind vertex/index buffers + BindBufferToCtx( kGLMVertexBuffer, NULL ); + BindBufferToCtx( kGLMIndexBuffer, NULL ); + + // draw + static float posns[] = { 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f }; + + static int indices[] = { 0, 1, 2 }; + + + gGL->glEnableVertexAttribArray( 0 ); + + gGL->glVertexAttribPointer( 0, 3, GL_FLOAT, 0, 0, posns ); + + gGL->glDrawRangeElements( GL_TRIANGLES, 0, 3, 3, GL_UNSIGNED_INT, indices); + + gGL->glDisableVertexAttribArray( 0 ); + + SetSamplerDirty( 15 ); + + BindTexToTMU( pPrevTex, 15 ); + + tex->m_texPreloaded = true; +} + + + +CGLMFBO *GLMContext::NewFBO( void ) +{ + GLM_FUNC; + + CGLMFBO *fbo = new CGLMFBO( this ); + + m_fboTable.AddToTail( fbo ); + + return fbo; +} + +void GLMContext::DelFBO( CGLMFBO *fbo ) +{ + GLM_FUNC; + + if (m_drawingFBO == fbo) + { + m_drawingFBO = NULL; //poof! + } + + if (m_boundReadFBO == fbo ) + { + BindFBOToCtx( NULL, GL_READ_FRAMEBUFFER_EXT ); + m_boundReadFBO = NULL; + } + + if (m_boundDrawFBO == fbo ) + { + BindFBOToCtx( NULL, GL_DRAW_FRAMEBUFFER_EXT ); + m_boundDrawFBO = NULL; + } + + int idx = m_fboTable.Find( fbo ); + Assert( idx >= 0 ); + if ( idx >= 0 ) + { + m_fboTable.FastRemove( idx ); + } + + delete fbo; +} + +//=============================================================================== + +CGLMProgram *GLMContext::NewProgram( EGLMProgramType type, char *progString, const char *pShaderName ) +{ + //hushed GLM_FUNC; + + CGLMProgram *prog = new CGLMProgram( this, type ); + + prog->SetProgramText( progString ); + prog->SetShaderName( pShaderName ); + prog->CompileActiveSources(); + + return prog; +} + +void GLMContext::DelProgram( CGLMProgram *pProg ) +{ + GLM_FUNC; + + if ( m_drawingProgram[ pProg->m_type ] == pProg ) + { + SetProgram( pProg->m_type, ( pProg->m_type == kGLMFragmentProgram ) ? m_pNullFragmentProgram : NULL ); + } + + // make sure to eliminate any cached pairs using this shader + bool purgeResult = m_pairCache->PurgePairsWithShader( pProg ); + (void)purgeResult; + Assert( !purgeResult ); // very unlikely to trigger + + NullProgram(); + + delete pProg; +} + +void GLMContext::NullProgram( void ) +{ + gGL->glUseProgram( 0 ); + m_pBoundPair = NULL; + m_bDirtyPrograms = true; +} + +void GLMContext::SetDrawingLang( EGLMProgramLang lang, bool immediate ) +{ + if ( !m_caps.m_hasDualShaders ) return; // ignore attempts to change language when -glmdualshaders is not engaged + + m_drawingLangAtFrameStart = lang; + if (immediate) + { + NullProgram(); + + m_drawingLang = m_drawingLangAtFrameStart; + } +} + +void GLMContext::LinkShaderPair( CGLMProgram *vp, CGLMProgram *fp ) +{ + if ( (m_pairCache) && (m_drawingLang==kGLMGLSL) && (vp) && (fp) ) + { + CGLMShaderPair *pair = m_pairCache->SelectShaderPair( vp, fp, 0 ); + (void)pair; + + Assert( pair != NULL ); + + NullProgram(); // clear out any binds that were done - next draw will set it right + } +} + +void GLMContext::ValidateShaderPair( CGLMProgram *vp, CGLMProgram *fp ) +{ + if ((m_pairCache) && (m_drawingLang == kGLMGLSL) && (vp) && (fp)) + { + CGLMShaderPair *pair = m_pairCache->SelectShaderPair( vp, fp, 0 ); + Assert( pair != NULL ); + pair->ValidateProgramPair(); + + NullProgram(); // clear out any binds that were done - next draw will set it right + } +} + +void GLMContext::ClearShaderPairCache( void ) +{ + if (m_pairCache) + { + NullProgram(); + m_pairCache->Purge(); // bye bye all linked pairs + NullProgram(); + } +} + +void GLMContext::QueryShaderPair( int index, GLMShaderPairInfo *infoOut ) +{ + if (m_pairCache) + { + m_pairCache->QueryShaderPair( index, infoOut ); + } + else + { + memset( infoOut, 0, sizeof( *infoOut ) ); + infoOut->m_status = -1; + } +} + +CGLMBuffer *GLMContext::NewBuffer( EGLMBufferType type, uint size, uint options ) +{ + //hushed GLM_FUNC; + + CGLMBuffer *prog = new CGLMBuffer( this, type, size, options ); + + return prog; +} + +void GLMContext::DelBuffer( CGLMBuffer *buff ) +{ + GLM_FUNC; + + for( int index = 0; index < kGLMVertexAttributeIndexMax; index++ ) + { + if ( m_drawVertexSetup.m_attrs[index].m_pBuffer == buff ) + { + // just clear the enable mask - this will force all the attrs to get re-sent on next sync + m_drawVertexSetup.m_attrMask = 0; + } + } + + BindGLBufferToCtx( buff->m_buffGLTarget, NULL, false ); + + delete buff; +} + +GLMVertexSetup g_blank_setup; + +void GLMContext::Clear( bool color, unsigned long colorValue, bool depth, float depthValue, bool stencil, unsigned int stencilValue, GLScissorBox_t *box ) +{ + GLM_FUNC; + + ++m_nBatchCounter; + +#if GLMDEBUG + GLMDebugHookInfo info; + memset( &info, 0, sizeof(info) ); + info.m_caller = eClear; + + do + { +#endif + uint mask = 0; + + GLClearColor_t clearcol; + GLClearDepth_t cleardep = { depthValue }; + GLClearStencil_t clearsten = { (GLint)stencilValue }; + + // depth write mask must be saved&restored + GLDepthMask_t olddepthmask; + GLDepthMask_t newdepthmask = { true }; + + // stencil write mask must be saved and restored + GLStencilWriteMask_t oldstenmask; + GLStencilWriteMask_t newstenmask = { (GLint)0xFFFFFFFF }; + + GLColorMaskSingle_t oldcolormask; + GLColorMaskSingle_t newcolormask = { -1,-1,-1,-1 }; // D3D clears do not honor color mask, so force it + + if (color) + { + // #define D3DCOLOR_ARGB(a,r,g,b) ((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff))) + + clearcol.r = ((colorValue >> 16) & 0xFF) / 255.0f; //R + clearcol.g = ((colorValue >> 8) & 0xFF) / 255.0f; //G + clearcol.b = ((colorValue ) & 0xFF) / 255.0f; //B + clearcol.a = ((colorValue >> 24) & 0xFF) / 255.0f; //A + + m_ClearColor.Write( &clearcol ); // no check, no wait + mask |= GL_COLOR_BUFFER_BIT; + + // save and set color mask + m_ColorMaskSingle.Read( &oldcolormask, 0 ); + m_ColorMaskSingle.Write( &newcolormask ); + } + + if (depth) + { + // get old depth write mask + m_DepthMask.Read( &olddepthmask, 0 ); + m_DepthMask.Write( &newdepthmask ); + m_ClearDepth.Write( &cleardep ); // no check, no wait + mask |= GL_DEPTH_BUFFER_BIT; + } + + if (stencil) + { + m_ClearStencil.Write( &clearsten ); // no check, no wait + mask |= GL_STENCIL_BUFFER_BIT; + + // save and set sten mask + m_StencilWriteMask.Read( &oldstenmask, 0 ); + m_StencilWriteMask.Write( &newstenmask ); + } + + bool subrect = (box != NULL); + GLScissorEnable_t scissorEnableSave; + GLScissorEnable_t scissorEnableNew = { true }; + + GLScissorBox_t scissorBoxSave; + GLScissorBox_t scissorBoxNew; + + if (subrect) + { + // save current scissorbox and enable + m_ScissorEnable.Read( &scissorEnableSave, 0 ); + m_ScissorBox.Read( &scissorBoxSave, 0 ); + + if(0) + { + // calc new scissorbox as intersection against *box + + // max of the mins + scissorBoxNew.x = MAX(scissorBoxSave.x, box->x); + scissorBoxNew.y = MAX(scissorBoxSave.y, box->y); + + // min of the maxes + scissorBoxNew.width = ( MIN(scissorBoxSave.x+scissorBoxSave.width, box->x+box->width)) - scissorBoxNew.x; + + // height is just min of the max y's, minus the new base Y + scissorBoxNew.height = ( MIN(scissorBoxSave.y+scissorBoxSave.height, box->y+box->height)) - scissorBoxNew.y; + } + else + { + // ignore old scissor box completely. + scissorBoxNew = *box; + } + // set new box and enable + m_ScissorEnable.Write( &scissorEnableNew ); + m_ScissorBox.Write( &scissorBoxNew ); + } + + gGL->glClear( mask ); + + if (subrect) + { + // put old scissor box and enable back + m_ScissorEnable.Write( &scissorEnableSave ); + m_ScissorBox.Write( &scissorBoxSave ); + } + + if (depth) + { + // put old depth write mask + m_DepthMask.Write( &olddepthmask ); + } + + if (color) + { + // put old color write mask + m_ColorMaskSingle.Write( &oldcolormask ); + } + + if (stencil) + { + // put old sten mask + m_StencilWriteMask.Write( &oldstenmask ); + } + +#if GLMDEBUG + DebugHook( &info ); + } while (info.m_loop); +#endif +} + + +// stolen from glmgrbasics.cpp +extern "C" uint GetCurrentKeyModifiers( void ); +enum ECarbonModKeyIndex +{ + EcmdKeyBit = 8, /* command key down?*/ + EshiftKeyBit = 9, /* shift key down?*/ + EalphaLockBit = 10, /* alpha lock down?*/ + EoptionKeyBit = 11, /* option key down?*/ + EcontrolKeyBit = 12 /* control key down?*/ +}; + +enum ECarbonModKeyMask +{ + EcmdKey = 1 << EcmdKeyBit, + EshiftKey = 1 << EshiftKeyBit, + EalphaLock = 1 << EalphaLockBit, + EoptionKey = 1 << EoptionKeyBit, + EcontrolKey = 1 << EcontrolKeyBit +}; + +static ConVar gl_flushpaircache ("gl_flushpaircache", "0"); +static ConVar gl_paircachestats ("gl_paircachestats", "0"); +static ConVar gl_mtglflush_at_tof ("gl_mtglflush_at_tof", "0"); +static ConVar gl_texlayoutstats ("gl_texlayoutstats", "0" ); + +void GLMContext::BeginFrame( void ) +{ + GLM_FUNC; + + m_debugFrameIndex++; + + // check for lang change at TOF + if (m_caps.m_hasDualShaders) + { + if (m_drawingLang != m_drawingLangAtFrameStart) + { + // language change. unbind everything.. + NullProgram(); + + m_drawingLang = m_drawingLangAtFrameStart; + } + } + + // scrub some critical shock absorbers + for( int i=0; i< 16; i++) + { + gGL->glDisableVertexAttribArray( i ); // enable GLSL attribute- this is just client state - will be turned back off + } + m_lastKnownVertexAttribMask = 0; + m_nNumSetVertexAttributes = 0; + + //FIXME should we also zap the m_lastKnownAttribs array ? (worst case it just sets them all again on first batch) + + BindBufferToCtx( kGLMVertexBuffer, NULL, true ); + BindBufferToCtx( kGLMIndexBuffer, NULL, true ); + + if (gl_flushpaircache.GetInt()) + { + // do the flush and then set back to zero + ClearShaderPairCache(); + + printf("\n\n##### shader pair cache cleared\n\n"); + gl_flushpaircache.SetValue( 0 ); + } + + if (gl_paircachestats.GetInt()) + { + // do the flush and then set back to zero + m_pairCache->DumpStats(); + + gl_paircachestats.SetValue( 0 ); + } + + if (gl_texlayoutstats.GetInt()) + { + m_texLayoutTable->DumpStats(); + + gl_texlayoutstats.SetValue( 0 ); + } + + if (gl_mtglflush_at_tof.GetInt()) + { + gGL->glFlush(); // TOF flush - skip this if benchmarking, enable it if human playing (smoothness) + } + +#if GLMDEBUG + // init debug hook information + GLMDebugHookInfo info; + memset( &info, 0, sizeof(info) ); + info.m_caller = eBeginFrame; + + do + { + DebugHook( &info ); + } while (info.m_loop); + +#endif + +} + +void GLMContext::EndFrame( void ) +{ + GLM_FUNC; + +#if GLMDEBUG + // init debug hook information + GLMDebugHookInfo info; + memset( &info, 0, sizeof(info) ); + info.m_caller = eEndFrame; + + do + { + DebugHook( &info ); + } while (info.m_loop); +#endif +} + +//=============================================================================== + +CGLMQuery *GLMContext::NewQuery( GLMQueryParams *params ) +{ + CGLMQuery *query = new CGLMQuery( this, params ); + + return query; +} + +void GLMContext::DelQuery( CGLMQuery *query ) +{ + // may want to do some finish/ + delete query; +} + +static ConVar mat_vsync( "mat_vsync", "0", 0, "Force sync to vertical retrace", true, 0.0, true, 1.0 ); + +//=============================================================================== + +ConVar glm_nullrefresh_capslock( "glm_nullrefresh_capslock", "0" ); +ConVar glm_literefresh_capslock( "glm_literefresh_capslock", "0" ); + +extern ConVar gl_blitmode; + +void GLMContext::Present( CGLMTex *tex ) +{ + GLM_FUNC; + + { +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "GLMContext::Present" ); + g_TelemetryGPUStats.m_nTotalPresent++; +#endif + + ProcessTextureDeletes(); + +#ifdef HAVE_GL_ARB_SYNC + + if ( gGL->m_bHave_GL_AMD_pinned_memory ) + { + m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer].InsertFence(); + + m_nCurPinnedMemoryBuffer = ( m_nCurPinnedMemoryBuffer + 1 ) % cNumPinnedMemoryBuffers; + + m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer].BlockUntilNotBusy(); + + gGL->glBindBufferARB( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer].GetHandle() ); + } + + if ( gGL->m_bHave_GL_ARB_buffer_storage ) + { + for (uint lpType = 0; lpType < kGLMNumBufferTypes; ++lpType) + { + m_persistentBuffer[m_nCurPersistentBuffer][lpType].InsertFence(); + } + + m_nCurPersistentBuffer = ( m_nCurPersistentBuffer + 1 ) % cNumPersistentBuffers; + + for (uint lpType = 0; lpType < kGLMNumBufferTypes; ++lpType) + { + m_persistentBuffer[m_nCurPersistentBuffer][lpType].BlockUntilNotBusy(); + } + } + +#endif // HAVE_GL_ARB_SYNC + + bool newRefreshMode = false; + // two ways to go: + + // old school, do the resolve, had the tex down to cocoamgr to actually blit. + // that way is required if you are not in one-context mode (10.5.8) + + if ( (gl_blitmode.GetInt() != 0) ) + { + newRefreshMode = true; + } + + // this is the path whether full screen or windowed... we always blit. + CShowPixelsParams showparams; + memset( &showparams, 0, sizeof(showparams) ); + + showparams.m_srcTexName = tex->m_texName; + showparams.m_width = tex->m_layout->m_key.m_xSize; + showparams.m_height = tex->m_layout->m_key.m_ySize; + showparams.m_vsyncEnable = m_displayParams.m_vsyncEnable = mat_vsync.GetBool(); + showparams.m_fsEnable = m_displayParams.m_fsEnable; + showparams.m_useBlit = m_caps.m_hasFramebufferBlit; + + // we call showpixels once with the "only sync view" arg set, so we know what the latest surface size is, before trying to do our own blit ! + showparams.m_onlySyncView = true; + ShowPixels(&showparams); // doesn't actually show anything, just syncs window/fs state (would make a useful separate call) + showparams.m_onlySyncView = false; + + bool refresh = true; + #ifdef OSX + if ( (glm_nullrefresh_capslock.GetInt()) && (GetCurrentKeyModifiers() & EalphaLock) ) + { + refresh = false; + } + #endif + static int counter; + counter ++; + + #ifdef OSX + if ( (glm_literefresh_capslock.GetInt()) && (GetCurrentKeyModifiers() & EalphaLock) && (counter & 127) ) + { + // just show every 128th frame + refresh = false; + } + #endif + + if (refresh) + { + if (newRefreshMode) + { + // blit to GL_BACK done here, not in CocoaMgr, this lets us do resolve directly if conditions are right + + GLMRect srcRect, dstRect; + + uint dstWidth,dstHeight; + DisplayedSize( dstWidth,dstHeight ); + + srcRect.xmin = 0; + srcRect.ymin = 0; + srcRect.xmax = showparams.m_width; + srcRect.ymax = showparams.m_height; + + dstRect.xmin = 0; + dstRect.ymin = 0; + dstRect.xmax = dstWidth; + dstRect.ymax = dstHeight; + + // do not ask for LINEAR if blit is unscaled + // NULL means targeting GL_BACK. Blit2 will break it down into two steps if needed, and will handle resolve, scale, flip. + bool blitScales = (showparams.m_width != static_cast(dstWidth)) || (showparams.m_height != static_cast(dstHeight)); + Blit2( tex, &srcRect, 0,0, + NULL, &dstRect, 0,0, + blitScales ? GL_LINEAR : GL_NEAREST ); + + // we set showparams.m_noBlit, and just let CocoaMgr handle the swap (flushbuffer / page flip) + showparams.m_noBlit = true; + + BindFBOToCtx( NULL, GL_FRAMEBUFFER_EXT ); + } + else + { + ResolveTex( tex, true ); // dxabstract used to do this unconditionally.we still do if new refresh mode doesn't engage. + + BindFBOToCtx( NULL, GL_FRAMEBUFFER_EXT ); + + // showparams.m_noBlit is left set to 0. CocoaMgr does the blit. + } + + ShowPixels(&showparams); + } + + // put the original FB back in place (both read and draw) + // this bind will hit both read and draw bindings + BindFBOToCtx( m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + // put em back !! + m_ScissorEnable.Flush(); + m_ScissorBox.Flush(); + m_ViewportBox.Flush(); + } + + m_nCurFrame++; + +#if GL_BATCH_PERF_ANALYSIS + tmMessage( TELEMETRY_LEVEL2, TMMF_ICON_EXCLAMATION, "VS Uniform Calls: %u, VS Uniforms: %u|VS Uniform Bone Calls: %u, VS Bone Uniforms: %u|PS Uniform Calls: %u, PS Uniforms: %u", m_nTotalVSUniformCalls, m_nTotalVSUniformsSet, m_nTotalVSUniformBoneCalls, m_nTotalVSUniformsBoneSet, m_nTotalPSUniformCalls, m_nTotalPSUniformsSet ); + m_nTotalVSUniformCalls = 0, m_nTotalVSUniformBoneCalls = 0, m_nTotalVSUniformsSet = 0, m_nTotalVSUniformsBoneSet = 0, m_nTotalPSUniformCalls = 0, m_nTotalPSUniformsSet = 0; +#endif + +#ifndef OSX + GLMGPUTimestampManagerTick(); +#endif +} + +//=============================================================================== +// GLMContext protected methods + +// a naive implementation of this would just clear-drawable on the context at entry, +// and then capture and set fullscreen if requested. +// however that would glitch thescreen every time the user changed resolution while staying in full screen. +// but in windowed mode there's really not much to do in here. Yeah, this routine centers around obtaining +// drawables for fullscreen mode, and/or dropping those drawables if we're going back to windowed. + +// um, are we expected to re-make the standard surfaces (color, depthstencil) if the res changes? is that now this routine's job ? + +// so, kick it off with an assessment of whather we were FS previously or not. +// if there was no prior display params latched, then it wasn't. + +// changes in here take place immediately. If you want to defer display changes then that's going to be a different method. +// common assumption is that there will be two places that call this: context create and the implementation of the DX9 Reset method. +// in either case the client code is aware of what it signed up for. + +bool GLMContext::SetDisplayParams( GLMDisplayParams *params ) +{ + m_displayParams = *params; // latch em + m_displayParamsValid = true; + + return true; +} + + +ConVar gl_can_query_fast("gl_can_query_fast", "0"); + +static uint gPersistentBufferSize[kGLMNumBufferTypes] = +{ + 2 * 1024 * 1024, // kGLMVertexBuffer + 1 * 1024 * 1024, // kGLMIndexBuffer + 0, // kGLMUniformBuffer + 0, // kGLMPixelBuffer +}; + +GLMContext::GLMContext( IDirect3DDevice9 *pDevice, GLMDisplayParams *params ) +{ +// m_bUseSamplerObjects = true; +// +// // On most AMD drivers (like the current latest, 12.10 Windows), the PCF depth comparison mode doesn't work on sampler objects, so just punt them. +// if ( gGL->m_nDriverProvider == cGLDriverProviderAMD ) +// { +// m_bUseSamplerObjects = false; +// } + +// if ( CommandLine()->CheckParm( "-gl_disablesamplerobjects" ) ) +// { + // Disable sampler object usage for now since ScaleForm isn't aware of them + // and doesn't know how to push/pop their binding state. It seems we don't + // really use them in this codebase anyhow, except to preload textures. + m_bUseSamplerObjects = false; + if ( CommandLine()->CheckParm( "-gl_enablesamplerobjects" ) ) + m_bUseSamplerObjects = true; + + // Try to get some more free memory by relying on driver host copies instead of ours. + // In some cases the driver will be able to discard their own host copy and rely on GPU + // memory, reducing memory usage. + // Sadly, we have to enable tex client storage for srgb decoding. This should only happen + // on Macs w/ OSX 10.6. + m_bTexClientStorage = !gGL->m_bHave_GL_EXT_texture_sRGB_decode; + if ( CommandLine()->CheckParm( "-gl_texclientstorage" ) ) + m_bTexClientStorage = true; + + GLMDebugPrintf( "GL sampler object usage: %s\n", m_bUseSamplerObjects ? "ENABLED" : "DISABLED" ); + + m_nCurOwnerThreadId = ThreadGetCurrentId(); + m_nThreadOwnershipReleaseCounter = 0; + + m_pDevice = pDevice; + m_nCurFrame = 0; + m_nBatchCounter = 0; + + ClearCurAttribs(); + +#ifndef OSX + m_nCurPinnedMemoryBuffer = 0; + if ( gGL->m_bHave_GL_AMD_pinned_memory ) + { + for ( uint t = 0; t < cNumPinnedMemoryBuffers; t++ ) + { + m_PinnedMemoryBuffers[t].Init( GLMGR_PINNED_MEMORY_BUFFER_SIZE ); + } + + gGL->glBindBufferARB( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer].GetHandle() ); + } +#endif // OSX + + m_nCurPersistentBuffer = 0; + if ( gGL->m_bHave_GL_ARB_buffer_storage ) + { + for ( uint lpType = 0; lpType < kGLMNumBufferTypes; ++lpType ) + { + for ( uint lpNum = 0; lpNum < cNumPersistentBuffers; ++lpNum ) + { + m_persistentBuffer[lpNum][lpType].Init( (EGLMBufferType)lpType, gPersistentBufferSize[lpType] ); + } + } + } + + m_bUseBoneUniformBuffers = true; + if (CommandLine()->CheckParm("-disableboneuniformbuffers")) + { + m_bUseBoneUniformBuffers = false; + } + + m_nMaxUsedVertexProgramConstantsHint = 256; + + // flag our copy of display params as blank + m_displayParamsValid = false; + + // peek at any CLI options + m_slowAssertEnable = CommandLine()->FindParm("-glmassertslow") != 0; + m_slowSpewEnable = CommandLine()->FindParm("-glmspewslow") != 0; + m_checkglErrorsAfterEveryBatch = CommandLine()->FindParm("-glcheckerrors") != 0; + m_slowCheckEnable = m_slowAssertEnable || m_slowSpewEnable || m_checkglErrorsAfterEveryBatch; + + m_drawingLangAtFrameStart = m_drawingLang = kGLMGLSL; // default to GLSL + + // this affects FlushDrawStates which will route program bindings, uniform delivery, sampler setup, and enables accordingly. + + if ( CommandLine()->FindParm("-glslmode") ) + { + m_drawingLangAtFrameStart = m_drawingLang = kGLMGLSL; + } + if ( CommandLine()->FindParm("-arbmode") && !CommandLine()->FindParm("-glslcontrolflow") ) + { + m_drawingLangAtFrameStart = m_drawingLang = kGLMARB; + } + + // proceed with rest of init + + m_dwRenderThreadId = 0; + m_bIsThreading = false; + + m_nsctx = NULL; + m_ctx = NULL; + + int *selAttribs = NULL; + uint selWords = 0; + + memset( &m_caps, 0, sizeof( m_caps ) ); + GetDesiredPixelFormatAttribsAndRendererInfo( (uint**)&selAttribs, &selWords, &m_caps ); + uint selBytes = selWords * sizeof( uint ); selBytes; + +#if defined( USE_SDL ) + m_ctx = (SDL_GLContext)GetGLContextForWindow( params ? (void*)params->m_focusWindow : NULL ); + MakeCurrent( true ); +#else +#error +#endif + IncrementWindowRefCount(); + + // If we're using GL_ARB_debug_output, go ahead and setup the callback here. + if ( gGL->m_bHave_GL_ARB_debug_output && CommandLine()->FindParm( "-gl_debug" ) ) + { +#if GLMDEBUG + // Turning this on is a perf loss, but it ensures that you can (at least) swap to the other + // threads to see what call is currently being made. + // Note that if the driver is in multithreaded mode, you can put it back into singlethreaded mode + // and get a real stack for the offending gl call. + gGL->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + +#ifdef WIN32 + // This happens early enough during init that DevMsg() does nothing. + OutputDebugStringA( "GLMContext::GLMContext: GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB enabled!\n" ); +#else + printf( "GLMContext::GLMContext: GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB enabled!\n" ); +#endif + +#endif + // This should be there if we get in here--make sure. + Assert(gGL->glDebugMessageControlARB); + gGL->glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, (const GLuint *)NULL, GL_TRUE); + + // Gonna filter these out, they're "chatty". + gGL->glDebugMessageControlARB(GL_DEBUG_SOURCE_API_ARB, GL_DEBUG_TYPE_OTHER_ARB, GL_DEBUG_SEVERITY_LOW_ARB, 0, (const GLuint *)NULL, GL_FALSE); + gGL->glDebugMessageCallbackARB(GL_Debug_Output_Callback, (void*)NULL); + + GLMDebugPrintf( "GLMContext::GLMContext: Debug output (gl_arb_debug_output) enabled!\n" ); + } + + + if (CommandLine()->FindParm("-glmspewcaps")) + { + DumpCaps(); + } + + SetDisplayParams( params ); + + m_texLayoutTable = new CGLMTexLayoutTable; + +#ifndef OSX + if ( m_bUseSamplerObjects ) + { + memset( m_samplerObjectHash, 0, sizeof( m_samplerObjectHash ) ); + m_nSamplerObjectHashNumEntries = 0; + + for ( uint i = 0; i < cSamplerObjectHashSize; ++i ) + { + gGL->glGenSamplers( 1, &m_samplerObjectHash[i].m_samplerObject ); + } + } +#endif // !OSX + + memset( m_samplers, 0, sizeof( m_samplers ) ); + for( int i=0; i< GLM_SAMPLER_COUNT; i++) + { + GLMTexSamplingParams ¶ms = m_samplers[i].m_samp; + params.m_packed.m_addressU = D3DTADDRESS_WRAP; + params.m_packed.m_addressV = D3DTADDRESS_WRAP; + params.m_packed.m_addressW = D3DTADDRESS_WRAP; + params.m_packed.m_minFilter = D3DTEXF_POINT; + params.m_packed.m_magFilter = D3DTEXF_POINT; + params.m_packed.m_mipFilter = D3DTEXF_NONE; + params.m_packed.m_maxAniso = 1; + params.m_packed.m_isValid = true; + params.m_packed.m_compareMode = 0; + } + + MarkAllSamplersDirty(); + + m_activeTexture = -1; + + m_texLocks.EnsureCapacity( 16 ); // should be sufficient + + // FIXME need a texture tracking table so we can reliably delete CGLMTex objects at context teardown + + m_boundReadFBO = NULL; + m_boundDrawFBO = NULL; + m_drawingFBO = NULL; + + memset( m_drawingProgram, 0, sizeof( m_drawingProgram ) ); + m_bDirtyPrograms = true; + memset( m_programParamsF , 0, sizeof( m_programParamsF ) ); + memset( m_programParamsB , 0, sizeof( m_programParamsB ) ); + memset( m_programParamsI , 0, sizeof( m_programParamsI ) ); + + for (uint i = 0; i < ARRAYSIZE(m_programParamsF); i++) + { + m_programParamsF[i].m_firstDirtySlotNonBone = 256; + m_programParamsF[i].m_dirtySlotHighWaterNonBone = 0; + + m_programParamsF[i].m_dirtySlotHighWaterBone = 0; + } + + m_paramWriteMode = eParamWriteDirtySlotRange; // default to fastest mode + + if (CommandLine()->FindParm("-glmwriteallslots")) m_paramWriteMode = eParamWriteAllSlots; + if (CommandLine()->FindParm("-glmwriteshaderslots")) m_paramWriteMode = eParamWriteShaderSlots; + if (CommandLine()->FindParm("-glmwriteshaderslotsoptional")) m_paramWriteMode = eParamWriteShaderSlotsOptional; + if (CommandLine()->FindParm("-glmwritedirtyslotrange")) m_paramWriteMode = eParamWriteDirtySlotRange; + + m_attribWriteMode = eAttribWriteDirty; + + if (CommandLine()->FindParm("-glmwriteallattribs")) m_attribWriteMode = eAttribWriteAll; + if (CommandLine()->FindParm("-glmwritedirtyattribs")) m_attribWriteMode = eAttribWriteDirty; + + m_pairCache = new CGLMShaderPairCache( this ); + m_pBoundPair = NULL; + + m_fragDataMask = 0; + + memset( m_nBoundGLBuffer, 0xFF, sizeof( m_nBoundGLBuffer ) ); + + memset( m_boundVertexAttribs, 0xFF, sizeof(m_boundVertexAttribs) ); + m_lastKnownVertexAttribMask = 0; + m_nNumSetVertexAttributes = 16; + + // make a null program for use when client asks for NULL FP + m_pNullFragmentProgram = NewProgram(kGLMFragmentProgram, g_nullFragmentProgramText, "null" ); + SetProgram( kGLMFragmentProgram, m_pNullFragmentProgram ); + + // make dummy programs for doing texture preload via dummy draw + m_preloadTexVertexProgram = NewProgram(kGLMVertexProgram, g_preloadTexVertexProgramText, "preloadTex" ); + m_preload2DTexFragmentProgram = NewProgram(kGLMFragmentProgram, g_preload2DTexFragmentProgramText, "preload2DTex" ); + m_preload3DTexFragmentProgram = NewProgram(kGLMFragmentProgram, g_preload3DTexFragmentProgramText, "preload3DTex" ); + m_preloadCubeTexFragmentProgram = NewProgram(kGLMFragmentProgram, g_preloadCubeTexFragmentProgramText, "preloadCube" ); + + //memset( &m_drawVertexSetup, 0, sizeof(m_drawVertexSetup) ); + SetVertexAttributes( NULL ); // will set up all the entries in m_drawVertexSetup + + m_debugFontTex = NULL; + + // debug state + m_debugFrameIndex = -1; + +#if GLMDEBUG + // ####################################################################################### + + // DebugHook state - we could set these to more interesting values in response to a CLI arg like "startpaused" or something if desired + //m_paused = false; + m_holdFrameBegin = -1; + m_holdFrameEnd = -1; + m_holdBatch = m_holdBatchFrame = -1; + + m_debugDelayEnable = false; + m_debugDelay = 1<<19; // ~0.5 sec delay + + m_autoClearColor = m_autoClearDepth = m_autoClearStencil = false; + m_autoClearColorValues[0] = 0.0; //red + m_autoClearColorValues[1] = 1.0; //green + m_autoClearColorValues[2] = 0.0; //blue + m_autoClearColorValues[3] = 1.0; //alpha + + m_selKnobIndex = 0; + m_selKnobMinValue = -10.0f; + + m_selKnobMaxValue = 10.0f; + m_selKnobIncrement = 1/256.0f; + + // ####################################################################################### +#endif + + // make two scratch FBO's for blit purposes + m_blitReadFBO = NewFBO(); + m_blitDrawFBO = NewFBO(); + + for( int i=0; iglGenBuffersARB( 1, &m_destroyPBO ); + gGL->glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_destroyPBO ); + gGL->glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, sizeof( g_garbageTextureBits ), g_garbageTextureBits, GL_STATIC_DRAW ); + gGL->glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_nBoundGLBuffer[ kGLMPixelBuffer ] ); + + // Create a bunch of texture names for us to use forever and ever ramen. + FillTexCache( false, kGLMInitialTexCount ); + +#ifdef OSX + bool new_mtgl = m_caps.m_hasPerfPackage1; // i.e. 10.6.4 plus new driver + + if ( CommandLine()->FindParm("-glmenablemtgl2") ) + { + new_mtgl = true; + } + + if ( CommandLine()->FindParm("-glmdisablemtgl2") ) + { + new_mtgl = false; + } + + bool mtgl_on = params->m_mtgl; + if (CommandLine()->FindParm("-glmenablemtgl")) + { + mtgl_on = true; + } + + if (CommandLine()->FindParm("-glmdisablemtgl")) + { + mtgl_on = false; + } + + CGLError result = (CGLError)0; + if (mtgl_on) + { + bool ready = false; + CGLContextObj context = GetCGLContextFromNSGL(m_ctx); + if (new_mtgl) + { + // afterburner + CGLContextEnable kCGLCPGCDMPEngine = ((CGLContextEnable)1314); + result = CGLEnable( context, kCGLCPGCDMPEngine ); + if (!result) + { + ready = true; // succeeded - no need to try non-MTGL + printf("\nMTGL detected.\n"); + } + else + { + printf("\nMTGL *not* detected, falling back.\n"); + } + } + + if (!ready) + { + // try old MTGL + result = CGLEnable( context, kCGLCEMPEngine ); + if (!result) + { + printf("\nMTGL has been detected.\n"); + ready = true; // succeeded - no need to try non-MTGL + } + } + } + + if ( m_caps.m_badDriver108Intel ) + { + // this way we have something to look for in terminal spew if users report issues related to this in the future. + printf( "\nEnabling GLSL compiler `malloc' workaround.\n" ); + if ( !IntelGLMallocWorkaround::Get()->Enable() ) + { + Warning( "Unable to enable OSX 10.8 / Intel HD4000 workaround, there might be crashes.\n" ); + } + } + +#endif + // also, set the remote convar "gl_can_query_fast" to 1 if perf package present, else 0. + gl_can_query_fast.SetValue( m_caps.m_hasPerfPackage1?1:0 ); + +#if GL_BATCH_PERF_ANALYSIS + m_nTotalVSUniformCalls = 0; + m_nTotalVSUniformBoneCalls = 0; + m_nTotalVSUniformsSet = 0; + m_nTotalVSUniformsBoneSet = 0; + m_nTotalPSUniformCalls = 0; + m_nTotalPSUniformsSet = 0; +#endif + + // See g_D3DRS_INFO_packed in dxabstract.cpp; dithering is a non-managed + // piece of state that we consider off by default. However it is actually + // enabled by default in the GL spec, so account for that here. + // See: https://bugs.freedesktop.org/show_bug.cgi?id=74700 + gGL->glDisable( GL_DITHER ); +} + +void GLMContext::Reset() +{ +} + +GLMContext::~GLMContext () +{ +#ifndef OSX + GLMGPUTimestampManagerDeinit(); + + for ( uint t = 0; t < cNumPinnedMemoryBuffers; t++ ) + { + m_PinnedMemoryBuffers[t].Deinit(); + } + + if (gGL->m_bHave_GL_ARB_buffer_storage) + { + for (uint lpType = 0; lpType < kGLMNumBufferTypes; ++lpType) + { + for (uint lpNum = 0; lpNum < cNumPersistentBuffers; ++lpNum) + { + m_persistentBuffer[lpNum][lpType].Deinit(); + } + } + } + + if ( m_bUseSamplerObjects ) + { + for( int i=0; i< GLM_SAMPLER_COUNT; i++) + { + gGL->glBindSampler( i, 0 ); + } + + for( int i=0; i< cSamplerObjectHashSize; i++) + { + gGL->glDeleteSamplers( 1, &m_samplerObjectHash[i].m_samplerObject ); + m_samplerObjectHash[i].m_samplerObject = 0; + } + } +#endif // !OSX + + if (m_debugFontTex) + { + DelTex( m_debugFontTex ); + m_debugFontTex = NULL; + } + + ProcessTextureDeletes(); + + if ( m_pNullFragmentProgram ) + { + DelProgram( m_pNullFragmentProgram ); + m_pNullFragmentProgram = NULL; + } + + // walk m_fboTable and free them up.. + FOR_EACH_VEC( m_fboTable, i ) + { + CGLMFBO *fbo = m_fboTable[i]; + DelFBO( fbo ); + } + m_fboTable.SetSize( 0 ); + + if (m_pairCache) + { + delete m_pairCache; + m_pairCache = NULL; + } + + // we need a m_texTable I think.. + + // m_texLayoutTable can be scrubbed once we know that all the tex are freed + + gGL->glDeleteBuffersARB( 1, &m_destroyPBO ); + + PurgeTexCache(); + + DecrementWindowRefCount(); +} + +// This method must call SelectTMU()/glActiveTexture() (it's expected as a side effect). +// This method is no longer called from any performance sensitive code paths. +void GLMContext::BindTexToTMU( CGLMTex *pTex, int tmu ) +{ +#if GLMDEBUG + GLM_FUNC; +#endif + + GLMPRINTF(("--- GLMContext::BindTexToTMU tex %p GL name %d -> TMU %d ", pTex, pTex ? pTex->m_texName : -1, tmu )); + + CheckCurrent(); + + SelectTMU( tmu ); + + if ( !pTex ) + { + gGL->glBindTexture( GL_TEXTURE_1D, 0 ); + gGL->glBindTexture( GL_TEXTURE_2D, 0 ); + gGL->glBindTexture( GL_TEXTURE_3D, 0 ); + gGL->glBindTexture( GL_TEXTURE_CUBE_MAP, 0 ); + } + else + { + const GLenum texGLTarget = pTex->m_texGLTarget; + if ( texGLTarget != GL_TEXTURE_1D ) gGL->glBindTexture( GL_TEXTURE_1D, 0 ); + if ( texGLTarget != GL_TEXTURE_2D ) gGL->glBindTexture( GL_TEXTURE_2D, 0 ); + if ( texGLTarget != GL_TEXTURE_3D ) gGL->glBindTexture( GL_TEXTURE_3D, 0 ); + if ( texGLTarget != GL_TEXTURE_CUBE_MAP ) gGL->glBindTexture( GL_TEXTURE_CUBE_MAP, 0 ); + gGL->glBindTexture( texGLTarget, pTex->m_texName ); + } + + m_samplers[tmu].m_pBoundTex = pTex; +} + +void GLMContext::BindFBOToCtx( CGLMFBO *fbo, GLenum bindPoint ) +{ +#if GLMDEBUG + GLM_FUNC; +#endif + GLMPRINTF(( "--- GLMContext::BindFBOToCtx fbo %p, GL name %d", fbo, (fbo) ? fbo->m_name : -1 )); + + CheckCurrent(); + + if ( bindPoint == GL_FRAMEBUFFER_EXT ) + { + gGL->glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo ? fbo->m_name : 0 ); + m_boundReadFBO = fbo; + m_boundDrawFBO = fbo; + return; + } + + bool targetRead = (bindPoint==GL_READ_FRAMEBUFFER_EXT); + bool targetDraw = (bindPoint==GL_DRAW_FRAMEBUFFER_EXT); + + if (targetRead) + { + if (fbo) // you can pass NULL to go back to no-FBO + { + gGL->glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, fbo->m_name ); + + m_boundReadFBO = fbo; + //dontcare fbo->m_bound = true; + } + else + { + gGL->glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, 0 ); + + m_boundReadFBO = NULL; + } + } + + if (targetDraw) + { + if (fbo) // you can pass NULL to go back to no-FBO + { + gGL->glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, fbo->m_name ); + + m_boundDrawFBO = fbo; + //dontcare fbo->m_bound = true; + } + else + { + gGL->glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, 0 ); + + m_boundDrawFBO = NULL; + } + } +} + +void GLMContext::BindBufferToCtx( EGLMBufferType type, CGLMBuffer *pBuff, bool bForce ) +{ +#if GLMDEBUG + GLM_FUNC; +#endif + GLMPRINTF(( "--- GLMContext::BindBufferToCtx buff %p, GL name %d", pBuff, (pBuff) ? pBuff->m_nHandle : -1 )); + + CheckCurrent(); + + GLuint nGLName = pBuff ? pBuff->GetHandle() : 0; + if ( !bForce ) + { + if ( m_nBoundGLBuffer[type] == nGLName ) + return; + } + + GLenum target = 0; + switch( type ) + { + case kGLMVertexBuffer: target = GL_ARRAY_BUFFER_ARB; break; + case kGLMIndexBuffer: target = GL_ELEMENT_ARRAY_BUFFER_ARB; break; + case kGLMUniformBuffer: target = GL_UNIFORM_BUFFER_EXT; break; + case kGLMPixelBuffer: target = GL_PIXEL_UNPACK_BUFFER_ARB; break; + default: Assert(!"Unknown buffer type" ); + } + + Assert( !pBuff || ( pBuff->m_buffGLTarget == target ) ); + + m_nBoundGLBuffer[type] = nGLName; + gGL->glBindBufferARB( target, nGLName ); +} + + +GLuint GLMContext::CreateTex( GLenum texBind, GLenum internalFormat ) +{ + GLM_FUNC; + + // If we're not doing batch create, just return one here. + if ( !gl_batch_tex_creates.GetBool() ) + { + GLuint tex = 0; + gGL->glGenTextures( 1, &tex ); + return tex; + } + + FOR_EACH_VEC( m_availableTextures, i ) + { + TextureEntry_t& tex = m_availableTextures[ i ]; + if ( ( tex.m_nTexBind == GL_NONE || tex.m_nTexBind == texBind ) + && ( tex.m_nInternalFormat == GL_NONE || tex.m_nInternalFormat == internalFormat ) ) + { + // Hit! + GLuint retVal = tex.m_nTexName; + m_availableTextures.Remove( i ); + return retVal; + } + } + + if ( m_availableTextures.Count() >= kGLMHighWaterUndeleted ) + { + PurgeTexCache(); + } + + return FillTexCache( true, kGLMReUpTexCount ); +} + +void GLMContext::CleanupTex( GLenum texBind, GLMTexLayout* pLayout, GLuint tex ) +{ + // If the total + if ( pLayout->m_storageTotalSize <= ( kDeletedTextureDim * kDeletedTextureDim * sizeof( uint32 ) ) ) + return; + + const GLuint oldPBO = m_nBoundGLBuffer[ kGLMPixelBuffer ]; + const GLuint oldTex = ( m_samplers[ m_activeTexture ].m_pBoundTex != NULL ) ? m_samplers[ m_activeTexture ].m_pBoundTex->GetTexName() : 0; + + gGL->glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_destroyPBO ); + gGL->glBindTexture( texBind, tex ); + + // Clear out old data. + for ( int i = 0; i < pLayout->m_mipCount; ++i ) + { + int mipDim = ( i == 0 ) ? kDeletedTextureDim : 0; + if ( pLayout->m_format->m_chunkSize != 1 ) + { + const int chunks = ( mipDim + ( pLayout->m_format->m_chunkSize - 1 ) ) / pLayout->m_format->m_chunkSize; + const int dataSize = ( chunks * chunks ) * pLayout->m_format->m_bytesPerSquareChunk; + Assert( dataSize <= ( sizeof( uint32) * ARRAYSIZE( g_garbageTextureBits ) ) ); + + gGL->glCompressedTexImage2D( texBind, i, pLayout->m_format->m_glIntFormat, mipDim, mipDim, 0, dataSize, 0 ); + } + else + { + gGL->glTexImage2D( texBind, i, pLayout->m_format->m_glIntFormat, mipDim, mipDim, 0, pLayout->m_format->m_glDataFormat, pLayout->m_format->m_glDataType, 0 ); + } + } + + gGL->glBindTexture( texBind, oldTex ); + gGL->glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, oldPBO ); +} + +void GLMContext::DestroyTex( GLenum texBind, GLMTexLayout* pLayout, GLuint tex ) +{ + GLM_FUNC; + + // Code only handles 2D for now. + if ( texBind != GL_TEXTURE_2D || !gl_batch_tex_destroys.GetBool() ) + { + gGL->glDeleteTextures( 1, &tex ); + return; + } + + CleanupTex( texBind, pLayout, tex ); + + TextureEntry_t entry; + entry.m_nTexBind = texBind; + entry.m_nInternalFormat = pLayout->m_format->m_glIntFormat; + entry.m_nTexName = tex; + + m_availableTextures.AddToTail( entry ); +} + +GLuint GLMContext::FillTexCache( bool holdOne, int newTextures ) +{ + // If we aren't doing batch creates, then don't fill the cache. + if ( !gl_batch_tex_creates.GetBool() ) + return 0; + + // If we have to hit the name table, might as well hit it a bunch because this causes + // serialization either way--at least we can do it less often. + GLuint* textures = (GLuint*) stackalloc( newTextures * sizeof( GLuint ) ); + gGL->glGenTextures( newTextures, textures ); + + Assert( textures[ 0 ] ); + + TextureEntry_t entry; + entry.m_nTexBind = GL_NONE; + entry.m_nInternalFormat = GL_NONE; + + // We may return 0, so skip adding it here. + for ( int i = 1; i < newTextures; ++i ) + { + Assert( textures[ i ] ); + if ( textures[ i ] ) + { + // We still add these to the tail because we'd prefer to reuse old textures (rather + // than these new ones). + entry.m_nTexName = textures[ i ]; + m_availableTextures.AddToTail( entry ); + } + } + + if ( holdOne ) + return textures[ 0 ]; + + // If not, stick that last one in the list and return 0. + entry.m_nTexName = textures[ 0 ]; + m_availableTextures.AddToTail( entry ); + + return 0; +} + +void GLMContext::PurgeTexCache() +{ + GLM_FUNC; + + int textureCount = m_availableTextures.Count(); + + if ( textureCount == 0 ) + return; + + GLuint* textures = (GLuint*) stackalloc( textureCount * sizeof( GLuint ) ); + + FOR_EACH_VEC( m_availableTextures, i ) + { + TextureEntry_t& tex = m_availableTextures[ i ]; + textures[ i ] = tex.m_nTexName; + } + + gGL->glDeleteTextures( textureCount, textures ); + + m_availableTextures.RemoveAll(); +} + +#ifdef OSX +// As far as I can tell this stuff is only useful under OSX. +ConVar gl_can_mix_shader_gammas( "gl_can_mix_shader_gammas", 0 ); +ConVar gl_cannot_mix_shader_gammas( "gl_cannot_mix_shader_gammas", 0 ); +#endif + +// ConVar param_write_mode("param_write_mode", "0"); + +void GLMContext::MarkAllSamplersDirty() +{ + m_nNumDirtySamplers = GLM_SAMPLER_COUNT; + for (uint i = 0; i < GLM_SAMPLER_COUNT; i++) + { + m_nDirtySamplerFlags[i] = 0; + m_nDirtySamplers[i] = (uint8)i; + } +} + +void GLMContext::FlushDrawStatesNoShaders( ) +{ + Assert( ( m_drawingFBO == m_boundDrawFBO ) && ( m_drawingFBO == m_boundReadFBO ) ); // this check MUST succeed + + GLM_FUNC; + + GL_BATCH_PERF( m_FlushStats.m_nTotalBatchFlushes++; ) + + NullProgram(); +} + +#if GLMDEBUG + +enum EGLMDebugDumpOptions +{ + eDumpBatchInfo, + eDumpSurfaceInfo, + eDumpStackCrawl, + eDumpShaderLinks, +// eDumpShaderText, // we never use this one + eDumpShaderParameters, + eDumpTextureSetup, + eDumpVertexAttribSetup, + eDumpVertexData, + eOpenShadersForEdit +}; + +enum EGLMVertDumpMode +{ + // options that affect eDumpVertexData above + eDumpVertsNoTransformDump, + eDumpVertsTransformedByViewProj, + eDumpVertsTransformedByModelViewProj, + eDumpVertsTransformedByBoneZeroThenViewProj, + eDumpVertsTransformedByBonesThenViewProj, + eLastDumpVertsMode +}; + +char *g_vertDumpModeNames[] = +{ + "noTransformDump", + "transformedByViewProj", + "transformedByModelViewProj", + "transformedByBoneZeroThenViewProj", + "transformedByBonesThenViewProj" +}; + +static void CopyTilEOL( char *dst, char *src, int dstSize ) +{ + dstSize--; + + int i=0; + while ( (im_caller==eDrawElements); + const char *batchtype = is_draw ? "draw" : "clear"; + + if (options & (1<m_attach[i].m_tex; + if (tex) + { + GLMPRINTF(("-D- bound FBO (%8x) attachment %d = tex %8x (GL %d) (%s)", fbo, i, tex, tex->m_texName, tex->m_layout->m_layoutSummary )); + } + else + { + // warning if no depthstencil attachment + switch(i) + { + case kAttDepth: + case kAttStencil: + case kAttDepthStencil: + GLMPRINTF(("-D- bound FBO (%8x) attachment %d = NULL, warning!", fbo, i )); + break; + } + } + } + } + + if (options & (1<m_text, "#//ATTRIBMAP"); + if (attribmap) + { + CopyTilEOL( attribtemp, attribmap, sizeof(attribtemp) ); + } + else + { + strcpy( attribtemp, "no attrib map" ); + } + + char *trans = strstr(vp->m_text, "#// trans#"); + if (trans) + { + CopyTilEOL( transtemp, trans, sizeof(transtemp) ); + } + else + { + strcpy( transtemp, "no translation info" ); + } + + char *linkpath = "no file link"; + + #if GLMDEBUG + linkpath = vp->m_editable->m_mirror->m_path; + #endif + + GLMPRINTF(("-D-")); + GLMPRINTF(("-D- ARBVP || GL %d || Path %s ", vp->m_descs[kGLMARB].m_object.arb, linkpath )); + GLMPRINTF(("-D- Attribs %s", attribtemp )); + GLMPRINTF(("-D- Trans %s", transtemp )); + + /* + if ( (options & (1<m_string, eDebugDump )); + } + */ + } + else + { + GLMPRINTF(("-D- VP (none)" )); + } + + if (fp) + { + char *trans = strstr(fp->m_text, "#// trans#"); + if (trans) + { + CopyTilEOL( transtemp, trans, sizeof(transtemp) ); + } + else + { + strcpy( transtemp, "no translation info" ); + } + + char *linkpath = "no file link"; + + #if GLMDEBUG + linkpath = fp->m_editable->m_mirror->m_path; + #endif + + GLMPRINTF(("-D-")); + GLMPRINTF(("-D- FP || GL %d || Path %s ", fp->m_descs[kGLMARB].m_object.arb, linkpath )); + GLMPRINTF(("-D- Trans %s", transtemp )); + + /* + if ( (options & (1<m_string, eDebugDump)); + } + */ + } + else + { + GLMPRINTF(("-D- FP (none)" )); + } + } + + if ( (options & (1<m_attrMask & (1<m_vtxAttribMap[index]>>4)== D3DDECLUSAGE_BLENDWEIGHT); + } + if (usesSkinning) + { + upperSlotLimit = 256; + } + + while( slotIndex < upperSlotLimit ) + { + // if slot index is in a masked range, skip it + // if slot index is the start of a matrix, label it, print it, skip ahead 4 slots + for( int maski=0; vmaskranges[maski] >=0; maski+=2) + { + if ( (slotIndex >= vmaskranges[maski]) && (slotIndex <= vmaskranges[maski+1]) ) + { + // that index is masked. set to one past end of range, print a blank line for clarity + slotIndex = vmaskranges[maski+1]+1; + GLMPrintStr("-D- ....."); + } + } + + if (slotIndex < upperSlotLimit) + { + float *values = &m_programParamsF[ kGLMVertexProgram ].m_values[slotIndex][0]; + switch( slotIndex ) + { + case 4: + printmat( "MODELVIEWPROJ", slotIndex, 4, values ); + slotIndex += 4; + break; + + case 8: + printmat( "VIEWPROJ", slotIndex, 4, values ); + slotIndex += 4; + break; + + default: + if (slotIndex>=58) + { + // bone + char bonelabel[100]; + + sprintf(bonelabel, "MODEL_BONE%-2d", (slotIndex-58)/3 ); + printmat( bonelabel, slotIndex, 3, values ); + + slotIndex += 3; + } + else + { + // just print the one slot + GLMPRINTF(("-D- %03d: [ %10.5f %10.5f %10.5f %10.5f ] %s", slotIndex, values[0], values[1], values[2], values[3], label )); + slotIndex++; + } + break; + } + } + } + + // VP stage still, if in GLSL mode, find the bound pair and see if it has live i0, b0-b3 uniforms + if (m_pBoundPair) // should only be non-NULL in GLSL mode + { +#if 0 + if (m_pBoundPair->m_locVertexBool0>=0) + { + GLMPRINTF(("-D- GLSL 'b0': %d", m_programParamsB[kGLMVertexProgram].m_values[0] )); + } + + if (m_pBoundPair->m_locVertexBool1>=0) + { + GLMPRINTF(("-D- GLSL 'b1': %d", m_programParamsB[kGLMVertexProgram].m_values[1] )); + } + + if (m_pBoundPair->m_locVertexBool2>=0) + { + GLMPRINTF(("-D- GLSL 'b2': %d", m_programParamsB[kGLMVertexProgram].m_values[2] )); + } + + if (m_pBoundPair->m_locVertexBool3>=0) + { + GLMPRINTF(("-D- GLSL 'b3': %d", m_programParamsB[kGLMVertexProgram].m_values[3] )); + } + + if (m_pBoundPair->m_locVertexInteger0>=0) + { + GLMPRINTF(("-D- GLSL 'i0': %d", m_programParamsI[kGLMVertexProgram].m_values[0][0] )); + } +#endif + } + + GLMPRINTF(("-D-")); + GLMPRINTF(("-D- FP parameters " )); + + static int fmaskranges[] = { 40,41, -1,-1 }; + + slotIndex = 0; + label = ""; + while(slotIndex < 40) + { + // if slot index is in a masked range, skip it + // if slot index is the start of a matrix, label it, print it, skip ahead 4 slots + for( int maski=0; fmaskranges[maski] >=0; maski+=2) + { + if ( (slotIndex >= fmaskranges[maski]) && (slotIndex <= fmaskranges[maski+1]) ) + { + // that index is masked. set to one past end of range, print a blank line for clarity + slotIndex = fmaskranges[maski+1]+1; + GLMPrintStr("-D- ....."); + } + } + + if (slotIndex < 40) + { + float *values = &m_programParamsF[ kGLMFragmentProgram ].m_values[slotIndex][0]; + switch( slotIndex ) + { + case 0: label = "g_EnvmapTint"; break; + case 1: label = "g_DiffuseModulation"; break; + case 2: label = "g_EnvmapContrast_ShadowTweaks"; break; + case 3: label = "g_EnvmapSaturation_SelfIllumMask (xyz, and w)"; break; + case 4: label = "g_SelfIllumTint_and_BlendFactor (xyz, and w)"; break; + + case 12: label = "g_ShaderControls"; break; + case 13: label = "g_DepthFeatheringConstants"; break; + + case 20: label = "g_EyePos"; break; + case 21: label = "g_FogParams"; break; + case 22: label = "g_FlashlightAttenuationFactors"; break; + case 23: label = "g_FlashlightPos"; break; + case 24: label = "g_FlashlightWorldToTexture"; break; + + case 28: label = "cFlashlightColor"; break; + case 29: label = "g_LinearFogColor"; break; + case 30: label = "cLightScale"; break; + case 31: label = "cFlashlightScreenScale"; break; + + default: + label = ""; + break; + } + + GLMPRINTF(("-D- %03d: [ %10.5f %10.5f %10.5f %10.5f ] %s", slotIndex, values[0], values[1], values[2], values[3], label )); + + slotIndex ++; + } + } + + if (m_pBoundPair->m_locFragmentFakeSRGBEnable) + { + GLMPRINTF(("-D- GLSL 'flEnableSRGBWrite': %f", m_pBoundPair->m_fakeSRGBEnableValue )); + } + } + + if ( (options & (1<m_layout->m_layoutSummary )); + + GLMPRINTF(("-D- addressMode[ %s %s %s ]", + GLMDecode( eGL_ENUM, samp->m_addressModes[0] ), + GLMDecode( eGL_ENUM, samp->m_addressModes[1] ), + GLMDecode( eGL_ENUM, samp->m_addressModes[2] ) + )); + + GLMPRINTF(("-D- magFilter [ %s ]", GLMDecode( eGL_ENUM, samp->m_magFilter ) )); + GLMPRINTF(("-D- minFilter [ %s ]", GLMDecode( eGL_ENUM, samp->m_minFilter ) )); + GLMPRINTF(("-D- srgb [ %s ]", samp->m_srgb ? "T" : "F" )); + GLMPRINTF(("-D- shadowFilter [ %s ]", samp->m_compareMode == GL_COMPARE_R_TO_TEXTURE_ARB ? "T" : "F" )); + + // add more as needed later.. + } + } +#endif + } + + if ( (options & (1<m_attrMask; + for( int index=0; index < kGLMVertexAttributeIndexMax; index++ ) + { + uint mask = 1<m_attrs[index]; + + char sizestr[100]; + if (setdesc->m_nCompCount < 32) + { + sprintf( sizestr, "%d", setdesc->m_nCompCount); + } + else + { + strcpy( sizestr, GLMDecode( eGL_ENUM, setdesc->m_nCompCount ) ); + } + + if (pSetup->m_vtxAttribMap[index] != 0xBB) + { + GLMPRINTF(("-D- attr=%-2d decl=$%s%1d stride=%-2d offset=%-3d buf=%08x size=%s type=%s normalized=%s ", + index, + GLMDecode(eD3D_VTXDECLUSAGE, pSetup->m_vtxAttribMap[index]>>4 ), + pSetup->m_vtxAttribMap[index]&0x0F, + setdesc->m_stride, + setdesc->m_offset, + setdesc->m_pBuffer, + sizestr, + GLMDecode( eGL_ENUM, setdesc->m_datatype), + setdesc->m_normalized?"Y":"N" + )); + } + else + { + // the attrib map is referencing an attribute that is not wired up in the vertex setup... + DebuggerBreak(); + } + } + } + } + + if ( (options & (1<m_drawStart; + int end = info->m_drawEnd; + int endLimit = start + (1<=0) + { + mark += sprintf(mark, "-D- %04d: ", vtxIndex ); + } + + // for transform dumping, we latch values as we spot them + float vtxPos[4]; + int vtxBoneIndices[4]; // only three get used + float vtxBoneWeights[4]; // only three get used and index 2 is synthesized from 0 and 1 + + vtxPos[0] = vtxPos[1] = vtxPos[2] = 0.0; + vtxPos[3] = 1.0; + + vtxBoneIndices[0] = vtxBoneIndices[1] = vtxBoneIndices[2] = vtxBoneIndices[3] = 0; + vtxBoneWeights[0] = vtxBoneWeights[1] = vtxBoneWeights[2] = vtxBoneWeights[3] = 0.0; + + for( int attr = 0; attr < kGLMVertexAttributeIndexMax; attr++ ) + { + if (pSetup->m_attrMask & (1<m_attrs[ attr ]; + + // print that attribute. + + // on OSX, VB's never move unless resized. You can peek at them when unmapped. Safe enough for debug.. + char *bufferBase = (char*)desc->m_pBuffer->m_pLastMappedAddress; + + uint stride = desc->m_stride; + uint fieldoffset = desc->m_offset; + uint baseoffset = vtxIndex * stride; + + char *attrBase = bufferBase + baseoffset + fieldoffset; + + uint usage = pSetup->m_vtxAttribMap[attr]>>4; + uint usageindex = pSetup->m_vtxAttribMap[attr]&0x0F; + + if (vtxIndex <0) + { + mark += sprintf(mark, "[%s%1d @ offs=%04d / strd %03d] ", GLMDecode(eD3D_VTXDECLUSAGE, usage ), usageindex, fieldoffset, stride ); + } + else + { + mark += sprintf(mark, "[%s%1d ", GLMDecode(eD3D_VTXDECLUSAGE, usage ), usageindex ); + + if (desc->m_nCompCount<32) + { + for( uint which = 0; which < desc->m_nCompCount; which++ ) + { + static char *fieldname = "xyzw"; + switch( desc->m_datatype ) + { + case GL_FLOAT: + { + float *floatbase = (float*)attrBase; + mark += sprintf(mark, (usage != D3DDECLUSAGE_TEXCOORD) ? "%c%7.3f " : "%c%.3f", fieldname[which], floatbase[which] ); + + if (usage==D3DDECLUSAGE_POSITION) + { + if (which<4) + { + // latch pos + vtxPos[which] = floatbase[which]; + } + } + + if (usage==D3DDECLUSAGE_BLENDWEIGHT) + { + if (which<4) + { + // latch weight + vtxBoneWeights[which] = floatbase[which]; + } + } + } + break; + + case GL_UNSIGNED_BYTE: + { + unsigned char *unchbase = (unsigned char*)attrBase; + mark += sprintf(mark, "%c$%02X ", fieldname[which], unchbase[which] ); + } + break; + + default: + // hold off on other formats for now + mark += sprintf(mark, "%c????? ", fieldname[which] ); + break; + } + } + } + else // special path for BGRA bytes which are expressed in GL by setting the *size* to GL_BGRA (gross large enum) + { + switch(desc->m_nCompCount) + { + case GL_BGRA: // byte reversed color + { + for( int which = 0; which < 4; which++ ) + { + static const char *fieldname = "BGRA"; + switch( desc->m_datatype ) + { + case GL_UNSIGNED_BYTE: + { + unsigned char *unchbase = (unsigned char*)attrBase; + mark += sprintf(mark, "%c$%02X ", fieldname[which], unchbase[which] ); + + if (usage==D3DDECLUSAGE_BLENDINDICES) + { + if (which<4) + { + // latch index + vtxBoneIndices[which] = unchbase[which]; // ignoring the component reverse which BGRA would inflict, but we also ignore it below so it matches up. + } + } + } + break; + + default: + DebuggerBreak(); + break; + } + } + } + break; + } + } + mark += sprintf(mark, "] " ); + } + } + } + GLMPrintStr( buf, eDebugDump ); + + if (vtxIndex >=0) + { + // if transform dumping requested, and we've reached the actual vert dump phase, do it + float vtxout[4]; + char *translabel = NULL; // NULL means no print... + + switch( g_vertDumpMode ) + { + case eDumpVertsNoTransformDump: break; + + case eDumpVertsTransformedByViewProj: // viewproj is slot 8 + { + float *viewproj = &m_programParamsF[ kGLMVertexProgram ].m_values[8][0]; + transform_dp4( vtxPos, viewproj, 4, vtxout ); + translabel = "post-viewproj"; + } + break; + + case eDumpVertsTransformedByModelViewProj: // modelviewproj is slot 4 + { + float *modelviewproj = &m_programParamsF[ kGLMVertexProgram ].m_values[4][0]; + transform_dp4( vtxPos, modelviewproj, 4, vtxout ); + translabel = "post-modelviewproj"; + } + break; + + case eDumpVertsTransformedByBoneZeroThenViewProj: + { + float postbone[4]; + postbone[3] = 1.0; + + float *bonemat = &m_programParamsF[ kGLMVertexProgram ].m_values[58][0]; + transform_dp4( vtxPos, bonemat, 3, postbone ); + + float *viewproj = &m_programParamsF[ kGLMVertexProgram ].m_values[8][0]; // viewproj is slot 8 + transform_dp4( postbone, viewproj, 4, vtxout ); + + translabel = "post-bone0-viewproj"; + } + break; + + case eDumpVertsTransformedByBonesThenViewProj: + { + //float bone[4][4]; // [bone index][bone member] // members are adjacent + + vtxout[0] = vtxout[1] = vtxout[2] = vtxout[3] = 0; + + // unpack the third weight + vtxBoneWeights[2] = 1.0 - (vtxBoneWeights[0] + vtxBoneWeights[1]); + + for( int ibone=0; ibone<3; ibone++ ) + { + int boneindex = vtxBoneIndices[ ibone ]; + float *bonemat = &m_programParamsF[ kGLMVertexProgram ].m_values[58+(boneindex*3)][0]; + + float boneweight = vtxBoneWeights[ibone]; + + float postbonevtx[4]; + + transform_dp4( vtxPos, bonemat, 3, postbonevtx ); + + // add weighted sum into output + for( int which=0; which<4; which++ ) + { + vtxout[which] += boneweight * postbonevtx[which]; + } + } + + // fix W ? do we care ? check shaders to see what they do... + translabel = "post-skin3bone-viewproj"; + } + break; + } + if(translabel) + { + // for extra credit, do the perspective divide and viewport + + GLMPRINTF(("-D- %-24s: [ %7.4f %7.4f %7.4f %7.4f ]", translabel, vtxout[0],vtxout[1],vtxout[2],vtxout[3] )); + GLMPRINTF(("-D-" )); + } + } + + if (vtxIndex<0) + { + vtxIndex = start-1; // for printing of the data (note it will be incremented at bottom of loop, so bias down by 1) + } + else + { // no more < and > around vert dump lines + //mark += sprintf(mark, "" ); + } + } + } + + if (options & (1<m_editable->OpenInEditor(); + } + + if (m_drawingProgram[ kGLMFragmentProgram ]) + { + m_drawingProgram[ kGLMFragmentProgram ]->m_editable->OpenInEditor(); + } + #endif + } +/* + if (options & (1<<)) + { + } +*/ + // trailer line + GLMPRINTF(("-D- ===================================================================================== end %s %d frame %d", batchtype, m_nBatchCounter, m_debugFrameIndex )); + + GLMSetIndent(oldIndent); +} + +// here is the table that binds knob numbers to names. change at will. +char *g_knobnames[] = +{ +/*0*/ "dummy", + +/*1*/ "FB-SRGB", + #if 0 + /*1*/ "tex-U0-bias", // src left + /*2*/ "tex-V0-bias", // src upper + /*3*/ "tex-U1-bias", // src right + /*4*/ "tex-V1-bias", // src bottom + + /*5*/ "pos-X0-bias", // dst left + /*6*/ "pos-Y0-bias", // dst upper + /*7*/ "pos-X1-bias", // dst right + /*8*/ "pos-Y1-bias", // dst bottom + #endif + +}; +int g_knobcount = sizeof( g_knobnames ) / sizeof( g_knobnames[0] ); + +void GLMContext::DebugHook( GLMDebugHookInfo *info ) +{ + // FIXME: This has seriously bitrotted. + return; + + bool debughook = false; + // debug hook is called after an action has taken place. + // that would be the initial action, or a repeat. + // if paused, we stay inside this function until return. + // when returning, we inform the caller if it should repeat its last action or continue. + // there is no global pause state. The rest of the app runs at the best speed it can. + + // initial stuff we do unconditionally + + // increment iteration + info->m_iteration++; // can be thought of as "number of times the caller's action has now occurred - starting at 1" + + // now set initial state guess for the info block (outcome may change below) + info->m_loop = false; + + // check prior hold-conditions to see if any of them hit. + // note we disarm each trigger once the hold has occurred (one-shot style) + + switch( info->m_caller ) + { + case eBeginFrame: + if (debughook) GLMPRINTF(("-D- Caller: BeginFrame" )); + if ( (m_holdFrameBegin>=0) && (m_holdFrameBegin==m_debugFrameIndex) ) // did we hit a frame breakpoint? + { + if (debughook) GLMPRINTF(("-D- BeginFrame trigger match, clearing m_holdFrameBegin, hold=true" )); + + m_holdFrameBegin = -1; + + info->m_holding = true; + } + break; + + case eClear: + if (debughook) GLMPRINTF(("-D- Caller: Clear" )); + if ( (m_holdBatch>=0) && (m_holdBatchFrame>=0) && ((int)m_holdBatch==(int)m_nBatchCounter) && ((int)m_holdBatchFrame==(int)m_debugFrameIndex) ) + { + if (debughook) GLMPRINTF(("-D- Clear trigger match, clearing m_holdBatch&Frame, hold=true" )); + + m_holdBatch = m_holdBatchFrame = -1; + + info->m_holding = true; + } + break; + + case eDrawElements: + if (debughook) GLMPRINTF(( (info->m_caller==eClear) ? "-D- Caller: Clear" : "-D- Caller: Draw" )); + if ( (m_holdBatch>=0) && (m_holdBatchFrame>=0) && ((int)m_holdBatch==(int)m_nBatchCounter) && ((int)m_holdBatchFrame==(int)m_debugFrameIndex) ) + { + if (debughook) GLMPRINTF(("-D- Draw trigger match, clearing m_holdBatch&Frame, hold=true" )); + + m_holdBatch = m_holdBatchFrame = -1; + + info->m_holding = true; + } + break; + + case eEndFrame: + if (debughook) GLMPRINTF(("-D- Caller: EndFrame" )); + + // check for any expired batch hold req + if ( (m_holdBatch>=0) && (m_holdBatchFrame>=0) && (m_holdBatchFrame==m_debugFrameIndex) ) + { + // you tried to say 'next batch', but there wasn't one in this frame. + // target first batch of next frame instead + if (debughook) GLMPRINTF(("-D- EndFrame noticed an expired draw hold trigger, rolling to next frame, hold=false")); + + m_holdBatch = 0; + m_holdBatchFrame++; + + info->m_holding = false; + } + + // now check for an explicit hold on end of this frame.. + if ( (m_holdFrameEnd>=0) && (m_holdFrameEnd==m_debugFrameIndex) ) + { + if (debughook) GLMPRINTF(("-D- EndFrame trigger match, clearing m_holdFrameEnd, hold=true" )); + + m_holdFrameEnd = -1; + + info->m_holding = true; + } + break; + } + + // spin until event queue is empty *and* hold is false + + int evtcount=0; + + bool refresh = info->m_holding || m_debugDelayEnable; // only refresh once per initial visit (if paused!) or follow up event input + int breakToDebugger = 0; + // 1 = break to GDB + // 2 = break to OpenGL Profiler if attached + + do + { + if (refresh) + { + if (debughook) GLMPRINTF(("-D- pushing pixels" )); + DebugPresent(); // show pixels + + uint minidumpOptions = (1<SyncWithEditable() ) + { + redrawBatch = true; + } + } + + if (m_drawingProgram[ kGLMFragmentProgram ]) + { + if( m_drawingProgram[ kGLMFragmentProgram ]->SyncWithEditable() ) + { + redrawBatch = true; + } + } + + if (redrawBatch) + { + // act as if user pressed the option-\ key + + if (m_drawingLang == kGLMGLSL) + { + // if GLSL mode, force relink - and refresh the pair cache as needed + if (m_pBoundPair) + { + // fix it in place + m_pBoundPair->RefreshProgramPair(); + } + } + + // TODO - need to retest this whole path + FlushDrawStates( 0, 0, 0 ); // this is key, because the linked shader pair may have changed (note call to PurgePairsWithShader in cglmprogram.cpp) + + GLMPRINTF(("-- Shader changed, re-running batch" )); + + m_holdBatch = m_nBatchCounter; + m_holdBatchFrame = m_debugFrameIndex; + m_debugDelayEnable = false; + + info->m_holding = false; + info->m_loop = true; + + eventCheck = false; + } + #endif + + if(eventCheck) + { + PumpWindowsMessageLoop(); + CCocoaEvent evt; + evtcount = GetEvents( &evt, 1, true ); // asking for debug events only. + if (evtcount) + { + // print it + if (debughook) GLMPRINTF(("-D- Received debug key '%c' with modifiers %x", evt.m_UnicodeKeyUnmodified, evt.m_ModifierKeyMask )); + + // flag for refresh if we spin again + refresh = 1; + + switch(evt.m_UnicodeKeyUnmodified) + { + case ' ': // toggle pause + // clear all the holds to be sure + m_holdFrameBegin = m_holdFrameEnd = m_holdBatch = m_holdBatchFrame = -1; + info->m_holding = !info->m_holding; + + if (!info->m_holding) + { + m_debugDelayEnable = false; // coming out of pause means no slow mo + } + + GLMPRINTF((info->m_holding ? "-D- Paused." : "-D- Unpaused." )); + break; + + case 'f': // frame advance + GLMPRINTF(("-D- Command: next frame" )); + m_holdFrameBegin = m_debugFrameIndex+1; // stop at top of next numbered frame + m_debugDelayEnable = false; // get there fast + + info->m_holding = false; + break; + + case ']': // ahead 1 batch + case '}': // ahead ten batches + { + int delta = evt.m_UnicodeKeyUnmodified == ']' ? 1 : 10; + m_holdBatch = m_nBatchCounter+delta; + m_holdBatchFrame = m_debugFrameIndex; + m_debugDelayEnable = false; // get there fast + info->m_holding = false; + GLMPRINTF(("-D- Command: advance %d batches to %d", delta, m_holdBatch )); + } + break; + + case '[': // back one batch + case '{': // back 10 batches + { + int delta = evt.m_UnicodeKeyUnmodified == '[' ? -1 : -10; + m_holdBatch = m_nBatchCounter + delta; + if (m_holdBatch<0) + { + m_holdBatch = 0; + } + m_holdBatchFrame = m_debugFrameIndex+1; // next frame, but prev batch # + m_debugDelayEnable = false; // get there fast + info->m_holding = false; + GLMPRINTF(("-D- Command: rewind %d batches to %d", delta, m_holdBatch )); + } + break; + + case '\\': // batch rerun + + m_holdBatch = m_nBatchCounter; + m_holdBatchFrame = m_debugFrameIndex; + m_debugDelayEnable = false; + info->m_holding = false; + info->m_loop = true; + GLMPRINTF(("-D- Command: re-run batch %d", m_holdBatch )); + break; + + case 'c': // toggle auto color clear + m_autoClearColor = !m_autoClearColor; + GLMPRINTF((m_autoClearColor ? "-D- Auto color clear ON" : "-D- Auto color clear OFF" )); + break; + + case 's': // toggle auto stencil clear + m_autoClearStencil = !m_autoClearStencil; + GLMPRINTF((m_autoClearStencil ? "-D- Auto stencil clear ON" : "-D- Auto stencil clear OFF" )); + break; + + case 'd': // toggle auto depth clear + m_autoClearDepth = !m_autoClearDepth; + GLMPRINTF((m_autoClearDepth ? "-D- Auto depth clear ON" : "-D- Auto depth clear OFF" )); + break; + + case '.': // break to debugger or insta-quit + if (evt.m_ModifierKeyMask & (1<m_holding = true; + info->m_loop = true; // so when you come back from debugger, you get another spin (i.e. you enter paused mode) + } + break; + + case 'g': // break to OGLP and enable OGLP logging of spew + if (GLMDetectOGLP()) // if this comes back true, there will be a breakpoint set on glColor4sv. + { + uint channelMask = GLMDetectAvailableChannels(); // will re-assert whether spew goes to OGLP log + + if (channelMask & (1<m_holding = true; + info->m_loop = true; // so when you come back from debugger, you get another spin (i.e. you enter paused mode) + } + } + break; + + case '_': // toggle slow mo + m_debugDelayEnable = !m_debugDelayEnable; + break; + + case '-': // go slower + if (m_debugDelayEnable) + { + // already in slow mo, so lower speed + m_debugDelay <<= 1; // double delay + if (m_debugDelay > (1<<24)) + { + m_debugDelay = (1<<24); + } + } + else + { + // enter slow mo + m_debugDelayEnable = true; + } + break; + + case '=': // go faster + if (m_debugDelayEnable) + { + // already in slow mo, so raise speed + m_debugDelay >>= 1; // halve delay + if (m_debugDelay < (1<<17)) + { + m_debugDelay = (1<<17); + } + } + else + { + // enter slow mo + m_debugDelayEnable = true; + } + break; + + case 'v': + // open vs in editor (foreground pop) + #if GLMDEBUG + if (m_drawingProgram[ kGLMVertexProgram ]) + { + m_drawingProgram[ kGLMVertexProgram ]->m_editable->OpenInEditor( true ); + } + #endif + break; + + case 'p': + // open fs/ps in editor (foreground pop) + #if GLMDEBUG + if (m_drawingProgram[ kGLMFragmentProgram ]) + { + m_drawingProgram[ kGLMFragmentProgram ]->m_editable->OpenInEditor( true ); + } + #endif + break; + + case '<': // dump fewer verts + case '>': // dump more verts + { + int delta = (evt.m_UnicodeKeyUnmodified=='>') ? 1 : -1; + g_maxVertsToDumpLog2 = MIN( MAX( g_maxVertsToDumpLog2+delta, 0 ), 16 ); + + // just re-dump the verts + DebugDump( info, 1<= eLastDumpVertsMode) + { + // wrap + newmode = eDumpVertsNoTransformDump; + } + g_vertDumpMode = (EGLMVertDumpMode)newmode; + + GLMPRINTF(("-D- New vert dump mode is %s", g_vertDumpModeNames[g_vertDumpMode] )); + } + break; + + case 'u': // more crawl + { + CStackCrawlParams cp; + memset( &cp, 0, sizeof(cp) ); + cp.m_frameLimit = kMaxCrawlFrames; + + GetStackCrawl(&cp); + + GLMPRINTF(("-D-" )); + GLMPRINTF(("-D- extended stack crawl:")); + for( uint i=0; i< cp.m_frameCount; i++) + { + GLMPRINTF(("-D-\t%s", cp.m_crawlNames[i] )); + } + } + + break; + + case 'q': + DebugDump( info, 0xFFFFFFFF, g_vertDumpMode ); + break; + + + case 'H': + case 'h': + { + // toggle drawing language. hold down shift key to do it immediately. + + if (m_caps.m_hasDualShaders) + { + bool immediate; + + immediate = evt.m_UnicodeKeyUnmodified == 'H'; // (evt.m_ModifierKeyMask & (1<=0) && (flavorSelect m_selKnobMaxValue) + { + val = m_selKnobMaxValue; + } + // send new value back to the knob + GLMKnob( g_knobnames[ m_selKnobIndex ], &val ); + } + + if (evt.m_UnicodeKeyUnmodified == 'z') + { + // zero + val = 0.0f; + + // send new value back to the knob + GLMKnob( g_knobnames[ m_selKnobIndex ], &val ); + } + + GLMPRINTF(("-D- Knob # %d (%s) set to %f (%f/1024.0)", m_selKnobIndex, g_knobnames[ m_selKnobIndex ], val, val * 1024.0 )); + + ThreadSleep( 500000 / 1000 ); + + refresh = false; + } + } + break; + + } + } + } + } while( ((evtcount>0) || info->m_holding) && (!breakToDebugger) ); + + if (m_debugDelayEnable) + { + ThreadSleep( m_debugDelay / 1000 ); + } + + if (breakToDebugger) + { + switch (breakToDebugger) + { + case 1: + DebuggerBreak(); + break; + + case 2: + short fakecolor[4] = { 0, 0, 0, 0 }; + gGL->glColor4sv( fakecolor ); // break to OGLP + break; + } + // re-flush all GLM states so you can fiddle with them in the debugger. then run the batch again and spin.. + ForceFlushStates(); + } +} + +void GLMContext::DebugPresent( void ) +{ + CGLMTex *drawBufferTex = m_drawingFBO->m_attach[kAttColor0].m_tex; + gGL->glFinish(); + Present( drawBufferTex ); +} + +void GLMContext::DebugClear( void ) +{ + // get old clear color + GLClearColor_t clearcol_orig; + m_ClearColor.Read( &clearcol_orig,0 ); + + // new clear color + GLClearColor_t clearcol; + clearcol.r = m_autoClearColorValues[0]; + clearcol.g = m_autoClearColorValues[1]; + clearcol.b = m_autoClearColorValues[2]; + clearcol.a = m_autoClearColorValues[3]; + m_ClearColor.Write( &clearcol ); // don't check, don't defer + + uint mask = 0; + + if (m_autoClearColor) mask |= GL_COLOR_BUFFER_BIT; + if (m_autoClearDepth) mask |= GL_DEPTH_BUFFER_BIT; + if (m_autoClearStencil) mask |= GL_STENCIL_BUFFER_BIT; + + gGL->glClear( mask ); + gGL->glFinish(); + + // put old color back + m_ClearColor.Write( &clearcol_orig ); // don't check, don't defer +} + +#endif + +void GLMContext::CheckNative( void ) +{ + // note that this is available in release. We don't use GLMPRINTF for that reason. + // note we do not get called unless either slow-batch asserting or logging is enabled. +#ifdef OSX + bool gpuProcessing; + GLint fragmentGPUProcessing, vertexGPUProcessing; + + CGLGetParameter (CGLGetCurrentContext(), kCGLCPGPUFragmentProcessing, &fragmentGPUProcessing); + CGLGetParameter(CGLGetCurrentContext(), kCGLCPGPUVertexProcessing, &vertexGPUProcessing); + + // spews then asserts. + // that way you can enable both, get log output on a pair if it's slow, and then the debugger will pop. + if(m_slowSpewEnable) + { + if ( !vertexGPUProcessing ) + { + m_drawingProgram[ kGLMVertexProgram ]->LogSlow( m_drawingLang ); + } + if ( !fragmentGPUProcessing ) + { + m_drawingProgram[ kGLMFragmentProgram ]->LogSlow( m_drawingLang ); + } + } + + if(m_slowAssertEnable) + { + if ( !vertexGPUProcessing || !fragmentGPUProcessing) + { + Assert( !"slow batch" ); + } + } +#else + //Assert( !"impl GLMContext::CheckNative()" ); + + if (m_checkglErrorsAfterEveryBatch) + { + // This is slow, and somewhat redundant (-gldebugoutput uses the GL_ARB_debug_output extension, which can be at least asynchronous), but having a straightforward backup can be useful. + // This is useful for callstack purposes - GL_ARB_debug_output may break in a different thread that the thread triggering the GL error. + //gGL->glFlush(); + GLenum errorcode = (GLenum)gGL->glGetError(); + if ( errorcode != GL_NO_ERROR ) + { + const char *decodedStr = GLMDecode( eGL_ERROR, errorcode ); + + char buf[512]; + V_snprintf( buf, sizeof( buf), "\nGL ERROR! %08x = '%s'\n", errorcode, decodedStr ); + + // Make sure the dev sees something, because these errors can happen early enough that DevMsg() does nothing. +#ifdef WIN32 + OutputDebugStringA( buf ); +#else + printf( "%s", buf ); +#endif + } + } + +#endif + +} + + + +// debug font +void GLMContext::GenDebugFontTex( void ) +{ + if(!m_debugFontTex) + { + // make a 128x128 RGBA texture + GLMTexLayoutKey key; + memset( &key, 0, sizeof(key) ); + + key.m_texGLTarget = GL_TEXTURE_2D; + key.m_xSize = 128; + key.m_ySize = 128; + key.m_zSize = 1; + key.m_texFormat = D3DFMT_A8R8G8B8; + key.m_texFlags = 0; + + m_debugFontTex = NewTex( &key, 1, "GLM debug font" ); + + + //----------------------------------------------------- + GLMTexLockParams lockreq; + + lockreq.m_tex = m_debugFontTex; + lockreq.m_face = 0; + lockreq.m_mip = 0; + + GLMTexLayoutSlice *slice = &m_debugFontTex->m_layout->m_slices[ lockreq.m_tex->CalcSliceIndex( lockreq.m_face, lockreq.m_mip ) ]; + + lockreq.m_region.xmin = lockreq.m_region.ymin = lockreq.m_region.zmin = 0; + lockreq.m_region.xmax = slice->m_xSize; + lockreq.m_region.ymax = slice->m_ySize; + lockreq.m_region.zmax = slice->m_zSize; + + lockreq.m_readback = false; + + char *lockAddress; + int yStride; + int zStride; + + m_debugFontTex->Lock( &lockreq, &lockAddress, &yStride, &zStride ); + + //----------------------------------------------------- + // fetch elements of font data and make texels... we're doing the whole slab so we don't really need the stride info + unsigned long *destTexelPtr = (unsigned long *)lockAddress; + + for( int index = 0; index < 16384; index++ ) + { + if (g_glmDebugFontMap[index] == ' ') + { + // clear + *destTexelPtr = 0x00000000; + } + else + { + // opaque white (drawing code can modulate if desired) + *destTexelPtr = 0xFFFFFFFF; + } + destTexelPtr++; + } + + //----------------------------------------------------- + GLMTexLockParams unlockreq; + + unlockreq.m_tex = m_debugFontTex; + unlockreq.m_face = 0; + unlockreq.m_mip = 0; + + // region need not matter for unlocks + unlockreq.m_region.xmin = unlockreq.m_region.ymin = unlockreq.m_region.zmin = 0; + unlockreq.m_region.xmax = unlockreq.m_region.ymax = unlockreq.m_region.zmax = 0; + + unlockreq.m_readback = false; + + m_debugFontTex->Unlock( &unlockreq ); + + //----------------------------------------------------- + // change up the tex sampling on this texture to be "nearest" not linear + + //----------------------------------------------------- + + // don't leave texture bound on the TMU + BindTexToTMU( NULL, 0 ); + + // also make the index and vertex buffers for use - up to 1K indices and 1K verts + + uint indexBufferSize = 1024*2; + + m_debugFontIndices = NewBuffer(kGLMIndexBuffer, indexBufferSize, 0); // two byte indices + + // we go ahead and lock it now, and fill it with indices 0-1023. + char *indices = NULL; + GLMBuffLockParams idxLock; + idxLock.m_nOffset = 0; + idxLock.m_nSize = indexBufferSize; + idxLock.m_bNoOverwrite = false; + idxLock.m_bDiscard = true; + m_debugFontIndices->Lock( &idxLock, &indices ); + for( int i=0; i<1024; i++) + { + unsigned short *idxPtr = &((unsigned short*)indices)[i]; + *idxPtr = i; + } + m_debugFontIndices->Unlock(); + + m_debugFontVertices = NewBuffer(kGLMVertexBuffer, 1024 * 128, 0); // up to 128 bytes per vert + } +} + +#define MAX_DEBUG_CHARS 256 +struct GLMDebugTextVertex +{ + float x,y,z; + float u,v; + char rgba[4]; +}; + +void GLMContext::DrawDebugText( float x, float y, float z, float drawCharWidth, float drawCharHeight, char *string ) +{ + if (!m_debugFontTex) + { + GenDebugFontTex(); + } + + // setup needed to draw text + + // we're assuming that +x goes left to right on screen, no billboarding math in here + // and that +y goes bottom up + // caller knows projection / rectangle so it gets to decide vertex spacing + + // debug font must be bound to TMU 0 + // texturing enabled + // alpha blending enabled + // generate a quad per character + // characters are 6px wide by 11 px high. + // upper left character in tex is 0x20 + // y axis will need to be flipped for display + + // for any character in 0x20 - 0x7F - here are the needed UV's + + // leftU = ((character % 16) * 6.0f / 128.0f) + // rightU = lowU + (6.0 / 128.0); + // topV = ((character - 0x20) * 11.0f / 128.0f) + // bottomV = lowV + (11.0f / 128.0f) + + int stringlen = strlen( string ); + if (stringlen > MAX_DEBUG_CHARS) + { + stringlen = MAX_DEBUG_CHARS; + } + + // lock + char *vertices = NULL; + GLMBuffLockParams vtxLock; + vtxLock.m_nOffset = 0; + vtxLock.m_nSize = 1024 * stringlen; + vtxLock.m_bNoOverwrite = false; + vtxLock.m_bDiscard = false; + m_debugFontVertices->Lock( &vtxLock, &vertices ); + + GLMDebugTextVertex *vtx = (GLMDebugTextVertex*)vertices; + GLMDebugTextVertex *vtxOutPtr = vtx; + + for( int charindex = 0; charindex < stringlen; charindex++ ) + { + float leftU,rightU,topV,bottomV; + + int character = (int)string[charindex]; + character -= 0x20; + if ( (character<0) || (character > 0x7F) ) + { + character = '*' - 0x20; + } + + leftU = ((character & 0x0F) * 6.0f ) / 128.0f; + rightU = leftU + (6.0f / 128.0f); + + topV = ((character >> 4) * 11.0f ) / 128.0f; + bottomV = topV + (11.0f / 128.0f); + + float posx,posy,posz; + + posx = x + (drawCharWidth * (float)charindex); + posy = y; + posz = z; + + // generate four verts + // first vert will be upper left of displayed quad (low X, high Y) then we go clockwise + for( int quadvert = 0; quadvert < 4; quadvert++ ) + { + bool isTop = (quadvert <2); // verts 0 and 1 + bool isLeft = (quadvert & 1) == (quadvert >> 1); // verts 0 and 3 + + vtxOutPtr->x = posx + (isLeft ? 0.0f : drawCharWidth); + vtxOutPtr->y = posy + (isTop ? drawCharHeight : 0.0f); + vtxOutPtr->z = posz; + + vtxOutPtr->u = isLeft ? leftU : rightU; + vtxOutPtr->v = isTop ? topV : bottomV; + + vtxOutPtr++; + } + } + + // verts are done. + // unlock... + + m_debugFontVertices->Unlock(); + + // make a vertex setup + GLMVertexSetup vertSetup; + + // position, color, tc = 0, 3, 8 + vertSetup.m_attrMask = (1<glDisable( GL_DEPTH_TEST ); + + gGL->glEnable(GL_TEXTURE_2D); + + if (0) + { + gGL->glEnableClientState(GL_VERTEX_ARRAY); + + gGL->glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + gGL->glVertexPointer( 3, GL_FLOAT, sizeof( vtx[0] ), &vtx[0].x ); + + gGL->glClientActiveTexture(GL_TEXTURE0); + + gGL->glTexCoordPointer( 2, GL_FLOAT, sizeof( vtx[0] ), &vtx[0].u ); + } + else + { + SetVertexAttributes( &vertSetup ); + } + + gGL->glDrawArrays( GL_QUADS, 0, stringlen * 4 ); + + // disable all the input streams + if (0) + { + gGL->glDisableClientState(GL_VERTEX_ARRAY); + + gGL->glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + else + { + SetVertexAttributes( NULL ); + } + + gGL->glDisable(GL_TEXTURE_2D); + + BindTexToTMU( pPrevTex, 0 ); +} + +//=============================================================================== + +void GLMgrSelfTests( void ) +{ + return; // until such time as the tests are revised or axed + + GLMDisplayParams glmParams; + glmParams.m_fsEnable = false; + + glmParams.m_vsyncEnable = false; // "The runtime updates the window client area immediately and might do so more + glmParams.m_backBufferWidth = 1024; + glmParams.m_backBufferHeight = 768; + glmParams.m_backBufferFormat = D3DFMT_A8R8G8B8; + glmParams.m_multiSampleCount = 2; + + glmParams.m_enableAutoDepthStencil = true; + glmParams.m_autoDepthStencilFormat = D3DFMT_D24S8; + + glmParams.m_fsRefreshHz = 60; + + glmParams.m_mtgl = true; + glmParams.m_focusWindow = 0; + + // make a new context on renderer 0. + GLMContext *ctx = GLMgr::aGLMgr()->NewContext( NULL, &glmParams ); ////FIXME you can't make contexts this way any more. + if (!ctx) + { + DebuggerBreak(); // no go + return; + } + + // make a test object based on that context. + //int alltests[] = {0,1,2,3, -1}; + //int newtests[] = {3, -1}; + int twotests[] = {2, -1}; + //int notests[] = {-1}; + + int *testlist = twotests; + + GLMTestParams params; + memset( ¶ms, 0, sizeof(params) ); + + params.m_ctx = ctx; + params.m_testList = testlist; + + params.m_glErrToDebugger = true; + params.m_glErrToConsole = true; + + params.m_intlErrToDebugger = true; + params.m_intlErrToConsole = true; + + params.m_frameCount = 1000; + + GLMTester testobj( ¶ms ); + + testobj.RunTests( ); + + GLMgr::aGLMgr()->DelContext( ctx ); +} + +void GLMContext::SetDefaultStates( void ) +{ + GLM_FUNC; + CheckCurrent(); + + m_AlphaTestEnable.Default(); + m_AlphaTestFunc.Default(); + + m_AlphaToCoverageEnable.Default(); + + m_CullFaceEnable.Default(); + m_CullFrontFace.Default(); + + m_PolygonMode.Default(); + m_DepthBias.Default(); + + m_ClipPlaneEnable.Default(); + m_ClipPlaneEquation.Default(); + + m_ScissorEnable.Default(); + m_ScissorBox.Default(); + + m_ViewportBox.Default(); + m_ViewportDepthRange.Default(); + + m_ColorMaskSingle.Default(); + m_ColorMaskMultiple.Default(); + + m_BlendEnable.Default(); + m_BlendFactor.Default(); + m_BlendEquation.Default(); + m_BlendColor.Default(); + //m_BlendEnableSRGB.Default(); // this isn't useful until there is an FBO bound - in fact it will trip a GL error. + + m_DepthTestEnable.Default(); + m_DepthFunc.Default(); + m_DepthMask.Default(); + + m_StencilTestEnable.Default(); + m_StencilFunc.Default(); + m_StencilOp.Default(); + m_StencilWriteMask.Default(); + + m_ClearColor.Default(); + m_ClearDepth.Default(); + m_ClearStencil.Default(); +} + +void GLMContext::VerifyStates ( void ) +{ + GLM_FUNC; + CheckCurrent(); + + // bare bones sanity check, head over to the debugger if our sense of the current context state is not correct + // we should only want to call this after a flush or the checks will flunk. + + if( m_AlphaTestEnable.Check() ) GLMStop(); + if( m_AlphaTestFunc.Check() ) GLMStop(); + + if( m_AlphaToCoverageEnable.Check() ) GLMStop(); + + if( m_CullFaceEnable.Check() ) GLMStop(); + if( m_CullFrontFace.Check() ) GLMStop(); + + if( m_PolygonMode.Check() ) GLMStop(); + if( m_DepthBias.Check() ) GLMStop(); + + if( m_ClipPlaneEnable.Check() ) GLMStop(); + //if( m_ClipPlaneEquation.Check() ) GLMStop(); + + if( m_ScissorEnable.Check() ) GLMStop(); + if( m_ScissorBox.Check() ) GLMStop(); + + + if( m_ViewportBox.Check() ) GLMStop(); + if( m_ViewportDepthRange.Check() ) GLMStop(); + + if( m_ColorMaskSingle.Check() ) GLMStop(); + if( m_ColorMaskMultiple.Check() ) GLMStop(); + + if( m_BlendEnable.Check() ) GLMStop(); + if( m_BlendFactor.Check() ) GLMStop(); + if( m_BlendEquation.Check() ) GLMStop(); + if( m_BlendColor.Check() ) GLMStop(); + + // only do this as caps permit + if (m_caps.m_hasGammaWrites) + { + if( m_BlendEnableSRGB.Check() ) GLMStop(); + } + + if( m_DepthTestEnable.Check() ) GLMStop(); + if( m_DepthFunc.Check() ) GLMStop(); + if( m_DepthMask.Check() ) GLMStop(); + + if( m_StencilTestEnable.Check() ) GLMStop(); + if( m_StencilFunc.Check() ) GLMStop(); + if( m_StencilOp.Check() ) GLMStop(); + if( m_StencilWriteMask.Check() ) GLMStop(); + + if( m_ClearColor.Check() ) GLMStop(); + if( m_ClearDepth.Check() ) GLMStop(); + if( m_ClearStencil.Check() ) GLMStop(); +} + +static inline uint GetDataTypeSizeInBytes( GLenum dataType ) +{ + switch ( dataType ) + { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + return 1; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_HALF_FLOAT: + return 2; + case GL_INT: + case GL_FLOAT: + return 4; + default: + Assert( 0 ); + break; + } + return 0; +} + +#ifndef OSX + +void GLMContext::DrawRangeElementsNonInline( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, uint baseVertex, CGLMBuffer *pIndexBuf ) +{ +#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 GL_ENABLE_INDEX_VERIFICATION + // Obviously only for debugging. + if ( !pIndexBuf->IsSpanValid( (uint)indices, count * GetDataTypeSizeInBytes( type ) ) ) + { + // The consumption range crosses more than one lock span, or the lock is trying to consume a bad IB range. + DXABSTRACT_BREAK_ON_ERROR(); + } + + if ( ( type == GL_UNSIGNED_SHORT ) && ( pIndexBuf->m_bPseudo ) ) + { + Assert( start <= end ); + for ( int i = 0; i < count; i++) + { + uint n = ((const uint16*)indicesActual)[i]; + if ( ( n < start ) || ( n > end ) ) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + } + + unsigned char *pVertexShaderAttribMap = m_pDevice->m_vertexShader->m_vtxAttribMap; + const int nMaxVertexAttributesToCheck = m_drawingProgram[ kGLMVertexProgram ]->m_maxVertexAttrs; + + IDirect3DVertexDeclaration9 *pVertDecl = m_pDevice->m_pVertDecl; + const uint8 *pVertexAttribDescToStreamIndex = pVertDecl->m_VertexAttribDescToStreamIndex; + + // FIXME: Having to duplicate all this flush logic is terrible here + for( int nMask = 1, nIndex = 0; nIndex < nMaxVertexAttributesToCheck; ++nIndex, nMask <<= 1 ) + { + uint8 vertexShaderAttrib = pVertexShaderAttribMap[ nIndex ]; + + uint nDeclIndex = pVertexAttribDescToStreamIndex[vertexShaderAttrib]; + if ( nDeclIndex == 0xFF ) + continue; + + D3DVERTEXELEMENT9_GL *pDeclElem = &pVertDecl->m_elements[nDeclIndex]; + + Assert( ( ( vertexShaderAttrib >> 4 ) == pDeclElem->m_dxdecl.Usage ) && ( ( vertexShaderAttrib & 0x0F ) == pDeclElem->m_dxdecl.UsageIndex) ); + + const uint nStreamIndex = pDeclElem->m_dxdecl.Stream; + const D3DStreamDesc *pStream = &m_pDevice->m_streams[ nStreamIndex ]; + + CGLMBuffer *pBuf = m_pDevice->m_vtx_buffers[ nStreamIndex ]; + if ( pBuf == m_pDevice->m_pDummy_vtx_buffer ) + continue; + + Assert( pStream->m_vtxBuffer->m_vtxBuffer == pBuf ); + + int nBufOffset = pDeclElem->m_gldecl.m_offset + pStream->m_offset; + Assert( nBufOffset >= 0 ); + Assert( nBufOffset < (int)pBuf->m_nSize ); + + uint nBufSize = pStream->m_vtxBuffer->m_vtxBuffer->m_nSize; + uint nDataTypeSize = GetDataTypeSizeInBytes( pDeclElem->m_gldecl.m_datatype ); + uint nActualStride = pStream->m_stride ? pStream->m_stride : nDataTypeSize; + uint nStart = nBufOffset + ( start + baseVertex ) * nActualStride; + uint nEnd = nBufOffset + ( end + baseVertex ) * nActualStride + nDataTypeSize; + + if ( nEnd > nBufSize ) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + if ( !pStream->m_vtxBuffer->m_vtxBuffer->IsSpanValid( nStart, nEnd - nStart ) ) + { + // The draw is trying to consume a range of the bound VB that hasn't been set to valid data! + DXABSTRACT_BREAK_ON_ERROR(); + } + } + } +#endif + + Assert( m_drawingLang == kGLMGLSL ); + + if ( m_pBoundPair ) + { + gGL->glDrawRangeElementsBaseVertex( mode, start, end, count, type, indicesActual, baseVertex ); + +#if GLMDEBUG + if ( m_slowCheckEnable ) + { + CheckNative(); + } +#endif + } +} + +#else + +// support for OSX 10.6 (no support for glDrawRangeElementsBaseVertex) +void GLMContext::DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, CGLMBuffer *pIndexBuf) +{ + GLM_FUNC; + + // CheckCurrent(); + ++m_nBatchCounter; // batch index increments unconditionally on entry + + 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 + // 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 + // TODO - fixup OSX 10.6 m_boundProg not used in this version togl (m_pBoundPair) + //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_boundProgram[kGLMVertexProgram]) + { + m_boundProgram[kGLMVertexProgram]->SyncWithEditable(); + } + else + { + AssertOnce(!"drawing with no vertex program bound"); + } + + if (m_boundProgram[kGLMFragmentProgram]) + { + m_boundProgram[kGLMFragmentProgram]->SyncWithEditable(); + } + else + { + AssertOnce(!"drawing with no fragment program bound"); + } +#endif + + // do the drawing + if ( m_pBoundPair ) + { + gGL->glDrawRangeElements(mode, start, end, count, type, indicesActual); + // GLMCheckError(); + + if (m_slowCheckEnable) + { + CheckNative(); + } + } + this->DebugHook(&info); + } while (info.m_loop); +#else + if ( m_pBoundPair ) + { + gGL->glDrawRangeElements(mode, start, end, count, type, indicesActual); + +#if GLMDEBUG + if ( m_slowCheckEnable ) + { + CheckNative(); + } +#endif + } +#endif +} + +#endif // !OSX + +#if 0 +// helper function to do enable or disable in one step +void glSetEnable( GLenum which, bool enable ) +{ + if (enable) + gGL->glEnable(which); + else + gGL->glDisable(which); +} + +// helper function for int vs enum clarity +void glGetEnumv( GLenum which, GLenum *dst ) +{ + gGL->glGetIntegerv( which, (int*)dst ); +} +#endif + +//=============================================================================== + + +GLMTester::GLMTester(GLMTestParams *params) +{ + m_params = *params; + + m_drawFBO = NULL; + m_drawColorTex = NULL; + m_drawDepthTex = NULL; +} + +GLMTester::~GLMTester() +{ +} + +void GLMTester::StdSetup( void ) +{ + GLMContext *ctx = m_params.m_ctx; + + m_drawWidth = 1024; + m_drawHeight = 768; + + // make an FBO to draw into and activate it. no depth buffer yet + m_drawFBO = ctx->NewFBO(); + + // make color buffer texture + + GLMTexLayoutKey colorkey; + //CGLMTex *colortex; + memset( &colorkey, 0, sizeof(colorkey) ); + + colorkey.m_texGLTarget = GL_TEXTURE_2D; + colorkey.m_xSize = m_drawWidth; + colorkey.m_ySize = m_drawHeight; + colorkey.m_zSize = 1; + + colorkey.m_texFormat = D3DFMT_A8R8G8B8; + colorkey.m_texFlags = kGLMTexRenderable; + + m_drawColorTex = ctx->NewTex( &colorkey ); + + // do not leave that texture bound on the TMU + ctx->BindTexToTMU(NULL, 0 ); + + + // attach color to FBO + GLMFBOTexAttachParams colorParams; + memset( &colorParams, 0, sizeof(colorParams) ); + + colorParams.m_tex = m_drawColorTex; + colorParams.m_face = 0; + colorParams.m_mip = 0; + colorParams.m_zslice= 0; // for clarity.. + + m_drawFBO->TexAttach( &colorParams, kAttColor0 ); + + // check it. + bool ready = m_drawFBO->IsReady(); + InternalError( !ready, "drawing FBO no go"); + + // bind it + ctx->BindFBOToCtx( m_drawFBO, GL_FRAMEBUFFER_EXT ); + + gGL->glViewport(0, 0, (GLsizei) m_drawWidth, (GLsizei) m_drawHeight ); + CheckGLError("stdsetup viewport"); + + gGL->glScissor( 0,0, (GLsizei) m_drawWidth, (GLsizei) m_drawHeight ); + CheckGLError("stdsetup scissor"); + + gGL->glOrtho( -1,1, -1,1, -1,1 ); + CheckGLError("stdsetup ortho"); + + // activate debug font + ctx->GenDebugFontTex(); +} + +void GLMTester::StdCleanup( void ) +{ + GLMContext *ctx = m_params.m_ctx; + + // unbind + ctx->BindFBOToCtx( NULL, GL_FRAMEBUFFER_EXT ); + + // del FBO + if (m_drawFBO) + { + ctx->DelFBO( m_drawFBO ); + m_drawFBO = NULL; + } + + // del tex + if (m_drawColorTex) + { + ctx->DelTex( m_drawColorTex ); + m_drawColorTex = NULL; + } + + if (m_drawDepthTex) + { + ctx->DelTex( m_drawDepthTex ); + m_drawDepthTex = NULL; + } +} + + +void GLMTester::Clear( void ) +{ + GLMContext *ctx = m_params.m_ctx; + ctx->MakeCurrent(); + + gGL->glViewport(0, 0, (GLsizei) m_drawWidth, (GLsizei) m_drawHeight ); + gGL->glScissor( 0,0, (GLsizei) m_drawWidth, (GLsizei) m_drawHeight ); + gGL->glOrtho( -1,1, -1,1, -1,1 ); + CheckGLError("clearing viewport"); + + // clear to black + gGL->glClearColor(0.0f, 0.0f, 0.0, 1.0f); + CheckGLError("clearing color"); + + gGL->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + CheckGLError("clearing"); + + //glFinish(); + //CheckGLError("clear finish"); +} + +void GLMTester::Present( int seed ) +{ + GLMContext *ctx = m_params.m_ctx; + ctx->Present( m_drawColorTex ); +} + +void GLMTester::CheckGLError( const char *comment ) +{ +return; + char errbuf[1024]; + + //borrowed from GLMCheckError.. slightly different + + if (!comment) + { + comment = ""; + } + + GLenum errorcode = (GLenum)gGL->glGetError(); + GLenum errorcode2 = 0; + if ( errorcode != GL_NO_ERROR ) + { + const char *decodedStr = GLMDecode( eGL_ERROR, errorcode ); + const char *decodedStr2 = ""; + + if ( errorcode == GL_INVALID_FRAMEBUFFER_OPERATION_EXT ) + { + // dig up the more detailed FBO status + errorcode2 = gGL->glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ); + + decodedStr2 = GLMDecode( eGL_ERROR, errorcode2 ); + + sprintf( errbuf, "\n%s - GL Error %08x/%08x = '%s / %s'\n", comment, errorcode, errorcode2, decodedStr, decodedStr2 ); + } + else + { + sprintf( errbuf, "\n%s - GL Error %08x = '%s'\n", comment, errorcode, decodedStr ); + } + + if ( m_params.m_glErrToConsole ) + { + printf("%s", errbuf ); + } + + if ( m_params.m_glErrToDebugger ) + { + DebuggerBreak(); + } + } +} + +void GLMTester::InternalError( int errcode, char *comment ) +{ + if (errcode) + { + if (m_params.m_intlErrToConsole) + { + printf("%s - error %d\n", comment, errcode ); + } + + if (m_params.m_intlErrToDebugger) + { + DebuggerBreak(); + } + } +} + + +void GLMTester::RunTests( void ) +{ + int *testList = m_params.m_testList; + + while( (*testList >=0) && (*testList < 20) ) + { + RunOneTest( *testList++ ); + } +} + +void GLMTester::RunOneTest( int testindex ) +{ + // this might be better with 'ptmf' style + switch(testindex) + { + case 0: Test0(); break; + case 1: Test1(); break; + case 2: Test2(); break; + case 3: Test3(); break; + + default: + DebuggerBreak(); // unrecognized + } +} + +// ##################################################################################################################### + +// some fixed lists which may be useful to all tests + +D3DFORMAT g_drawTexFormatsGLMT[] = // -1 terminated +{ + D3DFMT_A8R8G8B8, + D3DFMT_A4R4G4B4, + D3DFMT_X8R8G8B8, + D3DFMT_X1R5G5B5, + D3DFMT_A1R5G5B5, + D3DFMT_L8, + D3DFMT_A8L8, + D3DFMT_R8G8B8, + D3DFMT_A8, + D3DFMT_R5G6B5, + D3DFMT_DXT1, + D3DFMT_DXT3, + D3DFMT_DXT5, + D3DFMT_A32B32G32R32F, + D3DFMT_A16B16G16R16, + + (D3DFORMAT)-1 +}; + +D3DFORMAT g_fboColorTexFormatsGLMT[] = // -1 terminated +{ + D3DFMT_A8R8G8B8, + //D3DFMT_A4R4G4B4, //unsupported + D3DFMT_X8R8G8B8, + D3DFMT_X1R5G5B5, + //D3DFMT_A1R5G5B5, //unsupported + D3DFMT_A16B16G16R16F, + D3DFMT_A32B32G32R32F, + D3DFMT_R5G6B5, + + (D3DFORMAT)-1 +}; + +D3DFORMAT g_fboDepthTexFormatsGLMT[] = // -1 terminated, but note 0 for "no depth" mode +{ + (D3DFORMAT)0, + D3DFMT_D16, + D3DFMT_D24X8, + D3DFMT_D24S8, + + (D3DFORMAT)-1 +}; + + +// ##################################################################################################################### + +void GLMTester::Test0( void ) +{ + // make and delete a bunch of textures. + // lock and unlock them. + // use various combos of - + + // √texel format + // √2D | 3D | cube map + // √mipped / not + // √POT / NPOT + // large / small / square / rect + // square / rect + + GLMContext *ctx = m_params.m_ctx; + ctx->MakeCurrent(); + + CUtlVector< CGLMTex* > testTextures; // will hold all the built textures + + // test stage loop + // 0 is creation + // 1 is lock/unlock + // 2 is deletion + + for( int teststage = 0; teststage < 3; teststage++) + { + int innerindex = 0; // increment at stage switch + // format loop + for( D3DFORMAT *fmtPtr = g_drawTexFormatsGLMT; *fmtPtr != ((D3DFORMAT)-1); fmtPtr++ ) + { + // form loop + GLenum forms[] = { GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP, (GLenum)-1 }; + + for( GLenum *formPtr = forms; *formPtr != ((GLenum)-1); formPtr++ ) + { + // mip loop + for( int mipped = 0; mipped < 2; mipped++ ) + { + // large / square / pot loop + // &4 == large &2 == square &1 == POT + // NOTE you *have to be square* for cube maps. + + for( int aspect = 0; aspect < 8; aspect++ ) + { + switch( teststage ) + { + case 0: + { + GLMTexLayoutKey key; + memset( &key, 0, sizeof(key) ); + + key.m_texGLTarget = *formPtr; + key.m_texFormat = *fmtPtr; + if (mipped) + key.m_texFlags |= kGLMTexMipped; + + // assume big, square, POT, and 3D, then adjust as needed + key.m_xSize = key.m_ySize = key.m_zSize = 256; + + if ( !(aspect&4) ) // big or little ? + { + // little + key.m_xSize >>= 2; + key.m_ySize >>= 2; + key.m_zSize >>= 2; + } + + if ( key.m_texGLTarget != GL_TEXTURE_CUBE_MAP ) + { + if ( !(aspect & 2) ) // square or rect? + { + // rect + key.m_ySize >>= 1; + key.m_zSize >>= 2; + } + } + + if ( !(aspect&1) ) // POT or NPOT? + { + // NPOT + key.m_xSize += 56; + key.m_ySize += 56; + key.m_zSize += 56; + } + + // 2D, 3D, cube map ? + if (key.m_texGLTarget!=GL_TEXTURE_3D) + { + // 2D or cube map: flatten Z extent to one texel + key.m_zSize = 1; + } + else + { + // 3D: knock down Z quite a bit so our test case does not run out of RAM + key.m_zSize >>= 3; + if (!key.m_zSize) + { + key.m_zSize = 1; + } + } + + CGLMTex *newtex = ctx->NewTex( &key ); + CheckGLError( "tex create test"); + InternalError( newtex==NULL, "tex create test" ); + + testTextures.AddToTail( newtex ); + printf("\n[%5d] created tex %s",innerindex,newtex->m_layout->m_layoutSummary ); + } + break; + + case 1: + { + CGLMTex *ptex = testTextures[innerindex]; + + for( int face=0; face m_layout->m_faceCount; face++) + { + for( int mip=0; mip m_layout->m_mipCount; mip++) + { + GLMTexLockParams lockreq; + + lockreq.m_tex = ptex; + lockreq.m_face = face; + lockreq.m_mip = mip; + + GLMTexLayoutSlice *slice = &ptex->m_layout->m_slices[ ptex->CalcSliceIndex( face, mip ) ]; + + lockreq.m_region.xmin = lockreq.m_region.ymin = lockreq.m_region.zmin = 0; + lockreq.m_region.xmax = slice->m_xSize; + lockreq.m_region.ymax = slice->m_ySize; + lockreq.m_region.zmax = slice->m_zSize; + + char *lockAddress; + int yStride; + int zStride; + + ptex->Lock( &lockreq, &lockAddress, &yStride, &zStride ); + CheckGLError( "tex lock test"); + InternalError( lockAddress==NULL, "null lock address"); + + // write some texels of this flavor: + // red 75% green 40% blue 15% alpha 80% + + GLMGenTexelParams gtp; + + gtp.m_format = ptex->m_layout->m_format->m_d3dFormat; + gtp.m_dest = lockAddress; + gtp.m_chunkCount = (slice->m_xSize * slice->m_ySize * slice->m_zSize) / (ptex->m_layout->m_format->m_chunkSize * ptex->m_layout->m_format->m_chunkSize); + gtp.m_byteCountLimit = slice->m_storageSize; + gtp.r = 0.75; + gtp.g = 0.40; + gtp.b = 0.15; + gtp.a = 0.80; + + GLMGenTexels( >p ); + + InternalError( gtp.m_bytesWritten != gtp.m_byteCountLimit, "byte count mismatch from GLMGenTexels" ); + } + } + + for( int face=0; face m_layout->m_faceCount; face++) + { + for( int mip=0; mip m_layout->m_mipCount; mip++) + { + GLMTexLockParams unlockreq; + + unlockreq.m_tex = ptex; + unlockreq.m_face = face; + unlockreq.m_mip = mip; + + // region need not matter for unlocks + unlockreq.m_region.xmin = unlockreq.m_region.ymin = unlockreq.m_region.zmin = 0; + unlockreq.m_region.xmax = unlockreq.m_region.ymax = unlockreq.m_region.zmax = 0; + + //char *lockAddress; + //int yStride; + //int zStride; + + ptex->Unlock( &unlockreq ); + + CheckGLError( "tex unlock test"); + } + } + printf("\n[%5d] locked/wrote/unlocked tex %s",innerindex, ptex->m_layout->m_layoutSummary ); + } + break; + + case 2: + { + CGLMTex *dtex = testTextures[innerindex]; + + printf("\n[%5d] deleting tex %s",innerindex, dtex->m_layout->m_layoutSummary ); + ctx->DelTex( dtex ); + CheckGLError( "tex delete test"); + } + break; + } // end stage switch + innerindex++; + } // end aspect loop + } // end mip loop + } // end form loop + } // end format loop + } // end stage loop +} + +// ##################################################################################################################### +void GLMTester::Test1( void ) +{ + // FBO exercises + GLMContext *ctx = m_params.m_ctx; + ctx->MakeCurrent(); + + // FBO color format loop + for( D3DFORMAT *colorFmtPtr = g_fboColorTexFormatsGLMT; *colorFmtPtr != ((D3DFORMAT)-1); colorFmtPtr++ ) + { + // FBO depth format loop + for( D3DFORMAT *depthFmtPtr = g_fboDepthTexFormatsGLMT; *depthFmtPtr != ((D3DFORMAT)-1); depthFmtPtr++ ) + { + // mip loop + for( int mipped = 0; mipped < 2; mipped++ ) + { + GLenum forms[] = { GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP, (GLenum)-1 }; + + // form loop + for( GLenum *formPtr = forms; *formPtr != ((GLenum)-1); formPtr++ ) + { + //=============================================== make an FBO + CGLMFBO *fbo = ctx->NewFBO(); + + //=============================================== make a color texture + GLMTexLayoutKey colorkey; + memset( &colorkey, 0, sizeof(colorkey) ); + + switch(*formPtr) + { + case GL_TEXTURE_2D: + colorkey.m_texGLTarget = GL_TEXTURE_2D; + colorkey.m_xSize = 800; + colorkey.m_ySize = 600; + colorkey.m_zSize = 1; + break; + + case GL_TEXTURE_3D: + colorkey.m_texGLTarget = GL_TEXTURE_3D; + colorkey.m_xSize = 800; + colorkey.m_ySize = 600; + colorkey.m_zSize = 32; + break; + + case GL_TEXTURE_CUBE_MAP: + colorkey.m_texGLTarget = GL_TEXTURE_CUBE_MAP; + colorkey.m_xSize = 800; + colorkey.m_ySize = 800; // heh, cube maps have to have square sides... + colorkey.m_zSize = 1; + break; + } + + colorkey.m_texFormat = *colorFmtPtr; + colorkey.m_texFlags = kGLMTexRenderable; + // decide if we want mips + if (mipped) + { + colorkey.m_texFlags |= kGLMTexMipped; + } + + CGLMTex *colorTex = ctx->NewTex( &colorkey ); + // Note that GLM will notice the renderable flag, and force texels to be written + // so the FBO will be complete + + //=============================================== attach color + GLMFBOTexAttachParams colorParams; + memset( &colorParams, 0, sizeof(colorParams) ); + + colorParams.m_tex = colorTex; + colorParams.m_face = (colorkey.m_texGLTarget == GL_TEXTURE_CUBE_MAP) ? 2 : 0; // just steer to an alternate face as a test + + colorParams.m_mip = (colorkey.m_texFlags & kGLMTexMipped) ? 2 : 0; // pick non-base mip slice + + colorParams.m_zslice= (colorkey.m_texGLTarget == GL_TEXTURE_3D) ? 3 : 0; // just steer to an alternate slice as a test; + + fbo->TexAttach( &colorParams, kAttColor0 ); + + + //=============================================== optional depth tex + CGLMTex *depthTex = NULL; + + if (*depthFmtPtr > 0 ) + { + GLMTexLayoutKey depthkey; + memset( &depthkey, 0, sizeof(depthkey) ); + + depthkey.m_texGLTarget = GL_TEXTURE_2D; + depthkey.m_xSize = colorkey.m_xSize >> colorParams.m_mip; // scale depth tex to match color tex + depthkey.m_ySize = colorkey.m_ySize >> colorParams.m_mip; + depthkey.m_zSize = 1; + + depthkey.m_texFormat = *depthFmtPtr; + depthkey.m_texFlags = kGLMTexRenderable | kGLMTexIsDepth; // no mips. + if (depthkey.m_texFormat==D3DFMT_D24S8) + { + depthkey.m_texFlags |= kGLMTexIsStencil; + } + + depthTex = ctx->NewTex( &depthkey ); + + + //=============================================== attach depth + GLMFBOTexAttachParams depthParams; + memset( &depthParams, 0, sizeof(depthParams) ); + + depthParams.m_tex = depthTex; + depthParams.m_face = 0; + depthParams.m_mip = 0; + depthParams.m_zslice= 0; + + EGLMFBOAttachment depthAttachIndex = (depthkey.m_texFlags & kGLMTexIsStencil) ? kAttDepthStencil : kAttDepth; + fbo->TexAttach( &depthParams, depthAttachIndex ); + } + + printf("\n FBO:\n color tex %s\n depth tex %s", + colorTex->m_layout->m_layoutSummary, + depthTex ? depthTex->m_layout->m_layoutSummary : "none" + ); + + // see if FBO is happy + bool ready = fbo->IsReady(); + + printf("\n -> %s\n", ready ? "pass" : "fail" ); + + // unbind + ctx->BindFBOToCtx( NULL, GL_FRAMEBUFFER_EXT ); + + // del FBO + ctx->DelFBO(fbo); + + // del texes + ctx->DelTex( colorTex ); + if (depthTex) ctx->DelTex( depthTex ); + } // end form loop + } // end mip loop + } // end depth loop + } // end color loop +} + +// ##################################################################################################################### + +static int selftest2_seed = 0; // inc this every run to force main thread to teardown/reset display view +void GLMTester::Test2( void ) +{ + GLMContext *ctx = m_params.m_ctx; + ctx->MakeCurrent(); + + StdSetup(); // default test case drawing setup + + // draw stuff (loop...) + for( int i=0; iglClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); + CheckGLError("test2 clear color"); + + gGL->glClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT+GL_STENCIL_BUFFER_BIT); + CheckGLError("test2 clearing"); + + // try out debug text + for( int j=0; j<16; j++) + { + char text[256]; + sprintf(text, "The quick brown fox jumped over the lazy dog %d times", i ); + + float theta = ( (i*0.10f) + (j * 6.28f) ) / 16.0f; + + float posx = cos(theta) * 0.5; + float posy = sin(theta) * 0.5; + + float charwidth = 6.0 * (2.0 / 1024.0); + float charheight = 11.0 * (2.0 / 768.0); + + ctx->DrawDebugText( posx, posy, 0.0f, charwidth, charheight, text ); + } + gGL->glFinish(); + CheckGLError("test2 finish"); + + Present( selftest2_seed ); + } + + StdCleanup(); + + selftest2_seed++; +} + +// ##################################################################################################################### + +static char g_testVertexProgram01 [] = +{ + "!!ARBvp1.0 \n" + "TEMP vertexClip; \n" + "DP4 vertexClip.x, state.matrix.mvp.row[0], vertex.position; \n" + "DP4 vertexClip.y, state.matrix.mvp.row[1], vertex.position; \n" + "DP4 vertexClip.z, state.matrix.mvp.row[2], vertex.position; \n" + "DP4 vertexClip.w, state.matrix.mvp.row[3], vertex.position; \n" + "ADD vertexClip.y, vertexClip.x, vertexClip.y; \n" + "MOV result.position, vertexClip; \n" + "MOV result.color, vertex.color; \n" + "MOV result.texcoord[0], vertex.texcoord; \n" + "END \n" +}; + +static char g_testFragmentProgram01 [] = +{ + "!!ARBfp1.0 \n" + "TEMP color; \n" + "MUL color, fragment.texcoord[0].y, 2.0; \n" + "ADD color, 1.0, -color; \n" + "ABS color, color; \n" + "ADD result.color, 1.0, -color; \n" + "MOV result.color.a, 1.0; \n" + "END \n" +}; + + +// generic attrib versions.. + +static char g_testVertexProgram01_GA [] = +{ + "!!ARBvp1.0 \n" + "TEMP vertexClip; \n" + "DP4 vertexClip.x, state.matrix.mvp.row[0], vertex.attrib[0]; \n" + "DP4 vertexClip.y, state.matrix.mvp.row[1], vertex.attrib[0]; \n" + "DP4 vertexClip.z, state.matrix.mvp.row[2], vertex.attrib[0]; \n" + "DP4 vertexClip.w, state.matrix.mvp.row[3], vertex.attrib[0]; \n" + "ADD vertexClip.y, vertexClip.x, vertexClip.y; \n" + "MOV result.position, vertexClip; \n" + "MOV result.color, vertex.attrib[3]; \n" + "MOV result.texcoord[0], vertex.attrib[8]; \n" + "END \n" +}; + +static char g_testFragmentProgram01_GA [] = +{ + "!!ARBfp1.0 \n" + "TEMP color; \n" + "TEX color, fragment.texcoord[0], texture[0], 2D;" + //"MUL color, fragment.texcoord[0].y, 2.0; \n" + //"ADD color, 1.0, -color; \n" + //"ABS color, color; \n" + //"ADD result.color, 1.0, -color; \n" + //"MOV result.color.a, 1.0; \n" + "MOV result.color, color; \n" + "END \n" +}; + + +void GLMTester::Test3( void ) +{ + /************************** + XXXXXXXXXXXXXXXXXXXXXX stale test code until we revise the program interface + + GLMContext *ctx = m_params.m_ctx; + ctx->MakeCurrent(); + + StdSetup(); // default test case drawing setup + + // make vertex&pixel shader + CGLMProgram *vprog = ctx->NewProgram( kGLMVertexProgram, g_testVertexProgram01_GA ); + ctx->BindProgramToCtx( kGLMVertexProgram, vprog ); + + CGLMProgram *fprog = ctx->NewProgram( kGLMFragmentProgram, g_testFragmentProgram01_GA ); + ctx->BindProgramToCtx( kGLMFragmentProgram, fprog ); + + // draw stuff (loop...) + for( int i=0; iDrawDebugText( posx, posy, 0.0f, charwidth, charheight, text ); + } + glFinish(); + CheckGLError("test3 finish"); + + Present( 3333 ); + } + + StdCleanup(); + *****************************/ +} + +#if GLMDEBUG +void GLMTriggerDebuggerBreak() +{ + // we call an obscure GL function which we know has been breakpointed in the OGLP function list + static signed short nada[] = { -1,-1,-1,-1 }; + gGL->glColor4sv( nada ); +} +#endif diff --git a/togles/linuxwin/glmgr_flush.inl b/togles/linuxwin/glmgr_flush.inl new file mode 100644 index 00000000..fcddd616 --- /dev/null +++ b/togles/linuxwin/glmgr_flush.inl @@ -0,0 +1,622 @@ +// BE VERY VERY CAREFUL what you do in these function. They are extremely hot, and calling the wrong GL API's in here will crush perf. (especially on NVidia threaded drivers). + +FORCEINLINE uint32 bitmix32(uint32 a) +{ + a -= (a<<6); + //a ^= (a>>17); + //a -= (a<<9); + a ^= (a<<4); + //a -= (a<<3); + //a ^= (a<<10); + a ^= (a>>15); + return a; +} + +#ifndef OSX + +FORCEINLINE GLuint GLMContext::FindSamplerObject( const GLMTexSamplingParams &desiredParams ) +{ + int h = bitmix32( desiredParams.m_bits + desiredParams.m_borderColor ) & ( cSamplerObjectHashSize - 1 ); + while ( ( m_samplerObjectHash[h].m_params.m_bits != desiredParams.m_bits ) || ( m_samplerObjectHash[h].m_params.m_borderColor != desiredParams.m_borderColor ) ) + { + if ( !m_samplerObjectHash[h].m_params.m_packed.m_isValid ) + break; + if ( ++h >= cSamplerObjectHashSize ) + h = 0; + } + + if ( !m_samplerObjectHash[h].m_params.m_packed.m_isValid ) + { + GLMTexSamplingParams &hashParams = m_samplerObjectHash[h].m_params; + hashParams = desiredParams; + hashParams.SetToSamplerObject( m_samplerObjectHash[h].m_samplerObject ); + if ( ++m_nSamplerObjectHashNumEntries == cSamplerObjectHashSize ) + { + // TODO: Support resizing + Error( "Sampler object hash is full, increase cSamplerObjectHashSize" ); + } + } + + return m_samplerObjectHash[h].m_samplerObject; +} + +#endif // !OSX + +// BE VERY CAREFUL WHAT YOU DO IN HERE. This is called on every batch, even seemingly simple changes can kill perf. +FORCEINLINE void GLMContext::FlushDrawStates( uint nStartIndex, uint nEndIndex, uint nBaseVertex ) // shadersOn = true for draw calls, false for clear calls +{ + Assert( m_drawingLang == kGLMGLSL ); // no support for ARB shaders right now (and NVidia reports that they aren't worth targeting under Windows/Linux for various reasons anyway) + Assert( ( m_drawingFBO == m_boundDrawFBO ) && ( m_drawingFBO == m_boundReadFBO ) ); // this check MUST succeed + Assert( m_pDevice->m_pVertDecl ); + +#if GLMDEBUG + GLM_FUNC; +#endif + + GL_BATCH_PERF( m_FlushStats.m_nTotalBatchFlushes++; ) + +#if GLMDEBUG + bool tex0_srgb = (m_boundDrawFBO[0].m_attach[0].m_tex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0; + + // you can only actually use the sRGB FB state on some systems.. check caps + if (m_caps.m_hasGammaWrites) + { + GLBlendEnableSRGB_t writeSRGBState; + m_BlendEnableSRGB.Read( &writeSRGBState, 0 ); // the client set value, not the API-written value yet.. + bool draw_srgb = writeSRGBState.enable != 0; + + if (draw_srgb) + { + if (tex0_srgb) + { + // good - draw mode and color tex agree + } + else + { + // bad + + // Client has asked to write sRGB into a texture that can't do it. + // there is no way to satisfy this unless we change the RT tex and we avoid doing that. + // (although we might consider a ** ONE TIME ** promotion. + // this shouldn't be a big deal if the tex format is one where it doesn't matter like 32F. + + GLMPRINTF(("-Z- srgb-enabled FBO conflict: attached tex %08x [%s] is not SRGB", m_boundDrawFBO[0].m_attach[0].m_tex, m_boundDrawFBO[0].m_attach[0].m_tex->m_layout->m_layoutSummary )); + + // do we shoot down the srgb-write state for this batch? + // I think the runtime will just ignore it. + } + } + else + { + if (tex0_srgb) + { + // odd - client is not writing sRGB into a texture which *can* do it. + //GLMPRINTF(( "-Z- srgb-disabled FBO conflict: attached tex %08x [%s] is SRGB", m_boundFBO[0].m_attach[0].m_tex, m_boundFBO[0].m_attach[0].m_tex->m_layout->m_layoutSummary )); + //writeSRGBState.enable = true; + //m_BlendEnableSRGB.Write( &writeSRGBState ); + } + else + { + // good - draw mode and color tex agree + } + } + } +#endif + + Assert( m_drawingProgram[ kGLMVertexProgram ] ); + Assert( m_drawingProgram[ kGLMFragmentProgram ] ); + + Assert( ( m_drawingProgram[kGLMVertexProgram]->m_type == kGLMVertexProgram ) && ( m_drawingProgram[kGLMFragmentProgram]->m_type == kGLMFragmentProgram ) ); + Assert( m_drawingProgram[ kGLMVertexProgram ]->m_bTranslatedProgram && m_drawingProgram[ kGLMFragmentProgram ]->m_bTranslatedProgram ); + +#if GLMDEBUG + // Depth compare mode check + uint nCurMask = 1, nShaderSamplerMask = m_drawingProgram[kGLMFragmentProgram]->m_samplerMask; + for ( int nSamplerIndex = 0; nSamplerIndex < GLM_SAMPLER_COUNT; ++nSamplerIndex, nCurMask <<= 1 ) + { + if ( !m_samplers[nSamplerIndex].m_pBoundTex ) + continue; + + if ( m_samplers[nSamplerIndex].m_pBoundTex->m_layout->m_mipCount == 1 ) + { + if ( m_samplers[nSamplerIndex].m_samp.m_packed.m_mipFilter == D3DTEXF_LINEAR ) + { + GLMDebugPrintf( "Sampler %u has mipmap filtering enabled on a texture without mipmaps! (texture name: %s, pixel shader: %s)!\n", + nSamplerIndex, + m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel ? m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel : "?", + m_drawingProgram[kGLMFragmentProgram]->m_shaderName ); + } + } + + if ( ( nShaderSamplerMask & nCurMask ) == 0 ) + continue; + + if ( m_samplers[nSamplerIndex].m_pBoundTex->m_layout->m_mipCount == 1 ) + { + if ( m_samplers[nSamplerIndex].m_samp.m_packed.m_mipFilter == D3DTEXF_LINEAR ) + { + // Note this is not always an error - shadow buffer debug visualization shaders purposely want to read shadow depths (and not do the comparison) + GLMDebugPrintf( "Sampler %u has mipmap filtering enabled on a texture without mipmaps! (texture name: %s, pixel shader: %s)!\n", + nSamplerIndex, + m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel ? m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel : "?", + m_drawingProgram[kGLMFragmentProgram]->m_shaderName ); + } + } + + bool bSamplerIsDepth = ( m_samplers[nSamplerIndex].m_pBoundTex->m_layout->m_key.m_texFlags & kGLMTexIsDepth ) != 0; + bool bSamplerShadow = m_samplers[nSamplerIndex].m_samp.m_packed.m_compareMode != 0; + + bool bShaderShadow = ( m_drawingProgram[kGLMFragmentProgram]->m_nShadowDepthSamplerMask & nCurMask ) != 0; + + if ( bShaderShadow ) + { + // Shader expects shadow depth sampling at this sampler index + // Must have a depth texture and compare mode must be enabled + if ( !bSamplerIsDepth || !bSamplerShadow ) + { + // FIXME: This occasionally occurs in L4D2 when CShaderAPIDx8::ExecuteCommandBuffer() sets the TEXTURE_WHITE texture in the flashlight depth texture slot. + GLMDebugPrintf( "Sampler %u's compare mode (%u) or format (depth=%u) is not consistent with pixel shader's compare mode (%u) (texture name: %s, pixel shader: %s)!\n", + nSamplerIndex, bSamplerShadow, bSamplerIsDepth, bShaderShadow, + m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel ? m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel : "?", + m_drawingProgram[kGLMFragmentProgram]->m_shaderName ); + } + } + else + { + // Shader does not expect shadow depth sampling as this sampler index + // We don't care if comparemode is enabled, but we can't have a depth texture in this sampler + if ( bSamplerIsDepth ) + { + GLMDebugPrintf( "Sampler %u is a depth texture but the pixel shader's shadow depth sampler mask does not expect depth here (texture name: %s, pixel shader: %s)!\n", + nSamplerIndex, + m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel ? m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel : "?", + m_drawingProgram[kGLMFragmentProgram]->m_shaderName ); + } + } + } +#endif + + if ( m_bDirtyPrograms ) + { + m_bDirtyPrograms = false; + + CGLMShaderPair *pNewPair = m_pairCache->SelectShaderPair( m_drawingProgram[ kGLMVertexProgram ], m_drawingProgram[ kGLMFragmentProgram ], 0 ); + + if ( pNewPair != m_pBoundPair ) + { +#if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "NewProgram" ); +#endif + + if ( !pNewPair->m_valid ) + { + if ( !pNewPair->ValidateProgramPair() ) + { + goto flush_error_exit; + } + } + + gGL->glUseProgram( (GLuint)pNewPair->m_program ); + + GL_BATCH_PERF( m_FlushStats.m_nTotalProgramPairChanges++; ) + + if ( !m_pBoundPair ) + { + GL_BATCH_PERF( m_FlushStats.m_nNewPS++; ) + GL_BATCH_PERF( m_FlushStats.m_nNewVS++; ) + } + else + { + GL_BATCH_PERF( if ( pNewPair->m_fragmentProg != m_pBoundPair->m_fragmentProg ) m_FlushStats.m_nNewPS++; ) + GL_BATCH_PERF( if ( pNewPair->m_vertexProg != m_pBoundPair->m_vertexProg ) m_FlushStats.m_nNewVS++; ) + } + +#if GL_BATCH_PERF_ANALYSIS + tmMessage( TELEMETRY_LEVEL2, TMMF_ICON_NOTE, "V:%s (V Regs:%u V Bone Regs:%u) F:%s (F Regs:%u)", + m_drawingProgram[ kGLMVertexProgram ]->m_shaderName, + m_drawingProgram[ kGLMVertexProgram ]->m_descs[kGLMGLSL].m_highWater, + m_drawingProgram[ kGLMVertexProgram ]->m_descs[kGLMGLSL].m_VSHighWaterBone, + m_drawingProgram[ kGLMFragmentProgram ]->m_shaderName, + m_drawingProgram[ kGLMFragmentProgram ]->m_descs[kGLMGLSL].m_highWater ); +#endif + + m_pBoundPair = pNewPair; + + // set the dirty levels appropriately since the program changed and has never seen any of the current values. + m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = 0; + m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = m_drawingProgram[ kGLMVertexProgram ]->m_descs[kGLMGLSL].m_highWater; + m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone = m_drawingProgram[ kGLMVertexProgram ]->m_descs[kGLMGLSL].m_VSHighWaterBone; + + m_programParamsF[kGLMFragmentProgram].m_firstDirtySlotNonBone = 0; + m_programParamsF[kGLMFragmentProgram].m_dirtySlotHighWaterNonBone = m_drawingProgram[ kGLMFragmentProgram ]->m_descs[kGLMGLSL].m_highWater; + + // bool and int dirty levels get set to max, we don't have actual high water marks for them + // code which sends the values must clamp on these types. + m_programParamsB[kGLMVertexProgram].m_dirtySlotCount = kGLMProgramParamBoolLimit; + m_programParamsB[kGLMFragmentProgram].m_dirtySlotCount = kGLMProgramParamBoolLimit; + + m_programParamsI[kGLMVertexProgram].m_dirtySlotCount = kGLMProgramParamInt4Limit; + m_programParamsI[kGLMFragmentProgram].m_dirtySlotCount = 0; + + // check fragment buffers used (MRT) + if( pNewPair->m_fragmentProg->m_fragDataMask != m_fragDataMask ) + { + gGL->glDrawBuffers( pNewPair->m_fragmentProg->m_numDrawBuffers, pNewPair->m_fragmentProg->m_drawBuffers ); + m_fragDataMask = pNewPair->m_fragmentProg->m_fragDataMask; + } + } + } + + Assert( m_ViewportBox.GetData().width == (int)( m_ViewportBox.GetData().widthheight & 0xFFFF ) ); + Assert( m_ViewportBox.GetData().height == (int)( m_ViewportBox.GetData().widthheight >> 16 ) ); + + m_pBoundPair->UpdateScreenUniform( m_ViewportBox.GetData().widthheight ); + + GL_BATCH_PERF( m_FlushStats.m_nNumChangedSamplers += m_nNumDirtySamplers ); + +#if !defined( OSX ) // no support for sampler objects in OSX 10.6 (GL 2.1 profile) + if ( m_bUseSamplerObjects) + { + while ( m_nNumDirtySamplers ) + { + const uint nSamplerIndex = m_nDirtySamplers[--m_nNumDirtySamplers]; + Assert( ( nSamplerIndex < GLM_SAMPLER_COUNT ) && ( !m_nDirtySamplerFlags[nSamplerIndex]) ); + + m_nDirtySamplerFlags[nSamplerIndex] = 1; + + gGL->glBindSampler( nSamplerIndex, FindSamplerObject( m_samplers[nSamplerIndex].m_samp ) ); + + GL_BATCH_PERF( m_FlushStats.m_nNumSamplingParamsChanged++ ); + +#if defined( OSX ) // valid for OSX only if using GL 3.3 context + CGLMTex *pTex = m_samplers[nSamplerIndex].m_pBoundTex; + + if( pTex && !( gGL->m_bHave_GL_EXT_texture_sRGB_decode ) ) + { + // see if requested SRGB state differs from the known one + bool texSRGB = ( pTex->m_layout->m_key.m_texFlags & kGLMTexSRGB ) != 0; + bool glSampSRGB = m_samplers[nSamplerIndex].m_samp.m_packed.m_srgb; + + if ( texSRGB != glSampSRGB ) // mismatch + { + pTex->HandleSRGBMismatch( glSampSRGB, pTex->m_srgbFlipCount ); + } + } +#endif + } + } + else +#endif // if !defined( OSX ) + { + while ( m_nNumDirtySamplers ) + { + const uint nSamplerIndex = m_nDirtySamplers[--m_nNumDirtySamplers]; + Assert( ( nSamplerIndex < GLM_SAMPLER_COUNT ) && ( !m_nDirtySamplerFlags[nSamplerIndex]) ); + + m_nDirtySamplerFlags[nSamplerIndex] = 1; + + CGLMTex *pTex = m_samplers[nSamplerIndex].m_pBoundTex; + + if ( ( pTex ) && ( !( pTex->m_SamplingParams == m_samplers[nSamplerIndex].m_samp ) ) ) + { + SelectTMU( nSamplerIndex ); + + m_samplers[nSamplerIndex].m_samp.DeltaSetToTarget( pTex->m_texGLTarget, pTex->m_SamplingParams ); + + pTex->m_SamplingParams = m_samplers[nSamplerIndex].m_samp; + +#if defined( OSX ) + if( pTex && !( gGL->m_bHave_GL_EXT_texture_sRGB_decode ) ) + { + // see if requested SRGB state differs from the known one + bool texSRGB = ( pTex->m_layout->m_key.m_texFlags & kGLMTexSRGB ) != 0; + bool glSampSRGB = m_samplers[nSamplerIndex].m_samp.m_packed.m_srgb; + + if ( texSRGB != glSampSRGB ) // mismatch + { + pTex->HandleSRGBMismatch( glSampSRGB, pTex->m_srgbFlipCount ); + } + } +#endif + } + } + } + + // vertex stage -------------------------------------------------------------------- + if ( m_bUseBoneUniformBuffers ) + { + // vertex stage -------------------------------------------------------------------- + if ( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone ) + { + int firstDirtySlot = m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone; + int dirtySlotHighWater = MIN( m_drawingProgram[kGLMVertexProgram]->m_descs[kGLMGLSL].m_highWater, m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone ); + + GLint vconstLoc = m_pBoundPair->m_locVertexParams; + if ( ( vconstLoc >= 0 ) && ( dirtySlotHighWater > firstDirtySlot ) ) + { +#if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "VSNonBoneUniformUpdate %u %u", firstDirtySlot, dirtySlotHighWater ); +#endif + int numSlots = dirtySlotHighWater - DXABSTRACT_VS_FIRST_BONE_SLOT; + + // consts after the bones (c217 onwards), since we use the concatenated destination array vc[], upload these consts starting from vc[58] + if( numSlots > 0 ) + { + gGL->glUniform4fv( m_pBoundPair->m_UniformBufferParams[kGLMVertexProgram][DXABSTRACT_VS_FIRST_BONE_SLOT], numSlots, &m_programParamsF[kGLMVertexProgram].m_values[(DXABSTRACT_VS_LAST_BONE_SLOT+1)][0] ); + + dirtySlotHighWater = DXABSTRACT_VS_FIRST_BONE_SLOT; + + GL_BATCH_PERF( m_nTotalVSUniformCalls++; ) + GL_BATCH_PERF( m_nTotalVSUniformsSet += numSlots; ) + + GL_BATCH_PERF( m_FlushStats.m_nFirstVSConstant = DXABSTRACT_VS_FIRST_BONE_SLOT; ) + GL_BATCH_PERF( m_FlushStats.m_nNumVSConstants += numSlots; ) + } + + numSlots = dirtySlotHighWater - firstDirtySlot; + + // consts before the bones (c0-c57) + if( numSlots > 0 ) + { + gGL->glUniform4fv( m_pBoundPair->m_UniformBufferParams[kGLMVertexProgram][firstDirtySlot], dirtySlotHighWater - firstDirtySlot, &m_programParamsF[kGLMVertexProgram].m_values[firstDirtySlot][0] ); + + GL_BATCH_PERF( m_nTotalVSUniformCalls++; ) + GL_BATCH_PERF( m_nTotalVSUniformsSet += dirtySlotHighWater - firstDirtySlot; ) + + GL_BATCH_PERF( m_FlushStats.m_nFirstVSConstant = firstDirtySlot; ) + GL_BATCH_PERF( m_FlushStats.m_nNumVSConstants += (dirtySlotHighWater - firstDirtySlot); ) + } + } + + m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = 256; + m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = 0; + } + + if ( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone ) + { + const GLint vconstBoneLoc = m_pBoundPair->m_locVertexBoneParams; + if ( vconstBoneLoc >= 0 ) + { + int shaderSlotsBone = 0; + if ( ( m_drawingProgram[kGLMVertexProgram]->m_descs[kGLMGLSL].m_VSHighWaterBone > 0 ) && ( m_nMaxUsedVertexProgramConstantsHint > DXABSTRACT_VS_FIRST_BONE_SLOT ) ) + { + shaderSlotsBone = MIN( m_drawingProgram[kGLMVertexProgram]->m_descs[kGLMGLSL].m_VSHighWaterBone, m_nMaxUsedVertexProgramConstantsHint - DXABSTRACT_VS_FIRST_BONE_SLOT ); + } + + int dirtySlotHighWaterBone = MIN( shaderSlotsBone, m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone ); + if ( dirtySlotHighWaterBone ) + { + uint nNumBoneRegs = dirtySlotHighWaterBone; + +#if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "VSBoneUniformUpdate %u", nNumBoneRegs ); +#endif + + gGL->glUniform4fv( vconstBoneLoc, nNumBoneRegs, &m_programParamsF[kGLMVertexProgram].m_values[DXABSTRACT_VS_FIRST_BONE_SLOT][0] ); + + GL_BATCH_PERF( m_nTotalVSUniformBoneCalls++; ) + GL_BATCH_PERF( m_nTotalVSUniformsBoneSet += nNumBoneRegs; ) + GL_BATCH_PERF( m_FlushStats.m_nNumVSBoneConstants += nNumBoneRegs; ) + } + + m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone = 0; + } + } + + } + else + { + if ( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone ) + { + const int nMaxUsedShaderSlots = m_drawingProgram[kGLMVertexProgram]->m_descs[kGLMGLSL].m_highWater; + + int firstDirtySlot = m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone; + int dirtySlotHighWater = MIN( nMaxUsedShaderSlots, m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone ); + + GLint vconstLoc = m_pBoundPair->m_locVertexParams; + if ( ( vconstLoc >= 0 ) && ( dirtySlotHighWater > firstDirtySlot ) ) + { + #if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "VSNonBoneUniformUpdate %u %u", firstDirtySlot, dirtySlotHighWater ); + #endif + gGL->glUniform4fv( m_pBoundPair->m_UniformBufferParams[kGLMVertexProgram][firstDirtySlot], dirtySlotHighWater - firstDirtySlot, &m_programParamsF[kGLMVertexProgram].m_values[firstDirtySlot][0] ); + + GL_BATCH_PERF( m_nTotalVSUniformCalls++; ) + GL_BATCH_PERF( m_nTotalVSUniformsSet += dirtySlotHighWater - firstDirtySlot; ) + + GL_BATCH_PERF( m_FlushStats.m_nFirstVSConstant = firstDirtySlot; ) + GL_BATCH_PERF( m_FlushStats.m_nNumVSConstants += (dirtySlotHighWater - firstDirtySlot); ) + } + + m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = 256; + m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = 0; + } + } + + + // see if VS uses i0, b0, b1, b2, b3. + // use a glUniform1i to set any one of these if active. skip all of them if no dirties reported. + // my kingdom for the UBO extension! + + // ------- bools ---------- // + if ( m_pBoundPair->m_bHasBoolOrIntUniforms ) + { + if ( m_programParamsB[kGLMVertexProgram].m_dirtySlotCount ) // optimize this later after the float param pushes are proven out + { + const uint nLimit = MIN( CGLMShaderPair::cMaxVertexShaderBoolUniforms, m_programParamsB[kGLMVertexProgram].m_dirtySlotCount ); + for ( uint i = 0; i < nLimit; ++i ) + { + GLint constBoolLoc = m_pBoundPair->m_locVertexBool[i]; + if ( constBoolLoc >= 0 ) + gGL->glUniform1i( constBoolLoc, m_programParamsB[kGLMVertexProgram].m_values[i] ); + } + + m_programParamsB[kGLMVertexProgram].m_dirtySlotCount = 0; + } + + if ( m_programParamsB[kGLMFragmentProgram].m_dirtySlotCount ) // optimize this later after the float param pushes are proven out + { + const uint nLimit = MIN( CGLMShaderPair::cMaxFragmentShaderBoolUniforms, m_programParamsB[kGLMFragmentProgram].m_dirtySlotCount ); + for ( uint i = 0; i < nLimit; ++i ) + { + GLint constBoolLoc = m_pBoundPair->m_locFragmentBool[i]; + if ( constBoolLoc >= 0 ) + gGL->glUniform1i( constBoolLoc, m_programParamsB[kGLMFragmentProgram].m_values[i] ); + } + + m_programParamsB[kGLMFragmentProgram].m_dirtySlotCount = 0; + } + + if ( m_programParamsI[kGLMVertexProgram].m_dirtySlotCount ) + { + GLint vconstInt0Loc = m_pBoundPair->m_locVertexInteger0; //glGetUniformLocationARB( prog, "i0"); + if ( vconstInt0Loc >= 0 ) + { + gGL->glUniform1i( vconstInt0Loc, m_programParamsI[kGLMVertexProgram].m_values[0][0] ); //FIXME magic number + } + m_programParamsI[kGLMVertexProgram].m_dirtySlotCount = 0; + } + } + + Assert( ( m_pDevice->m_streams[0].m_vtxBuffer && ( m_pDevice->m_streams[0].m_vtxBuffer->m_vtxBuffer == m_pDevice->m_vtx_buffers[0] ) ) || ( ( !m_pDevice->m_streams[0].m_vtxBuffer ) && ( m_pDevice->m_vtx_buffers[0] == m_pDevice->m_pDummy_vtx_buffer ) ) ); + Assert( ( m_pDevice->m_streams[1].m_vtxBuffer && ( m_pDevice->m_streams[1].m_vtxBuffer->m_vtxBuffer == m_pDevice->m_vtx_buffers[1] ) ) || ( ( !m_pDevice->m_streams[1].m_vtxBuffer ) && ( m_pDevice->m_vtx_buffers[1] == m_pDevice->m_pDummy_vtx_buffer ) ) ); + Assert( ( m_pDevice->m_streams[2].m_vtxBuffer && ( m_pDevice->m_streams[2].m_vtxBuffer->m_vtxBuffer == m_pDevice->m_vtx_buffers[2] ) ) || ( ( !m_pDevice->m_streams[2].m_vtxBuffer ) && ( m_pDevice->m_vtx_buffers[2] == m_pDevice->m_pDummy_vtx_buffer ) ) ); + Assert( ( m_pDevice->m_streams[3].m_vtxBuffer && ( m_pDevice->m_streams[3].m_vtxBuffer->m_vtxBuffer == m_pDevice->m_vtx_buffers[3] ) ) || ( ( !m_pDevice->m_streams[3].m_vtxBuffer ) && ( m_pDevice->m_vtx_buffers[3] == m_pDevice->m_pDummy_vtx_buffer ) ) ); + + uint nCurTotalBufferRevision; + nCurTotalBufferRevision = m_pDevice->m_vtx_buffers[0]->m_nRevision + m_pDevice->m_vtx_buffers[1]->m_nRevision + m_pDevice->m_vtx_buffers[2]->m_nRevision + m_pDevice->m_vtx_buffers[3]->m_nRevision; + + // If any of these inputs have changed, we need to enumerate through all of the expected GL vertex attribs and modify anything in the GL layer that have changed. + // This is not always a win, but it is a net win on NVidia (by 1-4.8% depending on whether driver threading is enabled). + if ( ( nCurTotalBufferRevision != m_CurAttribs.m_nTotalBufferRevision ) || + ( m_CurAttribs.m_pVertDecl != m_pDevice->m_pVertDecl ) || + ( m_CurAttribs.m_vtxAttribMap[0] != reinterpret_cast(m_pDevice->m_vertexShader->m_vtxAttribMap)[0] ) || + ( m_CurAttribs.m_vtxAttribMap[1] != reinterpret_cast(m_pDevice->m_vertexShader->m_vtxAttribMap)[1] ) || + ( memcmp( m_CurAttribs.m_streams, m_pDevice->m_streams, sizeof( m_pDevice->m_streams ) ) != 0 ) ) + { + // This branch is taken 52.2% of the time in the L4D2 test1 (long) timedemo. + +#if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "SetVertexAttribs" ); +#endif + + m_CurAttribs.m_nTotalBufferRevision = nCurTotalBufferRevision; + m_CurAttribs.m_pVertDecl = m_pDevice->m_pVertDecl; + m_CurAttribs.m_vtxAttribMap[0] = reinterpret_cast(m_pDevice->m_vertexShader->m_vtxAttribMap)[0]; + m_CurAttribs.m_vtxAttribMap[1] = reinterpret_cast(m_pDevice->m_vertexShader->m_vtxAttribMap)[1]; + memcpy( m_CurAttribs.m_streams, m_pDevice->m_streams, sizeof( m_pDevice->m_streams ) ); + + unsigned char *pVertexShaderAttribMap = m_pDevice->m_vertexShader->m_vtxAttribMap; + const int nMaxVertexAttributesToCheck = m_drawingProgram[ kGLMVertexProgram ]->m_maxVertexAttrs; + + IDirect3DVertexDeclaration9 *pVertDecl = m_pDevice->m_pVertDecl; + const uint8 *pVertexAttribDescToStreamIndex = pVertDecl->m_VertexAttribDescToStreamIndex; + + for( int nMask = 1, nIndex = 0; nIndex < nMaxVertexAttributesToCheck; ++nIndex, nMask <<= 1 ) + { + uint8 vertexShaderAttrib = pVertexShaderAttribMap[ nIndex ]; + + uint nDeclIndex = pVertexAttribDescToStreamIndex[vertexShaderAttrib]; + if ( nDeclIndex == 0xFF ) + { + // Not good - the vertex shader has an attribute which can't be located in the decl! + // The D3D9 debug runtime is also going to complain. + Assert( 0 ); + + if ( m_lastKnownVertexAttribMask & nMask ) + { + m_lastKnownVertexAttribMask &= ~nMask; + gGL->glDisableVertexAttribArray( nIndex ); + } + continue; + } + + D3DVERTEXELEMENT9_GL *pDeclElem = &pVertDecl->m_elements[nDeclIndex]; + + Assert( ( ( vertexShaderAttrib >> 4 ) == pDeclElem->m_dxdecl.Usage ) && ( ( vertexShaderAttrib & 0x0F ) == pDeclElem->m_dxdecl.UsageIndex) ); + + const uint nStreamIndex = pDeclElem->m_dxdecl.Stream; + const D3DStreamDesc *pStream = &m_pDevice->m_streams[ nStreamIndex ]; + + CGLMBuffer *pBuf = m_pDevice->m_vtx_buffers[ nStreamIndex ]; + if ( pBuf == m_pDevice->m_pDummy_vtx_buffer ) + { + Assert( pStream->m_vtxBuffer == NULL ); + + // this shader doesn't use that pair. + if ( m_lastKnownVertexAttribMask & nMask ) + { + m_lastKnownVertexAttribMask &= ~nMask; + gGL->glDisableVertexAttribArray( nIndex ); + } + continue; + } + Assert( pStream->m_vtxBuffer->m_vtxBuffer == pBuf ); + + int nBufOffset = pDeclElem->m_gldecl.m_offset + pStream->m_offset; + Assert( nBufOffset >= 0 ); + Assert( nBufOffset < (int)pBuf->m_nSize ); + if ( pBuf->m_bUsingPersistentBuffer ) + { + nBufOffset += pBuf->m_nPersistentBufferStartOffset; + } + + SetBufAndVertexAttribPointer( nIndex, pBuf->GetHandle(), + pStream->m_stride, pDeclElem->m_gldecl.m_datatype, pDeclElem->m_gldecl.m_normalized, pDeclElem->m_gldecl.m_nCompCount, + reinterpret_cast< const GLvoid * >( reinterpret_cast< int >( pBuf->m_pPseudoBuf ) + nBufOffset ), + pBuf->m_nRevision ); + + if ( !( m_lastKnownVertexAttribMask & nMask ) ) + { + m_lastKnownVertexAttribMask |= nMask; + gGL->glEnableVertexAttribArray( nIndex ); + } + } + + for( int nIndex = nMaxVertexAttributesToCheck; nIndex < m_nNumSetVertexAttributes; nIndex++ ) + { + gGL->glDisableVertexAttribArray( nIndex ); + m_lastKnownVertexAttribMask &= ~(1 << nIndex); + } + + m_nNumSetVertexAttributes = nMaxVertexAttributesToCheck; + } + + // fragment stage -------------------------------------------------------------------- + if ( m_programParamsF[kGLMFragmentProgram].m_dirtySlotHighWaterNonBone ) + { + GLint fconstLoc; + fconstLoc = m_pBoundPair->m_locFragmentParams; + if ( fconstLoc >= 0 ) + { + const int nMaxUsedShaderSlots = m_drawingProgram[kGLMFragmentProgram]->m_descs[kGLMGLSL].m_highWater; + + int firstDirtySlot = m_programParamsF[kGLMFragmentProgram].m_firstDirtySlotNonBone; + int dirtySlotHighWater = MIN( nMaxUsedShaderSlots, m_programParamsF[kGLMFragmentProgram].m_dirtySlotHighWaterNonBone ); + + if ( dirtySlotHighWater > firstDirtySlot ) + { +#if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "PSUniformUpdate %u %u", firstDirtySlot, dirtySlotHighWater ); +#endif + + gGL->glUniform4fv( m_pBoundPair->m_UniformBufferParams[kGLMFragmentProgram][firstDirtySlot], dirtySlotHighWater - firstDirtySlot, &m_programParamsF[kGLMFragmentProgram].m_values[firstDirtySlot][0] ); + + GL_BATCH_PERF( m_nTotalPSUniformCalls++; ) + GL_BATCH_PERF( m_nTotalPSUniformsSet += dirtySlotHighWater - firstDirtySlot; ) + + GL_BATCH_PERF( m_FlushStats.m_nFirstPSConstant = firstDirtySlot; ) + GL_BATCH_PERF( m_FlushStats.m_nNumPSConstants += (dirtySlotHighWater - firstDirtySlot); ) + } + m_programParamsF[kGLMFragmentProgram].m_firstDirtySlotNonBone = 256; + m_programParamsF[kGLMFragmentProgram].m_dirtySlotHighWaterNonBone = 0; + } + } + + return; + +flush_error_exit: + m_pBoundPair = NULL; + m_bDirtyPrograms = true; + return; +} diff --git a/togles/linuxwin/glmgrbasics.cpp b/togles/linuxwin/glmgrbasics.cpp new file mode 100644 index 00000000..adf60714 --- /dev/null +++ b/togles/linuxwin/glmgrbasics.cpp @@ -0,0 +1,4688 @@ +//========= 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. +// +// glmgrbasics.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +#include "tier0/icommandline.h" +#include "tier1/utlhash.h" +#include "tier1/utlmap.h" +#include "tier0/vprof.h" + +#ifdef OSX +#include +#ifdef CGLPROFILER_ENABLE +#include +#endif +#endif + +#include "tier0/valve_minmax_off.h" +#include + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +//=============================================================================== +// decoding tables for debug + +typedef struct +{ + unsigned long value; + const char *name; +} GLMValueEntry_t; + +#define TERMVALUE 0x31415926 + // terminator for value tables + +#define VE( x ) { x, #x } + // "value entry" + +const GLMValueEntry_t g_d3d_devtypes[] = +{ + VE( D3DDEVTYPE_HAL ), + VE( D3DDEVTYPE_REF ), + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_d3d_formats[] = +{ + VE( D3DFMT_INDEX16 ), + VE( D3DFMT_D16 ), + VE( D3DFMT_D24S8 ), + VE( D3DFMT_A8R8G8B8 ), + VE( D3DFMT_A4R4G4B4 ), + VE( D3DFMT_X8R8G8B8 ), + VE( D3DFMT_R5G6R5 ), + VE( D3DFMT_X1R5G5B5 ), + VE( D3DFMT_A1R5G5B5 ), + VE( D3DFMT_L8 ), + VE( D3DFMT_A8L8 ), + VE( D3DFMT_A ), + VE( D3DFMT_DXT1 ), + VE( D3DFMT_DXT3 ), + VE( D3DFMT_DXT5 ), + VE( D3DFMT_V8U8 ), + VE( D3DFMT_Q8W8V8U8 ), + VE( D3DFMT_X8L8V8U8 ), + VE( D3DFMT_A16B16G16R16F ), + VE( D3DFMT_A16B16G16R16 ), + VE( D3DFMT_R32F ), + VE( D3DFMT_A32B32G32R32F ), + VE( D3DFMT_R8G8B8 ), + VE( D3DFMT_D24X4S4 ), + VE( D3DFMT_A8 ), + VE( D3DFMT_R5G6B5 ), + VE( D3DFMT_D15S1 ), + VE( D3DFMT_D24X8 ), + VE( D3DFMT_VERTEXDATA ), + VE( D3DFMT_INDEX32 ), + + // vendor specific formats (fourcc's) + VE( D3DFMT_NV_INTZ ), + VE( D3DFMT_NV_RAWZ ), + VE( D3DFMT_NV_NULL ), + VE( D3DFMT_ATI_D16 ), + VE( D3DFMT_ATI_D24S8 ), + VE( D3DFMT_ATI_2N ), + VE( D3DFMT_ATI_1N ), + + VE( D3DFMT_UNKNOWN ), + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_d3d_rtypes[] = +{ + VE( D3DRTYPE_SURFACE ), + VE( D3DRTYPE_TEXTURE ), + VE( D3DRTYPE_VOLUMETEXTURE ), + VE( D3DRTYPE_CUBETEXTURE ), + VE( D3DRTYPE_VERTEXBUFFER ), + VE( D3DRTYPE_INDEXBUFFER ), + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_d3d_usages[] = +{ + VE( D3DUSAGE_RENDERTARGET ), + VE( D3DUSAGE_DEPTHSTENCIL ), + VE( D3DUSAGE_DYNAMIC ), + VE( D3DUSAGE_AUTOGENMIPMAP ), + //VE( D3DUSAGE_DMAP ), + //VE( D3DUSAGE_QUERY_LEGACYBUMPMAP ), + VE( D3DUSAGE_QUERY_SRGBREAD ), + VE( D3DUSAGE_QUERY_FILTER ), + VE( D3DUSAGE_QUERY_SRGBWRITE ), + VE( D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING ), + VE( D3DUSAGE_QUERY_VERTEXTEXTURE ), + //VE( D3DUSAGE_QUERY_WRAPANDMIP ), + VE( D3DUSAGE_WRITEONLY ), + VE( D3DUSAGE_SOFTWAREPROCESSING ), + VE( D3DUSAGE_DONOTCLIP ), + VE( D3DUSAGE_POINTS ), + VE( D3DUSAGE_RTPATCHES ), + VE( D3DUSAGE_NPATCHES ), + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_d3d_rstates[] = +{ + VE( D3DRS_ZENABLE ), + VE( D3DRS_FILLMODE ), + VE( D3DRS_SHADEMODE ), + VE( D3DRS_ZWRITEENABLE ), + VE( D3DRS_ALPHATESTENABLE ), + VE( D3DRS_LASTPIXEL ), + VE( D3DRS_SRCBLEND ), + VE( D3DRS_DESTBLEND ), + VE( D3DRS_CULLMODE ), + VE( D3DRS_ZFUNC ), + VE( D3DRS_ALPHAREF ), + VE( D3DRS_ALPHAFUNC ), + VE( D3DRS_DITHERENABLE ), + VE( D3DRS_ALPHABLENDENABLE ), + VE( D3DRS_FOGENABLE ), + VE( D3DRS_SPECULARENABLE ), + VE( D3DRS_FOGCOLOR ), + VE( D3DRS_FOGTABLEMODE ), + VE( D3DRS_FOGSTART ), + VE( D3DRS_FOGEND ), + VE( D3DRS_FOGDENSITY ), + VE( D3DRS_RANGEFOGENABLE ), + VE( D3DRS_STENCILENABLE ), + VE( D3DRS_STENCILFAIL ), + VE( D3DRS_STENCILZFAIL ), + VE( D3DRS_STENCILPASS ), + VE( D3DRS_STENCILFUNC ), + VE( D3DRS_STENCILREF ), + VE( D3DRS_STENCILMASK ), + VE( D3DRS_STENCILWRITEMASK ), + VE( D3DRS_TEXTUREFACTOR ), + VE( D3DRS_WRAP0 ), + VE( D3DRS_WRAP1 ), + VE( D3DRS_WRAP2 ), + VE( D3DRS_WRAP3 ), + VE( D3DRS_WRAP4 ), + VE( D3DRS_WRAP5 ), + VE( D3DRS_WRAP6 ), + VE( D3DRS_WRAP7 ), + VE( D3DRS_CLIPPING ), + VE( D3DRS_LIGHTING ), + VE( D3DRS_AMBIENT ), + VE( D3DRS_FOGVERTEXMODE ), + VE( D3DRS_COLORVERTEX ), + VE( D3DRS_LOCALVIEWER ), + VE( D3DRS_NORMALIZENORMALS ), + VE( D3DRS_DIFFUSEMATERIALSOURCE ), + VE( D3DRS_SPECULARMATERIALSOURCE ), + VE( D3DRS_AMBIENTMATERIALSOURCE ), + VE( D3DRS_EMISSIVEMATERIALSOURCE ), + VE( D3DRS_VERTEXBLEND ), + VE( D3DRS_CLIPPLANEENABLE ), + VE( D3DRS_POINTSIZE ), + VE( D3DRS_POINTSIZE_MIN ), + VE( D3DRS_POINTSPRITEENABLE ), + VE( D3DRS_POINTSCALEENABLE ), + VE( D3DRS_POINTSCALE_A ), + VE( D3DRS_POINTSCALE_B ), + VE( D3DRS_POINTSCALE_C ), + VE( D3DRS_MULTISAMPLEANTIALIAS ), + VE( D3DRS_MULTISAMPLEMASK ), + VE( D3DRS_PATCHEDGESTYLE ), + VE( D3DRS_DEBUGMONITORTOKEN ), + VE( D3DRS_POINTSIZE_MAX ), + VE( D3DRS_INDEXEDVERTEXBLENDENABLE ), + VE( D3DRS_COLORWRITEENABLE ), + VE( D3DRS_TWEENFACTOR ), + VE( D3DRS_BLENDOP ), + VE( D3DRS_POSITIONDEGREE ), + VE( D3DRS_NORMALDEGREE ), + VE( D3DRS_SCISSORTESTENABLE ), + VE( D3DRS_SLOPESCALEDEPTHBIAS ), + VE( D3DRS_ANTIALIASEDLINEENABLE ), + VE( D3DRS_MINTESSELLATIONLEVEL ), + VE( D3DRS_MAXTESSELLATIONLEVEL ), + VE( D3DRS_ADAPTIVETESS_X ), + VE( D3DRS_ADAPTIVETESS_Y ), + VE( D3DRS_ADAPTIVETESS_Z ), + VE( D3DRS_ADAPTIVETESS_W ), + VE( D3DRS_ENABLEADAPTIVETESSELLATION ), + VE( D3DRS_TWOSIDEDSTENCILMODE ), + VE( D3DRS_CCW_STENCILFAIL ), + VE( D3DRS_CCW_STENCILZFAIL ), + VE( D3DRS_CCW_STENCILPASS ), + VE( D3DRS_CCW_STENCILFUNC ), + VE( D3DRS_COLORWRITEENABLE1 ), + VE( D3DRS_COLORWRITEENABLE2 ), + VE( D3DRS_COLORWRITEENABLE3 ), + VE( D3DRS_BLENDFACTOR ), + VE( D3DRS_SRGBWRITEENABLE ), + VE( D3DRS_DEPTHBIAS ), + VE( D3DRS_WRAP8 ), + VE( D3DRS_WRAP9 ), + VE( D3DRS_WRAP10 ), + VE( D3DRS_WRAP11 ), + VE( D3DRS_WRAP12 ), + VE( D3DRS_WRAP13 ), + VE( D3DRS_WRAP14 ), + VE( D3DRS_WRAP15 ), + VE( D3DRS_SEPARATEALPHABLENDENABLE ), + VE( D3DRS_SRCBLENDALPHA ), + VE( D3DRS_DESTBLENDALPHA ), + VE( D3DRS_BLENDOPALPHA ), + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_d3d_opcodes[] = +{ + VE( D3DSIO_NOP ), + VE( D3DSIO_PHASE ), + VE( D3DSIO_RET ), + VE( D3DSIO_ELSE ), + VE( D3DSIO_ENDIF ), + VE( D3DSIO_ENDLOOP ), + VE( D3DSIO_ENDREP ), + VE( D3DSIO_BREAK ), + VE( D3DSIO_TEXDEPTH ), + VE( D3DSIO_TEXKILL ), + VE( D3DSIO_BEM ), + VE( D3DSIO_TEXBEM ), + VE( D3DSIO_TEXBEML ), + VE( D3DSIO_TEXDP3 ), + VE( D3DSIO_TEXDP3TEX ), + VE( D3DSIO_TEXM3x2DEPTH ), + VE( D3DSIO_TEXM3x2TEX ), + VE( D3DSIO_TEXM3x3 ), + VE( D3DSIO_TEXM3x3PAD ), + VE( D3DSIO_TEXM3x3TEX ), + VE( D3DSIO_TEXM3x3VSPEC ), + VE( D3DSIO_TEXREG2AR ), + VE( D3DSIO_TEXREG2GB ), + VE( D3DSIO_TEXREG2RGB ), + VE( D3DSIO_LABEL ), + VE( D3DSIO_CALL ), + VE( D3DSIO_IF ), + VE( D3DSIO_LOOP ), + VE( D3DSIO_REP ), + VE( D3DSIO_BREAKP ), + VE( D3DSIO_DSX ), + VE( D3DSIO_DSY ), + VE( D3DSIO_NRM ), + VE( D3DSIO_MOVA ), + VE( D3DSIO_MOV ), + VE( D3DSIO_RCP ), + VE( D3DSIO_RSQ ), + VE( D3DSIO_EXP ), + VE( D3DSIO_EXPP ), + VE( D3DSIO_LOG ), + VE( D3DSIO_LOGP ), + VE( D3DSIO_FRC ), + VE( D3DSIO_LIT ), + VE( D3DSIO_ABS ), + VE( D3DSIO_TEXM3x3SPEC ), + VE( D3DSIO_M4x4 ), + VE( D3DSIO_M4x3 ), + VE( D3DSIO_M3x4 ), + VE( D3DSIO_M3x3 ), + VE( D3DSIO_M3x2 ), + VE( D3DSIO_CALLNZ ), + VE( D3DSIO_IFC ), + VE( D3DSIO_BREAKC ), + VE( D3DSIO_SETP ), + VE( D3DSIO_TEXLDL ), + VE( D3DSIO_ADD ), + VE( D3DSIO_SUB ), + VE( D3DSIO_MUL ), + VE( D3DSIO_DP3 ), + VE( D3DSIO_DP4 ), + VE( D3DSIO_MIN ), + VE( D3DSIO_MAX ), + VE( D3DSIO_DST ), + VE( D3DSIO_SLT ), + VE( D3DSIO_SGE ), + VE( D3DSIO_CRS ), + VE( D3DSIO_POW ), + VE( D3DSIO_DP2ADD ), + VE( D3DSIO_LRP ), + VE( D3DSIO_SGN ), + VE( D3DSIO_CND ), + VE( D3DSIO_CMP ), + VE( D3DSIO_SINCOS ), + VE( D3DSIO_MAD ), + VE( D3DSIO_TEXLDD ), + VE( D3DSIO_TEXCOORD ), + VE( D3DSIO_TEX ), + VE( D3DSIO_DCL ), + VE( D3DSTT_UNKNOWN ), + VE( D3DSTT_2D ), + VE( D3DSTT_CUBE ), + VE( D3DSTT_VOLUME ), + VE( D3DSIO_DEFB ), + VE( D3DSIO_DEFI ), + VE( D3DSIO_DEF ), + VE( D3DSIO_COMMENT ), + VE( D3DSIO_END ), +}; + + +const GLMValueEntry_t g_d3d_vtxdeclusages[] = +{ + { D3DDECLUSAGE_POSITION ,"POSN" }, // P + { D3DDECLUSAGE_BLENDWEIGHT ,"BLWT" }, // W + { D3DDECLUSAGE_BLENDINDICES ,"BLIX" }, // I + { D3DDECLUSAGE_NORMAL ,"NORM" }, // N + { D3DDECLUSAGE_PSIZE ,"PSIZ" }, // S + { D3DDECLUSAGE_TEXCOORD ,"TEXC" }, // T + { D3DDECLUSAGE_TANGENT ,"TANG" }, // G + { D3DDECLUSAGE_BINORMAL ,"BINO" }, // B + { D3DDECLUSAGE_TESSFACTOR ,"TESS" }, // S + { D3DDECLUSAGE_PLUGH ,"????" }, // ? + { D3DDECLUSAGE_COLOR ,"COLR" }, // C + { D3DDECLUSAGE_FOG ,"FOG " }, // F + { D3DDECLUSAGE_DEPTH ,"DEPT" }, // D + { D3DDECLUSAGE_SAMPLE ,"SAMP" } // M +}; + +const GLMValueEntry_t g_d3d_vtxdeclusages_short[] = +{ + { D3DDECLUSAGE_POSITION ,"P" }, + { D3DDECLUSAGE_BLENDWEIGHT ,"W" }, + { D3DDECLUSAGE_BLENDINDICES ,"I" }, + { D3DDECLUSAGE_NORMAL ,"N" }, + { D3DDECLUSAGE_PSIZE ,"S" }, + { D3DDECLUSAGE_TEXCOORD ,"T" }, + { D3DDECLUSAGE_TANGENT ,"G" }, + { D3DDECLUSAGE_BINORMAL ,"B" }, + { D3DDECLUSAGE_TESSFACTOR ,"S" }, + { D3DDECLUSAGE_PLUGH ,"?" }, + { D3DDECLUSAGE_COLOR ,"C" }, + { D3DDECLUSAGE_FOG ,"F" }, + { D3DDECLUSAGE_DEPTH ,"D" }, + { D3DDECLUSAGE_SAMPLE ,"M" } +}; + +const GLMValueEntry_t g_cgl_rendids[] = // need to mask with 0xFFFFFF00 to match on these (ex: 8800GT == 0x00022608 +{ +#ifdef OSX + VE( kCGLRendererGenericID ), + VE( kCGLRendererGenericFloatID ), + VE( kCGLRendererAppleSWID ), + VE( kCGLRendererATIRage128ID ), + VE( kCGLRendererATIRadeonID ), + VE( kCGLRendererATIRageProID ), + VE( kCGLRendererATIRadeon8500ID ), + VE( kCGLRendererATIRadeon9700ID ), + VE( kCGLRendererATIRadeonX1000ID ), + VE( kCGLRendererATIRadeonX2000ID ), + VE( kCGLRendererGeForce2MXID ), + VE( kCGLRendererGeForce3ID ), + VE( kCGLRendererGeForceFXID ), // also for GF6 and GF7 + VE( kCGLRendererGeForce8xxxID ), + VE( kCGLRendererVTBladeXP2ID ), + VE( kCGLRendererIntel900ID ), + VE( kCGLRendererMesa3DFXID ), +#endif + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_gl_errors[] = +{ + VE( GL_INVALID_ENUM ), + VE( GL_INVALID_VALUE ), + VE( GL_INVALID_OPERATION ), + VE( GL_STACK_OVERFLOW ), + VE( GL_STACK_UNDERFLOW ), + VE( GL_OUT_OF_MEMORY ), + VE( GL_INVALID_FRAMEBUFFER_OPERATION_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT ), + VE( GL_FRAMEBUFFER_UNSUPPORTED_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT ) +}; + +// there are some ARB/EXT dupes in this table but that doesn't matter too much +const GLMValueEntry_t g_gl_enums[] = +{ + { 0x0000, "GL_ZERO" }, + { 0x0001, "GL_ONE" }, + { 0x0004, "GL_TRIANGLES" }, + { 0x0005, "GL_TRIANGLE_STRIP" }, + { 0x0006, "GL_TRIANGLE_FAN" }, + { 0x0007, "GL_QUADS" }, + { 0x0008, "GL_QUAD_STRIP" }, + { 0x0009, "GL_POLYGON" }, + { 0x0200, "GL_NEVER" }, + { 0x0201, "GL_LESS" }, + { 0x0202, "GL_EQUAL" }, + { 0x0203, "GL_LEQUAL" }, + { 0x0204, "GL_GREATER" }, + { 0x0205, "GL_NOTEQUAL" }, + { 0x0206, "GL_GEQUAL" }, + { 0x0207, "GL_ALWAYS" }, + { 0x0300, "GL_SRC_COLOR" }, + { 0x0301, "GL_ONE_MINUS_SRC_COLOR" }, + { 0x0302, "GL_SRC_ALPHA" }, + { 0x0303, "GL_ONE_MINUS_SRC_ALPHA" }, + { 0x0304, "GL_DST_ALPHA" }, + { 0x0305, "GL_ONE_MINUS_DST_ALPHA" }, + { 0x0306, "GL_DST_COLOR" }, + { 0x0307, "GL_ONE_MINUS_DST_COLOR" }, + { 0x0308, "GL_SRC_ALPHA_SATURATE" }, + { 0x0400, "GL_FRONT_LEFT" }, + { 0x0401, "GL_FRONT_RIGHT" }, + { 0x0402, "GL_BACK_LEFT" }, + { 0x0403, "GL_BACK_RIGHT" }, + { 0x0404, "GL_FRONT" }, + { 0x0405, "GL_BACK" }, + { 0x0406, "GL_LEFT" }, + { 0x0407, "GL_RIGHT" }, + { 0x0408, "GL_FRONT_AND_BACK" }, + { 0x0409, "GL_AUX0" }, + { 0x040A, "GL_AUX1" }, + { 0x040B, "GL_AUX2" }, + { 0x040C, "GL_AUX3" }, + { 0x0500, "GL_INVALID_ENUM" }, + { 0x0501, "GL_INVALID_VALUE" }, + { 0x0502, "GL_INVALID_OPERATION" }, + { 0x0503, "GL_STACK_OVERFLOW" }, + { 0x0504, "GL_STACK_UNDERFLOW" }, + { 0x0505, "GL_OUT_OF_MEMORY" }, + { 0x0506, "GL_INVALID_FRAMEBUFFER_OPERATION" }, + { 0x0600, "GL_2D" }, + { 0x0601, "GL_3D" }, + { 0x0602, "GL_3D_COLOR" }, + { 0x0603, "GL_3D_COLOR_TEXTURE" }, + { 0x0604, "GL_4D_COLOR_TEXTURE" }, + { 0x0700, "GL_PASS_THROUGH_TOKEN" }, + { 0x0701, "GL_POINT_TOKEN" }, + { 0x0702, "GL_LINE_TOKEN" }, + { 0x0703, "GL_POLYGON_TOKEN" }, + { 0x0704, "GL_BITMAP_TOKEN" }, + { 0x0705, "GL_DRAW_PIXEL_TOKEN" }, + { 0x0706, "GL_COPY_PIXEL_TOKEN" }, + { 0x0707, "GL_LINE_RESET_TOKEN" }, + { 0x0800, "GL_EXP" }, + { 0x0801, "GL_EXP2" }, + { 0x0900, "GL_CW" }, + { 0x0901, "GL_CCW" }, + { 0x0A00, "GL_COEFF" }, + { 0x0A01, "GL_ORDER" }, + { 0x0A02, "GL_DOMAIN" }, + { 0x0B00, "GL_CURRENT_COLOR" }, + { 0x0B01, "GL_CURRENT_INDEX" }, + { 0x0B02, "GL_CURRENT_NORMAL" }, + { 0x0B03, "GL_CURRENT_TEXTURE_COORDS" }, + { 0x0B04, "GL_CURRENT_RASTER_COLOR" }, + { 0x0B05, "GL_CURRENT_RASTER_INDEX" }, + { 0x0B06, "GL_CURRENT_RASTER_TEXTURE_COORDS" }, + { 0x0B07, "GL_CURRENT_RASTER_POSITION" }, + { 0x0B08, "GL_CURRENT_RASTER_POSITION_VALID" }, + { 0x0B09, "GL_CURRENT_RASTER_DISTANCE" }, + { 0x0B10, "GL_POINT_SMOOTH" }, + { 0x0B11, "GL_POINT_SIZE" }, + { 0x0B12, "GL_POINT_SIZE_RANGE" }, + { 0x0B12, "GL_SMOOTH_POINT_SIZE_RANGE" }, + { 0x0B13, "GL_POINT_SIZE_GRANULARITY" }, + { 0x0B13, "GL_SMOOTH_POINT_SIZE_GRANULARITY" }, + { 0x0B20, "GL_LINE_SMOOTH" }, + { 0x0B21, "GL_LINE_WIDTH" }, + { 0x0B22, "GL_LINE_WIDTH_RANGE" }, + { 0x0B22, "GL_SMOOTH_LINE_WIDTH_RANGE" }, + { 0x0B23, "GL_LINE_WIDTH_GRANULARITY" }, + { 0x0B23, "GL_SMOOTH_LINE_WIDTH_GRANULARITY" }, + { 0x0B24, "GL_LINE_STIPPLE" }, + { 0x0B25, "GL_LINE_STIPPLE_PATTERN" }, + { 0x0B26, "GL_LINE_STIPPLE_REPEAT" }, + { 0x0B30, "GL_LIST_MODE" }, + { 0x0B31, "GL_MAX_LIST_NESTING" }, + { 0x0B32, "GL_LIST_BASE" }, + { 0x0B33, "GL_LIST_INDEX" }, + { 0x0B40, "GL_POLYGON_MODE" }, + { 0x0B41, "GL_POLYGON_SMOOTH" }, + { 0x0B42, "GL_POLYGON_STIPPLE" }, + { 0x0B43, "GL_EDGE_FLAG" }, + { 0x0B44, "GL_CULL_FACE" }, + { 0x0B45, "GL_CULL_FACE_MODE" }, + { 0x0B46, "GL_FRONT_FACE" }, + { 0x0B50, "GL_LIGHTING" }, + { 0x0B51, "GL_LIGHT_MODEL_LOCAL_VIEWER" }, + { 0x0B52, "GL_LIGHT_MODEL_TWO_SIDE" }, + { 0x0B53, "GL_LIGHT_MODEL_AMBIENT" }, + { 0x0B54, "GL_SHADE_MODEL" }, + { 0x0B55, "GL_COLOR_MATERIAL_FACE" }, + { 0x0B56, "GL_COLOR_MATERIAL_PARAMETER" }, + { 0x0B57, "GL_COLOR_MATERIAL" }, + { 0x0B60, "GL_FOG" }, + { 0x0B61, "GL_FOG_INDEX" }, + { 0x0B62, "GL_FOG_DENSITY" }, + { 0x0B63, "GL_FOG_START" }, + { 0x0B64, "GL_FOG_END" }, + { 0x0B65, "GL_FOG_MODE" }, + { 0x0B66, "GL_FOG_COLOR" }, + { 0x0B70, "GL_DEPTH_RANGE" }, + { 0x0B71, "GL_DEPTH_TEST" }, + { 0x0B72, "GL_DEPTH_WRITEMASK" }, + { 0x0B73, "GL_DEPTH_CLEAR_VALUE" }, + { 0x0B74, "GL_DEPTH_FUNC" }, + { 0x0B80, "GL_ACCUM_CLEAR_VALUE" }, + { 0x0B90, "GL_STENCIL_TEST" }, + { 0x0B91, "GL_STENCIL_CLEAR_VALUE" }, + { 0x0B92, "GL_STENCIL_FUNC" }, + { 0x0B93, "GL_STENCIL_VALUE_MASK" }, + { 0x0B94, "GL_STENCIL_FAIL" }, + { 0x0B95, "GL_STENCIL_PASS_DEPTH_FAIL" }, + { 0x0B96, "GL_STENCIL_PASS_DEPTH_PASS" }, + { 0x0B97, "GL_STENCIL_REF" }, + { 0x0B98, "GL_STENCIL_WRITEMASK" }, + { 0x0BA0, "GL_MATRIX_MODE" }, + { 0x0BA1, "GL_NORMALIZE" }, + { 0x0BA2, "GL_VIEWPORT" }, + { 0x0BA3, "GL_MODELVIEW_STACK_DEPTH" }, + { 0x0BA4, "GL_PROJECTION_STACK_DEPTH" }, + { 0x0BA5, "GL_TEXTURE_STACK_DEPTH" }, + { 0x0BA6, "GL_MODELVIEW_MATRIX" }, + { 0x0BA7, "GL_PROJECTION_MATRIX" }, + { 0x0BA8, "GL_TEXTURE_MATRIX" }, + { 0x0BB0, "GL_ATTRIB_STACK_DEPTH" }, + { 0x0BB1, "GL_CLIENT_ATTRIB_STACK_DEPTH" }, + { 0x0BC0, "GL_ALPHA_TEST" }, + { 0x0BC1, "GL_ALPHA_TEST_FUNC" }, + { 0x0BC2, "GL_ALPHA_TEST_REF" }, + { 0x0BD0, "GL_DITHER" }, + { 0x0BE0, "GL_BLEND_DST" }, + { 0x0BE1, "GL_BLEND_SRC" }, + { 0x0BE2, "GL_BLEND" }, + { 0x0BF0, "GL_LOGIC_OP_MODE" }, + { 0x0BF1, "GL_INDEX_LOGIC_OP" }, + { 0x0BF2, "GL_COLOR_LOGIC_OP" }, + { 0x0C00, "GL_AUX_BUFFERS" }, + { 0x0C01, "GL_DRAW_BUFFER" }, + { 0x0C02, "GL_READ_BUFFER" }, + { 0x0C10, "GL_SCISSOR_BOX" }, + { 0x0C11, "GL_SCISSOR_TEST" }, + { 0x0C20, "GL_INDEX_CLEAR_VALUE" }, + { 0x0C21, "GL_INDEX_WRITEMASK" }, + { 0x0C22, "GL_COLOR_CLEAR_VALUE" }, + { 0x0C23, "GL_COLOR_WRITEMASK" }, + { 0x0C30, "GL_INDEX_MODE" }, + { 0x0C31, "GL_RGBA_MODE" }, + { 0x0C32, "GL_DOUBLEBUFFER" }, + { 0x0C33, "GL_STEREO" }, + { 0x0C40, "GL_RENDER_MODE" }, + { 0x0C50, "GL_PERSPECTIVE_CORRECTION_HINT" }, + { 0x0C51, "GL_POINT_SMOOTH_HINT" }, + { 0x0C52, "GL_LINE_SMOOTH_HINT" }, + { 0x0C53, "GL_POLYGON_SMOOTH_HINT" }, + { 0x0C54, "GL_FOG_HINT" }, + { 0x0C60, "GL_TEXTURE_GEN_S" }, + { 0x0C61, "GL_TEXTURE_GEN_T" }, + { 0x0C62, "GL_TEXTURE_GEN_R" }, + { 0x0C63, "GL_TEXTURE_GEN_Q" }, + { 0x0C70, "GL_PIXEL_MAP_I_TO_I" }, + { 0x0C71, "GL_PIXEL_MAP_S_TO_S" }, + { 0x0C72, "GL_PIXEL_MAP_I_TO_R" }, + { 0x0C73, "GL_PIXEL_MAP_I_TO_G" }, + { 0x0C74, "GL_PIXEL_MAP_I_TO_B" }, + { 0x0C75, "GL_PIXEL_MAP_I_TO_A" }, + { 0x0C76, "GL_PIXEL_MAP_R_TO_R" }, + { 0x0C77, "GL_PIXEL_MAP_G_TO_G" }, + { 0x0C78, "GL_PIXEL_MAP_B_TO_B" }, + { 0x0C79, "GL_PIXEL_MAP_A_TO_A" }, + { 0x0CB0, "GL_PIXEL_MAP_I_TO_I_SIZE" }, + { 0x0CB1, "GL_PIXEL_MAP_S_TO_S_SIZE" }, + { 0x0CB2, "GL_PIXEL_MAP_I_TO_R_SIZE" }, + { 0x0CB3, "GL_PIXEL_MAP_I_TO_G_SIZE" }, + { 0x0CB4, "GL_PIXEL_MAP_I_TO_B_SIZE" }, + { 0x0CB5, "GL_PIXEL_MAP_I_TO_A_SIZE" }, + { 0x0CB6, "GL_PIXEL_MAP_R_TO_R_SIZE" }, + { 0x0CB7, "GL_PIXEL_MAP_G_TO_G_SIZE" }, + { 0x0CB8, "GL_PIXEL_MAP_B_TO_B_SIZE" }, + { 0x0CB9, "GL_PIXEL_MAP_A_TO_A_SIZE" }, + { 0x0CF0, "GL_UNPACK_SWAP_BYTES" }, + { 0x0CF1, "GL_UNPACK_LSB_FIRST" }, + { 0x0CF2, "GL_UNPACK_ROW_LENGTH" }, + { 0x0CF3, "GL_UNPACK_SKIP_ROWS" }, + { 0x0CF4, "GL_UNPACK_SKIP_PIXELS" }, + { 0x0CF5, "GL_UNPACK_ALIGNMENT" }, + { 0x0D00, "GL_PACK_SWAP_BYTES" }, + { 0x0D01, "GL_PACK_LSB_FIRST" }, + { 0x0D02, "GL_PACK_ROW_LENGTH" }, + { 0x0D03, "GL_PACK_SKIP_ROWS" }, + { 0x0D04, "GL_PACK_SKIP_PIXELS" }, + { 0x0D05, "GL_PACK_ALIGNMENT" }, + { 0x0D10, "GL_MAP_COLOR" }, + { 0x0D11, "GL_MAP_STENCIL" }, + { 0x0D12, "GL_INDEX_SHIFT" }, + { 0x0D13, "GL_INDEX_OFFSET" }, + { 0x0D14, "GL_RED_SCALE" }, + { 0x0D15, "GL_RED_BIAS" }, + { 0x0D16, "GL_ZOOM_X" }, + { 0x0D17, "GL_ZOOM_Y" }, + { 0x0D18, "GL_GREEN_SCALE" }, + { 0x0D19, "GL_GREEN_BIAS" }, + { 0x0D1A, "GL_BLUE_SCALE" }, + { 0x0D1B, "GL_BLUE_BIAS" }, + { 0x0D1C, "GL_ALPHA_SCALE" }, + { 0x0D1D, "GL_ALPHA_BIAS" }, + { 0x0D1E, "GL_DEPTH_SCALE" }, + { 0x0D1F, "GL_DEPTH_BIAS" }, + { 0x0D30, "GL_MAX_EVAL_ORDER" }, + { 0x0D31, "GL_MAX_LIGHTS" }, + { 0x0D32, "GL_MAX_CLIP_PLANES" }, + { 0x0D33, "GL_MAX_TEXTURE_SIZE" }, + { 0x0D34, "GL_MAX_PIXEL_MAP_TABLE" }, + { 0x0D35, "GL_MAX_ATTRIB_STACK_DEPTH" }, + { 0x0D36, "GL_MAX_MODELVIEW_STACK_DEPTH" }, + { 0x0D37, "GL_MAX_NAME_STACK_DEPTH" }, + { 0x0D38, "GL_MAX_PROJECTION_STACK_DEPTH" }, + { 0x0D39, "GL_MAX_TEXTURE_STACK_DEPTH" }, + { 0x0D3A, "GL_MAX_VIEWPORT_DIMS" }, + { 0x0D3B, "GL_MAX_CLIENT_ATTRIB_STACK_DEPTH" }, + { 0x0D50, "GL_SUBPIXEL_BITS" }, + { 0x0D51, "GL_INDEX_BITS" }, + { 0x0D52, "GL_RED_BITS" }, + { 0x0D53, "GL_GREEN_BITS" }, + { 0x0D54, "GL_BLUE_BITS" }, + { 0x0D55, "GL_ALPHA_BITS" }, + { 0x0D56, "GL_DEPTH_BITS" }, + { 0x0D57, "GL_STENCIL_BITS" }, + { 0x0D58, "GL_ACCUM_RED_BITS" }, + { 0x0D59, "GL_ACCUM_GREEN_BITS" }, + { 0x0D5A, "GL_ACCUM_BLUE_BITS" }, + { 0x0D5B, "GL_ACCUM_ALPHA_BITS" }, + { 0x0D70, "GL_NAME_STACK_DEPTH" }, + { 0x0D80, "GL_AUTO_NORMAL" }, + { 0x0D90, "GL_MAP1_COLOR_4" }, + { 0x0D91, "GL_MAP1_INDEX" }, + { 0x0D92, "GL_MAP1_NORMAL" }, + { 0x0D93, "GL_MAP1_TEXTURE_COORD_1" }, + { 0x0D94, "GL_MAP1_TEXTURE_COORD_2" }, + { 0x0D95, "GL_MAP1_TEXTURE_COORD_3" }, + { 0x0D96, "GL_MAP1_TEXTURE_COORD_4" }, + { 0x0D97, "GL_MAP1_VERTEX_3" }, + { 0x0D98, "GL_MAP1_VERTEX_4" }, + { 0x0DB0, "GL_MAP2_COLOR_4" }, + { 0x0DB1, "GL_MAP2_INDEX" }, + { 0x0DB2, "GL_MAP2_NORMAL" }, + { 0x0DB3, "GL_MAP2_TEXTURE_COORD_1" }, + { 0x0DB4, "GL_MAP2_TEXTURE_COORD_2" }, + { 0x0DB5, "GL_MAP2_TEXTURE_COORD_3" }, + { 0x0DB6, "GL_MAP2_TEXTURE_COORD_4" }, + { 0x0DB7, "GL_MAP2_VERTEX_3" }, + { 0x0DB8, "GL_MAP2_VERTEX_4" }, + { 0x0DD0, "GL_MAP1_GRID_DOMAIN" }, + { 0x0DD1, "GL_MAP1_GRID_SEGMENTS" }, + { 0x0DD2, "GL_MAP2_GRID_DOMAIN" }, + { 0x0DD3, "GL_MAP2_GRID_SEGMENTS" }, + { 0x0DE0, "GL_TEXTURE_1D" }, + { 0x0DE1, "GL_TEXTURE_2D" }, + { 0x0DF0, "GL_FEEDBACK_BUFFER_POINTER" }, + { 0x0DF1, "GL_FEEDBACK_BUFFER_SIZE" }, + { 0x0DF2, "GL_FEEDBACK_BUFFER_TYPE" }, + { 0x0DF3, "GL_SELECTION_BUFFER_POINTER" }, + { 0x0DF4, "GL_SELECTION_BUFFER_SIZE" }, + { 0x1000, "GL_TEXTURE_WIDTH" }, + { 0x1001, "GL_TEXTURE_HEIGHT" }, + { 0x1003, "GL_TEXTURE_INTERNAL_FORMAT" }, + { 0x1004, "GL_TEXTURE_BORDER_COLOR" }, + { 0x1005, "GL_TEXTURE_BORDER" }, + { 0x1100, "GL_DONT_CARE" }, + { 0x1101, "GL_FASTEST" }, + { 0x1102, "GL_NICEST" }, + { 0x1200, "GL_AMBIENT" }, + { 0x1201, "GL_DIFFUSE" }, + { 0x1202, "GL_SPECULAR" }, + { 0x1203, "GL_POSITION" }, + { 0x1204, "GL_SPOT_DIRECTION" }, + { 0x1205, "GL_SPOT_EXPONENT" }, + { 0x1206, "GL_SPOT_CUTOFF" }, + { 0x1207, "GL_CONSTANT_ATTENUATION" }, + { 0x1208, "GL_LINEAR_ATTENUATION" }, + { 0x1209, "GL_QUADRATIC_ATTENUATION" }, + { 0x1300, "GL_COMPILE" }, + { 0x1301, "GL_COMPILE_AND_EXECUTE" }, + { 0x1400, "GL_BYTE " }, + { 0x1401, "GL_UBYTE" }, + { 0x1402, "GL_SHORT" }, + { 0x1403, "GL_USHRT" }, + { 0x1404, "GL_INT " }, + { 0x1405, "GL_UINT " }, + { 0x1406, "GL_FLOAT" }, + { 0x1407, "GL_2_BYTES" }, + { 0x1408, "GL_3_BYTES" }, + { 0x1409, "GL_4_BYTES" }, + { 0x140A, "GL_DOUBLE" }, + { 0x140B, "GL_HALF_FLOAT" }, + { 0x1500, "GL_CLEAR" }, + { 0x1501, "GL_AND" }, + { 0x1502, "GL_AND_REVERSE" }, + { 0x1503, "GL_COPY" }, + { 0x1504, "GL_AND_INVERTED" }, + { 0x1505, "GL_NOOP" }, + { 0x1506, "GL_XOR" }, + { 0x1507, "GL_OR" }, + { 0x1508, "GL_NOR" }, + { 0x1509, "GL_EQUIV" }, + { 0x150A, "GL_INVERT" }, + { 0x150B, "GL_OR_REVERSE" }, + { 0x150C, "GL_COPY_INVERTED" }, + { 0x150D, "GL_OR_INVERTED" }, + { 0x150E, "GL_NAND" }, + { 0x150F, "GL_SET" }, + { 0x1600, "GL_EMISSION" }, + { 0x1601, "GL_SHININESS" }, + { 0x1602, "GL_AMBIENT_AND_DIFFUSE" }, + { 0x1603, "GL_COLOR_INDEXES" }, + { 0x1700, "GL_MODELVIEW" }, + { 0x1700, "GL_MODELVIEW0_ARB" }, + { 0x1701, "GL_PROJECTION" }, + { 0x1702, "GL_TEXTURE" }, + { 0x1800, "GL_COLOR" }, + { 0x1801, "GL_DEPTH" }, + { 0x1802, "GL_STENCIL" }, + { 0x1900, "GL_COLOR_INDEX" }, + { 0x1901, "GL_STENCIL_INDEX" }, + { 0x1902, "GL_DEPTH_COMPONENT" }, + { 0x1903, "GL_RED" }, + { 0x1904, "GL_GREEN" }, + { 0x1905, "GL_BLUE" }, + { 0x1906, "GL_ALPHA" }, + { 0x1907, "GL_RGB" }, + { 0x1908, "GL_RGBA" }, + { 0x1909, "GL_LUMINANCE" }, + { 0x190A, "GL_LUMINANCE_ALPHA" }, + { 0x1A00, "GL_BITMAP" }, + { 0x1B00, "GL_POINT" }, + { 0x1B01, "GL_LINE" }, + { 0x1B02, "GL_FILL" }, + { 0x1C00, "GL_RENDER" }, + { 0x1C01, "GL_FEEDBACK" }, + { 0x1C02, "GL_SELECT" }, + { 0x1D00, "GL_FLAT" }, + { 0x1D01, "GL_SMOOTH" }, + { 0x1E00, "GL_KEEP" }, + { 0x1E01, "GL_REPLACE" }, + { 0x1E02, "GL_INCR" }, + { 0x1E03, "GL_DECR" }, + { 0x1F00, "GL_VENDOR" }, + { 0x1F01, "GL_RENDERER" }, + { 0x1F02, "GL_VERSION" }, + { 0x1F03, "GL_EXTENSIONS" }, + { 0x2000, "GL_S" }, + { 0x2001, "GL_T" }, + { 0x2002, "GL_R" }, + { 0x2003, "GL_Q" }, + { 0x2100, "GL_MODULATE" }, + { 0x2101, "GL_DECAL" }, + { 0x2200, "GL_TEXTURE_ENV_MODE" }, + { 0x2201, "GL_TEXTURE_ENV_COLOR" }, + { 0x2300, "GL_TEXTURE_ENV" }, + { 0x2400, "GL_EYE_LINEAR" }, + { 0x2401, "GL_OBJECT_LINEAR" }, + { 0x2402, "GL_SPHERE_MAP" }, + { 0x2500, "GL_TEXTURE_GEN_MODE" }, + { 0x2501, "GL_OBJECT_PLANE" }, + { 0x2502, "GL_EYE_PLANE" }, + { 0x2600, "GL_NEAREST" }, + { 0x2601, "GL_LINEAR" }, + { 0x2700, "GL_NEAREST_MIPMAP_NEAREST" }, + { 0x2701, "GL_LINEAR_MIPMAP_NEAREST" }, + { 0x2702, "GL_NEAREST_MIPMAP_LINEAR" }, + { 0x2703, "GL_LINEAR_MIPMAP_LINEAR" }, + { 0x2800, "GL_TEXTURE_MAG_FILTER" }, + { 0x2801, "GL_TEXTURE_MIN_FILTER" }, + { 0x2802, "GL_TEXTURE_WRAP_S" }, + { 0x2803, "GL_TEXTURE_WRAP_T" }, + { 0x2900, "GL_CLAMP" }, + { 0x2901, "GL_REPEAT" }, + { 0x2A00, "GL_POLYGON_OFFSET_UNITS" }, + { 0x2A01, "GL_POLYGON_OFFSET_POINT" }, + { 0x2A02, "GL_POLYGON_OFFSET_LINE" }, + { 0x2A10, "GL_R3_G3_B2" }, + { 0x2A20, "GL_V2F" }, + { 0x2A21, "GL_V3F" }, + { 0x2A22, "GL_C4UB_V2F" }, + { 0x2A23, "GL_C4UB_V3F" }, + { 0x2A24, "GL_C3F_V3F" }, + { 0x2A25, "GL_N3F_V3F" }, + { 0x2A26, "GL_C4F_N3F_V3F" }, + { 0x2A27, "GL_T2F_V3F" }, + { 0x2A28, "GL_T4F_V4F" }, + { 0x2A29, "GL_T2F_C4UB_V3F" }, + { 0x2A2A, "GL_T2F_C3F_V3F" }, + { 0x2A2B, "GL_T2F_N3F_V3F" }, + { 0x2A2C, "GL_T2F_C4F_N3F_V3F" }, + { 0x2A2D, "GL_T4F_C4F_N3F_V4F" }, + { 0x3000, "GL_CLIP_PLANE0" }, + { 0x3001, "GL_CLIP_PLANE1" }, + { 0x3002, "GL_CLIP_PLANE2" }, + { 0x3003, "GL_CLIP_PLANE3" }, + { 0x3004, "GL_CLIP_PLANE4" }, + { 0x3005, "GL_CLIP_PLANE5" }, + { 0x4000, "GL_LIGHT0" }, + { 0x4001, "GL_LIGHT1" }, + { 0x4002, "GL_LIGHT2" }, + { 0x4003, "GL_LIGHT3" }, + { 0x4004, "GL_LIGHT4" }, + { 0x4005, "GL_LIGHT5" }, + { 0x4006, "GL_LIGHT6" }, + { 0x4007, "GL_LIGHT7" }, + { 0x8000, "GL_ABGR_EXT" }, + { 0x8001, "GL_CONSTANT_COLOR" }, + { 0x8002, "GL_ONE_MINUS_CONSTANT_COLOR" }, + { 0x8003, "GL_CONSTANT_ALPHA" }, + { 0x8004, "GL_ONE_MINUS_CONSTANT_ALPHA" }, + { 0x8005, "GL_BLEND_COLOR" }, + { 0x8006, "GL_FUNC_ADD" }, + { 0x8007, "GL_MIN" }, + { 0x8008, "GL_MAX" }, + { 0x8009, "GL_BLEND_EQUATION_RGB" }, + { 0x8009, "GL_BLEND_EQUATION" }, + { 0x800A, "GL_FUNC_SUBTRACT" }, + { 0x800B, "GL_FUNC_REVERSE_SUBTRACT" }, + { 0x8010, "GL_CONVOLUTION_1D" }, + { 0x8011, "GL_CONVOLUTION_2D" }, + { 0x8012, "GL_SEPARABLE_2D" }, + { 0x8013, "GL_CONVOLUTION_BORDER_MODE" }, + { 0x8014, "GL_CONVOLUTION_FILTER_SCALE" }, + { 0x8015, "GL_CONVOLUTION_FILTER_BIAS" }, + { 0x8016, "GL_REDUCE" }, + { 0x8017, "GL_CONVOLUTION_FORMAT" }, + { 0x8018, "GL_CONVOLUTION_WIDTH" }, + { 0x8019, "GL_CONVOLUTION_HEIGHT" }, + { 0x801A, "GL_MAX_CONVOLUTION_WIDTH" }, + { 0x801B, "GL_MAX_CONVOLUTION_HEIGHT" }, + { 0x801C, "GL_POST_CONVOLUTION_RED_SCALE" }, + { 0x801D, "GL_POST_CONVOLUTION_GREEN_SCALE" }, + { 0x801E, "GL_POST_CONVOLUTION_BLUE_SCALE" }, + { 0x801F, "GL_POST_CONVOLUTION_ALPHA_SCALE" }, + { 0x8020, "GL_POST_CONVOLUTION_RED_BIAS" }, + { 0x8021, "GL_POST_CONVOLUTION_GREEN_BIAS" }, + { 0x8022, "GL_POST_CONVOLUTION_BLUE_BIAS" }, + { 0x8023, "GL_POST_CONVOLUTION_ALPHA_BIAS" }, + { 0x8024, "GL_HISTOGRAM" }, + { 0x8025, "GL_PROXY_HISTOGRAM" }, + { 0x8026, "GL_HISTOGRAM_WIDTH" }, + { 0x8027, "GL_HISTOGRAM_FORMAT" }, + { 0x8028, "GL_HISTOGRAM_RED_SIZE" }, + { 0x8029, "GL_HISTOGRAM_GREEN_SIZE" }, + { 0x802A, "GL_HISTOGRAM_BLUE_SIZE" }, + { 0x802B, "GL_HISTOGRAM_ALPHA_SIZE" }, + { 0x802C, "GL_HISTOGRAM_LUMINANCE_SIZE" }, + { 0x802D, "GL_HISTOGRAM_SINK" }, + { 0x802E, "GL_MINMAX" }, + { 0x802F, "GL_MINMAX_FORMAT" }, + { 0x8030, "GL_MINMAX_SINK" }, + { 0x8031, "GL_TABLE_TOO_LARGE" }, + { 0x8032, "GL_UNSIGNED_BYTE_3_3_2" }, + { 0x8033, "GL_UNSIGNED_SHORT_4_4_4_4" }, + { 0x8034, "GL_UNSIGNED_SHORT_5_5_5_1" }, + { 0x8035, "GL_UNSIGNED_INT_8_8_8_8" }, + { 0x8036, "GL_UNSIGNED_INT_10_10_10_2" }, + { 0x8037, "GL_POLYGON_OFFSET_FILL" }, + { 0x8038, "GL_POLYGON_OFFSET_FACTOR" }, + { 0x803A, "GL_RESCALE_NORMAL" }, + { 0x803B, "GL_ALPHA4" }, + { 0x803C, "GL_ALPHA8" }, + { 0x803D, "GL_ALPHA12" }, + { 0x803E, "GL_ALPHA16" }, + { 0x803F, "GL_LUMINANCE4" }, + { 0x8040, "GL_LUMINANCE8" }, + { 0x8041, "GL_LUMINANCE12" }, + { 0x8042, "GL_LUMINANCE16" }, + { 0x8043, "GL_LUMINANCE4_ALPHA4" }, + { 0x8044, "GL_LUMINANCE6_ALPHA2" }, + { 0x8045, "GL_LUMINANCE8_ALPHA8" }, + { 0x8046, "GL_LUMINANCE12_ALPHA4" }, + { 0x8047, "GL_LUMINANCE12_ALPHA12" }, + { 0x8048, "GL_LUMINANCE16_ALPHA16" }, + { 0x8049, "GL_INTENSITY" }, + { 0x804A, "GL_INTENSITY4" }, + { 0x804B, "GL_INTENSITY8" }, + { 0x804C, "GL_INTENSITY12" }, + { 0x804D, "GL_INTENSITY16" }, + { 0x804F, "GL_RGB4" }, + { 0x8050, "GL_RGB5" }, + { 0x8051, "GL_RGB8" }, + { 0x8052, "GL_RGB10" }, + { 0x8053, "GL_RGB12" }, + { 0x8054, "GL_RGB16" }, + { 0x8055, "GL_RGBA2" }, + { 0x8056, "GL_RGBA4" }, + { 0x8057, "GL_RGB5_A1" }, + { 0x8058, "GL_RGBA8" }, + { 0x8059, "GL_RGB10_A2" }, + { 0x805A, "GL_RGBA12" }, + { 0x805B, "GL_RGBA16" }, + { 0x805C, "GL_TEXTURE_RED_SIZE" }, + { 0x805D, "GL_TEXTURE_GREEN_SIZE" }, + { 0x805E, "GL_TEXTURE_BLUE_SIZE" }, + { 0x805F, "GL_TEXTURE_ALPHA_SIZE" }, + { 0x8060, "GL_TEXTURE_LUMINANCE_SIZE" }, + { 0x8061, "GL_TEXTURE_INTENSITY_SIZE" }, + { 0x8063, "GL_PROXY_TEXTURE_1D" }, + { 0x8064, "GL_PROXY_TEXTURE_2D" }, + { 0x8066, "GL_TEXTURE_PRIORITY" }, + { 0x8067, "GL_TEXTURE_RESIDENT" }, + { 0x8068, "GL_TEXTURE_BINDING_1D" }, + { 0x8069, "GL_TEXTURE_BINDING_2D" }, + { 0x806A, "GL_TEXTURE_BINDING_3D" }, + { 0x806B, "GL_PACK_SKIP_IMAGES" }, + { 0x806C, "GL_PACK_IMAGE_HEIGHT" }, + { 0x806D, "GL_UNPACK_SKIP_IMAGES" }, + { 0x806E, "GL_UNPACK_IMAGE_HEIGHT" }, + { 0x806F, "GL_TEXTURE_3D" }, + { 0x8070, "GL_PROXY_TEXTURE_3D" }, + { 0x8071, "GL_TEXTURE_DEPTH" }, + { 0x8072, "GL_TEXTURE_WRAP_R" }, + { 0x8073, "GL_MAX_3D_TEXTURE_SIZE" }, + { 0x8074, "GL_VERTEX_ARRAY" }, + { 0x8075, "GL_NORMAL_ARRAY" }, + { 0x8076, "GL_COLOR_ARRAY" }, + { 0x8077, "GL_INDEX_ARRAY" }, + { 0x8078, "GL_TEXTURE_COORD_ARRAY" }, + { 0x8079, "GL_EDGE_FLAG_ARRAY" }, + { 0x807A, "GL_VERTEX_ARRAY_SIZE" }, + { 0x807B, "GL_VERTEX_ARRAY_TYPE" }, + { 0x807C, "GL_VERTEX_ARRAY_STRIDE" }, + { 0x807E, "GL_NORMAL_ARRAY_TYPE" }, + { 0x807F, "GL_NORMAL_ARRAY_STRIDE" }, + { 0x8081, "GL_COLOR_ARRAY_SIZE" }, + { 0x8082, "GL_COLOR_ARRAY_TYPE" }, + { 0x8083, "GL_COLOR_ARRAY_STRIDE" }, + { 0x8085, "GL_INDEX_ARRAY_TYPE" }, + { 0x8086, "GL_INDEX_ARRAY_STRIDE" }, + { 0x8088, "GL_TEXTURE_COORD_ARRAY_SIZE" }, + { 0x8089, "GL_TEXTURE_COORD_ARRAY_TYPE" }, + { 0x808A, "GL_TEXTURE_COORD_ARRAY_STRIDE" }, + { 0x808C, "GL_EDGE_FLAG_ARRAY_STRIDE" }, + { 0x808E, "GL_VERTEX_ARRAY_POINTER" }, + { 0x808F, "GL_NORMAL_ARRAY_POINTER" }, + { 0x8090, "GL_COLOR_ARRAY_POINTER" }, + { 0x8091, "GL_INDEX_ARRAY_POINTER" }, + { 0x8092, "GL_TEXTURE_COORD_ARRAY_POINTER" }, + { 0x8093, "GL_EDGE_FLAG_ARRAY_POINTER" }, + { 0x809D, "GL_MULTISAMPLE_ARB" }, + { 0x809D, "GL_MULTISAMPLE" }, + { 0x809E, "GL_SAMPLE_ALPHA_TO_COVERAGE_ARB" }, + { 0x809E, "GL_SAMPLE_ALPHA_TO_COVERAGE" }, + { 0x809F, "GL_SAMPLE_ALPHA_TO_ONE_ARB" }, + { 0x809F, "GL_SAMPLE_ALPHA_TO_ONE" }, + { 0x80A0, "GL_SAMPLE_COVERAGE_ARB" }, + { 0x80A0, "GL_SAMPLE_COVERAGE" }, + { 0x80A0, "GL_SAMPLE_MASK_EXT" }, + { 0x80A1, "GL_1PASS_EXT" }, + { 0x80A2, "GL_2PASS_0_EXT" }, + { 0x80A3, "GL_2PASS_1_EXT" }, + { 0x80A4, "GL_4PASS_0_EXT" }, + { 0x80A5, "GL_4PASS_1_EXT" }, + { 0x80A6, "GL_4PASS_2_EXT" }, + { 0x80A7, "GL_4PASS_3_EXT" }, + { 0x80A8, "GL_SAMPLE_BUFFERS" }, + { 0x80A9, "GL_SAMPLES" }, + { 0x80AA, "GL_SAMPLE_COVERAGE_VALUE" }, + { 0x80AB, "GL_SAMPLE_COVERAGE_INVERT" }, + { 0x80AC, "GL_SAMPLE_PATTERN_EXT" }, + { 0x80B1, "GL_COLOR_MATRIX" }, + { 0x80B2, "GL_COLOR_MATRIX_STACK_DEPTH" }, + { 0x80B3, "GL_MAX_COLOR_MATRIX_STACK_DEPTH" }, + { 0x80B4, "GL_POST_COLOR_MATRIX_RED_SCALE" }, + { 0x80B5, "GL_POST_COLOR_MATRIX_GREEN_SCALE" }, + { 0x80B6, "GL_POST_COLOR_MATRIX_BLUE_SCALE" }, + { 0x80B7, "GL_POST_COLOR_MATRIX_ALPHA_SCALE" }, + { 0x80B8, "GL_POST_COLOR_MATRIX_RED_BIAS" }, + { 0x80B9, "GL_POST_COLOR_MATRIX_GREEN_BIAS" }, + { 0x80BA, "GL_POST_COLOR_MATRIX_BLUE_BIAS" }, + { 0x80BB, "GL_POST_COLOR_MATRIX_ALPHA_BIAS" }, + { 0x80BF, "GL_TEXTURE_COMPARE_FAIL_VALUE_ARB" }, + { 0x80C8, "GL_BLEND_DST_RGB" }, + { 0x80C9, "GL_BLEND_SRC_RGB" }, + { 0x80CA, "GL_BLEND_DST_ALPHA" }, + { 0x80CB, "GL_BLEND_SRC_ALPHA" }, + { 0x80CC, "GL_422_EXT" }, + { 0x80CD, "GL_422_REV_EXT" }, + { 0x80CE, "GL_422_AVERAGE_EXT" }, + { 0x80CF, "GL_422_REV_AVERAGE_EXT" }, + { 0x80D0, "GL_COLOR_TABLE" }, + { 0x80D1, "GL_POST_CONVOLUTION_COLOR_TABLE" }, + { 0x80D2, "GL_POST_COLOR_MATRIX_COLOR_TABLE" }, + { 0x80D3, "GL_PROXY_COLOR_TABLE" }, + { 0x80D4, "GL_PROXY_POST_CONVOLUTION_COLOR_TABLE" }, + { 0x80D5, "GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE" }, + { 0x80D6, "GL_COLOR_TABLE_SCALE" }, + { 0x80D7, "GL_COLOR_TABLE_BIAS" }, + { 0x80D8, "GL_COLOR_TABLE_FORMAT" }, + { 0x80D9, "GL_COLOR_TABLE_WIDTH" }, + { 0x80DA, "GL_COLOR_TABLE_RED_SIZE" }, + { 0x80DB, "GL_COLOR_TABLE_GREEN_SIZE" }, + { 0x80DC, "GL_COLOR_TABLE_BLUE_SIZE" }, + { 0x80DD, "GL_COLOR_TABLE_ALPHA_SIZE" }, + { 0x80DE, "GL_COLOR_TABLE_LUMINANCE_SIZE" }, + { 0x80DF, "GL_COLOR_TABLE_INTENSITY_SIZE" }, + { 0x80E0, "GL_BGR_EXT" }, + { 0x80E0, "GL_BGR" }, + { 0x80E1, "GL_BGRA_EXT" }, + { 0x80E1, "GL_BGRA" }, + { 0x80E1, "GL_BGRA" }, + { 0x80E2, "GL_COLOR_INDEX1_EXT" }, + { 0x80E3, "GL_COLOR_INDEX2_EXT" }, + { 0x80E4, "GL_COLOR_INDEX4_EXT" }, + { 0x80E5, "GL_COLOR_INDEX8_EXT" }, + { 0x80E6, "GL_COLOR_INDEX12_EXT" }, + { 0x80E7, "GL_COLOR_INDEX16_EXT" }, + { 0x80E8, "GL_MAX_ELEMENTS_VERTICES_EXT" }, + { 0x80E8, "GL_MAX_ELEMENTS_VERTICES" }, + { 0x80E9, "GL_MAX_ELEMENTS_INDICES_EXT" }, + { 0x80E9, "GL_MAX_ELEMENTS_INDICES" }, + { 0x80ED, "GL_TEXTURE_INDEX_SIZE_EXT" }, + { 0x80F0, "GL_CLIP_VOLUME_CLIPPING_HINT_EXT" }, + { 0x8126, "GL_POINT_SIZE_MIN_ARB" }, + { 0x8126, "GL_POINT_SIZE_MIN" }, + { 0x8127, "GL_POINT_SIZE_MAX_ARB" }, + { 0x8127, "GL_POINT_SIZE_MAX" }, + { 0x8128, "GL_POINT_FADE_THRESHOLD_SIZE_ARB" }, + { 0x8128, "GL_POINT_FADE_THRESHOLD_SIZE" }, + { 0x8129, "GL_POINT_DISTANCE_ATTENUATION_ARB" }, + { 0x8129, "GL_POINT_DISTANCE_ATTENUATION" }, + { 0x812D, "GL_CLAMP_TO_BORDER_ARB" }, + { 0x812D, "GL_CLAMP_TO_BORDER" }, + { 0x812F, "GL_CLAMP_TO_EDGE" }, + { 0x813A, "GL_TEXTURE_MIN_LOD" }, + { 0x813B, "GL_TEXTURE_MAX_LOD" }, + { 0x813C, "GL_TEXTURE_BASE_LEVEL" }, + { 0x813D, "GL_TEXTURE_MAX_LEVEL" }, + { 0x8151, "GL_CONSTANT_BORDER" }, + { 0x8153, "GL_REPLICATE_BORDER" }, + { 0x8154, "GL_CONVOLUTION_BORDER_COLOR" }, + { 0x8191, "GL_GENERATE_MIPMAP" }, + { 0x8192, "GL_GENERATE_MIPMAP_HINT" }, + { 0x81A5, "GL_DEPTH_COMPONENT16_ARB" }, + { 0x81A5, "GL_DEPTH_COMPONENT16" }, + { 0x81A6, "GL_DEPTH_COMPONENT24_ARB" }, + { 0x81A6, "GL_DEPTH_COMPONENT24" }, + { 0x81A7, "GL_DEPTH_COMPONENT32_ARB" }, + { 0x81A7, "GL_DEPTH_COMPONENT32" }, + { 0x81A8, "GL_ARRAY_ELEMENT_LOCK_FIRST_EXT" }, + { 0x81A9, "GL_ARRAY_ELEMENT_LOCK_COUNT_EXT" }, + { 0x81AA, "GL_CULL_VERTEX_EXT" }, + { 0x81AB, "GL_CULL_VERTEX_EYE_POSITION_EXT" }, + { 0x81AC, "GL_CULL_VERTEX_OBJECT_POSITION_EXT" }, + { 0x81AD, "GL_IUI_V2F_EXT" }, + { 0x81AE, "GL_IUI_V3F_EXT" }, + { 0x81AF, "GL_IUI_N3F_V2F_EXT" }, + { 0x81B0, "GL_IUI_N3F_V3F_EXT" }, + { 0x81B1, "GL_T2F_IUI_V2F_EXT" }, + { 0x81B2, "GL_T2F_IUI_V3F_EXT" }, + { 0x81B3, "GL_T2F_IUI_N3F_V2F_EXT" }, + { 0x81B4, "GL_T2F_IUI_N3F_V3F_EXT" }, + { 0x81B5, "GL_INDEX_TEST_EXT" }, + { 0x81B6, "GL_INDEX_TEST_FUNC_EXT" }, + { 0x81B7, "GL_INDEX_TEST_REF_EXT" }, + { 0x81B8, "GL_INDEX_MATERIAL_EXT" }, + { 0x81B9, "GL_INDEX_MATERIAL_PARAMETER_EXT" }, + { 0x81BA, "GL_INDEX_MATERIAL_FACE_EXT" }, + { 0x81F8, "GL_LIGHT_MODEL_COLOR_CONTROL_EXT" }, + { 0x81F8, "GL_LIGHT_MODEL_COLOR_CONTROL" }, + { 0x81F9, "GL_SINGLE_COLOR_EXT" }, + { 0x81F9, "GL_SINGLE_COLOR" }, + { 0x81FA, "GL_SEPARATE_SPECULAR_COLOR_EXT" }, + { 0x81FA, "GL_SEPARATE_SPECULAR_COLOR" }, + { 0x81FB, "GL_SHARED_TEXTURE_PALETTE_EXT" }, + { 0x8210, "GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING" }, + { 0x8211, "GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE" }, + { 0x8212, "GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE" }, + { 0x8213, "GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE" }, + { 0x8214, "GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE" }, + { 0x8215, "GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE" }, + { 0x8216, "GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE" }, + { 0x8217, "GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE" }, + { 0x8218, "GL_FRAMEBUFFER_DEFAULT" }, + { 0x8219, "GL_FRAMEBUFFER_UNDEFINED" }, + { 0x821A, "GL_DEPTH_STENCIL_ATTACHMENT" }, + { 0x8225, "GL_COMPRESSED_RED" }, + { 0x8226, "GL_COMPRESSED_RG" }, + { 0x8227, "GL_RG" }, + { 0x8228, "GL_RG_INTEGER" }, + { 0x8229, "GL_R8" }, + { 0x822A, "GL_R16" }, + { 0x822B, "GL_RG8" }, + { 0x822C, "GL_RG16" }, + { 0x822D, "GL_R16F" }, + { 0x822E, "GL_R32F" }, + { 0x822F, "GL_RG16F" }, + { 0x8230, "GL_RG32F" }, + { 0x8231, "GL_R8I" }, + { 0x8232, "GL_R8UI" }, + { 0x8233, "GL_R16I" }, + { 0x8234, "GL_R16UI" }, + { 0x8235, "GL_R32I" }, + { 0x8236, "GL_R32UI" }, + { 0x8237, "GL_RG8I" }, + { 0x8238, "GL_RG8UI" }, + { 0x8239, "GL_RG16I" }, + { 0x823A, "GL_RG16UI" }, + { 0x823B, "GL_RG32I" }, + { 0x823C, "GL_RG32UI" }, + { 0x8330, "GL_PIXEL_TRANSFORM_2D_EXT" }, + { 0x8331, "GL_PIXEL_MAG_FILTER_EXT" }, + { 0x8332, "GL_PIXEL_MIN_FILTER_EXT" }, + { 0x8333, "GL_PIXEL_CUBIC_WEIGHT_EXT" }, + { 0x8334, "GL_CUBIC_EXT" }, + { 0x8335, "GL_AVERAGE_EXT" }, + { 0x8336, "GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT" }, + { 0x8337, "GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT" }, + { 0x8338, "GL_PIXEL_TRANSFORM_2D_MATRIX_EXT" }, + { 0x8349, "GL_FRAGMENT_MATERIAL_EXT" }, + { 0x834A, "GL_FRAGMENT_NORMAL_EXT" }, + { 0x834C, "GL_FRAGMENT_COLOR_EXT" }, + { 0x834D, "GL_ATTENUATION_EXT" }, + { 0x834E, "GL_SHADOW_ATTENUATION_EXT" }, + { 0x834F, "GL_TEXTURE_APPLICATION_MODE_EXT" }, + { 0x8350, "GL_TEXTURE_LIGHT_EXT" }, + { 0x8351, "GL_TEXTURE_MATERIAL_FACE_EXT" }, + { 0x8352, "GL_TEXTURE_MATERIAL_PARAMETER_EXT" }, + { 0x8362, "GL_UNSIGNED_BYTE_2_3_3_REV" }, + { 0x8363, "GL_UNSIGNED_SHORT_5_6_5" }, + { 0x8364, "GL_UNSIGNED_SHORT_5_6_5_REV" }, + { 0x8365, "GL_UNSIGNED_SHORT_4_4_4_4_REV" }, + { 0x8366, "GL_UNSIGNED_SHORT_1_5_5_5_REV" }, + { 0x8367, "GL_UNSIGNED_INT_8_8_8_8_REV" }, + { 0x8368, "GL_UNSIGNED_INT_2_10_10_10_REV" }, + { 0x8370, "GL_MIRRORED_REPEAT_ARB" }, + { 0x8370, "GL_MIRRORED_REPEAT" }, + { 0x83F0, "GL_COMPRESSED_RGB_S3TC_DXT1_EXT" }, + { 0x83F1, "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT" }, + { 0x83F2, "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT" }, + { 0x83F3, "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT" }, + { 0x8439, "GL_TANGENT_ARRAY_EXT" }, + { 0x843A, "GL_BINORMAL_ARRAY_EXT" }, + { 0x843B, "GL_CURRENT_TANGENT_EXT" }, + { 0x843C, "GL_CURRENT_BINORMAL_EXT" }, + { 0x843E, "GL_TANGENT_ARRAY_TYPE_EXT" }, + { 0x843F, "GL_TANGENT_ARRAY_STRIDE_EXT" }, + { 0x8440, "GL_BINORMAL_ARRAY_TYPE_EXT" }, + { 0x8441, "GL_BINORMAL_ARRAY_STRIDE_EXT" }, + { 0x8442, "GL_TANGENT_ARRAY_POINTER_EXT" }, + { 0x8443, "GL_BINORMAL_ARRAY_POINTER_EXT" }, + { 0x8444, "GL_MAP1_TANGENT_EXT" }, + { 0x8445, "GL_MAP2_TANGENT_EXT" }, + { 0x8446, "GL_MAP1_BINORMAL_EXT" }, + { 0x8447, "GL_MAP2_BINORMAL_EXT" }, + { 0x8450, "GL_FOG_COORD_SRC" }, + { 0x8450, "GL_FOG_COORDINATE_SOURCE_EXT" }, + { 0x8450, "GL_FOG_COORDINATE_SOURCE" }, + { 0x8451, "GL_FOG_COORD" }, + { 0x8451, "GL_FOG_COORDINATE_EXT" }, + { 0x8451, "GL_FOG_COORDINATE" }, + { 0x8452, "GL_FRAGMENT_DEPTH_EXT" }, + { 0x8452, "GL_FRAGMENT_DEPTH" }, + { 0x8453 , "GL_CURRENT_FOG_COORD" }, + { 0x8453 , "GL_CURRENT_FOG_COORDINATE" }, + { 0x8453, "GL_CURRENT_FOG_COORDINATE_EXT" }, + { 0x8454, "GL_FOG_COORD_ARRAY_TYPE" }, + { 0x8454, "GL_FOG_COORDINATE_ARRAY_TYPE_EXT" }, + { 0x8454, "GL_FOG_COORDINATE_ARRAY_TYPE" }, + { 0x8455, "GL_FOG_COORD_ARRAY_STRIDE" }, + { 0x8455, "GL_FOG_COORDINATE_ARRAY_STRIDE_EXT" }, + { 0x8455, "GL_FOG_COORDINATE_ARRAY_STRIDE" }, + { 0x8456, "GL_FOG_COORD_ARRAY_POINTER" }, + { 0x8456, "GL_FOG_COORDINATE_ARRAY_POINTER_EXT" }, + { 0x8456, "GL_FOG_COORDINATE_ARRAY_POINTER" }, + { 0x8457, "GL_FOG_COORD_ARRAY" }, + { 0x8457, "GL_FOG_COORDINATE_ARRAY_EXT" }, + { 0x8457, "GL_FOG_COORDINATE_ARRAY" }, + { 0x8458, "GL_COLOR_SUM_ARB" }, + { 0x8458, "GL_COLOR_SUM_EXT" }, + { 0x8458, "GL_COLOR_SUM" }, + { 0x8459, "GL_CURRENT_SECONDARY_COLOR_EXT" }, + { 0x8459, "GL_CURRENT_SECONDARY_COLOR" }, + { 0x845A, "GL_SECONDARY_COLOR_ARRAY_SIZE_EXT" }, + { 0x845A, "GL_SECONDARY_COLOR_ARRAY_SIZE" }, + { 0x845B, "GL_SECONDARY_COLOR_ARRAY_TYPE_EXT" }, + { 0x845B, "GL_SECONDARY_COLOR_ARRAY_TYPE" }, + { 0x845C, "GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT" }, + { 0x845C, "GL_SECONDARY_COLOR_ARRAY_STRIDE" }, + { 0x845D, "GL_SECONDARY_COLOR_ARRAY_POINTER_EXT" }, + { 0x845D, "GL_SECONDARY_COLOR_ARRAY_POINTER" }, + { 0x845E, "GL_SECONDARY_COLOR_ARRAY_EXT" }, + { 0x845E, "GL_SECONDARY_COLOR_ARRAY" }, + { 0x845F, "GL_CURRENT_RASTER_SECONDARY_COLOR" }, + { 0x846D, "GL_ALIASED_POINT_SIZE_RANGE" }, + { 0x846E, "GL_ALIASED_LINE_WIDTH_RANGE" }, + { 0x84C0, "GL_TEXTURE0" }, + { 0x84C1, "GL_TEXTURE1" }, + { 0x84C2, "GL_TEXTURE2" }, + { 0x84C3, "GL_TEXTURE3" }, + { 0x84C4, "GL_TEXTURE4" }, + { 0x84C5, "GL_TEXTURE5" }, + { 0x84C6, "GL_TEXTURE6" }, + { 0x84C7, "GL_TEXTURE7" }, + { 0x84C8, "GL_TEXTURE8" }, + { 0x84C9, "GL_TEXTURE9" }, + { 0x84CA, "GL_TEXTURE10" }, + { 0x84CB, "GL_TEXTURE11" }, + { 0x84CC, "GL_TEXTURE12" }, + { 0x84CD, "GL_TEXTURE13" }, + { 0x84CE, "GL_TEXTURE14" }, + { 0x84CF, "GL_TEXTURE15" }, + { 0x84D0, "GL_TEXTURE16" }, + { 0x84D1, "GL_TEXTURE17" }, + { 0x84D2, "GL_TEXTURE18" }, + { 0x84D3, "GL_TEXTURE19" }, + { 0x84D4, "GL_TEXTURE20" }, + { 0x84D5, "GL_TEXTURE21" }, + { 0x84D6, "GL_TEXTURE22" }, + { 0x84D7, "GL_TEXTURE23" }, + { 0x84D8, "GL_TEXTURE24" }, + { 0x84D9, "GL_TEXTURE25" }, + { 0x84DA, "GL_TEXTURE26" }, + { 0x84DB, "GL_TEXTURE27" }, + { 0x84DC, "GL_TEXTURE28" }, + { 0x84DD, "GL_TEXTURE29" }, + { 0x84DE, "GL_TEXTURE30" }, + { 0x84DF, "GL_TEXTURE31" }, + { 0x84E0, "GL_ACTIVE_TEXTURE" }, + { 0x84E1, "GL_CLIENT_ACTIVE_TEXTURE" }, + { 0x84E2, "GL_MAX_TEXTURE_UNITS" }, + { 0x84E3, "GL_TRANSPOSE_MODELVIEW_MATRIX" }, + { 0x84E4, "GL_TRANSPOSE_PROJECTION_MATRIX" }, + { 0x84E5, "GL_TRANSPOSE_TEXTURE_MATRIX" }, + { 0x84E6, "GL_TRANSPOSE_COLOR_MATRIX" }, + { 0x84E7, "GL_SUBTRACT" }, + { 0x84E8, "GL_MAX_RENDERBUFFER_SIZE" }, + { 0x84E9, "GL_COMPRESSED_ALPHA" }, + { 0x84EA, "GL_COMPRESSED_LUMINANCE" }, + { 0x84EB, "GL_COMPRESSED_LUMINANCE_ALPHA" }, + { 0x84EC, "GL_COMPRESSED_INTENSITY" }, + { 0x84ED, "GL_COMPRESSED_RGB" }, + { 0x84EE, "GL_COMPRESSED_RGBA" }, + { 0x84EF, "GL_TEXTURE_COMPRESSION_HINT" }, + { 0x84F5, "GL_TEXTURE_RECTANGLE_EXT" }, + { 0x84F6, "GL_TEXTURE_BINDING_RECTANGLE_EXT" }, + { 0x84F7, "GL_PROXY_TEXTURE_RECTANGLE_EXT" }, + { 0x84F8, "GL_MAX_RECTANGLE_TEXTURE_SIZE_EXT" }, + { 0x84F9, "GL_DEPTH_STENCIL" }, + { 0x84FA, "GL_UNSIGNED_INT_24_8" }, + { 0x84FD, "GL_MAX_TEXTURE_LOD_BIAS" }, + { 0x84FE, "GL_TEXTURE_MAX_ANISOTROPY_EXT" }, + { 0x84FF, "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT" }, + { 0x8500, "GL_TEXTURE_FILTER_CONTROL" }, + { 0x8501, "GL_TEXTURE_LOD_BIAS" }, + { 0x8502, "GL_MODELVIEW1_STACK_DEPTH_EXT" }, + { 0x8506, "GL_MODELVIEW_MATRIX1_EXT" }, + { 0x8507, "GL_INCR_WRAP" }, + { 0x8508, "GL_DECR_WRAP" }, + { 0x8509, "GL_VERTEX_WEIGHTING_EXT" }, + { 0x850A, "GL_MODELVIEW1_ARB" }, + { 0x850B, "GL_CURRENT_VERTEX_WEIGHT_EXT" }, + { 0x850C, "GL_VERTEX_WEIGHT_ARRAY_EXT" }, + { 0x850D, "GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT" }, + { 0x850E, "GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT" }, + { 0x850F, "GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT" }, + { 0x8510, "GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT" }, + { 0x8511, "GL_NORMAL_MAP_ARB" }, + { 0x8511, "GL_NORMAL_MAP_EXT" }, + { 0x8511, "GL_NORMAL_MAP" }, + { 0x8512, "GL_REFLECTION_MAP_ARB" }, + { 0x8512, "GL_REFLECTION_MAP_EXT" }, + { 0x8512, "GL_REFLECTION_MAP" }, + { 0x8513, "GL_TEXTURE_CUBE_MAP_ARB" }, + { 0x8513, "GL_TEXTURE_CUBE_MAP_EXT" }, + { 0x8513, "GL_TEXTURE_CUBE_MAP" }, + { 0x8514, "GL_TEXTURE_BINDING_CUBE_MAP_ARB" }, + { 0x8514, "GL_TEXTURE_BINDING_CUBE_MAP_EXT" }, + { 0x8514, "GL_TEXTURE_BINDING_CUBE_MAP" }, + { 0x8515, "GL_TEXTURE_CUBE_MAP_POSITIVE_X" }, + { 0x8516, "GL_TEXTURE_CUBE_MAP_NEGATIVE_X" }, + { 0x8517, "GL_TEXTURE_CUBE_MAP_POSITIVE_Y" }, + { 0x8518, "GL_TEXTURE_CUBE_MAP_NEGATIVE_Y" }, + { 0x8519, "GL_TEXTURE_CUBE_MAP_POSITIVE_Z" }, + { 0x851A, "GL_TEXTURE_CUBE_MAP_NEGATIVE_Z" }, + { 0x851B, "GL_PROXY_TEXTURE_CUBE_MAP" }, + { 0x851C, "GL_MAX_CUBE_MAP_TEXTURE_SIZE" }, + { 0x851D, "GL_VERTEX_ARRAY_RANGE_APPLE" }, + { 0x851E, "GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE" }, + { 0x851F, "GL_VERTEX_ARRAY_STORAGE_HINT_APPLE" }, + { 0x8520, "GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_APPLE" }, + { 0x8521, "GL_VERTEX_ARRAY_RANGE_POINTER_APPLE" }, + { 0x8570, "GL_COMBINE_ARB" }, + { 0x8570, "GL_COMBINE_EXT" }, + { 0x8570, "GL_COMBINE" }, + { 0x8571, "GL_COMBINE_RGB_ARB" }, + { 0x8571, "GL_COMBINE_RGB_EXT" }, + { 0x8571, "GL_COMBINE_RGB" }, + { 0x8572, "GL_COMBINE_ALPHA_ARB" }, + { 0x8572, "GL_COMBINE_ALPHA_EXT" }, + { 0x8572, "GL_COMBINE_ALPHA" }, + { 0x8573, "GL_RGB_SCALE_ARB" }, + { 0x8573, "GL_RGB_SCALE_EXT" }, + { 0x8573, "GL_RGB_SCALE" }, + { 0x8574, "GL_ADD_SIGNED_ARB" }, + { 0x8574, "GL_ADD_SIGNED_EXT" }, + { 0x8574, "GL_ADD_SIGNED" }, + { 0x8575, "GL_INTERPOLATE_ARB" }, + { 0x8575, "GL_INTERPOLATE_EXT" }, + { 0x8575, "GL_INTERPOLATE" }, + { 0x8576, "GL_CONSTANT_ARB" }, + { 0x8576, "GL_CONSTANT_EXT" }, + { 0x8576, "GL_CONSTANT" }, + { 0x8577, "GL_PRIMARY_COLOR_ARB" }, + { 0x8577, "GL_PRIMARY_COLOR_EXT" }, + { 0x8577, "GL_PRIMARY_COLOR" }, + { 0x8578, "GL_PREVIOUS_ARB" }, + { 0x8578, "GL_PREVIOUS_EXT" }, + { 0x8578, "GL_PREVIOUS" }, + { 0x8580, "GL_SOURCE0_RGB_ARB" }, + { 0x8580, "GL_SOURCE0_RGB_EXT" }, + { 0x8580, "GL_SOURCE0_RGB" }, + { 0x8580, "GL_SRC0_RGB" }, + { 0x8581, "GL_SOURCE1_RGB_ARB" }, + { 0x8581, "GL_SOURCE1_RGB_EXT" }, + { 0x8581, "GL_SOURCE1_RGB" }, + { 0x8581, "GL_SRC1_RGB" }, + { 0x8582, "GL_SOURCE2_RGB_ARB" }, + { 0x8582, "GL_SOURCE2_RGB_EXT" }, + { 0x8582, "GL_SOURCE2_RGB" }, + { 0x8582, "GL_SRC2_RGB" }, + { 0x8583, "GL_SOURCE3_RGB_ARB" }, + { 0x8583, "GL_SOURCE3_RGB_EXT" }, + { 0x8583, "GL_SOURCE3_RGB" }, + { 0x8583, "GL_SRC3_RGB" }, + { 0x8584, "GL_SOURCE4_RGB_ARB" }, + { 0x8584, "GL_SOURCE4_RGB_EXT" }, + { 0x8584, "GL_SOURCE4_RGB" }, + { 0x8584, "GL_SRC4_RGB" }, + { 0x8585, "GL_SOURCE5_RGB_ARB" }, + { 0x8585, "GL_SOURCE5_RGB_EXT" }, + { 0x8585, "GL_SOURCE5_RGB" }, + { 0x8585, "GL_SRC5_RGB" }, + { 0x8586, "GL_SOURCE6_RGB_ARB" }, + { 0x8586, "GL_SOURCE6_RGB_EXT" }, + { 0x8586, "GL_SOURCE6_RGB" }, + { 0x8586, "GL_SRC6_RGB" }, + { 0x8587, "GL_SOURCE7_RGB_ARB" }, + { 0x8587, "GL_SOURCE7_RGB_EXT" }, + { 0x8587, "GL_SOURCE7_RGB" }, + { 0x8587, "GL_SRC7_RGB" }, + { 0x8588, "GL_SOURCE0_ALPHA_ARB" }, + { 0x8588, "GL_SOURCE0_ALPHA_EXT" }, + { 0x8588, "GL_SOURCE0_ALPHA" }, + { 0x8588, "GL_SRC0_ALPHA" }, + { 0x8589, "GL_SOURCE1_ALPHA_ARB" }, + { 0x8589, "GL_SOURCE1_ALPHA_EXT" }, + { 0x8589, "GL_SOURCE1_ALPHA" }, + { 0x8589, "GL_SRC1_ALPHA" }, + { 0x858A, "GL_SOURCE2_ALPHA_ARB" }, + { 0x858A, "GL_SOURCE2_ALPHA_EXT" }, + { 0x858A, "GL_SOURCE2_ALPHA" }, + { 0x858A, "GL_SRC2_ALPHA" }, + { 0x858B, "GL_SOURCE3_ALPHA_ARB" }, + { 0x858B, "GL_SOURCE3_ALPHA_EXT" }, + { 0x858B, "GL_SOURCE3_ALPHA" }, + { 0x858B, "GL_SRC3_ALPHA" }, + { 0x858C, "GL_SOURCE4_ALPHA_ARB" }, + { 0x858C, "GL_SOURCE4_ALPHA_EXT" }, + { 0x858C, "GL_SOURCE4_ALPHA" }, + { 0x858C, "GL_SRC4_ALPHA" }, + { 0x858D, "GL_SOURCE5_ALPHA_ARB" }, + { 0x858D, "GL_SOURCE5_ALPHA_EXT" }, + { 0x858D, "GL_SOURCE5_ALPHA" }, + { 0x858D, "GL_SRC5_ALPHA" }, + { 0x858E, "GL_SOURCE6_ALPHA_ARB" }, + { 0x858E, "GL_SOURCE6_ALPHA_EXT" }, + { 0x858E, "GL_SOURCE6_ALPHA" }, + { 0x858E, "GL_SRC6_ALPHA" }, + { 0x858F, "GL_SOURCE7_ALPHA_ARB" }, + { 0x858F, "GL_SOURCE7_ALPHA_EXT" }, + { 0x858F, "GL_SOURCE7_ALPHA" }, + { 0x858F, "GL_SRC7_ALPHA" }, + { 0x8590, "GL_OPERAND0_RGB_ARB" }, + { 0x8590, "GL_OPERAND0_RGB_EXT" }, + { 0x8590, "GL_OPERAND0_RGB" }, + { 0x8591, "GL_OPERAND1_RGB_ARB" }, + { 0x8591, "GL_OPERAND1_RGB_EXT" }, + { 0x8591, "GL_OPERAND1_RGB" }, + { 0x8592, "GL_OPERAND2_RGB_ARB" }, + { 0x8592, "GL_OPERAND2_RGB_EXT" }, + { 0x8592, "GL_OPERAND2_RGB" }, + { 0x8593, "GL_OPERAND3_RGB_ARB" }, + { 0x8593, "GL_OPERAND3_RGB_EXT" }, + { 0x8593, "GL_OPERAND3_RGB" }, + { 0x8594, "GL_OPERAND4_RGB_ARB" }, + { 0x8594, "GL_OPERAND4_RGB_EXT" }, + { 0x8594, "GL_OPERAND4_RGB" }, + { 0x8595, "GL_OPERAND5_RGB_ARB" }, + { 0x8595, "GL_OPERAND5_RGB_EXT" }, + { 0x8595, "GL_OPERAND5_RGB" }, + { 0x8596, "GL_OPERAND6_RGB_ARB" }, + { 0x8596, "GL_OPERAND6_RGB_EXT" }, + { 0x8596, "GL_OPERAND6_RGB" }, + { 0x8597, "GL_OPERAND7_RGB_ARB" }, + { 0x8597, "GL_OPERAND7_RGB_EXT" }, + { 0x8597, "GL_OPERAND7_RGB" }, + { 0x8598, "GL_OPERAND0_ALPHA_ARB" }, + { 0x8598, "GL_OPERAND0_ALPHA_EXT" }, + { 0x8598, "GL_OPERAND0_ALPHA" }, + { 0x8599, "GL_OPERAND1_ALPHA_ARB" }, + { 0x8599, "GL_OPERAND1_ALPHA_EXT" }, + { 0x8599, "GL_OPERAND1_ALPHA" }, + { 0x859A, "GL_OPERAND2_ALPHA_ARB" }, + { 0x859A, "GL_OPERAND2_ALPHA_EXT" }, + { 0x859A, "GL_OPERAND2_ALPHA" }, + { 0x859B, "GL_OPERAND3_ALPHA_ARB" }, + { 0x859B, "GL_OPERAND3_ALPHA_EXT" }, + { 0x859B, "GL_OPERAND3_ALPHA" }, + { 0x859C, "GL_OPERAND4_ALPHA_ARB" }, + { 0x859C, "GL_OPERAND4_ALPHA_EXT" }, + { 0x859C, "GL_OPERAND4_ALPHA" }, + { 0x859D, "GL_OPERAND5_ALPHA_ARB" }, + { 0x859D, "GL_OPERAND5_ALPHA_EXT" }, + { 0x859D, "GL_OPERAND5_ALPHA" }, + { 0x859E, "GL_OPERAND6_ALPHA_ARB" }, + { 0x859E, "GL_OPERAND6_ALPHA_EXT" }, + { 0x859E, "GL_OPERAND6_ALPHA" }, + { 0x859F, "GL_OPERAND7_ALPHA_ARB" }, + { 0x859F, "GL_OPERAND7_ALPHA_EXT" }, + { 0x859F, "GL_OPERAND7_ALPHA" }, + { 0x85AE, "GL_PERTURB_EXT" }, + { 0x85AF, "GL_TEXTURE_NORMAL_EXT" }, + { 0x85B4, "GL_STORAGE_CLIENT_APPLE" }, + { 0x85B5, "GL_VERTEX_ARRAY_BINDING_APPLE" }, + { 0x85BD, "GL_STORAGE_PRIVATE_APPLE" }, + { 0x85BE, "GL_STORAGE_CACHED_APPLE" }, + { 0x85BF, "GL_STORAGE_SHARED_APPLE" }, + { 0x8620, "GL_VERTEX_PROGRAM_ARB" }, + { 0x8620, "GL_VERTEX_PROGRAM_NV" }, + { 0x8621, "GL_VERTEX_STATE_PROGRAM_NV" }, + { 0x8622, "GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB" }, + { 0x8622, "GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB" }, + { 0x8622, "GL_VERTEX_ATTRIB_ARRAY_ENABLED" }, + { 0x8623, "GL_ATTRIB_ARRAY_SIZE_NV" }, + { 0x8623, "GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB" }, + { 0x8623, "GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB" }, + { 0x8623, "GL_VERTEX_ATTRIB_ARRAY_SIZE" }, + { 0x8624, "GL_ATTRIB_ARRAY_STRIDE_NV" }, + { 0x8624, "GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB" }, + { 0x8624, "GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB" }, + { 0x8624, "GL_VERTEX_ATTRIB_ARRAY_STRIDE" }, + { 0x8625, "GL_ATTRIB_ARRAY_TYPE_NV" }, + { 0x8625, "GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB" }, + { 0x8625, "GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB" }, + { 0x8625, "GL_VERTEX_ATTRIB_ARRAY_TYPE" }, + { 0x8626, "GL_CURRENT_ATTRIB_NV" }, + { 0x8626, "GL_CURRENT_VERTEX_ATTRIB_ARB" }, + { 0x8626, "GL_CURRENT_VERTEX_ATTRIB_ARB" }, + { 0x8626, "GL_CURRENT_VERTEX_ATTRIB" }, + { 0x8627, "GL_PROGRAM_LENGTH_ARB" }, + { 0x8627, "GL_PROGRAM_LENGTH_NV" }, + { 0x8628, "GL_PROGRAM_STRING_ARB" }, + { 0x8628, "GL_PROGRAM_STRING_NV" }, + { 0x8629, "GL_MODELVIEW_PROJECTION_NV" }, + { 0x862A, "GL_IDENTITY_NV" }, + { 0x862B, "GL_INVERSE_NV" }, + { 0x862C, "GL_TRANSPOSE_NV" }, + { 0x862D, "GL_INVERSE_TRANSPOSE_NV" }, + { 0x862E, "GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB" }, + { 0x862E, "GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV" }, + { 0x862F, "GL_MAX_PROGRAM_MATRICES_ARB" }, + { 0x862F, "GL_MAX_TRACK_MATRICES_NV" }, + { 0x8630, "GL_MATRIX0_NV" }, + { 0x8631, "GL_MATRIX1_NV" }, + { 0x8632, "GL_MATRIX2_NV" }, + { 0x8633, "GL_MATRIX3_NV" }, + { 0x8634, "GL_MATRIX4_NV" }, + { 0x8635, "GL_MATRIX5_NV" }, + { 0x8636, "GL_MATRIX6_NV" }, + { 0x8637, "GL_MATRIX7_NV" }, + { 0x8640, "GL_CURRENT_MATRIX_STACK_DEPTH_ARB" }, + { 0x8640, "GL_CURRENT_MATRIX_STACK_DEPTH_NV" }, + { 0x8641, "GL_CURRENT_MATRIX_ARB" }, + { 0x8641, "GL_CURRENT_MATRIX_NV" }, + { 0x8642, "GL_PROGRAM_POINT_SIZE_EXT" }, + { 0x8642, "GL_VERTEX_PROGRAM_POINT_SIZE_ARB" }, + { 0x8642, "GL_VERTEX_PROGRAM_POINT_SIZE_ARB" }, + { 0x8642, "GL_VERTEX_PROGRAM_POINT_SIZE_NV" }, + { 0x8642, "GL_VERTEX_PROGRAM_POINT_SIZE" }, + { 0x8643, "GL_VERTEX_PROGRAM_TWO_SIDE_ARB" }, + { 0x8643, "GL_VERTEX_PROGRAM_TWO_SIDE_ARB" }, + { 0x8643, "GL_VERTEX_PROGRAM_TWO_SIDE_NV" }, + { 0x8643, "GL_VERTEX_PROGRAM_TWO_SIDE" }, + { 0x8644, "GL_PROGRAM_PARAMETER_NV" }, + { 0x8645, "GL_ATTRIB_ARRAY_POINTER_NV" }, + { 0x8645, "GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB" }, + { 0x8645, "GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB" }, + { 0x8645, "GL_VERTEX_ATTRIB_ARRAY_POINTER" }, + { 0x8646, "GL_PROGRAM_TARGET_NV" }, + { 0x8647, "GL_PROGRAM_RESIDENT_NV" }, + { 0x8648, "GL_TRACK_MATRIX_NV" }, + { 0x8649, "GL_TRACK_MATRIX_TRANSFORM_NV" }, + { 0x864A, "GL_VERTEX_PROGRAM_BINDING_NV" }, + { 0x864B, "GL_PROGRAM_ERROR_POSITION_ARB" }, + { 0x864B, "GL_PROGRAM_ERROR_POSITION_NV" }, + { 0x8650, "GL_VERTEX_ATTRIB_ARRAY0_NV" }, + { 0x8651, "GL_VERTEX_ATTRIB_ARRAY1_NV" }, + { 0x8652, "GL_VERTEX_ATTRIB_ARRAY2_NV" }, + { 0x8653, "GL_VERTEX_ATTRIB_ARRAY3_NV" }, + { 0x8654, "GL_VERTEX_ATTRIB_ARRAY4_NV" }, + { 0x8655, "GL_VERTEX_ATTRIB_ARRAY5_NV" }, + { 0x8656, "GL_VERTEX_ATTRIB_ARRAY6_NV" }, + { 0x8657, "GL_VERTEX_ATTRIB_ARRAY7_NV" }, + { 0x8658, "GL_VERTEX_ATTRIB_ARRAY8_NV" }, + { 0x8659, "GL_VERTEX_ATTRIB_ARRAY9_NV" }, + { 0x865A, "GL_VERTEX_ATTRIB_ARRAY10_NV" }, + { 0x865B, "GL_VERTEX_ATTRIB_ARRAY11_NV" }, + { 0x865C, "GL_VERTEX_ATTRIB_ARRAY12_NV" }, + { 0x865D, "GL_VERTEX_ATTRIB_ARRAY13_NV" }, + { 0x865E, "GL_VERTEX_ATTRIB_ARRAY14_NV" }, + { 0x865F, "GL_VERTEX_ATTRIB_ARRAY15_NV" }, + { 0x8660, "GL_MAP1_VERTEX_ATTRIB0_4_NV" }, + { 0x8661, "GL_MAP1_VERTEX_ATTRIB1_4_NV" }, + { 0x8662, "GL_MAP1_VERTEX_ATTRIB2_4_NV" }, + { 0x8663, "GL_MAP1_VERTEX_ATTRIB3_4_NV" }, + { 0x8664, "GL_MAP1_VERTEX_ATTRIB4_4_NV" }, + { 0x8665, "GL_MAP1_VERTEX_ATTRIB5_4_NV" }, + { 0x8666, "GL_MAP1_VERTEX_ATTRIB6_4_NV" }, + { 0x8667, "GL_MAP1_VERTEX_ATTRIB7_4_NV" }, + { 0x8668, "GL_MAP1_VERTEX_ATTRIB8_4_NV" }, + { 0x8669, "GL_MAP1_VERTEX_ATTRIB9_4_NV" }, + { 0x866A, "GL_MAP1_VERTEX_ATTRIB10_4_NV" }, + { 0x866B, "GL_MAP1_VERTEX_ATTRIB11_4_NV" }, + { 0x866C, "GL_MAP1_VERTEX_ATTRIB12_4_NV" }, + { 0x866D, "GL_MAP1_VERTEX_ATTRIB13_4_NV" }, + { 0x866E, "GL_MAP1_VERTEX_ATTRIB14_4_NV" }, + { 0x866F, "GL_MAP1_VERTEX_ATTRIB15_4_NV" }, + { 0x8670, "GL_MAP2_VERTEX_ATTRIB0_4_NV" }, + { 0x8671, "GL_MAP2_VERTEX_ATTRIB1_4_NV" }, + { 0x8672, "GL_MAP2_VERTEX_ATTRIB2_4_NV" }, + { 0x8673, "GL_MAP2_VERTEX_ATTRIB3_4_NV" }, + { 0x8674, "GL_MAP2_VERTEX_ATTRIB4_4_NV" }, + { 0x8675, "GL_MAP2_VERTEX_ATTRIB5_4_NV" }, + { 0x8676, "GL_MAP2_VERTEX_ATTRIB6_4_NV" }, + { 0x8677, "GL_MAP2_VERTEX_ATTRIB7_4_NV" }, + { 0x8677, "GL_PROGRAM_BINDING_ARB" }, + { 0x8677, "GL_PROGRAM_NAME_ARB" }, + { 0x8678, "GL_MAP2_VERTEX_ATTRIB8_4_NV" }, + { 0x8679, "GL_MAP2_VERTEX_ATTRIB9_4_NV" }, + { 0x867A, "GL_MAP2_VERTEX_ATTRIB10_4_NV" }, + { 0x867B, "GL_MAP2_VERTEX_ATTRIB11_4_NV" }, + { 0x867C, "GL_MAP2_VERTEX_ATTRIB12_4_NV" }, + { 0x867D, "GL_MAP2_VERTEX_ATTRIB13_4_NV" }, + { 0x867E, "GL_MAP2_VERTEX_ATTRIB14_4_NV" }, + { 0x867F, "GL_MAP2_VERTEX_ATTRIB15_4_NV" }, + { 0x86A0, "GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB" }, + { 0x86A0, "GL_TEXTURE_COMPRESSED_IMAGE_SIZE" }, + { 0x86A1, "GL_TEXTURE_COMPRESSED_ARB" }, + { 0x86A1, "GL_TEXTURE_COMPRESSED" }, + { 0x86A2, "GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB" }, + { 0x86A2, "GL_NUM_COMPRESSED_TEXTURE_FORMATS" }, + { 0x86A3, "GL_COMPRESSED_TEXTURE_FORMATS_ARB" }, + { 0x86A3, "GL_COMPRESSED_TEXTURE_FORMATS" }, + { 0x86A4, "GL_MAX_VERTEX_UNITS_ARB" }, + { 0x86A5, "GL_ACTIVE_VERTEX_UNITS_ARB" }, + { 0x86A6, "GL_WEIGHT_SUM_UNITY_ARB" }, + { 0x86A7, "GL_VERTEX_BLEND_ARB" }, + { 0x86A8, "GL_CURRENT_WEIGHT_ARB" }, + { 0x86A9, "GL_WEIGHT_ARRAY_TYPE_ARB" }, + { 0x86AA, "GL_WEIGHT_ARRAY_STRIDE_ARB" }, + { 0x86AB, "GL_WEIGHT_ARRAY_SIZE_ARB" }, + { 0x86AC, "GL_WEIGHT_ARRAY_POINTER_ARB" }, + { 0x86AD, "GL_WEIGHT_ARRAY_ARB" }, + { 0x86AE, "GL_DOT3_RGB_ARB" }, + { 0x86AE, "GL_DOT3_RGB" }, + { 0x86AF, "GL_DOT3_RGBA_ARB" }, + { 0x86AF, "GL_DOT3_RGBA" }, + { 0x8722, "GL_MODELVIEW2_ARB" }, + { 0x8723, "GL_MODELVIEW3_ARB" }, + { 0x8724, "GL_MODELVIEW4_ARB" }, + { 0x8725, "GL_MODELVIEW5_ARB" }, + { 0x8726, "GL_MODELVIEW6_ARB" }, + { 0x8727, "GL_MODELVIEW7_ARB" }, + { 0x8728, "GL_MODELVIEW8_ARB" }, + { 0x8729, "GL_MODELVIEW9_ARB" }, + { 0x872A, "GL_MODELVIEW10_ARB" }, + { 0x872B, "GL_MODELVIEW11_ARB" }, + { 0x872C, "GL_MODELVIEW12_ARB" }, + { 0x872D, "GL_MODELVIEW13_ARB" }, + { 0x872E, "GL_MODELVIEW14_ARB" }, + { 0x872F, "GL_MODELVIEW15_ARB" }, + { 0x8730, "GL_MODELVIEW16_ARB" }, + { 0x8731, "GL_MODELVIEW17_ARB" }, + { 0x8732, "GL_MODELVIEW18_ARB" }, + { 0x8733, "GL_MODELVIEW19_ARB" }, + { 0x8734, "GL_MODELVIEW20_ARB" }, + { 0x8735, "GL_MODELVIEW21_ARB" }, + { 0x8736, "GL_MODELVIEW22_ARB" }, + { 0x8737, "GL_MODELVIEW23_ARB" }, + { 0x8738, "GL_MODELVIEW24_ARB" }, + { 0x8739, "GL_MODELVIEW25_ARB" }, + { 0x873A, "GL_MODELVIEW26_ARB" }, + { 0x873B, "GL_MODELVIEW27_ARB" }, + { 0x873C, "GL_MODELVIEW28_ARB" }, + { 0x873D, "GL_MODELVIEW29_ARB" }, + { 0x873E, "GL_MODELVIEW30_ARB" }, + { 0x873F, "GL_MODELVIEW31_ARB" }, + { 0x8742, "GL_MIRROR_CLAMP_EXT" }, + { 0x8743, "GL_MIRROR_CLAMP_TO_EDGE_EXT" }, + { 0x8764, "GL_BUFFER_SIZE_ARB" }, + { 0x8764, "GL_BUFFER_SIZE" }, + { 0x8765, "GL_BUFFER_USAGE_ARB" }, + { 0x8765, "GL_BUFFER_USAGE" }, + { 0x8780, "GL_VERTEX_SHADER_EXT" }, + { 0x8781, "GL_VERTEX_SHADER_BINDING_EXT" }, + { 0x8782, "GL_OP_INDEX_EXT" }, + { 0x8783, "GL_OP_NEGATE_EXT" }, + { 0x8784, "GL_OP_DOT3_EXT" }, + { 0x8785, "GL_OP_DOT4_EXT" }, + { 0x8786, "GL_OP_MUL_EXT" }, + { 0x8787, "GL_OP_ADD_EXT" }, + { 0x8788, "GL_OP_MADD_EXT" }, + { 0x8789, "GL_OP_FRAC_EXT" }, + { 0x878A, "GL_OP_MAX_EXT" }, + { 0x878B, "GL_OP_MIN_EXT" }, + { 0x878C, "GL_OP_SET_GE_EXT" }, + { 0x878D, "GL_OP_SET_LT_EXT" }, + { 0x878E, "GL_OP_CLAMP_EXT" }, + { 0x878F, "GL_OP_FLOOR_EXT" }, + { 0x8790, "GL_OP_ROUND_EXT" }, + { 0x8791, "GL_OP_EXP_BASE_2_EXT" }, + { 0x8792, "GL_OP_LOG_BASE_2_EXT" }, + { 0x8793, "GL_OP_POWER_EXT" }, + { 0x8794, "GL_OP_RECIP_EXT" }, + { 0x8795, "GL_OP_RECIP_SQRT_EXT" }, + { 0x8796, "GL_OP_SUB_EXT" }, + { 0x8797, "GL_OP_CROSS_PRODUCT_EXT" }, + { 0x8798, "GL_OP_MULTIPLY_MATRIX_EXT" }, + { 0x8799, "GL_OP_MOV_EXT" }, + { 0x879A, "GL_OUTPUT_VERTEX_EXT" }, + { 0x879B, "GL_OUTPUT_COLOR0_EXT" }, + { 0x879C, "GL_OUTPUT_COLOR1_EXT" }, + { 0x879D, "GL_OUTPUT_TEXTURE_COORD0_EXT" }, + { 0x879E, "GL_OUTPUT_TEXTURE_COORD1_EXT" }, + { 0x879F, "GL_OUTPUT_TEXTURE_COORD2_EXT" }, + { 0x87A0, "GL_OUTPUT_TEXTURE_COORD3_EXT" }, + { 0x87A1, "GL_OUTPUT_TEXTURE_COORD4_EXT" }, + { 0x87A2, "GL_OUTPUT_TEXTURE_COORD5_EXT" }, + { 0x87A3, "GL_OUTPUT_TEXTURE_COORD6_EXT" }, + { 0x87A4, "GL_OUTPUT_TEXTURE_COORD7_EXT" }, + { 0x87A5, "GL_OUTPUT_TEXTURE_COORD8_EXT" }, + { 0x87A6, "GL_OUTPUT_TEXTURE_COORD9_EXT" }, + { 0x87A7, "GL_OUTPUT_TEXTURE_COORD10_EXT" }, + { 0x87A8, "GL_OUTPUT_TEXTURE_COORD11_EXT" }, + { 0x87A9, "GL_OUTPUT_TEXTURE_COORD12_EXT" }, + { 0x87AA, "GL_OUTPUT_TEXTURE_COORD13_EXT" }, + { 0x87AB, "GL_OUTPUT_TEXTURE_COORD14_EXT" }, + { 0x87AC, "GL_OUTPUT_TEXTURE_COORD15_EXT" }, + { 0x87AD, "GL_OUTPUT_TEXTURE_COORD16_EXT" }, + { 0x87AE, "GL_OUTPUT_TEXTURE_COORD17_EXT" }, + { 0x87AF, "GL_OUTPUT_TEXTURE_COORD18_EXT" }, + { 0x87B0, "GL_OUTPUT_TEXTURE_COORD19_EXT" }, + { 0x87B1, "GL_OUTPUT_TEXTURE_COORD20_EXT" }, + { 0x87B2, "GL_OUTPUT_TEXTURE_COORD21_EXT" }, + { 0x87B3, "GL_OUTPUT_TEXTURE_COORD22_EXT" }, + { 0x87B4, "GL_OUTPUT_TEXTURE_COORD23_EXT" }, + { 0x87B5, "GL_OUTPUT_TEXTURE_COORD24_EXT" }, + { 0x87B6, "GL_OUTPUT_TEXTURE_COORD25_EXT" }, + { 0x87B7, "GL_OUTPUT_TEXTURE_COORD26_EXT" }, + { 0x87B8, "GL_OUTPUT_TEXTURE_COORD27_EXT" }, + { 0x87B9, "GL_OUTPUT_TEXTURE_COORD28_EXT" }, + { 0x87BA, "GL_OUTPUT_TEXTURE_COORD29_EXT" }, + { 0x87BB, "GL_OUTPUT_TEXTURE_COORD30_EXT" }, + { 0x87BC, "GL_OUTPUT_TEXTURE_COORD31_EXT" }, + { 0x87BD, "GL_OUTPUT_FOG_EXT" }, + { 0x87BE, "GL_SCALAR_EXT" }, + { 0x87BF, "GL_VECTOR_EXT" }, + { 0x87C0, "GL_MATRIX_EXT" }, + { 0x87C1, "GL_VARIANT_EXT" }, + { 0x87C2, "GL_INVARIANT_EXT" }, + { 0x87C3, "GL_LOCAL_CONSTANT_EXT" }, + { 0x87C4, "GL_LOCAL_EXT" }, + { 0x87C5, "GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT" }, + { 0x87C6, "GL_MAX_VERTEX_SHADER_VARIANTS_EXT" }, + { 0x87C7, "GL_MAX_VERTEX_SHADER_INVARIANTS_EXT" }, + { 0x87C8, "GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT" }, + { 0x87C9, "GL_MAX_VERTEX_SHADER_LOCALS_EXT" }, + { 0x87CA, "GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT" }, + { 0x87CB, "GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT" }, + { 0x87CC, "GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT" }, + { 0x87CD, "GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT" }, + { 0x87CE, "GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT" }, + { 0x87CF, "GL_VERTEX_SHADER_INSTRUCTIONS_EXT" }, + { 0x87D0, "GL_VERTEX_SHADER_VARIANTS_EXT" }, + { 0x87D1, "GL_VERTEX_SHADER_INVARIANTS_EXT" }, + { 0x87D2, "GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT" }, + { 0x87D3, "GL_VERTEX_SHADER_LOCALS_EXT" }, + { 0x87D4, "GL_VERTEX_SHADER_OPTIMIZED_EXT" }, + { 0x87D5, "GL_X_EXT" }, + { 0x87D6, "GL_Y_EXT" }, + { 0x87D7, "GL_Z_EXT" }, + { 0x87D8, "GL_W_EXT" }, + { 0x87D9, "GL_NEGATIVE_X_EXT" }, + { 0x87DA, "GL_NEGATIVE_Y_EXT" }, + { 0x87DB, "GL_NEGATIVE_Z_EXT" }, + { 0x87DC, "GL_NEGATIVE_W_EXT" }, + { 0x87DF, "GL_NEGATIVE_ONE_EXT" }, + { 0x87E0, "GL_NORMALIZED_RANGE_EXT" }, + { 0x87E1, "GL_FULL_RANGE_EXT" }, + { 0x87E2, "GL_CURRENT_VERTEX_EXT" }, + { 0x87E3, "GL_MVP_MATRIX_EXT" }, + { 0x87E4, "GL_VARIANT_VALUE_EXT" }, + { 0x87E5, "GL_VARIANT_DATATYPE_EXT" }, + { 0x87E6, "GL_VARIANT_ARRAY_STRIDE_EXT" }, + { 0x87E7, "GL_VARIANT_ARRAY_TYPE_EXT" }, + { 0x87E8, "GL_VARIANT_ARRAY_EXT" }, + { 0x87E9, "GL_VARIANT_ARRAY_POINTER_EXT" }, + { 0x87EA, "GL_INVARIANT_VALUE_EXT" }, + { 0x87EB, "GL_INVARIANT_DATATYPE_EXT" }, + { 0x87EC, "GL_LOCAL_CONSTANT_VALUE_EXT" }, + { 0x87Ed, "GL_LOCAL_CONSTANT_DATATYPE_EXT" }, + { 0x8800, "GL_STENCIL_BACK_FUNC_ATI" }, + { 0x8800, "GL_STENCIL_BACK_FUNC" }, + { 0x8801, "GL_STENCIL_BACK_FAIL_ATI" }, + { 0x8801, "GL_STENCIL_BACK_FAIL" }, + { 0x8802, "GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI" }, + { 0x8802, "GL_STENCIL_BACK_PASS_DEPTH_FAIL" }, + { 0x8803, "GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI" }, + { 0x8803, "GL_STENCIL_BACK_PASS_DEPTH_PASS" }, + { 0x8804, "GL_FRAGMENT_PROGRAM_ARB" }, + { 0x8805, "GL_PROGRAM_ALU_INSTRUCTIONS_ARB" }, + { 0x8806, "GL_PROGRAM_TEX_INSTRUCTIONS_ARB" }, + { 0x8807, "GL_PROGRAM_TEX_INDIRECTIONS_ARB" }, + { 0x8808, "GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB" }, + { 0x8809, "GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB" }, + { 0x880A, "GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB" }, + { 0x880B, "GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB" }, + { 0x880C, "GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB" }, + { 0x880D, "GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB" }, + { 0x880E, "GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB" }, + { 0x880F, "GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB" }, + { 0x8810, "GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB" }, + { 0x8814, "GL_RGBA_FLOAT32_APPLE" }, + { 0x8814, "GL_RGBA_FLOAT32_ATI" }, + { 0x8814, "GL_RGBA32F_ARB" }, + { 0x8815, "GL_RGB_FLOAT32_APPLE" }, + { 0x8815, "GL_RGB_FLOAT32_ATI" }, + { 0x8815, "GL_RGB32F_ARB" }, + { 0x8816, "GL_ALPHA_FLOAT32_APPLE" }, + { 0x8816, "GL_ALPHA_FLOAT32_ATI" }, + { 0x8816, "GL_ALPHA32F_ARB" }, + { 0x8817, "GL_INTENSITY_FLOAT32_APPLE" }, + { 0x8817, "GL_INTENSITY_FLOAT32_ATI" }, + { 0x8817, "GL_INTENSITY32F_ARB" }, + { 0x8818, "GL_LUMINANCE_FLOAT32_APPLE" }, + { 0x8818, "GL_LUMINANCE_FLOAT32_ATI" }, + { 0x8818, "GL_LUMINANCE32F_ARB" }, + { 0x8819, "GL_LUMINANCE_ALPHA_FLOAT32_APPLE" }, + { 0x8819, "GL_LUMINANCE_ALPHA_FLOAT32_ATI" }, + { 0x8819, "GL_LUMINANCE_ALPHA32F_ARB" }, + { 0x881A, "GL_RGBA_FLOAT16_APPLE" }, + { 0x881A, "GL_RGBA_FLOAT16_ATI" }, + { 0x881A, "GL_RGBA16F_ARB" }, + { 0x881B, "GL_RGB_FLOAT16_APPLE" }, + { 0x881B, "GL_RGB_FLOAT16_ATI" }, + { 0x881B, "GL_RGB16F_ARB" }, + { 0x881C, "GL_ALPHA_FLOAT16_APPLE" }, + { 0x881C, "GL_ALPHA_FLOAT16_ATI" }, + { 0x881C, "GL_ALPHA16F_ARB" }, + { 0x881D, "GL_INTENSITY_FLOAT16_APPLE" }, + { 0x881D, "GL_INTENSITY_FLOAT16_ATI" }, + { 0x881D, "GL_INTENSITY16F_ARB" }, + { 0x881E, "GL_LUMINANCE_FLOAT16_APPLE" }, + { 0x881E, "GL_LUMINANCE_FLOAT16_ATI" }, + { 0x881E, "GL_LUMINANCE16F_ARB" }, + { 0x881F, "GL_LUMINANCE_ALPHA_FLOAT16_APPLE" }, + { 0x881F, "GL_LUMINANCE_ALPHA_FLOAT16_ATI" }, + { 0x881F, "GL_LUMINANCE_ALPHA16F_ARB" }, + { 0x8820, "GL_RGBA_FLOAT_MODE_ARB" }, + { 0x8824, "GL_MAX_DRAW_BUFFERS_ARB" }, + { 0x8824, "GL_MAX_DRAW_BUFFERS" }, + { 0x8825, "GL_DRAW_BUFFER0_ARB" }, + { 0x8825, "GL_DRAW_BUFFER0" }, + { 0x8826, "GL_DRAW_BUFFER1_ARB" }, + { 0x8826, "GL_DRAW_BUFFER1" }, + { 0x8827, "GL_DRAW_BUFFER2_ARB" }, + { 0x8827, "GL_DRAW_BUFFER2" }, + { 0x8828, "GL_DRAW_BUFFER3_ARB" }, + { 0x8828, "GL_DRAW_BUFFER3" }, + { 0x8829, "GL_DRAW_BUFFER4_ARB" }, + { 0x8829, "GL_DRAW_BUFFER4" }, + { 0x882A, "GL_DRAW_BUFFER5_ARB" }, + { 0x882A, "GL_DRAW_BUFFER5" }, + { 0x882B, "GL_DRAW_BUFFER6_ARB" }, + { 0x882B, "GL_DRAW_BUFFER6" }, + { 0x882C, "GL_DRAW_BUFFER7_ARB" }, + { 0x882C, "GL_DRAW_BUFFER7" }, + { 0x882D, "GL_DRAW_BUFFER8_ARB" }, + { 0x882D, "GL_DRAW_BUFFER8" }, + { 0x882E, "GL_DRAW_BUFFER9_ARB" }, + { 0x882E, "GL_DRAW_BUFFER9" }, + { 0x882F, "GL_DRAW_BUFFER10_ARB" }, + { 0x882F, "GL_DRAW_BUFFER10" }, + { 0x8830, "GL_DRAW_BUFFER11_ARB" }, + { 0x8830, "GL_DRAW_BUFFER11" }, + { 0x8831, "GL_DRAW_BUFFER12_ARB" }, + { 0x8831, "GL_DRAW_BUFFER12" }, + { 0x8832, "GL_DRAW_BUFFER13_ARB" }, + { 0x8832, "GL_DRAW_BUFFER13" }, + { 0x8833, "GL_DRAW_BUFFER14_ARB" }, + { 0x8833, "GL_DRAW_BUFFER14" }, + { 0x8834, "GL_DRAW_BUFFER15_ARB" }, + { 0x8834, "GL_DRAW_BUFFER15" }, + { 0x883D, "GL_ALPHA_BLEND_EQUATION_ATI" }, + { 0x883D, "GL_BLEND_EQUATION_ALPHA_EXT" }, + { 0x883D, "GL_BLEND_EQUATION_ALPHA" }, + { 0x884A, "GL_TEXTURE_DEPTH_SIZE_ARB" }, + { 0x884A, "GL_TEXTURE_DEPTH_SIZE" }, + { 0x884B, "GL_DEPTH_TEXTURE_MODE_ARB" }, + { 0x884B, "GL_DEPTH_TEXTURE_MODE" }, + { 0x884C, "GL_TEXTURE_COMPARE_MODE_ARB" }, + { 0x884C, "GL_TEXTURE_COMPARE_MODE" }, + { 0x884D, "GL_TEXTURE_COMPARE_FUNC_ARB" }, + { 0x884D, "GL_TEXTURE_COMPARE_FUNC" }, + { 0x884E, "GL_COMPARE_R_TO_TEXTURE_ARB" }, + { 0x884E, "GL_COMPARE_R_TO_TEXTURE" }, + { 0x884E, "GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT" }, + { 0x8861, "GL_POINT_SPRITE_ARB" }, + { 0x8861, "GL_POINT_SPRITE" }, + { 0x8862, "GL_COORD_REPLACE_ARB" }, + { 0x8862, "GL_COORD_REPLACE" }, + { 0x8864, "GL_QUERY_COUNTER_BITS_ARB" }, + { 0x8864, "GL_QUERY_COUNTER_BITS" }, + { 0x8865, "GL_CURRENT_QUERY_ARB" }, + { 0x8865, "GL_CURRENT_QUERY" }, + { 0x8866, "GL_QUERY_RESULT_ARB" }, + { 0x8866, "GL_QUERY_RESULT" }, + { 0x8867, "GL_QUERY_RESULT_AVAILABLE_ARB" }, + { 0x8867, "GL_QUERY_RESULT_AVAILABLE" }, + { 0x8869, "GL_MAX_VERTEX_ATTRIBS_ARB" }, + { 0x8869, "GL_MAX_VERTEX_ATTRIBS_ARB" }, + { 0x8869, "GL_MAX_VERTEX_ATTRIBS" }, + { 0x886A, "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB" }, + { 0x886A, "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB" }, + { 0x886A, "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED" }, + { 0x8871, "GL_MAX_TEXTURE_COORDS_ARB" }, + { 0x8871, "GL_MAX_TEXTURE_COORDS_ARB" }, + { 0x8871, "GL_MAX_TEXTURE_COORDS_ARB" }, + { 0x8871, "GL_MAX_TEXTURE_COORDS" }, + { 0x8872, "GL_MAX_TEXTURE_IMAGE_UNITS_ARB" }, + { 0x8872, "GL_MAX_TEXTURE_IMAGE_UNITS_ARB" }, + { 0x8872, "GL_MAX_TEXTURE_IMAGE_UNITS_ARB" }, + { 0x8872, "GL_MAX_TEXTURE_IMAGE_UNITS" }, + { 0x8874, "GL_PROGRAM_ERROR_STRING_ARB" }, + { 0x8875, "GL_PROGRAM_FORMAT_ASCII_ARB" }, + { 0x8876, "GL_PROGRAM_FORMAT_ARB" }, + { 0x8890, "GL_DEPTH_BOUNDS_TEST_EXT" }, + { 0x8891, "GL_DEPTH_BOUNDS_EXT" }, + { 0x8892, "GL_ARRAY_BUFFER_ARB" }, + { 0x8892, "GL_ARRAY_BUFFER" }, + { 0x8893, "GL_ELEMENT_ARRAY_BUFFER_ARB" }, + { 0x8893, "GL_ELEMENT_ARRAY_BUFFER" }, + { 0x8894, "GL_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8894, "GL_ARRAY_BUFFER_BINDING" }, + { 0x8895, "GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8895, "GL_ELEMENT_ARRAY_BUFFER_BINDING" }, + { 0x8896, "GL_VERTEX_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8896, "GL_VERTEX_ARRAY_BUFFER_BINDING" }, + { 0x8897, "GL_NORMAL_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8897, "GL_NORMAL_ARRAY_BUFFER_BINDING" }, + { 0x8898, "GL_COLOR_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8898, "GL_COLOR_ARRAY_BUFFER_BINDING" }, + { 0x8899, "GL_INDEX_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8899, "GL_INDEX_ARRAY_BUFFER_BINDING" }, + { 0x889A, "GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889A, "GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING" }, + { 0x889B, "GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889B, "GL_EDGE_FLAG_ARRAY_BUFFER_BINDING" }, + { 0x889C, "GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889C, "GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING" }, + { 0x889D, "GL_FOG_COORD_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889D, "GL_FOG_COORD_ARRAY_BUFFER_BINDING" }, + { 0x889D, "GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889D, "GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING" }, + { 0x889E, "GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889E, "GL_WEIGHT_ARRAY_BUFFER_BINDING" }, + { 0x889F, "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889F, "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING" }, + { 0x88A0, "GL_PROGRAM_INSTRUCTIONS_ARB" }, + { 0x88A1, "GL_MAX_PROGRAM_INSTRUCTIONS_ARB" }, + { 0x88A2, "GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB" }, + { 0x88A3, "GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB" }, + { 0x88A4, "GL_PROGRAM_TEMPORARIES_ARB" }, + { 0x88A5, "GL_MAX_PROGRAM_TEMPORARIES_ARB" }, + { 0x88A6, "GL_PROGRAM_NATIVE_TEMPORARIES_ARB" }, + { 0x88A7, "GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB" }, + { 0x88A8, "GL_PROGRAM_PARAMETERS_ARB" }, + { 0x88A9, "GL_MAX_PROGRAM_PARAMETERS_ARB" }, + { 0x88AA, "GL_PROGRAM_NATIVE_PARAMETERS_ARB" }, + { 0x88AB, "GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB" }, + { 0x88AC, "GL_PROGRAM_ATTRIBS_ARB" }, + { 0x88AD, "GL_MAX_PROGRAM_ATTRIBS_ARB" }, + { 0x88AE, "GL_PROGRAM_NATIVE_ATTRIBS_ARB" }, + { 0x88AF, "GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB" }, + { 0x88B0, "GL_PROGRAM_ADDRESS_REGISTERS_ARB" }, + { 0x88B1, "GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB" }, + { 0x88B2, "GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB" }, + { 0x88B3, "GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB" }, + { 0x88B4, "GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB" }, + { 0x88B5, "GL_MAX_PROGRAM_ENV_PARAMETERS_ARB" }, + { 0x88B6, "GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB" }, + { 0x88B7, "GL_TRANSPOSE_CURRENT_MATRIX_ARB" }, + { 0x88B8, "GL_READ_ONLY_ARB" }, + { 0x88B8, "GL_READ_ONLY" }, + { 0x88B9, "GL_WRITE_ONLY_ARB" }, + { 0x88B9, "GL_WRITE_ONLY" }, + { 0x88BA, "GL_READ_WRITE_ARB" }, + { 0x88BA, "GL_READ_WRITE" }, + { 0x88BB, "GL_BUFFER_ACCESS_ARB" }, + { 0x88BB, "GL_BUFFER_ACCESS" }, + { 0x88BC, "GL_BUFFER_MAPPED_ARB" }, + { 0x88BC, "GL_BUFFER_MAPPED" }, + { 0x88BD, "GL_BUFFER_MAP_POINTER_ARB" }, + { 0x88BD, "GL_BUFFER_MAP_POINTER" }, + { 0x88C0, "GL_MATRIX0_ARB" }, + { 0x88C1, "GL_MATRIX1_ARB" }, + { 0x88C2, "GL_MATRIX2_ARB" }, + { 0x88C3, "GL_MATRIX3_ARB" }, + { 0x88C4, "GL_MATRIX4_ARB" }, + { 0x88C5, "GL_MATRIX5_ARB" }, + { 0x88C6, "GL_MATRIX6_ARB" }, + { 0x88C7, "GL_MATRIX7_ARB" }, + { 0x88C8, "GL_MATRIX8_ARB" }, + { 0x88C9, "GL_MATRIX9_ARB" }, + { 0x88CA, "GL_MATRIX10_ARB" }, + { 0x88CB, "GL_MATRIX11_ARB" }, + { 0x88CC, "GL_MATRIX12_ARB" }, + { 0x88CD, "GL_MATRIX13_ARB" }, + { 0x88CE, "GL_MATRIX14_ARB" }, + { 0x88CF, "GL_MATRIX15_ARB" }, + { 0x88D0, "GL_MATRIX16_ARB" }, + { 0x88D1, "GL_MATRIX17_ARB" }, + { 0x88D2, "GL_MATRIX18_ARB" }, + { 0x88D3, "GL_MATRIX19_ARB" }, + { 0x88D4, "GL_MATRIX20_ARB" }, + { 0x88D5, "GL_MATRIX21_ARB" }, + { 0x88D6, "GL_MATRIX22_ARB" }, + { 0x88D7, "GL_MATRIX23_ARB" }, + { 0x88D8, "GL_MATRIX24_ARB" }, + { 0x88D9, "GL_MATRIX25_ARB" }, + { 0x88DA, "GL_MATRIX26_ARB" }, + { 0x88DB, "GL_MATRIX27_ARB" }, + { 0x88DC, "GL_MATRIX28_ARB" }, + { 0x88DD, "GL_MATRIX29_ARB" }, + { 0x88DE, "GL_MATRIX30_ARB" }, + { 0x88DF, "GL_MATRIX31_ARB" }, + { 0x88E0, "GL_STREAM_DRAW_ARB" }, + { 0x88E0, "GL_STREAM_DRAW" }, + { 0x88E1, "GL_STREAM_READ_ARB" }, + { 0x88E1, "GL_STREAM_READ" }, + { 0x88E2, "GL_STREAM_COPY_ARB" }, + { 0x88E2, "GL_STREAM_COPY" }, + { 0x88E4, "GL_STATIC_DRAW_ARB" }, + { 0x88E4, "GL_STATIC_DRAW" }, + { 0x88E5, "GL_STATIC_READ_ARB" }, + { 0x88E5, "GL_STATIC_READ" }, + { 0x88E6, "GL_STATIC_COPY_ARB" }, + { 0x88E6, "GL_STATIC_COPY" }, + { 0x88E8, "GL_DYNAMIC_DRAW_ARB" }, + { 0x88E8, "GL_DYNAMIC_DRAW" }, + { 0x88E9, "GL_DYNAMIC_READ_ARB" }, + { 0x88E9, "GL_DYNAMIC_READ" }, + { 0x88EA, "GL_DYNAMIC_COPY_ARB" }, + { 0x88EA, "GL_DYNAMIC_COPY" }, + { 0x88EB, "GL_PIXEL_PACK_BUFFER_ARB" }, + { 0x88EB, "GL_PIXEL_PACK_BUFFER" }, + { 0x88EC, "GL_PIXEL_UNPACK_BUFFER_ARB" }, + { 0x88EC, "GL_PIXEL_UNPACK_BUFFER" }, + { 0x88ED, "GL_PIXEL_PACK_BUFFER_BINDING_ARB" }, + { 0x88ED, "GL_PIXEL_PACK_BUFFER_BINDING" }, + { 0x88EF, "GL_PIXEL_UNPACK_BUFFER_BINDING_ARB" }, + { 0x88EF, "GL_PIXEL_UNPACK_BUFFER_BINDING" }, + { 0x88F0, "GL_DEPTH24_STENCIL8_EXT" }, + { 0x88F0, "GL_DEPTH24_STENCIL8" }, + { 0x88F1, "GL_TEXTURE_STENCIL_SIZE_EXT" }, + { 0x88F1, "GL_TEXTURE_STENCIL_SIZE" }, + { 0x88FD, "GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT" }, + { 0x88FE, "GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB" }, + { 0x88FF, "GL_MAX_ARRAY_TEXTURE_LAYERS_EXT" }, + { 0x8904, "GL_MIN_PROGRAM_TEXEL_OFFSET_EXT" }, + { 0x8905, "GL_MAX_PROGRAM_TEXEL_OFFSET_EXT" }, + { 0x8910, "GL_STENCIL_TEST_TWO_SIDE_EXT" }, + { 0x8911, "GL_ACTIVE_STENCIL_FACE_EXT" }, + { 0x8912, "GL_MIRROR_CLAMP_TO_BORDER_EXT" }, + { 0x8914, "GL_SAMPLES_PASSED_ARB" }, + { 0x8914, "GL_SAMPLES_PASSED" }, + { 0x891A, "GL_CLAMP_VERTEX_COLOR_ARB" }, + { 0x891B, "GL_CLAMP_FRAGMENT_COLOR_ARB" }, + { 0x891C, "GL_CLAMP_READ_COLOR_ARB" }, + { 0x891D, "GL_FIXED_ONLY_ARB" }, + { 0x8920, "GL_FRAGMENT_SHADER_EXT" }, + { 0x896D, "GL_SECONDARY_INTERPOLATOR_EXT" }, + { 0x896E, "GL_NUM_FRAGMENT_REGISTERS_EXT" }, + { 0x896F, "GL_NUM_FRAGMENT_CONSTANTS_EXT" }, + { 0x8A0C, "GL_ELEMENT_ARRAY_APPLE" }, + { 0x8A0D, "GL_ELEMENT_ARRAY_TYPE_APPLE" }, + { 0x8A0E, "GL_ELEMENT_ARRAY_POINTER_APPLE" }, + { 0x8A0F, "GL_COLOR_FLOAT_APPLE" }, + { 0x8A11, "GL_UNIFORM_BUFFER" }, + { 0x8A28, "GL_UNIFORM_BUFFER_BINDING" }, + { 0x8A29, "GL_UNIFORM_BUFFER_START" }, + { 0x8A2A, "GL_UNIFORM_BUFFER_SIZE" }, + { 0x8A2B, "GL_MAX_VERTEX_UNIFORM_BLOCKS" }, + { 0x8A2C, "GL_MAX_GEOMETRY_UNIFORM_BLOCKS" }, + { 0x8A2D, "GL_MAX_FRAGMENT_UNIFORM_BLOCKS" }, + { 0x8A2E, "GL_MAX_COMBINED_UNIFORM_BLOCKS" }, + { 0x8A2F, "GL_MAX_UNIFORM_BUFFER_BINDINGS" }, + { 0x8A30, "GL_MAX_UNIFORM_BLOCK_SIZE" }, + { 0x8A31, "GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS" }, + { 0x8A32, "GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS" }, + { 0x8A33, "GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS" }, + { 0x8A34, "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT" }, + { 0x8A35, "GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH" }, + { 0x8A36, "GL_ACTIVE_UNIFORM_BLOCKS" }, + { 0x8A37, "GL_UNIFORM_TYPE" }, + { 0x8A38, "GL_UNIFORM_SIZE" }, + { 0x8A39, "GL_UNIFORM_NAME_LENGTH" }, + { 0x8A3A, "GL_UNIFORM_BLOCK_INDEX" }, + { 0x8A3B, "GL_UNIFORM_OFFSET" }, + { 0x8A3C, "GL_UNIFORM_ARRAY_STRIDE" }, + { 0x8A3D, "GL_UNIFORM_MATRIX_STRIDE" }, + { 0x8A3E, "GL_UNIFORM_IS_ROW_MAJOR" }, + { 0x8A3F, "GL_UNIFORM_BLOCK_BINDING" }, + { 0x8A40, "GL_UNIFORM_BLOCK_DATA_SIZE" }, + { 0x8A41, "GL_UNIFORM_BLOCK_NAME_LENGTH" }, + { 0x8A42, "GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS" }, + { 0x8A43, "GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES" }, + { 0x8A44, "GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER" }, + { 0x8A45, "GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER" }, + { 0x8A46, "GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER" }, + { 0x8B30, "GL_FRAGMENT_SHADER_ARB" }, + { 0x8B30, "GL_FRAGMENT_SHADER" }, + { 0x8B31, "GL_VERTEX_SHADER_ARB" }, + { 0x8B31, "GL_VERTEX_SHADER" }, + { 0x8B40, "GL_PROGRAM_OBJECT_ARB" }, + { 0x8B48, "GL_SHADER_OBJECT_ARB" }, + { 0x8B49, "GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB" }, + { 0x8B49, "GL_MAX_FRAGMENT_UNIFORM_COMPONENTS" }, + { 0x8B4A, "GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB" }, + { 0x8B4A, "GL_MAX_VERTEX_UNIFORM_COMPONENTS" }, + { 0x8B4B, "GL_MAX_VARYING_COMPONENTS_EXT" }, + { 0x8B4B, "GL_MAX_VARYING_FLOATS_ARB" }, + { 0x8B4B, "GL_MAX_VARYING_FLOATS" }, + { 0x8B4C, "GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB" }, + { 0x8B4C, "GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS" }, + { 0x8B4D, "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB" }, + { 0x8B4D, "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS" }, + { 0x8B4E, "GL_OBJECT_TYPE_ARB" }, + { 0x8B4F, "GL_OBJECT_SUBTYPE_ARB" }, + { 0x8B4F, "GL_SHADER_TYPE" }, + { 0x8B50, "GL_FLOAT_VEC2_ARB" }, + { 0x8B50, "GL_FLOAT_VEC2" }, + { 0x8B51, "GL_FLOAT_VEC3_ARB" }, + { 0x8B51, "GL_FLOAT_VEC3" }, + { 0x8B52, "GL_FLOAT_VEC4_ARB" }, + { 0x8B52, "GL_FLOAT_VEC4" }, + { 0x8B53, "GL_INT_VEC2_ARB" }, + { 0x8B53, "GL_INT_VEC2" }, + { 0x8B54, "GL_INT_VEC3_ARB" }, + { 0x8B54, "GL_INT_VEC3" }, + { 0x8B55, "GL_INT_VEC4_ARB" }, + { 0x8B55, "GL_INT_VEC4" }, + { 0x8B56, "GL_BOOL_ARB" }, + { 0x8B56, "GL_BOOL" }, + { 0x8B57, "GL_BOOL_VEC2_ARB" }, + { 0x8B57, "GL_BOOL_VEC2" }, + { 0x8B58, "GL_BOOL_VEC3_ARB" }, + { 0x8B58, "GL_BOOL_VEC3" }, + { 0x8B59, "GL_BOOL_VEC4_ARB" }, + { 0x8B59, "GL_BOOL_VEC4" }, + { 0x8B5A, "GL_FLOAT_MAT2_ARB" }, + { 0x8B5A, "GL_FLOAT_MAT2" }, + { 0x8B5B, "GL_FLOAT_MAT3_ARB" }, + { 0x8B5B, "GL_FLOAT_MAT3" }, + { 0x8B5C, "GL_FLOAT_MAT4_ARB" }, + { 0x8B5C, "GL_FLOAT_MAT4" }, + { 0x8B5D, "GL_SAMPLER_1D_ARB" }, + { 0x8B5D, "GL_SAMPLER_1D" }, + { 0x8B5E, "GL_SAMPLER_2D_ARB" }, + { 0x8B5E, "GL_SAMPLER_2D" }, + { 0x8B5F, "GL_SAMPLER_3D_ARB" }, + { 0x8B5F, "GL_SAMPLER_3D" }, + { 0x8B60, "GL_SAMPLER_CUBE_ARB" }, + { 0x8B60, "GL_SAMPLER_CUBE" }, + { 0x8B61, "GL_SAMPLER_1D_SHADOW_ARB" }, + { 0x8B61, "GL_SAMPLER_1D_SHADOW" }, + { 0x8B62, "GL_SAMPLER_2D_SHADOW_ARB" }, + { 0x8B62, "GL_SAMPLER_2D_SHADOW" }, + { 0x8B63, "GL_SAMPLER_2D_RECT_ARB" }, + { 0x8B64, "GL_SAMPLER_2D_RECT_SHADOW_ARB" }, + { 0x8B65, "GL_FLOAT_MAT2x3" }, + { 0x8B66, "GL_FLOAT_MAT2x4" }, + { 0x8B67, "GL_FLOAT_MAT3x2" }, + { 0x8B68, "GL_FLOAT_MAT3x4" }, + { 0x8B69, "GL_FLOAT_MAT4x2" }, + { 0x8B6A, "GL_FLOAT_MAT4x3" }, + { 0x8B80, "GL_DELETE_STATUS" }, + { 0x8B80, "GL_OBJECT_DELETE_STATUS_ARB" }, + { 0x8B81, "GL_COMPILE_STATUS" }, + { 0x8B81, "GL_OBJECT_COMPILE_STATUS_ARB" }, + { 0x8B82, "GL_LINK_STATUS" }, + { 0x8B82, "GL_OBJECT_LINK_STATUS_ARB" }, + { 0x8B83, "GL_OBJECT_VALIDATE_STATUS_ARB" }, + { 0x8B83, "GL_VALIDATE_STATUS" }, + { 0x8B84, "GL_INFO_LOG_LENGTH" }, + { 0x8B84, "GL_OBJECT_INFO_LOG_LENGTH_ARB" }, + { 0x8B85, "GL_ATTACHED_SHADERS" }, + { 0x8B85, "GL_OBJECT_ATTACHED_OBJECTS_ARB" }, + { 0x8B86, "GL_ACTIVE_UNIFORMS" }, + { 0x8B86, "GL_OBJECT_ACTIVE_UNIFORMS_ARB" }, + { 0x8B87, "GL_ACTIVE_UNIFORM_MAX_LENGTH" }, + { 0x8B87, "GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB" }, + { 0x8B88, "GL_OBJECT_SHADER_SOURCE_LENGTH_ARB" }, + { 0x8B88, "GL_SHADER_SOURCE_LENGTH" }, + { 0x8B89, "GL_ACTIVE_ATTRIBUTES" }, + { 0x8B89, "GL_OBJECT_ACTIVE_ATTRIBUTES_ARB" }, + { 0x8B8A, "GL_ACTIVE_ATTRIBUTE_MAX_LENGTH" }, + { 0x8B8A, "GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB" }, + { 0x8B8B, "GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB" }, + { 0x8B8B, "GL_FRAGMENT_SHADER_DERIVATIVE_HINT" }, + { 0x8B8C, "GL_SHADING_LANGUAGE_VERSION_ARB" }, + { 0x8B8C, "GL_SHADING_LANGUAGE_VERSION" }, + { 0x8B8D, "GL_CURRENT_PROGRAM" }, + { 0x8C10, "GL_TEXTURE_RED_TYPE_ARB" }, + { 0x8C10, "GL_TEXTURE_RED_TYPE" }, + { 0x8C11, "GL_TEXTURE_GREEN_TYPE_ARB" }, + { 0x8C11, "GL_TEXTURE_GREEN_TYPE" }, + { 0x8C12, "GL_TEXTURE_BLUE_TYPE_ARB" }, + { 0x8C12, "GL_TEXTURE_BLUE_TYPE" }, + { 0x8C13, "GL_TEXTURE_ALPHA_TYPE_ARB" }, + { 0x8C13, "GL_TEXTURE_ALPHA_TYPE" }, + { 0x8C14, "GL_TEXTURE_LUMINANCE_TYPE_ARB" }, + { 0x8C15, "GL_TEXTURE_INTENSITY_TYPE_ARB" }, + { 0x8C16, "GL_TEXTURE_DEPTH_TYPE_ARB" }, + { 0x8C16, "GL_TEXTURE_DEPTH_TYPE" }, + { 0x8C17, "GL_UNSIGNED_NORMALIZED_ARB" }, + { 0x8C17, "GL_UNSIGNED_NORMALIZED" }, + { 0x8C18, "GL_TEXTURE_1D_ARRAY_EXT" }, + { 0x8C19, "GL_PROXY_TEXTURE_1D_ARRAY_EXT" }, + { 0x8C1A, "GL_TEXTURE_2D_ARRAY_EXT" }, + { 0x8C1B, "GL_PROXY_TEXTURE_2D_ARRAY_EXT" }, + { 0x8C1C, "GL_TEXTURE_BINDING_1D_ARRAY_EXT" }, + { 0x8C1D, "GL_TEXTURE_BINDING_2D_ARRAY_EXT" }, + { 0x8C29, "GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT" }, + { 0x8C3A, "GL_R11F_G11F_B10F_EXT" }, + { 0x8C3B, "GL_UNSIGNED_INT_10F_11F_11F_REV_EXT" }, + { 0x8C3C, "GL_RGBA_SIGNED_COMPONENTS_EXT" }, + { 0x8C3D, "GL_RGB9_E5_EXT" }, + { 0x8C3E, "GL_UNSIGNED_INT_5_9_9_9_REV_EXT" }, + { 0x8C3F, "GL_TEXTURE_SHARED_SIZE_EXT" }, + { 0x8C40, "GL_SRGB_EXT" }, + { 0x8C40, "GL_SRGB" }, + { 0x8C41, "GL_SRGB8_EXT" }, + { 0x8C41, "GL_SRGB8" }, + { 0x8C42, "GL_SRGB_ALPHA_EXT" }, + { 0x8C42, "GL_SRGB_ALPHA" }, + { 0x8C43, "GL_SRGB8_ALPHA8_EXT" }, + { 0x8C43, "GL_SRGB8_ALPHA8" }, + { 0x8C44, "GL_SLUMINANCE_ALPHA_EXT" }, + { 0x8C44, "GL_SLUMINANCE_ALPHA" }, + { 0x8C45, "GL_SLUMINANCE8_ALPHA8_EXT" }, + { 0x8C45, "GL_SLUMINANCE8_ALPHA8" }, + { 0x8C46, "GL_SLUMINANCE_EXT" }, + { 0x8C46, "GL_SLUMINANCE" }, + { 0x8C47, "GL_SLUMINANCE8_EXT" }, + { 0x8C47, "GL_SLUMINANCE8" }, + { 0x8C48, "GL_COMPRESSED_SRGB_EXT" }, + { 0x8C48, "GL_COMPRESSED_SRGB" }, + { 0x8C49, "GL_COMPRESSED_SRGB_ALPHA_EXT" }, + { 0x8C49, "GL_COMPRESSED_SRGB_ALPHA" }, + { 0x8C4A, "GL_COMPRESSED_SLUMINANCE_EXT" }, + { 0x8C4A, "GL_COMPRESSED_SLUMINANCE" }, + { 0x8C4B, "GL_COMPRESSED_SLUMINANCE_ALPHA_EXT" }, + { 0x8C4B, "GL_COMPRESSED_SLUMINANCE_ALPHA" }, + { 0x8C4C, "GL_COMPRESSED_SRGB_S3TC_DXT1_EXT" }, + { 0x8C4D, "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT" }, + { 0x8C4E, "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT" }, + { 0x8C4F, "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT" }, + { 0x8C76, "GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT" }, + { 0x8C7F, "GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT" }, + { 0x8C80, "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT" }, + { 0x8C83, "GL_TRANSFORM_FEEDBACK_VARYINGS_EXT" }, + { 0x8C84, "GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT" }, + { 0x8C85, "GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT" }, + { 0x8C87, "GL_PRIMITIVES_GENERATED_EXT" }, + { 0x8C88, "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT" }, + { 0x8C89, "GL_RASTERIZER_DISCARD_EXT" }, + { 0x8C8A, "GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT" }, + { 0x8C8B, "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT" }, + { 0x8C8C, "GL_INTERLEAVED_ATTRIBS_EXT" }, + { 0x8C8D, "GL_SEPARATE_ATTRIBS_EXT" }, + { 0x8C8E, "GL_TRANSFORM_FEEDBACK_BUFFER_EXT" }, + { 0x8C8F, "GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT" }, + { 0x8CA0, "GL_POINT_SPRITE_COORD_ORIGIN" }, + { 0x8CA1, "GL_LOWER_LEFT" }, + { 0x8CA2, "GL_UPPER_LEFT" }, + { 0x8CA3, "GL_STENCIL_BACK_REF" }, + { 0x8CA4, "GL_STENCIL_BACK_VALUE_MASK" }, + { 0x8CA5, "GL_STENCIL_BACK_WRITEMASK" }, + { 0x8CA6, "GL_DRAW_FRAMEBUFFER_BINDING_EXT" }, + { 0x8CA6, "GL_FRAMEBUFFER_BINDING_EXT" }, + { 0x8CA6, "GL_FRAMEBUFFER_BINDING" }, + { 0x8CA7, "GL_RENDERBUFFER_BINDING_EXT" }, + { 0x8CA7, "GL_RENDERBUFFER_BINDING" }, + { 0x8CA8, "GL_READ_FRAMEBUFFER_EXT" }, + { 0x8CA8, "GL_READ_FRAMEBUFFER" }, + { 0x8CA9, "GL_DRAW_FRAMEBUFFER_EXT" }, + { 0x8CA9, "GL_DRAW_FRAMEBUFFER" }, + { 0x8CAA, "GL_READ_FRAMEBUFFER_BINDING_EXT" }, + { 0x8CAA, "GL_READ_FRAMEBUFFER_BINDING" }, + { 0x8CAB, "GL_RENDERBUFFER_SAMPLES_EXT" }, + { 0x8CAB, "GL_RENDERBUFFER_SAMPLES" }, + { 0x8CAC, "GL_DEPTH_COMPONENT32F" }, + { 0x8CAD, "GL_DEPTH32F_STENCIL8" }, + { 0x8CD0, "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT" }, + { 0x8CD0, "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE" }, + { 0x8CD1, "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT" }, + { 0x8CD1, "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME" }, + { 0x8CD2, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT" }, + { 0x8CD2, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL" }, + { 0x8CD3, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT" }, + { 0x8CD3, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE" }, + { 0x8CD4, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT" }, + { 0x8CD4, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT" }, + { 0x8CD4, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER" }, + { 0x8CD5, "GL_FRAMEBUFFER_COMPLETE_EXT" }, + { 0x8CD5, "GL_FRAMEBUFFER_COMPLETE" }, + { 0x8CD6, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT" }, + { 0x8CD6, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT" }, + { 0x8CD7, "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT" }, + { 0x8CD7, "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT" }, + { 0x8CD9, "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT" }, + { 0x8CDA, "GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT" }, + { 0x8CDB, "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT" }, + { 0x8CDB, "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER" }, + { 0x8CDC, "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT" }, + { 0x8CDC, "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER" }, + { 0x8CDD, "GL_FRAMEBUFFER_UNSUPPORTED_EXT" }, + { 0x8CDD, "GL_FRAMEBUFFER_UNSUPPORTED" }, + { 0x8CDF, "GL_MAX_COLOR_ATTACHMENTS_EXT" }, + { 0x8CDF, "GL_MAX_COLOR_ATTACHMENTS" }, + { 0x8CE0, "GL_COLOR_ATTACHMENT0_EXT" }, + { 0x8CE0, "GL_COLOR_ATTACHMENT0" }, + { 0x8CE1, "GL_COLOR_ATTACHMENT1_EXT" }, + { 0x8CE1, "GL_COLOR_ATTACHMENT1" }, + { 0x8CE2, "GL_COLOR_ATTACHMENT2_EXT" }, + { 0x8CE2, "GL_COLOR_ATTACHMENT2" }, + { 0x8CE3, "GL_COLOR_ATTACHMENT3_EXT" }, + { 0x8CE3, "GL_COLOR_ATTACHMENT3" }, + { 0x8CE4, "GL_COLOR_ATTACHMENT4_EXT" }, + { 0x8CE4, "GL_COLOR_ATTACHMENT4" }, + { 0x8CE5, "GL_COLOR_ATTACHMENT5_EXT" }, + { 0x8CE5, "GL_COLOR_ATTACHMENT5" }, + { 0x8CE6, "GL_COLOR_ATTACHMENT6_EXT" }, + { 0x8CE6, "GL_COLOR_ATTACHMENT6" }, + { 0x8CE7, "GL_COLOR_ATTACHMENT7_EXT" }, + { 0x8CE7, "GL_COLOR_ATTACHMENT7" }, + { 0x8CE8, "GL_COLOR_ATTACHMENT8_EXT" }, + { 0x8CE8, "GL_COLOR_ATTACHMENT8" }, + { 0x8CE9, "GL_COLOR_ATTACHMENT9_EXT" }, + { 0x8CE9, "GL_COLOR_ATTACHMENT9" }, + { 0x8CEA, "GL_COLOR_ATTACHMENT10_EXT" }, + { 0x8CEA, "GL_COLOR_ATTACHMENT10" }, + { 0x8CEB, "GL_COLOR_ATTACHMENT11_EXT" }, + { 0x8CEB, "GL_COLOR_ATTACHMENT11" }, + { 0x8CEC, "GL_COLOR_ATTACHMENT12_EXT" }, + { 0x8CEC, "GL_COLOR_ATTACHMENT12" }, + { 0x8CED, "GL_COLOR_ATTACHMENT13_EXT" }, + { 0x8CED, "GL_COLOR_ATTACHMENT13" }, + { 0x8CEE, "GL_COLOR_ATTACHMENT14_EXT" }, + { 0x8CEE, "GL_COLOR_ATTACHMENT14" }, + { 0x8CEF, "GL_COLOR_ATTACHMENT15_EXT" }, + { 0x8CEF, "GL_COLOR_ATTACHMENT15" }, + { 0x8D00, "GL_DEPTH_ATTACHMENT_EXT" }, + { 0x8D00, "GL_DEPTH_ATTACHMENT" }, + { 0x8D20, "GL_STENCIL_ATTACHMENT_EXT" }, + { 0x8D20, "GL_STENCIL_ATTACHMENT" }, + { 0x8D40, "GL_FRAMEBUFFER_EXT" }, + { 0x8D40, "GL_FRAMEBUFFER" }, + { 0x8D41, "GL_RENDERBUFFER_EXT" }, + { 0x8D41, "GL_RENDERBUFFER" }, + { 0x8D42, "GL_RENDERBUFFER_WIDTH_EXT" }, + { 0x8D42, "GL_RENDERBUFFER_WIDTH" }, + { 0x8D43, "GL_RENDERBUFFER_HEIGHT_EXT" }, + { 0x8D43, "GL_RENDERBUFFER_HEIGHT" }, + { 0x8D44, "GL_RENDERBUFFER_INTERNAL_FORMAT_EXT" }, + { 0x8D44, "GL_RENDERBUFFER_INTERNAL_FORMAT" }, + { 0x8D46, "GL_STENCIL_INDEX1_EXT" }, + { 0x8D46, "GL_STENCIL_INDEX1" }, + { 0x8D47, "GL_STENCIL_INDEX4_EXT" }, + { 0x8D47, "GL_STENCIL_INDEX4" }, + { 0x8D48, "GL_STENCIL_INDEX8_EXT" }, + { 0x8D48, "GL_STENCIL_INDEX8" }, + { 0x8D49, "GL_STENCIL_INDEX16_EXT" }, + { 0x8D49, "GL_STENCIL_INDEX16" }, + { 0x8D50, "GL_RENDERBUFFER_RED_SIZE_EXT" }, + { 0x8D50, "GL_RENDERBUFFER_RED_SIZE" }, + { 0x8D51, "GL_RENDERBUFFER_GREEN_SIZE_EXT" }, + { 0x8D51, "GL_RENDERBUFFER_GREEN_SIZE" }, + { 0x8D52, "GL_RENDERBUFFER_BLUE_SIZE_EXT" }, + { 0x8D52, "GL_RENDERBUFFER_BLUE_SIZE" }, + { 0x8D53, "GL_RENDERBUFFER_ALPHA_SIZE_EXT" }, + { 0x8D53, "GL_RENDERBUFFER_ALPHA_SIZE" }, + { 0x8D54, "GL_RENDERBUFFER_DEPTH_SIZE_EXT" }, + { 0x8D54, "GL_RENDERBUFFER_DEPTH_SIZE" }, + { 0x8D55, "GL_RENDERBUFFER_STENCIL_SIZE_EXT" }, + { 0x8D55, "GL_RENDERBUFFER_STENCIL_SIZE" }, + { 0x8D56, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT" }, + { 0x8D56, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE" }, + { 0x8D57, "GL_MAX_SAMPLES_EXT" }, + { 0x8D57, "GL_MAX_SAMPLES" }, + { 0x8D70, "GL_RGBA32UI_EXT" }, + { 0x8D71, "GL_RGB32UI_EXT" }, + { 0x8D72, "GL_ALPHA32UI_EXT" }, + { 0x8D73, "GL_INTENSITY32UI_EXT" }, + { 0x8D74, "GL_LUMINANCE32UI_EXT" }, + { 0x8D75, "GL_LUMINANCE_ALPHA32UI_EXT" }, + { 0x8D76, "GL_RGBA16UI_EXT" }, + { 0x8D77, "GL_RGB16UI_EXT" }, + { 0x8D78, "GL_ALPHA16UI_EXT" }, + { 0x8D79, "GL_INTENSITY16UI_EXT" }, + { 0x8D7A, "GL_LUMINANCE16UI_EXT" }, + { 0x8D7B, "GL_LUMINANCE_ALPHA16UI_EXT" }, + { 0x8D7C, "GL_RGBA8UI_EXT" }, + { 0x8D7D, "GL_RGB8UI_EXT" }, + { 0x8D7E, "GL_ALPHA8UI_EXT" }, + { 0x8D7F, "GL_INTENSITY8UI_EXT" }, + { 0x8D80, "GL_LUMINANCE8UI_EXT" }, + { 0x8D81, "GL_LUMINANCE_ALPHA8UI_EXT" }, + { 0x8D82, "GL_RGBA32I_EXT" }, + { 0x8D83, "GL_RGB32I_EXT" }, + { 0x8D84, "GL_ALPHA32I_EXT" }, + { 0x8D85, "GL_INTENSITY32I_EXT" }, + { 0x8D86, "GL_LUMINANCE32I_EXT" }, + { 0x8D87, "GL_LUMINANCE_ALPHA32I_EXT" }, + { 0x8D88, "GL_RGBA16I_EXT" }, + { 0x8D89, "GL_RGB16I_EXT" }, + { 0x8D8A, "GL_ALPHA16I_EXT" }, + { 0x8D8B, "GL_INTENSITY16I_EXT" }, + { 0x8D8C, "GL_LUMINANCE16I_EXT" }, + { 0x8D8D, "GL_LUMINANCE_ALPHA16I_EXT" }, + { 0x8D8E, "GL_RGBA8I_EXT" }, + { 0x8D8F, "GL_RGB8I_EXT" }, + { 0x8D90, "GL_ALPHA8I_EXT" }, + { 0x8D91, "GL_INTENSITY8I_EXT" }, + { 0x8D92, "GL_LUMINANCE8I_EXT" }, + { 0x8D93, "GL_LUMINANCE_ALPHA8I_EXT" }, + { 0x8D94, "GL_RED_INTEGER_EXT" }, + { 0x8D95, "GL_GREEN_INTEGER_EXT" }, + { 0x8D96, "GL_BLUE_INTEGER_EXT" }, + { 0x8D97, "GL_ALPHA_INTEGER_EXT" }, + { 0x8D98, "GL_RGB_INTEGER_EXT" }, + { 0x8D99, "GL_RGBA_INTEGER_EXT" }, + { 0x8D9A, "GL_BGR_INTEGER_EXT" }, + { 0x8D9B, "GL_BGRA_INTEGER_EXT" }, + { 0x8D9C, "GL_LUMINANCE_INTEGER_EXT" }, + { 0x8D9D, "GL_LUMINANCE_ALPHA_INTEGER_EXT" }, + { 0x8D9E, "GL_RGBA_INTEGER_MODE_EXT" }, + { 0x8DA7, "GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT" }, + { 0x8DA8, "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT" }, + { 0x8DA9, "GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT" }, + { 0x8DAD, "GL_FLOAT_32_UNSIGNED_INT_24_8_REV" }, + { 0x8DB9, "GL_FRAMEBUFFER_SRGB_EXT" }, + { 0x8DBA, "GL_FRAMEBUFFER_SRGB_CAPABLE_EXT" }, + { 0x8DBB, "GL_COMPRESSED_RED_RGTC1" }, + { 0x8DBC, "GL_COMPRESSED_SIGNED_RED_RGTC1" }, + { 0x8DBD, "GL_COMPRESSED_RG_RGTC2" }, + { 0x8DBE, "GL_COMPRESSED_SIGNED_RG_RGTC2" }, + { 0x8DC0, "GL_SAMPLER_1D_ARRAY_EXT" }, + { 0x8DC1, "GL_SAMPLER_2D_ARRAY_EXT" }, + { 0x8DC2, "GL_SAMPLER_BUFFER_EXT" }, + { 0x8DC3, "GL_SAMPLER_1D_ARRAY_SHADOW_EXT" }, + { 0x8DC4, "GL_SAMPLER_2D_ARRAY_SHADOW_EXT" }, + { 0x8DC5, "GL_SAMPLER_CUBE_SHADOW_EXT" }, + { 0x8DC6, "GL_UNSIGNED_INT_VEC2_EXT" }, + { 0x8DC7, "GL_UNSIGNED_INT_VEC3_EXT" }, + { 0x8DC8, "GL_UNSIGNED_INT_VEC4_EXT" }, + { 0x8DC9, "GL_INT_SAMPLER_1D_EXT" }, + { 0x8DCA, "GL_INT_SAMPLER_2D_EXT" }, + { 0x8DCB, "GL_INT_SAMPLER_3D_EXT" }, + { 0x8DCC, "GL_INT_SAMPLER_CUBE_EXT" }, + { 0x8DCD, "GL_INT_SAMPLER_2D_RECT_EXT" }, + { 0x8DCE, "GL_INT_SAMPLER_1D_ARRAY_EXT" }, + { 0x8DCF, "GL_INT_SAMPLER_2D_ARRAY_EXT" }, + { 0x8DD0, "GL_INT_SAMPLER_BUFFER_EXT" }, + { 0x8DD1, "GL_UNSIGNED_INT_SAMPLER_1D_EXT" }, + { 0x8DD2, "GL_UNSIGNED_INT_SAMPLER_2D_EXT" }, + { 0x8DD3, "GL_UNSIGNED_INT_SAMPLER_3D_EXT" }, + { 0x8DD4, "GL_UNSIGNED_INT_SAMPLER_CUBE_EXT" }, + { 0x8DD5, "GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT" }, + { 0x8DD6, "GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT" }, + { 0x8DD7, "GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT" }, + { 0x8DD8, "GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT" }, + { 0x8DD9, "GL_GEOMETRY_SHADER_EXT" }, + { 0x8DDA, "GL_GEOMETRY_VERTICES_OUT_EXT" }, + { 0x8DDB, "GL_GEOMETRY_INPUT_TYPE_EXT" }, + { 0x8DDC, "GL_GEOMETRY_OUTPUT_TYPE_EXT" }, + { 0x8DDD, "GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT" }, + { 0x8DDE, "GL_MAX_VERTEX_VARYING_COMPONENTS_EXT" }, + { 0x8DDF, "GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT" }, + { 0x8DE0, "GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT" }, + { 0x8DE1, "GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT" }, + { 0x8DE2, "GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT" }, + { 0x8DE3, "GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT" }, + { 0x8DE4, "GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT" }, + { 0x8DED, "GL_MAX_BINDABLE_UNIFORM_SIZE_EXT" }, + { 0x8DEE, "GL_UNIFORM_BUFFER_EXT" }, + { 0x8DEF, "GL_UNIFORM_BUFFER_BINDING_EXT" }, + { 0x8E4C, "GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT" }, + { 0x8E4D, "GL_FIRST_VERTEX_CONVENTION_EXT" }, + { 0x8E4E, "GL_LAST_VERTEX_CONVENTION_EXT" }, + { 0x8E4F, "GL_PROVOKING_VERTEX_EXT" }, + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_gl_renderers[] = +{ + { 0x00020200, "Generic" }, + { 0x00020400, "GenericFloat" }, + { 0x00020600, "AppleSW" }, + { 0x00021000, "ATIRage128" }, + { 0x00021200, "ATIRadeon" }, + { 0x00021400, "ATIRagePro" }, + { 0x00021600, "ATIRadeon8500" }, + { 0x00021800, "ATIRadeon9700" }, + { 0x00021900, "ATIRadeonX1000" }, + { 0x00021A00, "ATIRadeonX2000" }, + { 0x00022000, "NVGeForce2MX" }, + { 0x00022200, "NVGeForce3" }, + { 0x00022400, "NVGeForceFX" }, + { 0x00022600, "NVGeForce8xxx" }, + { 0x00023000, "VTBladeXP2" }, + { 0x00024000, "Intel900" }, + { 0x00024200, "IntelX3100" }, + { 0x00040000, "Mesa3DFX" }, + + VE( TERMVALUE ) +}; + + +//=============================================================================== +// decode helper funcs + +char s_glmStrScratch[65536]; +int s_glmStrCursor = 0; + +const char * GLMDecode( GLMThing_t thingtype, unsigned long value ) +{ + const GLMValueEntry_t *table = NULL; + //char isflags = 0; + + switch( thingtype ) + { + case eD3D_DEVTYPE: table = g_d3d_devtypes; + break; + + case eD3D_FORMAT: table = g_d3d_formats; + break; + + case eD3D_RTYPE: table = g_d3d_rtypes; + break; + + case eD3D_USAGE: table = g_d3d_usages; + break; + + case eD3D_RSTATE: table = g_d3d_rstates; + break; + + case eD3D_SIO: table = g_d3d_opcodes; + break; + + case eD3D_VTXDECLUSAGE: table = g_d3d_vtxdeclusages; + break; + + case eCGL_RENDID: table = g_cgl_rendids; + break; + + case eGL_ERROR: table = g_gl_errors; + break; + + case eGL_ENUM: table = g_gl_enums; + break; + + case eGL_RENDERER: table = g_gl_renderers; + break; + + default: + GLMStop(); + return "UNKNOWNTYPE"; + break; + } + + if (table) + { + while( table->value != TERMVALUE ) + { + if (table->value == value) + { + return table->name; + } + table++; + } + } + return "UNKNOWN"; +} + +const char *GLMDecodeMask( GLMThing_t kind, unsigned long value ) +{ + // if cursor to scratch buffer is within 1K of EOB, rewind + // nobody is going to decode 63K of flag string values in a single call.. + + // this means that strings returned by this function have a short lifetime.. print them and do not save the pointer.. + + if ( (sizeof(s_glmStrScratch) - s_glmStrCursor) < 1000 ) + { + s_glmStrCursor = 0; + } + + char *start = &s_glmStrScratch[ s_glmStrCursor ]; + char *dest = start; + char first = 1; + + DWORD mask = static_cast(1L<<31); + while(mask) + { + if (mask & value) + { + sprintf(dest,"%s%s", (first) ? "" : "|", GLMDecode( kind, value&mask ) ); + first = 0; + + dest += strlen(dest); // leaves dest pointing at the end null + } + mask >>= 1; + } + s_glmStrCursor = (dest - s_glmStrScratch) + 1; // +1 so the next decoded flag set doesn't land on the ending null + return start; + +} + +#undef VE +#undef TERMVALUE + +//=============================================================================== + +bool GLMDetectOGLP( void ) +{ + bool result = false; +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + GLint forceFlush; + CGLError error = CGLGetParameter(CGLGetCurrentContext(), kCGLCPEnableForceFlush, &forceFlush); + result = error == 0; + if (result) + { + // enable a breakpoint on color4sv + int oglp_bkpt[3] = { kCGLFEglColor4sv, kCGLProfBreakBefore, 1 }; + + CGLSetOption( kCGLGOEnableBreakpoint, (GLint)oglp_bkpt ); + } + +#endif + return result; +} + + +// from http://blog.timac.org/?p=190 + +#ifndef _WIN32 + #include +#endif +#include +#ifndef _WIN32 + #include +#ifdef LINUX +#include +#else +#include +#endif +#endif + +// From Technical Q&A QA1361 +// Returns true if the current process +// is being debugged (either running +// under the debugger or has a debugger +// attached post facto). + +bool GLMDetectGDB( void ) // aka AmIBeingDebugged() +{ +#ifdef OSX + bool result; + int junk; + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, + // if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info + // we want, in this case we're looking for + // information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + + assert(junk == 0); + + // We're being debugged if the P_TRACED + // flag is set. + + result = ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + return result; +#else + return Sys_IsDebuggerPresent(); +#endif +} + + +static uint g_glmDebugChannelMask = 0; // which output channels are available (can be more than one) +static uint g_glmDebugFlavorMask = 0; // which message flavors are enabled for output (can be more than one) + +uint GLMDetectAvailableChannels( void ) +{ + uint result = 0; + + // printf is always available (except maybe in release... ?) + result |= (1 << ePrintf); + + // gdb + if (GLMDetectGDB()) + { + result |= (1 << eDebugger); + printf("\n############# GDB Detected"); + } + + // oglp + if (GLMDetectOGLP()) + { + result |= (1 << eGLProfiler); + printf("\n############# OGLP Detected"); + } + + return result; +} + + +static bool g_debugInitDone = false; + +#if GLMDEBUG + + // following funcs vanish if GLMDEBUG not set + +void GLMDebugInitialize( bool forceReinit ) +{ + if ( !g_debugInitDone || forceReinit ) + { + // detect channels + uint channelMask = GLMDetectAvailableChannels(); + + // see if there are any prohibitions on the commandline + // (also add any other desired reasons, release build say).. + + if ( CommandLine()->FindParm("-noprintconsole") ) + { + channelMask &= ~(1<FindParm("-noprintdebugger") ) + { + channelMask &= ~(1<FindParm("-noprintoglp") ) + { + channelMask &= ~(1<FindParm("-glmspew")) + { + channelMask = 0; + } + + // set the output channel mask + GLMDebugChannelMask( &channelMask ); + + // if any channels are enabled, enable some output + if ( channelMask ) + { + // start mostly quiet unless the -glmbootspew option is there + if ( CommandLine()->FindParm( "-glmbootspew" ) ) + { + g_glmDebugFlavorMask = 0xFFFFFFFF; + } + else + { + g_glmDebugFlavorMask = + (1< < + // | (1<m_bHave_GL_GREMEDY_string_marker) + { + gGL->glStringMarkerGREMEDY(0, string); + } + } +} + +int g_glm_indent = 0; +int g_glm_indent_max = 40; // 40 tabs max + +#ifndef OSX +const char *strnstr( const char *haystack, const char *needle, int len ) +{ + return strstr( haystack, needle ); +} +#endif + +EGLMDebugFlavor GLMAssessFlavor( char *str ) +{ + EGLMDebugFlavor flavor = eDefaultFlavor; + + if (strnstr(str, "-D-", 4)) + { + // debug dump + flavor = eDebugDump; + } + else if (strnstr(str, "-M-", 4)) + { + // matrix data + flavor = eMatrixData; + } + else if (strnstr(str, "-S-", 4)) + { + // shader data + flavor = eShaderData; + } + else if (strnstr(str, "-F-", 4)) + { + // framebuf data + flavor = eFrameBufData; + } + else if (strnstr(str, "-X-", 4)) + { + // DirectX data + flavor = eDXStuff; + } + else if (strnstr(str, "-A-", 4)) + { + // allocation data + flavor = eAllocations; + } + else if (strnstr(str, "-Z-", 4)) + { + // allocation data + flavor = eSlowness; + } + else if (str[0] == '<' || str[0] == '>') + { + // entry/exit (aka tenure) + flavor = eTenure; + } + else if (strnstr(str, "---", 4)) // note we check four chars worth so you can do >-M- and it will indent and be filterable + { + // comment + flavor = eComment; + } + + return flavor; +} + +void GLMPrintfVA( const char *fmt, va_list vargs ) +{ + // if no channels open, return + uint channelMask = GLMDebugChannelMask(); + if (!channelMask) + return; + + // if "all flavors" is off, return + uint flavorMask = GLMDebugFlavorMask(); + if (! ( flavorMask & (1<' - raise indent level after print. + // if first char is a '<' - lower indent level before print. + + char buf[100000]; + + if (fmt[0] == '<') + { + GLMIncIndent( -1 ); + } + + memset( buf, '\t', g_glm_indent ); + vsprintf( buf+g_glm_indent, fmt, vargs ); + GLMStringOut( buf ); + + if (fmt[0] == '>') + { + GLMIncIndent( 1 ); + } +} + +void GLMPrintf( const char *fmt, ... ) +{ + // if no channels open, return + uint channelMask = GLMDebugChannelMask(); + if (!channelMask) + return; + + // if "all flavors" is off, return + uint flavorMask = GLMDebugFlavorMask(); + if (! ( flavorMask & (1<' - raise indent level after print. + // if first char is a '<' - lower indent level before print. + + char buf[64000]; + + if (str[0] == '<') + { + GLMIncIndent( -1 ); + } + + memset( buf, '\t', g_glm_indent ); + + if (strlen(str) < sizeof(buf)-g_glm_indent-1) + { + strcpy( buf + g_glm_indent, str ); + } + else + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + + GLMStringOut( buf ); // single string out with indenting + + if (str[0] == '>') + { + GLMIncIndent( 1 ); + } +} + + +void GLMPrintText( const char *str, EGLMDebugFlavor flavor, uint options ) +{ + // if no channels open, return + uint channelMask = GLMDebugChannelMask(); + if (!channelMask) + return; + + // if "all flavors" is off, return + uint flavorMask = GLMDebugFlavorMask(); + if (! ( flavorMask & (1<0) + { + if (g_glm_indent > g_glm_indent_max) + { + g_glm_indent = g_glm_indent_max; + } + } + else + { + if (g_glm_indent < 0) + { + g_glm_indent = 0; + } + } + return g_glm_indent; +} + +int GLMGetIndent( void ) +{ + return g_glm_indent; +} + +void GLMSetIndent( int indent ) +{ + g_glm_indent = indent; +} + +#endif + +// PIX tracking - you can call these outside of GLMDEBUG=true +char sg_pPIXName[128]; + + +#ifndef OSX +ConVar gl_telemetry_gpu_pipeline_flushing( "gl_telemetry_gpu_pipeline_flushing", "0" ); + +class CGPUTimestampManager +{ + CGPUTimestampManager( const CGPUTimestampManager & ); + CGPUTimestampManager& operator= ( CGPUTimestampManager & ); + +public: + CGPUTimestampManager() : + m_bInitialized( false ), + m_nCurFrame( 0 ), + m_flGPUToCPUOffsetInS( 0 ), + m_flGPUToS( 0 ), + m_flRdtscToS( 0 ), + m_flSToRdtsc( 0 ), + m_nFreeQueryPoolSize( 0 ), + m_nOutstandingQueriesHead( 0 ), + m_nOutstandingQueriesTail( 0 ), + m_nNumOutstandingQueryZones( 0 ), + m_nQueryZoneStackSize( 0 ), + m_nNumFinishedZones( 0 ), + m_nTotalSpanWorkCount( 0 ) + { + memset( m_FreeQueryPool, 0, sizeof( m_FreeQueryPool ) ) ; + memset( m_QueryZoneStack, 0, sizeof( m_QueryZoneStack ) ); + memset( m_OutstandingQueryZones, 0, sizeof( m_OutstandingQueryZones ) ); + memset( m_FinishedZones, 0, sizeof( m_FinishedZones ) ); + } + + ~CGPUTimestampManager() + { + Deinit(); + } + + inline bool IsInitialized() const { return m_bInitialized; } + inline uint GetCurFrame() const { return m_nCurFrame; } + + void Init() + { + Deinit(); + + memset( m_FreeQueryPool, 0, sizeof( m_FreeQueryPool ) ) ; + memset( m_QueryZoneStack, 0, sizeof( m_QueryZoneStack ) ); + memset( m_OutstandingQueryZones, 0, sizeof( m_OutstandingQueryZones ) ); + memset( m_FinishedZones, 0, sizeof( m_FinishedZones ) ); + + InitRdtsc(); + + m_nCurFrame = 0; + + gGL->glGenQueries( cFreeQueryPoolSize, m_FreeQueryPool ); + m_nFreeQueryPoolSize = cFreeQueryPoolSize; + + m_nOutstandingQueriesHead = 0; + m_nOutstandingQueriesTail = 0; + m_nNumOutstandingQueryZones = 0; + + m_nQueryZoneStackSize = 0; + m_nNumFinishedZones = 0; + + m_bInitialized = true; + + m_nTotalSpanWorkCount = 0; + + Calibrate(); + } + + void Calibrate() + { + if ( !m_bInitialized ) + return; + + PipelineFlush(); + + m_flGPUToS = 1.0 / 1000000000.0; + + //0.99997541250006794; + //0.99997530000006662; + // Correction factor to prevent excessive drift, only calibrated on my system, we need a better way of computing/recording this. + double flGPURatio = 0.99997425000007034000; + + const uint NT = 1; + for ( uint nTrial = 0; nTrial < NT; nTrial++ ) + { + const uint R = 16; + double flClockOffsetsInS[R]; + for ( uint q = 0; q < R; q++) + { + uint64 nBestTotalCPUTimestamp = (uint64)-1; + uint64 nBestCPUTimestamp = 0; + GLuint64 nBestGPUTimestamp = 0; + + for ( uint i = 0; i < 10; i++) + { + const uint64 nStartCPUTimestamp = Plat_Rdtsc(); + + gGL->glQueryCounter( m_FreeQueryPool[0], GL_TIMESTAMP); + PipelineFlush(); + + const uint64 nEndCPUTimestamp = Plat_Rdtsc(); + + GLint nAvailable; + do + { + gGL->glGetQueryObjectiv( m_FreeQueryPool[0], GL_QUERY_RESULT_AVAILABLE, &nAvailable ); + } while ( !nAvailable ); + + GLuint64 nGPUTimestamp; + gGL->glGetQueryObjectui64v( m_FreeQueryPool[0], GL_QUERY_RESULT, &nGPUTimestamp ); + + const uint64 nTotalCPUTimestamp = nEndCPUTimestamp - nStartCPUTimestamp; + if ( nTotalCPUTimestamp < nBestTotalCPUTimestamp ) + { + nBestTotalCPUTimestamp = nTotalCPUTimestamp; + nBestCPUTimestamp = nStartCPUTimestamp; + nBestGPUTimestamp = nGPUTimestamp; + } + } + + double flCPUTimestampTimeInSeconds = nBestCPUTimestamp * m_flRdtscToS; + double flGPUTimestampTimeInSeconds = nBestGPUTimestamp * m_flGPUToS * flGPURatio; + + flClockOffsetsInS[q] = flCPUTimestampTimeInSeconds - flGPUTimestampTimeInSeconds; + + ThreadSleep(100); + + DbgPrintf("%f %f %1.20f\n", flCPUTimestampTimeInSeconds, flGPUTimestampTimeInSeconds, flClockOffsetsInS[q] ); + } + + m_flGPUToCPUOffsetInS = 0.0f; + for ( uint i = 0; i < R; i++ ) + m_flGPUToCPUOffsetInS += flClockOffsetsInS[i]; + m_flGPUToCPUOffsetInS /= R; + + if ( NT > 1 ) + { + DbgPrintf("------- Ratio: %2.20f\n", flGPURatio ); + + double flDelta = flClockOffsetsInS[0] - flClockOffsetsInS[R - 1]; + + DbgPrintf("------- %1.20f\n", flDelta ); + +#if 1 + if ( flDelta < 0.0000005f ) + { + flGPURatio += .000000125f; + } + else if ( flDelta > 0.0000005f ) + { + flGPURatio -= .000000125f; + } +#else + if ( flDelta < 0.0000005f ) + { + flGPURatio += .0000000125f; + } + else if ( flDelta > 0.0000005f ) + { + flGPURatio -= .0000000125f; + } +#endif + } + } + + m_flGPUToS *= flGPURatio; + +#if 0 + // dump drift over time to debugger output + double flLatency = 0; + for ( ; ; ) + { + // test + const uint64 nStartCPUTime = Plat_Rdtsc(); + + gGL->glQueryCounter( m_FreeQueryPool[0], GL_TIMESTAMP); + + PipelineFlush(); + + GLint nAvailable; + do + { + gGL->glGetQueryObjectiv( m_FreeQueryPool[0], GL_QUERY_RESULT_AVAILABLE, &nAvailable ); + } while ( !nAvailable ); + + GLuint64 nGPUTime; + gGL->glGetQueryObjectui64v( m_FreeQueryPool[0], GL_QUERY_RESULT, &nGPUTime ); + + double flStartGPUTime = ( ( nGPUTime * m_flGPUToS ) + m_flGPUToCPUOffsetInS ); + + flLatency = flStartGPUTime - nStartCPUTime * m_flRdtscToS; + DbgPrintf("%f\n", flLatency ); + } +#endif + } + + void Deinit() + { + if ( !m_bInitialized ) + return; + + if ( m_nFreeQueryPoolSize ) + { + gGL->glDeleteQueries( m_nFreeQueryPoolSize, m_FreeQueryPool ); + } + m_nFreeQueryPoolSize = 0; + + for ( uint i = 0; i < m_nNumOutstandingQueryZones; i++ ) + { + QueryZone_t &query = m_OutstandingQueryZones[ ( m_nOutstandingQueriesHead + i ) % cMaxQueryZones ]; + if ( query.m_nBeginQuery ) + { + gGL->glDeleteQueries( 1, &query.m_nBeginQuery ); + } + if ( query.m_nEndQuery ) + { + gGL->glDeleteQueries( 1, &query.m_nEndQuery ); + } + } + m_nOutstandingQueriesHead = 0; + m_nOutstandingQueriesTail = 0; + m_nNumOutstandingQueryZones = 0; + + for ( uint i = 0; i < m_nQueryZoneStackSize; i++ ) + { + QueryZone_t &query = m_QueryZoneStack[i]; + if ( query.m_nBeginQuery ) + { + gGL->glDeleteQueries( 1, &query.m_nBeginQuery ); + } + if ( query.m_nEndQuery ) + { + gGL->glDeleteQueries( 1, &query.m_nEndQuery ); + } + } + m_nQueryZoneStackSize = 0; + + m_flGPUToCPUOffsetInS = 0; + m_flGPUToS = 0; + m_flRdtscToS = 0; + m_flSToRdtsc = 0; + + m_bInitialized = false; + } + + // pName is assumed to be a telemetry dynamic string! + void BeginZone( const char *pName ) + { + if ( !m_bInitialized ) + return; + + if ( m_nQueryZoneStackSize >= cMaxQueryZoneStackSize ) + { + Panic( "Increase cMaxQueryZoneStackSize!" ); + } + + QueryZone_t &zone = m_QueryZoneStack[m_nQueryZoneStackSize]; + + zone.m_pName = pName; + + zone.m_nBeginQuery = AllocQueryHandle(); + zone.m_nEndQuery = 0; + zone.m_nStackLevel = m_nQueryZoneStackSize; + + zone.m_nTotalGPUWorkCount = g_nTotalDrawsOrClears; +#if GL_TELEMETRY_GPU_ZONES + zone.m_nTotalGPUWorkCount += g_TelemetryGPUStats.GetTotal(); +#endif + + gGL->glQueryCounter( m_QueryZoneStack[m_nQueryZoneStackSize].m_nBeginQuery, GL_TIMESTAMP ); + + m_nQueryZoneStackSize++; + } + + void EndZone() + { + if ( !m_bInitialized ) + return; + + if ( ( !m_nQueryZoneStackSize ) || ( m_nNumOutstandingQueryZones == cMaxQueryZones ) ) + { + Panic( "Query zone error!" ); + } + + m_nQueryZoneStackSize--; + + uint nCurGPUWorkCount = g_nTotalDrawsOrClears; +#if GL_TELEMETRY_GPU_ZONES + nCurGPUWorkCount += g_TelemetryGPUStats.GetTotal(); +#endif + + uint nTotalDraws = nCurGPUWorkCount - m_QueryZoneStack[m_nQueryZoneStackSize].m_nTotalGPUWorkCount; + + m_QueryZoneStack[m_nQueryZoneStackSize].m_nEndQuery = AllocQueryHandle(); + gGL->glQueryCounter( m_QueryZoneStack[m_nQueryZoneStackSize].m_nEndQuery, GL_TIMESTAMP ); + m_QueryZoneStack[m_nQueryZoneStackSize].m_nTotalGPUWorkCount = nTotalDraws; + + m_OutstandingQueryZones[m_nOutstandingQueriesHead] = m_QueryZoneStack[m_nQueryZoneStackSize]; + m_nOutstandingQueriesHead = ( m_nOutstandingQueriesHead + 1 ) % cMaxQueryZones; + m_nNumOutstandingQueryZones++; + + COMPILE_TIME_ASSERT( ( int )cMaxQueryZones > ( int )cMaxQueryZoneStackSize ); + if ( m_nNumOutstandingQueryZones >= ( cMaxQueryZones - cMaxQueryZoneStackSize ) ) + { + tmMessage( TELEMETRY_LEVEL2, TMMF_ICON_NOTE | TMMF_SEVERITY_WARNING, "CGPUTimestampManager::EndZone: Too many outstanding query zones - forcing a pipeline flush! This is probably expensive." ); + + FlushOutstandingQueries( true ); + } + + if ( gl_telemetry_gpu_pipeline_flushing.GetBool() ) + { + PipelineFlush(); + } + } + + void Tick() + { + m_nCurFrame++; + + if ( !m_bInitialized ) + return; + + if ( m_nQueryZoneStackSize > 0 ) + { + Panic( "Zone stack is not empty!" ); + } + + FlushOutstandingQueries( false ); + + tmMessage( TELEMETRY_LEVEL2, 0, "Total PIX timespan GPU work count: %u", m_nTotalSpanWorkCount ); + + m_nTotalSpanWorkCount = 0; + } + + void FlushOutstandingQueries( bool bForce ) + { + tmZone( TELEMETRY_LEVEL2, 0, "FlushOutstandingQueries: %u", m_nNumOutstandingQueryZones ); + + if ( bForce ) + { + PipelineFlush(); + } + + while ( m_nNumOutstandingQueryZones ) + { + QueryZone_t &zone = m_OutstandingQueryZones[m_nOutstandingQueriesTail]; + + GLint nEndAvailable = 0; + do + { + gGL->glGetQueryObjectiv( zone.m_nEndQuery, GL_QUERY_RESULT_AVAILABLE, &nEndAvailable ); + + } while ( ( bForce ) && ( nEndAvailable == 0 ) ); + + if ( !nEndAvailable ) + { + if ( bForce ) + { + Panic( "Query results not available after a full pipeline flush!" ); + } + break; + } + + GLuint64 nBeginGPUTime, nEndGPUTime; + gGL->glGetQueryObjectui64v( zone.m_nBeginQuery, GL_QUERY_RESULT, &nBeginGPUTime ); + gGL->glGetQueryObjectui64v( zone.m_nEndQuery, GL_QUERY_RESULT, &nEndGPUTime ); + + ReleaseQueryHandle( zone.m_nBeginQuery ); + zone.m_nBeginQuery = 0; + + ReleaseQueryHandle( zone.m_nEndQuery ); + zone.m_nEndQuery = 0; + + if ( m_nNumFinishedZones >= cMaxQueryZones ) + { + Panic( "Too many finished zones!" ); + } + + FinishedQueryZone_t &finishedZone = m_FinishedZones[m_nNumFinishedZones]; + finishedZone.m_pName = zone.m_pName; + finishedZone.m_nBeginGPUTime = nBeginGPUTime; + finishedZone.m_nEndGPUTime = nEndGPUTime; + finishedZone.m_nStackLevel = zone.m_nStackLevel; + finishedZone.m_nTotalGPUWorkCount = zone.m_nTotalGPUWorkCount; + m_nNumFinishedZones++; + + if ( !zone.m_nStackLevel ) + { + std::sort( m_FinishedZones, m_FinishedZones + m_nNumFinishedZones ); + FlushFinishedZones(); + m_nNumFinishedZones = 0; + } + + m_nOutstandingQueriesTail = ( m_nOutstandingQueriesTail + 1 ) % cMaxQueryZones; + m_nNumOutstandingQueryZones--; + } + } + +private: + bool m_bInitialized; + uint m_nCurFrame; + + double m_flGPUToCPUOffsetInS; + double m_flGPUToS; + double m_flRdtscToS; + double m_flSToRdtsc; + + enum { cMaxQueryZones = 4096, cFreeQueryPoolSize = cMaxQueryZones * 2 }; + GLuint m_FreeQueryPool[cFreeQueryPoolSize ]; + uint m_nFreeQueryPoolSize; + + GLuint AllocQueryHandle() + { + if ( !m_nFreeQueryPoolSize ) + { + Panic( "Out of query handles!"); + } + return m_FreeQueryPool[--m_nFreeQueryPoolSize]; + } + + void ReleaseQueryHandle( GLuint nHandle ) + { + if ( m_nFreeQueryPoolSize >= cFreeQueryPoolSize ) + { + Panic( "Query handle error!" ); + } + m_FreeQueryPool[m_nFreeQueryPoolSize++] = nHandle; + } + + struct QueryZone_t + { + const char *m_pName; + GLuint m_nBeginQuery; + GLuint m_nEndQuery; + uint m_nStackLevel; + uint m_nTotalGPUWorkCount; + }; + + QueryZone_t m_OutstandingQueryZones[cMaxQueryZones]; + uint m_nOutstandingQueriesHead; // index of first outstanding query (oldest) + uint m_nOutstandingQueriesTail; // index where next query goes (newest) + uint m_nNumOutstandingQueryZones; + + enum { cMaxQueryZoneStackSize = 256 }; + QueryZone_t m_QueryZoneStack[cMaxQueryZoneStackSize]; + uint m_nQueryZoneStackSize; + + struct FinishedQueryZone_t + { + const char *m_pName; + GLuint64 m_nBeginGPUTime; + GLuint64 m_nEndGPUTime; + uint m_nStackLevel; + uint m_nTotalGPUWorkCount; + + inline bool operator< ( const FinishedQueryZone_t &rhs ) const + { + if ( m_nBeginGPUTime == rhs.m_nBeginGPUTime) + return m_nStackLevel < rhs.m_nStackLevel; + + return m_nBeginGPUTime < rhs.m_nBeginGPUTime; + } + }; + + FinishedQueryZone_t m_FinishedZones[cMaxQueryZones]; + uint m_nNumFinishedZones; + + uint m_nTotalSpanWorkCount; + + void InitRdtsc() + { + m_flRdtscToS = 0.0f; + m_flSToRdtsc = 0.0f; + + for ( uint i = 0; i < 10; i++ ) + { + uint64 t0 = Plat_Rdtsc(); + double d0 = Plat_FloatTime(); + + ThreadSleep( 250 ); + + uint64 t1 = Plat_Rdtsc(); + double d1 = Plat_FloatTime(); + + double flRdtscToS = ( d1 - d0 ) / ( t1 - t0 ); + double flSToRdtsc = ( t1 - t0 ) / ( d1 - d0 ); + if ( flSToRdtsc > m_flSToRdtsc ) + { + m_flRdtscToS = flRdtscToS; + m_flSToRdtsc = flSToRdtsc; + } + } + } + + void PipelineFlush() + { +#ifdef HAVE_GL_ARB_SYNC + GLsync nSyncObj = gGL->glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 ); + if ( nSyncObj ) + { + gGL->glClientWaitSync( nSyncObj, GL_SYNC_FLUSH_COMMANDS_BIT, 300000000000ULL ); + gGL->glDeleteSync( nSyncObj ); + } +#endif + } + + inline void NewTimeSpan( uint64 nStartGPUTime, uint64 nEndGPUTime, const char *pName, uint nTotalDraws ) + { + // apparently we must use level0 for timespans? + tmBeginTimeSpanAt( TELEMETRY_LEVEL0, 1, 0, nStartGPUTime, "%s [C:%u]", pName ? pName : "", nTotalDraws ); + tmEndTimeSpanAt( TELEMETRY_LEVEL0, 1, 0, nEndGPUTime, "%s [C:%u]", pName ? pName : "", nTotalDraws ); + } + + void FlushFinishedZones() + { + for ( uint i = 0; i < m_nNumFinishedZones; i++ ) + { + FinishedQueryZone_t &zone = m_FinishedZones[i]; + if ( !zone.m_nTotalGPUWorkCount ) + continue; + + bool bEmit = false; + if ( i == ( m_nNumFinishedZones - 1 ) ) + bEmit = true; + else + { + FinishedQueryZone_t &nextZone = m_FinishedZones[i + 1]; + bEmit = zone.m_nEndGPUTime <= nextZone.m_nBeginGPUTime; + } + + if ( bEmit ) + { + uint64 nStartGPUTime = ( ( zone.m_nBeginGPUTime * m_flGPUToS ) + m_flGPUToCPUOffsetInS ) * m_flSToRdtsc; + uint64 nEndGPUTime = ( ( zone.m_nEndGPUTime * m_flGPUToS ) + m_flGPUToCPUOffsetInS ) * m_flSToRdtsc; + + NewTimeSpan( nStartGPUTime, nEndGPUTime, zone.m_pName, zone.m_nTotalGPUWorkCount ); + + m_nTotalSpanWorkCount += zone.m_nTotalGPUWorkCount; + } + } + } + + void Panic( const char *pMsg ) + { + DXABSTRACT_BREAK_ON_ERROR(); + Error( "%s", pMsg ); + } + + static void DbgPrintf( const char *pFmt, ... ) + { + va_list vargs; + va_start( vargs, pFmt ); + char buf[1024]; + V_vsnprintf( buf, sizeof( buf ), pFmt, vargs ); + +#ifdef WIN32 + OutputDebugStringA( buf ); +#else + printf( "%s", buf ); +#endif + + va_end( vargs ); + } +}; + + +static CGPUTimestampManager g_GPUTimestampManager; + +void GLMGPUTimestampManagerInit() +{ + g_GPUTimestampManager.Init(); +} + +void GLMGPUTimestampManagerDeinit() +{ + g_GPUTimestampManager.Deinit(); +} + +ConVar gl_telemetry_gpu( "gl_telemetry_gpu", "0" ); +static bool g_bPrevTelemetryGPU; + +void GLMGPUTimestampManagerTick() +{ + if ( g_bPrevTelemetryGPU != gl_telemetry_gpu.GetBool() ) + { + if ( !gl_telemetry_gpu.GetBool() ) + g_GPUTimestampManager.Deinit(); + else + { +#if !PIX_ENABLE || !GL_TELEMETRY_GPU_ZONES + ConMsg( "Must define PIX_ENABLE and GL_TELEMETRY_GPU_ZONES to use this feature" ); +#else + g_GPUTimestampManager.Init(); +#endif + } + + g_bPrevTelemetryGPU = gl_telemetry_gpu.GetBool(); + } + + g_GPUTimestampManager.Tick(); +} + +#endif // !OSX + +static uint g_nPIXEventIndex; + +void GLMBeginPIXEvent( const char *str ) +{ +#ifndef OSX + char szName[1024]; + V_snprintf( szName, sizeof( szName ), "[ID:%u FR:%u] %s", g_nPIXEventIndex, g_GPUTimestampManager.GetCurFrame(), str ); + const char *p = tmDynamicString( TELEMETRY_LEVEL2, szName ); //p can be null if tm is getting shut down + tmEnter( TELEMETRY_LEVEL2, TMZF_NONE, "PIX %s", p ? p : "" ); + + g_nPIXEventIndex++; + + g_GPUTimestampManager.BeginZone( p ); +#endif // !OSX + V_strncpy( sg_pPIXName, str, 128 ); + +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + CGLSetOption( kCGLGOComment, (GLint)sg_pPIXName ); +#endif + + if ( gGL->m_bHave_GL_GREMEDY_string_marker ) + { + gGL->glStringMarkerGREMEDY( 0, sg_pPIXName ); + } +} + +void GLMEndPIXEvent( void ) +{ +#ifndef OSX + g_GPUTimestampManager.EndZone(); +#endif + +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + CGLSetOption( kCGLGOComment, (GLint)sg_pPIXName ); +#endif + + if ( gGL->m_bHave_GL_GREMEDY_string_marker ) + { + gGL->glStringMarkerGREMEDY( 0, sg_pPIXName ); + } + + sg_pPIXName[0] = '\0'; + + tmLeave( TELEMETRY_LEVEL2 ); +} + +//=============================================================================== + +// Knob - hash table mapping string names to float values + + +struct GLMKnobKey +{ + char *m_knobName; +}; + +struct GLMKnobValue +{ + float m_value; +}; + +bool LessFunc_GLMKnobKey( const GLMKnobKey &a, const GLMKnobKey &b ) +{ + // Umm, what should this really be? + //return strcmp( a.m_knobName, b.m_knobName ); + return strcmp( a.m_knobName, b.m_knobName ) < 0; +} + +CUtlMap< GLMKnobKey, GLMKnobValue* > *g_knobMap = NULL; + +// add some special knob-names that just read mod keys +extern "C" uint GetCurrentKeyModifiers( void ); + +enum ECarbonModKeyIndex +{ + EcmdKeyBit = 8, /* command key down?*/ + EshiftKeyBit = 9, /* shift key down?*/ + EalphaLockBit = 10, /* alpha lock down?*/ + EoptionKeyBit = 11, /* option key down?*/ + EcontrolKeyBit = 12 /* control key down?*/ +}; + +enum ECarbonModKeyMask +{ + EcmdKey = 1 << EcmdKeyBit, + EshiftKey = 1 << EshiftKeyBit, + EalphaLock = 1 << EalphaLockBit, + EoptionKey = 1 << EoptionKeyBit, + EcontrolKey = 1 << EcontrolKeyBit +}; + +float GLMKnob( char *knobname, float *setvalue ) +{ +#if GLMDEBUG + float result = 0.0f; + + if (!g_knobMap) + { + g_knobMap = new CUtlMap< GLMKnobKey, GLMKnobValue* >; + g_knobMap->SetLessFunc( LessFunc_GLMKnobKey ); + } + +#ifdef OSX + uint mods = GetCurrentKeyModifiers(); +#else + uint mods = 0; +#endif + // is it a special key name ? + if (!strcmp(knobname,"caps-key")) + { + return (mods & (EalphaLock)) ? 1.0 : 0.0; + } + else if (!strcmp(knobname,"control-key")) + { + return (mods & (EcontrolKey)) ? 1.0 : 0.0; + } + else if (!strcmp(knobname,"shift-key")) + { + return (mods & (EshiftKey)) ? 1.0 : 0.0; + } + else if (!strcmp(knobname,"option-key")) + { + return (mods & (EoptionKey)) ? 1.0 : 0.0; + } + else + { + // does the key exist in the map ? + GLMKnobKey key; + key.m_knobName = knobname; + + GLMKnobValue *knob = NULL; + + unsigned short index = g_knobMap->Find( key ); + if (index != g_knobMap->InvalidIndex()) + { + // found it + knob = (*g_knobMap)[ index ]; + + if (setvalue) + { + knob->m_value = *setvalue; + } + + result = knob->m_value; + } + else + { + // need to make a new one + knob = new GLMKnobValue; + + knob->m_value = (setvalue) ? *setvalue : 0.0f; + + g_knobMap->Insert( key, knob ); + } + + return knob->m_value; + } + +#else + // GLM knobs just return 0.0 all the time when no GLMDEBUG + return 0.0f; +#endif +} + +// for boolean knobs.. +float GLMKnobToggle( char *knobname ) +{ + // if not 0.0, make it 0.0 + // if 0.0, make it 1.0 + + float newValue = 0.0; // assume falling edge + float curValue = GLMKnob( knobname, NULL ); + if (curValue == 0.0) + { + newValue = 1.0; + } + + return GLMKnob( knobname, &newValue ); +} + + +//=============================================================================== + +// helpers for CGLSetOption - no op if no profiler +void GLMProfilerClearTrace( void ) +{ +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + CGLSetOption( kCGLGOResetFunctionTrace, 0 ); +#else + Assert( !"impl me" ); +#endif +} + +void GLMProfilerEnableTrace( bool enable ) +{ +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + CGLSetOption( kCGLGOEnableFunctionTrace, enable ? GL_TRUE : GL_FALSE ); +#else + Assert( !"impl me" ); +#endif +} + +// helpers for CGLSetParameter - no op if no profiler +void GLMProfilerDumpState( void ) +{ +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + CGLContextObj curr = CGLGetCurrentContext(); + CGLSetParameter( curr, kCGLCPDumpState, (const GLint*)1 ); +#else + Assert( !"impl me" ); +#endif +} + + +//=============================================================================== + +CGLMFileMirror::CGLMFileMirror( char *fullpath ) +{ + m_path = strdup( fullpath ); + m_data = (char *)malloc(1); + m_size = 0; + UpdateStatInfo(); + if (m_exists) + { + ReadFile(); + } +} + +CGLMFileMirror::~CGLMFileMirror( ) +{ + if (m_path) + { + free (m_path); + m_path = NULL; + } + + if (m_data) + { + free (m_data); + m_data = NULL; + } +} + +bool CGLMFileMirror::HasData( void ) +{ + return (m_size != 0); +} + + +// return direct pointer to buffer. Will be invalidated if file is re-loaded or if data is written to +void CGLMFileMirror::GetData( char **dataPtr, uint *dataSizePtr ) +{ + *dataPtr = m_data; + *dataSizePtr = m_size; +} + + +void CGLMFileMirror::SetData( char *data, uint dataSize ) +{ + if (m_data) + { + free( m_data ); + m_data = NULL; + } + + m_size = dataSize; + + m_data = (char *)malloc( m_size +1 ); + m_data[ m_size ] = 0; // extra NULL terminator, no charge + + memcpy( m_data, data, m_size ); // copy data in + + WriteFile(); // keep disk copy sync'd +} + +static bool stat_diff( struct stat *a, struct stat *b ) +{ + if (a->st_size != b->st_size) + { + return true; + } + +#ifdef OSX + if (memcmp( &a->st_mtimespec, &b->st_mtimespec, sizeof( struct timespec ) ) ) +#else + if (memcmp( &a->st_mtime, &b->st_mtime, sizeof( time_t ) ) ) +#endif + { + return true; + } + + return false; +} + +bool CGLMFileMirror::PollForChanges( void ) +{ + // snapshot old stat + //bool old_exists = m_exists; + struct stat old_stat = m_stat; + + UpdateStatInfo(); + + if (m_exists) + { + if ( stat_diff( &old_stat, &m_stat ) ) + { + // initial difference detected. continue to poll at 0.1s intervals until it stops changing, then read it. + int stablecount = 0; + do + { + ThreadSleep(100000); + + struct stat last_stat = m_stat; + UpdateStatInfo(); + + if (stat_diff( &last_stat, &m_stat )) + { + stablecount = 0; + } + else + { + stablecount++; + } + } while(stablecount<3); + + // changes have settled down, now re-read it + ReadFile(); + return true; + } + else + { + return false; // no change + } + } + else + { + // file does not exist. remake it. not considered to be a change. + WriteFile(); + return false; + } +} + + + +void CGLMFileMirror::UpdateStatInfo( void ) +{ + // stat the path + struct stat newstat; + memset (&newstat, 0, sizeof(newstat) ); + int result = stat( m_path, &newstat ); + + if (!result) + { + m_exists = true; + m_stat = newstat; + } + else + { + m_exists = false; + memset( &m_stat, 0, sizeof( m_stat ) ); + } +} + + +void CGLMFileMirror::ReadFile( void ) +{ + // unconditional - we discard any old buffer, make a new one, + UpdateStatInfo(); + + if (m_data) + { + free( m_data ); + m_data = NULL; + } + + if (m_exists) + { + FILE *infile = fopen( m_path, "rb" ); + if (infile) + { + // get size from stat + m_size = m_stat.st_size; + + m_data = (char *)malloc( m_size +1 ); + m_data[ m_size ] = 0; // extra NULL terminator, no charge + + fread( m_data, 1, m_size, infile ); + + fclose( infile ); + } + else + { + GLMDebugger(); // ouch + } + + } + else + { + // hmmmmmm + m_data = (char *)malloc(1); + m_data[0] = 0; + m_size = 0; + } +} + + +void CGLMFileMirror::WriteFile( void ) +{ + FILE *outfile = fopen( m_path, "wb" ); + + if (outfile) + { + fwrite( m_data, 1, m_size, outfile ); + fclose( outfile ); + + UpdateStatInfo(); // sets m_stat and m_exists + } + else + { + GLMDebugger(); // ouch + } +} + +void CGLMFileMirror::OpenInEditor( bool foreground ) +{ + char temp[64000]; + + // pass -b if no desire to bring editor to foreground + sprintf(temp,"/usr/bin/bbedit %s %s", foreground ? "" : "-b", m_path ); + system( temp ); +} + + + +CGLMEditableTextItem::CGLMEditableTextItem( char *text, uint size, bool forceOverwrite, char *prefix, char *suffix ) +{ + // clone input text (exact size copy) + m_origSize = size; + m_origText = (char *)malloc( m_origSize ); + memcpy( m_origText, text, m_origSize ); + + // null out munged form til we generate it + m_mungedSize = 0; + m_mungedText = NULL; + + // null out mirror until we create it + m_mirrorBaseName = NULL; + m_mirrorFullPath = NULL; + m_mirror = NULL; + + GenHashOfOrigText(); // will fill out m_origDigest + GenMungedText( false ); + GenBaseNameAndFullPath( prefix, suffix ); // figure out where the mirror will go + + if (!strcmp(m_mirrorBaseName, "96c7e9d2faf76b1148f7274afd684d4b.fsh")) + { + printf("\nhello there\n"); + } + + // make the mirror from the filename. + // see if there was any content on disk + // if so, honor that content *unless* the force-option is set. + m_mirror = new CGLMFileMirror( m_mirrorFullPath ); + + // the logic is simple. + // the only time we will choose the copy on disk, is if + // a - forceOverwrite is false + // AND b - the copy on disk is bigger than 10 bytes. + + bool replaceDiskCopy = true; + + char *mirrorData = NULL; + uint mirrorSize = 0; + + if (!forceOverwrite) + { + if (m_mirror->HasData()) + { + // peek at it, and use it if it is more than some minimum number of bytes. + m_mirror->GetData( &mirrorData, &mirrorSize ); + if (mirrorSize > 10) + { + replaceDiskCopy = false; + } + } + } + + if (replaceDiskCopy) + { + // push our generated data to the mirror - disk copy is overwritten + m_mirror->SetData( m_mungedText, m_mungedSize ); + } + else + { + GenMungedText( true ); + } + +} + +CGLMEditableTextItem::~CGLMEditableTextItem( ) +{ + if (m_origText) + { + free (m_origText); + } + + if (m_mungedText) + { + free (m_mungedText); + } + + if (m_mirrorBaseName) + { + free (m_mirrorBaseName); + } + + if (m_mirrorFullPath) + { + free (m_mirrorFullPath); + } + + if (m_mirror) + { + free( m_mirror ); + } +} + +bool CGLMEditableTextItem::HasData( void ) +{ + return m_mirror->HasData(); +} + +bool CGLMEditableTextItem::PollForChanges( void ) +{ + bool changed = m_mirror->PollForChanges(); + if (changed) + { + // re-gen munged text from mirror (means "copy") + GenMungedText( true ); + } + return changed; +} + +void CGLMEditableTextItem::GetCurrentText( char **textOut, uint *sizeOut ) +{ + if (!m_mungedText) GLMDebugger(); + + *textOut = m_mungedText; + *sizeOut = m_mungedSize; +} + +void CGLMEditableTextItem::OpenInEditor( bool foreground ) +{ + m_mirror->OpenInEditor( foreground ); +} + + +void CGLMEditableTextItem::GenHashOfOrigText( void ) +{ + MD5Context_t md5ctx; + MD5Init( &md5ctx ); + MD5Update( &md5ctx, (unsigned char*)m_origText, m_origSize ); + MD5Final( m_origDigest, &md5ctx ); +} + + +void CGLMEditableTextItem::GenBaseNameAndFullPath( char *prefix, char *suffix ) +{ + // base name is hash digest in hex, plus the suffix. + char temp[5000]; + + Q_binarytohex( m_origDigest, sizeof(m_origDigest), temp, sizeof( temp ) ); + if (suffix) + { + strcat( temp, suffix ); + } + if (m_mirrorBaseName) free(m_mirrorBaseName); + m_mirrorBaseName = strdup( temp ); + + sprintf( temp, "%s%s", prefix, m_mirrorBaseName ); + if (m_mirrorFullPath) free(m_mirrorFullPath); + m_mirrorFullPath = strdup( temp ); +} + + +void CGLMEditableTextItem::GenMungedText( bool fromMirror ) +{ + if (fromMirror) + { + // just import the text as is from the mirror file. + + char *mirrorData = NULL; + uint mirrorSize = 0; + + if (m_mirror->HasData()) + { + // peek at it, and use it if it is more than some minimum number of bytes. + m_mirror->GetData( &mirrorData, &mirrorSize ); + + if (m_mungedText) + { + free( m_mungedText ); + m_mungedText = NULL; + } + + m_mungedText = (char *)malloc( mirrorSize+1 ); + m_mungedText[ mirrorSize ] = 0; + memcpy( m_mungedText, mirrorData, mirrorSize ); + + m_mungedSize = mirrorSize; + } + else + { + GLMDebugger(); + } + } + else + { + #if 1 + // we don't actually clone/munge any more. + if (m_mungedText) + { + free( m_mungedText ); + m_mungedText = NULL; + } + + m_mungedText = (char *)malloc( m_origSize+1 ); + m_mungedText[ m_origSize ] = 0; + memcpy( m_mungedText, m_origText, m_origSize ); + + m_mungedSize = m_origSize; + + #else + // take pure 'orig' text that came in from the engine, and clone it + // do not clone the first line + char temp[100000]; + char *dst = temp; + char *lim = &temp[ sizeof(temp) ]; + + // zero temp + memset( temp, 0, sizeof(temp) ); + + // write orig text to temp + if (m_origSize >= (sizeof(temp)/2) ) + { + GLMDebugger(); + } + + memcpy( dst, m_origText, m_origSize ); + dst += m_origSize; + + // add a newline if the last character wasn't + if ( (*(dst-1)) != '\n' ) + { + *dst++ = '\n'; + } + + // walk orig text again and copy it over, with these caveats + // don't copy the first line + // insert a # before all the other lines. + char *src = temp; + + // walk to end of first line + char *firstNewline = strchr( src, '\n' ); + if (!firstNewline) + { + GLMDebugger(); + } + else + { + // advance 'src' to that newline- we're not copying the !! line + src = firstNewline; + } + + + // now walk the rest - insert a # after each newline + while( (dst < lim) && ((src-temp) < m_origSize) ) + { + switch( *src ) + { + case '\n': + *dst++ = *src++; + *dst++ = '#'; + break; + + default: + *dst++ = *src++; + } + } + if (dst >= lim) + { + GLMDebugger(); + } + + // final newline + *dst++ = '\n'; + + // copyout + if (m_mungedText) + { + free( m_mungedText ); + m_mungedText = NULL; + } + + m_mungedSize = dst - temp; + m_mungedText = (char *)malloc( m_mungedSize ); + memcpy( m_mungedText, temp, m_mungedSize ); + #endif + } +} + +//=============================================================================== + +// class for cracking multi-part text blobs +// sections are demarcated by beginning-of-line markers submitted in a table by the caller +// typically +// asm flavors have first-line rules so we use those tags as is +// !!ARBvp (etc) +// !!ARBfp (etc) +// //!!GLSLF // slashes required +// //!GLSLV + +// maybe also introduce "present but disabled" markers like these +// -!!ARBvp (etc) +// -!!ARBfp (etc) +// -//!!GLSLF +// -//!GLSLV + +// resolved. there is no default section for text that doesn't have a marker in front of it. mark it or miss it. + +CGLMTextSectioner::CGLMTextSectioner( char *text, int textLength, const char **markers ) +{ + // find lines + // for each line, see if it starts with a marker + // if so, open a new section based at that line + + GLMTextSection *curSection = NULL; // no current section until we see a marker + + char *cursor = text; + char *textLimit = text+textLength; + + int foundMarker; + const char **markerCursor; + while( cursor < textLimit ) + { + // top of loop. cursor points to start of a line. + // find the end of the line and keep that handy. + char *eol = strchr( cursor, '\n' ); + int charsInLine = (eol) ? (eol-cursor)+1 : strlen(cursor); + + //see if any of the marker strings is located here. + foundMarker = -1; + markerCursor = markers; + + while( (foundMarker<0) && (*markerCursor!=NULL) ) + { + // see if the n'th marker is a hit + int markerLen = strlen(*markerCursor); + + if (!strncmp( cursor, *markerCursor, markerLen ) ) + { + // hit + foundMarker = markerCursor - markers; + } + markerCursor++; + } + + // outcome is either "marker spotted" or "no". + // if marker seen, open new section using that marker. + // else, grow active section if underway. + // then, move cursor to next line. + + if (foundMarker >= 0) + { + // found marker. start new section. + // no need to do anything special with prior section - it was up to date before seeing this marker. + int index = m_sectionTable.AddToTail(); + curSection = &m_sectionTable[ index ]; + + curSection->m_markerIndex = foundMarker; + curSection->m_textOffset = cursor - text; // text includes the marker + curSection->m_textLength = charsInLine; // this line goes in the tally, later lines add to it + } + else + { + // add this line to current section if live + if (curSection) + { + curSection->m_textLength += charsInLine; + } + } + cursor += charsInLine; + } +} + +CGLMTextSectioner::~CGLMTextSectioner( ) +{ + // not much to do. +} + + +int CGLMTextSectioner::Count( void ) +{ + return m_sectionTable.Count(); +} + +void CGLMTextSectioner::GetSection( int index, uint *offsetOut, uint *lengthOut, int *markerIndexOut ) +{ + Assert( index < m_sectionTable.Count() ); + + GLMTextSection *section = &m_sectionTable[ index ]; + + *offsetOut = section->m_textOffset; + *lengthOut = section->m_textLength; + *markerIndexOut = section->m_markerIndex; +} + +//=============================================================================== + +// how to make a compiled-in font: +// a. type in a matrix of characters in your fav editor +// b. take a screen shot of the characters (128x128 pixels in this case) +// c. export as TIFF raw. +// d. hex dump it +// e. column-copy just the hex data +// f. find and replace: chop out all the spaces and line feeds, change FFFFFF and 000000 to your marker chars of choice. +// g. wrap each line with quotes and a comma. + +unsigned char g_glmDebugFontMap[ 128 * 128 ] = +{ +" * " +" * * * * * " +" * * * * * *** * * ** * * * * * " +" * * * ***** * * * * * * * * * * * * * * * * " +" * * * * * * * * * * * * * *** * * " +" * ***** *** * * * * * * * ***** ***** * " +" * * * * * * * * * * * * * * * " +" * * * * * * * * * * * ** ** * " +" * *** * * ** * * * ** ** * " +" * * * * * " +" * " +" " +" " +" *** * *** *** * ***** *** ***** *** *** * * *** " +"* * ** * * * * ** * * * * * * * ** ** * * * * " +"* ** * * * * * **** **** * * * * * ** ** * ***** * * " +"* * * * * ** * * * * * * *** * * * * * " +"** * * * * ***** * * * * * * **** * ***** * * " +"* * * * * * * * * * * * * * * ** ** * * " +" *** * ***** *** * *** *** * *** *** ** ** * * * " +" * " +" * " +" " +" *** " +"* * *** **** *** **** ***** ***** *** * * * * * * * * * * * *** " +"* * * * * * * * * * * * * * * * * * * * * ** ** ** * * * " +"* * * * * * * * * * * * * * * * * * * * * * * * * * * * " +"*** * ***** **** * * * **** **** * ** ***** * * ** * * * * ** * * " +"* ** * * * * * * * * * * * * * * * * * * * * * * * * * " +"* * * * * * * * * * * * * * * * * * * * * * * * * * * " +"* * * * **** *** **** ***** * *** * * * *** * * ***** * * * * *** " +" *** " +" " +" * " +" ** * ** * " +"**** *** **** *** ***** * * * * * * * * * * ***** * * * * * " +"* * * * * * * * * * * * * * * * * * * * * * * * * " +"* * * * * * * * * * * * * * * * * * * * * " +"**** * * **** *** * * * * * * * * * * * * * * " +"* * * * * * * * * * * * * * * * * * * * " +"* * * * * * * * * * * ** ** * * * * * * * " +"* *** * * *** * *** * * * * * * ***** * * * ****** " +" * ** * ** " +" " +" " +" * " +" * * * ** * * * * * " +" * * * * * * * " +" **** **** *** **** *** *** **** **** * * * * * **** * ** *** " +" * * * * * * * * * * * * * * * * * * * * * * * ** * * * " +" * * * * * * * ***** * * * * * * * *** * * * * * * * * " +" * ** * * * * * * * * * * * * * * * * * * * * * * * " +" ** * **** **** **** **** * **** * * * * * * ** * * * * * *** " +" * * " +" *** ** " +" * " +" ** * ** " +" * * * * ** * " +" * * * * * ** *** " +"**** **** * ** **** **** * * * * * * * * * * * ***** * * * ***** " +"* * * * ** * * * * * * * * * * * * * * * ** * ** ***** " +"* * * * * *** * * * * * * * * * * * * * * * ***** " +"* * * * * * * * ** * * * * * * * * * * * * * *** " +"**** **** * **** ** ** * * * * * * **** ***** * * * " +"* * * ** * ** " +"* * *** * " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +}; + + + + + + + + diff --git a/togles/linuxwin/glmgrcocoa.mm b/togles/linuxwin/glmgrcocoa.mm new file mode 100644 index 00000000..717c913d --- /dev/null +++ b/togles/linuxwin/glmgrcocoa.mm @@ -0,0 +1,34 @@ +//========= Copyright 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: provide some call-out glue to ObjC from the C++ GLMgr code +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + + +#include +#include +#include +#include + +#undef MIN +#undef MAX +#define DONT_DEFINE_BOOL // Don't define BOOL! +#include "tier0/threadtools.h" +#include "tier1/interface.h" +#include "tier1/strtools.h" +#include "tier1/utllinkedlist.h" +#include "togl/rendermechanism.h" + + + +// ------------------------------------------------------------------------------------ // +// some glue to let GLMgr call into NS/ObjC classes. +// ------------------------------------------------------------------------------------ // + +CGLContextObj GetCGLContextFromNSGL( PseudoNSGLContextPtr nsglCtx ) +{ + return (CGLContextObj)[ (NSOpenGLContext*)nsglCtx CGLContextObj]; +} + diff --git a/togles/linuxwin/glmtexinlines.h b/togles/linuxwin/glmtexinlines.h new file mode 100644 index 00000000..cbdcc546 --- /dev/null +++ b/togles/linuxwin/glmtexinlines.h @@ -0,0 +1,29 @@ +//========= 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. +#ifndef CGLMTEXINLINES_H +#define CGLMTEXINLINES_H + +#pragma once + +#endif // CGLMTEXINLINES_H diff --git a/togles/linuxwin/intelglmallocworkaround.cpp b/togles/linuxwin/intelglmallocworkaround.cpp new file mode 100644 index 00000000..efe0e0ad --- /dev/null +++ b/togles/linuxwin/intelglmallocworkaround.cpp @@ -0,0 +1,71 @@ +//========= 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. +#include "intelglmallocworkaround.h" +#include "mach_override.h" + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +IntelGLMallocWorkaround* IntelGLMallocWorkaround::s_pWorkaround = NULL; + +void *IntelGLMallocWorkaround::ZeroingAlloc(size_t size) +{ + // We call into this pointer that resumes the original malloc. + void *memory = s_pWorkaround->m_pfnMallocReentry(size); + if (size < 96) + { + // Since the Intel driver has an issue with a small allocation + // that's left uninitialized, we use memset to ensure it's zero-initialized. + memset(memory, 0, size); + } + + return memory; +} + +IntelGLMallocWorkaround* IntelGLMallocWorkaround::Get() +{ + if (!s_pWorkaround) + { + s_pWorkaround = new IntelGLMallocWorkaround(); + } + + return s_pWorkaround; +} + +bool IntelGLMallocWorkaround::Enable() +{ + if ( m_pfnMallocReentry != NULL ) + { + return true; + } + + mach_error_t error = mach_override_ptr( (void*)&malloc, (const void*)&ZeroingAlloc, (void**)&m_pfnMallocReentry ); + if ( error == err_cannot_override ) + { + m_pfnMallocReentry = NULL; + return false; + } + + return true; +} \ No newline at end of file diff --git a/togles/linuxwin/intelglmallocworkaround.h b/togles/linuxwin/intelglmallocworkaround.h new file mode 100644 index 00000000..630972d1 --- /dev/null +++ b/togles/linuxwin/intelglmallocworkaround.h @@ -0,0 +1,61 @@ +//========= 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. +// +// intelglmallocworkaround.h +// class responsible for setting up a malloc override that zeroes allocated +// memory of less than 96 bytes. this is to work around a bug +// in the Intel GLSL compiler on Mac OS X 10.8 due to uninitialized memory. +// +// 96 was chosen due to this quote from Apple: +// "I verified that the size of the structure is exactly 64 bytes on 10.8.3, 10.8.4 and will be on 10.8.5." +// +// certain GLSL shaders would (intermittently) cause a crash the first time they +// were drawn, and the bug has supposedly been fixed in 10.9, but is unlikely to +// ever make it to 10.8. +// +//=============================================================================== + +#ifndef INTELGLMALLOCWORKAROUND_H +#define INTELGLMALLOCWORKAROUND_H + +#include + +class IntelGLMallocWorkaround +{ +public: + static IntelGLMallocWorkaround *Get(); + bool Enable(); + +protected: + IntelGLMallocWorkaround() :m_pfnMallocReentry(NULL) {} + ~IntelGLMallocWorkaround() {} + + static IntelGLMallocWorkaround *s_pWorkaround; + static void* ZeroingAlloc(size_t); + + typedef void* (*pfnMalloc_t)(size_t); + pfnMalloc_t m_pfnMallocReentry; +}; + +#endif // INTELGLMALLOCWORKAROUND_H \ No newline at end of file diff --git a/togles/linuxwin/mach_override.c b/togles/linuxwin/mach_override.c new file mode 100644 index 00000000..76302767 --- /dev/null +++ b/togles/linuxwin/mach_override.c @@ -0,0 +1,765 @@ +// mach_override.c semver:1.2.0 +// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/mit +// https://github.com/rentzsch/mach_override + +#include "mach_override.h" + +#include +#include +#include +#include +#include +#include + +#include + +/************************** +* +* Constants +* +**************************/ +#pragma mark - +#pragma mark (Constants) + +#define kPageSize 4096 +#if defined(__ppc__) || defined(__POWERPC__) + +long kIslandTemplate[] = { + 0x9001FFFC, // stw r0,-4(SP) + 0x3C00DEAD, // lis r0,0xDEAD + 0x6000BEEF, // ori r0,r0,0xBEEF + 0x7C0903A6, // mtctr r0 + 0x8001FFFC, // lwz r0,-4(SP) + 0x60000000, // nop ; optionally replaced + 0x4E800420 // bctr +}; + +#define kAddressHi 3 +#define kAddressLo 5 +#define kInstructionHi 10 +#define kInstructionLo 11 + +#elif defined(__i386__) + +#define kOriginalInstructionsSize 16 +// On X86 we migh need to instert an add with a 32 bit immediate after the +// original instructions. +#define kMaxFixupSizeIncrease 5 + +unsigned char kIslandTemplate[] = { + // kOriginalInstructionsSize nop instructions so that we + // should have enough space to host original instructions + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + // Now the real jump instruction + 0xE9, 0xEF, 0xBE, 0xAD, 0xDE +}; + +#define kInstructions 0 +#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1 +#elif defined(__x86_64__) + +#define kOriginalInstructionsSize 32 +// On X86-64 we never need to instert a new instruction. +#define kMaxFixupSizeIncrease 0 + +#define kJumpAddress kOriginalInstructionsSize + 6 + +unsigned char kIslandTemplate[] = { + // kOriginalInstructionsSize nop instructions so that we + // should have enough space to host original instructions + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + // Now the real jump instruction + 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +#endif + +/************************** +* +* Data Types +* +**************************/ +#pragma mark - +#pragma mark (Data Types) + +typedef struct { + char instructions[sizeof(kIslandTemplate)]; +} BranchIsland; + +/************************** +* +* Funky Protos +* +**************************/ +#pragma mark - +#pragma mark (Funky Protos) + +static mach_error_t +allocateBranchIsland( + BranchIsland **island, + void *originalFunctionAddress); + + mach_error_t +freeBranchIsland( + BranchIsland *island ); + +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ); +#endif + +#if defined(__i386__) || defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ); +void +atomic_mov64( + uint64_t *targetAddress, + uint64_t value ); + + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions, + int *originalInstructionCount, + uint8_t *originalInstructionSizes ); + + static void +fixupInstructions( + uint32_t offset, + void *instructionsToFix, + int instructionCount, + uint8_t *instructionSizes ); +#endif + +/******************************************************************************* +* +* Interface +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Interface) + +#if defined(__i386__) || defined(__x86_64__) +mach_error_t makeIslandExecutable(void *address) { + mach_error_t err = err_none; + uintptr_t page = (uintptr_t)address & ~(uintptr_t)(kPageSize-1); + int e = err_none; + e |= mprotect((void *)page, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE); + e |= msync((void *)page, kPageSize, MS_INVALIDATE ); + if (e) { + err = err_cannot_override; + } + return err; +} +#endif + + mach_error_t +mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ) +{ + assert( originalFunctionAddress ); + assert( overrideFunctionAddress ); + + // this addresses overriding such functions as AudioOutputUnitStart() + // test with modified DefaultOutputUnit project +#if defined(__x86_64__) + for(;;){ + if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????] + originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1)); + else break; + } +#elif defined(__i386__) + for(;;){ + if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x???????? + originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1); + else break; + } +#endif + + long *originalFunctionPtr = (long*) originalFunctionAddress; + mach_error_t err = err_none; + +#if defined(__ppc__) || defined(__POWERPC__) + // Ensure first instruction isn't 'mfctr'. + #define kMFCTRMask 0xfc1fffff + #define kMFCTRInstruction 0x7c0903a6 + + long originalInstruction = *originalFunctionPtr; + if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) ) + err = err_cannot_override; +#elif defined(__i386__) || defined(__x86_64__) + int eatenCount = 0; + int originalInstructionCount = 0; + char originalInstructions[kOriginalInstructionsSize]; + uint8_t originalInstructionSizes[kOriginalInstructionsSize]; + uint64_t jumpRelativeInstruction = 0; // JMP + + Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, + &jumpRelativeInstruction, &eatenCount, + originalInstructions, &originalInstructionCount, + originalInstructionSizes ); + if (eatenCount + kMaxFixupSizeIncrease > kOriginalInstructionsSize) { + //printf ("Too many instructions eaten\n"); + overridePossible = false; + } + if (!overridePossible) err = err_cannot_override; + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); +#endif + + // Make the original function implementation writable. + if( !err ) { + err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, 8, false, + (VM_PROT_ALL | VM_PROT_COPY) ); + if( err ) + err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, 8, false, + (VM_PROT_DEFAULT | VM_PROT_COPY) ); + } + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + + // Allocate and target the escape island to the overriding function. + BranchIsland *escapeIsland = NULL; + if( !err ) + err = allocateBranchIsland( &escapeIsland, originalFunctionAddress ); + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + + +#if defined(__ppc__) || defined(__POWERPC__) + if( !err ) + err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 ); + + // Build the branch absolute instruction to the escape island. + long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning. + if( !err ) { + long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF; + branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; + } +#elif defined(__i386__) || defined(__x86_64__) + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + + if( !err ) + err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); + + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + // Build the jump relative instruction to the escape island +#endif + + +#if defined(__i386__) || defined(__x86_64__) + if (!err) { + uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5); + addressOffset = OSSwapInt32(addressOffset); + + jumpRelativeInstruction |= 0xE900000000000000LL; + jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; + jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); + } +#endif + + // Optionally allocate & return the reentry island. This may contain relocated + // jmp instructions and so has all the same addressing reachability requirements + // the escape island has to the original function, except the escape island is + // technically our original function. + BranchIsland *reentryIsland = NULL; + if( !err && originalFunctionReentryIsland ) { + err = allocateBranchIsland( &reentryIsland, escapeIsland); + if( !err ) + *originalFunctionReentryIsland = reentryIsland; + } + +#if defined(__ppc__) || defined(__POWERPC__) + // Atomically: + // o If the reentry island was allocated: + // o Insert the original instruction into the reentry island. + // o Target the reentry island at the 2nd instruction of the + // original function. + // o Replace the original instruction with the branch absolute. + if( !err ) { + int escapeIslandEngaged = false; + do { + if( reentryIsland ) + err = setBranchIslandTarget( reentryIsland, + (void*) (originalFunctionPtr+1), originalInstruction ); + if( !err ) { + escapeIslandEngaged = CompareAndSwap( originalInstruction, + branchAbsoluteInstruction, + (UInt32*)originalFunctionPtr ); + if( !escapeIslandEngaged ) { + // Someone replaced the instruction out from under us, + // re-read the instruction, make sure it's still not + // 'mfctr' and try again. + originalInstruction = *originalFunctionPtr; + if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction) + err = err_cannot_override; + } + } + } while( !err && !escapeIslandEngaged ); + } +#elif defined(__i386__) || defined(__x86_64__) + // Atomically: + // o If the reentry island was allocated: + // o Insert the original instructions into the reentry island. + // o Target the reentry island at the first non-replaced + // instruction of the original function. + // o Replace the original first instructions with the jump relative. + // + // Note that on i386, we do not support someone else changing the code under our feet + if ( !err ) { + uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland; + fixupInstructions(offset, originalInstructions, + originalInstructionCount, originalInstructionSizes ); + + if( reentryIsland ) + err = setBranchIslandTarget_i386( reentryIsland, + (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); + // try making islands executable before planting the jmp +#if defined(__x86_64__) || defined(__i386__) + if( !err ) + err = makeIslandExecutable(escapeIsland); + if( !err && reentryIsland ) + err = makeIslandExecutable(reentryIsland); +#endif + if ( !err ) + atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); + } +#endif + + // Clean up on error. + if( err ) { + if( reentryIsland ) + freeBranchIsland( reentryIsland ); + if( escapeIsland ) + freeBranchIsland( escapeIsland ); + } + + return err; +} + +/******************************************************************************* +* +* Implementation +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Implementation) + +static bool jump_in_range(intptr_t from, intptr_t to) { + intptr_t field_value = to - from - 5; + int32_t field_value_32 = field_value; + return field_value == field_value_32; +} + +/******************************************************************************* + Implementation: Allocates memory for a branch island. + + @param island <- The allocated island. + @result <- mach_error_t + + ***************************************************************************/ + +static mach_error_t +allocateBranchIslandAux( + BranchIsland **island, + void *originalFunctionAddress, + bool forward) +{ + assert( island ); + assert( sizeof( BranchIsland ) <= kPageSize ); + + vm_map_t task_self = mach_task_self(); + vm_address_t original_address = (vm_address_t) originalFunctionAddress; + vm_address_t address = original_address; + + for (;;) { + vm_size_t vmsize = 0; + memory_object_name_t object = 0; + kern_return_t kr = 0; + vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; + // Find the region the address is in. +#if __WORDSIZE == 32 + vm_region_basic_info_data_t info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; + kr = vm_region(task_self, &address, &vmsize, flavor, + (vm_region_info_t)&info, &info_count, &object); +#else + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; + kr = vm_region_64(task_self, &address, &vmsize, flavor, + (vm_region_info_t)&info, &info_count, &object); +#endif + if (kr != KERN_SUCCESS) + return kr; + assert((address & (kPageSize - 1)) == 0); + + // Go to the first page before or after this region + vm_address_t new_address = forward ? address + vmsize : address - kPageSize; +#if __WORDSIZE == 64 + if(!jump_in_range(original_address, new_address)) + break; +#endif + address = new_address; + + // Try to allocate this page. + kr = vm_allocate(task_self, &address, kPageSize, 0); + if (kr == KERN_SUCCESS) { + *island = (BranchIsland*) address; + return err_none; + } + if (kr != KERN_NO_SPACE) + return kr; + } + + return KERN_NO_SPACE; +} + +static mach_error_t +allocateBranchIsland( + BranchIsland **island, + void *originalFunctionAddress) +{ + mach_error_t err = + allocateBranchIslandAux(island, originalFunctionAddress, true); + if (!err) + return err; + return allocateBranchIslandAux(island, originalFunctionAddress, false); +} + + +/******************************************************************************* + Implementation: Deallocates memory for a branch island. + + @param island -> The island to deallocate. + @result <- mach_error_t + + ***************************************************************************/ + + mach_error_t +freeBranchIsland( + BranchIsland *island ) +{ + assert( island ); + assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); + assert( sizeof( BranchIsland ) <= kPageSize ); + return vm_deallocate( mach_task_self(), (vm_address_t) island, + kPageSize ); +} + +/******************************************************************************* + Implementation: Sets the branch island's target, with an optional + instruction. + + @param island -> The branch island to insert target into. + @param branchTo -> The address of the target. + @param instruction -> Optional instruction to execute prior to branch. Set + to zero for nop. + @result <- mach_error_t + + ***************************************************************************/ +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ) +{ + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // Fill in the address. + ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF; + ((short*)island->instructions)[kAddressHi] + = (((long) branchTo) >> 16) & 0x0000FFFF; + + // Fill in the (optional) instuction. + if( instruction != 0 ) { + ((short*)island->instructions)[kInstructionLo] + = instruction & 0x0000FFFF; + ((short*)island->instructions)[kInstructionHi] + = (instruction >> 16) & 0x0000FFFF; + } + + //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) ); + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + + return err_none; +} +#endif + +#if defined(__i386__) + mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ) +{ + + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // copy original instructions + if (instructions) { + bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize); + } + + // Fill in the address. + int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4); + *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset; + + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + return err_none; +} + +#elif defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ) +{ + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // Copy original instructions. + if (instructions) { + bcopy (instructions, island->instructions, kOriginalInstructionsSize); + } + + // Fill in the address. + *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo; + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + + return err_none; +} +#endif + + +#if defined(__i386__) || defined(__x86_64__) +// simplistic instruction matching +typedef struct { + unsigned int length; // max 15 + unsigned char mask[15]; // sequence of bytes in memory order + unsigned char constraint[15]; // sequence of bytes in memory order +} AsmInstructionMatch; + +#if defined(__i386__) +static AsmInstructionMatch possibleInstructions[] = { + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? + { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret + { 0x1, {0xFF}, {0x90} }, // nop + { 0x1, {0xFF}, {0x55} }, // push %esp + { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp + { 0x1, {0xFF}, {0x53} }, // push %ebx + { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp + { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate + { 0x1, {0xFF}, {0x57} }, // push %edi + { 0x1, {0xFF}, {0x56} }, // push %esi + { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax + { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg + { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg + { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax + { 0x6, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0xE8, 0x00, 0x00, 0x00, 0x00, 0x58} }, // call $imm; pop %eax + { 0x0 } +}; +#elif defined(__x86_64__) +static AsmInstructionMatch possibleInstructions[] = { + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? + { 0x1, {0xFF}, {0x90} }, // nop + { 0x1, {0xF8}, {0x50} }, // push %rX + { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp + { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp + { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp + { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx + { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX + { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX + { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg + { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi) + { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax + { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax + + //leaq offset(%rip),%rax + { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00} }, + + { 0x0 } +}; +#endif + +static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction) +{ + Boolean match = true; + + size_t i; + for (i=0; ilength; i++) { + unsigned char mask = instruction->mask[i]; + unsigned char constraint = instruction->constraint[i]; + unsigned char codeValue = code[i]; + + match = ((codeValue & mask) == constraint); + if (!match) break; + } + + return match; +} + +#if defined(__i386__) || defined(__x86_64__) + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions, + int *originalInstructionCount, + uint8_t *originalInstructionSizes ) +{ + Boolean allInstructionsKnown = true; + int totalEaten = 0; + unsigned char* ptr = code; + int remainsToEat = 5; // a JMP instruction takes 5 bytes + int instructionIndex = 0; + + if (howManyEaten) *howManyEaten = 0; + if (originalInstructionCount) *originalInstructionCount = 0; + while (remainsToEat > 0) { + Boolean curInstructionKnown = false; + + // See if instruction matches one we know + AsmInstructionMatch* curInstr = possibleInstructions; + do { + if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break; + curInstr++; + } while (curInstr->length > 0); + + // if all instruction matches failed, we don't know current instruction then, stop here + if (!curInstructionKnown) { + allInstructionsKnown = false; + fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n"); + break; + } + + // At this point, we've matched curInstr + int eaten = curInstr->length; + ptr += eaten; + remainsToEat -= eaten; + totalEaten += eaten; + + if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; + instructionIndex += 1; + if (originalInstructionCount) *originalInstructionCount = instructionIndex; + } + + + if (howManyEaten) *howManyEaten = totalEaten; + + if (originalInstructions) { + Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize); + + if (enoughSpaceForOriginalInstructions) { + memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP + bcopy(code, originalInstructions, totalEaten); + } else { + // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); + return false; + } + } + + if (allInstructionsKnown) { + // save last 3 bytes of first 64bits of codre we'll replace + uint64_t currentFirst64BitsOfCode = *((uint64_t *)code); + currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation + currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL; + + // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr + *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes + *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes + } + + return allInstructionsKnown; +} + + static void +fixupInstructions( + uint32_t offset, + void *instructionsToFix, + int instructionCount, + uint8_t *instructionSizes ) +{ + // The start of "leaq offset(%rip),%rax" + static const uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05}; + + int index; + for (index = 0;index < instructionCount;index += 1) + { + if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative + { + uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1); + *jumpOffsetPtr += offset; + } + + // leaq offset(%rip),%rax + if (memcmp(instructionsToFix, LeaqHeader, 3) == 0) { + uint32_t *LeaqOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 3); + *LeaqOffsetPtr += offset; + } + + // 32-bit call relative to the next addr; pop %eax + if (*(uint8_t*)instructionsToFix == 0xE8) + { + // Just this call is larger than the jump we use, so we + // know this is the last instruction. + assert(index == (instructionCount - 1)); + assert(instructionSizes[index] == 6); + + // Insert "addl $offset, %eax" in the end so that when + // we jump to the rest of the function %eax has the + // value it would have if eip had been pushed by the + // call in its original position. + uint8_t *op = (uint8_t*)instructionsToFix; + op += 6; + *op = 0x05; // addl + uint32_t *addImmPtr = (uint32_t*)(op + 1); + *addImmPtr = offset; + } + + instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]); + } +} +#endif + +#if defined(__i386__) +void atomic_mov64( + uint64_t *targetAddress, + uint64_t value) +{ + while (true) + { + uint64_t old_value = *targetAddress; + if (OSAtomicCompareAndSwap64(old_value, value, (int64_t*)targetAddress)) return; + } +} +#elif defined(__x86_64__) +void atomic_mov64( + uint64_t *targetAddress, + uint64_t value ) +{ + *targetAddress = value; +} +#endif +#endif diff --git a/togles/linuxwin/mach_override.h b/togles/linuxwin/mach_override.h new file mode 100644 index 00000000..ecd319c1 --- /dev/null +++ b/togles/linuxwin/mach_override.h @@ -0,0 +1,76 @@ +// mach_override.h semver:1.2.0 +// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/mit +// https://github.com/rentzsch/mach_override + +#ifndef _mach_override_ +#define _mach_override_ + +#include +#include + +#define err_cannot_override (err_local|1) + +__BEGIN_DECLS + +/**************************************************************************************** + Dynamically overrides the function implementation referenced by + originalFunctionAddress with the implentation pointed to by overrideFunctionAddress. + Optionally returns a pointer to a "reentry island" which, if jumped to, will resume + the original implementation. + + @param originalFunctionAddress -> Required address of the function to + override (with overrideFunctionAddress). + @param overrideFunctionAddress -> Required address to the overriding + function. + @param originalFunctionReentryIsland <- Optional pointer to pointer to the + reentry island. Can be NULL. + @result <- err_cannot_override if the original + function's implementation begins with + the 'mfctr' instruction. + + ************************************************************************************/ + + mach_error_t +mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ); + +__END_DECLS + +/**************************************************************************************** + If you're using C++ this macro will ease the tedium of typedef'ing, naming, keeping + track of reentry islands and defining your override code. See test_mach_override.cp + for example usage. + + ************************************************************************************/ + +#ifdef __cplusplus +#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \ +{ \ + static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \ + static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \ + class mach_override_class__##ORIGINAL_FUNCTION_NAME { \ + public: \ + static kern_return_t override(void *originalFunctionPtr) { \ + kern_return_t result = err_none; \ + if (!ORIGINAL_FUNCTION_NAME##_overriden) { \ + ORIGINAL_FUNCTION_NAME##_overriden = true; \ + result = mach_override_ptr( (void*)originalFunctionPtr, \ + (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \ + (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \ + } \ + return result; \ + } \ + static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS { + +#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \ + } \ + }; \ + \ + err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \ +} +#endif + +#endif // _mach_override_ diff --git a/togles/togl.vpc b/togles/togl.vpc new file mode 100644 index 00000000..dadbcd72 --- /dev/null +++ b/togles/togl.vpc @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// TOGL.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR ".." [$WIN32] +$Macro SRCDIR ".." [!$WIN32] +$Macro OUTBINDIR $LIBPUBLIC +$Macro OUTBINNAME "togl" +$Macro TOGL_SRCDIR "$SRCDIR/togl/linuxwin" +$Macro TOGL_INCDIR "$SRCDIR/public/togl/linuxwin" + + +$include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +// Common Configuration +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;..\" + $PreprocessorDefinitions "$BASE;TOGL_DLL_EXPORT;PROTECTED_THINGS_ENABLE;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead" [!$OSXALL] + $PreprocessorDefinitions "$BASE;TOGL_DLL_EXPORT" [$OSXALL] + + } + + $Linker + { + $ImportLibrary "$LIBPUBLIC\$_IMPLIB_PREFIX$OUTBINNAME$_IMPLIB_EXT" [!$X360 && !$OSXALL] + $ImportLibrary "$SRCDIR\lib\$PLATFORM\$_IMPLIB_PREFIX$OUTBINNAME$_IMPLIB_EXT" [$OSXALL] + } + + $Linker [$OSXALL] + { + $SystemFrameworks "Carbon;OpenGL;Quartz;Cocoa;IOKit" + } + + // togl/tier0/vstdlib traditionally used "lib" prefix though nobody else seems to. + $Linker [$POSIX] + { + $OutputFile "$(OBJ_DIR)/$_IMPLIB_PREFIX$OUTBINNAME$_DLL_EXT" + } + + $General [$POSIX] + { + $GameOutputFile "$OUTBINDIR/$_IMPLIB_PREFIX$OUTBINNAME$_DLL_EXT" + } + + $PreLinkEvent [$WINDOWS] + { + $CommandLine "call $SRCDIR\vpc_scripts\valve_p4_edit.cmd $LIBPUBLIC\$(TargetName).lib $SRCDIR" "\n" \ + "$BASE" + } +} + +$Project "togl" +{ + $Folder "Source Files" [$GL] + { + $File "$TOGL_SRCDIR/dx9asmtogl2.cpp" + $File "$TOGL_SRCDIR/dxabstract.cpp" + $File "$TOGL_SRCDIR/glentrypoints.cpp" + $File "$TOGL_SRCDIR/glmgr.cpp" + $File "$TOGL_SRCDIR/glmgrbasics.cpp" + $File "$TOGL_SRCDIR/glmgrcocoa.mm" [$OSXALL] + $File "$TOGL_SRCDIR/intelglmallocworkaround.cpp" [$OSXALL] + $File "$TOGL_SRCDIR/mach_override.c" [$OSXALL] + $File "$TOGL_SRCDIR/cglmtex.cpp" + $File "$TOGL_SRCDIR/cglmfbo.cpp" + $File "$TOGL_SRCDIR/cglmprogram.cpp" + $File "$TOGL_SRCDIR/cglmbuffer.cpp" + $File "$TOGL_SRCDIR/cglmquery.cpp" + $File "$TOGL_SRCDIR/asanstubs.cpp" + } + + $Folder "DirectX Header Files" [$WIN32 && !$GL] + { + } + + $Folder "Header Files" [$GL] + { + $File "$TOGL_SRCDIR/dx9asmtogl2.h" + $File "$TOGL_SRCDIR/glmgr_flush.inl" + $File "$TOGL_SRCDIR/intelglmallocworkaround.h" [$OSXALL] + $File "$TOGL_SRCDIR/mach_override.h" [$OSXALL] + } + + $Folder "Public Header Files" [$GL] + { + $File "$SRCDIR/public/togl/rendermechanism.h" + $File "$TOGL_INCDIR/dxabstract.h" + $File "$TOGL_INCDIR/dxabstract_types.h" + $File "$TOGL_INCDIR/glbase.h" + $File "$TOGL_INCDIR/glentrypoints.h" + $File "$TOGL_INCDIR/glmgr.h" + $File "$TOGL_INCDIR/glmdebug.h" + $File "$TOGL_INCDIR/glmgrbasics.h" + $File "$TOGL_INCDIR/glmgrext.h" + $File "$TOGL_INCDIR/glmdisplay.h" + $File "$TOGL_INCDIR/glmdisplaydb.h" + $File "$TOGL_INCDIR/glfuncs.h" + $File "$TOGL_INCDIR/cglmtex.h" + $File "$TOGL_INCDIR/cglmfbo.h" + $File "$TOGL_INCDIR/cglmprogram.h" + $File "$TOGL_INCDIR/cglmbuffer.h" + $File "$TOGL_INCDIR/cglmquery.h" + } + + $Folder "Link Libraries" + { + $Lib tier2 + $Lib mathlib + } +} + diff --git a/togles/wscript b/togles/wscript new file mode 100755 index 00000000..ab31edfe --- /dev/null +++ b/togles/wscript @@ -0,0 +1,62 @@ +#! /usr/bin/env python +# encoding: utf-8 + +from waflib import Utils +import os + +top = '.' +PROJECT_NAME = 'togl' + +def options(opt): + # stub + return + +def configure(conf): + conf.define('TOGL_DLL_EXPORT',1) + conf.env.append_unique('DEFINES',['strncpy=use_Q_strncpy_instead', + '_snprintf=use_Q_snprintf_instead']) + +def build(bld): + source = [ + 'linuxwin/dx9asmtogl2.cpp', + 'linuxwin/dxabstract.cpp', + 'linuxwin/glentrypoints.cpp', + 'linuxwin/glmgr.cpp', + 'linuxwin/glmgrbasics.cpp', + #'linuxwin/glmgrcocoa.mm', [$OSXALL] + #'linuxwin/intelglmallocworkaround.cpp', [$OSXALL] + #'linuxwin/mach_override.c', [$OSXALL] + 'linuxwin/cglmtex.cpp', + 'linuxwin/cglmfbo.cpp', + 'linuxwin/cglmprogram.cpp', + 'linuxwin/cglmbuffer.cpp', + 'linuxwin/cglmquery.cpp', + 'linuxwin/asanstubs.cpp' + ] + + includes = [ + '.', + '../public', + '../public/tier0', + '../public/tier1' + ] + bld.env.INCLUDES_SDL2 + + defines = [] + + libs = ['tier0','tier1','tier2','vstdlib','mathlib'] + + install_path = bld.env.LIBDIR + + bld.shlib( + source = source, + target = PROJECT_NAME, + name = PROJECT_NAME, + features = 'c cxx', + includes = includes, + defines = defines, + use = libs, + install_path = install_path, + subsystem = bld.env.MSVC_SUBSYSTEM, + idx = bld.get_taskgen_count() + ) +