1357 lines
42 KiB
C++
1357 lines
42 KiB
C++
/*
|
|
gl_shader.cpp - glsl shaders
|
|
Copyright (C) 2014 Uncle Mike
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
*/
|
|
|
|
#include "hud.h"
|
|
#include "cl_util.h"
|
|
#include "const.h"
|
|
#include "ref_params.h"
|
|
#include "gl_local.h"
|
|
#include <mathlib.h>
|
|
#include <stringlib.h>
|
|
#include "gl_shader.h"
|
|
#include "virtualfs.h"
|
|
#include "gl_world.h"
|
|
#include "gl_decals.h"
|
|
#include "studio.h"
|
|
#include "gl_grass.h"
|
|
|
|
//#define _DEBUG_UNIFORMS
|
|
#define SHADERS_HASH_SIZE (MAX_GLSL_PROGRAMS >> 2)
|
|
#define MAX_FILE_STACK 64
|
|
|
|
static char filenames_stack[MAX_FILE_STACK][256];
|
|
glsl_program_t glsl_programs[MAX_GLSL_PROGRAMS];
|
|
glsl_program_t *glsl_programsHashTable[SHADERS_HASH_SIZE];
|
|
int num_glsl_programs;
|
|
static int file_stack_pos;
|
|
static bool cache_needs_update = false;
|
|
|
|
typedef struct
|
|
{
|
|
const char *name;
|
|
uniformType_t type;
|
|
int flags; // hints
|
|
} uniformTable_t;
|
|
|
|
glsl_program_t *shader_t :: GetShader( void )
|
|
{
|
|
return &glsl_programs[shadernum];
|
|
}
|
|
|
|
void shader_t :: SetShader( unsigned short hand )
|
|
{
|
|
sequence = tr.glsl_valid_sequence;
|
|
shadernum = hand;
|
|
}
|
|
|
|
bool shader_t :: IsValid( void )
|
|
{
|
|
return (shadernum > 0) && (sequence == tr.glsl_valid_sequence);
|
|
}
|
|
|
|
int uniform_t :: GetSizeInBytes( void )
|
|
{
|
|
switch( format )
|
|
{
|
|
case GL_SAMPLER_1D_ARB:
|
|
case GL_SAMPLER_2D_ARB:
|
|
case GL_SAMPLER_3D_ARB:
|
|
case GL_SAMPLER_CUBE_ARB:
|
|
case GL_SAMPLER_1D_SHADOW_ARB:
|
|
case GL_SAMPLER_2D_SHADOW_ARB:
|
|
case GL_SAMPLER_2D_RECT_ARB:
|
|
case GL_SAMPLER_2D_RECT_SHADOW_ARB:
|
|
case GL_SAMPLER_CUBE_SHADOW_EXT:
|
|
return 4;
|
|
case GL_FLOAT:
|
|
case GL_INT:
|
|
case GL_UNSIGNED_INT:
|
|
return 4;
|
|
case GL_FLOAT_VEC2_ARB:
|
|
case GL_INT_VEC2_ARB:
|
|
return 8;
|
|
case GL_FLOAT_VEC3_ARB:
|
|
case GL_INT_VEC3_ARB:
|
|
return 12;
|
|
case GL_FLOAT_VEC4_ARB:
|
|
case GL_INT_VEC4_ARB:
|
|
return 16;
|
|
case GL_FLOAT_MAT2_ARB:
|
|
return 16;
|
|
case GL_FLOAT_MAT3_ARB:
|
|
return 36;
|
|
case GL_FLOAT_MAT4_ARB:
|
|
return 64;
|
|
}
|
|
|
|
ALERT( at_error, "uniform %s has unspecified size\n", name );
|
|
return 0; // assume error
|
|
}
|
|
|
|
void uniform_t :: SetValue( const void *pdata, int count )
|
|
{
|
|
unicache_t *check = (unicache_t *)pdata;
|
|
|
|
// set texture unit
|
|
if( FBitSet( flags, UFL_TEXTURE_UNIT ) && unit >= 0 )
|
|
{
|
|
GL_BindTexture( unit, check->iValue[0] );
|
|
}
|
|
else if( size > 1 )
|
|
{
|
|
// handle arrays
|
|
if( count == -1 )
|
|
count = size;
|
|
|
|
switch( format )
|
|
{
|
|
case GL_FLOAT:
|
|
pglUniform1fvARB( location, count, (const float *)pdata );
|
|
break;
|
|
case GL_FLOAT_VEC2_ARB:
|
|
pglUniform2fvARB( location, count, (const float *)pdata );
|
|
break;
|
|
case GL_FLOAT_VEC3_ARB:
|
|
pglUniform3fvARB( location, count, (const float *)pdata );
|
|
break;
|
|
case GL_FLOAT_VEC4_ARB:
|
|
pglUniform4fvARB( location, count, (const float *)pdata );
|
|
break;
|
|
case GL_FLOAT_MAT2_ARB:
|
|
pglUniformMatrix2fvARB( location, count, GL_FALSE, (const float *)pdata );
|
|
break;
|
|
case GL_FLOAT_MAT3_ARB:
|
|
pglUniformMatrix3fvARB( location, count, GL_FALSE, (const float *)pdata );
|
|
break;
|
|
case GL_FLOAT_MAT4_ARB:
|
|
pglUniformMatrix4fvARB( location, count, GL_FALSE, (const float *)pdata );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int testSize = GetSizeInBytes();
|
|
|
|
// some values could be cached
|
|
if( testSize <= 16 && !memcmp( &cache, check, testSize ))
|
|
return;
|
|
|
|
// single cached values
|
|
switch( format )
|
|
{
|
|
case GL_FLOAT:
|
|
pglUniform1fARB( location, check->fValue[0] );
|
|
cache.fValue[0] = check->fValue[0];
|
|
break;
|
|
case GL_FLOAT_VEC2_ARB:
|
|
pglUniform2fARB( location, check->fValue[0], check->fValue[1] );
|
|
cache.fValue[0] = check->fValue[0];
|
|
cache.fValue[1] = check->fValue[1];
|
|
break;
|
|
case GL_FLOAT_VEC3_ARB:
|
|
pglUniform3fARB( location, check->fValue[0], check->fValue[1], check->fValue[2] );
|
|
cache.fValue[0] = check->fValue[0];
|
|
cache.fValue[1] = check->fValue[1];
|
|
cache.fValue[2] = check->fValue[2];
|
|
break;
|
|
case GL_FLOAT_VEC4_ARB:
|
|
pglUniform4fARB( location, check->fValue[0], check->fValue[1], check->fValue[2], check->fValue[3] );
|
|
cache.fValue[0] = check->fValue[0];
|
|
cache.fValue[1] = check->fValue[1];
|
|
cache.fValue[2] = check->fValue[2];
|
|
cache.fValue[3] = check->fValue[3];
|
|
break;
|
|
case GL_INT:
|
|
pglUniform1iARB( location, check->iValue[0] );
|
|
cache.iValue[0] = check->iValue[0];
|
|
break;
|
|
case GL_INT_VEC2_ARB:
|
|
pglUniform2iARB( location, check->iValue[0], check->iValue[1] );
|
|
cache.iValue[0] = check->iValue[0];
|
|
cache.iValue[1] = check->iValue[1];
|
|
break;
|
|
case GL_INT_VEC3_ARB:
|
|
pglUniform3iARB( location, check->iValue[0], check->iValue[1], check->iValue[2] );
|
|
cache.iValue[0] = check->iValue[0];
|
|
cache.iValue[1] = check->iValue[1];
|
|
cache.iValue[2] = check->iValue[2];
|
|
break;
|
|
case GL_INT_VEC4_ARB:
|
|
pglUniform4iARB( location, check->iValue[0], check->iValue[1], check->iValue[2], check->iValue[3] );
|
|
cache.iValue[0] = check->iValue[0];
|
|
cache.iValue[1] = check->iValue[1];
|
|
cache.iValue[2] = check->iValue[2];
|
|
cache.iValue[3] = check->iValue[3];
|
|
break;
|
|
case GL_FLOAT_MAT2_ARB:
|
|
pglUniformMatrix2fvARB( location, 1, GL_FALSE, (const float *)pdata );
|
|
cache.fValue[0] = check->fValue[0];
|
|
cache.fValue[1] = check->fValue[1];
|
|
cache.fValue[2] = check->fValue[2];
|
|
cache.fValue[3] = check->fValue[3];
|
|
break;
|
|
case GL_FLOAT_MAT3_ARB:
|
|
pglUniformMatrix3fvARB( location, 1, GL_FALSE, (const float *)pdata );
|
|
break;
|
|
case GL_FLOAT_MAT4_ARB:
|
|
pglUniformMatrix4fvARB( location, 1, GL_FALSE, (const float *)pdata );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// all known engine uniforms
|
|
static uniformTable_t glsl_uniformTable[] =
|
|
{
|
|
{ "u_ColorMap", UT_COLORMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_DepthMap", UT_DEPTHMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_NormalMap", UT_NORMALMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_GlossMap", UT_GLOSSMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_DetailMap", UT_DETAILMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_ProjectMap", UT_PROJECTMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_ShadowMap0", UT_SHADOWMAP0, UFL_TEXTURE_UNIT },
|
|
{ "u_ShadowMap1", UT_SHADOWMAP1, UFL_TEXTURE_UNIT },
|
|
{ "u_ShadowMap2", UT_SHADOWMAP2, UFL_TEXTURE_UNIT },
|
|
{ "u_ShadowMap3", UT_SHADOWMAP3, UFL_TEXTURE_UNIT },
|
|
{ "u_ShadowMap", UT_SHADOWMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_LightMap", UT_LIGHTMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_DeluxeMap", UT_DELUXEMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_DecalMap", UT_DECALMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_ScreenMap", UT_SCREENMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_VisLightMap0", UT_VISLIGHTMAP0, UFL_TEXTURE_UNIT },
|
|
{ "u_VisLightMap1", UT_VISLIGHTMAP1, UFL_TEXTURE_UNIT },
|
|
{ "u_EnvMap0", UT_ENVMAP0, UFL_TEXTURE_UNIT },
|
|
{ "u_EnvMap1", UT_ENVMAP1, UFL_TEXTURE_UNIT },
|
|
{ "u_EnvMap", UT_ENVMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_GlowMap", UT_GLOWMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_HeightMap", UT_HEIGHTMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_LayerMap", UT_LAYERMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_FragData0", UT_FRAGDATA0, UFL_TEXTURE_UNIT },
|
|
{ "u_FragData1", UT_FRAGDATA1, UFL_TEXTURE_UNIT },
|
|
{ "u_FragData2", UT_FRAGDATA2, UFL_TEXTURE_UNIT },
|
|
{ "u_BspPlanesMap", UT_BSPPLANESMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_BspModelsMap", UT_BSPMODELSMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_BspNodesMap", UT_BSPNODESMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_BspLightsMap", UT_BSPLIGHTSMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_FitNormalMap", UT_FITNORMALMAP, UFL_TEXTURE_UNIT },
|
|
{ "u_ModelMatrix", UT_MODELMATRIX, 0 },
|
|
{ "u_ReflectMatrix", UT_REFLECTMATRIX, 0 },
|
|
{ "u_BonesArray", UT_BONESARRAY, 0 },
|
|
{ "u_BoneQuaternion", UT_BONEQUATERNION, 0 },
|
|
{ "u_BonePosition", UT_BONEPOSITION, 0 },
|
|
{ "u_ScreenSizeInv", UT_SCREENSIZEINV, UFL_GLOBAL_PARM },
|
|
{ "u_zFar", UT_ZFAR, UFL_GLOBAL_PARM },
|
|
{ "u_LightStyleValues", UT_LIGHTSTYLEVALUES, UFL_GLOBAL_PARM },
|
|
{ "u_LightStyles", UT_LIGHTSTYLES, 0 },
|
|
{ "u_RealTime", UT_REALTIME, UFL_GLOBAL_PARM },
|
|
{ "u_DetailScale", UT_DETAILSCALE, 0 },
|
|
{ "u_FogParams", UT_FOGPARAMS, UFL_GLOBAL_PARM },
|
|
{ "u_ShadowParams", UT_SHADOWPARMS, 0 },
|
|
{ "u_TexOffset", UT_TEXOFFSET, 0 },
|
|
{ "u_ViewOrigin", UT_VIEWORIGIN, 0 }, // not in a global because it's transformed into modelspace
|
|
{ "u_ViewRight", UT_VIEWRIGHT, 0 },
|
|
{ "u_RenderColor", UT_RENDERCOLOR, 0 },
|
|
{ "u_RenderAlpha", UT_RENDERALPHA, 0 },
|
|
{ "u_Smoothness", UT_SMOOTHNESS, 0 },
|
|
{ "u_ShadowMatrix", UT_SHADOWMATRIX, 0 },
|
|
{ "u_ShadowSplitDist", UT_SHADOWSPLITDIST, UFL_GLOBAL_PARM },
|
|
{ "u_TexelSize", UT_TEXELSIZE, UFL_GLOBAL_PARM },
|
|
{ "u_GammaTable", UT_GAMMATABLE, UFL_GLOBAL_PARM },
|
|
{ "u_LightDir", UT_LIGHTDIR, 0 },
|
|
{ "u_LightDiffuse", UT_LIGHTDIFFUSE, 0 },
|
|
{ "u_LightShade", UT_LIGHTSHADE, 0 },
|
|
{ "u_LightOrigin", UT_LIGHTORIGIN, 0 },
|
|
{ "u_LightViewProjMatrix", UT_LIGHTVIEWPROJMATRIX, 0 },
|
|
{ "u_DiffuseFactor", UT_DIFFUSEFACTOR, UFL_GLOBAL_PARM },
|
|
{ "u_AmbientFactor", UT_AMBIENTFACTOR, UFL_GLOBAL_PARM },
|
|
{ "u_AmbientCube", UT_AMBIENTCUBE, 0 },
|
|
{ "u_SunRefract", UT_SUNREFRACT, UFL_GLOBAL_PARM },
|
|
{ "u_LerpFactor", UT_LERPFACTOR, 0 },
|
|
{ "u_RefractScale", UT_REFRACTSCALE, 0 },
|
|
{ "u_ReflectScale", UT_REFLECTSCALE, 0 },
|
|
{ "u_AberrationScale", UT_ABERRATIONSCALE, 0 },
|
|
{ "u_BoxMins", UT_BOXMINS, 0 },
|
|
{ "u_BoxMaxs", UT_BOXMAXS, 0 },
|
|
{ "u_CubeOrigin", UT_CUBEORIGIN, 0 },
|
|
{ "u_CubeMipCount", UT_CUBEMIPCOUNT, 0 },
|
|
{ "u_LightNums0", UT_LIGHTNUMS0, 0 },
|
|
{ "u_LightNums1", UT_LIGHTNUMS1, 0 },
|
|
{ "u_GrassParams", UT_GRASSPARAMS, 0 },
|
|
{ "u_ReliefParams", UT_RELIEFPARAMS, 0 },
|
|
{ "u_BlurFactor", UT_BLURFACTOR, 0 },
|
|
{ "u_ScreenWidth", UT_SCREENWIDTH, 0 },
|
|
{ "u_ScreenHeight", UT_SCREENHEIGHT, 0 },
|
|
{ "u_FocalDepth", UT_FOCALDEPTH, 0 },
|
|
{ "u_FocalLength", UT_FOCALLENGTH, 0 },
|
|
{ "u_DofDebug", UT_DOFDEBUG, 0 },
|
|
{ "u_FStop", UT_FSTOP, 0 },
|
|
{ "u_GrayScale", UT_GRAYSCALE, 0 },
|
|
{ "u_LightGamma", UT_LIGHTGAMMA, UFL_GLOBAL_PARM },
|
|
{ "u_LightScale", UT_LIGHTSCALE, UFL_GLOBAL_PARM },
|
|
{ "u_LightThreshold", UT_LIGHTTHRESHOLD, UFL_GLOBAL_PARM },
|
|
{ "u_NumVisibleModels", UT_NUMVISIBLEMODELS, UFL_GLOBAL_PARM },
|
|
{ "u_Undefined", UT_UNDEFINED, 0 },
|
|
};
|
|
|
|
static char *GL_PrintInfoLog( GLhandleARB object )
|
|
{
|
|
static char msg[32768];
|
|
int maxLength = 0;
|
|
|
|
pglGetObjectParameterivARB( object, GL_OBJECT_INFO_LOG_LENGTH_ARB, &maxLength );
|
|
|
|
if( maxLength >= sizeof( msg ))
|
|
{
|
|
ALERT( at_warning, "GL_PrintInfoLog: message exceeds %i symbols\n", sizeof( msg ));
|
|
maxLength = sizeof( msg ) - 1;
|
|
}
|
|
|
|
pglGetInfoLogARB( object, maxLength, &maxLength, msg );
|
|
|
|
return msg;
|
|
}
|
|
|
|
static char *GL_PrintShaderSource( GLhandleARB object )
|
|
{
|
|
static char msg[8192];
|
|
int maxLength = 0;
|
|
|
|
pglGetObjectParameterivARB( object, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &maxLength );
|
|
|
|
if( maxLength >= sizeof( msg ))
|
|
{
|
|
ALERT( at_warning, "GL_PrintShaderSource: message exceeds %i symbols\n", sizeof( msg ));
|
|
maxLength = sizeof( msg ) - 1;
|
|
}
|
|
|
|
pglGetShaderSourceARB( object, maxLength, &maxLength, msg );
|
|
|
|
return msg;
|
|
}
|
|
|
|
static bool GL_PushFileStack( const char *filename )
|
|
{
|
|
if( file_stack_pos < MAX_FILE_STACK )
|
|
{
|
|
Q_strncpy( filenames_stack[file_stack_pos], filename, sizeof( filenames_stack[0] ));
|
|
file_stack_pos++;
|
|
return true;
|
|
}
|
|
|
|
ALERT( at_error, "GL_PushFileStack: stack overflow\n" );
|
|
return false;
|
|
}
|
|
|
|
static bool GL_PopFileStack( void )
|
|
{
|
|
file_stack_pos--;
|
|
|
|
if( file_stack_pos < 0 )
|
|
{
|
|
ALERT( at_error, "GL_PushFileStack: stack underflow\n" );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool GL_CheckFileStack( const char *filename )
|
|
{
|
|
for( int i = 0; i < file_stack_pos; i++ )
|
|
{
|
|
if( !Q_stricmp( filenames_stack[i], filename ))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool GL_TestSource( const char *szFilename, const char *szCacheName, CVirtualFS *file )
|
|
{
|
|
int iCompare;
|
|
|
|
if( GL_CheckFileStack( szFilename ))
|
|
return false;
|
|
|
|
// check vertex shader source or include-file
|
|
if( COMPARE_FILE_TIME( szFilename, szCacheName, &iCompare ))
|
|
{
|
|
// glsl file is newer.
|
|
if( iCompare > 0 )
|
|
{
|
|
Msg( "%s was changed, %s will be updated\n", szFilename, szCacheName );
|
|
cache_needs_update = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Msg( "%s will be created\n", szCacheName );
|
|
cache_needs_update = true;
|
|
}
|
|
|
|
int size;
|
|
char *source = (char *)gEngfuncs.COM_LoadFile((char *)szFilename, 5, &size );
|
|
if( !source ) return false;
|
|
|
|
GL_PushFileStack( szFilename );
|
|
file->Write( source, size );
|
|
file->Seek( 0, SEEK_SET ); // rewind
|
|
|
|
gEngfuncs.COM_FreeFile( source );
|
|
|
|
return true;
|
|
}
|
|
|
|
static void GL_TestFile( const char *szFilename, const char *szCacheName, CVirtualFS *file )
|
|
{
|
|
char *pfile, token[256];
|
|
char line[2048];
|
|
int ret;
|
|
|
|
do
|
|
{
|
|
ret = file->Gets( line, sizeof( line ));
|
|
pfile = line;
|
|
|
|
// NOTE: if first keyword it's not an '#include' just ignore it
|
|
pfile = COM_ParseFile( pfile, token );
|
|
if( !Q_strcmp( token, "#include" ))
|
|
{
|
|
CVirtualFS incfile;
|
|
char incname[256];
|
|
|
|
pfile = COM_ParseLine( pfile, token );
|
|
Q_snprintf( incname, sizeof( incname ), "glsl/%s", token );
|
|
if( !GL_TestSource( incname, szCacheName, &incfile ))
|
|
continue;
|
|
|
|
if( cache_needs_update )
|
|
break; // no reason to seek more
|
|
GL_TestFile( incname, szCacheName, &incfile );
|
|
}
|
|
} while( ret != EOF );
|
|
|
|
GL_PopFileStack();
|
|
}
|
|
|
|
static bool GL_TestShader( const char *szFilename, const char *szCacheName )
|
|
{
|
|
CVirtualFS file;
|
|
|
|
cache_needs_update = false;
|
|
file_stack_pos = 0;
|
|
|
|
if( !GL_TestSource( szFilename, szCacheName, &file ))
|
|
return false;
|
|
GL_TestFile( szFilename, szCacheName, &file );
|
|
|
|
return cache_needs_update;
|
|
}
|
|
|
|
static bool GL_LoadSource( const char *filename, CVirtualFS *file )
|
|
{
|
|
if( GL_CheckFileStack( filename ))
|
|
{
|
|
ALERT( at_error, "recursive include for %s\n", filename );
|
|
return false;
|
|
}
|
|
|
|
int size;
|
|
char *source = (char *)gEngfuncs.COM_LoadFile((char *)filename, 5, &size );
|
|
if( !source )
|
|
{
|
|
ALERT( at_error, "couldn't load %s\n", filename );
|
|
return false;
|
|
}
|
|
|
|
GL_PushFileStack( filename );
|
|
file->Write( source, size );
|
|
file->Seek( 0, SEEK_SET ); // rewind
|
|
|
|
gEngfuncs.COM_FreeFile( source );
|
|
|
|
return true;
|
|
}
|
|
|
|
static void GL_ParseFile( const char *filename, CVirtualFS *file, CVirtualFS *out )
|
|
{
|
|
char *pfile, token[256];
|
|
int ret, fileline = 1;
|
|
char line[2048];
|
|
|
|
// out->Printf( "#file %s\n", filename ); // OpenGL doesn't support #file :-(
|
|
out->Printf( "#line 0\n" );
|
|
|
|
do
|
|
{
|
|
ret = file->Gets( line, sizeof( line ));
|
|
pfile = line;
|
|
|
|
// NOTE: if first keyword it's not an '#include' just ignore it
|
|
pfile = COM_ParseFile( pfile, token );
|
|
if( !Q_strcmp( token, "#include" ))
|
|
{
|
|
CVirtualFS incfile;
|
|
char incname[256];
|
|
|
|
pfile = COM_ParseLine( pfile, token );
|
|
Q_snprintf( incname, sizeof( incname ), "glsl/%s", token );
|
|
if( !GL_LoadSource( incname, &incfile ))
|
|
{
|
|
fileline++;
|
|
continue;
|
|
}
|
|
GL_ParseFile( incname, &incfile, out );
|
|
// out->Printf( "#file %s\n", filename ); // OpenGL doesn't support #file :-(
|
|
out->Printf( "#line %i\n", fileline );
|
|
}
|
|
else out->Printf( "%s\n", line );
|
|
fileline++;
|
|
} while( ret != EOF );
|
|
|
|
GL_PopFileStack();
|
|
}
|
|
|
|
static bool GL_ProcessShader( const char *filename, CVirtualFS *out, const char *defines = NULL )
|
|
{
|
|
CVirtualFS file;
|
|
|
|
file_stack_pos = 0;
|
|
|
|
if( !GL_LoadSource( filename, &file ))
|
|
return false;
|
|
|
|
// add internal defines
|
|
out->Printf( "#version 120\n" ); // OpenGL 2.1 required (because 'flat' modifier support only starts from this version)
|
|
out->Printf( "#ifndef M_PI\n#define M_PI 3.14159265358979323846\n#endif\n" );
|
|
out->Printf( "#ifndef M_PI2\n#define M_PI2 6.28318530717958647692\n#endif\n" );
|
|
if( GL_Support( R_EXT_GPU_SHADER4 ))
|
|
{
|
|
out->Printf( "#extension GL_EXT_gpu_shader4 : require\n" ); // support bitwise ops
|
|
out->Printf( "#define GLSL_gpu_shader4\n" );
|
|
if( GL_Support( R_TEXTURE_ARRAY_EXT ))
|
|
out->Printf( "#define GLSL_ALLOW_TEXTURE_ARRAY\n" );
|
|
}
|
|
else if( GL_Support( R_TEXTURE_ARRAY_EXT ))
|
|
{
|
|
out->Printf( "#extension GL_EXT_texture_array : require\n" ); // support texture arrays
|
|
out->Printf( "#define GLSL_ALLOW_TEXTURE_ARRAY\n" );
|
|
}
|
|
|
|
if( GL_Support( R_TEXTURE_2D_RECT_EXT ))
|
|
{
|
|
out->Printf( "#extension GL_ARB_texture_rectangle : enable\n" ); // support texture rectangle
|
|
}
|
|
|
|
if( defines ) out->Print( defines );
|
|
|
|
// user may override this constants
|
|
out->Printf( "#ifndef MAXSTUDIOBONES\n#define MAXSTUDIOBONES %i\n#endif\n", glConfig.max_skinning_bones );
|
|
out->Printf( "#ifndef MAX_SHADOWMAPS\n#define MAX_SHADOWMAPS %i\n#endif\n", MAX_SHADOWMAPS );
|
|
out->Printf( "#ifndef NUM_SHADOW_SPLITS\n#define NUM_SHADOW_SPLITS %i\n#endif\n", NUM_SHADOW_SPLITS );
|
|
out->Printf( "#ifndef LIGHT_SAMPLES\n#define LIGHT_SAMPLES %i\n#endif\n", LIGHT_SAMPLES );
|
|
out->Printf( "#ifndef MAX_LIGHTSTYLES\n#define MAX_LIGHTSTYLES %i\n#endif\n", MAX_LIGHTSTYLES );
|
|
out->Printf( "#ifndef MAXLIGHTMAPS\n#define MAXLIGHTMAPS %i\n#endif\n", MAXLIGHTMAPS );
|
|
out->Printf( "#ifndef MAXDYNLIGHTS\n#define MAXDYNLIGHTS %i\n#endif\n", (int)cv_deferred_maxlights->value );
|
|
out->Printf( "#ifndef GRASS_ANIM_DIST\n#define GRASS_ANIM_DIST %f\n#endif\n", GRASS_ANIM_DIST );
|
|
|
|
GL_ParseFile( filename, &file, out );
|
|
out->Write( "", 1 ); // terminator
|
|
|
|
return true;
|
|
}
|
|
|
|
static void GL_LoadGPUShader( glsl_program_t *shader, const char *name, GLenum shaderType, const char *defines = NULL )
|
|
{
|
|
char filename[256];
|
|
GLhandleARB object;
|
|
CVirtualFS source;
|
|
GLint compiled;
|
|
|
|
ASSERT( shader != NULL );
|
|
|
|
switch( shaderType )
|
|
{
|
|
case GL_VERTEX_SHADER_ARB:
|
|
Q_snprintf( filename, sizeof( filename ), "glsl/%s_vp.glsl", name );
|
|
break;
|
|
case GL_FRAGMENT_SHADER_ARB:
|
|
Q_snprintf( filename, sizeof( filename ), "glsl/%s_fp.glsl", name );
|
|
break;
|
|
default:
|
|
ALERT( at_error, "GL_LoadGPUShader: unknown shader type %p\n", shaderType );
|
|
return;
|
|
}
|
|
|
|
// load includes, add some directives
|
|
if( !GL_ProcessShader( filename, &source, defines ))
|
|
return;
|
|
|
|
GLcharARB *buffer = (GLcharARB *)source.GetBuffer();
|
|
int bufferSize = source.GetSize();
|
|
|
|
ALERT( at_aiconsole, "loading '%s'\n", filename );
|
|
object = pglCreateShaderObjectARB( shaderType );
|
|
pglShaderSourceARB( object, GL_TRUE, (const GLcharARB **)&buffer, &bufferSize );
|
|
|
|
// compile shader
|
|
pglCompileShaderARB( object );
|
|
|
|
// check if shader compiled
|
|
pglGetObjectParameterivARB( object, GL_OBJECT_COMPILE_STATUS_ARB, &compiled );
|
|
|
|
if( !compiled )
|
|
{
|
|
if( developer_level ) Msg( "%s", GL_PrintInfoLog( object ));
|
|
if( developer_level ) Msg( "Shader options:%s\n", GL_PretifyListOptions( defines ));
|
|
ALERT( at_error, "Couldn't compile %s\n", filename );
|
|
return;
|
|
}
|
|
|
|
if( shaderType == GL_VERTEX_SHADER_ARB )
|
|
shader->status |= SHADER_VERTEX_COMPILED;
|
|
else shader->status |= SHADER_FRAGMENT_COMPILED;
|
|
|
|
// attach shader to program
|
|
pglAttachObjectARB( shader->handle, object );
|
|
|
|
// delete shader, no longer needed
|
|
pglDeleteObjectARB( object );
|
|
}
|
|
|
|
static bool GL_LoadGPUBinaryShader( glsl_program_t *shader, const char *vpname, const char *fpname, uint checksum )
|
|
{
|
|
char szFilename[MAX_PATH];
|
|
char szVpSource[MAX_PATH];
|
|
char szFpSource[MAX_PATH];
|
|
GLint linked = 0;
|
|
int length;
|
|
|
|
if( !GL_Support( R_BINARY_SHADER_EXT ))
|
|
return false;
|
|
|
|
Q_snprintf( szFilename, sizeof( szFilename ), "cache/glsl/%p.bin", checksum );
|
|
Q_snprintf( szVpSource, sizeof( szVpSource ), "glsl/%s_vp.glsl", vpname );
|
|
Q_snprintf( szFpSource, sizeof( szFpSource ), "glsl/%s_fp.glsl", fpname );
|
|
|
|
// check vertex shader source
|
|
if( GL_TestShader( szVpSource, szFilename ))
|
|
return false;
|
|
|
|
// check fragment shader source
|
|
if( GL_TestShader( szFpSource, szFilename ))
|
|
return false;
|
|
|
|
byte *aMemFile = LOAD_FILE( szFilename, &length );
|
|
if( !aMemFile ) return false;
|
|
|
|
pglProgramBinary( shader->handle, glConfig.binary_formats, aMemFile, length );
|
|
pglGetObjectParameterivARB( shader->handle, GL_OBJECT_LINK_STATUS_ARB, &linked );
|
|
SetBits( shader->status, SHADER_FRAGMENT_COMPILED|SHADER_VERTEX_COMPILED );
|
|
FREE_FILE( aMemFile );
|
|
|
|
if( linked )
|
|
{
|
|
ALERT( at_aiconsole, "loading %s\n", szFilename );
|
|
SetBits( shader->status, SHADER_PROGRAM_LINKED );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool GL_SaveGPUBinaryShader( glsl_program_t *shader, uint checksum )
|
|
{
|
|
char szFilename[MAX_PATH];
|
|
int length, result;
|
|
GLenum outFormat;
|
|
byte *binary;
|
|
|
|
if( !GL_Support( R_BINARY_SHADER_EXT ))
|
|
return false;
|
|
|
|
Q_snprintf( szFilename, sizeof( szFilename ), "cache/glsl/%p.bin", checksum );
|
|
pglGetObjectParameterivARB( shader->handle, GL_PROGRAM_BINARY_LENGTH, &length );
|
|
if( length <= 0 ) return false;
|
|
|
|
binary = (byte *)malloc( length );
|
|
pglGetProgramBinary( shader->handle, length, &length, &outFormat, binary );
|
|
|
|
result = SAVE_FILE( szFilename, binary, length );
|
|
ALERT( at_aiconsole, "write glsl cache: %s\n", szFilename );
|
|
free( binary );
|
|
|
|
return (result != 0 );
|
|
}
|
|
|
|
static void GL_LinkProgram( glsl_program_t *shader )
|
|
{
|
|
GLint linked = 0;
|
|
|
|
if( !shader ) return;
|
|
|
|
if( GL_Support( R_BINARY_SHADER_EXT ))
|
|
pglProgramParameteri( shader->handle, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE );
|
|
|
|
pglLinkProgramARB( shader->handle );
|
|
|
|
pglGetObjectParameterivARB( shader->handle, GL_OBJECT_LINK_STATUS_ARB, &linked );
|
|
if( !linked )
|
|
{
|
|
ALERT( at_error, "%s\n%s shader failed to link\n", GL_PrintInfoLog( shader->handle ), shader->name );
|
|
if( developer_level ) Msg( "Shader options:%s\n", GL_PretifyListOptions( shader->options ));
|
|
}
|
|
else shader->status |= SHADER_PROGRAM_LINKED;
|
|
}
|
|
|
|
static void GL_ValidateProgram( glsl_program_t *shader )
|
|
{
|
|
GLint validated = 0;
|
|
|
|
if( !shader ) return;
|
|
|
|
pglValidateProgramARB( shader->handle );
|
|
|
|
pglGetObjectParameterivARB( shader->handle, GL_OBJECT_VALIDATE_STATUS_ARB, &validated );
|
|
if( !validated ) ALERT( at_error, "%s\n%s shader failed to validate\n", GL_PrintInfoLog( shader->handle ), shader->name );
|
|
}
|
|
|
|
int GL_UniformTypeToDwordCount( GLuint type, bool align = false )
|
|
{
|
|
switch( type )
|
|
{
|
|
case GL_INT:
|
|
case GL_UNSIGNED_INT:
|
|
case GL_SAMPLER_1D_ARB:
|
|
case GL_SAMPLER_2D_ARB:
|
|
case GL_SAMPLER_3D_ARB:
|
|
case GL_SAMPLER_CUBE_ARB:
|
|
case GL_SAMPLER_1D_SHADOW_ARB:
|
|
case GL_SAMPLER_2D_SHADOW_ARB:
|
|
case GL_SAMPLER_2D_RECT_ARB:
|
|
case GL_SAMPLER_2D_RECT_SHADOW_ARB:
|
|
case GL_SAMPLER_CUBE_SHADOW_EXT:
|
|
return align ? 4 : 1; // int[1]
|
|
case GL_FLOAT:
|
|
return align ? 4 : 1; // float[1]
|
|
case GL_FLOAT_VEC2_ARB:
|
|
return align ? 4 : 2; // float[2]
|
|
case GL_FLOAT_VEC3_ARB:
|
|
return align ? 4 : 3; // float[3]
|
|
case GL_FLOAT_VEC4_ARB:
|
|
return 4; // float[4]
|
|
case GL_FLOAT_MAT2_ARB:
|
|
return 4; // float[2][2]
|
|
case GL_FLOAT_MAT3_ARB:
|
|
return align ? 12 : 9; // float[3][3]
|
|
case GL_FLOAT_MAT4_ARB:
|
|
return 16;// float[4][4]
|
|
default: return align ? 4 : 1; // assume error
|
|
}
|
|
}
|
|
|
|
const char *GL_UniformTypeToName( GLuint type )
|
|
{
|
|
switch( type )
|
|
{
|
|
case GL_INT:
|
|
return "int";
|
|
case GL_UNSIGNED_INT:
|
|
return "uint";
|
|
case GL_FLOAT:
|
|
return "float";
|
|
case GL_FLOAT_VEC2_ARB:
|
|
return "vec2";
|
|
case GL_FLOAT_VEC3_ARB:
|
|
return "vec3";
|
|
case GL_FLOAT_VEC4_ARB:
|
|
return "vec4";
|
|
case GL_FLOAT_MAT2_ARB:
|
|
return "mat2";
|
|
case GL_FLOAT_MAT3_ARB:
|
|
return "mat3";
|
|
case GL_FLOAT_MAT4_ARB:
|
|
return "mat4";
|
|
case GL_SAMPLER_1D_ARB:
|
|
return "sampler1D";
|
|
case GL_SAMPLER_2D_ARB:
|
|
return "sampler2D";
|
|
case GL_SAMPLER_3D_ARB:
|
|
return "sampler3D";
|
|
case GL_SAMPLER_CUBE_ARB:
|
|
return "samplerCube";
|
|
case GL_SAMPLER_1D_ARRAY_EXT:
|
|
return "sampler1DArray";
|
|
case GL_SAMPLER_2D_ARRAY_EXT:
|
|
return "sampler2DArray";
|
|
case GL_SAMPLER_1D_SHADOW_ARB:
|
|
return "sampler1DShadow";
|
|
case GL_SAMPLER_2D_SHADOW_ARB:
|
|
return "sampler2DShadow";
|
|
case GL_SAMPLER_2D_RECT_ARB:
|
|
return "sampler2DRect";
|
|
case GL_SAMPLER_2D_RECT_SHADOW_ARB:
|
|
return "sampler2DRectShadow";
|
|
case GL_SAMPLER_CUBE_SHADOW_EXT:
|
|
return "samplerCubeShadow";
|
|
default: return "???";
|
|
}
|
|
}
|
|
|
|
void GL_ShowProgramUniforms( glsl_program_t *shader )
|
|
{
|
|
int count, size;
|
|
char uniformName[256];
|
|
int total_uniforms_used = 0;
|
|
GLuint type;
|
|
|
|
if( !shader || developer_level < DEV_EXTENDED )
|
|
return;
|
|
|
|
// install the executables in the program object as part of current state.
|
|
pglUseProgramObjectARB( shader->handle );
|
|
|
|
// query the number of active uniforms
|
|
pglGetObjectParameterivARB( shader->handle, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &count );
|
|
|
|
// Loop over each of the active uniforms, and set their value
|
|
for( int i = 0; i < count; i++ )
|
|
{
|
|
pglGetActiveUniformARB( shader->handle, i, sizeof( uniformName ), NULL, &size, &type, uniformName );
|
|
if( developer_level >= DEV_EXTENDED )
|
|
{
|
|
if( size != 1 )
|
|
{
|
|
char *end = Q_strchr( uniformName, '[' );
|
|
if( end ) *end = '\0'; // cutoff [0]
|
|
}
|
|
|
|
if( size == 1 ) ALERT( at_aiconsole, "uniform %s %s;\n", GL_UniformTypeToName( type ), uniformName );
|
|
else ALERT( at_aiconsole, "uniform %s %s[%i];\n", GL_UniformTypeToName( type ), uniformName, size );
|
|
}
|
|
total_uniforms_used += GL_UniformTypeToDwordCount( type ) * size;
|
|
}
|
|
|
|
if( total_uniforms_used >= glConfig.max_vertex_uniforms )
|
|
ALERT( at_error, "used uniforms %i is overflowed max count %i\n", total_uniforms_used, glConfig.max_vertex_uniforms );
|
|
else ALERT( at_aiconsole, "used uniforms %i from %i\n", total_uniforms_used, glConfig.max_vertex_uniforms );
|
|
|
|
int max_shader_uniforms = total_uniforms_used;
|
|
|
|
if( max_shader_uniforms > ( glConfig.max_skinning_bones * 12 ))
|
|
max_shader_uniforms -= ( glConfig.max_skinning_bones * 12 );
|
|
|
|
ALERT( at_aiconsole, "%s used %i uniforms\n", shader->name, max_shader_uniforms );
|
|
|
|
if( max_shader_uniforms > glConfig.peak_used_uniforms )
|
|
{
|
|
glConfig.peak_used_uniforms = max_shader_uniforms;
|
|
tr.show_uniforms_peak = true;
|
|
}
|
|
|
|
pglUseProgramObjectARB( GL_NONE );
|
|
}
|
|
|
|
static void GL_ParseProgramUniforms( glsl_program_t *shader )
|
|
{
|
|
int count, size;
|
|
char uniformName[256];
|
|
int total_uniforms_used = 0;
|
|
char cleanName[256];
|
|
int enumUnits = 0;
|
|
GLuint format;
|
|
|
|
// query the number of active uniforms
|
|
pglGetObjectParameterivARB( shader->handle, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &count );
|
|
pglUseProgramObjectARB( shader->handle );
|
|
|
|
shader->uniforms = (uniform_t *)Mem_Alloc( count * sizeof( uniform_t ));
|
|
shader->numUniforms = 0;
|
|
#ifdef _DEBUG_UNIFORMS
|
|
Msg( "%c%s%s\n", 3, shader->name, GL_PretifyListOptions( shader->options ));
|
|
#endif
|
|
// Loop over each of the active uniforms, and set their value
|
|
for( int i = 0; i < count; i++ )
|
|
{
|
|
uniformTable_t *desc;
|
|
uniform_t *uniform;
|
|
int location;
|
|
|
|
pglGetActiveUniformARB( shader->handle, i, sizeof( uniformName ), NULL, &size, &format, uniformName );
|
|
|
|
if(( location = pglGetUniformLocationARB( shader->handle, uniformName )) == -1 )
|
|
continue; // ignore built-in uniforms
|
|
|
|
// remove array size from name
|
|
Q_strncpy( cleanName, uniformName, sizeof( cleanName ));
|
|
char *end = Q_strchr( cleanName, '[' );
|
|
if( end ) *end = '\0'; // cutoff [0]
|
|
|
|
// check for description
|
|
for( int j = 0; j < ARRAYSIZE( glsl_uniformTable ); j++ )
|
|
{
|
|
desc = &glsl_uniformTable[j];
|
|
if( !Q_strcmp( desc->name, cleanName ))
|
|
break;
|
|
}
|
|
|
|
if( desc->type == UT_UNDEFINED )
|
|
{
|
|
ALERT( at_error, "%cUnhandled uniform %s. Ignoring\n", 3, uniformName );
|
|
continue;
|
|
}
|
|
|
|
// fill next uniform
|
|
uniform = &shader->uniforms[shader->numUniforms++];
|
|
Q_strncpy( uniform->name, cleanName, sizeof( uniform->name ));
|
|
uniform->location = location;
|
|
uniform->flags = desc->flags;
|
|
uniform->type = desc->type;
|
|
uniform->format = format;
|
|
uniform->size = size;
|
|
|
|
if( FBitSet( uniform->flags, UFL_TEXTURE_UNIT ))
|
|
{
|
|
uniform->unit = enumUnits++;
|
|
pglUniform1iARB( uniform->location, uniform->unit );
|
|
}
|
|
else uniform->unit = -1; // not a texture
|
|
|
|
if( enumUnits >= glConfig.max_texture_units )
|
|
ALERT( at_warning, "%c%s [%d] exceeded GL_MAX_IMAGE_UNITS\n", 3, cleanName, enumUnits );
|
|
#ifdef _DEBUG_UNIFORMS
|
|
if( uniform->unit != -1 ) Msg( "%c%s %s : %d;\n", 3, GL_UniformTypeToName( format ), cleanName, uniform->unit );
|
|
else if( size == 1 ) Msg( "%cuniform %s %s;\n", 3, GL_UniformTypeToName( format ), cleanName );
|
|
else Msg( "%cuniform %s %s[%i];\n", 3, GL_UniformTypeToName( format ), cleanName, size );
|
|
#endif
|
|
total_uniforms_used += GL_UniformTypeToDwordCount( format ) * size;
|
|
}
|
|
|
|
if( total_uniforms_used >= glConfig.max_vertex_uniforms )
|
|
ALERT( at_error, "%cused uniforms %i is overflowed max count %i\n", 3, total_uniforms_used, glConfig.max_vertex_uniforms );
|
|
#ifdef _DEBUG_UNIFORMS
|
|
ALERT( at_aiconsole, "%c%s used %i uniforms\n", 3, shader->name, total_uniforms_used );
|
|
#endif
|
|
if( total_uniforms_used > glConfig.peak_used_uniforms )
|
|
{
|
|
glConfig.peak_used_uniforms = total_uniforms_used;
|
|
tr.show_uniforms_peak = true;
|
|
}
|
|
|
|
pglUseProgramObjectARB( GL_NONE );
|
|
}
|
|
|
|
static void GL_SetDefaultVertexAttribs( glsl_program_t *shader )
|
|
{
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_POSITION, "attr_Position" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_TEXCOORD0, "attr_TexCoord0" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_TEXCOORD1, "attr_TexCoord1" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_TEXCOORD2, "attr_TexCoord2" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_TANGENT, "attr_Tangent" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_BINORMAL, "attr_Binormal" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_NORMAL, "attr_Normal" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_BONE_INDEXES, "attr_BoneIndexes" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_BONE_WEIGHTS, "attr_BoneWeights" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_LIGHT_STYLES, "attr_LightStyles" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_LIGHT_COLOR, "attr_LightColor" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_LIGHT_VECS, "attr_LightVecs" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_LIGHT_NUMS0, "attr_LightNums0" );
|
|
pglBindAttribLocationARB( shader->handle, ATTR_INDEX_LIGHT_NUMS1, "attr_LightNums1" );
|
|
}
|
|
|
|
static void GL_ParseProgramVertexAttribs( glsl_program_t *shader )
|
|
{
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_Position" ) == ATTR_INDEX_POSITION )
|
|
SetBits( shader->attribs, FATTR_POSITION );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_TexCoord0" ) == ATTR_INDEX_TEXCOORD0 )
|
|
SetBits( shader->attribs, FATTR_TEXCOORD0 );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_TexCoord1" ) == ATTR_INDEX_TEXCOORD1 )
|
|
SetBits( shader->attribs, FATTR_TEXCOORD1 );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_TexCoord2" ) == ATTR_INDEX_TEXCOORD2 )
|
|
SetBits( shader->attribs, FATTR_TEXCOORD2 );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_Tangent" ) == ATTR_INDEX_TANGENT )
|
|
SetBits( shader->attribs, FATTR_TANGENT );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_Binormal" ) == ATTR_INDEX_BINORMAL )
|
|
SetBits( shader->attribs, FATTR_BINORMAL );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_Normal" ) == ATTR_INDEX_NORMAL )
|
|
SetBits( shader->attribs, FATTR_NORMAL );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_BoneIndexes" ) == ATTR_INDEX_BONE_INDEXES )
|
|
SetBits( shader->attribs, FATTR_BONE_INDEXES );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_BoneWeights" ) == ATTR_INDEX_BONE_WEIGHTS )
|
|
SetBits( shader->attribs, FATTR_BONE_WEIGHTS );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_LightStyles" ) == ATTR_INDEX_LIGHT_STYLES )
|
|
SetBits( shader->attribs, FATTR_LIGHT_STYLES );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_LightColor" ) == ATTR_INDEX_LIGHT_COLOR )
|
|
SetBits( shader->attribs, FATTR_LIGHT_COLOR );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_LightVecs" ) == ATTR_INDEX_LIGHT_VECS )
|
|
SetBits( shader->attribs, FATTR_LIGHT_VECS );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_LightNums0" ) == ATTR_INDEX_LIGHT_NUMS0 )
|
|
SetBits( shader->attribs, FATTR_LIGHT_NUMS0 );
|
|
if( pglGetAttribLocationARB( shader->handle, "attr_LightNums1" ) == ATTR_INDEX_LIGHT_NUMS1 )
|
|
SetBits( shader->attribs, FATTR_LIGHT_NUMS1 );
|
|
}
|
|
|
|
void GL_BindShader( glsl_program_t *shader )
|
|
{
|
|
if( !shader && RI->currentshader )
|
|
{
|
|
pglUseProgramObjectARB( GL_NONE );
|
|
RI->currentshader = NULL;
|
|
}
|
|
else if( shader != RI->currentshader && shader != NULL && shader->handle )
|
|
{
|
|
pglUseProgramObjectARB( shader->handle );
|
|
r_stats.num_shader_binds++;
|
|
RI->currentshader = shader;
|
|
}
|
|
}
|
|
|
|
static void GL_FreeGPUShader( glsl_program_t *shader )
|
|
{
|
|
if( shader && shader->handle )
|
|
{
|
|
uint hash;
|
|
glsl_program_t *cur;
|
|
glsl_program_t **prev;
|
|
const char *find;
|
|
|
|
find = va( "%s %s", shader->name, shader->options );
|
|
if( shader->uniforms != NULL )
|
|
{
|
|
Mem_Free( shader->uniforms );
|
|
shader->uniforms = NULL;
|
|
}
|
|
|
|
// remove from hash table
|
|
hash = COM_HashKey( find, SHADERS_HASH_SIZE );
|
|
prev = &glsl_programsHashTable[hash];
|
|
|
|
while( 1 )
|
|
{
|
|
cur = *prev;
|
|
if( !cur ) break;
|
|
|
|
if( cur == shader )
|
|
{
|
|
*prev = cur->nextHash;
|
|
break;
|
|
}
|
|
prev = &cur->nextHash;
|
|
}
|
|
|
|
pglDeleteObjectARB( shader->handle );
|
|
memset( shader, 0, sizeof( *shader ));
|
|
}
|
|
}
|
|
|
|
static glsl_program_t *GL_CreateUberShader( GLint slot, const char *glname, const char *vpname, const char *fpname, const char *options, uint checksum )
|
|
{
|
|
if( !GL_Support( R_SHADER_GLSL100_EXT ))
|
|
return NULL;
|
|
|
|
if( num_glsl_programs >= MAX_GLSL_PROGRAMS )
|
|
{
|
|
ALERT( at_error, "GL_CreateUberShader: GLSL shaders limit exceeded (%i max)\n", MAX_GLSL_PROGRAMS );
|
|
return NULL;
|
|
}
|
|
|
|
// alloc new shader
|
|
glsl_program_t *shader = &glsl_programs[slot];
|
|
|
|
shader->handle = pglCreateProgramObjectARB();
|
|
if( !shader->handle ) return NULL; // some bad happens
|
|
|
|
Q_strncpy( shader->name, glname, sizeof( shader->name ));
|
|
Q_strncpy( shader->options, options, sizeof( shader->options ));
|
|
|
|
if( !GL_LoadGPUBinaryShader( shader, vpname, fpname, checksum ))
|
|
{
|
|
if( vpname ) GL_LoadGPUShader( shader, vpname, GL_VERTEX_SHADER_ARB, options );
|
|
else SetBits( shader->status, SHADER_VERTEX_COMPILED );
|
|
if( fpname ) GL_LoadGPUShader( shader, fpname, GL_FRAGMENT_SHADER_ARB, options );
|
|
else SetBits( shader->status, SHADER_FRAGMENT_COMPILED );
|
|
|
|
if( vpname && FBitSet( shader->status, SHADER_VERTEX_COMPILED ))
|
|
GL_SetDefaultVertexAttribs( shader );
|
|
|
|
GL_LinkProgram( shader );
|
|
|
|
if( FBitSet( shader->status, SHADER_PROGRAM_LINKED ))
|
|
GL_SaveGPUBinaryShader( shader, checksum );
|
|
}
|
|
|
|
// dynamic ubershaders has the identically name of fragment and vertex program
|
|
if(( glname == vpname ) && ( glname == fpname ))
|
|
SetBits( shader->status, SHADER_UBERSHADER ); // it's UberShader!
|
|
|
|
if( FBitSet( shader->status, SHADER_PROGRAM_LINKED ))
|
|
{
|
|
// register shader uniforms
|
|
GL_ParseProgramVertexAttribs( shader );
|
|
GL_ParseProgramUniforms( shader );
|
|
GL_ValidateProgram( shader );
|
|
}
|
|
|
|
// rewind generated errors if shader compile was failed
|
|
// to avoid show them later e.g. on textures loading
|
|
while( pglGetError() != GL_NO_ERROR );
|
|
#ifdef _DEBUG_UNIFORMS
|
|
if( FBitSet( shader->status, SHADER_UBERSHADER ))
|
|
ALERT( at_aiconsole, "CompileUberShader #%i: %s\n%s\n", slot, glname, options );
|
|
else ALERT( at_aiconsole, "CompileShader #%i: %s\n%s\n", slot, glname, options );
|
|
#endif
|
|
if( slot == num_glsl_programs )
|
|
{
|
|
if( num_glsl_programs == MAX_GLSL_PROGRAMS )
|
|
{
|
|
ALERT( at_error, "GL_CreateUberShader: GLSL shaders limit exceeded (%i max)\n", MAX_GLSL_PROGRAMS );
|
|
GL_FreeGPUShader( shader );
|
|
return NULL;
|
|
}
|
|
num_glsl_programs++;
|
|
}
|
|
|
|
// all done, program is loaded
|
|
|
|
return shader;
|
|
}
|
|
|
|
word GL_FindUberShader( const char *glname, const char *options )
|
|
{
|
|
glsl_program_t *prog;
|
|
|
|
if( !GL_Support( R_SHADER_GLSL100_EXT ))
|
|
return 0;
|
|
|
|
ASSERT( glname != NULL );
|
|
|
|
const char *find = va( "%s %s", glname, options );
|
|
uint hash = COM_HashKey( find, SHADERS_HASH_SIZE );
|
|
|
|
// check for coexist
|
|
for( prog = glsl_programsHashTable[hash]; prog != NULL; prog = prog->nextHash )
|
|
{
|
|
if( !Q_strcmp( prog->name, glname ) && !Q_strcmp( prog->options, options ))
|
|
return (word)(prog - glsl_programs);
|
|
}
|
|
|
|
// find free spot
|
|
for( int i = 1; i < num_glsl_programs; i++ )
|
|
if( !glsl_programs[i].name[0] )
|
|
break;
|
|
|
|
double start = Sys_DoubleTime();
|
|
uint checksum = FILE_CRC32( find, Q_strlen( find ));
|
|
prog = GL_CreateUberShader( i, glname, glname, glname, options, checksum );
|
|
double end = Sys_DoubleTime();
|
|
r_buildstats.compile_shader += (end - start);
|
|
if( RENDER_GET_PARM( PARM_CLIENT_ACTIVE, 0 ))
|
|
r_buildstats.total_buildtime += (end - start);
|
|
|
|
if( prog != NULL )
|
|
{
|
|
// add to hash table
|
|
prog->nextHash = glsl_programsHashTable[hash];
|
|
glsl_programsHashTable[hash] = prog;
|
|
}
|
|
|
|
return (word)(prog - glsl_programs);
|
|
}
|
|
|
|
word GL_FindShader( const char *glname, const char *vpname, const char *fpname, const char *options )
|
|
{
|
|
glsl_program_t *prog;
|
|
|
|
if( !GL_Support( R_SHADER_GLSL100_EXT ))
|
|
return 0;
|
|
|
|
ASSERT( glname != NULL );
|
|
|
|
const char *find = va( "%s %s", glname, options );
|
|
uint hash = COM_HashKey( find, SHADERS_HASH_SIZE );
|
|
|
|
// check for coexist
|
|
for( prog = glsl_programsHashTable[hash]; prog != NULL; prog = prog->nextHash )
|
|
{
|
|
if( !Q_strcmp( prog->name, glname ) && !Q_strcmp( prog->options, options ))
|
|
return (word)(prog - glsl_programs);
|
|
}
|
|
|
|
// find free spot
|
|
for( int i = 1; i < num_glsl_programs; i++ )
|
|
if( !glsl_programs[i].name[0] )
|
|
break;
|
|
|
|
double start = Sys_DoubleTime();
|
|
uint checksum = FILE_CRC32( find, Q_strlen( find ));
|
|
prog = GL_CreateUberShader( i, glname, vpname, fpname, options, checksum );
|
|
double end = Sys_DoubleTime();
|
|
r_buildstats.compile_shader += (end - start);
|
|
if( RENDER_GET_PARM( PARM_CLIENT_ACTIVE, 0 ))
|
|
r_buildstats.total_buildtime += (end - start);
|
|
|
|
if( prog != NULL )
|
|
{
|
|
// add to hash table
|
|
prog->nextHash = glsl_programsHashTable[hash];
|
|
glsl_programsHashTable[hash] = prog;
|
|
}
|
|
|
|
return (word)(prog - glsl_programs);
|
|
}
|
|
|
|
void GL_AddShaderFeature( word shaderNum, int feature )
|
|
{
|
|
if( shaderNum <= 0 || shaderNum >= MAX_GLSL_PROGRAMS )
|
|
return;
|
|
|
|
glsl_program_t *shader = &glsl_programs[shaderNum];
|
|
SetBits( shader->status, feature );
|
|
}
|
|
|
|
void GL_SetShaderDirective( char *options, const char *directive )
|
|
{
|
|
options[0] = '\0';
|
|
Q_strncat( options, va( "#define %s\n", directive ), MAX_OPTIONS_LENGTH );
|
|
}
|
|
|
|
void GL_AddShaderDirective( char *options, const char *directive )
|
|
{
|
|
Q_strncat( options, va( "#define %s\n", directive ), MAX_OPTIONS_LENGTH );
|
|
}
|
|
|
|
const char *GL_PretifyListOptions( const char *options, bool newlines )
|
|
{
|
|
static char output[MAX_OPTIONS_LENGTH];
|
|
const char *pstart = options;
|
|
const char *pend = options + Q_strlen( options );
|
|
char *pout = output;
|
|
|
|
*pout = '\0';
|
|
|
|
while( pstart < pend )
|
|
{
|
|
const char *pfind = Q_strstr( pstart, "#define" );
|
|
if( !pfind ) break;
|
|
|
|
pstart = pfind + Q_strlen( "#define" );
|
|
|
|
for( ; *pstart != '\n'; pstart++, pout++ )
|
|
*pout = *pstart;
|
|
if( newlines )
|
|
*pout++ = *pstart++;
|
|
else pstart++; // skip '\n'
|
|
}
|
|
|
|
if( pout == output )
|
|
return ""; // nothing found
|
|
|
|
*pout++ = ' ';
|
|
*pout = '\0';
|
|
|
|
return output;
|
|
}
|
|
|
|
void GL_CheckTextureAlpha( char *options, int texturenum )
|
|
{
|
|
if( RENDER_GET_PARM( PARM_TEX_ENCODE, texturenum ) == DXT_ENCODE_ALPHA_SDF )
|
|
GL_AddShaderDirective( options, "SIGNED_DISTANCE_FIELD" );
|
|
}
|
|
|
|
void GL_EncodeNormal( char *options, int texturenum )
|
|
{
|
|
if( RENDER_GET_PARM( PARM_TEX_GLFORMAT, texturenum ) == GL_COMPRESSED_RED_GREEN_RGTC2_EXT )
|
|
{
|
|
GL_AddShaderDirective( options, "NORMAL_RG_PARABOLOID" );
|
|
}
|
|
else if( RENDER_GET_PARM( PARM_TEX_GLFORMAT, texturenum ) == GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI )
|
|
{
|
|
GL_AddShaderDirective( options, "NORMAL_3DC_PARABOLOID" );
|
|
}
|
|
else if( RENDER_GET_PARM( PARM_TEX_ENCODE, texturenum ) == DXT_ENCODE_NORMAL_AG_PARABOLOID )
|
|
{
|
|
GL_AddShaderDirective( options, "NORMAL_AG_PARABOLOID" );
|
|
}
|
|
else if( RENDER_GET_PARM( PARM_TEX_GLFORMAT, texturenum ) == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT )
|
|
{
|
|
// implicit DXT5NM format (Paranoia2 v 1.2 old stuff)
|
|
if( FBitSet( RENDER_GET_PARM( PARM_TEX_FLAGS, texturenum ), TF_HAS_ALPHA ))
|
|
GL_AddShaderDirective( options, "NORMAL_AG_PARABOLOID" );
|
|
}
|
|
}
|
|
|
|
void GL_ListGPUShaders( void )
|
|
{
|
|
int count = 0;
|
|
|
|
for( uint i = 1; i < num_glsl_programs; i++ )
|
|
{
|
|
glsl_program_t *cur = &glsl_programs[i];
|
|
if( !cur->name[0] ) continue;
|
|
|
|
const char *options = GL_PretifyListOptions( cur->options );
|
|
|
|
if( Q_stricmp( options, "" ))
|
|
Msg( "#%i %s [%s]\n", i, cur->name, options );
|
|
else Msg( "#%i %s\n", i, cur->name );
|
|
count++;
|
|
}
|
|
|
|
Msg( "total %i shaders\n", count );
|
|
}
|
|
|
|
void GL_InitGPUShaders( void )
|
|
{
|
|
char options[MAX_OPTIONS_LENGTH];
|
|
|
|
memset( &glsl_programs, 0, sizeof( glsl_programs ));
|
|
num_glsl_programs = 1; // entry #0 isn't used
|
|
|
|
if( !GL_Support( R_SHADER_GLSL100_EXT ))
|
|
return;
|
|
|
|
ADD_COMMAND( "shaderlist", GL_ListGPUShaders );
|
|
|
|
// init sky shaders
|
|
GL_SetShaderDirective( options, "SKYBOX_DAYTIME" );
|
|
tr.skyboxEnv[0] = GL_FindShader( "forward/skybox", "forward/generic", "forward/skybox" );
|
|
tr.skyboxEnv[1] = GL_FindShader( "forward/skybox", "forward/generic", "forward/skybox", options );
|
|
tr.defSceneSky = GL_FindShader( "deferred/skybox", "deferred/generic", "deferred/sky_scene" );
|
|
tr.defLightSky = GL_FindShader( "deferred/skybox", "deferred/generic", "deferred/sky_light" );
|
|
}
|
|
|
|
void GL_FreeUberShaders( void )
|
|
{
|
|
if( !GL_Support( R_SHADER_GLSL100_EXT ))
|
|
return;
|
|
|
|
for( uint i = 1; i < num_glsl_programs; i++ )
|
|
{
|
|
if( FBitSet( glsl_programs[i].status, SHADER_UBERSHADER ))
|
|
GL_FreeGPUShader( &glsl_programs[i] );
|
|
}
|
|
|
|
GL_BindShader( GL_NONE );
|
|
}
|
|
|
|
void GL_FreeGPUShaders( void )
|
|
{
|
|
if( !GL_Support( R_SHADER_GLSL100_EXT ))
|
|
return;
|
|
|
|
for( uint i = 1; i < num_glsl_programs; i++ )
|
|
GL_FreeGPUShader( &glsl_programs[i] );
|
|
|
|
GL_BindShader( GL_NONE );
|
|
} |