Sync light state, bound shader, and various gl context states similarly to render matrices. Texture handles now refcounted, as multiple viewer textures could ref the same handle (cubemaps do this) Clean up gl extension loading a bit. Not necessary, but only look for ARB variants if not included in current core version. Removed unused extensions. Use core shader api if supported, else use ARB. (FN signatures are identical. Just doing some pointer substitution to ARB if not core.) Attempt at improving VBO update batching. Subdata updates better batched to gether per-frame. There's probably other stuff I forgot that is in this changeset, too. Todo: Fix lightstate assertion when toggling fullscreen with shaders off.
1328 lines
36 KiB
C++
1328 lines
36 KiB
C++
/**
|
|
* @file llshadermgr.cpp
|
|
* @brief Shader manager implementation.
|
|
*
|
|
* $LicenseInfo:firstyear=2005&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "linden_common.h"
|
|
#include <boost/filesystem.hpp> //First, because glh_linear #defines equivalent.. which boost uses internally
|
|
|
|
#include "llshadermgr.h"
|
|
|
|
#include "llfile.h"
|
|
#include "llrender.h"
|
|
#include "llcontrol.h" //for LLCachedControl
|
|
#include "lldir.h" //for gDirUtilp
|
|
|
|
#if LL_DARWIN
|
|
#include "OpenGL/OpenGL.h"
|
|
#endif
|
|
|
|
#ifdef LL_RELEASE_FOR_DOWNLOAD
|
|
#define UNIFORM_ERRS LL_WARNS_ONCE("Shader")
|
|
#else
|
|
#define UNIFORM_ERRS LL_ERRS("Shader")
|
|
#endif
|
|
|
|
// Lots of STL stuff in here, using namespace std to keep things more readable
|
|
using std::vector;
|
|
using std::pair;
|
|
using std::make_pair;
|
|
using std::string;
|
|
|
|
LLShaderMgr * LLShaderMgr::sInstance = NULL;
|
|
|
|
LLShaderMgr::LLShaderMgr()
|
|
{
|
|
{
|
|
const std::string dumpdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"shader_dump")+gDirUtilp->getDirDelimiter();
|
|
gDirUtilp->deleteDirAndContents(dumpdir);
|
|
}
|
|
}
|
|
|
|
|
|
LLShaderMgr::~LLShaderMgr()
|
|
{
|
|
}
|
|
|
|
// static
|
|
LLShaderMgr * LLShaderMgr::instance()
|
|
{
|
|
if(NULL == sInstance)
|
|
{
|
|
LL_ERRS("Shaders") << "LLShaderMgr should already have been instantiated by the application!" << LL_ENDL;
|
|
}
|
|
|
|
return sInstance;
|
|
}
|
|
|
|
BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
|
|
{
|
|
llassert_always(shader != NULL);
|
|
LLShaderFeatures *features = & shader->mFeatures;
|
|
|
|
if (features->attachNothing)
|
|
{
|
|
return TRUE;
|
|
}
|
|
//////////////////////////////////////
|
|
// Attach Vertex Shader Features First
|
|
//////////////////////////////////////
|
|
|
|
// NOTE order of shader object attaching is VERY IMPORTANT!!!
|
|
if (features->calculatesAtmospherics)
|
|
{
|
|
if (features->hasWaterFog)
|
|
{
|
|
if (!shader->attachObject("windlight/atmosphericsVarsWaterV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (!shader->attachObject("windlight/atmosphericsVarsV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (features->calculatesLighting || features->atmosphericHelpers)
|
|
{
|
|
if (!shader->attachObject("windlight/atmosphericsHelpersV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (features->calculatesLighting)
|
|
{
|
|
if (features->isSpecular)
|
|
{
|
|
if (!shader->attachObject("lighting/lightFuncSpecularV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!features->isAlphaLighting)
|
|
{
|
|
if (!shader->attachObject("lighting/sumLightsSpecularV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!shader->attachObject("lighting/lightSpecularV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightFuncV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!features->isAlphaLighting)
|
|
{
|
|
if (!shader->attachObject("lighting/sumLightsV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!shader->attachObject("lighting/lightV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// NOTE order of shader object attaching is VERY IMPORTANT!!!
|
|
if (features->calculatesAtmospherics)
|
|
{
|
|
if (!shader->attachObject("windlight/atmosphericsV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (features->hasSkinning)
|
|
{
|
|
if (!shader->attachObject("avatar/avatarSkinV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (features->hasObjectSkinning)
|
|
{
|
|
if (!shader->attachObject("avatar/objectSkinV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////
|
|
// Attach Fragment Shader Features Next
|
|
///////////////////////////////////////
|
|
|
|
if(features->calculatesAtmospherics)
|
|
{
|
|
if (features->hasWaterFog)
|
|
{
|
|
if (!shader->attachObject("windlight/atmosphericsVarsWaterF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (!shader->attachObject("windlight/atmosphericsVarsF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// NOTE order of shader object attaching is VERY IMPORTANT!!!
|
|
if (features->hasGamma)
|
|
{
|
|
if (!shader->attachObject("windlight/gammaF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (features->hasAtmospherics)
|
|
{
|
|
if (!shader->attachObject("windlight/atmosphericsF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (features->hasTransport)
|
|
{
|
|
if (!shader->attachObject("windlight/transportF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Test hasFullbright and hasShiny and attach fullbright and
|
|
// fullbright shiny atmos transport if we split them out.
|
|
}
|
|
|
|
// NOTE order of shader object attaching is VERY IMPORTANT!!!
|
|
if (features->hasWaterFog)
|
|
{
|
|
if (!shader->attachObject("environment/waterFogF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (features->hasLighting)
|
|
{
|
|
if (features->hasWaterFog)
|
|
{
|
|
if (features->disableTextureIndex)
|
|
{
|
|
if (features->hasAlphaMask)
|
|
{
|
|
if (!shader->attachObject("lighting/lightWaterAlphaMaskNonIndexedF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightWaterNonIndexedF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (features->hasAlphaMask)
|
|
{
|
|
if (!shader->attachObject("lighting/lightWaterAlphaMaskF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightWaterF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
if (features->disableTextureIndex)
|
|
{
|
|
if (features->hasAlphaMask)
|
|
{
|
|
if (!shader->attachObject("lighting/lightAlphaMaskNonIndexedF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightNonIndexedF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (features->hasAlphaMask)
|
|
{
|
|
if (!shader->attachObject("lighting/lightAlphaMaskF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// NOTE order of shader object attaching is VERY IMPORTANT!!!
|
|
else if (features->isFullbright)
|
|
{
|
|
|
|
if (features->isShiny && features->hasWaterFog)
|
|
{
|
|
if (features->disableTextureIndex)
|
|
{
|
|
if (!shader->attachObject("lighting/lightFullbrightShinyWaterNonIndexedF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightFullbrightShinyWaterF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
|
|
}
|
|
}
|
|
else if (features->hasWaterFog)
|
|
{
|
|
if (features->disableTextureIndex)
|
|
{
|
|
if (features->hasAlphaMask)
|
|
{
|
|
if (!shader->attachObject("lighting/lightFullbrightWaterNonIndexedAlphaMaskF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (!shader->attachObject("lighting/lightFullbrightWaterNonIndexedF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (features->hasAlphaMask)
|
|
{
|
|
if (!shader->attachObject("lighting/lightFullbrightWaterAlphaMaskF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (!shader->attachObject("lighting/lightFullbrightWaterF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
|
|
}
|
|
}
|
|
|
|
else if (features->isShiny)
|
|
{
|
|
if (features->disableTextureIndex)
|
|
{
|
|
if (!shader->attachObject("lighting/lightFullbrightShinyNonIndexedF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightFullbrightShinyF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
if (features->disableTextureIndex)
|
|
{
|
|
|
|
if (features->hasAlphaMask)
|
|
{
|
|
if (!shader->attachObject("lighting/lightFullbrightNonIndexedAlphaMaskF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightFullbrightNonIndexedF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (features->hasAlphaMask)
|
|
{
|
|
if (!shader->attachObject("lighting/lightFullbrightAlphaMaskF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightFullbrightF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// NOTE order of shader object attaching is VERY IMPORTANT!!!
|
|
else if (features->isShiny)
|
|
{
|
|
|
|
if (features->hasWaterFog)
|
|
{
|
|
if (features->disableTextureIndex)
|
|
{
|
|
if (!shader->attachObject("lighting/lightShinyWaterNonIndexedF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightShinyWaterF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
if (features->disableTextureIndex)
|
|
{
|
|
if (!shader->attachObject("lighting/lightShinyNonIndexedF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("lighting/lightShinyF.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (features->mIndexedTextureChannels <= 1)
|
|
{
|
|
if (!shader->attachObject("objects/nonindexedTextureV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!shader->attachObject("objects/indexedTextureV.glsl"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//============================================================================
|
|
// Load Shader
|
|
|
|
static std::string get_object_log(GLhandleARB ret, bool isProgram)
|
|
{
|
|
std::string res;
|
|
|
|
//get log length
|
|
GLint length;
|
|
(isProgram ? glGetProgramiv : glGetShaderiv)(ret, GL_INFO_LOG_LENGTH, &length);
|
|
|
|
if (length > 0)
|
|
{
|
|
//the log could be any size, so allocate appropriately
|
|
GLcharARB* log = new GLcharARB[length];
|
|
(isProgram ? glGetProgramInfoLog : glGetShaderInfoLog)(ret, length, &length, log);
|
|
res = std::string((char *)log);
|
|
delete[] log;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void LLShaderMgr::dumpObjectLog(GLhandleARB ret, bool isProgram, bool warns)
|
|
{
|
|
std::string log = get_object_log(ret, isProgram);
|
|
if ( log.length() > 0 )
|
|
{
|
|
if (warns)
|
|
{
|
|
LL_WARNS("ShaderLoading") << log << LL_ENDL;
|
|
}
|
|
else
|
|
{
|
|
LL_INFOS("ShaderLoading") << log << LL_ENDL;
|
|
}
|
|
}
|
|
}
|
|
|
|
GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, std::map<std::string, std::string>* defines, S32 texture_index_channels)
|
|
{
|
|
std::pair<std::multimap<std::string, CachedObjectInfo >::iterator, std::multimap<std::string, CachedObjectInfo>::iterator> range;
|
|
range = mShaderObjects.equal_range(filename);
|
|
for (std::multimap<std::string, CachedObjectInfo>::iterator it = range.first; it != range.second;++it)
|
|
{
|
|
if((*it).second.mLevel == shader_level && (*it).second.mType == type && (*it).second.mIndexChannels == texture_index_channels && (*it).second.mDefinitions == (defines ? *defines : std::map<std::string, std::string>()))
|
|
{
|
|
//LL_INFOS("ShaderLoading") << "Loading cached shader for " << filename << LL_ENDL;
|
|
return (*it).second.mHandle;
|
|
}
|
|
}
|
|
|
|
GLenum error = GL_NO_ERROR;
|
|
if (gDebugGL)
|
|
{
|
|
error = glGetError();
|
|
if (error != GL_NO_ERROR)
|
|
{
|
|
LL_WARNS("ShaderLoading") << "GL ERROR entering loadShaderFile(): " << error << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
LL_INFOS("ShaderLoading") << "Loading shader file: " << filename << " class " << shader_level << LL_ENDL;
|
|
|
|
if (filename.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//read in from file
|
|
LLFILE* file = NULL;
|
|
|
|
S32 try_gpu_class = shader_level;
|
|
S32 gpu_class;
|
|
|
|
//find the most relevant file
|
|
for (gpu_class = try_gpu_class; gpu_class > 0; gpu_class--)
|
|
{ //search from the current gpu class down to class 1 to find the most relevant shader
|
|
std::stringstream fname;
|
|
fname << getShaderDirPrefix();
|
|
fname << gpu_class << "/" << filename;
|
|
|
|
LL_DEBUGS("ShaderLoading") << "Looking in " << fname.str() << LL_ENDL;
|
|
file = LLFile::fopen(fname.str(), "r"); /* Flawfinder: ignore */
|
|
if (file)
|
|
{
|
|
//LL_INFOS("ShaderLoading") << "Loading file: shaders/class" << gpu_class << "/" << filename << " (Want class " << gpu_class << ")" << LL_ENDL;
|
|
break; // done
|
|
}
|
|
}
|
|
|
|
if (file == NULL)
|
|
{
|
|
LL_WARNS("ShaderLoading") << "GLSL Shader file not found: " << filename << LL_ENDL;
|
|
return 0;
|
|
}
|
|
|
|
//we can't have any lines longer than 1024 characters
|
|
//or any shaders longer than 4096 lines... deal - DaveP
|
|
GLcharARB buff[1024];
|
|
GLcharARB* text[4096];
|
|
GLuint count = 0;
|
|
|
|
S32 major_version = gGLManager.mGLSLVersionMajor;
|
|
S32 minor_version = gGLManager.mGLSLVersionMinor;
|
|
|
|
if (major_version == 1 && minor_version < 30)
|
|
{
|
|
if (minor_version < 10)
|
|
{
|
|
//should NEVER get here -- if major version is 1 and minor version is less than 10,
|
|
// viewer should never attempt to use shaders, continuing will result in undefined behavior
|
|
LL_ERRS() << "Unsupported GLSL Version." << LL_ENDL;
|
|
}
|
|
|
|
if (minor_version <= 19)
|
|
{
|
|
text[count++] = strdup("#version 110\n");
|
|
}
|
|
else if (minor_version <= 29)
|
|
{
|
|
//set version to 1.20
|
|
text[count++] = strdup("#version 120\n");
|
|
if (type == GL_FRAGMENT_SHADER_ARB)
|
|
{
|
|
// Need to enable extensions here instead of in the shader files,
|
|
// before any non-preprocessor directives (per spec)
|
|
text[count++] = strdup("#extension GL_ARB_shader_texture_lod : enable\n");
|
|
text[count++] = strdup("#define FXAA_GLSL_120 1\n");
|
|
text[count++] = strdup("#define FXAA_FAST_PIXEL_OFFSET 0\n");
|
|
}
|
|
}
|
|
|
|
text[count++] = strdup("#define ATTRIBUTE attribute\n");
|
|
text[count++] = strdup("#define VARYING varying\n");
|
|
text[count++] = strdup("#define VARYING_FLAT varying\n");
|
|
}
|
|
else
|
|
{
|
|
if (major_version < 4)
|
|
{
|
|
if (major_version > 1 || minor_version >= 40)
|
|
{
|
|
//set version to 1.40
|
|
text[count++] = strdup("#version 140\n");
|
|
}
|
|
else
|
|
{
|
|
//set version to 1.30
|
|
text[count++] = strdup("#version 130\n");
|
|
}
|
|
if (minor_version == 50 && gGLManager.mHasGpuShader5)
|
|
{
|
|
// Need to enable extensions here instead of in the shader files,
|
|
// before any non-preprocessor directives (per spec)
|
|
text[count++] = strdup("#extension GL_ARB_gpu_shader5 : enable\n");
|
|
}
|
|
//some implementations of GLSL 1.30 require integer precision be explicitly declared
|
|
text[count++] = strdup("precision mediump int;\n");
|
|
text[count++] = strdup("precision highp float;\n");
|
|
if (type == GL_FRAGMENT_SHADER_ARB)
|
|
{
|
|
text[count++] = strdup("#define FXAA_GLSL_130 1\n");
|
|
}
|
|
}
|
|
else
|
|
{ //set version to 400
|
|
text[count++] = strdup("#version 400\n");
|
|
if (type == GL_FRAGMENT_SHADER_ARB)
|
|
{
|
|
text[count++] = strdup("#define FXAA_GLSL_400 1\n");
|
|
}
|
|
}
|
|
|
|
if (type == GL_VERTEX_SHADER_ARB)
|
|
{ //"varying" state is "out" in a vertex program, "in" in a fragment program
|
|
// ("varying" is deprecated after version 1.20)
|
|
text[count++] = strdup("#define ATTRIBUTE in\n");
|
|
text[count++] = strdup("#define VARYING out\n");
|
|
text[count++] = strdup("#define VARYING_FLAT flat out\n");
|
|
}
|
|
else
|
|
{
|
|
text[count++] = strdup("#define DEFINE_GL_FRAGCOLOR 1\n");
|
|
text[count++] = strdup("#define VARYING in\n");
|
|
text[count++] = strdup("#define VARYING_FLAT flat in\n");
|
|
|
|
//backwards compatibility with legacy texture lookup syntax
|
|
text[count++] = strdup("#define texture2D texture\n");
|
|
text[count++] = strdup("#define textureCube texture\n");
|
|
text[count++] = strdup("#define texture2DLod textureLod\n");
|
|
text[count++] = strdup("#define shadow2D(a,b) vec2(texture(a,b))\n");
|
|
}
|
|
}
|
|
|
|
if(defines)
|
|
{
|
|
for (std::map<std::string,std::string>::iterator iter = defines->begin(); iter != defines->end(); ++iter)
|
|
{
|
|
std::string define = "#define " + iter->first + " " + iter->second + "\n";
|
|
text[count++] = (GLcharARB *) strdup(define.c_str());
|
|
}
|
|
}
|
|
|
|
if (texture_index_channels > 0 && type == GL_FRAGMENT_SHADER_ARB)
|
|
{
|
|
//use specified number of texture channels for indexed texture rendering
|
|
|
|
/* prepend shader code that looks like this:
|
|
|
|
uniform sampler2D tex0;
|
|
uniform sampler2D tex1;
|
|
uniform sampler2D tex2;
|
|
.
|
|
.
|
|
.
|
|
uniform sampler2D texN;
|
|
|
|
VARYING_FLAT ivec4 vary_texture_index;
|
|
|
|
vec4 ret = vec4(1,0,1,1);
|
|
|
|
vec4 diffuseLookup(vec2 texcoord)
|
|
{
|
|
switch (vary_texture_index.r))
|
|
{
|
|
case 0: ret = texture2D(tex0, texcoord); break;
|
|
case 1: ret = texture2D(tex1, texcoord); break;
|
|
case 2: ret = texture2D(tex2, texcoord); break;
|
|
.
|
|
.
|
|
.
|
|
case N: return texture2D(texN, texcoord); break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
*/
|
|
|
|
text[count++] = strdup("#define HAS_DIFFUSE_LOOKUP 1\n");
|
|
|
|
//uniform declartion
|
|
for (S32 i = 0; i < texture_index_channels; ++i)
|
|
{
|
|
std::string decl = llformat("uniform sampler2D tex%d;\n", i);
|
|
text[count++] = strdup(decl.c_str());
|
|
}
|
|
|
|
if (texture_index_channels > 1)
|
|
{
|
|
text[count++] = strdup("VARYING_FLAT ivec4 vary_texture_index;\n");
|
|
}
|
|
|
|
text[count++] = strdup("vec4 diffuseLookup(vec2 texcoord)\n");
|
|
text[count++] = strdup("{\n");
|
|
|
|
|
|
if (texture_index_channels == 1)
|
|
{ //don't use flow control, that's silly
|
|
text[count++] = strdup("return texture2D(tex0, texcoord);\n");
|
|
text[count++] = strdup("}\n");
|
|
}
|
|
else if (major_version > 1 || minor_version >= 30)
|
|
{ //switches are supported in GLSL 1.30 and later
|
|
if (gGLManager.mIsNVIDIA || (gGLManager.mIsATI && gGLManager.mGLVersion < 3.3f))
|
|
{ //switches are unreliable on old drivers
|
|
for (S32 i = 0; i < texture_index_channels; ++i)
|
|
{
|
|
std::string if_string = llformat("\t%sif (vary_texture_index.r == %d) { return texture2D(tex%d, texcoord); }\n", i > 0 ? "else " : "", i, i);
|
|
text[count++] = strdup(if_string.c_str());
|
|
}
|
|
text[count++] = strdup("\treturn vec4(1,0,1,1);\n");
|
|
text[count++] = strdup("}\n");
|
|
}
|
|
else
|
|
{
|
|
text[count++] = strdup("\tvec4 ret = vec4(1,0,1,1);\n");
|
|
text[count++] = strdup("\tswitch (vary_texture_index.r)\n");
|
|
text[count++] = strdup("\t{\n");
|
|
|
|
//switch body
|
|
for (S32 i = 0; i < texture_index_channels; ++i)
|
|
{
|
|
std::string case_str = llformat("\t\tcase %d: ret = texture2D(tex%d, texcoord); break;\n", i, i);
|
|
text[count++] = strdup(case_str.c_str());
|
|
}
|
|
|
|
text[count++] = strdup("\t}\n");
|
|
text[count++] = strdup("\treturn ret;\n");
|
|
text[count++] = strdup("}\n");
|
|
}
|
|
}
|
|
else
|
|
{ //should never get here. Indexed texture rendering requires GLSL 1.30 or later
|
|
// (for passing integers between vertex and fragment shaders)
|
|
LL_ERRS() << "Indexed texture rendering requires GLSL 1.30 or later." << LL_ENDL;
|
|
}
|
|
}
|
|
else if( type == GL_FRAGMENT_SHADER_ARB )
|
|
{
|
|
text[count++] = strdup("#define HAS_DIFFUSE_LOOKUP 0\n");
|
|
}
|
|
|
|
//copy file into memory
|
|
while( fgets((char *)buff, 1024, file) != NULL && count < LL_ARRAY_SIZE(text) )
|
|
{
|
|
text[count++] = (GLcharARB *)strdup((char *)buff);
|
|
}
|
|
fclose(file);
|
|
|
|
//create shader object
|
|
GLhandleARB ret = glCreateShaderObjectARB(type);
|
|
if (gDebugGL)
|
|
{
|
|
error = glGetError();
|
|
if (error != GL_NO_ERROR)
|
|
{
|
|
LL_WARNS("ShaderLoading") << "GL ERROR in glCreateShaderObjectARB: " << error << LL_ENDL;
|
|
glDeleteShader(ret); //no longer need handle
|
|
ret=0;
|
|
}
|
|
}
|
|
|
|
//load source
|
|
if(ret)
|
|
{
|
|
glShaderSourceARB(ret, count, (const GLcharARB**) text, NULL);
|
|
|
|
if (gDebugGL)
|
|
{
|
|
error = glGetError();
|
|
if (error != GL_NO_ERROR)
|
|
{
|
|
LL_WARNS("ShaderLoading") << "GL ERROR in glShaderSourceARB: " << error << LL_ENDL;
|
|
glDeleteShader(ret); //no longer need handle
|
|
ret=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//compile source
|
|
if(ret)
|
|
{
|
|
glCompileShaderARB(ret);
|
|
|
|
if (gDebugGL)
|
|
{
|
|
error = glGetError();
|
|
if (error != GL_NO_ERROR)
|
|
{
|
|
LL_WARNS("ShaderLoading") << "GL ERROR in glCompileShaderARB: " << error << LL_ENDL;
|
|
glDeleteShader(ret); //no longer need handle
|
|
ret=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string error_str;
|
|
|
|
if (error == GL_NO_ERROR)
|
|
{
|
|
//check for errors
|
|
GLint success = GL_TRUE;
|
|
glGetShaderiv(ret, GL_COMPILE_STATUS, &success);
|
|
if (gDebugGL || success == GL_FALSE)
|
|
{
|
|
error = glGetError();
|
|
if (error != GL_NO_ERROR || success == GL_FALSE)
|
|
{
|
|
//an error occured, print log
|
|
LL_WARNS("ShaderLoading") << "GLSL Compilation Error: (" << error << ") in " << filename << LL_ENDL;
|
|
dumpObjectLog(ret, false);
|
|
error_str = get_object_log(ret, false);
|
|
|
|
std::stringstream ostr;
|
|
//dump shader source for debugging
|
|
for (GLuint i = 0; i < count; i++)
|
|
{
|
|
ostr << i << ": " << text[i];
|
|
|
|
if (i % 128 == 0)
|
|
{ //dump every 128 lines
|
|
|
|
LL_WARNS("ShaderLoading") << "\n" << ostr.str() << LL_ENDL;
|
|
ostr.clear();
|
|
ostr.str(LLStringUtil::null);
|
|
}
|
|
|
|
}
|
|
|
|
LL_WARNS("ShaderLoading") << "\n" << ostr.str() << LL_ENDL;
|
|
glDeleteShader(ret); //no longer need handle
|
|
ret = 0;
|
|
}
|
|
}
|
|
if(ret)
|
|
dumpObjectLog(ret, false, false);
|
|
}
|
|
|
|
static const LLCachedControl<bool> dump_raw_shaders("ShyotlDumpRawShaders",false);
|
|
if(dump_raw_shaders || !ret)
|
|
{
|
|
std::stringstream ostr;
|
|
for (GLuint i = 0; i < count; i++)
|
|
{
|
|
ostr << text[i];
|
|
}
|
|
|
|
std::string delim = gDirUtilp->getDirDelimiter();
|
|
std::string shader_name = filename.substr(filename.find_last_of("/")+1); //shader_name.glsl
|
|
shader_name = shader_name.substr(0,shader_name.find_last_of(".")); //shader_name
|
|
std::string maindir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"shader_dump"+delim);
|
|
//mkdir is NOT recursive. Step through the folders one by one.
|
|
LLFile::mkdir(maindir); //..Roaming/SecondLife/logs/shader_dump/
|
|
LLFile::mkdir(maindir+="class" + llformat("%i",gpu_class) + delim); //..shader_dump/class1/
|
|
LLFile::mkdir(maindir+=filename.substr(0,filename.find_last_of("/")+1)); //..shader_dump/class1/windlight/
|
|
|
|
LLAPRFile file(maindir + shader_name + (ret ? "" : llformat("_FAILED(%i)",error)) + ".glsl", LL_APR_W);
|
|
file.write(ostr.str().c_str(),ostr.str().length());
|
|
if(!error_str.empty())
|
|
{
|
|
LLAPRFile file2(maindir + shader_name + "_ERROR.txt", LL_APR_W);
|
|
file2.write(error_str.c_str(),error_str.length());
|
|
}
|
|
}
|
|
stop_glerror();
|
|
|
|
//free memory
|
|
for (GLuint i = 0; i < count; i++)
|
|
{
|
|
free(text[i]);
|
|
}
|
|
|
|
//successfully loaded, save results
|
|
if (ret)
|
|
{
|
|
// Add shader file to map
|
|
mShaderObjects.insert(make_pair(filename,CachedObjectInfo(ret,try_gpu_class,type, texture_index_channels,defines)));
|
|
shader_level = try_gpu_class;
|
|
}
|
|
else
|
|
{
|
|
if (shader_level > 1)
|
|
{
|
|
shader_level--;
|
|
return loadShaderFile(filename,shader_level,type, defines, texture_index_channels);
|
|
}
|
|
LL_WARNS("ShaderLoading") << "Failed to load " << filename << LL_ENDL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void LLShaderMgr::unloadShaders()
|
|
{
|
|
//Instead of manually unloading, shaders are now automatically accumulated in a list.
|
|
//Simply iterate and unload.
|
|
std::vector<LLGLSLShader *> &shader_list = LLShaderMgr::getGlobalShaderList();
|
|
for (std::vector<LLGLSLShader *>::iterator it = shader_list.begin(); it != shader_list.end(); ++it)
|
|
(*it)->unload();
|
|
mShaderObjects.clear();
|
|
mProgramObjects.clear();
|
|
}
|
|
|
|
void LLShaderMgr::unloadShaderObjects()
|
|
{
|
|
std::multimap<std::string, LLShaderMgr::CachedObjectInfo >::iterator it = mShaderObjects.begin();
|
|
for (; it != mShaderObjects.end(); ++it)
|
|
if (it->second.mHandle)
|
|
glDeleteShader(it->second.mHandle);
|
|
mShaderObjects.clear();
|
|
cleanupShaderSources();
|
|
}
|
|
|
|
void LLShaderMgr::cleanupShaderSources()
|
|
{
|
|
if (!mProgramObjects.empty())
|
|
{
|
|
for (auto iter = mProgramObjects.cbegin(),
|
|
iter_end = mProgramObjects.cend(); iter != iter_end; ++iter)
|
|
{
|
|
GLuint program = iter->second;
|
|
if (program > 0)
|
|
{
|
|
GLhandleARB shaders[1024] = {};
|
|
GLsizei count = -1;
|
|
glGetAttachedObjectsARB(program, 1024, &count, shaders);
|
|
if (count > 0)
|
|
{
|
|
for (GLsizei i = 0; i < count; ++i)
|
|
{
|
|
std::multimap<std::string, LLShaderMgr::CachedObjectInfo>::iterator it = mShaderObjects.begin();
|
|
for (; it != LLShaderMgr::instance()->mShaderObjects.end(); it++)
|
|
{
|
|
if ((*it).second.mHandle == shaders[i])
|
|
{
|
|
glDetachObjectARB(program, shaders[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear the linked program list as its no longer needed
|
|
mProgramObjects.clear();
|
|
}
|
|
}
|
|
|
|
BOOL LLShaderMgr::linkProgramObject(GLhandleARB obj, BOOL suppress_errors)
|
|
{
|
|
//check for errors
|
|
glLinkProgramARB(obj);
|
|
GLint success = GL_TRUE;
|
|
glGetProgramiv(obj, GL_LINK_STATUS, &success);
|
|
if (!suppress_errors && success == GL_FALSE)
|
|
{
|
|
//an error occured, print log
|
|
LL_WARNS("ShaderLoading") << "GLSL Linker Error:" << LL_ENDL;
|
|
}
|
|
|
|
#if LL_DARWIN
|
|
|
|
// For some reason this absolutely kills the frame rate when VBO's are enabled
|
|
if (0)
|
|
{
|
|
// Force an evaluation of the gl state so the driver can tell if the shader will run in hardware or software
|
|
// per Apple's suggestion
|
|
LLGLSLShader::sNoFixedFunction = false;
|
|
|
|
glUseProgramObjectARB(obj);
|
|
|
|
gGL.begin(LLRender::TRIANGLES);
|
|
gGL.vertex3f(0.0f, 0.0f, 0.0f);
|
|
gGL.vertex3f(0.0f, 0.0f, 0.0f);
|
|
gGL.vertex3f(0.0f, 0.0f, 0.0f);
|
|
gGL.end();
|
|
gGL.flush();
|
|
|
|
glUseProgramObjectARB(0);
|
|
|
|
LLGLSLShader::sNoFixedFunction = true;
|
|
|
|
// Query whether the shader can or cannot run in hardware
|
|
// http://developer.apple.com/qa/qa2007/qa1502.html
|
|
GLint vertexGPUProcessing, fragmentGPUProcessing;
|
|
CGLContextObj ctx = CGLGetCurrentContext();
|
|
CGLGetParameter(ctx, kCGLCPGPUVertexProcessing, &vertexGPUProcessing);
|
|
CGLGetParameter(ctx, kCGLCPGPUFragmentProcessing, &fragmentGPUProcessing);
|
|
if (!fragmentGPUProcessing || !vertexGPUProcessing)
|
|
{
|
|
LL_WARNS("ShaderLoading") << "GLSL Linker: Running in Software:" << LL_ENDL;
|
|
success = GL_FALSE;
|
|
suppress_errors = FALSE;
|
|
}
|
|
}
|
|
|
|
#else
|
|
std::string log = get_object_log(obj, true);
|
|
LLStringUtil::toLower(log);
|
|
if (log.find("software") != std::string::npos)
|
|
{
|
|
LL_WARNS("ShaderLoading") << "GLSL Linker: Running in Software:" << LL_ENDL;
|
|
success = GL_FALSE;
|
|
suppress_errors = FALSE;
|
|
}
|
|
#endif
|
|
if (!suppress_errors)
|
|
{
|
|
dumpObjectLog(obj, true, !success);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
BOOL LLShaderMgr::validateProgramObject(GLhandleARB obj)
|
|
{
|
|
//check program validity against current GL
|
|
glValidateProgramARB(obj);
|
|
GLint success = GL_TRUE;
|
|
glGetProgramiv(obj, GL_VALIDATE_STATUS, &success);
|
|
if (success == GL_FALSE)
|
|
{
|
|
LL_WARNS("ShaderLoading") << "GLSL program not valid: " << LL_ENDL;
|
|
dumpObjectLog(obj, true);
|
|
}
|
|
else
|
|
{
|
|
dumpObjectLog(obj, true, false);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
//virtual
|
|
void LLShaderMgr::initAttribsAndUniforms()
|
|
{
|
|
//MUST match order of enum in LLVertexBuffer.h
|
|
mReservedAttribs.push_back("position");
|
|
mReservedAttribs.push_back("normal");
|
|
mReservedAttribs.push_back("texcoord0");
|
|
mReservedAttribs.push_back("texcoord1");
|
|
mReservedAttribs.push_back("texcoord2");
|
|
mReservedAttribs.push_back("texcoord3");
|
|
mReservedAttribs.push_back("diffuse_color");
|
|
mReservedAttribs.push_back("emissive");
|
|
mReservedAttribs.push_back("tangent");
|
|
mReservedAttribs.push_back("weight");
|
|
mReservedAttribs.push_back("weight4");
|
|
mReservedAttribs.push_back("clothing");
|
|
mReservedAttribs.push_back("texture_index");
|
|
|
|
//matrix state
|
|
mReservedUniforms.push_back("modelview_matrix");
|
|
mReservedUniforms.push_back("projection_matrix");
|
|
mReservedUniforms.push_back("inv_proj");
|
|
mReservedUniforms.push_back("modelview_projection_matrix");
|
|
mReservedUniforms.push_back("normal_matrix");
|
|
mReservedUniforms.push_back("texture_matrix0");
|
|
mReservedUniforms.push_back("texture_matrix1");
|
|
mReservedUniforms.push_back("texture_matrix2");
|
|
mReservedUniforms.push_back("texture_matrix3");
|
|
mReservedUniforms.push_back("object_plane_s");
|
|
mReservedUniforms.push_back("object_plane_t");
|
|
llassert(mReservedUniforms.size() == LLShaderMgr::OBJECT_PLANE_T+1);
|
|
|
|
mReservedUniforms.push_back("light_position");
|
|
mReservedUniforms.push_back("light_direction");
|
|
mReservedUniforms.push_back("light_attenuation");
|
|
mReservedUniforms.push_back("light_diffuse");
|
|
mReservedUniforms.push_back("light_ambient");
|
|
mReservedUniforms.push_back("light_count");
|
|
mReservedUniforms.push_back("light");
|
|
mReservedUniforms.push_back("light_col");
|
|
mReservedUniforms.push_back("far_z");
|
|
|
|
llassert(mReservedUniforms.size() == LLShaderMgr::MULTI_LIGHT_FAR_Z+1);
|
|
|
|
|
|
mReservedUniforms.push_back("proj_mat");
|
|
mReservedUniforms.push_back("proj_near");
|
|
mReservedUniforms.push_back("proj_p");
|
|
mReservedUniforms.push_back("proj_n");
|
|
mReservedUniforms.push_back("proj_origin");
|
|
mReservedUniforms.push_back("proj_range");
|
|
mReservedUniforms.push_back("proj_ambiance");
|
|
mReservedUniforms.push_back("proj_shadow_idx");
|
|
mReservedUniforms.push_back("shadow_fade");
|
|
mReservedUniforms.push_back("proj_focus");
|
|
mReservedUniforms.push_back("proj_lod");
|
|
mReservedUniforms.push_back("proj_ambient_lod");
|
|
|
|
llassert(mReservedUniforms.size() == LLShaderMgr::PROJECTOR_AMBIENT_LOD+1);
|
|
|
|
mReservedUniforms.push_back("color");
|
|
|
|
mReservedUniforms.push_back("diffuseMap");
|
|
mReservedUniforms.push_back("specularMap");
|
|
mReservedUniforms.push_back("bumpMap");
|
|
mReservedUniforms.push_back("environmentMap");
|
|
mReservedUniforms.push_back("cloude_noise_texture");
|
|
mReservedUniforms.push_back("fullbright");
|
|
mReservedUniforms.push_back("lightnorm");
|
|
mReservedUniforms.push_back("sunlight_color_copy");
|
|
mReservedUniforms.push_back("ambient");
|
|
mReservedUniforms.push_back("blue_horizon");
|
|
mReservedUniforms.push_back("blue_density");
|
|
mReservedUniforms.push_back("haze_horizon");
|
|
mReservedUniforms.push_back("haze_density");
|
|
mReservedUniforms.push_back("cloud_shadow");
|
|
mReservedUniforms.push_back("density_multiplier");
|
|
mReservedUniforms.push_back("distance_multiplier");
|
|
mReservedUniforms.push_back("max_y");
|
|
mReservedUniforms.push_back("glow");
|
|
mReservedUniforms.push_back("cloud_color");
|
|
mReservedUniforms.push_back("cloud_pos_density1");
|
|
mReservedUniforms.push_back("cloud_pos_density2");
|
|
mReservedUniforms.push_back("cloud_scale");
|
|
mReservedUniforms.push_back("gamma");
|
|
mReservedUniforms.push_back("scene_light_strength");
|
|
|
|
llassert(mReservedUniforms.size() == LLShaderMgr::SCENE_LIGHT_STRENGTH+1);
|
|
|
|
mReservedUniforms.push_back("center");
|
|
mReservedUniforms.push_back("size");
|
|
mReservedUniforms.push_back("falloff");
|
|
|
|
mReservedUniforms.push_back("box_center");
|
|
mReservedUniforms.push_back("box_size");
|
|
|
|
|
|
mReservedUniforms.push_back("minLuminance");
|
|
mReservedUniforms.push_back("maxExtractAlpha");
|
|
mReservedUniforms.push_back("lumWeights");
|
|
mReservedUniforms.push_back("warmthWeights");
|
|
mReservedUniforms.push_back("warmthAmount");
|
|
mReservedUniforms.push_back("glowStrength");
|
|
mReservedUniforms.push_back("glowDelta");
|
|
|
|
llassert(mReservedUniforms.size() == LLShaderMgr::GLOW_DELTA+1);
|
|
|
|
|
|
mReservedUniforms.push_back("minimum_alpha");
|
|
mReservedUniforms.push_back("emissive_brightness");
|
|
|
|
mReservedUniforms.push_back("shadow_matrix");
|
|
mReservedUniforms.push_back("env_mat");
|
|
mReservedUniforms.push_back("shadow_clip");
|
|
mReservedUniforms.push_back("sun_wash");
|
|
mReservedUniforms.push_back("shadow_noise");
|
|
mReservedUniforms.push_back("blur_size");
|
|
mReservedUniforms.push_back("ssao_radius");
|
|
mReservedUniforms.push_back("ssao_max_radius");
|
|
mReservedUniforms.push_back("ssao_factor");
|
|
mReservedUniforms.push_back("ssao_factor_inv");
|
|
mReservedUniforms.push_back("ssao_effect");
|
|
mReservedUniforms.push_back("ssao_scale");
|
|
mReservedUniforms.push_back("kern_scale");
|
|
mReservedUniforms.push_back("noise_scale");
|
|
mReservedUniforms.push_back("near_clip");
|
|
mReservedUniforms.push_back("shadow_offset");
|
|
mReservedUniforms.push_back("shadow_bias");
|
|
mReservedUniforms.push_back("spot_shadow_bias");
|
|
mReservedUniforms.push_back("spot_shadow_offset");
|
|
mReservedUniforms.push_back("sun_dir");
|
|
mReservedUniforms.push_back("shadow_res");
|
|
mReservedUniforms.push_back("proj_shadow_res");
|
|
mReservedUniforms.push_back("depth_cutoff");
|
|
mReservedUniforms.push_back("norm_cutoff");
|
|
mReservedUniforms.push_back("shadow_target_width");
|
|
mReservedUniforms.push_back("downsampled_depth_scale");
|
|
|
|
llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_DOWNSAMPLED_DEPTH_SCALE+1);
|
|
|
|
mReservedUniforms.push_back("rcp_screen_res");
|
|
mReservedUniforms.push_back("rcp_frame_opt");
|
|
mReservedUniforms.push_back("rcp_frame_opt2");
|
|
|
|
mReservedUniforms.push_back("focal_distance");
|
|
mReservedUniforms.push_back("blur_constant");
|
|
mReservedUniforms.push_back("tan_pixel_angle");
|
|
mReservedUniforms.push_back("magnification");
|
|
mReservedUniforms.push_back("max_cof");
|
|
mReservedUniforms.push_back("res_scale");
|
|
mReservedUniforms.push_back("dof_width");
|
|
mReservedUniforms.push_back("dof_height");
|
|
|
|
mReservedUniforms.push_back("depthMap");
|
|
mReservedUniforms.push_back("depthMapDownsampled");
|
|
mReservedUniforms.push_back("shadowMap0");
|
|
mReservedUniforms.push_back("shadowMap1");
|
|
mReservedUniforms.push_back("shadowMap2");
|
|
mReservedUniforms.push_back("shadowMap3");
|
|
mReservedUniforms.push_back("shadowMap4");
|
|
mReservedUniforms.push_back("shadowMap5");
|
|
|
|
llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW5+1);
|
|
|
|
mReservedUniforms.push_back("normalMap");
|
|
mReservedUniforms.push_back("positionMap");
|
|
mReservedUniforms.push_back("diffuseRect");
|
|
mReservedUniforms.push_back("specularRect");
|
|
mReservedUniforms.push_back("noiseMap");
|
|
mReservedUniforms.push_back("lightFunc");
|
|
mReservedUniforms.push_back("lightMap");
|
|
mReservedUniforms.push_back("bloomMap");
|
|
mReservedUniforms.push_back("projectionMap");
|
|
mReservedUniforms.push_back("norm_mat");
|
|
|
|
mReservedUniforms.push_back("global_gamma");
|
|
mReservedUniforms.push_back("texture_gamma");
|
|
|
|
mReservedUniforms.push_back("specular_color");
|
|
mReservedUniforms.push_back("env_intensity");
|
|
|
|
mReservedUniforms.push_back("matrixPalette");
|
|
mReservedUniforms.push_back("translationPalette");
|
|
mReservedUniforms.push_back("maxWeight");
|
|
|
|
mReservedUniforms.push_back("screenTex");
|
|
mReservedUniforms.push_back("screenDepth");
|
|
mReservedUniforms.push_back("refTex");
|
|
mReservedUniforms.push_back("eyeVec");
|
|
mReservedUniforms.push_back("time");
|
|
mReservedUniforms.push_back("d1");
|
|
mReservedUniforms.push_back("d2");
|
|
mReservedUniforms.push_back("lightDir");
|
|
mReservedUniforms.push_back("specular");
|
|
mReservedUniforms.push_back("lightExp");
|
|
mReservedUniforms.push_back("waterFogColor");
|
|
mReservedUniforms.push_back("waterFogDensity");
|
|
mReservedUniforms.push_back("waterFogKS");
|
|
mReservedUniforms.push_back("refScale");
|
|
mReservedUniforms.push_back("waterHeight");
|
|
mReservedUniforms.push_back("waterPlane");
|
|
mReservedUniforms.push_back("normScale");
|
|
mReservedUniforms.push_back("fresnelScale");
|
|
mReservedUniforms.push_back("fresnelOffset");
|
|
mReservedUniforms.push_back("blurMultiplier");
|
|
mReservedUniforms.push_back("sunAngle");
|
|
mReservedUniforms.push_back("scaledAngle");
|
|
mReservedUniforms.push_back("sunAngle2");
|
|
|
|
mReservedUniforms.push_back("camPosLocal");
|
|
|
|
mReservedUniforms.push_back("gWindDir");
|
|
mReservedUniforms.push_back("gSinWaveParams");
|
|
mReservedUniforms.push_back("gGravity");
|
|
|
|
mReservedUniforms.push_back("detail_0");
|
|
mReservedUniforms.push_back("detail_1");
|
|
mReservedUniforms.push_back("detail_2");
|
|
mReservedUniforms.push_back("detail_3");
|
|
mReservedUniforms.push_back("alpha_ramp");
|
|
|
|
mReservedUniforms.push_back("origin");
|
|
mReservedUniforms.push_back("display_gamma");
|
|
llassert(mReservedUniforms.size() == END_RESERVED_UNIFORMS);
|
|
|
|
std::set<std::string> dupe_check;
|
|
|
|
for (U32 i = 0; i < mReservedUniforms.size(); ++i)
|
|
{
|
|
if (dupe_check.find(mReservedUniforms[i]) != dupe_check.end())
|
|
{
|
|
LL_ERRS() << "Duplicate reserved uniform name found: " << mReservedUniforms[i] << LL_ENDL;
|
|
}
|
|
dupe_check.insert(mReservedUniforms[i]);
|
|
}
|
|
}
|
|
|