Skip to content
Snippets Groups Projects
llglslshader.cpp 44 KiB
Newer Older
/** 
 * @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 "llvertexbuffer.h"

// 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;

Rye Mutt's avatar
Rye Mutt committed
GLuint LLGLSLShader::sCurBoundShader = 0;
LLGLSLShader* LLGLSLShader::sCurBoundShaderPtr = NULL;
David Parks's avatar
David Parks committed
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
LLGLSLShader    gUIProgram;
LLGLSLShader    gSolidColorProgram;
BOOL shouldChange(const LLVector4& v1, const LLVector4& 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)
    , isDeferred(false)
    , hasIndirect(false)
    , hasShadows(false)
    , hasAmbientOcclusion(false)
    , mIndexedTextureChannels(0)
    , disableTextureIndex(false)
    , hasAlphaMask(false)
    , attachNothing(false)
{
}

//===============================
// LLGLSL Shader implementation
//===============================
    sProfileEnabled = true;
    sTotalTimeElapsed = 0;
    sTotalTrianglesDrawn = 0;
    sTotalSamplesDrawn = 0;
    sTotalDrawCalls = 0;

    for (std::set<LLGLSLShader*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
    {
        (*iter)->clearStats();
    }
David Parks's avatar
David Parks committed
struct LLGLSLShaderCompareTimeElapsed
{
        bool operator()(const LLGLSLShader* const& lhs, const LLGLSLShader* const& rhs)
        {
            return lhs->mTimeElapsed < rhs->mTimeElapsed;
        }
David Parks's avatar
David Parks committed
};

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;
    mTrianglesDrawn = 0;
    mTimeElapsed = 0;
    mSamplesDrawn = 0;
    mDrawCalls = 0;
    mTextureStateFetched = false;
    mTextureMagFilter.clear();
    mTextureMinFilter.clear();
    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)
            
            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;
    if (sProfileEnabled && sCurBoundShaderPtr)
    {
        sCurBoundShaderPtr->placeProfileQuery();
    }

}

//static
void LLGLSLShader::stopProfile(U32 count, U32 mode)
{
    if (sProfileEnabled && sCurBoundShaderPtr)
    {
        sCurBoundShaderPtr->readProfileQuery(count, mode);
    }
David Parks's avatar
David Parks committed
#if !LL_DARWIN
Rye Mutt's avatar
Rye Mutt committed
        glGenQueries(1, &mSamplesQuery);
        glGenQueries(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)
            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();
    }
Rye Mutt's avatar
Rye Mutt committed
    glBeginQuery(GL_SAMPLES_PASSED, mSamplesQuery);
    glBeginQuery(GL_TIME_ELAPSED, mTimerQuery);
David Parks's avatar
David Parks committed
#endif
}

void LLGLSLShader::readProfileQuery(U32 count, U32 mode)
{
David Parks's avatar
David Parks committed
#if !LL_DARWIN
Rye Mutt's avatar
Rye Mutt committed
    glEndQuery(GL_TIME_ELAPSED);
    glEndQuery(GL_SAMPLES_PASSED);
    GLuint64 time_elapsed = 0;
    glGetQueryObjectui64v(mTimerQuery, GL_QUERY_RESULT, &time_elapsed);

    GLuint64 samples_passed = 0;
    glGetQueryObjectui64v(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++;
David Parks's avatar
David Parks committed
#endif
LLGLSLShader::LLGLSLShader()
    : mProgramObject(0), 
      mAttributeMask(0),
      mTotalUniformSize(0),
      mActiveTextureChannels(0), 
      mShaderLevel(0), 
      mShaderGroup(SG_DEFAULT), 
      mUniformsDirty(FALSE),
      mTimerQuery(0),
      mSamplesQuery(0)
}

void LLGLSLShader::unload()
{
    mShaderFiles.clear();
    mDefines.clear();

    unloadInternal();
}

void LLGLSLShader::unloadInternal()
    sInstances.erase(this);

    stop_glerror();
    mAttribute.clear();
    mTexture.clear();
    mUniform.clear();

    if (mProgramObject)
    {
Rye Mutt's avatar
Rye Mutt committed
        GLuint obj[1024];
Rye Mutt's avatar
Rye Mutt committed
        glGetAttachedShaders(mProgramObject, 1024, &count, obj);

        for (GLsizei i = 0; i < count; i++)
        {
Rye Mutt's avatar
Rye Mutt committed
            glDetachShader(mProgramObject, obj[i]);
            glDeleteShader(obj[i]);
Graham Linden's avatar
Graham Linden committed
        }
Rye Mutt's avatar
Rye Mutt committed
        glDeleteProgram(mProgramObject);
Rye Mutt's avatar
Rye Mutt committed
        glDeleteQueries(1, &mTimerQuery);
Rye Mutt's avatar
Rye Mutt committed
        glDeleteQueries(1, &mSamplesQuery);
        mSamplesQuery = 0;
    }

    //hack to make apple not complain
    glGetError();
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;
Rye Mutt's avatar
Rye Mutt committed
    mProgramObject = glCreateProgram();
#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++ )
    {
Rye Mutt's avatar
Rye Mutt committed
        GLuint shaderhandle = LLShaderMgr::instance()->loadShaderFile((*fileIter).first, mShaderLevel, (*fileIter).second, &mDefines, mFeatures.mIndexedTextureChannels);
        LL_DEBUGS("ShaderLoading") << "SHADER FILE: " << (*fileIter).first << " mShaderLevel=" << mShaderLevel << LL_ENDL;
        {
            attachObject(shaderhandle);
        }
        else
        {
            success = FALSE;
        }
    }

    // Attach existing objects
    if (!LLShaderMgr::instance()->attachShaderFeatures(this))
    {
        return FALSE;
    }

    if (gGLManager.mGLSLVersionMajor < 2 && gGLManager.mGLSLVersionMinor < 3)
    { //indexed texture rendering requires GLSL 1.3 or later
        //attachShaderFeatures may have set the number of indexed texture channels, so set to 1 again
        mFeatures.mIndexedTextureChannels = llmin(mFeatures.mIndexedTextureChannels, 1);
    }
    // Map attributes and uniforms
    if (success)
    {
        success = mapAttributes(attributes);
    }
    if (success)
    {
        success = mapUniforms(uniforms);
    }
    if( !success )
    {
Graham Linden's avatar
Graham Linden committed
        LL_SHADER_LOADING_WARNS() << "Failed to link shader: " << mName << LL_ENDL;

        // Try again using a lower shader level;
        if (mShaderLevel > 0)
        {
Graham Linden's avatar
Graham Linden committed
            LL_SHADER_LOADING_WARNS() << "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);
BOOL LLGLSLShader::attachVertexObject(std::string object_path) {
    if (LLShaderMgr::instance()->mVertexShaderObjects.count(object_path) > 0)
    {
        stop_glerror();
Rye Mutt's avatar
Rye Mutt committed
        glAttachShader(mProgramObject, LLShaderMgr::instance()->mVertexShaderObjects[object_path]);
        stop_glerror();
        return TRUE;
    }
    else
    {
        LL_SHADER_LOADING_WARNS() << "Attempting to attach shader object: '" << object_path << "' that hasn't been compiled." << LL_ENDL;
        return FALSE;
    }
}

BOOL LLGLSLShader::attachFragmentObject(std::string object_path)
    if (LLShaderMgr::instance()->mFragmentShaderObjects.count(object_path) > 0)
Rye Mutt's avatar
Rye Mutt committed
        glAttachShader(mProgramObject, LLShaderMgr::instance()->mFragmentShaderObjects[object_path]);
        stop_glerror();
        return TRUE;
    }
    else
    {
        LL_SHADER_LOADING_WARNS() << "Attempting to attach shader object: '" << object_path << "' that hasn't been compiled." << LL_ENDL;
Rye Mutt's avatar
Rye Mutt committed
void LLGLSLShader::attachObject(GLuint object)
    if (object != 0)
    {
        stop_glerror();
Rye Mutt's avatar
Rye Mutt committed
        glAttachShader(mProgramObject, object);
Graham Linden's avatar
Graham Linden committed
        LL_SHADER_LOADING_WARNS() << "Attempting to attach non existing shader object. " << LL_ENDL;
Rye Mutt's avatar
Rye Mutt committed
void LLGLSLShader::attachObjects(GLuint* 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();
Rye Mutt's avatar
Rye Mutt committed
        glBindAttribLocation(mProgramObject, i, (const GLchar *) name);
    }
    
    //link the program
    BOOL res = link();

    mAttribute.clear();
    U32 numAttributes = (attributes == NULL) ? 0 : attributes->size();
#if LL_RELEASE_WITH_DEBUG_INFO
    mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, { -1, NULL });
#else
    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 < LLShaderMgr::instance()->mReservedAttribs.size(); i++)
        {
            const char* name = LLShaderMgr::instance()->mReservedAttribs[i].c_str();
Rye Mutt's avatar
Rye Mutt committed
            S32 index = glGetAttribLocation(mProgramObject, (const GLchar *)name);
#if LL_RELEASE_WITH_DEBUG_INFO
                mAttribute[i] = { index, name };
#else
                mAttribute[i] = index;
                mAttributeMask |= 1 << i;
                LL_DEBUGS("ShaderUniform") << "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();
Rye Mutt's avatar
Rye Mutt committed
                S32 index = glGetAttribLocation(mProgramObject, name);
                if (index != -1)
                {
                    mAttribute[LLShaderMgr::instance()->mReservedAttribs.size() + i] = index;
                    LL_DEBUGS("ShaderUniform") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
void LLGLSLShader::mapUniform(GLint index, const vector<LLStaticHashedString> * uniforms)
    GLenum type;
    GLsizei length;
    GLint size = -1;
    char name[1024];        /* Flawfinder: ignore */
    name[0] = 0;
