Team Fortress 2 Source Code as on 22/4/2020
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

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