You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
458 lines
15 KiB
458 lines
15 KiB
//========= 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 <sys/stat.h> |
|
|
|
#pragma once |
|
|
|
// good ARB program references |
|
// http://petewarden.com/notes/archives/2005/05/fragment_progra_2.html |
|
// http://petewarden.com/notes/archives/2005/06/fragment_progra_3.html |
|
|
|
// ext links |
|
|
|
// http://www.opengl.org/registry/specs/ARB/vertex_program.txt |
|
// http://www.opengl.org/registry/specs/ARB/fragment_program.txt |
|
// http://www.opengl.org/registry/specs/EXT/gpu_program_parameters.txt |
|
|
|
|
|
//=============================================================================== |
|
|
|
// tokens not in the SDK headers |
|
|
|
//#ifndef GL_DEPTH_STENCIL_ATTACHMENT_EXT |
|
// #define GL_DEPTH_STENCIL_ATTACHMENT_EXT 0x84F9 |
|
//#endif |
|
|
|
//=============================================================================== |
|
|
|
// forward declarations |
|
|
|
class GLMContext; |
|
class CGLMShaderPair; |
|
class CGLMShaderPairCache; |
|
|
|
// CGLMProgram can contain two flavors of the same program, one in assembler, one in GLSL. |
|
// these flavors are pretty different in terms of the API's that are used to activate them - |
|
// for example, assembler programs can just get bound to the context, whereas GLSL programs |
|
// have to be linked. To some extent we try to hide that detail inside GLM. |
|
|
|
// for now, make CGLMProgram a container, it does not set policy or hold a preference as to which |
|
// flavor you want to use. GLMContext has to handle that. |
|
|
|
enum EGLMProgramType |
|
{ |
|
kGLMVertexProgram, |
|
kGLMFragmentProgram, |
|
|
|
kGLMNumProgramTypes |
|
}; |
|
|
|
enum EGLMProgramLang |
|
{ |
|
kGLMARB, |
|
kGLMGLSL, |
|
|
|
kGLMNumProgramLangs |
|
}; |
|
|
|
struct GLMShaderDesc |
|
{ |
|
union |
|
{ |
|
GLuint arb; // ARB program object name |
|
GLuint 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<<n) mask of sampler active locs, if this is a fragment shader (dxabstract sets this field) |
|
uint m_samplerTypes; // SAMPLER_2D, etc. |
|
uint m_fragDataMask; // (1<<n) mask of gl_FragData[n] outputs referenced, if this is a fragment shader (dxabstract sets this field) |
|
uint m_numDrawBuffers; // number of draw buffers used |
|
GLenum m_drawBuffers[4]; // GL_COLOR_ATTACHMENT0_EXT1, etc |
|
uint m_nNumUsedSamplers; |
|
uint m_maxSamplers; |
|
uint m_maxVertexAttrs; |
|
uint m_nCentroidMask; |
|
uint m_nShadowDepthSamplerMask; |
|
|
|
bool m_bTranslatedProgram; |
|
|
|
char m_shaderName[64]; |
|
|
|
// Cache label string from the shader text |
|
// example: |
|
// trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234 |
|
char m_labelName[1024]; |
|
int m_labelIndex; |
|
int m_labelCombo; |
|
}; |
|
|
|
//=============================================================================== |
|
|
|
struct GLMShaderPairInfo |
|
{ |
|
int m_status; // -1 means req'd index was out of bounds (loop stop..) 0 means not present. 1 means present/active. |
|
|
|
char m_vsName[ 128 ]; |
|
int m_vsStaticIndex; |
|
int m_vsDynamicIndex; |
|
|
|
char m_psName[ 128 ]; |
|
int m_psStaticIndex; |
|
int m_psDynamicIndex; |
|
}; |
|
|
|
class CGLMShaderPair // a container for a linked GLSL shader pair, and metadata obtained post-link |
|
{ |
|
|
|
public: |
|
|
|
friend class CGLMProgram; |
|
friend class GLMContext; |
|
friend class CGLMShaderPairCache; |
|
|
|
//=============================== |
|
|
|
// constructor just sets up a GLSL program object and leaves it empty. |
|
CGLMShaderPair( GLMContext *ctx ); |
|
~CGLMShaderPair( ); |
|
|
|
bool SetProgramPair ( CGLMProgram *vp, CGLMProgram *fp ); |
|
// true result means successful link and query |
|
// Note that checking the link status and querying the uniform can be optionally |
|
// deferred to take advantage of multi-threaded compilation in the driver |
|
|
|
bool RefreshProgramPair ( void ); |
|
// re-link and re-query the uniforms |
|
|
|
bool ValidateProgramPair( void ); |
|
// true result means successful link and query |
|
|
|
FORCEINLINE void UpdateScreenUniform( uint nWidthHeight ) |
|
{ |
|
if ( m_nScreenWidthHeight == nWidthHeight ) |
|
return; |
|
|
|
m_nScreenWidthHeight = nWidthHeight; |
|
|
|
float fWidth = (float)( nWidthHeight & 0xFFFF ), fHeight = (float)( nWidthHeight >> 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; |
|
|
|
GLuint 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
|
|
|