/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * Copyright (C) 2003-2005 3Dlabs Inc. Ltd. * Copyright (C) 2004-2005 Nathan Cournia * Copyright (C) 2008 Zebra Imaging * Copyright (C) 2010 VIRES Simulationstechnologie GmbH * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commercial and non commercial * applications, as long as this copyright notice is maintained. * * This application 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. * */ /* file: src/osg/Shader.cpp * author: Mike Weiblen 2008-01-02 * Holger Helmich 2010-10-21 */ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace osg { template inline std::string::size_type find_first(const std::string& str, const M& match, std::string::size_type startpos, std::string::size_type endpos=std::string::npos) { std::string::size_type endp = (endpos!=std::string::npos) ? endpos : str.size(); while(startposisGlslSupported) extensions->glDeleteShader( globj ); } }; /////////////////////////////////////////////////////////////////////////////////// // // ShaderComponent // ShaderComponent::ShaderComponent() { } ShaderComponent::ShaderComponent(const ShaderComponent& sc,const CopyOp& copyop): osg::Object(sc, copyop), _shaders(sc._shaders) { } unsigned int ShaderComponent::addShader(osg::Shader* shader) { for(unsigned int i=0; i<_shaders.size();++i) { if (_shaders[i]==shader) return i; } _shaders.push_back(shader); return _shaders.size()-1; } void ShaderComponent::removeShader(unsigned int i) { _shaders.erase(_shaders.begin()+i); } void ShaderComponent::compileGLObjects(State& state) const { for(Shaders::const_iterator itr = _shaders.begin(); itr != _shaders.end(); ++itr) { (*itr)->compileShader(state); } } void ShaderComponent::resizeGLObjectBuffers(unsigned int maxSize) { for(Shaders::const_iterator itr = _shaders.begin(); itr != _shaders.end(); ++itr) { (*itr)->resizeGLObjectBuffers(maxSize); } } void ShaderComponent::releaseGLObjects(State* state) const { for(Shaders::const_iterator itr = _shaders.begin(); itr != _shaders.end(); ++itr) { (*itr)->releaseGLObjects(state); } } /////////////////////////////////////////////////////////////////////////////////// // // ShaderBinary // ShaderBinary::ShaderBinary() { } ShaderBinary::ShaderBinary(const ShaderBinary& rhs, const osg::CopyOp& copyop): osg::Object(rhs, copyop), _data(rhs._data) { } void ShaderBinary::allocate(unsigned int size) { _data.clear(); _data.resize(size); } void ShaderBinary::assign(unsigned int size, const unsigned char* data) { allocate(size); if (data) { for(unsigned int i=0; i shaderBinary = new osg::ShaderBinary; shaderBinary->allocate(length); fin.seekg(0, std::ios::beg); fin.read(reinterpret_cast(shaderBinary->getData()), length); fin.close(); return shaderBinary.release(); } /////////////////////////////////////////////////////////////////////////// // osg::Shader /////////////////////////////////////////////////////////////////////////// Shader::Shader(Type type) : _type(type), _shaderDefinesMode(USE_SHADER_PRAGMA) { } Shader::Shader(Type type, const std::string& source) : _type(type), _shaderDefinesMode(USE_SHADER_PRAGMA) { setShaderSource( source); } Shader::Shader(Type type, ShaderBinary* shaderBinary) : _type(type), _shaderBinary(shaderBinary), _shaderDefinesMode(USE_SHADER_PRAGMA) { } Shader::Shader(const Shader& rhs, const osg::CopyOp& copyop): osg::Object( rhs, copyop ), _type(rhs._type), _shaderFileName(rhs._shaderFileName), _shaderSource(rhs._shaderSource), _shaderBinary(rhs._shaderBinary), _codeInjectionMap(rhs._codeInjectionMap), _shaderDefinesMode(rhs._shaderDefinesMode) { } Shader::~Shader() { } bool Shader::setType(Type t) { if (_type==t) return true; if (_type != UNDEFINED) { OSG_WARN << "cannot change type of Shader" << std::endl; return false; } _type = t; return true; } int Shader::compare(const Shader& rhs) const { if( this == &rhs ) return 0; if( getType() < rhs.getType() ) return -1; if( rhs.getType() < getType() ) return 1; if( getName() < rhs.getName() ) return -1; if( rhs.getName() < getName() ) return 1; if( getShaderSource() < rhs.getShaderSource() ) return -1; if( rhs.getShaderSource() < getShaderSource() ) return 1; if( getShaderBinary() < rhs.getShaderBinary() ) return -1; if( rhs.getShaderBinary() < getShaderBinary() ) return 1; if( getFileName() < rhs.getFileName() ) return -1; if( rhs.getFileName() < getFileName() ) return 1; return 0; } void Shader::setShaderSource( const std::string& sourceText ) { _shaderSource = sourceText; _computeShaderDefines(); dirtyShader(); } Shader* Shader::readShaderFile( Type type, const std::string& fileName ) { ref_ptr shader = new Shader(type); if (shader->loadShaderSourceFromFile(fileName)) return shader.release(); return 0; } bool Shader::loadShaderSourceFromFile( const std::string& fileName ) { std::ifstream sourceFile; sourceFile.open(fileName.c_str(), std::ios::binary); if(!sourceFile) { OSG_WARN<<"Error: can't open file \""<getContextID(); _pcsList[contextID] = 0; } } void Shader::compileShader( osg::State& state ) const { PerContextShader* pcs = getPCS( state ); if( pcs ) pcs->compileShader( state ); } Shader::ShaderObjects::ShaderObjects(const osg::Shader* shader, unsigned int contextID): _contextID(contextID), _shader(shader) { } Shader::PerContextShader* Shader::ShaderObjects::getPCS(const std::string& defineStr) const { for(PerContextShaders::const_iterator itr = _perContextShaders.begin(); itr != _perContextShaders.end(); ++itr) { if ((*itr)->getDefineString()==defineStr) { // OSG_NOTICE<<"ShaderPtr = "<<_shader<<" FileName = '"<<_shader->getFileName()<<"' returning PCS "<get()<<" DefineString = "<<(*itr)->getDefineString()<get(); } } // OSG_NOTICE<<"ShaderPtr = "<<_shader<<" FileName = '"<<_shader->getFileName()<<"' returning NULL"<setDefineString(defineStr); // OSG_NOTICE<<"ShaderPtr = "<<_shader<<" FileName = '"<<_shader->getFileName()<<"' Creating PCS "<getDefineString()<<"]"<requestCompile(); } } Shader::PerContextShader* Shader::getPCS(osg::State& state) const { if( getType() == UNDEFINED ) { OSG_WARN << "Shader type is UNDEFINED" << std::endl; return 0; } if (!state.supportsShaderRequirements(_shaderRequirements)) { // OSG_NOTICE<<"Shader not supported "<<_shaderRequirements.size()<getPCS(defineStr); if (pcs) return pcs; if (state.supportsShaderRequirements(_shaderRequirements)) { pcs = _pcsList[contextID]->createPerContextShader(defineStr); } return pcs; } ///////////////////////////////////////////////////////////////////////// // A Shader stores pointers to the osg::Programs to which it is attached, // so that if the Shader is marked for recompilation with // Shader::dirtyShader(), the upstream Program can be marked for relinking. // _programSet does not use ref_ptrs, as that would cause a cyclical // dependency, and neither the Program nor the Shader would be deleted. bool Shader::addProgramRef( Program* program ) { ProgramSet::iterator itr = _programSet.find(program); if( itr != _programSet.end() ) return false; _programSet.insert( program ); return true; } bool Shader::removeProgramRef( Program* program ) { ProgramSet::iterator itr = _programSet.find(program); if( itr == _programSet.end() ) return false; _programSet.erase( itr ); return true; } void Shader::dirtyShader() { // Mark our PCSs as needing recompilation. for( unsigned int cxt=0; cxt < _pcsList.size(); ++cxt ) { if( _pcsList[cxt].valid() ) _pcsList[cxt]->requestCompile(); } // Also mark Programs that depend on us as needing relink. for( ProgramSet::iterator itr = _programSet.begin(); itr != _programSet.end(); ++itr ) { (*itr)->dirtyProgram(); } } ///////////////////////////////////////////////////////////////////////// // osg::Shader::PerContextShader // PCS is the OSG abstraction of the per-context glShader /////////////////////////////////////////////////////////////////////////// Shader::PerContextShader::PerContextShader(const Shader* shader, unsigned int contextID) : osg::Referenced(), _contextID( contextID ) { _shader = shader; _extensions = GLExtensions::Get( _contextID, true ); _glShaderHandle = _extensions->glCreateShader( shader->getType() ); requestCompile(); } Shader::PerContextShader::~PerContextShader() { osg::get(_contextID)->scheduleGLObjectForDeletion(_glShaderHandle); } void Shader::PerContextShader::requestCompile() { _needsCompile = true; _isCompiled = false; } namespace { std::string insertLineNumbers(const std::string& source) { if (source.empty()) return source; unsigned int lineNum = 1; // Line numbers start at 1 std::ostringstream ostr; std::string::size_type previous_pos = 0; do { std::string::size_type pos = find_first(source, EqualTo('\n'), previous_pos); if (pos != std::string::npos) { ostr << std::setw(5)<getShaderBinary()) { GLint numFormats = 0; glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &numFormats); if (numFormats>0) { std::vector formats(numFormats); glGetIntegerv(GL_SHADER_BINARY_FORMATS, &formats[0]); for(GLint i=0; igetShaderSource().empty()) { OSG_WARN<<"Warning: No shader binary formats supported by GLES driver, unable to compile shader."<getShaderSource(); if (_shader->getType()==osg::Shader::VERTEX && (state.getUseVertexAttributeAliasing() || state.getUseModelViewAndProjectionUniforms())) { state.convertVertexShaderSourceToOsgBuiltIns(source); } if (osg::getNotifyLevel()>=osg::INFO) { std::string sourceWithLineNumbers = insertLineNumbers(source); OSG_INFO << "\nCompiling " << _shader->getTypename() << " source:\n" << sourceWithLineNumbers << std::endl; } GLint compiled = GL_FALSE; // OSG_NOTICE<<"Compiling PerContextShader "<=8 && source.compare(start_of_line, 8, "#version")==0) { versionLine = source.substr(start_of_line, end_of_line-start_of_line+1); if (versionLine[versionLine.size()-1]!='\n') versionLine.push_back('\n'); source.insert(start_of_line, "// following version spec has been automatically reassigned to start of source list: "); break; } previous_pos = end_of_line+1(versionLine.c_str()); sourceText[1] = reinterpret_cast(_defineStr.c_str()); sourceText[2] = reinterpret_cast(source.c_str()); _extensions->glShaderSource( _glShaderHandle, 3, sourceText, NULL ); } else { const GLchar* sourceText[2]; //OSG_NOTICE<<"glShaderSource() ["<<_defineStr<<"], ["<(_defineStr.c_str()); sourceText[1] = reinterpret_cast(source.c_str()); _extensions->glShaderSource( _glShaderHandle, 2, sourceText, NULL ); } } _extensions->glCompileShader( _glShaderHandle ); _extensions->glGetShaderiv( _glShaderHandle, GL_COMPILE_STATUS, &compiled ); _isCompiled = (compiled == GL_TRUE); if( ! _isCompiled ) { OSG_WARN << _shader->getTypename() << " glCompileShader \"" << _shader->getName() << "\" FAILED" << std::endl; std::string infoLog; if( getInfoLog(infoLog) ) { OSG_WARN << _shader->getTypename() << " Shader \"" << _shader->getName() << "\" infolog:\n" << infoLog << std::endl; } } else { std::string infoLog; if( getInfoLog(infoLog) ) { OSG_INFO << _shader->getTypename() << " Shader \"" << _shader->getName() << "\" infolog:\n" << infoLog << std::endl; } _extensions->debugObjectLabel(GL_SHADER, _glShaderHandle, _shader->getName()); } } bool Shader::PerContextShader::getInfoLog( std::string& infoLog ) const { return _extensions->getShaderInfoLog( _glShaderHandle, infoLog ); } void Shader::PerContextShader::attachShader(GLuint program) const { _extensions->glAttachShader( program, _glShaderHandle ); } void Shader::PerContextShader::detachShader(GLuint program) const { _extensions->glDetachShader( program, _glShaderHandle ); } void Shader::_parseShaderDefines(const std::string& str, ShaderDefines& defines) { OSG_INFO<<"Shader::_parseShaderDefines("<