Rye Mutt's avatar
Rye Mutt committed
    glGetActiveUniform(mProgramObject, index, 1024, &length, &size, &type, (GLchar *)name);
David Parks's avatar
David Parks committed
#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;
    }
David Parks's avatar
David Parks committed
#endif
Rye Mutt's avatar
Rye Mutt committed
    S32 location = glGetUniformLocation(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("ShaderUniform") << "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;
Graham Linden's avatar
Graham Linden committed
void LLGLSLShader::clearPermutations()
{
    mDefines.clear();
}

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)
{
Rye Mutt's avatar
Rye Mutt committed
    if ((type >= GL_SAMPLER_1D && type <= GL_SAMPLER_2D_RECT_SHADOW) ||
        type == GL_SAMPLER_2D_MULTISAMPLE)
    {   //this here is a texture
Rye Mutt's avatar
Rye Mutt committed
        glUniform1i(location, mActiveTextureChannels);
        LL_DEBUGS("ShaderUniform") << "Assigned to texture channel " << mActiveTextureChannels << LL_ENDL;
        return mActiveTextureChannels++;
    }
    return -1;
BOOL LLGLSLShader::mapUniforms(const vector<LLStaticHashedString> * uniforms)
Graham Linden's avatar
Graham Linden committed
	BOOL res = TRUE;

	mTotalUniformSize = 0;
	mActiveTextureChannels = 0;
	mUniform.clear();
	mUniformMap.clear();
	mUniformNameMap.clear();
	mTexture.clear();
	mValue.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;
Rye Mutt's avatar
Rye Mutt committed
	glGetProgramiv(mProgramObject, GL_ACTIVE_UNIFORMS, &activeCount);
Graham Linden's avatar
Graham Linden committed

	//........................................................................................................................................
	//........................................................................................

	/*
	EXPLANATION:
	This is part of code is temporary because as the final result the mapUniform() should be rewrited.
	But it's a huge a volume of work which is need to be a more carefully performed for avoid possible
	regression's (i.e. it should be formalized a separate ticket in JIRA).

	RESON:
	The reason of this code is that SL engine is very sensitive to fact that "diffuseMap" should be appear
	first as uniform parameter which is should get 0-"texture channel" index (see mapUniformTextureChannel() and mActiveTextureChannels)
	it influence to which is texture matrix will be updated during rendering.

	But, order of indexe's of uniform variables is not defined and GLSL compiler can change it as want
	, even if the "diffuseMap" will be appear and use first in shader code.

	As example where this situation appear see: "Deferred Material Shader 28/29/30/31"
	And tickets: MAINT-4165, MAINT-4839, MAINT-3568, MAINT-6437
	*/


Rye Mutt's avatar
Rye Mutt committed
	S32 diffuseMap = glGetUniformLocation(mProgramObject, "diffuseMap");
	S32 specularMap = glGetUniformLocation(mProgramObject, "specularMap");
	S32 bumpMap = glGetUniformLocation(mProgramObject, "bumpMap");
    S32 altDiffuseMap = glGetUniformLocation(mProgramObject, "altDiffuseMap");
	S32 environmentMap = glGetUniformLocation(mProgramObject, "environmentMap");
Graham Linden's avatar
Graham Linden committed
	std::set<S32> skip_index;
Graham Linden's avatar
Graham Linden committed
	if (-1 != diffuseMap && (-1 != specularMap || -1 != bumpMap || -1 != environmentMap || -1 != altDiffuseMap))
	{
		GLenum type;
		GLsizei length;
		GLint size = -1;
		char name[1024];
Graham Linden's avatar
Graham Linden committed
		diffuseMap = altDiffuseMap = specularMap = bumpMap = environmentMap = -1;
Graham Linden's avatar
Graham Linden committed
		for (S32 i = 0; i < activeCount; i++)
		{
			name[0] = '\0';
Rye Mutt's avatar
Rye Mutt committed
			glGetActiveUniform(mProgramObject, i, 1024, &length, &size, &type, (GLchar *)name);
Graham Linden's avatar
Graham Linden committed
			if (-1 == diffuseMap && std::string(name) == "diffuseMap")
			{
				diffuseMap = i;
				continue;
			}
Graham Linden's avatar
Graham Linden committed
			if (-1 == specularMap && std::string(name) == "specularMap")
			{
				specularMap = i;
				continue;
			}
Graham Linden's avatar
Graham Linden committed
			if (-1 == bumpMap && std::string(name) == "bumpMap")
			{
				bumpMap = i;
				continue;
			}
Graham Linden's avatar
Graham Linden committed
			if (-1 == environmentMap && std::string(name) == "environmentMap")
			{
				environmentMap = i;
				continue;
			}

            if (-1 == altDiffuseMap && std::string(name) == "altDiffuseMap")
Graham Linden's avatar
Graham Linden committed
			{
				altDiffuseMap = i;
				continue;
			}
		}

		bool specularDiff = specularMap < diffuseMap && -1 != specularMap;
		bool bumpLessDiff = bumpMap < diffuseMap && -1 != bumpMap;
		bool envLessDiff = environmentMap < diffuseMap && -1 != environmentMap;

		if (specularDiff || bumpLessDiff || envLessDiff)
		{
			mapUniform(diffuseMap, uniforms);
			skip_index.insert(diffuseMap);

			if (-1 != specularMap) {
				mapUniform(specularMap, uniforms);
				skip_index.insert(specularMap);
			}

			if (-1 != bumpMap) {
				mapUniform(bumpMap, uniforms);
				skip_index.insert(bumpMap);
			}

			if (-1 != environmentMap) {
				mapUniform(environmentMap, uniforms);
				skip_index.insert(environmentMap);
			}
		}
	}

	//........................................................................................

	for (S32 i = 0; i < activeCount; i++)
	{
		//........................................................................................
		if (skip_index.end() != skip_index.find(i)) continue;
		//........................................................................................

		mapUniform(i, uniforms);
	}
	//........................................................................................................................................

	unbind();

	LL_DEBUGS("ShaderUniform") << "Total Uniform Size: " << mTotalUniformSize << LL_ENDL;
	return res;
BOOL LLGLSLShader::link(BOOL suppress_errors)
{
    BOOL success = LLShaderMgr::instance()->linkProgramObject(mProgramObject, suppress_errors);
Graham Linden's avatar
Graham Linden committed
    if (!success && !suppress_errors)
Rye Mutt's avatar
Rye Mutt committed
        LLShaderMgr::instance()->dumpObjectLog(true, mProgramObject, !success, mName);
}

void LLGLSLShader::bind()
{
    gGL.flush();
    if (gGLManager.mHasShaderObjects)
    {
        LLVertexBuffer::unbind();
Rye Mutt's avatar
Rye Mutt committed
        glUseProgram(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();
Rye Mutt's avatar
Rye Mutt committed
        glUseProgram(0);
        sCurBoundShader = 0;
        sCurBoundShaderPtr = NULL;
        stop_glerror();
    }
}

void LLGLSLShader::bindNoShader(void)
{
    LLVertexBuffer::unbind();
    if (gGLManager.mHasShaderObjects)
    {
Rye Mutt's avatar
Rye Mutt committed
        glUseProgram(0);
        sCurBoundShader = 0;
        sCurBoundShaderPtr = NULL;
    }
S32 LLGLSLShader::bindTexture(const std::string &uniform, LLTexture *texture, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace colorspace)
    S32 channel = 0;
    channel = getUniformLocation(uniform);
    
    return bindTexture(channel, texture, mode, colorspace);
S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture *texture, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace colorspace)
    if (uniform < 0 || uniform >= (S32)mTexture.size())
    {
Graham Linden's avatar
Graham Linden committed
        LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
        gGL.getTexUnit(uniform)->bind(texture, mode);
        gGL.getTexUnit(uniform)->setTextureColorSpace(colorspace);
}

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())
Graham Linden's avatar
Graham Linden committed
        LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
        return -1;
    }
    
    uniform = mTexture[uniform];
    
    if (uniform > -1)
    {
        gGL.getTexUnit(uniform)->unbind(mode);
S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space)
    if (uniform < 0 || uniform >= (S32)mTexture.size())
    {
Graham Linden's avatar
Graham Linden committed
        LL_SHADER_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);
        gGL.getTexUnit(index)->setTextureColorSpace(space);
S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space)