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.

1420 lines
39 KiB

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