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.

1551 lines
43 KiB

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