/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2004 Robert Osfield * Copyright (C) 2003-2004 3Dlabs Inc. Ltd. * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial * 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/osgGL2/ProgramObject.cpp * author: Mike Weiblen 2004-07-08 * * See http://www.3dlabs.com/opengl2/ for more information regarding * the OpenGL Shading Language. */ #include #include #include #include #include #include #include #include #include #ifdef THREAD_SAFE_GLOBJECT_DELETE_LISTS #include #include #endif using namespace osgGL2; /////////////////////////////////////////////////////////////////////////// namespace { class InfoLog { public: InfoLog( Extensions* ext, const GLhandleARB handle ) { GLint blen = 0; // length of buffer to allocate GLint slen = 0; // strlen GL actually wrote to buffer ext->glGetObjectParameteriv(handle, GL_OBJECT_INFO_LOG_LENGTH_ARB , &blen); if (blen > 1) { GLcharARB* infoLog = new GLcharARB[blen]; ext->glGetInfoLog( handle, blen, &slen, infoLog ); _text = infoLog; delete [] infoLog; } } friend std::ostream& operator<< ( std::ostream& o, const InfoLog& log ) { return o << log._text; } private: InfoLog(); std::string _text; }; } /////////////////////////////////////////////////////////////////////////// // static cache of deleted GL2 objects which may only // by actually deleted in the correct GL context. typedef std::list GL2ObjectList; typedef std::map DeletedGL2ObjectCache; #ifdef THREAD_SAFE_GLOBJECT_DELETE_LISTS static OpenThreads::Mutex s_mutex_deletedGL2ObjectCache; #endif static DeletedGL2ObjectCache s_deletedGL2ObjectCache; void ProgramObject::deleteObject(unsigned int contextID, GLhandleARB handle) { if (handle!=0) { #ifdef THREAD_SAFE_GLOBJECT_DELETE_LISTS OpenThreads::ScopedLock lock(s_mutex_deletedGL2ObjectCache); #endif // add handle to the cache for the appropriate context. s_deletedGL2ObjectCache[contextID].push_back(handle); } } void ProgramObject::flushDeletedGL2Objects(unsigned int contextID,double /*currentTime*/, double& availableTime) { // if no time available don't try to flush objects. if (availableTime<=0.0) return; const osg::Timer& timer = *osg::Timer::instance(); osg::Timer_t start_tick = timer.tick(); double elapsedTime = 0.0; { #ifdef THREAD_SAFE_GLOBJECT_DELETE_LISTS OpenThreads::ScopedLock lock(s_mutex_deletedGL2ObjectCache); #endif DeletedGL2ObjectCache::iterator citr = s_deletedGL2ObjectCache.find(contextID); if( citr != s_deletedGL2ObjectCache.end() ) { const Extensions* extensions = Extensions::Get(contextID,true); if (!extensions->isGlslSupported()) { // can we really get here? osg::notify(osg::WARN) << "flushDeletedGL2Objects not supported by OpenGL driver" << std::endl; return; } GL2ObjectList& vpObjectList = citr->second; for(GL2ObjectList::iterator titr=vpObjectList.begin(); titr!=vpObjectList.end() && elapsedTimeglDeleteObject( *titr ); titr = vpObjectList.erase( titr ); elapsedTime = timer.delta_s(start_tick,timer.tick()); } } } availableTime -= elapsedTime; } /////////////////////////////////////////////////////////////////////////// // osgGL2::ProgramObject /////////////////////////////////////////////////////////////////////////// ProgramObject::ProgramObject() { // To ensure all PCPOs consistently get the same values, we must // postpone updates until all PCPOs have been created. // They are created during ProgramObject::apply(), so let a frame // go by before sending the updates. _frameNumberOfLastPCPOUpdate = 1; _enabled = true; } ProgramObject::ProgramObject(const ProgramObject& rhs, const osg::CopyOp& copyop): osg::StateAttribute(rhs, copyop) { osg::notify(osg::FATAL) << "how got here?" << std::endl; } // virtual ProgramObject::~ProgramObject() { for( unsigned int cxt=0; cxt < _pcpoList.size(); ++cxt ) { if( ! _pcpoList[cxt] ) continue; PerContextProgObj* pcpo = _pcpoList[cxt].get(); deleteObject( cxt, pcpo->getHandle() ); // TODO add shader objects to delete list. _pcpoList[cxt] = 0; } } int ProgramObject::compare(const osg::StateAttribute& sa) const { // check the types are equal and then create the rhs variable // used by the COMPARE_StateAttribute_Paramter macro's below. COMPARE_StateAttribute_Types(ProgramObject,sa) if (_shaderObjectList.size()rhs._shaderObjectList.size()) return 1; ShaderObjectList::const_iterator litr=_shaderObjectList.begin(); ShaderObjectList::const_iterator ritr=rhs._shaderObjectList.begin(); for(; litr!=_shaderObjectList.end(); ++litr,++ritr) { int result = (*litr)->compare(*(*ritr)); if (result!=0) return result; } if (_univalList.size()rhs._univalList.size()) return 1; UniformValueList::const_iterator luitr=_univalList.begin(); UniformValueList::const_iterator ruitr=rhs._univalList.begin(); for(; luitr!=_univalList.end(); ++luitr,++ruitr) { int result = (*luitr)->compare(*(*ruitr)); if (result!=0) return result; } return 0; // passed all the above comparison macro's, must be equal. } // mark all PCPOs as needing a relink void ProgramObject::dirtyProgramObject() { for( unsigned int cxt=0; cxt < _pcpoList.size(); ++cxt ) { if( ! _pcpoList[cxt] ) continue; PerContextProgObj* pcpo = _pcpoList[cxt].get(); pcpo->markAsDirty(); } } // mark all attached ShaderObjects as needing a rebuild void ProgramObject::dirtyShaderObjects() { for( unsigned int i=0; i < _shaderObjectList.size() ; ++i ) { _shaderObjectList[i]->dirtyShaderObject(); } } void ProgramObject::releaseGLObjects(osg::State* state) const { if (!state) const_cast(this)->dirtyShaderObjects(); else { unsigned int contextID = state->getContextID(); const_cast(this)->_shaderObjectList[contextID]->dirtyShaderObject(); } } void ProgramObject::addShader( ShaderObject* shadObj ) { _shaderObjectList.push_back( shadObj ); shadObj->addProgObjRef( this ); dirtyProgramObject(); } void ProgramObject::setUniform( const char* uniformName, int value ) { _univalList.push_back( new UniformValue_int( uniformName, value ) ); } void ProgramObject::setUniform( const char* uniformName, float value ) { _univalList.push_back( new UniformValue_float( uniformName, value ) ); } void ProgramObject::setUniform( const char* uniformName, osg::Vec2 value ) { _univalList.push_back( new UniformValue_Vec2( uniformName, value ) ); } void ProgramObject::setUniform( const char* uniformName, osg::Vec3 value ) { _univalList.push_back( new UniformValue_Vec3( uniformName, value ) ); } void ProgramObject::setUniform( const char* uniformName, osg::Vec4 value ) { _univalList.push_back( new UniformValue_Vec4( uniformName, value ) ); } void ProgramObject::apply(osg::State& state) const { const unsigned int contextID = state.getContextID(); const Extensions* extensions = Extensions::Get(contextID,true); // if there are no ShaderObjects on this ProgramObject, // use GL 1.x "fixed functionality" rendering. if( !_enabled || _shaderObjectList.empty() ) { if( extensions->isGlslSupported() ) { extensions->glUseProgramObject( 0 ); } return; } if( ! extensions->isGlslSupported() ) { osg::notify(osg::WARN) << "GLSL not supported by OpenGL driver" << std::endl; return; } const osg::FrameStamp* frameStamp = state.getFrameStamp(); const int frameNumber = (frameStamp) ? frameStamp->getFrameNumber() : -1; updateUniforms( frameNumber ); PerContextProgObj* pcpo = getPCPO( contextID ); if( pcpo->isDirty() ) { for( unsigned int i=0; i < _shaderObjectList.size() ; ++i ) { _shaderObjectList[i]->build( contextID ); } pcpo->build(); } // make this glProgramObject part of current GL state pcpo->use(); // consume any pending setUniform messages pcpo->applyUniformValues(); } ProgramObject::PerContextProgObj* ProgramObject::getPCPO(unsigned int contextID) const { if( ! _pcpoList[contextID].valid() ) { _pcpoList[contextID] = new PerContextProgObj( this, contextID ); // attach all PCSOs to this new PCPO for( unsigned int i=0; i < _shaderObjectList.size() ; ++i ) { _shaderObjectList[i]->attach( contextID, _pcpoList[contextID]->getHandle() ); } } return _pcpoList[contextID].get(); } void ProgramObject::updateUniforms( int frameNumber ) const { if( frameNumber <= _frameNumberOfLastPCPOUpdate ) return; _frameNumberOfLastPCPOUpdate = frameNumber; if( _univalList.empty() ) return; for( unsigned int cxt=0; cxt < _pcpoList.size(); ++cxt ) { if( ! _pcpoList[cxt] ) continue; PerContextProgObj* pcpo = _pcpoList[cxt].get(); pcpo->updateUniforms( _univalList ); } _univalList.clear(); } /////////////////////////////////////////////////////////////////////////// // PCPO : OSG abstraction of the per-context Program Object ProgramObject::PerContextProgObj::PerContextProgObj(const ProgramObject* progObj, unsigned int contextID ) : osg::Referenced(), _contextID( contextID ) { _progObj = progObj; _extensions = Extensions::Get( _contextID, true ); _glProgObjHandle = _extensions->glCreateProgramObject(); markAsDirty(); } ProgramObject::PerContextProgObj::PerContextProgObj(const PerContextProgObj& rhs) : osg::Referenced(), _contextID( rhs._contextID ) { _progObj = rhs._progObj; _extensions = rhs._extensions; _glProgObjHandle = rhs._glProgObjHandle ; _dirty = rhs._dirty; } ProgramObject::PerContextProgObj::~PerContextProgObj() { } void ProgramObject::PerContextProgObj::build() { GLint linked; _extensions->glLinkProgram( _glProgObjHandle ); _extensions->glGetObjectParameteriv(_glProgObjHandle, GL_OBJECT_LINK_STATUS_ARB, &linked); _dirty = (linked == 0); if( _dirty ) { InfoLog log( _extensions.get(), _glProgObjHandle ); osg::notify(osg::WARN) << "glLinkProgram FAILED:\n" << log << std::endl; } } void ProgramObject::PerContextProgObj::use() const { _extensions->glUseProgramObject( _glProgObjHandle ); } void ProgramObject::PerContextProgObj::updateUniforms( const UniformValueList& univalList ) { // TODO: should the incoming list be appended rather than assigned? _univalList = univalList; } void ProgramObject::PerContextProgObj::applyUniformValues() { Extensions *ext = _extensions.get(); for( unsigned int i=0; i < _univalList.size() ; ++i ) { _univalList[i]->apply( ext, _glProgObjHandle ); } _univalList.clear(); } /////////////////////////////////////////////////////////////////////////// // osgGL2::ShaderObject /////////////////////////////////////////////////////////////////////////// ShaderObject::ShaderObject() : _type(UNKNOWN) { } ShaderObject::ShaderObject(Type type) : _type(type) { } ShaderObject::ShaderObject(Type type, const char* sourceText) : _type(type) { setShaderSource(sourceText); } ShaderObject::ShaderObject(const ShaderObject& rhs, const osg::CopyOp& copyop): osg::Object(rhs, copyop) { /*TODO*/ } ShaderObject::~ShaderObject() { /*TODO*/ } int ShaderObject::compare(const ShaderObject& so) const { if (getShaderSource()markAsDirty(); } // mark attached ProgramObjects dirty as well for( unsigned int i=0; i < _programObjectList.size(); ++i ) { _programObjectList[i]->dirtyProgramObject(); } } void ShaderObject::setShaderSource( const char* sourceText ) { _shaderSource = sourceText; dirtyShaderObject(); } bool ShaderObject::loadShaderSourceFromFile( const char* fileName ) { std::ifstream sourceFile; sourceFile.open(fileName, std::ios::binary); if(!sourceFile) { osg::notify(osg::WARN)<<"Error: can't open file \""<isDirty() ) { pcso->build(); } } ShaderObject::PerContextShaderObj* ShaderObject::getPCSO(unsigned int contextID) const { if( ! _pcsoList[contextID].valid() ) { _pcsoList[contextID] = new PerContextShaderObj( this, contextID ); } return _pcsoList[contextID].get(); } void ShaderObject::attach(unsigned int contextID, GLhandleARB progObj) const { getPCSO( contextID )->attach( progObj ); } void ShaderObject::addProgObjRef( ProgramObject* progObj ) { _programObjectList.push_back( progObj ); } /////////////////////////////////////////////////////////////////////////// // PCSO : OSG abstraction of the per-context Shader Object ShaderObject::PerContextShaderObj::PerContextShaderObj(const ShaderObject* shadObj, unsigned int contextID) : osg::Referenced(), _contextID( contextID ) { _shadObj = shadObj; _extensions = Extensions::Get( _contextID, true ); _glShaderObjHandle = _extensions->glCreateShaderObject( shadObj->getType() ); markAsDirty(); } ShaderObject::PerContextShaderObj::PerContextShaderObj(const PerContextShaderObj& rhs) : osg::Referenced(), _contextID( rhs._contextID ) { _shadObj = rhs._shadObj; _extensions = rhs._extensions; _glShaderObjHandle = rhs._glShaderObjHandle; _dirty = rhs._dirty; } ShaderObject::PerContextShaderObj::~PerContextShaderObj() { } void ShaderObject::PerContextShaderObj::build() { GLint compiled; const char* sourceText = _shadObj->getShaderSource().c_str(); _extensions->glShaderSource( _glShaderObjHandle, 1, &sourceText, NULL ); _extensions->glCompileShader( _glShaderObjHandle ); _extensions->glGetObjectParameteriv(_glShaderObjHandle, GL_OBJECT_COMPILE_STATUS_ARB, &compiled); _dirty = (compiled == 0); if( _dirty ) { InfoLog log( _extensions.get(), _glShaderObjHandle ); osg::notify(osg::WARN) << _shadObj->getTypename() << " glCompileShader FAILED:\n" << log << std::endl; } } void ShaderObject::PerContextShaderObj::attach(GLhandleARB progObj) const { _extensions->glAttachObject( progObj, _glShaderObjHandle ); } /*EOF*/