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.
956 lines
25 KiB
C++
956 lines
25 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);
|
|
}
|
|
}
|
|
else if (mFeatures.mIndexedTextureChannels > 0)
|
|
{ //override texture channels for indexed texture rendering
|
|
bind();
|
|
S32 channel_count = mFeatures.mIndexedTextureChannels;
|
|
|
|
for (S32 i = 0; i < channel_count; i++)
|
|
{
|
|
LLStaticHashedString uniName(llformat("tex%d", i));
|
|
uniform1i(uniName, i);
|
|
}
|
|
|
|
S32 cur_tex = channel_count; //adjust any texture channels that might have been overwritten
|
|
for (U32 i = 0; i < mTexture.size(); i++)
|
|
{
|
|
if (mTexture[i] > -1 && mTexture[i] < channel_count)
|
|
{
|
|
llassert(cur_tex < gGLManager.mNumTextureImageUnits);
|
|
uniform1i(i, cur_tex);
|
|
mTexture[i] = cur_tex++;
|
|
}
|
|
}
|
|
unbind();
|
|
}
|
|
|
|
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(GLint index, const vector<LLStaticHashedString> * uniforms)
|
|
{
|
|
if (index == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GLenum type;
|
|
GLsizei length;
|
|
GLint size = -1;
|
|
char name[1024]; /* Flawfinder: ignore */
|
|
name[0] = 0;
|
|
|
|
glGetActiveUniformARB(mProgramObject, index, 1024, &length, &size, &type, (GLcharARB *)name);
|
|
#if !LL_DARWIN
|
|
if (size > 0)
|
|
{
|
|
switch(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;
|
|
|
|
//find the index of this uniform
|
|
for (S32 i = 0; i < (S32) LLShaderMgr::instance()->mReservedUniforms.size(); i++)
|
|
{
|
|
if ( (mUniform[i] == -1)
|
|
&& (LLShaderMgr::instance()->mReservedUniforms[i] == name))
|
|
{
|
|
//found it
|
|
mUniform[i] = location;
|
|
mTexture[i] = mapUniformTextureChannel(location, type);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (uniforms != NULL)
|
|
{
|
|
for (U32 i = 0; i < uniforms->size(); i++)
|
|
{
|
|
if ( (mUniform[i+LLShaderMgr::instance()->mReservedUniforms.size()] == -1)
|
|
&& ((*uniforms)[i].String() == name))
|
|
{
|
|
//found it
|
|
mUniform[i+LLShaderMgr::instance()->mReservedUniforms.size()] = location;
|
|
mTexture[i+LLShaderMgr::instance()->mReservedUniforms.size()] = mapUniformTextureChannel(location, 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;
|
|
|
|
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 + LLShaderMgr::instance()->mReservedUniforms.size(), -1);
|
|
mTexture.resize(numUniforms + LLShaderMgr::instance()->mReservedUniforms.size(), -1);
|
|
|
|
bind();
|
|
|
|
//get the number of active uniforms
|
|
GLint activeCount;
|
|
glGetProgramiv(mProgramObject, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &activeCount);
|
|
|
|
for (S32 i = 0; i < activeCount; i++)
|
|
{
|
|
mapUniform(i, 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)
|
|
{
|
|
gGL.flush();
|
|
uniform1f(LLShaderMgr::MINIMUM_ALPHA, minimum);
|
|
}
|