From 1d55efb72109d02313b949f8ccdbee6fc0050a7f Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 14 Feb 2011 12:54:21 +0000 Subject: [PATCH] From Michael Platings, I've added initial support to osg for glGetProgramBinary and glProgramBinary. This means that shader programs can now be cached to disk and later reloaded, which is much faster than linking shaders from source code. This should mean significantly shorter load times for people who use lots of combinations of shaders. --- include/osg/GL2Extensions | 23 +++++ include/osg/Program | 71 +++++++++++++++ include/osg/StateSet | 3 - src/osg/GL2Extensions.cpp | 47 +++++++++- src/osg/Program.cpp | 176 +++++++++++++++++++++++++++----------- 5 files changed, 266 insertions(+), 54 deletions(-) diff --git a/include/osg/GL2Extensions b/include/osg/GL2Extensions index b999288ac..8137951bf 100644 --- a/include/osg/GL2Extensions +++ b/include/osg/GL2Extensions @@ -284,6 +284,13 @@ typedef char GLchar; #define GL_INVALID_INDEX 0xFFFFFFFFu #endif +//ARB_get_program_binary +#ifndef GL_PROGRAM_BINARY_RETRIEVABLE_HINT +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#endif namespace osg { @@ -326,6 +333,10 @@ class OSG_EXPORT GL2Extensions : public osg::Referenced void setUniformBufferObjectSupported(bool flag) { _isUniformBufferObjectSupported = flag; } bool isUniformBufferObjectSupported() {return _isUniformBufferObjectSupported; } + + void setGetProgramBinarySupported(bool flag) { _isGetProgramBinarySupported = flag; } + bool isGetProgramBinarySupported() {return _isGetProgramBinarySupported; } + /** Function to call to get the extension of a specified context. * If the Exentsion object for that context has not yet been created then * and the 'createIfNotInitalized' flag been set to false then returns NULL. @@ -480,6 +491,11 @@ class OSG_EXPORT GL2Extensions : public osg::Referenced void glGetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params) const; void glGetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) const; void glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) const; + + // ARB_get_program_binary + void glGetProgramBinary(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary) const; + void glProgramBinary(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLsizei length) const; + protected: ~GL2Extensions() {} @@ -494,6 +510,7 @@ class OSG_EXPORT GL2Extensions : public osg::Referenced bool _areTessellationShadersSupported; bool _isGpuShader4Supported; bool _isUniformBufferObjectSupported; + bool _isGetProgramBinarySupported; typedef void (GL_APIENTRY * BlendEquationSeparateProc)(GLenum modeRGB, GLenum modeAlpha); typedef void (GL_APIENTRY * DrawBuffersProc)(GLsizei n, const GLenum *bufs); @@ -622,6 +639,8 @@ class OSG_EXPORT GL2Extensions : public osg::Referenced typedef void (GL_APIENTRY * GetActiveUniformBlockivProc)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); typedef void (GL_APIENTRY * GetActiveUniformBlockNameProc)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); typedef void (GL_APIENTRY * UniformBlockBindingProc)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); + typedef void (GL_APIENTRY * GetProgramBinaryProc)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary); + typedef void (GL_APIENTRY * ProgramBinaryProc)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLsizei length); BlendEquationSeparateProc _glBlendEquationSeparate; DrawBuffersProc _glDrawBuffers; @@ -761,6 +780,10 @@ class OSG_EXPORT GL2Extensions : public osg::Referenced GetActiveUniformBlockivProc _glGetActiveUniformBlockiv; GetActiveUniformBlockNameProc _glGetActiveUniformBlockName; UniformBlockBindingProc _glUniformBlockBinding; + + //ARB_get_program_binary + GetProgramBinaryProc _glGetProgramBinary; + ProgramBinaryProc _glProgramBinary; }; } diff --git a/include/osg/Program b/include/osg/Program index 75be77a2b..173b99bf6 100644 --- a/include/osg/Program +++ b/include/osg/Program @@ -35,6 +35,51 @@ namespace osg { class State; +/** Simple class for wrapping up the data used in glProgramBinary and glGetProgramBinary. + * On the first run of your application Programs should be assigned an empty ProgramBinary. + * Before your application exits it should retrieve the program binary via + * Program::PerContextProgram::compileProgramBinary and save it to disk. + * When your application is run subsequently, load your binary from disk and use it to set + * the data of a ProgramBinary, and set the ProgramBinary on the associated Program. + * This will typically result in Program::compileGLObjects executing much faster.*/ +class OSG_EXPORT ProgramBinary : public osg::Object +{ + public: + + ProgramBinary(); + + /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ + ProgramBinary(const ProgramBinary& rhs, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); + + META_Object(osg, ProgramBinary); + + /** Allocated a data buffer of specified size/*/ + void allocate(unsigned int size); + + /** Assign program binary data, copying the specified data into locally stored data buffer, the original data can then be deleted.*/ + void assign(unsigned int size, const unsigned char* data); + + /** Set the format of the program binary data.*/ + void setFormat(GLenum format) {_format = format;} + + /** Get the format of the program binary data.*/ + GLenum getFormat() const {return _format;} + + /** Get the size of the program binary data.*/ + unsigned int getSize() const { return _data.size(); } + + /** Get a ptr to the program binary data.*/ + unsigned char* getData() { return _data.empty() ? 0 : &(_data.front()); } + + /** Get a const ptr to the program binary data.*/ + const unsigned char* getData() const { return _data.empty() ? 0 : &(_data.front()); } + + protected: + std::vector _data; + GLenum _format; +}; + + /////////////////////////////////////////////////////////////////////////// /** osg::Program is an application-level abstraction of an OpenGL glProgram. * It is an osg::StateAttribute that, when applied, will activate a @@ -122,6 +167,17 @@ class OSG_EXPORT Program : public osg::StateAttribute /** Remove a uniform block binding. */ void removeBindUniformBlock(const std::string& name); + /** Set the Program using a ProgramBinary. If a ProgramBinary is not yet + * available then setting an empty one signals that compileProgramBinary + * will be called later.*/ + void setProgramBinary(ProgramBinary* programBinary) { _programBinary = programBinary; } + + /** Get the Program's ProgramBinary, return NULL if none is assigned. */ + ProgramBinary* getProgramBinary() { return _programBinary.get(); } + + /** Get the const Program's ProgramBinary, return NULL if none is assigned. */ + const ProgramBinary* getProgramBinary() const { return _programBinary.get(); } + typedef std::map AttribBindingList; typedef std::map FragDataBindingList; typedef std::map UniformBlockBindingList; @@ -196,6 +252,17 @@ class OSG_EXPORT Program : public osg::StateAttribute bool isLinked() const {return _isLinked;} bool getInfoLog( std::string& infoLog ) const; + /** Was glProgramBinary called successfully? */ + bool loadedBinary() const {return _loadedBinary;} + + /** Compile a program binary. For this to work setProgramBinary must have + * been called on the osg::Program with an empty ProgramBinary prior to + * compileGLObjects being called. + * compileProgramBinary should be called after the program has been + * "exercised" by rendering with it. The ProgramBinary can then be saved + * to disk for faster subsequent compiling. */ + ProgramBinary* compileProgramBinary(osg::State& state); + void useProgram() const; void resetAppliedUniforms() const @@ -275,6 +342,8 @@ class OSG_EXPORT Program : public osg::StateAttribute bool _needsLink; /** Is our glProgram successfully linked? */ bool _isLinked; + /** Was glProgramBinary called successfully? */ + bool _loadedBinary; const unsigned int _contextID; ActiveUniformMap _uniformInfoMap; @@ -311,6 +380,8 @@ class OSG_EXPORT Program : public osg::StateAttribute typedef std::vector< ref_ptr > ShaderList; ShaderList _shaderList; + osg::ref_ptr _programBinary; + /** Parameters maintained with glProgramParameteriEXT */ GLint _geometryVerticesOut; GLint _geometryInputType; diff --git a/include/osg/StateSet b/include/osg/StateSet index 2bbe260ad..5b4752bcd 100644 --- a/include/osg/StateSet +++ b/include/osg/StateSet @@ -17,10 +17,7 @@ #include #include #include - #include -#include - #include #include diff --git a/src/osg/GL2Extensions.cpp b/src/osg/GL2Extensions.cpp index 9f837b6ac..030ca6851 100644 --- a/src/osg/GL2Extensions.cpp +++ b/src/osg/GL2Extensions.cpp @@ -48,6 +48,7 @@ GL2Extensions::GL2Extensions(const GL2Extensions& rhs) : osg::Referenced() _isGeometryShader4Supported = rhs._isGeometryShader4Supported; _isGpuShader4Supported = rhs._isGpuShader4Supported; _isUniformBufferObjectSupported = rhs._isUniformBufferObjectSupported; + _isGetProgramBinarySupported = rhs._isGetProgramBinarySupported; _glBlendEquationSeparate = rhs._glBlendEquationSeparate; _glDrawBuffers = rhs._glDrawBuffers; @@ -183,6 +184,10 @@ GL2Extensions::GL2Extensions(const GL2Extensions& rhs) : osg::Referenced() _glGetActiveUniformBlockiv = rhs._glGetActiveUniformBlockiv; _glGetActiveUniformBlockName = rhs._glGetActiveUniformBlockName; _glUniformBlockBinding = rhs._glUniformBlockBinding; + + // ARB_get_program_binary + _glGetProgramBinary = rhs._glGetProgramBinary; + _glProgramBinary = rhs._glProgramBinary; } @@ -338,6 +343,9 @@ void GL2Extensions::lowestCommonDenominator(const GL2Extensions& rhs) if (!rhs._glGetActiveUniformBlockName) _glGetActiveUniformBlockName = 0; if (!rhs._glUniformBlockBinding) _glUniformBlockBinding = 0; + // ARB_get_program_binary + if (!rhs._glGetProgramBinary) _glGetProgramBinary = 0; + if (!rhs._glProgramBinary) _glProgramBinary = 0; } @@ -362,7 +370,8 @@ void GL2Extensions::setupGL2Extensions(unsigned int contextID) _isGeometryShader4Supported = osg::isGLExtensionSupported(contextID,"GL_EXT_geometry_shader4"); _isGpuShader4Supported = osg::isGLExtensionSupported(contextID,"GL_EXT_gpu_shader4"); _areTessellationShadersSupported = osg::isGLExtensionSupported(contextID, "GL_ARB_tessellation_shader"); - _isUniformBufferObjectSupported = osg::isGLExtensionSupported(contextID,"ARB_uniform_buffer_object"); + _isUniformBufferObjectSupported = osg::isGLExtensionSupported(contextID,"GL_ARB_uniform_buffer_object"); + _isGetProgramBinarySupported = osg::isGLExtensionSupported(contextID,"GL_ARB_get_program_binary"); if( isGlslSupported() ) { @@ -520,7 +529,9 @@ void GL2Extensions::setupGL2Extensions(unsigned int contextID) setGLExtensionFuncPtr(_glGetActiveUniformBlockiv, "glGetActiveUniformBlockiv"); setGLExtensionFuncPtr(_glGetActiveUniformBlockName, "glGetActiveUniformBlockName"); setGLExtensionFuncPtr(_glUniformBlockBinding, "glUniformBlockBinding"); - + //ARB_get_program_binary + setGLExtensionFuncPtr(_glGetProgramBinary, "glGetProgramBinary"); + setGLExtensionFuncPtr(_glProgramBinary, "glProgramBinary"); } @@ -2287,6 +2298,38 @@ void GL2Extensions::glUniformBlockBinding(GLuint program, } } +//ARB_get_program_binary +void GL2Extensions::glGetProgramBinary(GLuint program, + GLsizei bufSize, + GLsizei *length, + GLenum *binaryFormat, + GLvoid *binary) const +{ + if (_glGetProgramBinary) + { + _glGetProgramBinary(program, bufSize, length, binaryFormat, binary); + } + else + { + NotSupported("glGetProgramBinary"); + } +} + +void GL2Extensions::glProgramBinary(GLuint program, + GLenum binaryFormat, + const GLvoid *binary, + GLsizei length) const +{ + if (_glProgramBinary) + { + _glProgramBinary(program, binaryFormat, binary, length); + } + else + { + NotSupported("glProgramBinary"); + } +} + /////////////////////////////////////////////////////////////////////////// // C++-friendly convenience methods diff --git a/src/osg/Program.cpp b/src/osg/Program.cpp index 7baff3833..ccbe3b25a 100644 --- a/src/osg/Program.cpp +++ b/src/osg/Program.cpp @@ -20,6 +20,7 @@ */ #include +#include #include #include @@ -93,6 +94,38 @@ void Program::discardDeletedGlPrograms(unsigned int contextID) } +/////////////////////////////////////////////////////////////////////////// +// osg::ProgramBinary +/////////////////////////////////////////////////////////////////////////// + +ProgramBinary::ProgramBinary() : _format(0) +{ +} + +ProgramBinary::ProgramBinary(const ProgramBinary& rhs, const osg::CopyOp&) : + _data(rhs._data), _format(rhs._format) +{ +} + +void ProgramBinary::allocate(unsigned int size) +{ + _data.clear(); + _data.resize(size); +} + +void ProgramBinary::assign(unsigned int size, const unsigned char* data) +{ + allocate(size); + if (data) + { + for(unsigned int i=0; isecond<<", "<first<glBindAttribLocation( _glProgramHandle, itr->second, reinterpret_cast(itr->first.c_str()) ); - } - - // set any explicit vertex attribute bindings that are set up via osg::State, such as the vertex arrays - // that have been aliase to vertex attrib arrays - if (state.getUseVertexAttributeAliasing()) - { - const AttribBindingList& stateBindlist = state.getAttributeBindingList(); - for( AttribBindingList::const_iterator itr = stateBindlist.begin(); - itr != stateBindlist.end(); ++itr ) + // set any explicit vertex attribute bindings + const AttribBindingList& programBindlist = _program->getAttribBindingList(); + for( AttribBindingList::const_iterator itr = programBindlist.begin(); + itr != programBindlist.end(); ++itr ) { - OSG_INFO<<"State's vertex attrib binding "<second<<", "<first<second<<", "<first<glBindAttribLocation( _glProgramHandle, itr->second, reinterpret_cast(itr->first.c_str()) ); } + + // set any explicit vertex attribute bindings that are set up via osg::State, such as the vertex arrays + // that have been aliase to vertex attrib arrays + if (state.getUseVertexAttributeAliasing()) + { + const AttribBindingList& stateBindlist = state.getAttributeBindingList(); + for( AttribBindingList::const_iterator itr = stateBindlist.begin(); + itr != stateBindlist.end(); ++itr ) + { + OSG_INFO<<"State's vertex attrib binding "<second<<", "<first<glBindAttribLocation( _glProgramHandle, itr->second, reinterpret_cast(itr->first.c_str()) ); + } + } + + // set any explicit frag data bindings + const FragDataBindingList& fdbindlist = _program->getFragDataBindingList(); + for( FragDataBindingList::const_iterator itr = fdbindlist.begin(); + itr != fdbindlist.end(); ++itr ) + { + _extensions->glBindFragDataLocation( _glProgramHandle, itr->second, reinterpret_cast(itr->first.c_str()) ); + } + + // if any program binary has been set then assume we want to retrieve a binary later. + if (programBinary) + { + _extensions->glProgramParameteri( _glProgramHandle, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE ); + } + + // link the glProgram + GLint linked = GL_FALSE; + _extensions->glLinkProgram( _glProgramHandle ); + _extensions->glGetProgramiv( _glProgramHandle, GL_LINK_STATUS, &linked ); + _isLinked = (linked == GL_TRUE); } - // set any explicit frag data bindings - const FragDataBindingList& fdbindlist = _program->getFragDataBindingList(); - for( FragDataBindingList::const_iterator itr = fdbindlist.begin(); - itr != fdbindlist.end(); ++itr ) - { - _extensions->glBindFragDataLocation( _glProgramHandle, itr->second, reinterpret_cast(itr->first.c_str()) ); - } - - // link the glProgram - GLint linked = GL_FALSE; - _extensions->glLinkProgram( _glProgramHandle ); - _extensions->glGetProgramiv( _glProgramHandle, GL_LINK_STATUS, &linked ); - _isLinked = (linked == GL_TRUE); if( ! _isLinked ) { OSG_WARN << "glLinkProgram \""<< _program->getName() << "\" FAILED" << std::endl; @@ -600,7 +661,7 @@ void Program::PerContextProgram::linkProgram(osg::State& state) if( getInfoLog(infoLog) ) { OSG_INFO << "Program \""<< _program->getName() << "\" "<< - "link succeded, infolog:\n" << infoLog << std::endl; + "link succeeded, infolog:\n" << infoLog << std::endl; } } @@ -747,6 +808,23 @@ bool Program::PerContextProgram::getInfoLog( std::string& infoLog ) const return _extensions->getProgramInfoLog( _glProgramHandle, infoLog ); } +ProgramBinary* Program::PerContextProgram::compileProgramBinary(osg::State& state) +{ + linkProgram(state); + GLint binaryLength = 0; + _extensions->glGetProgramiv( _glProgramHandle, GL_PROGRAM_BINARY_LENGTH, &binaryLength ); + if (binaryLength) + { + ProgramBinary* programBinary = new ProgramBinary; + programBinary->allocate(binaryLength); + GLenum binaryFormat = 0; + _extensions->glGetProgramBinary( _glProgramHandle, binaryLength, 0, &binaryFormat, reinterpret_cast(programBinary->getData()) ); + programBinary->setFormat(binaryFormat); + return programBinary; + } + return 0; +} + void Program::PerContextProgram::useProgram() const { _extensions->glUseProgram( _glProgramHandle );