diff --git a/include/osg/Program b/include/osg/Program index dd92f8f9c..b88bc17f9 100644 --- a/include/osg/Program +++ b/include/osg/Program @@ -1,5 +1,6 @@ /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield * Copyright (C) 2003-2005 3Dlabs Inc. Ltd. + * Copyright (C) 2004-2005 Nathan Cournia * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial @@ -11,7 +12,7 @@ */ /* file: include/osg/Program - * author: Mike Weiblen 2005-02-20 + * author: Mike Weiblen 2005-03-23 */ // NOTICE: This code is CLOSED during construction and/or renovation! @@ -24,22 +25,20 @@ #ifndef OSG_PROGRAM #define OSG_PROGRAM 1 -#include -#include -#include -#include -#include -#include -#include -#include - #include #include +#include +#include +#include +#include +#include +#include namespace osg { class State; +class Shader; class SG_EXPORT GL2Extensions : public osg::Referenced { @@ -293,13 +292,6 @@ class SG_EXPORT GL2Extensions : public osg::Referenced void* _glGetHandleARB; }; -class Program; -typedef osg::ref_ptr ProgramPtr; - -class Shader; -typedef osg::ref_ptr ShaderPtr; -typedef std::vector< ShaderPtr > ShaderList; - /////////////////////////////////////////////////////////////////////////// /** osg::Program is an application-level abstraction of an OpenGL glProgram. * It is an osg::StateAttribute that, when applied, will activate a @@ -307,12 +299,12 @@ typedef std::vector< ShaderPtr > ShaderList; * osg::Shaders containing the actual shader source code are * attached to a Program, which will then manage the compilation, * linking, and activation of the GLSL program. - * Program will automatically manage per-context instancing of the - * OpenGL objects, if that is necessary for a particular display + * osg::Program will automatically manage per-context instancing of the + * OpenGL glPrograms, if that is necessary for a particular display * configuration. */ -class SG_EXPORT Program : public osg::Object +class SG_EXPORT Program : public osg::StateAttribute { public: Program(); @@ -320,10 +312,10 @@ class SG_EXPORT Program : public osg::Object /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ Program(const Program& rhs, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); - META_Object(osg, Program); + META_StateAttribute(osg, Program, PROGRAM); /** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.*/ - virtual int compare(const osg::Program& sa) const; + virtual int compare(const osg::StateAttribute& sa) const; /** If enabled, activate our program in the GL pipeline, * performing any rebuild operations that might be pending. */ @@ -336,30 +328,30 @@ class SG_EXPORT Program : public osg::Object State object pointer NULL.*/ virtual void releaseGLObjects(osg::State* state=0) const; - // data access methods. - - /** Mark us as "dirty" and in need of relinking. */ + /** Mark our PCSOs as needing relink */ void dirtyProgram(); - /** Mark our attached Shaders as "dirty" and in need of recompilation. */ - void dirtyShaders(); + /** Attach an osg::Shader to this osg::Program. + * Mark Program as needing relink. Return true for success */ + bool addShader( Shader* shader ); - /** Attach a Shader to this Program */ - void addShader( Shader* shader ); + /** Remove osg::Shader from this osg::Program. + * Mark Program as needing relink. Return true for success */ + bool removeShader( Shader* shader ); - /** Mark internal GL glProgram for deletion. - * Deletion requests are queued until they can be executed - * in the proper GL context. */ - static void deleteProgram(unsigned int contextID, GLuint program); + /** */ + void bindAttribLocation( GLuint index, const char* name ); - /** flush all the cached glPrograms which need to be deleted - * in the OpenGL context related to contextID.*/ - static void flushDeletedGlslObjects(unsigned int contextID,double currentTime, double& availableTime); + /** Return true if this Program represents "fixed-functionality" rendering */ + bool isFixedFunction() const; - /** An annotation/comment for use by the application */ - void setComment( const std::string& comment ) { _comment = comment; } - void setComment( const char* comment ) { _comment = comment; } - const std::string& getComment() const { return _comment; } + /** Query InfoLog from a glProgram */ + void getGlProgramInfoLog(unsigned int contextID, std::string& log) const; + + /** A name for use by the application */ + void setName( const std::string& name ) { _name = name; } + void setName( const char* name ) { _name = name; } + const std::string& getName() const { return _name; } /** should Uniform values be tested to avoid redundant setting? */ void setAvoidRedundantUniformSetting( bool flag ) { _avoidRedundantUniformSetting = flag; } @@ -367,6 +359,15 @@ class SG_EXPORT Program : public osg::Object // TODO glBindAttribLocation + /** Mark internal glProgram for deletion. + * Deletion requests are queued until they can be executed + * in the proper GL context. */ + static void deleteGlProgram(unsigned int contextID, GLuint program); + + /** flush all the cached glPrograms which need to be deleted + * in the OpenGL context related to contextID.*/ + static void flushDeletedGlPrograms(unsigned int contextID,double currentTime, double& availableTime); + protected: /** An OSG-internal encapsulation of glProgram's active uniforms */ @@ -389,25 +390,26 @@ class SG_EXPORT Program : public osg::Object protected: - /** PCPO is an OSG-internal encapsulation of glPrograms per-GL context. */ - class PerContextProgObj : public osg::Referenced + /** PerContextProgram (PCP) is an OSG-internal encapsulation of glPrograms per-GL context. */ + class PerContextProgram : public osg::Referenced { public: - PerContextProgObj(const Program* program, unsigned int contextID); - PerContextProgObj(const PerContextProgObj& rhs); + PerContextProgram(const Program* program, unsigned int contextID); - const GLuint getHandle() {return _glProgramHandle;} + GLuint getHandle() const {return _glProgramHandle;} - bool isDirty() const {return _dirty;} - void markAsDirty(); + void requestLink(); void linkProgram(); + bool needsLink() const {return _needsLink;} + bool isLinked() const {return _isLinked;} + void getInfoLog( std::string& infoLog ) const; + void useProgram() const; void applyUniforms( osg::State& state ) const; protected: /*methods*/ - PerContextProgObj(); - ~PerContextProgObj(); + ~PerContextProgram(); protected: /*data*/ /** Pointer to our parent Program */ @@ -416,30 +418,36 @@ class SG_EXPORT Program : public osg::Object osg::ref_ptr _extensions; /** Handle to the actual OpenGL glProgram */ GLuint _glProgramHandle; - /** Do we need to be linked? */ - bool _dirty; - /** Queue of UniformValues awaiting assignment */ + /** Does our glProgram need to be linked? */ + bool _needsLink; + /** Is our glProgram successfully linked? */ + bool _isLinked; const unsigned int _contextID; - /** List of PCPO's active uniforms */ + /** List of PCP's active uniforms */ ActiveUniformList _activeUniformList; + + private: + PerContextProgram(); // disallowed + PerContextProgram(const PerContextProgram&); // disallowed + PerContextProgram& operator=(const PerContextProgram&); // disallowed }; protected: /*methods*/ virtual ~Program(); - /** Get the PCPO for a particular GL context */ - PerContextProgObj* getPCPO(unsigned int contextID) const; + /** Get the PCP for a particular GL context */ + PerContextProgram* getPCP(unsigned int contextID) const; protected: /*data*/ bool _avoidRedundantUniformSetting; - std::string _comment; + std::string _name; + typedef std::vector< ref_ptr > ShaderList; ShaderList _shaderList; - mutable osg::buffered_value< osg::ref_ptr > _pcpoList; + mutable osg::buffered_value< osg::ref_ptr > _pcpList; private: - const Program& operator=(const Program&); + Program& operator=(const Program&); // disallowed }; - } #endif diff --git a/include/osg/Shader b/include/osg/Shader index 9b72112fe..4e822bc05 100644 --- a/include/osg/Shader +++ b/include/osg/Shader @@ -1,5 +1,6 @@ /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield * Copyright (C) 2003-2005 3Dlabs Inc. Ltd. + * Copyright (C) 2004-2005 Nathan Cournia * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial @@ -10,8 +11,8 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -/* file: include/osg/Program - * author: Mike Weiblen 2005-02-20 +/* file: include/osg/Shader + * author: Mike Weiblen 2005-03-23 */ // NOTICE: This code is CLOSED during construction and/or renovation! @@ -24,6 +25,9 @@ #ifndef OSG_SHADER #define OSG_SHADER 1 +#include +#include + #include namespace osg { @@ -32,7 +36,7 @@ namespace osg { /** osg::Shader is an application-level abstraction of an OpenGL glShader. * It is a container to load the shader source code text and manage its * compilation. - * A Shader may be attached to more than one osg::Program. + * An osg::Shader may be attached to more than one osg::Program. * Shader will automatically manage per-context instancing of the * internal objects, if that is necessary for a particular display * configuration. @@ -44,7 +48,7 @@ class SG_EXPORT Shader : public osg::Object enum Type { VERTEX = GL_VERTEX_SHADER, - FRAGMENT = GL_FRAGMENT_SHADER, + FRAGMENT = GL_FRAGMENT_SHADER }; Shader( Type type, const char* sourceText = 0 ); @@ -52,6 +56,8 @@ class SG_EXPORT Shader : public osg::Object /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ Shader(const Shader& rhs, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); + META_Object(osg, Shader); // see note in Shader.cpp Shader() + int compare(const Shader& rhs) const; // data access methods. @@ -71,80 +77,97 @@ class SG_EXPORT Shader : public osg::Object /** Get the Shader type as a descriptive string. */ const char* getTypename() const; - /** Mark us as "dirty" and in need of recompilation */ + /** Mark our PCSs as needing recompilation. + * Also mark Programs that depend on us as needing relink */ void dirtyShader(); - /** Mark internal GL glShader for deletion. - * Deletion requests are queued until they can be executed - * in the proper GL context. */ - static void deleteShader(unsigned int contextID, GLuint shader); - - /** Perform a recompilation of all our PCSOs */ + /** If needed, compile the PCS's glShader */ void compileShader(unsigned int contextID) const; /** For a given GL context, attach a glShader to a glProgram */ void attachShader(unsigned int contextID, GLuint program) const; - /** An annotation/comment for use by the application */ - void setComment( const std::string& comment ) { _comment = comment; } - void setComment( const char* comment ) { _comment = comment; } - const std::string& getComment() const { return _comment; }; + /** Query InfoLog from a glShader */ + void getGlShaderInfoLog(unsigned int contextID, std::string& log) const; + /** A name for use by the application */ + void setName( const std::string& name ) { _name = name; } + void setName( const char* name ) { _name = name; } + const std::string& getName() const { return _name; }; + + /** Mark internal glShader for deletion. + * Deletion requests are queued tuntil they can be executed + * in the proper GL context. */ + static void deleteGlShader(unsigned int contextID, GLuint shader); + + /** flush all the cached glShaders which need to be deleted + * in the OpenGL context related to contextID.*/ + static void flushDeletedGlShaders(unsigned int contextID,double currentTime, double& availableTime); protected: - /** PCSO is an OSG-internal encapsulation of glShader per-GL context. */ - class PerContextShaderObj : public osg::Referenced + /** PerContextShader (PCS) is an OSG-internal encapsulation of glShader per-GL context. */ + class PerContextShader : public osg::Referenced { public: - PerContextShaderObj(const Shader* shader, unsigned int contextID); - PerContextShaderObj(const PerContextShaderObj& rhs); + PerContextShader(const Shader* shader, unsigned int contextID); - const GLuint getHandle() {return _glShaderHandle;} + GLuint getHandle() const {return _glShaderHandle;} - bool isDirty() const {return _dirty;} - void markAsDirty() {_dirty = true; } + void requestCompile(); void compileShader(); + void getInfoLog( std::string& infoLog ) const; /** Attach our glShader to a glProgram */ void attachShader(GLuint program) const; + /** Detach our glShader from a glProgram */ + void detachShader(GLuint program) const; + protected: /*methods*/ - PerContextShaderObj(); - ~PerContextShaderObj(); + ~PerContextShader(); protected: /*data*/ - /** Pointer to our parent Shader */ + /** Pointer to our parent osg::Shader */ const Shader* _shader; /** Pointer to this context's extension functions. */ - osg::ref_ptr _extensions; + osg::ref_ptr _extensions; /** Handle to the actual glShader. */ GLuint _glShaderHandle; - /** Do we need to be recompiled? */ - bool _dirty; + /** Does our glShader need to be recompiled? */ + bool _needsCompile; + /** Is our glShader successfully compiled? */ + bool _isCompiled; const unsigned int _contextID; + + private: + PerContextShader(); // disallowed + PerContextShader(const PerContextShader&); // disallowed + PerContextShader& operator=(const PerContextShader&); // disallowed }; protected: /*methods*/ + Shader(); // undesired, temporarily required by META_Object. virtual ~Shader(); - PerContextShaderObj* getPCSO(unsigned int contextID) const; - friend void Program::addShader( Shader* shader ); // to access addProgObjRef() - void addProgObjRef( Program* program ); + PerContextShader* getPCS(unsigned int contextID) const; + + friend class Program; + bool addProgramRef( Program* program ); + bool removeProgramRef( Program* program ); protected: /*data*/ const Type _type; - std::string _comment; + std::string _name; std::string _shaderSource; - /** list of Programs that this Shader is attached to */ - std::vector< ProgramPtr > _programList; - mutable osg::buffered_value< osg::ref_ptr > _pcsoList; + /** osg::Programs that this osg::Shader is attached to */ + typedef std::set< Program* > ProgramSet; + ProgramSet _programSet; + mutable osg::buffered_value< osg::ref_ptr > _pcsList; private: - Shader(); // disallowed Shader& operator=(const Shader&); // disallowed }; - } #endif diff --git a/include/osg/StateAttribute b/include/osg/StateAttribute index 3cd652a60..f0c197399 100644 --- a/include/osg/StateAttribute +++ b/include/osg/StateAttribute @@ -184,6 +184,9 @@ class SG_EXPORT StateAttribute : public Object // osgNVParse OSGNVPARSE_PROGRAM_PARSER, + + /// core GLSL support + PROGRAM }; /** Simple pairing between an attribute type and the member within that attribute type group.*/ diff --git a/include/osg/StateSet b/include/osg/StateSet index b95323c39..b7cf690c6 100644 --- a/include/osg/StateSet +++ b/include/osg/StateSet @@ -259,31 +259,31 @@ class SG_EXPORT StateSet : public Object - /** Simple pairing between an attribute and its override flag.*/ + /** Simple pairing between a Uniform and its override flag.*/ typedef std::pair,StateAttribute::OverrideValue> RefUniformPair; - /** a container to map to their respective RefAttributePair.*/ + /** a container to map Uniform name to its respective RefUniformPair.*/ typedef std::map UniformList; /** Set this StateSet to contain specified uniform and override flag.*/ - void setUniform(Uniform* uniform, StateAttribute::OverrideValue value=StateAttribute::OFF); + void addUniform(Uniform* uniform, StateAttribute::OverrideValue value=StateAttribute::ON); /** remove uniform of specified name from StateSet.*/ void removeUniform(const std::string& name); - /** remove attribute from StateSet.*/ - void removeUniform(Uniform* attribute); + /** remove Uniform from StateSet.*/ + void removeUniform(Uniform* uniform); /** Get Uniform for specified name. - * Returns NULL if no matching uniform is contained within StateSet.*/ + * Returns NULL if no matching Uniform is contained within StateSet.*/ Uniform* getUniform(const std::string& name); /** Get const Uniform for specified name. - * Returns NULL if no matching uniform is contained within StateSet.*/ + * Returns NULL if no matching Uniform is contained within StateSet.*/ const Uniform* getUniform(const std::string& name) const; - /** Get specified RefUniformPair for specified type. - * Returns NULL if no type is contained within StateSet.*/ + /** Get specified RefUniformPair for specified Uniform name. + * Returns NULL if no Uniform is contained within StateSet.*/ const RefUniformPair* getUniformPair(const std::string& name) const; /** set the list of all Uniforms contained in this StateSet.*/ diff --git a/include/osg/Uniform b/include/osg/Uniform index cb318c27a..e96f5a406 100644 --- a/include/osg/Uniform +++ b/include/osg/Uniform @@ -11,7 +11,7 @@ */ /* file: include/osg/Uniform - * author: Mike Weiblen 2005-02-20 + * author: Mike Weiblen 2005-03-23 */ // NOTICE: This code is CLOSED during construction and/or renovation! @@ -190,13 +190,13 @@ class SG_EXPORT Uniform : public Object //TODO void set( const osg::Matrix3& m3 ); void set( const osg::Matrix& m4 ); void set( int i ); - //TODO void set( int i0, int i1 ); - //TODO void set( int i0, int i1, int i2 ); - //TODO void set( int i0, int i1, int i2, int i3 ); - //TODO void set( bool b ); - //TODO void set( bool b0, bool b1 ); - //TODO void set( bool b0, bool b1, bool b2 ); - //TODO void set( bool b0, bool b1, bool b2, bool b3 ); + void set( int i0, int i1 ); + void set( int i0, int i1, int i2 ); + void set( int i0, int i1, int i2, int i3 ); + void set( bool b ); + void set( bool b0, bool b1 ); + void set( bool b0, bool b1, bool b2 ); + void set( bool b0, bool b1, bool b2, bool b3 ); protected: @@ -231,21 +231,21 @@ class SG_EXPORT Uniform : public Object Uniform( const char* name, Value::Type type ); /** convenient construction w/ assignment */ - Uniform( const char* name, float f ); + explicit Uniform( const char* name, float f ); + explicit Uniform( const char* name, int i ); + explicit Uniform( const char* name, bool b ); Uniform( const char* name, const osg::Vec2& v2 ); Uniform( const char* name, const osg::Vec3& v3 ); Uniform( const char* name, const osg::Vec4& v4 ); //TODO Uniform( const char* name, const osg::Matrix2& m2 ); //TODO Uniform( const char* name, const osg::Matrix3& m3 ); Uniform( const char* name, const osg::Matrix& m4 ); - Uniform( const char* name, int i ); - //TODO Uniform( const char* name, int i0, int i1 ); - //TODO Uniform( const char* name, int i0, int i1, int i2 ); - //TODO Uniform( const char* name, int i0, int i1, int i2, int i3 ); - //TODO Uniform( const char* name, bool b ); - //TODO Uniform( const char* name, bool b0, bool b1 ); - //TODO Uniform( const char* name, bool b0, bool b1, bool b2 ); - //TODO Uniform( const char* name, bool b0, bool b1, bool b2, bool b3 ); + Uniform( const char* name, int i0, int i1 ); + Uniform( const char* name, int i0, int i1, int i2 ); + Uniform( const char* name, int i0, int i1, int i2, int i3 ); + Uniform( const char* name, bool b0, bool b1 ); + Uniform( const char* name, bool b0, bool b1, bool b2 ); + Uniform( const char* name, bool b0, bool b1, bool b2, bool b3 ); /** Copy constructor using CopyOp to manage deep vs shallow copy. */ Uniform(const Uniform& gu,const CopyOp& copyop=CopyOp::SHALLOW_COPY); @@ -268,20 +268,20 @@ class SG_EXPORT Uniform : public Object /** assignment */ bool set( float f ); + bool set( int i ); + bool set( bool b ); bool set( const osg::Vec2& v2 ); bool set( const osg::Vec3& v3 ); bool set( const osg::Vec4& v4 ); //TODO bool set( const osg::Matrix2& m2 ); //TODO bool set( const osg::Matrix3& m3 ); bool set( const osg::Matrix& m4 ); - bool set( int i ); - //TODO bool set( int i0, int i1 ); - //TODO bool set( int i0, int i1, int i2 ); - //TODO bool set( int i0, int i1, int i2, int i3 ); - //TODO bool set( bool b ); - //TODO bool set( bool b0, bool b1 ); - //TODO bool set( bool b0, bool b1, bool b2 ); - //TODO bool set( bool b0, bool b1, bool b2, bool b3 ); + bool set( int i0, int i1 ); + bool set( int i0, int i1, int i2 ); + bool set( int i0, int i1, int i2, int i3 ); + bool set( bool b0, bool b1 ); + bool set( bool b0, bool b1, bool b2 ); + bool set( bool b0, bool b1, bool b2, bool b3 ); protected: virtual ~Uniform() {} diff --git a/src/osg/Program.cpp b/src/osg/Program.cpp index 046240484..e4bcbc948 100644 --- a/src/osg/Program.cpp +++ b/src/osg/Program.cpp @@ -1,5 +1,6 @@ /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield * Copyright (C) 2003-2005 3Dlabs Inc. Ltd. + * Copyright (C) 2004-2005 Nathan Cournia * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial @@ -12,7 +13,7 @@ */ /* file: src/osg/Program.cpp - * author: Mike Weiblen 2005-02-20 + * author: Mike Weiblen 2005-03-23 */ // NOTICE: This code is CLOSED during construction and/or renovation! @@ -28,11 +29,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include @@ -41,15 +42,7 @@ using namespace osg; /////////////////////////////////////////////////////////////////////////// -// static cache of GL objects flagged for deletion, which will actually -// be deleted in the correct GL context. - -typedef std::list GlProgramHandleList; -typedef std::map DeletedGlProgramCache; - -static OpenThreads::Mutex s_mutex_deletedGL2ObjectCache; -static DeletedGlProgramCache s_deletedGlProgramCache; - +// Extension function pointers for OpenGL v2.0 GL2Extensions::GL2Extensions() { @@ -1855,19 +1848,28 @@ bool GL2Extensions::getShaderInfoLog( GLuint shader, std::string& result ) const return (strLen > 0); } +/////////////////////////////////////////////////////////////////////////// +// static cache of glPrograms flagged for deletion, which will actually +// be deleted in the correct GL context. -void Program::deleteProgram(unsigned int contextID, GLuint program) +typedef std::list GlProgramHandleList; +typedef std::map DeletedGlProgramCache; + +static OpenThreads::Mutex s_mutex_deletedGlProgramCache; +static DeletedGlProgramCache s_deletedGlProgramCache; + +void Program::deleteGlProgram(unsigned int contextID, GLuint program) { if( program ) { - OpenThreads::ScopedLock lock(s_mutex_deletedGL2ObjectCache); + OpenThreads::ScopedLock lock(s_mutex_deletedGlProgramCache); - // add program to the cache for the appropriate context. + // add glProgram to the cache for the appropriate context. s_deletedGlProgramCache[contextID].push_back(program); } } -void Program::flushDeletedGlslObjects(unsigned int contextID,double /*currentTime*/, double& availableTime) +void Program::flushDeletedGlPrograms(unsigned int contextID,double /*currentTime*/, double& availableTime) { // if no time available don't try to flush objects. if (availableTime<=0.0) return; @@ -1880,7 +1882,7 @@ void Program::flushDeletedGlslObjects(unsigned int contextID,double /*currentTim double elapsedTime = 0.0; { - OpenThreads::ScopedLock lock(s_mutex_deletedGL2ObjectCache); + OpenThreads::ScopedLock lock(s_mutex_deletedGlProgramCache); DeletedGlProgramCache::iterator citr = s_deletedGlProgramCache.find(contextID); if( citr != s_deletedGlProgramCache.end() ) @@ -1912,7 +1914,7 @@ Program::Program() Program::Program(const Program& rhs, const osg::CopyOp& copyop): - osg::Object(rhs, copyop) + osg::StateAttribute(rhs, copyop) { osg::notify(osg::FATAL) << "how got here?" << std::endl; } @@ -1920,19 +1922,15 @@ Program::Program(const Program& rhs, const osg::CopyOp& copyop): Program::~Program() { - for( unsigned int cxt=0; cxt < _pcpoList.size(); ++cxt ) + // inform any attached Shaders that we're going away + for( unsigned int i=0; i < _shaderList.size(); ++i ) { - if( ! _pcpoList[cxt] ) continue; - - PerContextProgObj* pcpo = _pcpoList[cxt].get(); - - deleteProgram( cxt, pcpo->getHandle() ); - // TODO add shader objects to delete list. - _pcpoList[cxt] = 0; + _shaderList[i]->removeProgramRef( this ); } } -int Program::compare(const osg::Program& sa) const + +int Program::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. @@ -1941,8 +1939,8 @@ int Program::compare(const osg::Program& sa) const if( _shaderList.size() < rhs._shaderList.size() ) return -1; if( rhs._shaderList.size() < _shaderList.size() ) return 1; - if( getComment() < rhs.getComment() ) return -1; - if( rhs.getComment() < getComment() ) return 1; + if( getName() < rhs.getName() ) return -1; + if( rhs.getName() < getName() ) return 1; ShaderList::const_iterator litr=_shaderList.begin(); ShaderList::const_iterator ritr=rhs._shaderList.begin(); @@ -1954,73 +1952,79 @@ int Program::compare(const osg::Program& sa) const if (result!=0) return result; } + // TODO should Program comparison depend on the values of + // its uniforms? I'd assert not. + return 0; // passed all the above comparison macro's, must be equal. } void Program::compileGLObjects( osg::State& state ) const { - if( _shaderList.empty() ) return; + if( isFixedFunction() ) return; const unsigned int contextID = state.getContextID(); - const GL2Extensions* extensions = GL2Extensions::Get(contextID,true); - if( ! extensions->isGlslSupported() ) - { - osg::notify(osg::WARN) << "GLSL not supported by OpenGL driver" << std::endl; - return; - } - PerContextProgObj* pcpo = getPCPO( contextID ); - for( unsigned int i=0; i < _shaderList.size() ; ++i ) + for( unsigned int i=0; i < _shaderList.size(); ++i ) { _shaderList[i]->compileShader( contextID ); } - pcpo->linkProgram(); - // TODO pcpo->releaseActiveUniformList(); - // TODO pcpo->buildActiveUniformList; - - // TODO regenerate pcpo->_activeAttribList; + getPCP( contextID )->linkProgram(); } void Program::dirtyProgram() { - // mark all PCPOs as needing a relink - for( unsigned int cxt=0; cxt < _pcpoList.size(); ++cxt ) + // mark our PCPs as needing relink + for( unsigned int cxt=0; cxt < _pcpList.size(); ++cxt ) { - if( ! _pcpoList[cxt] ) continue; - - PerContextProgObj* pcpo = _pcpoList[cxt].get(); - pcpo->markAsDirty(); + if( _pcpList[cxt].valid() ) _pcpList[cxt]->requestLink(); } } -void Program::dirtyShaders() -{ - // mark all attached Shaders as needing a rebuild - for( unsigned int i=0; i < _shaderList.size() ; ++i ) - { - _shaderList[i]->dirtyShader(); - } -} - void Program::releaseGLObjects(osg::State* state) const { - if (!state) const_cast(this)->dirtyShaders(); - else - { - unsigned int contextID = state->getContextID(); - const_cast(this)->_shaderList[contextID]->dirtyShader(); - } + // TODO } -void Program::addShader( Shader* shader ) +bool Program::addShader( Shader* shader ) { + // Shader can only be added once to a Program + for( unsigned int i=0; i < _shaderList.size(); ++i ) + { + if( shader == _shaderList[i].get() ) return false; + } + + shader->addProgramRef( this ); _shaderList.push_back( shader ); - shader->addProgObjRef( this ); + dirtyProgram(); + return true; +} + + +bool Program::removeShader( Shader* shader ) +{ + // Shader must exist to be removed. + for( unsigned int i=0; i < _shaderList.size(); ++i ) + { + if( shader == _shaderList[i].get() ) + { + shader->removeProgramRef( this ); + _shaderList[i] = 0; + dirtyProgram(); + return true; + } + } + return false; +} + + +void Program::bindAttribLocation( GLuint index, const char* name ) +{ + // TODO add to binding list dirtyProgram(); } @@ -2031,44 +2035,55 @@ void Program::apply( osg::State& state ) const const GL2Extensions* extensions = GL2Extensions::Get(contextID,true); if( ! extensions->isGlslSupported() ) return; - // A Program object having no attached Shaders is a special - // case: it indicates that programmable shading is to be disabled, - // and thus use GL 1.x "fixed functionality" rendering. - if( _shaderList.empty() ) + if( isFixedFunction() ) { extensions->glUseProgram( 0 ); return; } - PerContextProgObj* pcpo = getPCPO( contextID ); - if( pcpo->isDirty() ) compileGLObjects( state ); - - if( pcpo->isDirty() ) + PerContextProgram* pcp = getPCP( contextID ); + if( pcp->needsLink() ) compileGLObjects( state ); + if( pcp->isLinked() ) { - // _still_ dirty, something went wrong - extensions->glUseProgram( 0 ); + pcp->useProgram(); + pcp->applyUniforms( state ); } else { - pcpo->useProgram(); - pcpo->applyUniforms( state ); + // program not usable, fallback to fixed function. + extensions->glUseProgram( 0 ); } } -Program::PerContextProgObj* Program::getPCPO(unsigned int contextID) const +Program::PerContextProgram* Program::getPCP(unsigned int contextID) const { - if( ! _pcpoList[contextID].valid() ) + if( ! _pcpList[contextID].valid() ) { - _pcpoList[contextID] = new PerContextProgObj( this, contextID ); + _pcpList[contextID] = new PerContextProgram( this, contextID ); - // attach all PCSOs to this new PCPO - for( unsigned int i=0; i < _shaderList.size() ; ++i ) + // attach all PCSs to this new PCP + for( unsigned int i=0; i < _shaderList.size(); ++i ) { - _shaderList[i]->attachShader( contextID, _pcpoList[contextID]->getHandle() ); + _shaderList[i]->attachShader( contextID, _pcpList[contextID]->getHandle() ); } } - return _pcpoList[contextID].get(); + return _pcpList[contextID].get(); +} + + +bool Program::isFixedFunction() const +{ + // A Program object having no attached Shaders is a special case: + // it indicates that programmable shading is to be disabled, + // and thus use GL 1.x "fixed functionality" rendering. + return _shaderList.empty(); +} + + +void Program::getGlProgramInfoLog(unsigned int contextID, std::string& log) const +{ + getPCP( contextID )->getInfoLog( log ); } @@ -2083,67 +2098,69 @@ Program::ActiveUniform::ActiveUniform( const GLchar* name, GLenum type ) : /////////////////////////////////////////////////////////////////////////// -// osg::Program::PerContextProgObj -// PCPO is an OSG abstraction of the per-context glProgram +// osg::Program::PerContextProgram +// PCP is an OSG abstraction of the per-context glProgram /////////////////////////////////////////////////////////////////////////// -Program::PerContextProgObj::PerContextProgObj(const Program* program, unsigned int contextID ) : +Program::PerContextProgram::PerContextProgram(const Program* program, unsigned int contextID ) : osg::Referenced(), _contextID( contextID ) { _program = program; _extensions = GL2Extensions::Get( _contextID, true ); _glProgramHandle = _extensions->glCreateProgram(); - markAsDirty(); + requestLink(); } -Program::PerContextProgObj::PerContextProgObj(const PerContextProgObj& rhs) : - osg::Referenced(), - _contextID( rhs._contextID ) -{ - _program = rhs._program; - _extensions = rhs._extensions; - _glProgramHandle = rhs._glProgramHandle ; - _dirty = rhs._dirty; -} - -Program::PerContextProgObj::~PerContextProgObj() +Program::PerContextProgram::~PerContextProgram() { + Program::deleteGlProgram( _contextID, _glProgramHandle ); } -void Program::PerContextProgObj::markAsDirty() +void Program::PerContextProgram::requestLink() { - // TODO releaseActiveUniformList(); - _dirty = true; + _needsLink = true; + _isLinked = false; } -void Program::PerContextProgObj::linkProgram() +void Program::PerContextProgram::linkProgram() { + if( ! _needsLink ) return; + _needsLink = false; + + // TODO for( each itr in binding list ) + // { + // _extensions->glBindAttribLocation( _glProgramHandle, index, name ); + // } + GLint linked; - _extensions->glLinkProgram( _glProgramHandle ); _extensions->glGetProgramiv( _glProgramHandle, GL_LINK_STATUS, &linked ); - _dirty = (linked == 0); - if( _dirty ) + _isLinked = (linked == GL_TRUE); + if( ! _isLinked ) { - // _still_ dirty, something went wrong + // link failed std::string infoLog; - _extensions->getProgramInfoLog( _glProgramHandle, infoLog ); + getInfoLog( infoLog ); osg::notify(osg::WARN) << "glLinkProgram FAILED:\n" << infoLog << std::endl; } } +void Program::PerContextProgram::getInfoLog( std::string& infoLog ) const +{ + _extensions->getProgramInfoLog( _glProgramHandle, infoLog ); +} -void Program::PerContextProgObj::useProgram() const +void Program::PerContextProgram::useProgram() const { _extensions->glUseProgram( _glProgramHandle ); } -void Program::PerContextProgObj::applyUniforms( osg::State& /*state*/ ) const +void Program::PerContextProgram::applyUniforms( osg::State& /*state*/ ) const { bool uniformDoesNeedSetting = true; @@ -2161,3 +2178,5 @@ void Program::PerContextProgObj::applyUniforms( osg::State& /*state*/ ) const // TODO set the uniform value on the currently active glProgram. } } + +/*EOF*/ diff --git a/src/osg/Shader.cpp b/src/osg/Shader.cpp index 48e9ac95c..f4059fd9a 100644 --- a/src/osg/Shader.cpp +++ b/src/osg/Shader.cpp @@ -1,5 +1,6 @@ /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield * Copyright (C) 2003-2005 3Dlabs Inc. Ltd. + * Copyright (C) 2004-2005 Nathan Cournia * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial @@ -11,8 +12,8 @@ * */ -/* file: src/osg/Program.cpp - * author: Mike Weiblen 2005-02-20 +/* file: src/osg/Shader.cpp + * author: Mike Weiblen 2005-03-23 */ // NOTICE: This code is CLOSED during construction and/or renovation! @@ -39,12 +40,61 @@ using namespace osg; +/////////////////////////////////////////////////////////////////////////// +// static cache of glShaders flagged for deletion, which will actually +// be deleted in the correct GL context. + typedef std::list GlShaderHandleList; typedef std::map DeletedGlShaderCache; -static OpenThreads::Mutex s_mutex_deletedGL2ShaderCache; +static OpenThreads::Mutex s_mutex_deletedGlShaderCache; static DeletedGlShaderCache s_deletedGlShaderCache; +void Shader::deleteGlShader(unsigned int contextID, GLuint shader) +{ + if( shader ) + { + OpenThreads::ScopedLock lock(s_mutex_deletedGlShaderCache); + + // add glShader to the cache for the appropriate context. + s_deletedGlShaderCache[contextID].push_back(shader); + } +} + +void Shader::flushDeletedGlShaders(unsigned int contextID,double /*currentTime*/, double& availableTime) +{ + // if no time available don't try to flush objects. + if (availableTime<=0.0) return; + + const GL2Extensions* extensions = GL2Extensions::Get(contextID,true); + if( ! extensions->isGlslSupported() ) return; + + const osg::Timer& timer = *osg::Timer::instance(); + osg::Timer_t start_tick = timer.tick(); + double elapsedTime = 0.0; + + { + OpenThreads::ScopedLock lock(s_mutex_deletedGlShaderCache); + + DeletedGlShaderCache::iterator citr = s_deletedGlShaderCache.find(contextID); + if( citr != s_deletedGlShaderCache.end() ) + { + GlShaderHandleList& pList = citr->second; + for(GlShaderHandleList::iterator titr=pList.begin(); + titr!=pList.end() && elapsedTimeglDeleteShader( *titr ); + titr = pList.erase( titr ); + elapsedTime = timer.delta_s(start_tick,timer.tick()); + } + } + } + + availableTime -= elapsedTime; +} + + /////////////////////////////////////////////////////////////////////////// // osg::Shader /////////////////////////////////////////////////////////////////////////// @@ -55,54 +105,49 @@ Shader::Shader(Type type, const char* sourceText) : setShaderSource( sourceText ); } -Shader::Shader(const Shader& rhs, const osg::CopyOp& copyop): - Object( rhs, copyop ), - _type(rhs._type) +Shader::Shader() : + _type(VERTEX) +{ + // TODO this default constructor is inappropriate for the Shader class. + // It exists for now because it is required by META_OBject + osg::notify(osg::FATAL) << "how got here?" << std::endl; +} + +Shader::Shader(const Shader& rhs, const osg::CopyOp& copyop): + osg::Object( rhs, copyop ), + _type(rhs._type), + _name(rhs._name), + _shaderSource(rhs._shaderSource) { - /*TODO*/ } Shader::~Shader() { - /*TODO*/ } + int Shader::compare(const Shader& rhs) const { if( this == &rhs ) return 0; - if( getComment() < rhs.getComment() ) return -1; - if( rhs.getComment() < getComment() ) return 1; + 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; return 0; } -void Shader::dirtyShader() -{ - // mark each PCSO as needing a recompile - for( unsigned int cxt=0; cxt < _pcsoList.size(); ++cxt ) - { - if( ! _pcsoList[cxt] ) continue; - - PerContextShaderObj* pcso = _pcsoList[cxt].get(); - pcso->markAsDirty(); - } - - // also mark-as-dirty the Programs we're attach to - for( unsigned int i=0; i < _programList.size(); ++i ) - { - _programList[i]->dirtyProgram(); - } -} - void Shader::setShaderSource( const char* sourceText ) { _shaderSource = ( sourceText ? sourceText : "" ); dirtyShader(); } + bool Shader::loadShaderSourceFromFile( const char* fileName ) { std::ifstream sourceFile; @@ -143,97 +188,138 @@ const char* Shader::getTypename() const void Shader::compileShader( unsigned int contextID ) const { - PerContextShaderObj* pcso = getPCSO( contextID ); - if( pcso->isDirty() ) pcso->compileShader(); + getPCS( contextID )->compileShader(); } -Shader::PerContextShaderObj* Shader::getPCSO(unsigned int contextID) const +Shader::PerContextShader* Shader::getPCS(unsigned int contextID) const { - if( ! _pcsoList[contextID].valid() ) + if( ! _pcsList[contextID].valid() ) { - _pcsoList[contextID] = new PerContextShaderObj( this, contextID ); + _pcsList[contextID] = new PerContextShader( this, contextID ); } - return _pcsoList[contextID].get(); + return _pcsList[contextID].get(); } void Shader::attachShader(unsigned int contextID, GLuint program) const { - getPCSO( contextID )->attachShader( program ); + getPCS( contextID )->attachShader( program ); } -void Shader::addProgObjRef( Program* program ) +void Shader::getGlShaderInfoLog(unsigned int contextID, std::string& log) const { - _programList.push_back( program ); + getPCS( contextID )->getInfoLog( log ); } -void Shader::deleteShader(unsigned int contextID, GLuint shader) +///////////////////////////////////////////////////////////////////////// +// 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 ) { - if( shader ) + 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 ) { - OpenThreads::ScopedLock lock(s_mutex_deletedGL2ShaderCache); + if( _pcsList[cxt].valid() ) _pcsList[cxt]->requestCompile(); + } - // add shader to the cache for the appropriate context. - s_deletedGlShaderCache[contextID].push_back(shader); + // Also mark Programs that depend on us as needing relink. + for( ProgramSet::iterator itr = _programSet.begin(); + itr != _programSet.end(); ++itr ) + { + (*itr)->dirtyProgram(); } } -/////////////////////////////////////////////////////////////////////////// -// osg::Shader::PerContextShaderObj -// PCSO is the OSG abstraction of the per-context glShader +///////////////////////////////////////////////////////////////////////// +// osg::Shader::PerContextShader +// PCS is the OSG abstraction of the per-context glShader /////////////////////////////////////////////////////////////////////////// -Shader::PerContextShaderObj::PerContextShaderObj(const Shader* shader, unsigned int contextID) : +Shader::PerContextShader::PerContextShader(const Shader* shader, unsigned int contextID) : osg::Referenced(), _contextID( contextID ) { _shader = shader; _extensions = GL2Extensions::Get( _contextID, true ); _glShaderHandle = _extensions->glCreateShader( shader->getType() ); - markAsDirty(); + requestCompile(); } -Shader::PerContextShaderObj::PerContextShaderObj(const PerContextShaderObj& rhs) : - osg::Referenced(), - _contextID( rhs._contextID ) + +Shader::PerContextShader::~PerContextShader() { - _shader = rhs._shader; - _extensions = rhs._extensions; - _glShaderHandle = rhs._glShaderHandle; - _dirty = rhs._dirty; + Shader::deleteGlShader( _contextID, _glShaderHandle ); } -Shader::PerContextShaderObj::~PerContextShaderObj() + +void Shader::PerContextShader::requestCompile() { + _needsCompile = true; + _isCompiled = false; } -void Shader::PerContextShaderObj::compileShader() + +void Shader::PerContextShader::compileShader() { + if( ! _needsCompile ) return; + _needsCompile = false; + GLint compiled; const char* sourceText = _shader->getShaderSource().c_str(); - _extensions->glShaderSource( _glShaderHandle, 1, &sourceText, NULL ); _extensions->glCompileShader( _glShaderHandle ); _extensions->glGetShaderiv( _glShaderHandle, GL_COMPILE_STATUS, &compiled ); - _dirty = (compiled == 0); - if( _dirty ) + _isCompiled = (compiled == GL_TRUE); + if( ! _isCompiled ) { - // _still_ dirty, something went wrong + // compile failed std::string infoLog; - _extensions->getShaderInfoLog( _glShaderHandle, infoLog ); + getInfoLog( infoLog ); osg::notify(osg::WARN) << _shader->getTypename() << " glCompileShader FAILED:\n" << infoLog << std::endl; } } -void Shader::PerContextShaderObj::attachShader(GLuint program) const +void Shader::PerContextShader::getInfoLog( std::string& infoLog ) const +{ + _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 ); +} + /*EOF*/ diff --git a/src/osg/StateSet.cpp b/src/osg/StateSet.cpp index 79a63779a..22ed850ad 100644 --- a/src/osg/StateSet.cpp +++ b/src/osg/StateSet.cpp @@ -29,7 +29,6 @@ #include - using namespace osg; // local class to help porting from OSG0.8.x to 0.9.x @@ -113,7 +112,22 @@ StateSet::StateSet(const StateSet& rhs,const CopyOp& copyop):Object(rhs,copyop) if (attr) lhs_attributeList[typemember]=RefAttributePair(attr,rap.second); } } - + +#if 0 //[ TODO + // copy uniform values + for(UniformList::const_iterator rhs_uitr = rhs._uniformList.begin(); + rhs_uitr != rhs._uniformList.end(); + ++rhs_uitr) + { + const std::string& name = rhs_uitr->first; + const RefUniformPair& rup = rhs_uitr->second; + Uniform* uni = copyop(rup.first.get()); + if( uni ) _uniformList[name] = RefUniformPair(uni, rup.second); + } + + Program* prog = copyop( rhs._program ); + if( prog ) _program = prog; +#endif //] _renderingHint = rhs._renderingHint; @@ -410,6 +424,9 @@ void StateSet::clear() _textureModeList.clear(); _textureAttributeList.clear(); + + _uniformList.clear(); + _program = 0; } @@ -521,6 +538,29 @@ void StateSet::merge(const StateSet& rhs) } } + // merge the uniforms of rhs into this, + // this overrides rhs if OVERRIDE defined in this. + for(UniformList::const_iterator rhs_uitr = rhs._uniformList.begin(); + rhs_uitr != rhs._uniformList.end(); + ++rhs_uitr) + { + UniformList::iterator lhs_uitr = _uniformList.find(rhs_uitr->first); + if (lhs_uitr!=_uniformList.end()) + { + if (!(lhs_uitr->second.second & StateAttribute::OVERRIDE)) + { + // override isn't on in rhs, so overrite it with incomming + // value. + lhs_uitr->second = rhs_uitr->second; + } + } + else + { + // entry doesn't exist so insert it. + _uniformList.insert(*rhs_uitr); + } + } + // need to merge rendering hints // but will need to think how best to do this first // RO, Nov. 2001. @@ -662,7 +702,7 @@ const StateSet::RefAttributePair* StateSet::getAttributePair(StateAttribute::Typ return getAttributePair(_attributeList,type,member); } -void StateSet::setUniform(Uniform* uniform, StateAttribute::OverrideValue value) +void StateSet::addUniform(Uniform* uniform, StateAttribute::OverrideValue value) { if (uniform) { diff --git a/src/osg/Uniform.cpp b/src/osg/Uniform.cpp index 675f6da03..94e48a23b 100644 --- a/src/osg/Uniform.cpp +++ b/src/osg/Uniform.cpp @@ -11,7 +11,7 @@ */ /* file: src/osg/Uniform.cpp - * author: Mike Weiblen 2005-02-20 + * author: Mike Weiblen 2005-03-23 */ // NOTICE: This code is CLOSED during construction and/or renovation! @@ -368,6 +368,13 @@ Uniform::Uniform( const char* name, Value::Type type ) : {} +Uniform::Uniform( const Uniform& gu, const CopyOp& copyop ) : + Object(gu,copyop), + _value( gu._value ) +{ +} + + Uniform::Uniform( const char* name, float f ) : _value( name, Value::FLOAT ) { @@ -423,13 +430,6 @@ Uniform::Uniform( const char* name, int i ) : //TODO Uniform::Uniform( const char* name, bool b0, bool b1, bool b2, bool b3 ) -Uniform::Uniform( const Uniform& gu, const CopyOp& copyop ) : - Object(gu,copyop), - _value( gu._value ) -{ -} - - bool Uniform::set( float f ) { if( ! isCompatibleType( Value::FLOAT ) ) return false; @@ -503,5 +503,4 @@ bool Uniform::isCompatibleType( Value::Type t ) const return false; } - /*EOF*/