RenderProgs_GLSL.cpp (43234B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 29 #pragma hdrstop 30 #include "../idlib/precompiled.h" 31 32 #include "tr_local.h" 33 34 idCVar r_skipStripDeadCode( "r_skipStripDeadCode", "0", CVAR_BOOL, "Skip stripping dead code" ); 35 idCVar r_useUniformArrays( "r_useUniformArrays", "1", CVAR_BOOL, "" ); 36 37 38 #define VERTEX_UNIFORM_ARRAY_NAME "_va_" 39 #define FRAGMENT_UNIFORM_ARRAY_NAME "_fa_" 40 41 static const int AT_VS_IN = BIT( 1 ); 42 static const int AT_VS_OUT = BIT( 2 ); 43 static const int AT_PS_IN = BIT( 3 ); 44 static const int AT_PS_OUT = BIT( 4 ); 45 46 struct idCGBlock { 47 idStr prefix; // tokens that comes before the name 48 idStr name; // the name 49 idStr postfix; // tokens that comes after the name 50 bool used; // whether or not this block is referenced anywhere 51 }; 52 53 /* 54 ================================================ 55 attribInfo_t 56 ================================================ 57 */ 58 struct attribInfo_t { 59 const char * type; 60 const char * name; 61 const char * semantic; 62 const char * glsl; 63 int bind; 64 int flags; 65 int vertexMask; 66 }; 67 68 /* 69 ================================================ 70 vertexMask_t 71 72 NOTE: There is a PS3 dependency between the bit flag specified here and the vertex 73 attribute index and attribute semantic specified in DeclRenderProg.cpp because the 74 stored render prog vertexMask is initialized with cellCgbGetVertexConfiguration(). 75 The ATTRIB_INDEX_ defines are used to make sure the vertexMask_t and attrib assignment 76 in DeclRenderProg.cpp are in sync. 77 78 Even though VERTEX_MASK_XYZ_SHORT and VERTEX_MASK_ST_SHORT are not real attributes, 79 they come before the VERTEX_MASK_MORPH to reduce the range of vertex program 80 permutations defined by the vertexMask_t bits on the Xbox 360 (see MAX_VERTEX_DECLARATIONS). 81 ================================================ 82 */ 83 enum vertexMask_t { 84 VERTEX_MASK_XYZ = BIT( PC_ATTRIB_INDEX_VERTEX ), 85 VERTEX_MASK_ST = BIT( PC_ATTRIB_INDEX_ST ), 86 VERTEX_MASK_NORMAL = BIT( PC_ATTRIB_INDEX_NORMAL ), 87 VERTEX_MASK_COLOR = BIT( PC_ATTRIB_INDEX_COLOR ), 88 VERTEX_MASK_TANGENT = BIT( PC_ATTRIB_INDEX_TANGENT ), 89 VERTEX_MASK_COLOR2 = BIT( PC_ATTRIB_INDEX_COLOR2 ), 90 }; 91 92 attribInfo_t attribsPC[] = { 93 // vertex attributes 94 { "float4", "position", "POSITION", "in_Position", PC_ATTRIB_INDEX_VERTEX, AT_VS_IN, VERTEX_MASK_XYZ }, 95 { "float2", "texcoord", "TEXCOORD0", "in_TexCoord", PC_ATTRIB_INDEX_ST, AT_VS_IN, VERTEX_MASK_ST }, 96 { "float4", "normal", "NORMAL", "in_Normal", PC_ATTRIB_INDEX_NORMAL, AT_VS_IN, VERTEX_MASK_NORMAL }, 97 { "float4", "tangent", "TANGENT", "in_Tangent", PC_ATTRIB_INDEX_TANGENT, AT_VS_IN, VERTEX_MASK_TANGENT }, 98 { "float4", "color", "COLOR0", "in_Color", PC_ATTRIB_INDEX_COLOR, AT_VS_IN, VERTEX_MASK_COLOR }, 99 { "float4", "color2", "COLOR1", "in_Color2", PC_ATTRIB_INDEX_COLOR2, AT_VS_IN, VERTEX_MASK_COLOR2 }, 100 101 // pre-defined vertex program output 102 { "float4", "position", "POSITION", "gl_Position", 0, AT_VS_OUT, 0 }, 103 { "float", "clip0", "CLP0", "gl_ClipDistance[0]", 0, AT_VS_OUT, 0 }, 104 { "float", "clip1", "CLP1", "gl_ClipDistance[1]", 0, AT_VS_OUT, 0 }, 105 { "float", "clip2", "CLP2", "gl_ClipDistance[2]", 0, AT_VS_OUT, 0 }, 106 { "float", "clip3", "CLP3", "gl_ClipDistance[3]", 0, AT_VS_OUT, 0 }, 107 { "float", "clip4", "CLP4", "gl_ClipDistance[4]", 0, AT_VS_OUT, 0 }, 108 { "float", "clip5", "CLP5", "gl_ClipDistance[5]", 0, AT_VS_OUT, 0 }, 109 110 // pre-defined fragment program input 111 { "float4", "position", "WPOS", "gl_FragCoord", 0, AT_PS_IN, 0 }, 112 { "half4", "hposition", "WPOS", "gl_FragCoord", 0, AT_PS_IN, 0 }, 113 { "float", "facing", "FACE", "gl_FrontFacing", 0, AT_PS_IN, 0 }, 114 115 // fragment program output 116 { "float4", "color", "COLOR", "gl_FragColor", 0, AT_PS_OUT, 0 }, // GLSL version 1.2 doesn't allow for custom color name mappings 117 { "half4", "hcolor", "COLOR", "gl_FragColor", 0, AT_PS_OUT, 0 }, 118 { "float4", "color0", "COLOR0", "gl_FragColor", 0, AT_PS_OUT, 0 }, 119 { "float4", "color1", "COLOR1", "gl_FragColor", 1, AT_PS_OUT, 0 }, 120 { "float4", "color2", "COLOR2", "gl_FragColor", 2, AT_PS_OUT, 0 }, 121 { "float4", "color3", "COLOR3", "gl_FragColor", 3, AT_PS_OUT, 0 }, 122 { "float", "depth", "DEPTH", "gl_FragDepth", 4, AT_PS_OUT, 0 }, 123 124 // vertex to fragment program pass through 125 { "float4", "color", "COLOR", "gl_FrontColor", 0, AT_VS_OUT, 0 }, 126 { "float4", "color0", "COLOR0", "gl_FrontColor", 0, AT_VS_OUT, 0 }, 127 { "float4", "color1", "COLOR1", "gl_FrontSecondaryColor", 0, AT_VS_OUT, 0 }, 128 129 130 { "float4", "color", "COLOR", "gl_Color", 0, AT_PS_IN, 0 }, 131 { "float4", "color0", "COLOR0", "gl_Color", 0, AT_PS_IN, 0 }, 132 { "float4", "color1", "COLOR1", "gl_SecondaryColor", 0, AT_PS_IN, 0 }, 133 134 { "half4", "hcolor", "COLOR", "gl_Color", 0, AT_PS_IN, 0 }, 135 { "half4", "hcolor0", "COLOR0", "gl_Color", 0, AT_PS_IN, 0 }, 136 { "half4", "hcolor1", "COLOR1", "gl_SecondaryColor", 0, AT_PS_IN, 0 }, 137 138 { "float4", "texcoord0", "TEXCOORD0_centroid", "vofi_TexCoord0", 0, AT_PS_IN, 0 }, 139 { "float4", "texcoord1", "TEXCOORD1_centroid", "vofi_TexCoord1", 0, AT_PS_IN, 0 }, 140 { "float4", "texcoord2", "TEXCOORD2_centroid", "vofi_TexCoord2", 0, AT_PS_IN, 0 }, 141 { "float4", "texcoord3", "TEXCOORD3_centroid", "vofi_TexCoord3", 0, AT_PS_IN, 0 }, 142 { "float4", "texcoord4", "TEXCOORD4_centroid", "vofi_TexCoord4", 0, AT_PS_IN, 0 }, 143 { "float4", "texcoord5", "TEXCOORD5_centroid", "vofi_TexCoord5", 0, AT_PS_IN, 0 }, 144 { "float4", "texcoord6", "TEXCOORD6_centroid", "vofi_TexCoord6", 0, AT_PS_IN, 0 }, 145 { "float4", "texcoord7", "TEXCOORD7_centroid", "vofi_TexCoord7", 0, AT_PS_IN, 0 }, 146 { "float4", "texcoord8", "TEXCOORD8_centroid", "vofi_TexCoord8", 0, AT_PS_IN, 0 }, 147 { "float4", "texcoord9", "TEXCOORD9_centroid", "vofi_TexCoord9", 0, AT_PS_IN, 0 }, 148 149 { "float4", "texcoord0", "TEXCOORD0", "vofi_TexCoord0", 0, AT_VS_OUT | AT_PS_IN, 0 }, 150 { "float4", "texcoord1", "TEXCOORD1", "vofi_TexCoord1", 0, AT_VS_OUT | AT_PS_IN, 0 }, 151 { "float4", "texcoord2", "TEXCOORD2", "vofi_TexCoord2", 0, AT_VS_OUT | AT_PS_IN, 0 }, 152 { "float4", "texcoord3", "TEXCOORD3", "vofi_TexCoord3", 0, AT_VS_OUT | AT_PS_IN, 0 }, 153 { "float4", "texcoord4", "TEXCOORD4", "vofi_TexCoord4", 0, AT_VS_OUT | AT_PS_IN, 0 }, 154 { "float4", "texcoord5", "TEXCOORD5", "vofi_TexCoord5", 0, AT_VS_OUT | AT_PS_IN, 0 }, 155 { "float4", "texcoord6", "TEXCOORD6", "vofi_TexCoord6", 0, AT_VS_OUT | AT_PS_IN, 0 }, 156 { "float4", "texcoord7", "TEXCOORD7", "vofi_TexCoord7", 0, AT_VS_OUT | AT_PS_IN, 0 }, 157 { "float4", "texcoord8", "TEXCOORD8", "vofi_TexCoord8", 0, AT_VS_OUT | AT_PS_IN, 0 }, 158 { "float4", "texcoord9", "TEXCOORD9", "vofi_TexCoord9", 0, AT_VS_OUT | AT_PS_IN, 0 }, 159 160 { "half4", "htexcoord0", "TEXCOORD0", "vofi_TexCoord0", 0, AT_PS_IN, 0 }, 161 { "half4", "htexcoord1", "TEXCOORD1", "vofi_TexCoord1", 0, AT_PS_IN, 0 }, 162 { "half4", "htexcoord2", "TEXCOORD2", "vofi_TexCoord2", 0, AT_PS_IN, 0 }, 163 { "half4", "htexcoord3", "TEXCOORD3", "vofi_TexCoord3", 0, AT_PS_IN, 0 }, 164 { "half4", "htexcoord4", "TEXCOORD4", "vofi_TexCoord4", 0, AT_PS_IN, 0 }, 165 { "half4", "htexcoord5", "TEXCOORD5", "vofi_TexCoord5", 0, AT_PS_IN, 0 }, 166 { "half4", "htexcoord6", "TEXCOORD6", "vofi_TexCoord6", 0, AT_PS_IN, 0 }, 167 { "half4", "htexcoord7", "TEXCOORD7", "vofi_TexCoord7", 0, AT_PS_IN, 0 }, 168 { "half4", "htexcoord8", "TEXCOORD8", "vofi_TexCoord8", 0, AT_PS_IN, 0 }, 169 { "half4", "htexcoord9", "TEXCOORD9", "vofi_TexCoord9", 0, AT_PS_IN, 0 }, 170 { "float", "fog", "FOG", "gl_FogFragCoord", 0, AT_VS_OUT, 0 }, 171 { "float4", "fog", "FOG", "gl_FogFragCoord", 0, AT_PS_IN, 0 }, 172 { NULL, NULL, NULL, NULL, 0, 0, 0 } 173 }; 174 175 const char * types[] = { 176 "int", 177 "float", 178 "half", 179 "fixed", 180 "bool", 181 "cint", 182 "cfloat", 183 "void" 184 }; 185 static const int numTypes = sizeof( types ) / sizeof( types[0] ); 186 187 const char * typePosts[] = { 188 "1", "2", "3", "4", 189 "1x1", "1x2", "1x3", "1x4", 190 "2x1", "2x2", "2x3", "2x4", 191 "3x1", "3x2", "3x3", "3x4", 192 "4x1", "4x2", "4x3", "4x4" 193 }; 194 static const int numTypePosts = sizeof( typePosts ) / sizeof( typePosts[0] ); 195 196 const char * prefixes[] = { 197 "static", 198 "const", 199 "uniform", 200 "struct", 201 202 "sampler", 203 204 "sampler1D", 205 "sampler2D", 206 "sampler3D", 207 "samplerCUBE", 208 209 "sampler1DShadow", // GLSL 210 "sampler2DShadow", // GLSL 211 "sampler3DShadow", // GLSL 212 "samplerCubeShadow", // GLSL 213 214 "sampler2DMS", // GLSL 215 }; 216 static const int numPrefixes = sizeof( prefixes ) / sizeof( prefixes[0] ); 217 218 // For GLSL we need to have the names for the renderparms so we can look up their run time indices within the renderprograms 219 static const char * GLSLParmNames[] = { 220 "rpScreenCorrectionFactor", 221 "rpWindowCoord", 222 "rpDiffuseModifier", 223 "rpSpecularModifier", 224 225 "rpLocalLightOrigin", 226 "rpLocalViewOrigin", 227 228 "rpLightProjectionS", 229 "rpLightProjectionT", 230 "rpLightProjectionQ", 231 "rpLightFalloffS", 232 233 "rpBumpMatrixS", 234 "rpBumpMatrixT", 235 236 "rpDiffuseMatrixS", 237 "rpDiffuseMatrixT", 238 239 "rpSpecularMatrixS", 240 "rpSpecularMatrixT", 241 242 "rpVertexColorModulate", 243 "rpVertexColorAdd", 244 245 "rpColor", 246 "rpViewOrigin", 247 "rpGlobalEyePos", 248 249 "rpMVPmatrixX", 250 "rpMVPmatrixY", 251 "rpMVPmatrixZ", 252 "rpMVPmatrixW", 253 254 "rpModelMatrixX", 255 "rpModelMatrixY", 256 "rpModelMatrixZ", 257 "rpModelMatrixW", 258 259 "rpProjectionMatrixX", 260 "rpProjectionMatrixY", 261 "rpProjectionMatrixZ", 262 "rpProjectionMatrixW", 263 264 "rpModelViewMatrixX", 265 "rpModelViewMatrixY", 266 "rpModelViewMatrixZ", 267 "rpModelViewMatrixW", 268 269 "rpTextureMatrixS", 270 "rpTextureMatrixT", 271 272 "rpTexGen0S", 273 "rpTexGen0T", 274 "rpTexGen0Q", 275 "rpTexGen0Enabled", 276 277 "rpTexGen1S", 278 "rpTexGen1T", 279 "rpTexGen1Q", 280 "rpTexGen1Enabled", 281 282 "rpWobbleSkyX", 283 "rpWobbleSkyY", 284 "rpWobbleSkyZ", 285 286 "rpOverbright", 287 "rpEnableSkinning", 288 "rpAlphaTest" 289 }; 290 291 /* 292 ======================== 293 StripDeadCode 294 ======================== 295 */ 296 idStr StripDeadCode( const idStr & in, const char * name ) { 297 if ( r_skipStripDeadCode.GetBool() ) { 298 return in; 299 } 300 301 //idLexer src( LEXFL_NOFATALERRORS ); 302 idParser src( LEXFL_NOFATALERRORS ); 303 src.LoadMemory( in.c_str(), in.Length(), name ); 304 src.AddDefine("PC"); 305 306 idList< idCGBlock, TAG_RENDERPROG > blocks; 307 308 blocks.SetNum( 100 ); 309 310 idToken token; 311 while ( !src.EndOfFile() ) { 312 idCGBlock & block = blocks.Alloc(); 313 // read prefix 314 while ( src.ReadToken( &token ) ) { 315 bool found = false; 316 for ( int i = 0; i < numPrefixes; i++ ) { 317 if ( token == prefixes[i] ) { 318 found = true; 319 break; 320 } 321 } 322 if ( !found ) { 323 for ( int i = 0; i < numTypes; i++ ) { 324 if ( token == types[i] ) { 325 found = true; 326 break; 327 } 328 int typeLen = idStr::Length( types[i] ); 329 if ( token.Cmpn( types[i], typeLen ) == 0 ) { 330 for ( int j = 0; j < numTypePosts; j++ ) { 331 if ( idStr::Cmp( token.c_str() + typeLen, typePosts[j] ) == 0 ) { 332 found = true; 333 break; 334 } 335 } 336 if ( found ) { 337 break; 338 } 339 } 340 } 341 } 342 if ( found ) { 343 if ( block.prefix.Length() > 0 && token.WhiteSpaceBeforeToken() ) { 344 block.prefix += ' '; 345 } 346 block.prefix += token; 347 } else { 348 src.UnreadToken( &token ); 349 break; 350 } 351 } 352 if ( !src.ReadToken( &token ) ) { 353 blocks.SetNum( blocks.Num() - 1 ); 354 break; 355 } 356 block.name = token; 357 358 if ( src.PeekTokenString( "=" ) || src.PeekTokenString( ":" ) || src.PeekTokenString( "[" ) ) { 359 src.ReadToken( &token ); 360 block.postfix = token; 361 while ( src.ReadToken( &token ) ) { 362 if ( token == ";" ) { 363 block.postfix += ';'; 364 break; 365 } else { 366 if ( token.WhiteSpaceBeforeToken() ){ 367 block.postfix += ' '; 368 } 369 block.postfix += token; 370 } 371 } 372 } else if ( src.PeekTokenString( "(" ) ) { 373 idStr parms, body; 374 src.ParseBracedSection( parms, -1, true, '(', ')' ); 375 if ( src.CheckTokenString( ";" ) ) { 376 block.postfix = parms + ";"; 377 } else { 378 src.ParseBracedSection( body, -1, true, '{', '}' ); 379 block.postfix = parms + " " + body; 380 } 381 } else if ( src.PeekTokenString( "{" ) ) { 382 src.ParseBracedSection( block.postfix, -1, true, '{', '}' ); 383 if ( src.CheckTokenString( ";" ) ) { 384 block.postfix += ';'; 385 } 386 } else if ( src.CheckTokenString( ";" ) ) { 387 block.postfix = idStr( ';' ); 388 } else { 389 src.Warning( "Could not strip dead code -- unknown token %s\n", token.c_str() ); 390 return in; 391 } 392 } 393 394 idList<int, TAG_RENDERPROG> stack; 395 for ( int i = 0; i < blocks.Num(); i++ ) { 396 blocks[i].used = ( ( blocks[i].name == "main" ) 397 || blocks[i].name.Right( 4 ) == "_ubo" 398 ); 399 400 if ( blocks[i].name == "include" ) { 401 blocks[i].used = true; 402 blocks[i].name = ""; // clear out the include tag 403 } 404 405 if ( blocks[i].used ) { 406 stack.Append( i ); 407 } 408 } 409 410 while ( stack.Num() > 0 ) { 411 int i = stack[stack.Num() - 1]; 412 stack.SetNum( stack.Num() - 1 ); 413 414 idLexer src( LEXFL_NOFATALERRORS ); 415 src.LoadMemory( blocks[i].postfix.c_str(), blocks[i].postfix.Length(), name ); 416 while ( src.ReadToken( &token ) ) { 417 for ( int j = 0; j < blocks.Num(); j++ ) { 418 if ( !blocks[j].used ) { 419 if ( token == blocks[j].name ) { 420 blocks[j].used = true; 421 stack.Append( j ); 422 } 423 } 424 } 425 } 426 } 427 428 idStr out; 429 430 for ( int i = 0; i < blocks.Num(); i++ ) { 431 if ( blocks[i].used ) { 432 out += blocks[i].prefix; 433 out += ' '; 434 out += blocks[i].name; 435 out += ' '; 436 out += blocks[i].postfix; 437 out += '\n'; 438 } 439 } 440 441 return out; 442 } 443 444 struct typeConversion_t { 445 const char * typeCG; 446 const char * typeGLSL; 447 } typeConversion[] = { 448 { "void", "void" }, 449 450 { "fixed", "float" }, 451 452 { "float", "float" }, 453 { "float2", "vec2" }, 454 { "float3", "vec3" }, 455 { "float4", "vec4" }, 456 457 { "half", "float" }, 458 { "half2", "vec2" }, 459 { "half3", "vec3" }, 460 { "half4", "vec4" }, 461 462 { "int", "int" }, 463 { "int2", "ivec2" }, 464 { "int3", "ivec3" }, 465 { "int4", "ivec4" }, 466 467 { "bool", "bool" }, 468 { "bool2", "bvec2" }, 469 { "bool3", "bvec3" }, 470 { "bool4", "bvec4" }, 471 472 { "float2x2", "mat2x2" }, 473 { "float2x3", "mat2x3" }, 474 { "float2x4", "mat2x4" }, 475 476 { "float3x2", "mat3x2" }, 477 { "float3x3", "mat3x3" }, 478 { "float3x4", "mat3x4" }, 479 480 { "float4x2", "mat4x2" }, 481 { "float4x3", "mat4x3" }, 482 { "float4x4", "mat4x4" }, 483 484 { "sampler1D", "sampler1D" }, 485 { "sampler2D", "sampler2D" }, 486 { "sampler3D", "sampler3D" }, 487 { "samplerCUBE", "samplerCube" }, 488 489 { "sampler1DShadow", "sampler1DShadow" }, 490 { "sampler2DShadow", "sampler2DShadow" }, 491 { "sampler3DShadow", "sampler3DShadow" }, 492 { "samplerCubeShadow", "samplerCubeShadow" }, 493 494 { "sampler2DMS", "sampler2DMS" }, 495 496 { NULL, NULL } 497 }; 498 499 const char * vertexInsert = { 500 "#version 150\n" 501 "#define PC\n" 502 "\n" 503 "float saturate( float v ) { return clamp( v, 0.0, 1.0 ); }\n" 504 "vec2 saturate( vec2 v ) { return clamp( v, 0.0, 1.0 ); }\n" 505 "vec3 saturate( vec3 v ) { return clamp( v, 0.0, 1.0 ); }\n" 506 "vec4 saturate( vec4 v ) { return clamp( v, 0.0, 1.0 ); }\n" 507 "vec4 tex2Dlod( sampler2D sampler, vec4 texcoord ) { return textureLod( sampler, texcoord.xy, texcoord.w ); }\n" 508 "\n" 509 }; 510 511 const char * fragmentInsert = { 512 "#version 150\n" 513 "#define PC\n" 514 "\n" 515 "void clip( float v ) { if ( v < 0.0 ) { discard; } }\n" 516 "void clip( vec2 v ) { if ( any( lessThan( v, vec2( 0.0 ) ) ) ) { discard; } }\n" 517 "void clip( vec3 v ) { if ( any( lessThan( v, vec3( 0.0 ) ) ) ) { discard; } }\n" 518 "void clip( vec4 v ) { if ( any( lessThan( v, vec4( 0.0 ) ) ) ) { discard; } }\n" 519 "\n" 520 "float saturate( float v ) { return clamp( v, 0.0, 1.0 ); }\n" 521 "vec2 saturate( vec2 v ) { return clamp( v, 0.0, 1.0 ); }\n" 522 "vec3 saturate( vec3 v ) { return clamp( v, 0.0, 1.0 ); }\n" 523 "vec4 saturate( vec4 v ) { return clamp( v, 0.0, 1.0 ); }\n" 524 "\n" 525 "vec4 tex2D( sampler2D sampler, vec2 texcoord ) { return texture( sampler, texcoord.xy ); }\n" 526 "vec4 tex2D( sampler2DShadow sampler, vec3 texcoord ) { return vec4( texture( sampler, texcoord.xyz ) ); }\n" 527 "\n" 528 "vec4 tex2D( sampler2D sampler, vec2 texcoord, vec2 dx, vec2 dy ) { return textureGrad( sampler, texcoord.xy, dx, dy ); }\n" 529 "vec4 tex2D( sampler2DShadow sampler, vec3 texcoord, vec2 dx, vec2 dy ) { return vec4( textureGrad( sampler, texcoord.xyz, dx, dy ) ); }\n" 530 "\n" 531 "vec4 texCUBE( samplerCube sampler, vec3 texcoord ) { return texture( sampler, texcoord.xyz ); }\n" 532 "vec4 texCUBE( samplerCubeShadow sampler, vec4 texcoord ) { return vec4( texture( sampler, texcoord.xyzw ) ); }\n" 533 "\n" 534 "vec4 tex1Dproj( sampler1D sampler, vec2 texcoord ) { return textureProj( sampler, texcoord ); }\n" 535 "vec4 tex2Dproj( sampler2D sampler, vec3 texcoord ) { return textureProj( sampler, texcoord ); }\n" 536 "vec4 tex3Dproj( sampler3D sampler, vec4 texcoord ) { return textureProj( sampler, texcoord ); }\n" 537 "\n" 538 "vec4 tex1Dbias( sampler1D sampler, vec4 texcoord ) { return texture( sampler, texcoord.x, texcoord.w ); }\n" 539 "vec4 tex2Dbias( sampler2D sampler, vec4 texcoord ) { return texture( sampler, texcoord.xy, texcoord.w ); }\n" 540 "vec4 tex3Dbias( sampler3D sampler, vec4 texcoord ) { return texture( sampler, texcoord.xyz, texcoord.w ); }\n" 541 "vec4 texCUBEbias( samplerCube sampler, vec4 texcoord ) { return texture( sampler, texcoord.xyz, texcoord.w ); }\n" 542 "\n" 543 "vec4 tex1Dlod( sampler1D sampler, vec4 texcoord ) { return textureLod( sampler, texcoord.x, texcoord.w ); }\n" 544 "vec4 tex2Dlod( sampler2D sampler, vec4 texcoord ) { return textureLod( sampler, texcoord.xy, texcoord.w ); }\n" 545 "vec4 tex3Dlod( sampler3D sampler, vec4 texcoord ) { return textureLod( sampler, texcoord.xyz, texcoord.w ); }\n" 546 "vec4 texCUBElod( samplerCube sampler, vec4 texcoord ) { return textureLod( sampler, texcoord.xyz, texcoord.w ); }\n" 547 "\n" 548 }; 549 550 struct builtinConversion_t { 551 const char * nameCG; 552 const char * nameGLSL; 553 } builtinConversion[] = { 554 { "frac", "fract" }, 555 { "lerp", "mix" }, 556 { "rsqrt", "inversesqrt" }, 557 { "ddx", "dFdx" }, 558 { "ddy", "dFdy" }, 559 560 { NULL, NULL } 561 }; 562 563 struct inOutVariable_t { 564 idStr type; 565 idStr nameCg; 566 idStr nameGLSL; 567 bool declareInOut; 568 }; 569 570 /* 571 ======================== 572 ParseInOutStruct 573 ======================== 574 */ 575 void ParseInOutStruct( idLexer & src, int attribType, idList< inOutVariable_t > & inOutVars ) { 576 src.ExpectTokenString( "{" ); 577 578 while( !src.CheckTokenString( "}" ) ) { 579 inOutVariable_t var; 580 581 idToken token; 582 src.ReadToken( &token ); 583 var.type = token; 584 src.ReadToken( &token ); 585 var.nameCg = token; 586 587 if ( !src.CheckTokenString( ":" ) ) { 588 src.SkipUntilString( ";" ); 589 continue; 590 } 591 592 src.ReadToken( &token ); 593 var.nameGLSL = token; 594 src.ExpectTokenString( ";" ); 595 596 // convert the type 597 for ( int i = 0; typeConversion[i].typeCG != NULL; i++ ) { 598 if ( var.type.Cmp( typeConversion[i].typeCG ) == 0 ) { 599 var.type = typeConversion[i].typeGLSL; 600 break; 601 } 602 } 603 604 // convert the semantic to a GLSL name 605 for ( int i = 0; attribsPC[i].semantic != NULL; i++ ) { 606 if ( ( attribsPC[i].flags & attribType ) != 0 ) { 607 if ( var.nameGLSL.Cmp( attribsPC[i].semantic ) == 0 ) { 608 var.nameGLSL = attribsPC[i].glsl; 609 break; 610 } 611 } 612 } 613 614 // check if it was defined previously 615 var.declareInOut = true; 616 for ( int i = 0; i < inOutVars.Num(); i++ ) { 617 if ( var.nameGLSL == inOutVars[i].nameGLSL ) { 618 var.declareInOut = false; 619 break; 620 } 621 } 622 623 inOutVars.Append( var ); 624 } 625 626 src.ExpectTokenString( ";" ); 627 } 628 629 /* 630 ======================== 631 ConvertCG2GLSL 632 ======================== 633 */ 634 idStr ConvertCG2GLSL( const idStr & in, const char * name, bool isVertexProgram, idStr & uniforms ) { 635 idStr program; 636 program.ReAllocate( in.Length() * 2, false ); 637 638 idList< inOutVariable_t, TAG_RENDERPROG > varsIn; 639 idList< inOutVariable_t, TAG_RENDERPROG > varsOut; 640 idList< idStr > uniformList; 641 642 idLexer src( LEXFL_NOFATALERRORS ); 643 src.LoadMemory( in.c_str(), in.Length(), name ); 644 645 bool inMain = false; 646 const char * uniformArrayName = isVertexProgram ? VERTEX_UNIFORM_ARRAY_NAME : FRAGMENT_UNIFORM_ARRAY_NAME; 647 char newline[128] = { "\n" }; 648 649 idToken token; 650 while ( src.ReadToken( &token ) ) { 651 652 // check for uniforms 653 while ( token == "uniform" && src.CheckTokenString( "float4" ) ) { 654 src.ReadToken( &token ); 655 uniformList.Append( token ); 656 657 // strip ': register()' from uniforms 658 if ( src.CheckTokenString( ":" ) ) { 659 if ( src.CheckTokenString( "register" ) ) { 660 src.SkipUntilString( ";" ); 661 } 662 } 663 664 src.ReadToken( & token ); 665 } 666 667 // convert the in/out structs 668 if ( token == "struct" ) { 669 if ( src.CheckTokenString( "VS_IN" ) ) { 670 ParseInOutStruct( src, AT_VS_IN, varsIn ); 671 program += "\n\n"; 672 for ( int i = 0; i < varsIn.Num(); i++ ) { 673 if ( varsIn[i].declareInOut ) { 674 program += "in " + varsIn[i].type + " " + varsIn[i].nameGLSL + ";\n"; 675 } 676 } 677 continue; 678 } else if ( src.CheckTokenString( "VS_OUT" ) ) { 679 ParseInOutStruct( src, AT_VS_OUT, varsOut ); 680 program += "\n"; 681 for ( int i = 0; i < varsOut.Num(); i++ ) { 682 if ( varsOut[i].declareInOut ) { 683 program += "out " + varsOut[i].type + " " + varsOut[i].nameGLSL + ";\n"; 684 } 685 } 686 continue; 687 } else if ( src.CheckTokenString( "PS_IN" ) ) { 688 ParseInOutStruct( src, AT_PS_IN, varsIn ); 689 program += "\n\n"; 690 for ( int i = 0; i < varsIn.Num(); i++ ) { 691 if ( varsIn[i].declareInOut ) { 692 program += "in " + varsIn[i].type + " " + varsIn[i].nameGLSL + ";\n"; 693 } 694 } 695 inOutVariable_t var; 696 var.type = "vec4"; 697 var.nameCg = "position"; 698 var.nameGLSL = "gl_FragCoord"; 699 varsIn.Append( var ); 700 continue; 701 } else if ( src.CheckTokenString( "PS_OUT" ) ) { 702 ParseInOutStruct( src, AT_PS_OUT, varsOut ); 703 program += "\n"; 704 for ( int i = 0; i < varsOut.Num(); i++ ) { 705 if ( varsOut[i].declareInOut ) { 706 program += "out " + varsOut[i].type + " " + varsOut[i].nameGLSL + ";\n"; 707 } 708 } 709 continue; 710 } 711 } 712 713 // strip 'static' 714 if ( token == "static" ) { 715 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 716 src.SkipWhiteSpace( true ); // remove white space between 'static' and the next token 717 continue; 718 } 719 720 // strip ': register()' from uniforms 721 if ( token == ":" ) { 722 if ( src.CheckTokenString( "register" ) ) { 723 src.SkipUntilString( ";" ); 724 program += ";"; 725 continue; 726 } 727 } 728 729 // strip in/program parameters from main 730 if ( token == "void" && src.CheckTokenString( "main" ) ) { 731 src.ExpectTokenString( "(" ); 732 while( src.ReadToken( &token ) ) { 733 if ( token == ")" ) { 734 break; 735 } 736 } 737 738 program += "\nvoid main()"; 739 inMain = true; 740 continue; 741 } 742 743 // strip 'const' from local variables in main() 744 if ( token == "const" && inMain ) { 745 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 746 src.SkipWhiteSpace( true ); // remove white space between 'const' and the next token 747 continue; 748 } 749 750 // maintain indentation 751 if ( token == "{" ) { 752 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 753 program += "{"; 754 755 int len = Min( idStr::Length( newline ) + 1, (int)sizeof( newline ) - 1 ); 756 newline[len - 1] = '\t'; 757 newline[len - 0] = '\0'; 758 continue; 759 } 760 if ( token == "}" ) { 761 int len = Max( idStr::Length( newline ) - 1, 0 ); 762 newline[len] = '\0'; 763 764 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 765 program += "}"; 766 continue; 767 } 768 769 // check for a type conversion 770 bool foundType = false; 771 for ( int i = 0; typeConversion[i].typeCG != NULL; i++ ) { 772 if ( token.Cmp( typeConversion[i].typeCG ) == 0 ) { 773 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 774 program += typeConversion[i].typeGLSL; 775 foundType = true; 776 break; 777 } 778 } 779 if ( foundType ) { 780 continue; 781 } 782 783 if ( r_useUniformArrays.GetBool() ) { 784 // check for uniforms that need to be converted to the array 785 bool isUniform = false; 786 for ( int i = 0; i < uniformList.Num(); i++ ) { 787 if ( token == uniformList[i] ) { 788 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 789 program += va( "%s[%d /* %s */]", uniformArrayName, i, uniformList[i].c_str() ); 790 isUniform = true; 791 break; 792 } 793 } 794 if ( isUniform ) { 795 continue; 796 } 797 } 798 799 // check for input/output parameters 800 if ( src.CheckTokenString( "." ) ) { 801 802 if ( token == "vertex" || token == "fragment" ) { 803 idToken member; 804 src.ReadToken( &member ); 805 806 bool foundInOut = false; 807 for ( int i = 0; i < varsIn.Num(); i++ ) { 808 if ( member.Cmp( varsIn[i].nameCg ) == 0 ) { 809 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 810 program += varsIn[i].nameGLSL; 811 foundInOut = true; 812 break; 813 } 814 } 815 if ( !foundInOut ) { 816 src.Error( "invalid input parameter %s.%s", token.c_str(), member.c_str() ); 817 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 818 program += token; 819 program += "."; 820 program += member; 821 } 822 continue; 823 } 824 825 if ( token == "result" ) { 826 idToken member; 827 src.ReadToken( &member ); 828 829 bool foundInOut = false; 830 for ( int i = 0; i < varsOut.Num(); i++ ) { 831 if ( member.Cmp( varsOut[i].nameCg ) == 0 ) { 832 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 833 program += varsOut[i].nameGLSL; 834 foundInOut = true; 835 break; 836 } 837 } 838 if ( !foundInOut ) { 839 src.Error( "invalid output parameter %s.%s", token.c_str(), member.c_str() ); 840 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 841 program += token; 842 program += "."; 843 program += member; 844 } 845 continue; 846 } 847 848 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 849 program += token; 850 program += "."; 851 continue; 852 } 853 854 // check for a function conversion 855 bool foundFunction = false; 856 for ( int i = 0; builtinConversion[i].nameCG != NULL; i++ ) { 857 if ( token.Cmp( builtinConversion[i].nameCG ) == 0 ) { 858 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 859 program += builtinConversion[i].nameGLSL; 860 foundFunction = true; 861 break; 862 } 863 } 864 if ( foundFunction ) { 865 continue; 866 } 867 868 program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); 869 program += token; 870 } 871 872 idStr out; 873 874 if ( isVertexProgram ) { 875 out.ReAllocate( idStr::Length( vertexInsert ) + in.Length() * 2, false ); 876 out += vertexInsert; 877 } else { 878 out.ReAllocate( idStr::Length( fragmentInsert ) + in.Length() * 2, false ); 879 out += fragmentInsert; 880 } 881 882 if ( uniformList.Num() > 0 ) { 883 if ( r_useUniformArrays.GetBool() ) { 884 out += va( "\nuniform vec4 %s[%d];\n", uniformArrayName, uniformList.Num() ); 885 } else { 886 out += "\n"; 887 for ( int i = 0; i < uniformList.Num(); i++ ) { 888 out += "uniform vec4 "; 889 out += uniformList[i]; 890 out += ";\n"; 891 } 892 } 893 } 894 895 out += program; 896 897 for ( int i = 0; i < uniformList.Num(); i++ ) { 898 uniforms += uniformList[i]; 899 uniforms += "\n"; 900 } 901 uniforms += "\n"; 902 903 return out; 904 } 905 906 /* 907 ================================================================================================ 908 idRenderProgManager::LoadGLSLShader 909 ================================================================================================ 910 */ 911 GLuint idRenderProgManager::LoadGLSLShader( GLenum target, const char * name, idList<int> & uniforms ) { 912 913 idStr inFile; 914 idStr outFileHLSL; 915 idStr outFileGLSL; 916 idStr outFileUniforms; 917 inFile.Format( "renderprogs\\%s", name ); 918 inFile.StripFileExtension(); 919 outFileHLSL.Format( "renderprogs\\glsl\\%s", name ); 920 outFileHLSL.StripFileExtension(); 921 outFileGLSL.Format( "renderprogs\\glsl\\%s", name ); 922 outFileGLSL.StripFileExtension(); 923 outFileUniforms.Format( "renderprogs\\glsl\\%s", name ); 924 outFileUniforms.StripFileExtension(); 925 if ( target == GL_FRAGMENT_SHADER ) { 926 inFile += ".pixel"; 927 outFileHLSL += "_fragment.hlsl"; 928 outFileGLSL += "_fragment.glsl"; 929 outFileUniforms += "_fragment.uniforms"; 930 } else { 931 inFile += ".vertex"; 932 outFileHLSL += "_vertex.hlsl"; 933 outFileGLSL += "_vertex.glsl"; 934 outFileUniforms += "_vertex.uniforms"; 935 } 936 937 // first check whether we already have a valid GLSL file and compare it to the hlsl timestamp; 938 ID_TIME_T hlslTimeStamp; 939 int hlslFileLength = fileSystem->ReadFile( inFile.c_str(), NULL, &hlslTimeStamp ); 940 941 ID_TIME_T glslTimeStamp; 942 int glslFileLength = fileSystem->ReadFile( outFileGLSL.c_str(), NULL, &glslTimeStamp ); 943 944 // if the glsl file doesn't exist or we have a newer HLSL file we need to recreate the glsl file. 945 idStr programGLSL; 946 idStr programUniforms; 947 if ( ( glslFileLength <= 0 ) || ( hlslTimeStamp > glslTimeStamp ) ) { 948 if ( hlslFileLength <= 0 ) { 949 // hlsl file doesn't even exist bail out 950 return false; 951 } 952 953 void * hlslFileBuffer = NULL; 954 int len = fileSystem->ReadFile( inFile.c_str(), &hlslFileBuffer ); 955 if ( len <= 0 ) { 956 return false; 957 } 958 idStr hlslCode( ( const char* ) hlslFileBuffer ); 959 idStr programHLSL = StripDeadCode( hlslCode, inFile ); 960 programGLSL = ConvertCG2GLSL( programHLSL, inFile, target == GL_VERTEX_SHADER, programUniforms ); 961 962 fileSystem->WriteFile( outFileHLSL, programHLSL.c_str(), programHLSL.Length(), "fs_basepath" ); 963 fileSystem->WriteFile( outFileGLSL, programGLSL.c_str(), programGLSL.Length(), "fs_basepath" ); 964 if ( r_useUniformArrays.GetBool() ) { 965 fileSystem->WriteFile( outFileUniforms, programUniforms.c_str(), programUniforms.Length(), "fs_basepath" ); 966 } 967 } else { 968 // read in the glsl file 969 void * fileBufferGLSL = NULL; 970 int lengthGLSL = fileSystem->ReadFile( outFileGLSL.c_str(), &fileBufferGLSL ); 971 if ( lengthGLSL <= 0 ) { 972 idLib::Error( "GLSL file %s could not be loaded and may be corrupt", outFileGLSL.c_str() ); 973 } 974 programGLSL = ( const char * ) fileBufferGLSL; 975 Mem_Free( fileBufferGLSL ); 976 977 if ( r_useUniformArrays.GetBool() ) { 978 // read in the uniform file 979 void * fileBufferUniforms = NULL; 980 int lengthUniforms = fileSystem->ReadFile( outFileUniforms.c_str(), &fileBufferUniforms ); 981 if ( lengthUniforms <= 0 ) { 982 idLib::Error( "uniform file %s could not be loaded and may be corrupt", outFileUniforms.c_str() ); 983 } 984 programUniforms = ( const char* ) fileBufferUniforms; 985 Mem_Free( fileBufferUniforms ); 986 } 987 } 988 989 // find the uniforms locations in either the vertex or fragment uniform array 990 if ( r_useUniformArrays.GetBool() ) { 991 uniforms.Clear(); 992 993 idLexer src( programUniforms, programUniforms.Length(), "uniforms" ); 994 idToken token; 995 while ( src.ReadToken( &token ) ) { 996 int index = -1; 997 for ( int i = 0; i < RENDERPARM_TOTAL && index == -1; i++ ) { 998 const char * parmName = GetGLSLParmName( i ); 999 if ( token == parmName ) { 1000 index = i; 1001 } 1002 } 1003 for ( int i = 0; i < MAX_GLSL_USER_PARMS && index == -1; i++ ) { 1004 const char * parmName = GetGLSLParmName( RENDERPARM_USER + i ); 1005 if ( token == parmName ) { 1006 index = RENDERPARM_USER + i; 1007 } 1008 } 1009 if ( index == -1 ) { 1010 idLib::Error( "couldn't find uniform %s for %s", token.c_str(), outFileGLSL.c_str() ); 1011 } 1012 uniforms.Append( index ); 1013 } 1014 } 1015 1016 // create and compile the shader 1017 const GLuint shader = qglCreateShader( target ); 1018 if ( shader ) { 1019 const char * source[1] = { programGLSL.c_str() }; 1020 1021 qglShaderSource( shader, 1, source, NULL ); 1022 qglCompileShader( shader ); 1023 1024 int infologLength = 0; 1025 qglGetShaderiv( shader, GL_INFO_LOG_LENGTH, &infologLength ); 1026 if ( infologLength > 1 ) { 1027 idTempArray<char> infoLog( infologLength ); 1028 int charsWritten = 0; 1029 qglGetShaderInfoLog( shader, infologLength, &charsWritten, infoLog.Ptr() ); 1030 1031 // catch the strings the ATI and Intel drivers output on success 1032 if ( strstr( infoLog.Ptr(), "successfully compiled to run on hardware" ) != NULL || 1033 strstr( infoLog.Ptr(), "No errors." ) != NULL ) { 1034 //idLib::Printf( "%s program %s from %s compiled to run on hardware\n", typeName, GetName(), GetFileName() ); 1035 } else { 1036 idLib::Printf( "While compiling %s program %s\n", ( target == GL_FRAGMENT_SHADER ) ? "fragment" : "vertex" , inFile.c_str() ); 1037 1038 const char separator = '\n'; 1039 idList<idStr> lines; 1040 lines.Clear(); 1041 idStr source( programGLSL ); 1042 lines.Append( source ); 1043 for ( int index = 0, ofs = lines[index].Find( separator ); ofs != -1; index++, ofs = lines[index].Find( separator ) ) { 1044 lines.Append( lines[index].c_str() + ofs + 1 ); 1045 lines[index].CapLength( ofs ); 1046 } 1047 1048 idLib::Printf( "-----------------\n" ); 1049 for ( int i = 0; i < lines.Num(); i++ ) { 1050 idLib::Printf( "%3d: %s\n", i+1, lines[i].c_str() ); 1051 } 1052 idLib::Printf( "-----------------\n" ); 1053 1054 idLib::Printf( "%s\n", infoLog.Ptr() ); 1055 } 1056 } 1057 1058 GLint compiled = GL_FALSE; 1059 qglGetShaderiv( shader, GL_COMPILE_STATUS, &compiled ); 1060 if ( compiled == GL_FALSE ) { 1061 qglDeleteShader( shader ); 1062 return INVALID_PROGID; 1063 } 1064 } 1065 1066 return shader; 1067 } 1068 /* 1069 ================================================================================================ 1070 idRenderProgManager::FindGLSLProgram 1071 ================================================================================================ 1072 */ 1073 int idRenderProgManager::FindGLSLProgram( const char * name, int vIndex, int fIndex ) { 1074 1075 for ( int i = 0; i < glslPrograms.Num(); ++i ) { 1076 if ( ( glslPrograms[i].vertexShaderIndex == vIndex ) && ( glslPrograms[i].fragmentShaderIndex == fIndex ) ) { 1077 LoadGLSLProgram( i, vIndex, fIndex ); 1078 return i; 1079 } 1080 } 1081 1082 glslProgram_t program; 1083 program.name = name; 1084 int index = glslPrograms.Append( program ); 1085 LoadGLSLProgram( index, vIndex, fIndex ); 1086 return index; 1087 } 1088 1089 /* 1090 ================================================================================================ 1091 idRenderProgManager::GetGLSLParmName 1092 ================================================================================================ 1093 */ 1094 const char* idRenderProgManager::GetGLSLParmName( int rp ) const { 1095 if ( rp >= RENDERPARM_USER ) { 1096 int userParmIndex = rp - RENDERPARM_USER; 1097 return va("rpUser%d", userParmIndex ); 1098 } 1099 assert( rp < RENDERPARM_TOTAL ); 1100 return GLSLParmNames[ rp ]; 1101 } 1102 1103 /* 1104 ================================================================================================ 1105 idRenderProgManager::SetUniformValue 1106 ================================================================================================ 1107 */ 1108 void idRenderProgManager::SetUniformValue( const renderParm_t rp, const float * value ) { 1109 for ( int i = 0; i < 4; i++ ) { 1110 glslUniforms[rp][i] = value[i]; 1111 } 1112 } 1113 1114 /* 1115 ================================================================================================ 1116 idRenderProgManager::CommitUnforms 1117 ================================================================================================ 1118 */ 1119 void idRenderProgManager::CommitUniforms() { 1120 const int progID = GetGLSLCurrentProgram(); 1121 const glslProgram_t & prog = glslPrograms[progID]; 1122 1123 if ( r_useUniformArrays.GetBool() ) { 1124 ALIGNTYPE16 idVec4 localVectors[RENDERPARM_USER + MAX_GLSL_USER_PARMS]; 1125 1126 if ( prog.vertexShaderIndex >= 0 ) { 1127 const idList<int> & vertexUniforms = vertexShaders[prog.vertexShaderIndex].uniforms; 1128 if ( prog.vertexUniformArray != -1 && vertexUniforms.Num() > 0 ) { 1129 for ( int i = 0; i < vertexUniforms.Num(); i++ ) { 1130 localVectors[i] = glslUniforms[vertexUniforms[i]]; 1131 } 1132 qglUniform4fv( prog.vertexUniformArray, vertexUniforms.Num(), localVectors->ToFloatPtr() ); 1133 } 1134 } 1135 1136 if ( prog.fragmentShaderIndex >= 0 ) { 1137 const idList<int> & fragmentUniforms = fragmentShaders[prog.fragmentShaderIndex].uniforms; 1138 if ( prog.fragmentUniformArray != -1 && fragmentUniforms.Num() > 0 ) { 1139 for ( int i = 0; i < fragmentUniforms.Num(); i++ ) { 1140 localVectors[i] = glslUniforms[fragmentUniforms[i]]; 1141 } 1142 qglUniform4fv( prog.fragmentUniformArray, fragmentUniforms.Num(), localVectors->ToFloatPtr() ); 1143 } 1144 } 1145 } else { 1146 for ( int i = 0; i < prog.uniformLocations.Num(); i++ ) { 1147 const glslUniformLocation_t & uniformLocation = prog.uniformLocations[i]; 1148 qglUniform4fv( uniformLocation.uniformIndex, 1, glslUniforms[uniformLocation.parmIndex].ToFloatPtr() ); 1149 } 1150 } 1151 } 1152 1153 class idSort_QuickUniforms : public idSort_Quick< glslUniformLocation_t, idSort_QuickUniforms > { 1154 public: 1155 int Compare( const glslUniformLocation_t & a, const glslUniformLocation_t & b ) const { return a.uniformIndex - b.uniformIndex; } 1156 }; 1157 1158 /* 1159 ================================================================================================ 1160 idRenderProgManager::LoadGLSLProgram 1161 ================================================================================================ 1162 */ 1163 void idRenderProgManager::LoadGLSLProgram( const int programIndex, const int vertexShaderIndex, const int fragmentShaderIndex ) { 1164 glslProgram_t & prog = glslPrograms[programIndex]; 1165 1166 if ( prog.progId != INVALID_PROGID ) { 1167 return; // Already loaded 1168 } 1169 1170 GLuint vertexProgID = ( vertexShaderIndex != -1 ) ? vertexShaders[ vertexShaderIndex ].progId : INVALID_PROGID; 1171 GLuint fragmentProgID = ( fragmentShaderIndex != -1 ) ? fragmentShaders[ fragmentShaderIndex ].progId : INVALID_PROGID; 1172 1173 const GLuint program = qglCreateProgram(); 1174 if ( program ) { 1175 1176 if ( vertexProgID != INVALID_PROGID ) { 1177 qglAttachShader( program, vertexProgID ); 1178 } 1179 1180 if ( fragmentProgID != INVALID_PROGID ) { 1181 qglAttachShader( program, fragmentProgID ); 1182 } 1183 1184 // bind vertex attribute locations 1185 for ( int i = 0; attribsPC[i].glsl != NULL; i++ ) { 1186 if ( ( attribsPC[i].flags & AT_VS_IN ) != 0 ) { 1187 qglBindAttribLocation( program, attribsPC[i].bind, attribsPC[i].glsl ); 1188 } 1189 } 1190 1191 qglLinkProgram( program ); 1192 1193 int infologLength = 0; 1194 qglGetProgramiv( program, GL_INFO_LOG_LENGTH, &infologLength ); 1195 if ( infologLength > 1 ) { 1196 char * infoLog = (char *)malloc( infologLength ); 1197 int charsWritten = 0; 1198 qglGetProgramInfoLog( program, infologLength, &charsWritten, infoLog ); 1199 1200 // catch the strings the ATI and Intel drivers output on success 1201 if ( strstr( infoLog, "Vertex shader(s) linked, fragment shader(s) linked." ) != NULL || strstr( infoLog, "No errors." ) != NULL ) { 1202 //idLib::Printf( "render prog %s from %s linked\n", GetName(), GetFileName() ); 1203 } else { 1204 idLib::Printf( "While linking GLSL program %d with vertexShader %s and fragmentShader %s\n", 1205 programIndex, 1206 ( vertexShaderIndex >= 0 ) ? vertexShaders[vertexShaderIndex].name.c_str() : "<Invalid>", 1207 ( fragmentShaderIndex >= 0 ) ? fragmentShaders[ fragmentShaderIndex ].name.c_str() : "<Invalid>" ); 1208 idLib::Printf( "%s\n", infoLog ); 1209 } 1210 1211 free( infoLog ); 1212 } 1213 } 1214 1215 int linked = GL_FALSE; 1216 qglGetProgramiv( program, GL_LINK_STATUS, &linked ); 1217 if ( linked == GL_FALSE ) { 1218 qglDeleteProgram( program ); 1219 idLib::Error( "While linking GLSL program %d with vertexShader %s and fragmentShader %s\n", 1220 programIndex, 1221 ( vertexShaderIndex >= 0 ) ? vertexShaders[vertexShaderIndex].name.c_str() : "<Invalid>", 1222 ( fragmentShaderIndex >= 0 ) ? fragmentShaders[ fragmentShaderIndex ].name.c_str() : "<Invalid>" ); 1223 return; 1224 } 1225 1226 if ( r_useUniformArrays.GetBool() ) { 1227 prog.vertexUniformArray = qglGetUniformLocation( program, VERTEX_UNIFORM_ARRAY_NAME ); 1228 prog.fragmentUniformArray = qglGetUniformLocation( program, FRAGMENT_UNIFORM_ARRAY_NAME ); 1229 1230 assert( prog.vertexUniformArray != -1 || vertexShaderIndex < 0 || vertexShaders[vertexShaderIndex].uniforms.Num() == 0 ); 1231 assert( prog.fragmentUniformArray != -1 || fragmentShaderIndex < 0 || fragmentShaders[fragmentShaderIndex].uniforms.Num() == 0 ); 1232 } else { 1233 // store the uniform locations after we have linked the GLSL program 1234 prog.uniformLocations.Clear(); 1235 for ( int i = 0; i < RENDERPARM_TOTAL; i++ ) { 1236 const char * parmName = GetGLSLParmName( i ); 1237 GLint loc = qglGetUniformLocation( program, parmName ); 1238 if ( loc != -1 ) { 1239 glslUniformLocation_t uniformLocation; 1240 uniformLocation.parmIndex = i; 1241 uniformLocation.uniformIndex = loc; 1242 prog.uniformLocations.Append( uniformLocation ); 1243 } 1244 } 1245 1246 // store the USER uniform locations 1247 for ( int i = 0; i < MAX_GLSL_USER_PARMS; i++ ) { 1248 const char * parmName = GetGLSLParmName( RENDERPARM_USER + i ); 1249 GLint loc = qglGetUniformLocation( program, parmName ); 1250 if ( loc != -1 ) { 1251 glslUniformLocation_t uniformLocation; 1252 uniformLocation.parmIndex = RENDERPARM_USER + i; 1253 uniformLocation.uniformIndex = loc; 1254 prog.uniformLocations.Append( uniformLocation ); 1255 } 1256 } 1257 1258 // sort the uniforms based on index 1259 prog.uniformLocations.SortWithTemplate( idSort_QuickUniforms() ); 1260 } 1261 1262 // get the uniform buffer binding for skinning joint matrices 1263 GLint blockIndex = qglGetUniformBlockIndex( program, "matrices_ubo" ); 1264 if ( blockIndex != -1 ) { 1265 qglUniformBlockBinding( program, blockIndex, 0 ); 1266 } 1267 1268 // set the texture unit locations once for the render program. We only need to do this once since we only link the program once 1269 qglUseProgram( program ); 1270 for ( int i = 0; i < MAX_PROG_TEXTURE_PARMS; ++i ) { 1271 GLint loc = qglGetUniformLocation( program, va( "samp%d", i ) ); 1272 if ( loc != -1 ) { 1273 qglUniform1i( loc, i ); 1274 } 1275 } 1276 1277 idStr programName = vertexShaders[ vertexShaderIndex ].name; 1278 programName.StripFileExtension(); 1279 prog.name = programName; 1280 prog.progId = program; 1281 prog.fragmentShaderIndex = fragmentShaderIndex; 1282 prog.vertexShaderIndex = vertexShaderIndex; 1283 } 1284 1285 /* 1286 ================================================================================================ 1287 idRenderProgManager::ZeroUniforms 1288 ================================================================================================ 1289 */ 1290 void idRenderProgManager::ZeroUniforms() { 1291 memset( glslUniforms.Ptr(), 0, glslUniforms.Allocated() ); 1292 } 1293