/** * @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 "llshadermgr.h" #include "llrender.h" #include "llfile.h" #if LL_DARWIN #include "OpenGL/OpenGL.h" #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() { } 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->calculatesAtmospherics) { 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 /////////////////////////////////////// // NOTE order of shader object attaching is VERY IMPORTANT!!! if(features->calculatesAtmospherics) { if (features->hasWaterFog) { if (!shader->attachObject("windlight/atmosphericsVarsWaterF.glsl")) { return FALSE; } } else if (!shader->attachObject("windlight/atmosphericsVarsF.glsl")) { return FALSE; } } if (features->calculatesLighting || features->calculatesAtmospherics) { if (!shader->attachObject("windlight/atmosphericsHelpersF.glsl")) { return FALSE; } } // we want this BEFORE shadows and AO because those facilities use pos/norm access if (features->isDeferred) { if (!shader->attachObject("deferred/deferredUtil.glsl")) { return FALSE; } } if (features->hasShadows) { if (!shader->attachObject("deferred/shadowUtil.glsl")) { return FALSE; } } if (features->hasAmbientOcclusion) { if (!shader->attachObject("deferred/aoUtil.glsl")) { return FALSE; } } if (features->hasIndirect) { if (!shader->attachObject("deferred/indirect.glsl")) { return FALSE; } } if (features->hasGamma) { if (!shader->attachObject("windlight/gammaF.glsl")) { return FALSE; } } if (features->hasSrgb) { if (!shader->attachObject("environment/srgbF.glsl")) { return FALSE; } } if (features->encodesNormal) { if (!shader->attachObject("environment/encodeNormF.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) { std::string res; //get log length GLint length; glGetObjectParameterivARB(ret, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length); if (length > 0) { //the log could be any size, so allocate appropriately GLcharARB* log = new GLcharARB[length]; glGetInfoLogARB(ret, length, &length, log); res = std::string((char *)log); delete[] log; } return res; } //dump shader source for debugging void LLShaderMgr::dumpShaderSource(U32 shader_code_count, GLcharARB** shader_code_text) { for (GLuint i = 0; i < shader_code_count; i++) { LL_SHADER_LOADING_WARNS() << i << ": " << shader_code_text[i] << LL_ENDL; } LL_SHADER_LOADING_WARNS() << LL_ENDL; } void LLShaderMgr::dumpObjectLog(GLhandleARB ret, BOOL warns, const std::string& filename) { std::string log = get_object_log(ret); std::string fname = filename; if (filename.empty()) { fname = "unknown shader file"; } if (log.length() > 0) { LL_SHADER_LOADING_WARNS() << "Shader loading from " << fname << ":\n" << LL_ENDL; LL_SHADER_LOADING_WARNS() << log << LL_ENDL; } } GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, boost::unordered_map* defines, S32 texture_index_channels) { // endsure work-around for missing GLSL funcs gets propogated to feature shader files (e.g. srgbF.glsl) #if LL_DARWIN if (defines) { (*defines)["OLD_SELECT"] = "1"; } #endif GLenum error = GL_NO_ERROR; if (gDebugGL) { error = glGetError(); if (error != GL_NO_ERROR) { LL_SHADER_LOADING_WARNS() << "GL ERROR entering loadShaderFile(): " << error << LL_ENDL; } } //LL_SHADER_LOADING_WARNS() << "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; std::string open_file_name; //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; open_file_name = fname.str(); /* Would be awesome, if we didn't have shaders that re-use files with different environments to say, add skinning, etc can't depend on cached version to have evaluate ifdefs identically... if we can define a deterministic hash for the shader based on all the inputs, maybe we can save some time here. if (mShaderObjects.count(filename) > 0) { return mShaderObjects[filename]; } */ LL_DEBUGS("ShaderLoading") << "Looking in " << open_file_name << LL_ENDL; file = LLFile::fopen(open_file_name, "r"); /* Flawfinder: ignore */ if (file) { LL_DEBUGS("ShaderLoading") << "Loading file: " << open_file_name << " (Want class " << gpu_class << ")" << LL_ENDL; break; // done } } if (file == NULL) { LL_SHADER_LOADING_WARNS() << "GLSL Shader file not found: " << open_file_name << 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 *extra_code_text[1024]; GLcharARB *shader_code_text[4096 + LL_ARRAY_SIZE(extra_code_text)] = { NULL }; GLuint extra_code_count = 0, shader_code_count = 0; BOOST_STATIC_ASSERT(LL_ARRAY_SIZE(extra_code_text) < LL_ARRAY_SIZE(shader_code_text)); 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) { shader_code_text[shader_code_count++] = strdup("#version 110\n"); extra_code_text[extra_code_count++] = strdup("#define ATTRIBUTE attribute\n"); extra_code_text[extra_code_count++] = strdup("#define VARYING varying\n"); extra_code_text[extra_code_count++] = strdup("#define VARYING_FLAT varying\n"); } else if (minor_version <= 29) { //set version to 1.20 shader_code_text[shader_code_count++] = strdup("#version 120\n"); extra_code_text[extra_code_count++] = strdup("#define FXAA_GLSL_120 1\n"); extra_code_text[extra_code_count++] = strdup("#define FXAA_FAST_PIXEL_OFFSET 0\n"); extra_code_text[extra_code_count++] = strdup("#define ATTRIBUTE attribute\n"); extra_code_text[extra_code_count++] = strdup("#define VARYING varying\n"); extra_code_text[extra_code_count++] = strdup("#define VARYING_FLAT varying\n"); } } else { if (major_version < 4) { //set version to 1.30 shader_code_text[shader_code_count++] = strdup("#version 130\n"); //some implementations of GLSL 1.30 require integer precision be explicitly declared extra_code_text[extra_code_count++] = strdup("precision mediump int;\n"); extra_code_text[extra_code_count++] = strdup("precision highp float;\n"); } else { //set version to 400 shader_code_text[shader_code_count++] = strdup("#version 400\n"); } extra_code_text[extra_code_count++] = strdup("#define DEFINE_GL_FRAGCOLOR 1\n"); extra_code_text[extra_code_count++] = strdup("#define FXAA_GLSL_130 1\n"); extra_code_text[extra_code_count++] = strdup("#define ATTRIBUTE in\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) extra_code_text[extra_code_count++] = strdup("#define VARYING out\n"); extra_code_text[extra_code_count++] = strdup("#define VARYING_FLAT flat out\n"); } else { extra_code_text[extra_code_count++] = strdup("#define VARYING in\n"); extra_code_text[extra_code_count++] = strdup("#define VARYING_FLAT flat in\n"); } //backwards compatibility with legacy texture lookup syntax extra_code_text[extra_code_count++] = strdup("#define texture2D texture\n"); extra_code_text[extra_code_count++] = strdup("#define textureCube texture\n"); extra_code_text[extra_code_count++] = strdup("#define texture2DLod textureLod\n"); extra_code_text[extra_code_count++] = strdup("#define shadow2D(a,b) vec2(texture(a,b))\n"); if (major_version > 1 || minor_version >= 40) { //GLSL 1.40 replaces texture2DRect et al with texture extra_code_text[extra_code_count++] = strdup("#define texture2DRect texture\n"); extra_code_text[extra_code_count++] = strdup("#define shadow2DRect(a,b) vec2(texture(a,b))\n"); } } if (defines) { for (boost::unordered_map::iterator iter = defines->begin(); iter != defines->end(); ++iter) { std::string define = "#define " + iter->first + " " + iter->second + "\n"; extra_code_text[extra_code_count++] = (GLcharARB *) strdup(define.c_str()); } } if( gGLManager.mIsATI ) { extra_code_text[extra_code_count++] = strdup( "#define IS_AMD_CARD 1\n" ); } 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; } */ extra_code_text[extra_code_count++] = strdup("#define HAS_DIFFUSE_LOOKUP\n"); //uniform declartion for (S32 i = 0; i < texture_index_channels; ++i) { std::string decl = llformat("uniform sampler2D tex%d;\n", i); extra_code_text[extra_code_count++] = strdup(decl.c_str()); } if (texture_index_channels > 1) { extra_code_text[extra_code_count++] = strdup("VARYING_FLAT int vary_texture_index;\n"); } extra_code_text[extra_code_count++] = strdup("vec4 diffuseLookup(vec2 texcoord)\n"); extra_code_text[extra_code_count++] = strdup("{\n"); if (texture_index_channels == 1) { //don't use flow control, that's silly extra_code_text[extra_code_count++] = strdup("return texture2D(tex0, texcoord);\n"); extra_code_text[extra_code_count++] = strdup("}\n"); } else if (major_version > 1 || minor_version >= 30) { //switches are supported in GLSL 1.30 and later if (gGLManager.mIsNVIDIA) { //switches are unreliable on some NVIDIA drivers for (U32 i = 0; i < texture_index_channels; ++i) { std::string if_string = llformat("\t%sif (vary_texture_index == %d) { return texture2D(tex%d, texcoord); }\n", i > 0 ? "else " : "", i, i); extra_code_text[extra_code_count++] = strdup(if_string.c_str()); } extra_code_text[extra_code_count++] = strdup("\treturn vec4(1,0,1,1);\n"); extra_code_text[extra_code_count++] = strdup("}\n"); } else { extra_code_text[extra_code_count++] = strdup("\tvec4 ret = vec4(1,0,1,1);\n"); extra_code_text[extra_code_count++] = strdup("\tswitch (vary_texture_index)\n"); extra_code_text[extra_code_count++] = strdup("\t{\n"); //switch body for (S32 i = 0; i < texture_index_channels; ++i) { std::string case_str = llformat("\t\tcase %d: return texture2D(tex%d, texcoord);\n", i, i); extra_code_text[extra_code_count++] = strdup(case_str.c_str()); } extra_code_text[extra_code_count++] = strdup("\t}\n"); extra_code_text[extra_code_count++] = strdup("\treturn ret;\n"); extra_code_text[extra_code_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; } } //copy file into memory enum { flag_write_to_out_of_extra_block_area = 0x01 , flag_extra_block_marker_was_found = 0x02 }; unsigned char flags = flag_write_to_out_of_extra_block_area; GLuint out_of_extra_block_counter = 0, start_shader_code = shader_code_count, file_lines_count = 0; while(NULL != fgets((char *)buff, 1024, file) && shader_code_count < (LL_ARRAY_SIZE(shader_code_text) - LL_ARRAY_SIZE(extra_code_text))) { file_lines_count++; bool extra_block_area_found = NULL != strstr((const char*)buff, "[EXTRA_CODE_HERE]"); if(extra_block_area_found && !(flag_extra_block_marker_was_found & flags)) { if(!(flag_write_to_out_of_extra_block_area & flags)) { //shift for(GLuint to = start_shader_code, from = extra_code_count + start_shader_code; from < shader_code_count; ++to, ++from) { shader_code_text[to] = shader_code_text[from]; } shader_code_count -= extra_code_count; } //copy extra code for(GLuint n = 0; n < extra_code_count && shader_code_count < (LL_ARRAY_SIZE(shader_code_text) - LL_ARRAY_SIZE(extra_code_text)); ++n) { shader_code_text[shader_code_count++] = extra_code_text[n]; } extra_code_count = 0; flags &= ~flag_write_to_out_of_extra_block_area; flags |= flag_extra_block_marker_was_found; } else { shader_code_text[shader_code_count] = (GLcharARB *)strdup((char *)buff); if(flag_write_to_out_of_extra_block_area & flags) { shader_code_text[extra_code_count + start_shader_code + out_of_extra_block_counter] = shader_code_text[shader_code_count]; out_of_extra_block_counter++; if(out_of_extra_block_counter == extra_code_count) { shader_code_count += extra_code_count; flags &= ~flag_write_to_out_of_extra_block_area; } } ++shader_code_count; } } //while if(!(flag_extra_block_marker_was_found & flags)) { for(GLuint n = start_shader_code; n < extra_code_count + start_shader_code; ++n) { shader_code_text[n] = extra_code_text[n - start_shader_code]; } if (file_lines_count < extra_code_count) { shader_code_count += extra_code_count; } extra_code_count = 0; } 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; } } //load source glShaderSourceARB(ret, shader_code_count, (const GLcharARB**) shader_code_text, NULL); if (gDebugGL) { error = glGetError(); if (error != GL_NO_ERROR) { LL_WARNS("ShaderLoading") << "GL ERROR in glShaderSourceARB: " << error << LL_ENDL; } } //compile source glCompileShaderARB(ret); if (gDebugGL) { error = glGetError(); if (error != GL_NO_ERROR) { LL_WARNS("ShaderLoading") << "GL ERROR in glCompileShaderARB: " << error << LL_ENDL; } } if (error == GL_NO_ERROR) { //check for errors GLint success = GL_TRUE; glGetObjectParameterivARB(ret, GL_OBJECT_COMPILE_STATUS_ARB, &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:" << LL_ENDL; dumpObjectLog(ret, TRUE, open_file_name); dumpShaderSource(shader_code_count, shader_code_text); ret = 0; } } } else { ret = 0; } stop_glerror(); //free memory for (GLuint i = 0; i < shader_code_count; i++) { free(shader_code_text[i]); } //successfully loaded, save results if (ret) { // Add shader file to map mShaderObjects[filename] = ret; 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; } BOOL LLShaderMgr::linkProgramObject(GLhandleARB obj, BOOL suppress_errors) { //check for errors glLinkProgramARB(obj); GLint success = GL_TRUE; glGetObjectParameterivARB(obj, GL_OBJECT_LINK_STATUS_ARB, &success); if (!suppress_errors && success == GL_FALSE) { //an error occured, print log LL_SHADER_LOADING_WARNS() << "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_SHADER_LOADING_WARNS() << "GLSL Linker: Running in Software:" << LL_ENDL; success = GL_FALSE; suppress_errors = FALSE; } } #else std::string log = get_object_log(obj); LLStringUtil::toLower(log); if (log.find("software") != std::string::npos) { LL_SHADER_LOADING_WARNS() << "GLSL Linker: Running in Software:" << LL_ENDL; success = GL_FALSE; suppress_errors = FALSE; } #endif return success; } BOOL LLShaderMgr::validateProgramObject(GLhandleARB obj) { //check program validity against current GL glValidateProgramARB(obj); GLint success = GL_TRUE; glGetObjectParameterivARB(obj, GL_OBJECT_VALIDATE_STATUS_ARB, &success); if (success == GL_FALSE) { LL_SHADER_LOADING_WARNS() << "GLSL program not valid: " << LL_ENDL; dumpObjectLog(obj); } else { dumpObjectLog(obj, 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("inv_modelview"); 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("viewport"); 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("altDiffuseMap"); mReservedUniforms.push_back("specularMap"); mReservedUniforms.push_back("bumpMap"); mReservedUniforms.push_back("bumpMap2"); mReservedUniforms.push_back("environmentMap"); mReservedUniforms.push_back("cloud_noise_texture"); mReservedUniforms.push_back("cloud_noise_texture_next"); mReservedUniforms.push_back("fullbright"); mReservedUniforms.push_back("lightnorm"); mReservedUniforms.push_back("sunlight_color"); 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_mat"); mReservedUniforms.push_back("screen_res"); 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("moon_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"); llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH+1); mReservedUniforms.push_back("tc_scale"); 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("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("screenTex"); mReservedUniforms.push_back("screenDepth"); mReservedUniforms.push_back("refTex"); mReservedUniforms.push_back("eyeVec"); mReservedUniforms.push_back("time"); mReservedUniforms.push_back("waveDir1"); mReservedUniforms.push_back("waveDir2"); 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"); mReservedUniforms.push_back("inscatter"); mReservedUniforms.push_back("sun_size"); mReservedUniforms.push_back("fog_color"); mReservedUniforms.push_back("transmittance_texture"); mReservedUniforms.push_back("scattering_texture"); mReservedUniforms.push_back("single_mie_scattering_texture"); mReservedUniforms.push_back("irradiance_texture"); mReservedUniforms.push_back("blend_factor"); mReservedUniforms.push_back("no_atmo"); mReservedUniforms.push_back("moisture_level"); mReservedUniforms.push_back("droplet_radius"); mReservedUniforms.push_back("ice_level"); mReservedUniforms.push_back("rainbow_map"); mReservedUniforms.push_back("halo_map"); mReservedUniforms.push_back("moon_brightness"); mReservedUniforms.push_back("cloud_variance"); mReservedUniforms.push_back("sh_input_r"); mReservedUniforms.push_back("sh_input_g"); mReservedUniforms.push_back("sh_input_b"); mReservedUniforms.push_back("sun_moon_glow_factor"); mReservedUniforms.push_back("water_edge"); mReservedUniforms.push_back("sun_up_factor"); mReservedUniforms.push_back("moonlight_color"); llassert(mReservedUniforms.size() == END_RESERVED_UNIFORMS); std::set 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]); } }