987 lines
27 KiB
C++
987 lines
27 KiB
C++
/**
|
|
* @file llglslshader.cpp
|
|
* @brief GLSL helper functions and state.
|
|
*
|
|
* $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 "llglslshader.h"
|
|
|
|
#include "llshadermgr.h"
|
|
#include "llfile.h"
|
|
#include "llrender.h"
|
|
#include "llcontrol.h"
|
|
#include "llvertexbuffer.h"
|
|
|
|
#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;
|
|
|
|
GLhandleARB LLGLSLShader::sCurBoundShader = 0;
|
|
LLGLSLShader* LLGLSLShader::sCurBoundShaderPtr = NULL;
|
|
S32 LLGLSLShader::sIndexedTextureChannels = 0;
|
|
bool LLGLSLShader::sNoFixedFunction = false;
|
|
bool LLGLSLShader::sProfileEnabled = false;
|
|
std::set<LLGLSLShader*> LLGLSLShader::sInstances;
|
|
U64 LLGLSLShader::sTotalTimeElapsed = 0;
|
|
U32 LLGLSLShader::sTotalTrianglesDrawn = 0;
|
|
U64 LLGLSLShader::sTotalSamplesDrawn = 0;
|
|
U32 LLGLSLShader::sTotalDrawCalls = 0;
|
|
|
|
//UI shader -- declared here so llui_libtest will link properly
|
|
//Singu note: Not using llui_libtest... and LLViewerShaderMgr is a part of newview. So,
|
|
// these are declared in newview/llviewershadermanager.cpp just like every other shader.
|
|
//LLGLSLShader gUIProgram(LLViewerShaderMgr::SHADER_INTERFACE);
|
|
//LLGLSLShader gSolidColorProgram(LLViewerShaderMgr::SHADER_INTERFACE);
|
|
|
|
BOOL shouldChange(const LLVector4& v1, const LLVector4& v2)
|
|
{
|
|
return v1 != v2;
|
|
}
|
|
|
|
LLShaderFeatures::LLShaderFeatures()
|
|
: atmosphericHelpers(false)
|
|
, calculatesLighting(false)
|
|
, calculatesAtmospherics(false)
|
|
, hasLighting(false)
|
|
, isAlphaLighting(false)
|
|
, isShiny(false)
|
|
, isFullbright(false)
|
|
, isSpecular(false)
|
|
, hasWaterFog(false)
|
|
, hasTransport(false)
|
|
, hasSkinning(false)
|
|
, hasObjectSkinning(false)
|
|
, hasAtmospherics(false)
|
|
, hasGamma(false)
|
|
, mIndexedTextureChannels(0)
|
|
, disableTextureIndex(false)
|
|
, hasAlphaMask(false)
|
|
, attachNothing(false)
|
|
{
|
|
}
|
|
|
|
//===============================
|
|
// LLGLSL Shader implementation
|
|
//===============================
|
|
|
|
//static
|
|
void LLGLSLShader::initProfile()
|
|
{
|
|
sProfileEnabled = true;
|
|
sTotalTimeElapsed = 0;
|
|
sTotalTrianglesDrawn = 0;
|
|
sTotalSamplesDrawn = 0;
|
|
sTotalDrawCalls = 0;
|
|
|
|
for (std::set<LLGLSLShader*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
|
|
{
|
|
(*iter)->clearStats();
|
|
}
|
|
}
|
|
|
|
|
|
struct LLGLSLShaderCompareTimeElapsed
|
|
{
|
|
bool operator()(const LLGLSLShader* const& lhs, const LLGLSLShader* const& rhs)
|
|
{
|
|
return lhs->mTimeElapsed < rhs->mTimeElapsed;
|
|
}
|
|
};
|
|
|
|
//static
|
|
void LLGLSLShader::finishProfile(bool emit_report)
|
|
{
|
|
sProfileEnabled = false;
|
|
|
|
if (emit_report)
|
|
{
|
|
std::vector<LLGLSLShader*> sorted;
|
|
|
|
for (std::set<LLGLSLShader*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
|
|
{
|
|
sorted.push_back(*iter);
|
|
}
|
|
|
|
std::sort(sorted.begin(), sorted.end(), LLGLSLShaderCompareTimeElapsed());
|
|
|
|
for (std::vector<LLGLSLShader*>::iterator iter = sorted.begin(); iter != sorted.end(); ++iter)
|
|
{
|
|
(*iter)->dumpStats();
|
|
}
|
|
|
|
LL_INFOS() << "-----------------------------------" << LL_ENDL;
|
|
LL_INFOS() << "Total rendering time: " << llformat("%.4f ms", sTotalTimeElapsed/1000000.f) << LL_ENDL;
|
|
LL_INFOS() << "Total samples drawn: " << llformat("%.4f million", sTotalSamplesDrawn/1000000.f) << LL_ENDL;
|
|
LL_INFOS() << "Total triangles drawn: " << llformat("%.3f million", sTotalTrianglesDrawn/1000000.f) << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
void LLGLSLShader::clearStats()
|
|
{
|
|
mTrianglesDrawn = 0;
|
|
mTimeElapsed = 0;
|
|
mSamplesDrawn = 0;
|
|
mDrawCalls = 0;
|
|
mTextureStateFetched = false;
|
|
mTextureMagFilter.clear();
|
|
mTextureMinFilter.clear();
|
|
}
|
|
|
|
void LLGLSLShader::dumpStats()
|
|
{
|
|
if (mDrawCalls > 0)
|
|
{
|
|
LL_INFOS() << "=============================================" << LL_ENDL;
|
|
LL_INFOS() << mName << LL_ENDL;
|
|
for (U32 i = 0; i < mShaderFiles.size(); ++i)
|
|
{
|
|
LL_INFOS() << mShaderFiles[i].first << LL_ENDL;
|
|
}
|
|
for (U32 i = 0; i < mTexture.size(); ++i)
|
|
{
|
|
GLint idx = mTexture[i];
|
|
|
|
if (idx >= 0)
|
|
{
|
|
GLint uniform_idx = getUniformLocation(i);
|
|
LL_INFOS() << mUniformNameMap[uniform_idx] << " - " << std::hex << mTextureMagFilter[i] << "/" << mTextureMinFilter[i] << std::dec << LL_ENDL;
|
|
}
|
|
}
|
|
LL_INFOS() << "=============================================" << LL_ENDL;
|
|
|
|
F32 ms = mTimeElapsed/1000000.f;
|
|
F32 seconds = ms/1000.f;
|
|
|
|
F32 pct_tris = (F32) mTrianglesDrawn/(F32)sTotalTrianglesDrawn*100.f;
|
|
F32 tris_sec = (F32) (mTrianglesDrawn/1000000.0);
|
|
tris_sec /= seconds;
|
|
|
|
F32 pct_samples = (F32) ((F64)mSamplesDrawn/(F64)sTotalSamplesDrawn)*100.f;
|
|
F32 samples_sec = (F32) mSamplesDrawn/1000000000.0;
|
|
samples_sec /= seconds;
|
|
|
|
F32 pct_calls = (F32) mDrawCalls/(F32)sTotalDrawCalls*100.f;
|
|
U32 avg_batch = mTrianglesDrawn/mDrawCalls;
|
|
|
|
LL_INFOS() << "Triangles Drawn: " << mTrianglesDrawn << " " << llformat("(%.2f pct of total, %.3f million/sec)", pct_tris, tris_sec ) << LL_ENDL;
|
|
LL_INFOS() << "Draw Calls: " << mDrawCalls << " " << llformat("(%.2f pct of total, avg %d tris/call)", pct_calls, avg_batch) << LL_ENDL;
|
|
LL_INFOS() << "SamplesDrawn: " << mSamplesDrawn << " " << llformat("(%.2f pct of total, %.3f billion/sec)", pct_samples, samples_sec) << LL_ENDL;
|
|
LL_INFOS() << "Time Elapsed: " << mTimeElapsed << " " << llformat("(%.2f pct of total, %.5f ms)\n", (F32) ((F64)mTimeElapsed/(F64)sTotalTimeElapsed)*100.f, ms) << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLGLSLShader::startProfile()
|
|
{
|
|
if (sProfileEnabled && sCurBoundShaderPtr)
|
|
{
|
|
sCurBoundShaderPtr->placeProfileQuery();
|
|
}
|
|
|
|
}
|
|
|
|
//static
|
|
void LLGLSLShader::stopProfile(U32 count, U32 mode)
|
|
{
|
|
if (sProfileEnabled && sCurBoundShaderPtr)
|
|
{
|
|
sCurBoundShaderPtr->readProfileQuery(count, mode);
|
|
}
|
|
}
|
|
|
|
void LLGLSLShader::placeProfileQuery()
|
|
{
|
|
#if !LL_DARWIN
|
|
if (mTimerQuery == 0)
|
|
{
|
|
glGenQueriesARB(1, &mSamplesQuery);
|
|
glGenQueriesARB(1, &mTimerQuery);
|
|
}
|
|
|
|
if (!mTextureStateFetched)
|
|
{
|
|
mTextureStateFetched = true;
|
|
mTextureMagFilter.resize(mTexture.size());
|
|
mTextureMinFilter.resize(mTexture.size());
|
|
|
|
U32 cur_active = gGL.getCurrentTexUnitIndex();
|
|
|
|
for (U32 i = 0; i < mTexture.size(); ++i)
|
|
{
|
|
GLint idx = mTexture[i];
|
|
|
|
if (idx >= 0)
|
|
{
|
|
gGL.getTexUnit(idx)->activate();
|
|
|
|
U32 mag = 0xFFFFFFFF;
|
|
U32 min = 0xFFFFFFFF;
|
|
|
|
U32 type = LLTexUnit::getInternalType(gGL.getTexUnit(idx)->getCurrType());
|
|
|
|
glGetTexParameteriv(type, GL_TEXTURE_MAG_FILTER, (GLint*) &mag);
|
|
glGetTexParameteriv(type, GL_TEXTURE_MIN_FILTER, (GLint*) &min);
|
|
|
|
mTextureMagFilter[i] = mag;
|
|
mTextureMinFilter[i] = min;
|
|
}
|
|
}
|
|
|
|
gGL.getTexUnit(cur_active)->activate();
|
|
}
|
|
|
|
|
|
glBeginQueryARB(GL_SAMPLES_PASSED, mSamplesQuery);
|
|
glBeginQueryARB(GL_TIME_ELAPSED, mTimerQuery);
|
|
#endif
|
|
}
|
|
|
|
void LLGLSLShader::readProfileQuery(U32 count, U32 mode)
|
|
{
|
|
#if !LL_DARWIN
|
|
glEndQueryARB(GL_TIME_ELAPSED);
|
|
glEndQueryARB(GL_SAMPLES_PASSED);
|
|
|
|
GLuint64 time_elapsed = 0;
|
|
glGetQueryObjectui64vEXT(mTimerQuery, GL_QUERY_RESULT, &time_elapsed);
|
|
|
|
GLuint64 samples_passed = 0;
|
|
glGetQueryObjectui64vEXT(mSamplesQuery, GL_QUERY_RESULT, &samples_passed);
|
|
|
|
sTotalTimeElapsed += time_elapsed;
|
|
mTimeElapsed += time_elapsed;
|
|
|
|
sTotalSamplesDrawn += samples_passed;
|
|
mSamplesDrawn += samples_passed;
|
|
|
|
U32 tri_count = 0;
|
|
switch (mode)
|
|
{
|
|
case LLRender::TRIANGLES: tri_count = count/3; break;
|
|
case LLRender::TRIANGLE_FAN: tri_count = count-2; break;
|
|
case LLRender::TRIANGLE_STRIP: tri_count = count-2; break;
|
|
default: tri_count = count; break; //points lines etc just use primitive count
|
|
}
|
|
|
|
mTrianglesDrawn += tri_count;
|
|
sTotalTrianglesDrawn += tri_count;
|
|
|
|
sTotalDrawCalls++;
|
|
mDrawCalls++;
|
|
#endif
|
|
}
|
|
LLGLSLShader::LLGLSLShader(S32 shader_class)
|
|
: mProgramObject(0),
|
|
mShaderClass(shader_class),
|
|
mAttributeMask(0),
|
|
mTotalUniformSize(0),
|
|
mActiveTextureChannels(0),
|
|
mShaderLevel(0),
|
|
mShaderGroup(SG_DEFAULT),
|
|
mUniformsDirty(FALSE),
|
|
mTimerQuery(0),
|
|
mLightHash(0),
|
|
mSamplesQuery(0)
|
|
|
|
{
|
|
LLShaderMgr::getGlobalShaderList().push_back(this);
|
|
}
|
|
|
|
LLGLSLShader::~LLGLSLShader()
|
|
{
|
|
}
|
|
|
|
void LLGLSLShader::unload()
|
|
{
|
|
sInstances.erase(this);
|
|
|
|
stop_glerror();
|
|
mAttribute.clear();
|
|
mTexture.clear();
|
|
mUniform.clear();
|
|
mShaderFiles.clear();
|
|
mDefines.clear();
|
|
|
|
if (mProgramObject)
|
|
{
|
|
//Don't do this! Attached objects are already flagged for deletion.
|
|
//They will be deleted when no programs have them attached. (deleting a program auto-detaches them!)
|
|
/*GLhandleARB obj[1024];
|
|
GLsizei count;
|
|
|
|
glGetAttachedObjectsARB(mProgramObject, 1024, &count, obj);
|
|
for (GLsizei i = 0; i < count; i++)
|
|
{
|
|
glDeleteObjectARB(obj[i]);
|
|
}*/
|
|
if(mProgramObject)
|
|
glDeleteProgram(mProgramObject);
|
|
mProgramObject = 0;
|
|
}
|
|
|
|
if (mTimerQuery)
|
|
{
|
|
glDeleteQueriesARB(1, &mTimerQuery);
|
|
mTimerQuery = 0;
|
|
}
|
|
|
|
if (mSamplesQuery)
|
|
{
|
|
glDeleteQueriesARB(1, &mSamplesQuery);
|
|
mSamplesQuery = 0;
|
|
}
|
|
|
|
//hack to make apple not complain
|
|
glGetError();
|
|
|
|
stop_glerror();
|
|
}
|
|
|
|
BOOL LLGLSLShader::createShader(std::vector<LLStaticHashedString> * attributes,
|
|
std::vector<LLStaticHashedString> * uniforms,
|
|
U32 varying_count,
|
|
const char** varyings)
|
|
{
|
|
sInstances.insert(this);
|
|
|
|
//reloading, reset matrix hash values
|
|
for (U32 i = 0; i < LLRender::NUM_MATRIX_MODES; ++i)
|
|
{
|
|
mMatHash[i] = 0xFFFFFFFF;
|
|
}
|
|
mLightHash = 0xFFFFFFFF;
|
|
|
|
llassert_always(!mShaderFiles.empty());
|
|
BOOL success = TRUE;
|
|
|
|
if(mProgramObject) //purge the old program
|
|
glDeleteProgram(mProgramObject);
|
|
// Create program
|
|
mProgramObject = glCreateProgramObjectARB();
|
|
|
|
#if LL_DARWIN
|
|
// work-around missing mix(vec3,vec3,bvec3)
|
|
mDefines["OLD_SELECT"] = "1";
|
|
#endif
|
|
|
|
//compile new source
|
|
vector< pair<string,GLenum> >::iterator fileIter = mShaderFiles.begin();
|
|
for ( ; fileIter != mShaderFiles.end(); fileIter++ )
|
|
{
|
|
GLhandleARB shaderhandle = LLShaderMgr::instance()->loadShaderFile((*fileIter).first, mShaderLevel, (*fileIter).second, &mDefines, mFeatures.mIndexedTextureChannels);
|
|
LL_DEBUGS("ShaderLoading") << "SHADER FILE: " << (*fileIter).first << " mShaderLevel=" << mShaderLevel << LL_ENDL;
|
|
if (shaderhandle > 0)
|
|
{
|
|
attachObject(shaderhandle);
|
|
}
|
|
else
|
|
{
|
|
success = FALSE;
|
|
}
|
|
}
|
|
|
|
// Attach existing objects
|
|
if (!LLShaderMgr::instance()->attachClassSharedShaders(*this, mShaderClass) || !LLShaderMgr::instance()->attachShaderFeatures(this))
|
|
{
|
|
if(mProgramObject)
|
|
glDeleteProgram(mProgramObject);
|
|
mProgramObject = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
static const LLCachedControl<bool> no_texture_indexing("ShyotlUseLegacyTextureBatching",false);
|
|
if ((gGLManager.mGLSLVersionMajor < 2 && gGLManager.mGLSLVersionMinor < 3) || no_texture_indexing)
|
|
{ //attachShaderFeatures may have set the number of indexed texture channels, so set to 1 again
|
|
mFeatures.mIndexedTextureChannels = llmin(mFeatures.mIndexedTextureChannels, 1);
|
|
}
|
|
|
|
#ifdef GL_INTERLEAVED_ATTRIBS
|
|
if (varying_count > 0 && varyings)
|
|
{
|
|
glTransformFeedbackVaryings(mProgramObject, varying_count, varyings, GL_INTERLEAVED_ATTRIBS);
|
|
}
|
|
#endif
|
|
|
|
// Map attributes and uniforms
|
|
if (success)
|
|
{
|
|
success = mapAttributes(attributes);
|
|
}
|
|
if (success)
|
|
{
|
|
success = mapUniforms(uniforms);
|
|
}
|
|
if( !success )
|
|
{
|
|
if(mProgramObject)
|
|
glDeleteProgram(mProgramObject);
|
|
mProgramObject = 0;
|
|
|
|
LL_WARNS("ShaderLoading") << "Failed to link shader: " << mName << LL_ENDL;
|
|
|
|
// Try again using a lower shader level;
|
|
if (mShaderLevel > 0)
|
|
{
|
|
LL_WARNS("ShaderLoading") << "Failed to link using shader level " << mShaderLevel << " trying again using shader level " << (mShaderLevel - 1) << LL_ENDL;
|
|
mShaderLevel--;
|
|
return createShader(attributes,uniforms);
|
|
}
|
|
}
|
|
|
|
if (LLShaderMgr::instance()->mProgramObjects.find(mName) == LLShaderMgr::instance()->mProgramObjects.end())
|
|
{
|
|
LLShaderMgr::instance()->mProgramObjects.emplace(mName, mProgramObject);
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("ShaderLoading") << "Attempting to create shader program with duplicate name: " << mName << LL_ENDL;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
BOOL LLGLSLShader::attachObject(std::string object)
|
|
{
|
|
std::multimap<std::string, LLShaderMgr::CachedObjectInfo>::iterator it = LLShaderMgr::instance()->mShaderObjects.begin();
|
|
for(; it!=LLShaderMgr::instance()->mShaderObjects.end(); it++)
|
|
{
|
|
if((*it).first == object)
|
|
{
|
|
glAttachObjectARB(mProgramObject, (*it).second.mHandle);
|
|
stop_glerror();
|
|
return TRUE;
|
|
}
|
|
}
|
|
{
|
|
LL_WARNS("ShaderLoading") << "Attempting to attach shader object that hasn't been compiled: " << object << LL_ENDL;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void LLGLSLShader::attachObject(GLhandleARB object)
|
|
{
|
|
if (object != 0)
|
|
{
|
|
std::multimap<std::string, LLShaderMgr::CachedObjectInfo>::iterator it = LLShaderMgr::instance()->mShaderObjects.begin();
|
|
for(; it!=LLShaderMgr::instance()->mShaderObjects.end(); it++)
|
|
{
|
|
if((*it).second.mHandle == object)
|
|
{
|
|
LL_INFOS("ShaderLoading") << "Attached: " << (*it).first << LL_ENDL;
|
|
break;
|
|
}
|
|
}
|
|
if(it == LLShaderMgr::instance()->mShaderObjects.end())
|
|
{
|
|
LL_WARNS("ShaderLoading") << "Attached unknown shader!" << LL_ENDL;
|
|
}
|
|
|
|
stop_glerror();
|
|
glAttachObjectARB(mProgramObject, object);
|
|
stop_glerror();
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("ShaderLoading") << "Attempting to attach non existing shader object. " << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
void LLGLSLShader::attachObjects(GLhandleARB* objects, S32 count)
|
|
{
|
|
for (S32 i = 0; i < count; i++)
|
|
{
|
|
attachObject(objects[i]);
|
|
}
|
|
}
|
|
|
|
BOOL LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString> * attributes)
|
|
{
|
|
//before linking, make sure reserved attributes always have consistent locations
|
|
for (U32 i = 0; i < LLShaderMgr::instance()->mReservedAttribs.size(); i++)
|
|
{
|
|
const char* name = LLShaderMgr::instance()->mReservedAttribs[i].c_str();
|
|
glBindAttribLocationARB(mProgramObject, i, (const GLcharARB *) name);
|
|
}
|
|
|
|
//link the program
|
|
BOOL res = link();
|
|
|
|
mAttribute.clear();
|
|
U32 numAttributes = (attributes == NULL) ? 0 : attributes->size();
|
|
mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, -1);
|
|
|
|
if (res)
|
|
{ //read back channel locations
|
|
|
|
mAttributeMask = 0;
|
|
|
|
//read back reserved channels first
|
|
for (U32 i = 0; i < (S32) LLShaderMgr::instance()->mReservedAttribs.size(); i++)
|
|
{
|
|
const char* name = LLShaderMgr::instance()->mReservedAttribs[i].c_str();
|
|
S32 index = glGetAttribLocationARB(mProgramObject, (const GLcharARB *)name);
|
|
if (index != -1)
|
|
{
|
|
mAttribute[i] = index;
|
|
mAttributeMask |= 1 << i;
|
|
LL_DEBUGS("ShaderLoading") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
|
|
}
|
|
}
|
|
if (attributes != NULL)
|
|
{
|
|
for (U32 i = 0; i < numAttributes; i++)
|
|
{
|
|
const char* name = (*attributes)[i].String().c_str();
|
|
S32 index = glGetAttribLocationARB(mProgramObject, name);
|
|
if (index != -1)
|
|
{
|
|
mAttribute[LLShaderMgr::instance()->mReservedAttribs.size() + i] = index;
|
|
LL_DEBUGS("ShaderLoading") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void LLGLSLShader::mapUniform(const gl_uniform_data_t& gl_uniform, const vector<LLStaticHashedString> * uniforms)
|
|
{
|
|
GLint size = gl_uniform.size;
|
|
char* name = (char*)gl_uniform.name.c_str(); //blegh
|
|
#if !LL_DARWIN
|
|
if (size > 0)
|
|
{
|
|
switch(gl_uniform.type)
|
|
{
|
|
case GL_FLOAT_VEC2: size *= 2; break;
|
|
case GL_FLOAT_VEC3: size *= 3; break;
|
|
case GL_FLOAT_VEC4: size *= 4; break;
|
|
case GL_DOUBLE: size *= 2; break;
|
|
case GL_DOUBLE_VEC2: size *= 2; break;
|
|
case GL_DOUBLE_VEC3: size *= 6; break;
|
|
case GL_DOUBLE_VEC4: size *= 8; break;
|
|
case GL_INT_VEC2: size *= 2; break;
|
|
case GL_INT_VEC3: size *= 3; break;
|
|
case GL_INT_VEC4: size *= 4; break;
|
|
case GL_UNSIGNED_INT_VEC2: size *= 2; break;
|
|
case GL_UNSIGNED_INT_VEC3: size *= 3; break;
|
|
case GL_UNSIGNED_INT_VEC4: size *= 4; break;
|
|
case GL_BOOL_VEC2: size *= 2; break;
|
|
case GL_BOOL_VEC3: size *= 3; break;
|
|
case GL_BOOL_VEC4: size *= 4; break;
|
|
case GL_FLOAT_MAT2: size *= 4; break;
|
|
case GL_FLOAT_MAT3: size *= 9; break;
|
|
case GL_FLOAT_MAT4: size *= 16; break;
|
|
case GL_FLOAT_MAT2x3: size *= 6; break;
|
|
case GL_FLOAT_MAT2x4: size *= 8; break;
|
|
case GL_FLOAT_MAT3x2: size *= 6; break;
|
|
case GL_FLOAT_MAT3x4: size *= 12; break;
|
|
case GL_FLOAT_MAT4x2: size *= 8; break;
|
|
case GL_FLOAT_MAT4x3: size *= 12; break;
|
|
case GL_DOUBLE_MAT2: size *= 8; break;
|
|
case GL_DOUBLE_MAT3: size *= 18; break;
|
|
case GL_DOUBLE_MAT4: size *= 32; break;
|
|
case GL_DOUBLE_MAT2x3: size *= 12; break;
|
|
case GL_DOUBLE_MAT2x4: size *= 16; break;
|
|
case GL_DOUBLE_MAT3x2: size *= 12; break;
|
|
case GL_DOUBLE_MAT3x4: size *= 24; break;
|
|
case GL_DOUBLE_MAT4x2: size *= 16; break;
|
|
case GL_DOUBLE_MAT4x3: size *= 24; break;
|
|
}
|
|
mTotalUniformSize += size;
|
|
}
|
|
#endif
|
|
|
|
S32 location = glGetUniformLocationARB(mProgramObject, name);
|
|
if (location != -1)
|
|
{
|
|
//chop off "[0]" so we can always access the first element
|
|
//of an array by the array name
|
|
char* is_array = strstr(name, "[0]");
|
|
if (is_array)
|
|
{
|
|
is_array[0] = 0;
|
|
}
|
|
|
|
LLStaticHashedString hashedName(name);
|
|
mUniformNameMap[location] = name;
|
|
mUniformMap[hashedName] = location;
|
|
|
|
LL_DEBUGS("ShaderLoading") << "Uniform " << name << " is at location " << location << LL_ENDL;
|
|
|
|
// Indexed textures are referenced by hardcoded tex unit index. This is where that mapping happens.
|
|
if (gl_uniform.texunit_priority < (U32)mFeatures.mIndexedTextureChannels)
|
|
{
|
|
// mUniform and mTexture are irrelivant for indexed textures, since there's no enum to look them up through.
|
|
// Thus, only call mapUniformTextureChannel to create the texunit => uniform location mapping in opengl.
|
|
mapUniformTextureChannel(location, gl_uniform.type);
|
|
return;
|
|
}
|
|
|
|
//find the index of this uniform
|
|
for (U32 i = 0; i < LLShaderMgr::instance()->mReservedUniforms.size(); i++)
|
|
{
|
|
if ( (mUniform[i] == -1)
|
|
&& (LLShaderMgr::instance()->mReservedUniforms[i] == name))
|
|
{
|
|
//found it
|
|
mUniform[i] = location;
|
|
mTexture[i] = mapUniformTextureChannel(location, gl_uniform.type);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (uniforms != NULL)
|
|
{
|
|
U32 j = LLShaderMgr::instance()->mReservedUniforms.size();
|
|
for (U32 i = 0; i < uniforms->size(); i++, j++)
|
|
{
|
|
if ( (mUniform[j] == -1)
|
|
&& ((*uniforms)[i].String() == name))
|
|
{
|
|
//found it
|
|
mUniform[j] = location;
|
|
mTexture[j] = mapUniformTextureChannel(location, gl_uniform.type);;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLGLSLShader::addPermutation(std::string name, std::string value)
|
|
{
|
|
mDefines[name] = value;
|
|
}
|
|
|
|
void LLGLSLShader::removePermutation(std::string name)
|
|
{
|
|
mDefines[name].erase();
|
|
}
|
|
|
|
GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type)
|
|
{
|
|
if (type >= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB /*||
|
|
type == GL_SAMPLER_2D_MULTISAMPLE*/)
|
|
{ //this here is a texture
|
|
gGL.syncShaders();
|
|
glUniform1iARB(location, mActiveTextureChannels);
|
|
LL_DEBUGS("ShaderLoading") << "Assigned to texture channel " << mActiveTextureChannels << LL_ENDL;
|
|
return mActiveTextureChannels++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
BOOL LLGLSLShader::mapUniforms(const vector<LLStaticHashedString> * uniforms)
|
|
{
|
|
BOOL res = TRUE;
|
|
|
|
auto& reservedUniforms = LLShaderMgr::instance()->mReservedUniforms;
|
|
|
|
mTotalUniformSize = 0;
|
|
mActiveTextureChannels = 0;
|
|
mUniform.clear();
|
|
mUniformMap.clear();
|
|
mUniformNameMap.clear();
|
|
mTexture.clear();
|
|
mValueVec4.clear();
|
|
mValueMat3.clear();
|
|
mValueMat4.clear();
|
|
//initialize arrays
|
|
U32 numUniforms = (uniforms == NULL) ? 0 : uniforms->size();
|
|
mUniform.resize(numUniforms + reservedUniforms.size(), -1);
|
|
mTexture.resize(numUniforms + reservedUniforms.size(), -1);
|
|
|
|
bind();
|
|
|
|
//get the number of active uniforms
|
|
GLint activeCount;
|
|
glGetProgramiv(mProgramObject, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &activeCount);
|
|
|
|
std::vector< gl_uniform_data_t > gl_uniforms;
|
|
|
|
bool has_diffuse = false;
|
|
U32 max_index = mFeatures.mIndexedTextureChannels;
|
|
// Gather active uniforms.
|
|
for (S32 i = 0; i < activeCount; i++)
|
|
{
|
|
// Fetch name and size from opengl
|
|
char name[1024];
|
|
gl_uniform_data_t gl_uniform;
|
|
GLsizei length;
|
|
glGetActiveUniformARB(mProgramObject, i, 1024, &length, &gl_uniform.size, &gl_uniform.type, (GLcharARB *)name);
|
|
if (length && name[length - 1] == '\0')
|
|
{
|
|
--length; // Some drivers can't be trusted...
|
|
}
|
|
if (gl_uniform.size < 0 || length <= 0)
|
|
continue;
|
|
gl_uniform.name = std::string(name, length);
|
|
|
|
// Track if diffuseMap uniform was detected. If so, flag as such to assert indexed textures aren't also used in this shader.
|
|
has_diffuse |= gl_uniform.name == "diffuseMap";
|
|
|
|
// Use mReservedUniforms to calculate texunit ordering. Reserve priority [0,max_index) for indexed textures if applicable.
|
|
auto it = std::find(reservedUniforms.begin(), reservedUniforms.end(), gl_uniform.name);
|
|
gl_uniform.texunit_priority = it != reservedUniforms.end() ? max_index + std::distance(reservedUniforms.begin(), it) : UINT_MAX;
|
|
|
|
// Indexed texture uniforms must ALWAYS have highest texunit priority. Ensures [texunit[0],texunit[max_index]) map to [tex[0],tex[max_index]) uniforms.
|
|
// Note that this logic will break if a tex# index is skipped over in the shader.
|
|
if (gl_uniform.texunit_priority == UINT_MAX)
|
|
{
|
|
S32 idx;
|
|
if (sscanf(gl_uniform.name.c_str(), "tex%d", &idx))
|
|
{
|
|
gl_uniform.texunit_priority = idx;
|
|
}
|
|
}
|
|
|
|
gl_uniforms.push_back(gl_uniform);
|
|
}
|
|
|
|
// Sort uniforms by texunit_priority
|
|
std::sort(gl_uniforms.begin(), gl_uniforms.end(), [](gl_uniform_data_t& lhs, gl_uniform_data_t& rhs)
|
|
{
|
|
return lhs.texunit_priority < rhs.texunit_priority;
|
|
});
|
|
|
|
// Sanity check
|
|
if (gl_uniforms.size() && gl_uniforms[0].name == "tex0")
|
|
{
|
|
llassert_always_msg(!has_diffuse, "Indexed textures and diffuseMap are incompatible!");
|
|
}
|
|
|
|
for (auto& gl_uniform : gl_uniforms)
|
|
{
|
|
mapUniform(gl_uniform, uniforms);
|
|
}
|
|
|
|
unbind();
|
|
|
|
LL_DEBUGS("ShaderLoading") << "Total Uniform Size: " << mTotalUniformSize << LL_ENDL;
|
|
return res;
|
|
}
|
|
|
|
BOOL LLGLSLShader::link(BOOL suppress_errors)
|
|
{
|
|
return LLShaderMgr::instance()->linkProgramObject(mProgramObject, suppress_errors);
|
|
}
|
|
|
|
void LLGLSLShader::bind()
|
|
{
|
|
gGL.flush();
|
|
if (gGLManager.mHasShaderObjects)
|
|
{
|
|
LLVertexBuffer::unbind();
|
|
gGL.setShader(mProgramObject);
|
|
sCurBoundShader = mProgramObject;
|
|
sCurBoundShaderPtr = this;
|
|
if (mUniformsDirty)
|
|
{
|
|
LLShaderMgr::instance()->updateShaderUniforms(this);
|
|
mUniformsDirty = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLGLSLShader::unbind()
|
|
{
|
|
gGL.flush();
|
|
if (gGLManager.mHasShaderObjects)
|
|
{
|
|
stop_glerror();
|
|
if (gGLManager.mIsNVIDIA)
|
|
{
|
|
for (U32 i = 0; i < mAttribute.size(); ++i)
|
|
{
|
|
vertexAttrib4f(i, 0,0,0,1);
|
|
stop_glerror();
|
|
}
|
|
}
|
|
LLVertexBuffer::unbind();
|
|
gGL.setShader(0);
|
|
sCurBoundShader = 0;
|
|
sCurBoundShaderPtr = NULL;
|
|
stop_glerror();
|
|
}
|
|
}
|
|
|
|
void LLGLSLShader::bindNoShader(void)
|
|
{
|
|
LLVertexBuffer::unbind();
|
|
if (gGLManager.mHasShaderObjects)
|
|
{
|
|
gGL.setShader(0);
|
|
sCurBoundShader = 0;
|
|
sCurBoundShaderPtr = NULL;
|
|
}
|
|
}
|
|
|
|
S32 LLGLSLShader::bindTexture(const std::string &uniform, LLTexture *texture, LLTexUnit::eTextureType mode)
|
|
{
|
|
S32 channel = 0;
|
|
channel = getUniformLocation(uniform);
|
|
|
|
return bindTexture(channel, texture, mode);
|
|
}
|
|
|
|
S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture *texture, LLTexUnit::eTextureType mode)
|
|
{
|
|
if (uniform < 0 || uniform >= (S32)mTexture.size())
|
|
{
|
|
UNIFORM_ERRS << "Uniform out of range: " << uniform << LL_ENDL;
|
|
return -1;
|
|
}
|
|
|
|
uniform = mTexture[uniform];
|
|
|
|
if (uniform > -1)
|
|
{
|
|
gGL.getTexUnit(uniform)->bind(texture, mode);
|
|
}
|
|
|
|
return uniform;
|
|
}
|
|
|
|
S32 LLGLSLShader::unbindTexture(const std::string &uniform, LLTexUnit::eTextureType mode)
|
|
{
|
|
S32 channel = 0;
|
|
channel = getUniformLocation(uniform);
|
|
|
|
return unbindTexture(channel);
|
|
}
|
|
|
|
S32 LLGLSLShader::unbindTexture(S32 uniform, LLTexUnit::eTextureType mode)
|
|
{
|
|
if (uniform < 0 || uniform >= (S32)mTexture.size())
|
|
{
|
|
UNIFORM_ERRS << "Uniform out of range: " << uniform << LL_ENDL;
|
|
return -1;
|
|
}
|
|
|
|
uniform = mTexture[uniform];
|
|
|
|
if (uniform > -1)
|
|
{
|
|
gGL.getTexUnit(uniform)->unbind(mode);
|
|
}
|
|
|
|
return uniform;
|
|
}
|
|
|
|
S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode)
|
|
{
|
|
if (uniform < 0 || uniform >= (S32)mTexture.size())
|
|
{
|
|
UNIFORM_ERRS << "Uniform out of range: " << uniform << LL_ENDL;
|
|
return -1;
|
|
}
|
|
S32 index = mTexture[uniform];
|
|
if (index != -1)
|
|
{
|
|
gGL.getTexUnit(index)->activate();
|
|
gGL.getTexUnit(index)->enable(mode);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode)
|
|
{
|
|
if (uniform < 0 || uniform >= (S32)mTexture.size())
|
|
{
|
|
UNIFORM_ERRS << "Uniform out of range: " << uniform << LL_ENDL;
|
|
return -1;
|
|
}
|
|
S32 index = mTexture[uniform];
|
|
if (index != -1 && gGL.getTexUnit(index)->getCurrType() != LLTexUnit::TT_NONE)
|
|
{
|
|
if (gDebugGL && gGL.getTexUnit(index)->getCurrType() != mode)
|
|
{
|
|
if (gDebugSession)
|
|
{
|
|
gFailLog << "Texture channel " << index << " texture type corrupted." << std::endl;
|
|
ll_fail("LLGLSLShader::disableTexture failed");
|
|
}
|
|
else
|
|
{
|
|
LL_ERRS() << "Texture channel " << index << " texture type corrupted." << LL_ENDL;
|
|
}
|
|
}
|
|
gGL.getTexUnit(index)->disable();
|
|
}
|
|
return index;
|
|
}
|
|
|
|
GLint LLGLSLShader::getUniformLocation(U32 index)
|
|
{
|
|
GLint ret = -1;
|
|
if (mProgramObject > 0)
|
|
{
|
|
llassert(index < mUniform.size());
|
|
return mUniform[index];
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
GLint LLGLSLShader::getAttribLocation(U32 attrib)
|
|
{
|
|
if (attrib < mAttribute.size())
|
|
{
|
|
return mAttribute[attrib];
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void LLGLSLShader::vertexAttrib4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
|
|
{
|
|
if (mAttribute[index] > 0)
|
|
{
|
|
glVertexAttrib4fARB(mAttribute[index], x, y, z, w);
|
|
}
|
|
}
|
|
|
|
void LLGLSLShader::setMinimumAlpha(F32 minimum)
|
|
{
|
|
uniform1f(LLShaderMgr::MINIMUM_ALPHA, minimum);
|
|
}
|