From 7883574d28c67d0670243602f8ec080e970277fe Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 24 Mar 2005 09:37:45 +0000 Subject: [PATCH] From Mike Weiblen, "updates for GLSL core integration: Code compiles and runs on win32. Basic functionality of Program and Shader in place. Program derived from StateAttribute. Uniform value propagation is not yet functional (in development) Includes some patches by Nathan Cournia. includes example testcase to demo use of new classes." --- include/osg/Program | 126 ++++++++++--------- include/osg/Shader | 97 +++++++++------ include/osg/StateAttribute | 3 + include/osg/StateSet | 18 +-- include/osg/Uniform | 50 ++++---- src/osg/Program.cpp | 239 ++++++++++++++++++++----------------- src/osg/Shader.cpp | 210 ++++++++++++++++++++++---------- src/osg/StateSet.cpp | 46 ++++++- src/osg/Uniform.cpp | 17 ++- 9 files changed, 492 insertions(+), 314 deletions(-) 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*/