#include "anki/resource/ShaderProgram.h" #include "anki/resource/ShaderProgramPrePreprocessor.h" #include "anki/core/App.h" // To get cache dir #include "anki/gl/GlException.h" #include "anki/core/Logger.h" #include "anki/util/Util.h" #include "anki/core/Globals.h" #include "anki/util/Exception.h" #include #include #include #include #include #include namespace anki { #define SHADER_PROGRAM_EXCEPTION(x) ANKI_EXCEPTION( \ "Shader program \"" + rsrcFilename + \ "\": " + x) //============================================================================== // Statics = //============================================================================== const char* ShaderProgram::stdSourceCode = "#version 330 core\n" //"precision lowp float;\n" #if defined(NDEBUG) "#pragma optimize(on)\n" "#pragma debug(off)\n"; #else "#pragma optimize(of)\n" "#pragma debug(on)\n"; #endif //============================================================================== // Destructor = //============================================================================== ShaderProgram::~ShaderProgram() { /// @todo add code } //============================================================================== // createAndCompileShader = //============================================================================== uint ShaderProgram::createAndCompileShader(const char* sourceCode, const char* preproc, int type) const { uint glId = 0; const char* sourceStrs[2] = {NULL, NULL}; // create the shader glId = glCreateShader(type); // attach the source sourceStrs[1] = sourceCode; sourceStrs[0] = preproc; // compile glShaderSource(glId, 2, sourceStrs, NULL); glCompileShader(glId); int success; glGetShaderiv(glId, GL_COMPILE_STATUS, &success); if(!success) { // print info log int infoLen = 0; int charsWritten = 0; std::vector infoLog; glGetShaderiv(glId, GL_INFO_LOG_LENGTH, &infoLen); infoLog.resize(infoLen + 1); glGetShaderInfoLog(glId, infoLen, &charsWritten, &infoLog[0]); infoLog[charsWritten] = '\0'; const char* shaderType = "*dummy*"; switch(type) { case GL_VERTEX_SHADER: shaderType = "Vertex shader"; break; case GL_FRAGMENT_SHADER: shaderType = "Fragment shader"; break; default: ASSERT(0); // Not supported } throw SHADER_PROGRAM_EXCEPTION(shaderType + " compiler error log follows:\n" "===================================\n" + &infoLog[0] + "\n===================================\n" + sourceCode); } return glId; } //============================================================================== // link = //============================================================================== void ShaderProgram::link() const { // link glLinkProgram(glId); // check if linked correctly int success; glGetProgramiv(glId, GL_LINK_STATUS, &success); if(!success) { int info_len = 0; int charsWritten = 0; std::string infoLogTxt; glGetProgramiv(glId, GL_INFO_LOG_LENGTH, &info_len); infoLogTxt.resize(info_len + 1); glGetProgramInfoLog(glId, info_len, &charsWritten, &infoLogTxt[0]); throw SHADER_PROGRAM_EXCEPTION("Link error log follows:\n" + infoLogTxt); } } //============================================================================== // getUniAndAttribVars = //============================================================================== void ShaderProgram::getUniAndAttribVars() { int num; boost::array name_; GLsizei length; GLint size; GLenum type; // attrib locations glGetProgramiv(glId, GL_ACTIVE_ATTRIBUTES, &num); for(int i = 0; i < num; i++) // loop all attributes { glGetActiveAttrib(glId, i, sizeof(name_) / sizeof(char), &length, &size, &type, &name_[0]); name_[length] = '\0'; // check if its FFP location int loc = glGetAttribLocation(glId, &name_[0]); if(loc == -1) // if -1 it means that its an FFP var { ANKI_WARNING("Shader prog: \"" << rsrcFilename << "\": You are using FFP vertex attributes (\"" << &name_[0] << "\")"); continue; } ShaderProgramAttributeVariable* var = new ShaderProgramAttributeVariable(loc, &name_[0], type, *this); vars.push_back(var); nameToVar[var->getName().c_str()] = var; nameToAttribVar[var->getName().c_str()] = var; } // uni locations glGetProgramiv(glId, GL_ACTIVE_UNIFORMS, &num); for(int i = 0; i < num; i++) // loop all uniforms { glGetActiveUniform(glId, i, sizeof(name_) / sizeof(char), &length, &size, &type, &name_[0]); name_[length] = '\0'; // check if its FFP location int loc = glGetUniformLocation(glId, &name_[0]); if(loc == -1) // if -1 it means that its an FFP var { ANKI_WARNING("Shader prog: \"" << rsrcFilename << "\": You are using FFP vertex uniforms (\"" << &name_[0] << "\")"); continue; } ShaderProgramUniformVariable* var = new ShaderProgramUniformVariable(loc, &name_[0], type, *this); vars.push_back(var); nameToVar[var->getName().c_str()] = var; nameToUniVar[var->getName().c_str()] = var; } } //============================================================================== // load = //============================================================================== void ShaderProgram::load(const char* filename) { rsrcFilename = filename; ASSERT(glId == std::numeric_limits::max()); ShaderProgramPrePreprocessor pars(filename); // 1) create and compile the shaders std::string preprocSource = stdSourceCode; vertShaderGlId = createAndCompileShader( pars.getShaderSource(ST_VERTEX).c_str(), preprocSource.c_str(), GL_VERTEX_SHADER); fragShaderGlId = createAndCompileShader( pars.getShaderSource(ST_FRAGMENT).c_str(), preprocSource.c_str(), GL_FRAGMENT_SHADER); // 2) create program and attach shaders glId = glCreateProgram(); if(glId == 0) { throw SHADER_PROGRAM_EXCEPTION("glCreateProgram failed"); } glAttachShader(glId, vertShaderGlId); glAttachShader(glId, fragShaderGlId); // 3) set the TRFFB varyings if(pars.getTranformFeedbackVaryings().size() > 0) { boost::array varsArr; for(uint i = 0; i < pars.getTranformFeedbackVaryings().size(); i++) { varsArr[i] = pars.getTranformFeedbackVaryings()[i].c_str(); } glTransformFeedbackVaryings(glId, pars.getTranformFeedbackVaryings().size(), &varsArr[0], GL_SEPARATE_ATTRIBS); } // 4) link link(); // init the rest getUniAndAttribVars(); } //============================================================================== // getVariableByName = //============================================================================== const ShaderProgramVariable& ShaderProgram::getVariableByName( const char* name) const { VarsHashMap::const_iterator it = nameToVar.find(name); if(it == nameToVar.end()) { throw SHADER_PROGRAM_EXCEPTION("Cannot get variable: " + name); } return *(it->second); } //============================================================================== // getAttributeVariableByName = //============================================================================== const ShaderProgramAttributeVariable& ShaderProgram::getAttributeVariableByName( const char* name) const { AttribVarsHashMap::const_iterator it = nameToAttribVar.find(name); if(it == nameToAttribVar.end()) { throw SHADER_PROGRAM_EXCEPTION("Cannot get attribute loc: " + name); } return *(it->second); } //============================================================================== // getUniformVariableByName = //============================================================================== const ShaderProgramUniformVariable& ShaderProgram::getUniformVariableByName( const char* name) const { UniVarsHashMap::const_iterator it = nameToUniVar.find(name); if(it == nameToUniVar.end()) { throw SHADER_PROGRAM_EXCEPTION("Cannot get uniform loc: " + name); } return *(it->second); } //============================================================================== // variableExists = //============================================================================== bool ShaderProgram::variableExists(const char* name) const { VarsHashMap::const_iterator it = nameToVar.find(name); return it != nameToVar.end(); } //============================================================================== // uniformVariableExists = //============================================================================== bool ShaderProgram::uniformVariableExists(const char* name) const { UniVarsHashMap::const_iterator it = nameToUniVar.find(name); return it != nameToUniVar.end(); } //============================================================================== // attributeVariableExists = //============================================================================== bool ShaderProgram::attributeVariableExists(const char* name) const { AttribVarsHashMap::const_iterator it = nameToAttribVar.find(name); return it != nameToAttribVar.end(); } //============================================================================== // createSrcCodeToCache = //============================================================================== std::string ShaderProgram::createSrcCodeToCache(const char* sProgFPathName, const char* preAppendedSrcCode) { if(strlen(preAppendedSrcCode) < 1) { return sProgFPathName; } // Create suffix boost::hash stringHash; std::size_t h = stringHash(preAppendedSrcCode); std::string suffix = boost::lexical_cast(h); // boost::filesystem::path newfPathName = AppSingleton::get().getCachePath() / (boost::filesystem::path(sProgFPathName).filename().string() + "." + suffix); if(boost::filesystem::exists(newfPathName)) { return newfPathName.string(); } std::string src_ = util::readFile(sProgFPathName); std::string src = preAppendedSrcCode + src_; std::ofstream f(newfPathName.string().c_str()); if(!f.is_open()) { throw ANKI_EXCEPTION("Cannot open file for writing \"" + newfPathName.string() + "\""); } f.write(src.c_str(), src.length()); return newfPathName.string(); } //============================================================================== // getShaderInfoString = //============================================================================== std::string ShaderProgram::getShaderInfoString() const { std::stringstream ss; ss << "Variables:\n"; BOOST_FOREACH(const ShaderProgramVariable& var, vars) { ss << var.getName() << " " << var.getLocation() << " "; if(var.getType() == ShaderProgramVariable::T_ATTRIBUTE) { ss << "attribute"; } else { ss << "uniform"; } ss << std::endl; } return ss.str(); } } // end namespace