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.

1570 lines
44 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.cpp
  26. //
  27. //===============================================================================
  28. #include "togl/rendermechanism.h"
  29. #include "filesystem.h"
  30. #include "tier1/fmtstr.h"
  31. #include "tier1/KeyValues.h"
  32. #include "tier0/fasttimer.h"
  33. #if GLMDEBUG && defined( _MSC_VER )
  34. #include <direct.h>
  35. #endif
  36. // memdbgon -must- be the last include file in a .cpp file.
  37. #include "tier0/memdbgon.h"
  38. #if GLMDEBUG
  39. #define GLM_FREE_SHADER_TEXT 0
  40. #else
  41. #define GLM_FREE_SHADER_TEXT 1
  42. #endif
  43. //===============================================================================
  44. ConVar gl_shaderpair_cacherows_lg2( "gl_paircache_rows_lg2", "10"); // 10 is minimum
  45. ConVar gl_shaderpair_cacheways_lg2( "gl_paircache_ways_lg2", "5"); // 5 is minimum
  46. ConVar gl_shaderpair_cachelog( "gl_shaderpair_cachelog", "0" );
  47. static CCycleCount gShaderCompileTime;
  48. static int gShaderCompileCount = 0;
  49. static CCycleCount gShaderCompileQueryTime;
  50. static CCycleCount gShaderLinkTime;
  51. static int gShaderLinkCount = 0;
  52. static CCycleCount gShaderLinkQueryTime;
  53. CON_COMMAND( gl_shader_compile_time_dump, "Dump stats shader compile time." )
  54. {
  55. ConMsg( "Shader Compile Time: %u ms (Count: %d) / Query: %u ms \n", (uint32)gShaderCompileTime.GetMilliseconds(), gShaderCompileCount, (uint32)gShaderCompileQueryTime.GetMilliseconds() );
  56. ConMsg( "Shader Link Time : %u ms (Count: %d) / Query: %u ms \n", (uint32)gShaderLinkTime.GetMilliseconds(), gShaderLinkCount, (uint32)gShaderLinkQueryTime.GetMilliseconds() );
  57. }
  58. //===============================================================================
  59. GLenum GLMProgTypeToARBEnum( EGLMProgramType type )
  60. {
  61. GLenum result = 0;
  62. switch(type)
  63. {
  64. case kGLMVertexProgram: result = GL_VERTEX_PROGRAM_ARB; break;
  65. case kGLMFragmentProgram: result = GL_FRAGMENT_PROGRAM_ARB; break;
  66. default: Assert( !"bad program type"); result = 0; break;
  67. }
  68. return result;
  69. }
  70. GLenum GLMProgTypeToGLSLEnum( EGLMProgramType type )
  71. {
  72. GLenum result = 0;
  73. switch(type)
  74. {
  75. case kGLMVertexProgram: result = GL_VERTEX_SHADER_ARB; break;
  76. case kGLMFragmentProgram: result = GL_FRAGMENT_SHADER_ARB; break;
  77. default: Assert( !"bad program type"); result = 0; break;
  78. }
  79. return result;
  80. }
  81. CGLMProgram::CGLMProgram( GLMContext *ctx, EGLMProgramType type )
  82. {
  83. m_ctx = ctx;
  84. m_ctx->CheckCurrent();
  85. m_type = type;
  86. m_nHashTag = rand() ^ ( rand() << 15 );
  87. m_text = NULL; // no text yet
  88. #if GLMDEBUG
  89. m_editable = NULL;
  90. #endif
  91. memset( &m_descs, 0, sizeof( m_descs ) );
  92. m_samplerMask = 0; // dxabstract sets this field later
  93. m_samplerTypes = 0;
  94. m_fragDataMask = 0;
  95. m_numDrawBuffers = 0;
  96. memset( &m_drawBuffers, 0, sizeof( m_drawBuffers ) );
  97. m_maxSamplers = GLM_SAMPLER_COUNT;
  98. m_nNumUsedSamplers = GLM_SAMPLER_COUNT;
  99. m_maxVertexAttrs = kGLMVertexAttributeIndexMax;
  100. // create an ARB vp/fp program object name. No need to bind it yet.
  101. GLMShaderDesc *arbDesc = &m_descs[ kGLMARB ];
  102. Assert(gGL);
  103. gGL->glGenProgramsARB( 1, &arbDesc->m_object.arb );
  104. // create a GLSL shader object.
  105. GLMShaderDesc *glslDesc = &m_descs[ kGLMGLSL ];
  106. GLenum glslStage = GLMProgTypeToGLSLEnum( m_type );
  107. glslDesc->m_object.glsl = gGL->glCreateShaderObjectARB( glslStage );;
  108. m_shaderName[0] = '\0';
  109. m_bTranslatedProgram = false;
  110. m_nCentroidMask = 0;
  111. m_nShadowDepthSamplerMask = 0;
  112. m_labelName[0] = '\0';
  113. m_labelIndex = -1;
  114. m_labelCombo = -1;
  115. // no text has arrived yet. That's done in SetProgramText.
  116. }
  117. CGLMProgram::~CGLMProgram( )
  118. {
  119. m_ctx->CheckCurrent();
  120. // if there is an arb program, delete it
  121. GLMShaderDesc *arbDesc = &m_descs[ kGLMARB ];
  122. if (arbDesc->m_object.arb)
  123. {
  124. gGL->glDeleteProgramsARB( 1, &arbDesc->m_object.arb );
  125. arbDesc->m_object.arb = 0;
  126. }
  127. // if there is a GLSL shader, delete it
  128. GLMShaderDesc *glslDesc = &m_descs[kGLMGLSL];
  129. if (glslDesc->m_object.glsl)
  130. {
  131. gGL->glDeleteShader( (uint)glslDesc->m_object.glsl ); // why do I need a cast here again ?
  132. glslDesc->m_object.glsl = 0;
  133. }
  134. #if GLMDEBUG
  135. if (m_editable)
  136. {
  137. delete m_editable;
  138. m_editable = NULL;
  139. }
  140. #endif
  141. if (m_text)
  142. {
  143. free( m_text );
  144. m_text = NULL;
  145. }
  146. m_ctx = NULL;
  147. }
  148. enum EShaderSection
  149. {
  150. kGLMARBVertex, kGLMARBVertexDisabled,
  151. kGLMARBFragment, kGLMARBFragmentDisabled,
  152. kGLMGLSLVertex, kGLMGLSLVertexDisabled,
  153. kGLMGLSLFragment, kGLMGLSLFragmentDisabled,
  154. };
  155. const char *g_shaderSectionMarkers[] = // match ordering of enum
  156. {
  157. "!!ARBvp", "-!!ARBvp", // enabled and disabled markers. so you can have multiple flavors in a blob and activate the one you want.
  158. "!!ARBfp", "-!!ARBfp",
  159. "//GLSLvp", "-//GLSLvp",
  160. "//GLSLfp", "-//GLSLfp",
  161. NULL
  162. };
  163. void CGLMProgram::SetShaderName( const char *name )
  164. {
  165. V_strncpy( m_shaderName, name, sizeof( m_shaderName ) );
  166. }
  167. void CGLMProgram::SetProgramText( char *text )
  168. {
  169. // free old text if any
  170. // clone new text
  171. // scan newtext to find sections
  172. // walk sections, and mark descs to indicate where text is at
  173. if (m_text)
  174. {
  175. free( m_text );
  176. m_text = NULL;
  177. }
  178. // scrub desc text references
  179. for( int i=0; i<kGLMNumProgramTypes; i++)
  180. {
  181. GLMShaderDesc *desc = &m_descs[i];
  182. desc->m_textPresent = false;
  183. desc->m_textOffset = 0;
  184. desc->m_textLength = 0;
  185. }
  186. m_text = strdup( text );
  187. Assert( m_text != NULL );
  188. #if GLMDEBUG
  189. // create editable text item, if it does not already exist
  190. if (!m_editable)
  191. {
  192. char *suffix = "";
  193. switch(m_type)
  194. {
  195. case kGLMVertexProgram: suffix = ".vsh"; break;
  196. case kGLMFragmentProgram: suffix = ".fsh"; break;
  197. default: GLMDebugger();
  198. }
  199. #ifdef POSIX
  200. CFmtStr debugShaderPath( "%s/debugshaders/", getenv( "HOME" ) );
  201. #else
  202. CFmtStr debugShaderPath( "debugshaders/" );
  203. #endif
  204. _mkdir( debugShaderPath.Access() );
  205. m_editable = new CGLMEditableTextItem( m_text, strlen(m_text), false, debugShaderPath.Access(), suffix );
  206. // pull our string back from the editable (it has probably munged it)
  207. if (m_editable->HasData())
  208. {
  209. ReloadStringFromEditable();
  210. }
  211. }
  212. #endif
  213. // scan the text and find sections
  214. CGLMTextSectioner sections( m_text, strlen( m_text ), g_shaderSectionMarkers );
  215. int sectionCount = sections.Count();
  216. for( int i=0; i < sectionCount; i++ )
  217. {
  218. uint subtextOffset = 0;
  219. uint subtextLength = 0;
  220. int markerIndex = 0;
  221. sections.GetSection( i, &subtextOffset, &subtextLength, &markerIndex );
  222. // act on the section
  223. GLMShaderDesc *desc = NULL;
  224. switch( m_type )
  225. {
  226. case kGLMVertexProgram:
  227. switch( markerIndex )
  228. {
  229. case kGLMARBVertex:
  230. case kGLMGLSLVertex:
  231. desc = &m_descs[ (markerIndex==kGLMARBVertex) ? kGLMARB : kGLMGLSL];
  232. // these steps are generic across both langs
  233. desc->m_textPresent = true;
  234. desc->m_textOffset = subtextOffset;
  235. desc->m_textLength = subtextLength;
  236. desc->m_compiled = false;
  237. desc->m_valid = false;
  238. break;
  239. case kGLMARBVertexDisabled:
  240. case kGLMGLSLVertexDisabled:
  241. // ignore quietly
  242. break;
  243. default: Assert(!"Mismatched section marker seen in SetProgramText (VP)"); break;
  244. }
  245. break;
  246. case kGLMFragmentProgram:
  247. switch( markerIndex )
  248. {
  249. case kGLMARBFragment:
  250. case kGLMGLSLFragment:
  251. desc = &m_descs[ (markerIndex==kGLMARBFragment) ? kGLMARB : kGLMGLSL];
  252. // these steps are generic across both langs
  253. desc->m_textPresent = true;
  254. desc->m_textOffset = subtextOffset;
  255. desc->m_textLength = subtextLength;
  256. desc->m_compiled = false;
  257. desc->m_valid = false;
  258. break;
  259. case kGLMARBFragmentDisabled:
  260. case kGLMGLSLFragmentDisabled:
  261. // ignore quietly
  262. break;
  263. default: Assert(!"Mismatched section marker seen in SetProgramText (VP)"); break;
  264. }
  265. break;
  266. }
  267. }
  268. // find the label string
  269. // example:
  270. // trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234
  271. char *lineStr = strstr( m_text, "// trans#" );
  272. if (lineStr)
  273. {
  274. int scratch = -1;
  275. if (this->m_type == kGLMVertexProgram)
  276. {
  277. sscanf( lineStr, "// trans#%d label:vs-file %s vs-index %d vs-combo %d", &scratch, m_labelName, &m_labelIndex, &m_labelCombo );
  278. }
  279. else
  280. {
  281. sscanf( lineStr, "// trans#%d label:ps-file %s ps-index %d ps-combo %d", &scratch, m_labelName, &m_labelIndex, &m_labelCombo );
  282. }
  283. }
  284. }
  285. void CGLMProgram::CompileActiveSources ( void )
  286. {
  287. // compile everything we have text for
  288. for( int i=0; i<kGLMNumProgramTypes; i++)
  289. {
  290. if (m_descs[i].m_textPresent)
  291. {
  292. Compile( (EGLMProgramLang)i );
  293. }
  294. }
  295. }
  296. void CGLMProgram::Compile( EGLMProgramLang lang )
  297. {
  298. bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0);
  299. // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles.
  300. CFastTimer shaderCompileTimer;
  301. if (bTimeShaderCompiles)
  302. {
  303. shaderCompileTimer.Start();
  304. }
  305. bool noisy = false; noisy;
  306. int loglevel = gl_shaderpair_cachelog.GetInt();
  307. switch( lang )
  308. {
  309. case kGLMARB:
  310. {
  311. GLMShaderDesc *arbDesc;
  312. arbDesc = &m_descs[ kGLMARB ];
  313. // make sure no GLSL program is set up
  314. gGL->glUseProgram(0);
  315. // bind our program container to context
  316. GLenum arbTarget = GLMProgTypeToARBEnum( m_type );
  317. glSetEnable( arbTarget, true ); // unclear if I need this to compile or just to draw...
  318. gGL->glBindProgramARB( arbTarget, arbDesc->m_object.arb ); // object created or just re-bound
  319. char *section = m_text + arbDesc->m_textOffset;
  320. char *lastCharOfSection = section + arbDesc->m_textLength; // actually it's one past the last textual character
  321. lastCharOfSection;
  322. #if GLMDEBUG
  323. if(noisy)
  324. {
  325. GLMPRINTF((">-D- CGLMProgram::Compile submitting following text for ARB %s program (name %d) ---------------------",
  326. arbTarget == GL_FRAGMENT_PROGRAM_ARB ? "fragment" : "vertex",
  327. arbDesc->m_object.arb ));
  328. // we don't have a "print this many chars" call yet
  329. // just temporarily null terminate the text we want to print
  330. char saveChar = *lastCharOfSection;
  331. *lastCharOfSection= 0;
  332. GLMPRINTTEXT(( section, eDebugDump ));
  333. *lastCharOfSection= saveChar;
  334. GLMPRINTF(("<-D- CGLMProgram::Compile ARB EOT--" ));
  335. }
  336. #endif
  337. gGL->glProgramStringARB( arbTarget, GL_PROGRAM_FORMAT_ASCII_ARB, arbDesc->m_textLength, section );
  338. arbDesc->m_compiled = true; // compiled but not necessarily valid
  339. CheckValidity( lang );
  340. // leave it bound n enabled, don't care (draw will sort it all out)
  341. }
  342. break;
  343. case kGLMGLSL:
  344. {
  345. GLMShaderDesc *glslDesc;
  346. glslDesc = &m_descs[ kGLMGLSL ];
  347. GLenum glslStage = GLMProgTypeToGLSLEnum( m_type );
  348. glslStage;
  349. // there's no binding to do for GLSL. but make sure no ARB stuff is bound for tidiness.
  350. glSetEnable( GL_VERTEX_PROGRAM_ARB, false );
  351. glSetEnable( GL_FRAGMENT_PROGRAM_ARB, false ); // add check errors on these
  352. gGL->glBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 );
  353. gGL->glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, 0 );
  354. // no GLSL program either
  355. gGL->glUseProgram(0);
  356. // pump text into GLSL shader object
  357. char *section = m_text + glslDesc->m_textOffset;
  358. char *lastCharOfSection = section + glslDesc->m_textLength; // actually it's one past the last textual character
  359. lastCharOfSection;
  360. #if GLMDEBUG
  361. if(noisy)
  362. {
  363. GLMPRINTF((">-D- CGLMProgram::Compile submitting following text for GLSL %s program (name %d) ---------------------",
  364. glslStage == GL_FRAGMENT_SHADER_ARB ? "fragment" : "vertex",
  365. glslDesc->m_object.glsl ));
  366. // we don't have a "print this many chars" call yet
  367. // just temporarily null terminate the text we want to print
  368. char saveChar = *lastCharOfSection;
  369. *lastCharOfSection= 0;
  370. GLMPRINTTEXT(( section, eDebugDump ));
  371. *lastCharOfSection= saveChar;
  372. GLMPRINTF(("<-D- CGLMProgram::Compile GLSL EOT--" ));
  373. }
  374. #endif
  375. gGL->glShaderSourceARB( glslDesc->m_object.glsl, 1, (const GLchar **)&section, &glslDesc->m_textLength);
  376. #if GLM_FREE_SHADER_TEXT
  377. // Free the shader program text - not needed anymore (GL has its own copy)
  378. if ( m_text && !m_descs[kGLMARB].m_textPresent )
  379. {
  380. free( m_text );
  381. m_text = NULL;
  382. }
  383. #endif
  384. // compile
  385. gGL->glCompileShaderARB( glslDesc->m_object.glsl );
  386. glslDesc->m_compiled = true; // compiled but not necessarily valid
  387. // Check shader validity at creation time. This will cause the driver to not be able to
  388. // multi-thread/defer shader compiles, but it is useful for getting error messages on the
  389. // shader when it is compiled
  390. bool bValidateShaderEarly = (CommandLine()->FindParm( "-gl_validate_shader_early" ) != 0);
  391. if (bValidateShaderEarly)
  392. {
  393. CheckValidity( lang );
  394. }
  395. if (loglevel>=2)
  396. {
  397. char tempname[128];
  398. //int tempindex = -1;
  399. //int tempcombo = -1;
  400. //GetLabelIndexCombo( tempname, sizeof(tempname), &tempindex, &tempcombo );
  401. //printf("\ncompile: - [ %s/%d/%d ] on GL name %d ", tempname, tempindex, tempcombo, glslDesc->m_object.glsl );
  402. GetComboIndexNameString( tempname, sizeof(tempname) );
  403. printf("\ncompile: %s on GL name %d ", tempname, glslDesc->m_object.glsl );
  404. }
  405. }
  406. break;
  407. }
  408. if (bTimeShaderCompiles)
  409. {
  410. shaderCompileTimer.End();
  411. gShaderCompileTime += shaderCompileTimer.GetDuration();
  412. gShaderCompileCount++;
  413. }
  414. }
  415. #if GLMDEBUG
  416. bool CGLMProgram::PollForChanges( void )
  417. {
  418. bool result = false;
  419. if (m_editable)
  420. {
  421. result = m_editable->PollForChanges();
  422. }
  423. return result;
  424. }
  425. void CGLMProgram::ReloadStringFromEditable( void )
  426. {
  427. uint dataSize=0;
  428. char *data=NULL;
  429. m_editable->GetCurrentText( &data, &dataSize );
  430. char *buf = (char *)malloc( dataSize+1 ); // we will NULL terminate it, since the mirror copy might not be
  431. memcpy( buf, data, dataSize );
  432. buf[dataSize] = 0;
  433. SetProgramText( buf );
  434. free( buf );
  435. }
  436. bool CGLMProgram::SyncWithEditable( void )
  437. {
  438. bool result = false;
  439. if (m_editable->PollForChanges())
  440. {
  441. ReloadStringFromEditable();
  442. CompileActiveSources();
  443. // invalidate shader pair cache entries using this shader..
  444. m_ctx->m_pairCache->PurgePairsWithShader( this );
  445. result = true; // result true means "it changed"
  446. }
  447. return result;
  448. }
  449. #endif
  450. // attributes which are general to both stages
  451. // VP and FP:
  452. //
  453. // 0x88A0 PROGRAM_INSTRUCTIONS_ARB VP FP
  454. // 0x88A1 MAX_PROGRAM_INSTRUCTIONS_ARB VP FP
  455. // 0x88A2 PROGRAM_NATIVE_INSTRUCTIONS_ARB VP FP
  456. // 0x88A3 MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB VP FP
  457. //
  458. // 0x88A4 PROGRAM_TEMPORARIES_ARB VP FP
  459. // 0x88A5 MAX_PROGRAM_TEMPORARIES_ARB VP FP
  460. // 0x88A6 PROGRAM_NATIVE_TEMPORARIES_ARB VP FP
  461. // 0x88A7 MAX_PROGRAM_NATIVE_TEMPORARIES_ARB VP FP
  462. //
  463. // 0x88A8 PROGRAM_PARAMETERS_ARB VP FP
  464. // 0x88A9 MAX_PROGRAM_PARAMETERS_ARB VP FP
  465. // 0x88AA PROGRAM_NATIVE_PARAMETERS_ARB VP FP
  466. // 0x88AB MAX_PROGRAM_NATIVE_PARAMETERS_ARB VP FP
  467. //
  468. // 0x88AC PROGRAM_ATTRIBS_ARB VP FP
  469. // 0x88AD MAX_PROGRAM_ATTRIBS_ARB VP FP
  470. // 0x88AE PROGRAM_NATIVE_ATTRIBS_ARB VP FP
  471. // 0x88AF MAX_PROGRAM_NATIVE_ATTRIBS_ARB VP FP
  472. //
  473. // 0x88B4 MAX_PROGRAM_LOCAL_PARAMETERS_ARB VP FP
  474. // 0x88B5 MAX_PROGRAM_ENV_PARAMETERS_ARB VP FP
  475. // 0x88B6 PROGRAM_UNDER_NATIVE_LIMITS_ARB VP FP
  476. //
  477. // VP only:
  478. //
  479. // 0x88B0 PROGRAM_ADDRESS_REGISTERS_ARB VP
  480. // 0x88B1 MAX_PROGRAM_ADDRESS_REGISTERS_ARB VP
  481. // 0x88B2 PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB VP
  482. // 0x88B3 MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB VP
  483. //
  484. // FP only:
  485. //
  486. // 0x8805 PROGRAM_ALU_INSTRUCTIONS_ARB FP
  487. // 0x880B MAX_PROGRAM_ALU_INSTRUCTIONS_ARB FP
  488. // 0x8808 PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB FP
  489. // 0x880E MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB FP
  490. // 0x8806 PROGRAM_TEX_INSTRUCTIONS_ARB FP
  491. // 0x880C MAX_PROGRAM_TEX_INSTRUCTIONS_ARB FP
  492. // 0x8809 PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB FP
  493. // 0x880F MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB FP
  494. // 0x8807 PROGRAM_TEX_INDIRECTIONS_ARB FP
  495. // 0x880D MAX_PROGRAM_TEX_INDIRECTIONS_ARB FP
  496. // 0x880A PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB FP
  497. // 0x8810 MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB FP
  498. struct GLMShaderLimitDesc
  499. {
  500. GLenum m_valueEnum;
  501. GLenum m_limitEnum;
  502. const char *m_debugName;
  503. char m_flags;
  504. // m_flags - 0x01 for VP, 0x02 for FP, or set both if applicable to both
  505. };
  506. // macro to help make the table of what to check
  507. #ifndef LMD
  508. #define LMD( val, flags ) { GL_PROGRAM_##val##_ARB, GL_MAX_PROGRAM_##val##_ARB, #val, flags }
  509. #else
  510. #error you need to use a different name for this macro.
  511. #endif
  512. GLMShaderLimitDesc g_glmShaderLimitDescs[] =
  513. {
  514. // VP and FP..
  515. LMD( INSTRUCTIONS, 3 ),
  516. LMD( NATIVE_INSTRUCTIONS, 3 ),
  517. LMD( NATIVE_TEMPORARIES, 3 ),
  518. LMD( PARAMETERS, 3 ),
  519. LMD( NATIVE_PARAMETERS, 3 ),
  520. LMD( ATTRIBS, 3 ),
  521. LMD( NATIVE_ATTRIBS, 3 ),
  522. // VP only..
  523. LMD( ADDRESS_REGISTERS, 1 ),
  524. LMD( NATIVE_ADDRESS_REGISTERS, 1 ),
  525. // FP only..
  526. LMD( ALU_INSTRUCTIONS, 2 ),
  527. LMD( NATIVE_ALU_INSTRUCTIONS, 2 ),
  528. LMD( TEX_INSTRUCTIONS, 2 ),
  529. LMD( NATIVE_TEX_INSTRUCTIONS, 2 ),
  530. LMD( TEX_INDIRECTIONS, 2 ),
  531. LMD( NATIVE_TEX_INDIRECTIONS, 2 ),
  532. { 0, 0, NULL, 0 }
  533. };
  534. #undef LMD
  535. bool CGLMProgram::CheckValidity( EGLMProgramLang lang )
  536. {
  537. static char *targnames[] = { "vertex", "fragment" };
  538. bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0);
  539. // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles.
  540. CFastTimer shaderCompileTimer;
  541. if (bTimeShaderCompiles)
  542. {
  543. shaderCompileTimer.Start();
  544. }
  545. bool bValid = false;
  546. switch(lang)
  547. {
  548. case kGLMARB:
  549. {
  550. GLMShaderDesc *arbDesc;
  551. arbDesc = &m_descs[ kGLMARB ];
  552. GLenum arbTarget = GLMProgTypeToARBEnum( m_type );
  553. Assert( arbDesc->m_compiled );
  554. arbDesc->m_valid = true; // assume success til we see otherwise
  555. // assume program is bound. is there anything wrong with it ?
  556. GLint isNative=0;
  557. gGL->glGetProgramivARB( arbTarget, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &isNative );
  558. // If the program is over the hardware's limits, print out some information
  559. if (isNative!=1)
  560. {
  561. arbDesc->m_valid = false;
  562. // check everything we can check
  563. char checkmask = (1<<m_type); // 1 for VP, 2 for FP
  564. for( GLMShaderLimitDesc *desc = g_glmShaderLimitDescs; desc->m_valueEnum !=0; desc++ )
  565. {
  566. if ( desc->m_flags & checkmask )
  567. {
  568. // test it
  569. GLint value = 0;
  570. GLint limit = 0;
  571. gGL->glGetProgramivARB(arbTarget, desc->m_valueEnum, &value);
  572. gGL->glGetProgramivARB(arbTarget, desc->m_limitEnum, &limit);
  573. if (value > limit)
  574. {
  575. GLMPRINTF(("-D- Invalid %s program: program has %d %s; limit is %d", targnames[ m_type ], value, desc->m_debugName, limit ));
  576. }
  577. }
  578. }
  579. }
  580. // syntax error check
  581. GLint errorLine;
  582. gGL->glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errorLine );
  583. if ( errorLine!=-1 )
  584. {
  585. const GLubyte* errorString = gGL->glGetString(GL_PROGRAM_ERROR_STRING_ARB); errorString;
  586. GLMPRINTF(( "-D- Syntax error in ARB %s program: %s",targnames[ m_type ], errorString ));
  587. arbDesc->m_valid = false;
  588. }
  589. if (!arbDesc->m_valid)
  590. {
  591. char *temp = strdup(m_text);
  592. temp[ arbDesc->m_textOffset + arbDesc->m_textLength ] = 0;
  593. GLMPRINTF(("-D- ----- ARB compile failed; bad source follows -----" ));
  594. GLMPRINTTEXT(( temp + arbDesc->m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES ));
  595. GLMPRINTF(("-D- -----end-----" ));
  596. free( temp );
  597. }
  598. bValid = arbDesc->m_valid;
  599. }
  600. break;
  601. case kGLMGLSL:
  602. {
  603. GLMShaderDesc *glslDesc;
  604. GLcharARB *logString = NULL;
  605. glslDesc = &m_descs[ kGLMGLSL ];
  606. GLenum glslStage = GLMProgTypeToGLSLEnum( m_type ); glslStage;
  607. Assert( glslDesc->m_compiled );
  608. glslDesc->m_valid = true; // assume success til we see otherwise
  609. // GLSL error check
  610. int compiled = 0, length = 0, laux = 0;
  611. gGL->glGetObjectParameterivARB( (GLhandleARB)glslDesc->m_object.glsl, GL_OBJECT_COMPILE_STATUS_ARB, &compiled);
  612. gGL->glGetObjectParameterivARB( (GLhandleARB)glslDesc->m_object.glsl, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
  613. if ( length > 0 )
  614. {
  615. logString = (GLcharARB *)malloc(length * sizeof(GLcharARB));
  616. gGL->glGetInfoLogARB((GLhandleARB)glslDesc->m_object.glsl, length, &laux, logString);
  617. }
  618. // we may not be able to check "native limits" stuff until link time. meh
  619. if (!compiled)
  620. {
  621. glslDesc->m_valid = false;
  622. }
  623. if (!glslDesc->m_valid)
  624. {
  625. GLMPRINTF(("-D- ----- GLSL compile failed: \n %s \n",logString ));
  626. #if !GLM_FREE_SHADER_TEXT
  627. char *temp = strdup(m_text);
  628. temp[ glslDesc->m_textOffset + glslDesc->m_textLength ] = 0;
  629. GLMPRINTTEXT(( temp + glslDesc->m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES ));
  630. free( temp );
  631. #endif
  632. GLMPRINTF(("-D- -----end-----" ));
  633. }
  634. if ( logString )
  635. free( logString );
  636. bValid = glslDesc->m_valid;
  637. }
  638. break;
  639. }
  640. if ( !bValid )
  641. {
  642. GLMDebugPrintf( "Compile of \"%s\" Failed:\n%s\n", m_shaderName, m_text ? m_text : "" );
  643. }
  644. AssertOnce( bValid );
  645. if (bTimeShaderCompiles)
  646. {
  647. shaderCompileTimer.End();
  648. gShaderCompileQueryTime += shaderCompileTimer.GetDuration();
  649. }
  650. return bValid;
  651. }
  652. void CGLMProgram::LogSlow( EGLMProgramLang lang )
  653. {
  654. // find the desc, see if it's marked
  655. GLMShaderDesc *desc = &m_descs[ lang ];
  656. if (!desc->m_slowMark)
  657. {
  658. #if !GLM_FREE_SHADER_TEXT
  659. // log it
  660. printf( "\n-------------- Slow %s ( CGLMProgram @ %p, lang %s, name %d ) : \n%s \n",
  661. m_type==kGLMVertexProgram ? "VS" : "FS",
  662. this,
  663. lang==kGLMGLSL ? "GLSL" : "ARB",
  664. (int)(lang==kGLMGLSL ? (int)desc->m_object.glsl : (int)desc->m_object.arb),
  665. m_text
  666. );
  667. #endif
  668. }
  669. else // complain on a decreasing basis (powers of two)
  670. {
  671. if ( (desc->m_slowMark & (desc->m_slowMark-1)) == 0 )
  672. {
  673. // short blurb
  674. printf( "\n Slow %s ( CGLMProgram @ %p, lang %s, name %d ) (%d times)",
  675. m_type==kGLMVertexProgram ? "VS" : "FS",
  676. this,
  677. lang==kGLMGLSL ? "GLSL" : "ARB",
  678. (int)(lang==kGLMGLSL ? (int)desc->m_object.glsl : (int)desc->m_object.arb),
  679. desc->m_slowMark+1
  680. );
  681. }
  682. }
  683. // mark it
  684. desc->m_slowMark++;
  685. }
  686. void CGLMProgram::GetLabelIndexCombo ( char *labelOut, int labelOutMaxChars, int *indexOut, int *comboOut )
  687. {
  688. // find the label string
  689. // example:
  690. // trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234
  691. // Done in SetProgramText
  692. *labelOut = 0;
  693. *indexOut = -1;
  694. if ((strlen( m_labelName ) != 0))
  695. {
  696. Q_strncpy( labelOut, m_labelName, labelOutMaxChars );
  697. *indexOut = m_labelIndex;
  698. *comboOut = m_labelCombo;
  699. }
  700. }
  701. void CGLMProgram::GetComboIndexNameString ( char *stringOut, int stringOutMaxChars ) // mmmmmmmm-nnnnnnnn-filename
  702. {
  703. // find the label string
  704. // example:
  705. // trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234
  706. // Done in SetProgramText
  707. *stringOut = 0;
  708. int len = strlen( m_labelName );
  709. if ( (len+20) < stringOutMaxChars )
  710. {
  711. // output formatted version
  712. sprintf( stringOut, "%08X-%08X-%s", m_labelCombo, m_labelIndex, m_labelName );
  713. }
  714. }
  715. //===============================================================================
  716. CGLMShaderPair::CGLMShaderPair( GLMContext *ctx )
  717. {
  718. m_ctx = ctx;
  719. m_vertexProg = m_fragmentProg = NULL;
  720. m_program = gGL->glCreateProgramObjectARB();
  721. m_locVertexParams = -1;
  722. m_locVertexBoneParams = -1;
  723. m_locVertexScreenParams = -1;
  724. m_nScreenWidthHeight = 0xFFFFFFFF;
  725. m_locVertexInteger0 = -1; // "i0"
  726. memset( m_locVertexBool, 0xFF, sizeof( m_locVertexBool ) );
  727. memset( m_locFragmentBool, 0xFF, sizeof( m_locFragmentBool ) );
  728. m_bHasBoolOrIntUniforms = false;
  729. m_locFragmentParams = -1;
  730. m_locFragmentFakeSRGBEnable = -1;
  731. m_fakeSRGBEnableValue = -1.0f;
  732. memset( m_locSamplers, 0xFF, sizeof( m_locSamplers ) );
  733. m_valid = false;
  734. m_bCheckLinkStatus = false;
  735. m_revision = 0; // bumps to 1 once linked
  736. }
  737. CGLMShaderPair::~CGLMShaderPair( )
  738. {
  739. if (m_program)
  740. {
  741. gGL->glDeleteObjectARB( (GLhandleARB)m_program );
  742. m_program = 0;
  743. }
  744. }
  745. bool CGLMShaderPair::ValidateProgramPair()
  746. {
  747. if ( m_vertexProg && m_vertexProg->m_descs[kGLMGLSL].m_textPresent && !m_vertexProg->m_descs[kGLMGLSL].m_valid )
  748. {
  749. m_vertexProg->CheckValidity( kGLMGLSL );
  750. }
  751. if (m_fragmentProg && m_fragmentProg->m_descs[kGLMGLSL].m_textPresent && !m_fragmentProg->m_descs[kGLMGLSL].m_valid)
  752. {
  753. m_fragmentProg->CheckValidity( kGLMGLSL );
  754. }
  755. if ( !m_valid && m_bCheckLinkStatus )
  756. {
  757. bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0);
  758. // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles.
  759. CFastTimer shaderCompileTimer;
  760. if (bTimeShaderCompiles)
  761. {
  762. shaderCompileTimer.Start();
  763. }
  764. // check for success
  765. GLint result = 0;
  766. gGL->glGetObjectParameterivARB( m_program, GL_OBJECT_LINK_STATUS_ARB, &result ); // want GL_TRUE
  767. m_bCheckLinkStatus = false;
  768. if (result == GL_TRUE)
  769. {
  770. // success
  771. m_valid = true;
  772. m_revision++;
  773. }
  774. else
  775. {
  776. GLint length = 0;
  777. GLint laux = 0;
  778. // do some digging
  779. gGL->glGetObjectParameterivARB( m_program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length );
  780. GLcharARB *logString = (GLcharARB *)malloc( length * sizeof(GLcharARB) );
  781. gGL->glGetInfoLogARB( m_program, length, &laux, logString );
  782. GLMPRINTF( ("-D- ----- GLSL link failed: \n %s ", logString) );
  783. #if !GLM_FREE_SHADER_TEXT
  784. char *vtemp = strdup( m_vertexProg->m_text );
  785. vtemp[m_vertexProg->m_descs[kGLMGLSL].m_textOffset + m_vertexProg->m_descs[kGLMGLSL].m_textLength] = 0;
  786. char *ftemp = strdup( m_fragmentProg->m_text );
  787. ftemp[m_fragmentProg->m_descs[kGLMGLSL].m_textOffset + m_fragmentProg->m_descs[kGLMGLSL].m_textLength] = 0;
  788. GLMPRINTF( ("-D- ----- GLSL vertex program selected: %08x (handle %08x)", m_vertexProg, m_vertexProg->m_descs[kGLMGLSL].m_object.glsl) );
  789. GLMPRINTTEXT( (vtemp + m_vertexProg->m_descs[kGLMGLSL].m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES) );
  790. GLMPRINTF( ("-D- ----- GLSL fragment program selected: %08x (handle %08x)", m_fragmentProg, m_fragmentProg->m_descs[kGLMGLSL].m_object.glsl) );
  791. GLMPRINTTEXT( (ftemp + m_fragmentProg->m_descs[kGLMGLSL].m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES) );
  792. free( ftemp );
  793. free( vtemp );
  794. #endif
  795. free( logString );
  796. GLMPRINTF( ("-D- -----end-----") );
  797. }
  798. if (m_valid)
  799. {
  800. gGL->glUseProgram( m_program );
  801. m_ctx->NewLinkedProgram();
  802. m_locVertexParams = gGL->glGetUniformLocationARB( m_program, "vc" );
  803. m_locVertexBoneParams = gGL->glGetUniformLocationARB( m_program, "vcbones" );
  804. m_locVertexScreenParams = gGL->glGetUniformLocationARB( m_program, "vcscreen" );
  805. m_nScreenWidthHeight = 0xFFFFFFFF;
  806. m_locVertexInteger0 = gGL->glGetUniformLocationARB( m_program, "i0" );
  807. m_bHasBoolOrIntUniforms = false;
  808. if (m_locVertexInteger0 >= 0)
  809. m_bHasBoolOrIntUniforms = true;
  810. for (uint i = 0; i < cMaxVertexShaderBoolUniforms; i++)
  811. {
  812. char buf[256];
  813. V_snprintf( buf, sizeof(buf), "b%d", i );
  814. m_locVertexBool[i] = gGL->glGetUniformLocationARB( m_program, buf );
  815. if (m_locVertexBool[i] != -1)
  816. m_bHasBoolOrIntUniforms = true;
  817. }
  818. for (uint i = 0; i < cMaxFragmentShaderBoolUniforms; i++)
  819. {
  820. char buf[256];
  821. V_snprintf( buf, sizeof(buf), "fb%d", i );
  822. m_locFragmentBool[i] = gGL->glGetUniformLocationARB( m_program, buf );
  823. if (m_locFragmentBool[i] != -1)
  824. m_bHasBoolOrIntUniforms = true;
  825. }
  826. m_locFragmentParams = gGL->glGetUniformLocationARB( m_program, "pc" );
  827. for (uint i = 0; i < kGLMNumProgramTypes; i++)
  828. {
  829. m_NumUniformBufferParams[i] = 0;
  830. if (i == kGLMVertexProgram)
  831. {
  832. if (m_locVertexParams < 0)
  833. continue;
  834. }
  835. else if (m_locFragmentParams < 0)
  836. continue;
  837. const uint nNum = (i == kGLMVertexProgram) ? m_vertexProg->m_descs[kGLMGLSL].m_highWater : m_fragmentProg->m_descs[kGLMGLSL].m_highWater;
  838. uint j;
  839. for (j = 0; j < nNum; j++)
  840. {
  841. char buf[256];
  842. V_snprintf( buf, sizeof(buf), "%cc[%i]", "vp"[i], j );
  843. // Grab the handle of each array element, so we can more efficiently update array elements in the middle.
  844. int l = m_UniformBufferParams[i][j] = gGL->glGetUniformLocationARB( m_program, buf );
  845. if (l < 0)
  846. break;
  847. }
  848. m_NumUniformBufferParams[i] = j;
  849. }
  850. m_locFragmentFakeSRGBEnable = gGL->glGetUniformLocationARB( m_program, "flSRGBWrite" );
  851. m_fakeSRGBEnableValue = -1.0f;
  852. for (int sampler = 0; sampler < 16; sampler++)
  853. {
  854. char tmp[16];
  855. sprintf( tmp, "sampler%d", sampler ); // sampler0 .. sampler1.. etc
  856. GLint nLoc = gGL->glGetUniformLocationARB( m_program, tmp );
  857. m_locSamplers[sampler] = nLoc;
  858. if (nLoc >= 0)
  859. {
  860. gGL->glUniform1iARB( nLoc, sampler );
  861. }
  862. }
  863. }
  864. else
  865. {
  866. m_locVertexParams = -1;
  867. m_locVertexBoneParams = -1;
  868. m_locVertexScreenParams = -1;
  869. m_nScreenWidthHeight = 0xFFFFFFFF;
  870. m_locVertexInteger0 = -1;
  871. memset( m_locVertexBool, 0xFF, sizeof(m_locVertexBool) );
  872. memset( m_locFragmentBool, 0xFF, sizeof(m_locFragmentBool) );
  873. m_bHasBoolOrIntUniforms = false;
  874. m_locFragmentParams = -1;
  875. m_locFragmentFakeSRGBEnable = -1;
  876. m_fakeSRGBEnableValue = -999;
  877. memset( m_locSamplers, 0xFF, sizeof(m_locSamplers) );
  878. m_revision = 0;
  879. }
  880. if (bTimeShaderCompiles)
  881. {
  882. shaderCompileTimer.End();
  883. gShaderLinkQueryTime += shaderCompileTimer.GetDuration();
  884. }
  885. }
  886. return m_valid;
  887. }
  888. // glUseProgram() will be called as a side effect!
  889. bool CGLMShaderPair::SetProgramPair( CGLMProgram *vp, CGLMProgram *fp )
  890. {
  891. bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0);
  892. // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles.
  893. CFastTimer shaderCompileTimer;
  894. if (bTimeShaderCompiles)
  895. {
  896. shaderCompileTimer.Start();
  897. }
  898. m_valid = false; // assume failure
  899. // No need to check that vp and fp are valid at this point (ie shader compile succeed)
  900. // It is permissible to attach a shader object to a program before source code has been loaded
  901. // into the shader object or before the shader object has been compiled. The program won't
  902. // link if one or more of the shader objects has not been successfully compiled.
  903. // (Defer querying the compile and link status to take advantage of GLSL shaders
  904. // building in parallels)
  905. bool vpgood = (vp != NULL);
  906. bool fpgood = (fp != NULL);
  907. if ( !fpgood )
  908. {
  909. // fragment side allowed to be "null".
  910. fp = m_ctx->m_pNullFragmentProgram;
  911. }
  912. if ( vpgood && fpgood )
  913. {
  914. if ( vp->m_nCentroidMask != fp->m_nCentroidMask )
  915. {
  916. Warning( "CGLMShaderPair::SetProgramPair: Centroid masks differ at link time of vertex shader %s and pixel shader %s!\n",
  917. vp->m_shaderName, fp->m_shaderName );
  918. }
  919. // attempt link. but first, detach any previously attached programs
  920. if (m_vertexProg)
  921. {
  922. gGL->glDetachObjectARB(m_program, m_vertexProg->m_descs[kGLMGLSL].m_object.glsl);
  923. m_vertexProg = NULL;
  924. }
  925. if (m_fragmentProg)
  926. {
  927. gGL->glDetachObjectARB(m_program, m_fragmentProg->m_descs[kGLMGLSL].m_object.glsl);
  928. m_fragmentProg = NULL;
  929. }
  930. // now attach
  931. gGL->glAttachObjectARB( m_program, vp->m_descs[kGLMGLSL].m_object.glsl );
  932. m_vertexProg = vp;
  933. gGL->glAttachObjectARB( m_program, fp->m_descs[kGLMGLSL].m_object.glsl );
  934. m_fragmentProg = fp;
  935. // force the locations for input attributes v0-vN to be at locations 0-N
  936. // 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.
  937. // fallback - just force v0-v15 to land in locations 0-15 as a standard.
  938. for( int i = 0; i < 16; i++ )
  939. {
  940. char tmp[16];
  941. sprintf(tmp, "v%d", i); // v0 v1 v2 ... et al
  942. gGL->glBindAttribLocationARB( m_program, i, tmp );
  943. }
  944. #if !GLM_FREE_SHADER_TEXT
  945. if (CommandLine()->CheckParm("-dumpallshaders"))
  946. {
  947. // Dump all shaders, for debugging.
  948. FILE* pFile = fopen("shaderdump.txt", "a+");
  949. if (pFile)
  950. {
  951. fprintf(pFile, "--------------VP:%s\n%s\n", vp->m_shaderName, vp->m_text);
  952. fprintf(pFile, "--------------FP:%s\n%s\n", fp->m_shaderName, fp->m_text);
  953. fclose(pFile);
  954. }
  955. }
  956. #endif
  957. // now link
  958. gGL->glLinkProgramARB( m_program );
  959. m_bCheckLinkStatus = true;
  960. }
  961. else
  962. {
  963. // fail
  964. Assert(!"Can't link these programs");
  965. }
  966. // Check shader validity at creation time. This will cause the driver to not be able to
  967. // multi-thread/defer shader compiles, but it is useful for getting error messages on the
  968. // shader when it is compiled
  969. bool bValidateShaderEarly = (CommandLine()->FindParm( "-gl_validate_shader_early" ) != 0);
  970. if (bValidateShaderEarly)
  971. {
  972. ValidateProgramPair();
  973. }
  974. if (bTimeShaderCompiles)
  975. {
  976. shaderCompileTimer.End();
  977. gShaderLinkTime += shaderCompileTimer.GetDuration();
  978. gShaderLinkCount++;
  979. }
  980. return m_valid;
  981. }
  982. bool CGLMShaderPair::RefreshProgramPair ( void )
  983. {
  984. // re-link and re-query the uniforms.
  985. // since SetProgramPair knows how to detach previously attached shader objects, just pass the same ones in again.
  986. CGLMProgram *vp = m_vertexProg;
  987. CGLMProgram *fp = m_fragmentProg;
  988. bool vpgood = (vp!=NULL) && (vp->m_descs[ kGLMGLSL ].m_valid);
  989. bool fpgood = (fp!=NULL) && (fp->m_descs[ kGLMGLSL ].m_valid);
  990. if (vpgood && fpgood)
  991. {
  992. SetProgramPair( vp, fp );
  993. }
  994. else
  995. {
  996. DebuggerBreak();
  997. return false;
  998. }
  999. return false;
  1000. }
  1001. //===============================================================================
  1002. CGLMShaderPairCache::CGLMShaderPairCache( GLMContext *ctx )
  1003. {
  1004. m_ctx = ctx;
  1005. m_mark = 1;
  1006. m_rowsLg2 = gl_shaderpair_cacherows_lg2.GetInt();
  1007. if (m_rowsLg2 < 10)
  1008. m_rowsLg2 = 10;
  1009. m_rows = 1<<m_rowsLg2;
  1010. m_rowsMask = m_rows - 1;
  1011. m_waysLg2 = gl_shaderpair_cacheways_lg2.GetInt();
  1012. if (m_waysLg2 < 5)
  1013. m_waysLg2 = 5;
  1014. m_ways = 1<<m_waysLg2;
  1015. m_entryCount = m_rows * m_ways;
  1016. uint entryTableSize = m_rows * m_ways * sizeof(CGLMPairCacheEntry);
  1017. m_entries = (CGLMPairCacheEntry*)malloc( entryTableSize ); // array[ m_rows ][ m_ways ]
  1018. memset( m_entries, 0, entryTableSize );
  1019. uint evictTableSize = m_rows * sizeof(uint);
  1020. m_evictions = (uint*)malloc( evictTableSize );
  1021. memset (m_evictions, 0, evictTableSize);
  1022. #if GL_SHADER_PAIR_CACHE_STATS
  1023. // hit counter table is same size
  1024. m_hits = (uint*)malloc( evictTableSize );
  1025. memset (m_hits, 0, evictTableSize);
  1026. #endif
  1027. }
  1028. CGLMShaderPairCache::~CGLMShaderPairCache( )
  1029. {
  1030. if (gl_shaderpair_cachelog.GetInt())
  1031. {
  1032. DumpStats();
  1033. }
  1034. // free all the built pairs
  1035. // free the entry table
  1036. bool purgeResult = this->Purge();
  1037. (void)purgeResult;
  1038. Assert( !purgeResult );
  1039. if (m_entries)
  1040. {
  1041. free( m_entries );
  1042. m_entries = NULL;
  1043. }
  1044. if (m_evictions)
  1045. {
  1046. free( m_evictions );
  1047. m_evictions = NULL;
  1048. }
  1049. #if GL_SHADER_PAIR_CACHE_STATS
  1050. if (m_hits)
  1051. {
  1052. free( m_hits );
  1053. m_hits = NULL;
  1054. }
  1055. #endif
  1056. }
  1057. // Set this convar internally to build or add to the shader pair cache file (link hints)
  1058. // We really only expect this to work on POSIX
  1059. static ConVar glm_cacheprograms( "glm_cacheprograms", "0", FCVAR_DEVELOPMENTONLY );
  1060. #define PROGRAM_CACHE_FILE "program_cache.cfg"
  1061. static void WriteToProgramCache( CGLMShaderPair *pair )
  1062. {
  1063. KeyValues *pProgramCache = new KeyValues( "programcache" );
  1064. pProgramCache->LoadFromFile( g_pFullFileSystem, PROGRAM_CACHE_FILE, "MOD" );
  1065. if ( !pProgramCache )
  1066. {
  1067. Warning( "Could not write to program cache file!\n" );
  1068. return;
  1069. }
  1070. // extract values of interest which represent a pair of shaders
  1071. char vprogramName[128];
  1072. int vprogramStaticIndex = -1;
  1073. int vprogramDynamicIndex = -1;
  1074. pair->m_vertexProg->GetLabelIndexCombo( vprogramName, sizeof(vprogramName), &vprogramStaticIndex, &vprogramDynamicIndex );
  1075. char pprogramName[128];
  1076. int pprogramStaticIndex = -1;
  1077. int pprogramDynamicIndex = -1;
  1078. pair->m_fragmentProg->GetLabelIndexCombo( pprogramName, sizeof(pprogramName), &pprogramStaticIndex, &pprogramDynamicIndex );
  1079. // make up a key - this thing is really a list of tuples, so need not be keyed by anything particular
  1080. KeyValues *pProgramKey = pProgramCache->CreateNewKey();
  1081. Assert( pProgramKey );
  1082. pProgramKey->SetString ( "vs", vprogramName );
  1083. pProgramKey->SetString ( "ps", pprogramName );
  1084. pProgramKey->SetInt ( "vs_static", vprogramStaticIndex );
  1085. pProgramKey->SetInt ( "ps_static", pprogramStaticIndex );
  1086. pProgramKey->SetInt ( "vs_dynamic", vprogramDynamicIndex );
  1087. pProgramKey->SetInt ( "ps_dynamic", pprogramDynamicIndex );
  1088. pProgramCache->SaveToFile( g_pFullFileSystem, PROGRAM_CACHE_FILE, "MOD" );
  1089. pProgramCache->deleteThis();
  1090. }
  1091. // Calls glUseProgram() as a side effect
  1092. CGLMShaderPair *CGLMShaderPairCache::SelectShaderPairInternal( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits, int rowIndex )
  1093. {
  1094. CGLMShaderPair *result = NULL;
  1095. #if GLMDEBUG
  1096. int loglevel = gl_shaderpair_cachelog.GetInt();
  1097. #else
  1098. const int loglevel = 0;
  1099. #endif
  1100. char vtempname[128];
  1101. int vtempindex = -1; vtempindex;
  1102. int vtempcombo = -1; vtempcombo;
  1103. char ptempname[128];
  1104. int ptempindex = -1; ptempindex;
  1105. int ptempcombo = -1; ptempcombo;
  1106. CGLMPairCacheEntry *row = HashRowPtr( rowIndex );
  1107. // 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).
  1108. int hitway, emptyway, oldestway;
  1109. HashRowProbe( row, vp, fp, extraKeyBits, hitway, emptyway, oldestway );
  1110. Assert( hitway == -1 );
  1111. // we missed. if there is no empty way, then somebody's getting evicted.
  1112. int destway = -1;
  1113. if (emptyway>=0)
  1114. {
  1115. destway = emptyway;
  1116. if (loglevel >= 2) // misses logged at level 3 and higher
  1117. {
  1118. printf("\nSSP: miss - row %05d - ", rowIndex );
  1119. }
  1120. }
  1121. else
  1122. {
  1123. // evict the oldest way
  1124. Assert( oldestway >= 0); // better not come back negative
  1125. CGLMPairCacheEntry *evict = row + oldestway;
  1126. Assert( evict->m_pair != NULL );
  1127. Assert( evict->m_pair != m_ctx->m_pBoundPair ); // just check
  1128. ///////////////////////FIXME may need to do a shoot-down if the pair being evicted is currently active in the context
  1129. m_evictions[ rowIndex ]++;
  1130. // log eviction if desired
  1131. if (loglevel >= 2) // misses logged at level 3 and higher
  1132. {
  1133. //evict->m_vertexProg->GetLabelIndexCombo( vtempname, sizeof(vtempname), &vtempindex, &vtempcombo );
  1134. //evict->m_fragmentProg->GetLabelIndexCombo( ptempname, sizeof(ptempname), &ptempindex, &ptempcombo );
  1135. //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 ] );
  1136. evict->m_vertexProg->GetComboIndexNameString( vtempname, sizeof(vtempname) );
  1137. evict->m_fragmentProg->GetComboIndexNameString( ptempname, sizeof(ptempname) );
  1138. printf("\nSSP: miss - row %05d - [ %s + %s ]'s %d'th eviction - ", rowIndex, vtempname, ptempname, m_evictions[ rowIndex ] );
  1139. }
  1140. delete evict->m_pair; evict->m_pair = NULL;
  1141. memset( evict, 0, sizeof(*evict) );
  1142. destway = oldestway;
  1143. }
  1144. // make the new entry
  1145. CGLMPairCacheEntry *newentry = row + destway;
  1146. newentry->m_lastMark = m_mark;
  1147. newentry->m_vertexProg = vp;
  1148. newentry->m_fragmentProg = fp;
  1149. newentry->m_extraKeyBits = extraKeyBits;
  1150. newentry->m_pair = new CGLMShaderPair( m_ctx );
  1151. Assert( newentry->m_pair );
  1152. newentry->m_pair->SetProgramPair( vp, fp );
  1153. if (loglevel >= 2) // say a little bit more
  1154. {
  1155. //newentry->m_vertexProg->GetLabelIndexCombo( vtempname, sizeof(vtempname), &vtempindex, &vtempcombo );
  1156. //newentry->m_fragmentProg->GetLabelIndexCombo( ptempname, sizeof(ptempname), &ptempindex, &ptempcombo );
  1157. //printf("new [ %s/%d/%d %s/%d/%d ]", vtempname, vtempindex, vtempcombo, ptempname, ptempindex, ptempcombo );
  1158. newentry->m_vertexProg->GetComboIndexNameString( vtempname, sizeof(vtempname) );
  1159. newentry->m_fragmentProg->GetComboIndexNameString( ptempname, sizeof(ptempname) );
  1160. printf("new [ %s + %s ]", vtempname, ptempname );
  1161. }
  1162. m_mark = m_mark+1;
  1163. if (!m_mark) // somewhat unlikely this will ever be reached.. but we need to avoid zero as a mark value
  1164. {
  1165. m_mark = 1;
  1166. }
  1167. result = newentry->m_pair;
  1168. if (glm_cacheprograms.GetInt())
  1169. {
  1170. WriteToProgramCache( newentry->m_pair );
  1171. }
  1172. return result;
  1173. }
  1174. void CGLMShaderPairCache::QueryShaderPair( int index, GLMShaderPairInfo *infoOut )
  1175. {
  1176. if ( (index<0) || ( static_cast<uint>(index) >= (m_rows*m_ways) ) )
  1177. {
  1178. // no such location
  1179. memset( infoOut, 0, sizeof(*infoOut) );
  1180. infoOut->m_status = -1;
  1181. }
  1182. else
  1183. {
  1184. // locate the entry, and see if an active pair is present.
  1185. // if so, extract info and return with m_status=1.
  1186. // if not, exit with m_status = 0.
  1187. CGLMPairCacheEntry *entry = &m_entries[index];
  1188. if (entry->m_pair)
  1189. {
  1190. // live
  1191. // extract values of interest for caller
  1192. entry->m_pair->m_vertexProg->GetLabelIndexCombo ( infoOut->m_vsName, sizeof(infoOut->m_vsName), &infoOut->m_vsStaticIndex, &infoOut->m_vsDynamicIndex );
  1193. entry->m_pair->m_fragmentProg->GetLabelIndexCombo ( infoOut->m_psName, sizeof(infoOut->m_psName), &infoOut->m_psStaticIndex, &infoOut->m_psDynamicIndex );
  1194. infoOut->m_status = 1;
  1195. }
  1196. else
  1197. {
  1198. // not
  1199. memset( infoOut, 0, sizeof(*infoOut) );
  1200. infoOut->m_status = 0;
  1201. }
  1202. }
  1203. }
  1204. bool CGLMShaderPairCache::PurgePairsWithShader( CGLMProgram *prog )
  1205. {
  1206. bool result = false;
  1207. // walk all rows*ways
  1208. int limit = m_rows * m_ways;
  1209. for( int i=0; i < limit; i++)
  1210. {
  1211. CGLMPairCacheEntry *entry = &m_entries[i];
  1212. if (entry->m_pair)
  1213. {
  1214. //scrub it, if not currently bound, and if the supplied shader matches either stage
  1215. if ( (entry->m_vertexProg==prog) || (entry->m_fragmentProg==prog) )
  1216. {
  1217. // found it, but does it conflict with bound pair ?
  1218. if (entry->m_pair == m_ctx->m_pBoundPair)
  1219. {
  1220. m_ctx->m_pBoundPair = NULL;
  1221. m_ctx->m_bDirtyPrograms = true;
  1222. }
  1223. delete entry->m_pair;
  1224. memset( entry, 0, sizeof(*entry) );
  1225. }
  1226. }
  1227. }
  1228. return result;
  1229. }
  1230. bool CGLMShaderPairCache::Purge( void )
  1231. {
  1232. bool result = false;
  1233. // walk all rows*ways
  1234. int limit = m_rows * m_ways;
  1235. for( int i=0; i < limit; i++)
  1236. {
  1237. CGLMPairCacheEntry *entry = &m_entries[i];
  1238. if (entry->m_pair)
  1239. {
  1240. //scrub it, unless the pair is the currently bound pair in our parent glm context
  1241. if (entry->m_pair != m_ctx->m_pBoundPair)
  1242. {
  1243. delete entry->m_pair;
  1244. memset( entry, 0, sizeof(*entry) );
  1245. }
  1246. else
  1247. {
  1248. result = true;
  1249. }
  1250. }
  1251. }
  1252. return result;
  1253. }
  1254. void CGLMShaderPairCache::DumpStats ( void )
  1255. {
  1256. #if GL_SHADER_PAIR_CACHE_STATS
  1257. printf("\n------------------\npair cache stats");
  1258. int total = 0;
  1259. for( uint row=0; row < m_rows; row++ )
  1260. {
  1261. if ( (m_evictions[row] != 0) || (m_hits[row] != 0) )
  1262. {
  1263. printf("\n row %d : %d evictions, %d hits",row,m_evictions[row], m_hits[row]);
  1264. total += m_evictions[row];
  1265. }
  1266. }
  1267. printf("\n\npair cache evictions: %d\n-----------------------\n",total );
  1268. #endif
  1269. }
  1270. //===============================