Counter Strike : Global Offensive Source Code
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.

436 lines
14 KiB

  1. //============ Copyright (c) Valve Corporation, All rights reserved. ============
  2. //
  3. // cglmprogram.h
  4. // GLMgr programs (ARBVP/ARBfp)
  5. //
  6. //===============================================================================
  7. #ifndef CGLMPROGRAM_H
  8. #define CGLMPROGRAM_H
  9. #include <sys/stat.h>
  10. #pragma once
  11. // good ARB program references
  12. // http://petewarden.com/notes/archives/2005/05/fragment_progra_2.html
  13. // http://petewarden.com/notes/archives/2005/06/fragment_progra_3.html
  14. // ext links
  15. // http://www.opengl.org/registry/specs/ARB/vertex_program.txt
  16. // http://www.opengl.org/registry/specs/ARB/fragment_program.txt
  17. // http://www.opengl.org/registry/specs/EXT/gpu_program_parameters.txt
  18. //===============================================================================
  19. // tokens not in the SDK headers
  20. //#ifndef GL_DEPTH_STENCIL_ATTACHMENT_EXT
  21. // #define GL_DEPTH_STENCIL_ATTACHMENT_EXT 0x84F9
  22. //#endif
  23. //===============================================================================
  24. // forward declarations
  25. class GLMContext;
  26. class CGLMShaderPair;
  27. class CGLMShaderPairCache;
  28. // CGLMProgram can contain two flavors of the same program, one in assembler, one in GLSL.
  29. // these flavors are pretty different in terms of the API's that are used to activate them -
  30. // for example, assembler programs can just get bound to the context, whereas GLSL programs
  31. // have to be linked. To some extent we try to hide that detail inside GLM.
  32. // for now, make CGLMProgram a container, it does not set policy or hold a preference as to which
  33. // flavor you want to use. GLMContext has to handle that.
  34. enum EGLMProgramType
  35. {
  36. kGLMVertexProgram,
  37. kGLMFragmentProgram,
  38. kGLMNumProgramTypes
  39. };
  40. enum EGLMProgramLang
  41. {
  42. kGLMARB,
  43. kGLMGLSL,
  44. kGLMNumProgramLangs
  45. };
  46. struct GLMShaderDesc
  47. {
  48. union
  49. {
  50. GLuint arb; // ARB program object name
  51. GLhandleARB glsl; // GLSL shader object handle (void*)
  52. } m_object;
  53. // these can change if shader text is edited
  54. bool m_textPresent; // is this flavor(lang) of text present in the buffer?
  55. int m_textOffset; // where is it
  56. int m_textLength; // how big
  57. bool m_compiled; // has this text been through a compile attempt
  58. bool m_valid; // and if so, was the compile successful
  59. int m_slowMark; // has it been flagged during a non native draw batch before. increment every time it's slow.
  60. int m_highWater; // count of vec4's in the major uniform array ("vc" on vs, "pc" on ps)
  61. // written by dxabstract.... gross!
  62. int m_VSHighWaterBone; // count of vec4's in the bone-specific uniform array (only valid for vertex shaders)
  63. };
  64. GLenum GLMProgTypeToARBEnum( EGLMProgramType type ); // map vert/frag to ARB asm bind target
  65. GLenum GLMProgTypeToGLSLEnum( EGLMProgramType type ); // map vert/frag to ARB asm bind target
  66. #define GL_SHADER_PAIR_CACHE_STATS 0
  67. class CGLMProgram
  68. {
  69. public:
  70. friend class CGLMShaderPairCache;
  71. friend class CGLMShaderPair;
  72. friend class GLMContext; // only GLMContext can make CGLMProgram objects
  73. friend class GLMTester;
  74. friend struct IDirect3D9;
  75. friend struct IDirect3DDevice9;
  76. //===============================
  77. // constructor is very light, it just makes one empty program object per flavor.
  78. CGLMProgram( GLMContext *ctx, EGLMProgramType type );
  79. ~CGLMProgram( );
  80. void SetProgramText ( char *text ); // import text to GLM object - invalidate any prev compiled program
  81. void SetShaderName ( const char *name ); // only used for debugging/telemetry markup
  82. void CompileActiveSources ( void ); // compile only the flavors that were provided.
  83. void Compile ( EGLMProgramLang lang );
  84. bool CheckValidity ( EGLMProgramLang lang );
  85. void LogSlow ( EGLMProgramLang lang ); // detailed spew when called for first time; one liner or perhaps silence after that
  86. void GetLabelIndexCombo ( char *labelOut, int labelOutMaxChars, int *indexOut, int *comboOut );
  87. void GetComboIndexNameString ( char *stringOut, int stringOutMaxChars ); // mmmmmmmm-nnnnnnnn-filename
  88. #if GLMDEBUG
  89. bool PollForChanges( void ); // check mirror for changes.
  90. void ReloadStringFromEditable( void ); // populate m_string from editable item (react to change)
  91. bool SyncWithEditable( void );
  92. #endif
  93. //===============================
  94. // common stuff
  95. GLMContext *m_ctx; // link back to parent context
  96. EGLMProgramType m_type; // vertex or pixel
  97. uint m_nHashTag; // serial number for hashing
  98. char *m_text; // copy of text passed into constructor. Can change if editable shaders is enabled.
  99. // note - it can contain multiple flavors, so use CGLMTextSectioner to scan it and locate them
  100. #if GLMDEBUG
  101. CGLMEditableTextItem *m_editable; // editable text item for debugging
  102. #endif
  103. GLMShaderDesc m_descs[ kGLMNumProgramLangs ];
  104. uint m_samplerMask; // (1<<n) mask of sampler active locs, if this is a fragment shader (dxabstract sets this field)
  105. uint m_samplerTypes; // SAMPLER_2D, etc.
  106. uint m_fragDataMask; // (1<<n) mask of gl_FragData[n] outputs referenced, if this is a fragment shader (dxabstract sets this field)
  107. uint m_numDrawBuffers; // number of draw buffers used
  108. GLenum m_drawBuffers[4]; // GL_COLOR_ATTACHMENT0_EXT1, etc
  109. uint m_nNumUsedSamplers;
  110. uint m_maxSamplers;
  111. uint m_maxVertexAttrs;
  112. uint m_nCentroidMask;
  113. uint m_nShadowDepthSamplerMask;
  114. bool m_bTranslatedProgram;
  115. char m_shaderName[64];
  116. // Cache label string from the shader text
  117. // example:
  118. // trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234
  119. char m_labelName[1024];
  120. int m_labelIndex;
  121. int m_labelCombo;
  122. };
  123. //===============================================================================
  124. struct GLMShaderPairInfo
  125. {
  126. int m_status; // -1 means req'd index was out of bounds (loop stop..) 0 means not present. 1 means present/active.
  127. char m_vsName[ 128 ];
  128. int m_vsStaticIndex;
  129. int m_vsDynamicIndex;
  130. char m_psName[ 128 ];
  131. int m_psStaticIndex;
  132. int m_psDynamicIndex;
  133. };
  134. class CGLMShaderPair // a container for a linked GLSL shader pair, and metadata obtained post-link
  135. {
  136. public:
  137. friend class CGLMProgram;
  138. friend class GLMContext;
  139. friend class CGLMShaderPairCache;
  140. //===============================
  141. // constructor just sets up a GLSL program object and leaves it empty.
  142. CGLMShaderPair( GLMContext *ctx );
  143. ~CGLMShaderPair( );
  144. bool SetProgramPair ( CGLMProgram *vp, CGLMProgram *fp );
  145. // true result means successful link and query
  146. // Note that checking the link status and querying the uniform can be optionally
  147. // deferred to take advantage of multi-threaded compilation in the driver
  148. bool RefreshProgramPair ( void );
  149. // re-link and re-query the uniforms
  150. bool ValidateProgramPair( void );
  151. // true result means successful link and query
  152. FORCEINLINE void UpdateScreenUniform( uint nWidthHeight )
  153. {
  154. if ( m_nScreenWidthHeight == nWidthHeight )
  155. return;
  156. m_nScreenWidthHeight = nWidthHeight;
  157. float fWidth = (float)( nWidthHeight & 0xFFFF ), fHeight = (float)( nWidthHeight >> 16 );
  158. // Apply half pixel offset to output vertices to account for the pixel center difference between D3D9 and OpenGL.
  159. // 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
  160. float v[4] = { 1.0f / fWidth, 1.0f / fHeight, fWidth, fHeight };
  161. if ( m_locVertexScreenParams >= 0 )
  162. gGL->glUniform4fv( m_locVertexScreenParams, 1, v );
  163. }
  164. //===============================
  165. // common stuff
  166. GLMContext *m_ctx; // link back to parent context
  167. CGLMProgram *m_vertexProg;
  168. CGLMProgram *m_fragmentProg;
  169. GLhandleARB m_program; // linked program object
  170. // need meta data for attribs / samplers / params
  171. // actually we only need it for samplers and params.
  172. // attributes are hardwired.
  173. // vertex stage uniforms
  174. GLint m_locVertexParams; // "vc" per dx9asmtogl2 convention
  175. GLint m_locVertexBoneParams; // "vcbones"
  176. GLint m_locVertexInteger0; // "i0"
  177. enum { cMaxVertexShaderBoolUniforms = 4, cMaxFragmentShaderBoolUniforms = 1 };
  178. GLint m_locVertexBool[cMaxVertexShaderBoolUniforms]; // "b0", etc.
  179. GLint m_locFragmentBool[cMaxFragmentShaderBoolUniforms]; // "fb0", etc.
  180. bool m_bHasBoolOrIntUniforms;
  181. // fragment stage uniforms
  182. GLint m_locFragmentParams; // "pc" per dx9asmtogl2 convention
  183. int m_NumUniformBufferParams[kGLMNumProgramTypes];
  184. GLint m_UniformBufferParams[kGLMNumProgramTypes][256];
  185. GLint m_locFragmentFakeSRGBEnable; // "flSRGBWrite" - set to 1.0 to effect sRGB encoding on output
  186. float m_fakeSRGBEnableValue; // shadow to avoid redundant sets of the m_locFragmentFakeSRGBEnable uniform
  187. // init it to -1.0 at link or relink, so it will trip on any legit incoming value (0.0 or 1.0)
  188. GLint m_locSamplers[ GLM_SAMPLER_COUNT ]; // "sampler0 ... sampler1..."
  189. // other stuff
  190. bool m_valid; // true on successful link
  191. bool m_bCheckLinkStatus;
  192. uint m_revision; // if this pair is relinked, bump this number.
  193. GLint m_locVertexScreenParams; // vcscreen
  194. uint m_nScreenWidthHeight;
  195. };
  196. //===============================================================================
  197. // N-row, M-way associative cache with LRU per row.
  198. // still needs some metric dump ability and some parameter tuning.
  199. // extra credit would be to make an auto-tuner.
  200. struct CGLMPairCacheEntry
  201. {
  202. long long m_lastMark; // a mark of zero means an empty entry
  203. CGLMProgram *m_vertexProg;
  204. CGLMProgram *m_fragmentProg;
  205. uint m_extraKeyBits;
  206. CGLMShaderPair *m_pair;
  207. };
  208. class CGLMShaderPairCache // cache for linked GLSL shader pairs
  209. {
  210. public:
  211. protected:
  212. friend class CGLMShaderPair;
  213. friend class CGLMProgram;
  214. friend class GLMContext;
  215. //===============================
  216. CGLMShaderPairCache( GLMContext *ctx );
  217. ~CGLMShaderPairCache( );
  218. FORCEINLINE CGLMShaderPair *SelectShaderPair ( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits );
  219. void QueryShaderPair ( int index, GLMShaderPairInfo *infoOut );
  220. // shoot down linked pairs that use the program in the arg
  221. // return true if any had to be skipped due to conflict with currently bound pair
  222. bool PurgePairsWithShader( CGLMProgram *prog );
  223. // purge everything (when would GLM know how to do this ? at context destroy time, but any other times?)
  224. // return true if any had to be skipped due to conflict with currently bound pair
  225. bool Purge ( void );
  226. // stats
  227. void DumpStats ( void );
  228. //===============================
  229. FORCEINLINE uint HashRowIndex( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits ) const;
  230. FORCEINLINE CGLMPairCacheEntry* HashRowPtr( uint hashRowIndex ) const;
  231. FORCEINLINE void HashRowProbe( CGLMPairCacheEntry *row, CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits, int &hitway, int &emptyway, int &oldestway );
  232. CGLMShaderPair *SelectShaderPairInternal( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits, int rowIndex );
  233. //===============================
  234. // common stuff
  235. GLMContext *m_ctx; // link back to parent context
  236. long long m_mark;
  237. uint m_rowsLg2;
  238. uint m_rows;
  239. uint m_rowsMask;
  240. uint m_waysLg2;
  241. uint m_ways;
  242. uint m_entryCount;
  243. CGLMPairCacheEntry *m_entries; // array[ m_rows ][ m_ways ]
  244. uint *m_evictions; // array[ m_rows ];
  245. #if GL_SHADER_PAIR_CACHE_STATS
  246. uint *m_hits; // array[ m_rows ];
  247. #endif
  248. };
  249. FORCEINLINE uint CGLMShaderPairCache::HashRowIndex( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits ) const
  250. {
  251. return ( vp->m_nHashTag + fp->m_nHashTag + extraKeyBits * 7 ) & m_rowsMask;
  252. }
  253. FORCEINLINE CGLMPairCacheEntry* CGLMShaderPairCache::HashRowPtr( uint hashRowIndex ) const
  254. {
  255. return &m_entries[ hashRowIndex * m_ways ];
  256. }
  257. FORCEINLINE void CGLMShaderPairCache::HashRowProbe( CGLMPairCacheEntry *row, CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits, int& hitway, int& emptyway, int& oldestway )
  258. {
  259. hitway = -1;
  260. emptyway = -1;
  261. oldestway = -1;
  262. // scan this row to see if the desired pair is present
  263. CGLMPairCacheEntry *cursor = row;
  264. long long oldestmark = 0xFFFFFFFFFFFFFFFFLL;
  265. for( uint way = 0; way < m_ways; ++way )
  266. {
  267. if ( cursor->m_lastMark != 0 ) // occupied slot
  268. {
  269. // check if this is the oldest one on the row - only occupied slots are checked
  270. if ( cursor->m_lastMark < oldestmark )
  271. {
  272. oldestway = way;
  273. oldestmark = cursor->m_lastMark;
  274. }
  275. if ( ( cursor->m_vertexProg == vp ) && ( cursor->m_fragmentProg == fp ) && ( cursor->m_extraKeyBits == extraKeyBits ) ) // match?
  276. {
  277. // found it
  278. hitway = way;
  279. break;
  280. }
  281. }
  282. else
  283. {
  284. // empty way, log it if first one seen
  285. if (emptyway<0)
  286. {
  287. emptyway = way;
  288. }
  289. }
  290. cursor++;
  291. }
  292. }
  293. FORCEINLINE CGLMShaderPair *CGLMShaderPairCache::SelectShaderPair( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits )
  294. {
  295. // select row where pair would be found if it exists
  296. uint rowIndex = HashRowIndex( vp, fp, extraKeyBits );
  297. CGLMPairCacheEntry *pCursor = HashRowPtr( rowIndex );
  298. if ( ( pCursor->m_fragmentProg != fp ) || ( pCursor->m_vertexProg != vp ) || ( pCursor->m_extraKeyBits != extraKeyBits ) )
  299. {
  300. CGLMPairCacheEntry *pLastCursor = pCursor + m_ways;
  301. ++pCursor;
  302. while ( pCursor != pLastCursor )
  303. {
  304. if ( ( pCursor->m_fragmentProg == fp ) && ( pCursor->m_vertexProg == vp ) && ( pCursor->m_extraKeyBits == extraKeyBits ) ) // match?
  305. break;
  306. ++pCursor;
  307. };
  308. if ( pCursor == pLastCursor )
  309. return SelectShaderPairInternal( vp, fp, extraKeyBits, rowIndex );
  310. }
  311. // found it. mark it and return
  312. pCursor->m_lastMark = m_mark++;
  313. #if GL_SHADER_PAIR_CACHE_STATS
  314. // count the hit
  315. m_hits[ rowIndex ] ++;
  316. #endif
  317. return pCursor->m_pair;
  318. }
  319. #endif