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.
This commit is contained in:
parent
597f978128
commit
1d55efb721
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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<unsigned char> _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<std::string,GLuint> AttribBindingList;
|
||||
typedef std::map<std::string,GLuint> FragDataBindingList;
|
||||
typedef std::map<std::string,GLuint> 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<Shader> > ShaderList;
|
||||
ShaderList _shaderList;
|
||||
|
||||
osg::ref_ptr<ProgramBinary> _programBinary;
|
||||
|
||||
/** Parameters maintained with glProgramParameteriEXT */
|
||||
GLint _geometryVerticesOut;
|
||||
GLint _geometryInputType;
|
||||
|
@ -17,10 +17,7 @@
|
||||
#include <osg/Object>
|
||||
#include <osg/StateAttribute>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <osg/Uniform>
|
||||
#include <osg/Program>
|
||||
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
@ -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
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include <list>
|
||||
#include <fstream>
|
||||
|
||||
#include <osg/Notify>
|
||||
#include <osg/State>
|
||||
@ -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; i<size; ++i)
|
||||
{
|
||||
_data[i] = data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// osg::Program
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -240,7 +273,6 @@ void Program::releaseGLObjects(osg::State* state) const
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Program::addShader( Shader* shader )
|
||||
{
|
||||
if( !shader ) return false;
|
||||
@ -336,14 +368,14 @@ void Program::setParameterfv( GLenum pname, const GLfloat* value )
|
||||
|
||||
const GLfloat* Program::getParameterfv( GLenum pname ) const
|
||||
{
|
||||
switch( pname )
|
||||
/*switch( pname )
|
||||
{
|
||||
;
|
||||
// todo tessellation default level
|
||||
// case GL_PATCH_DEFAULT_INNER_LEVEL: return _patchDefaultInnerLevel;
|
||||
// case GL_PATCH_DEFAULT_OUTER_LEVEL: return _patchDefaultOuterLevel;
|
||||
|
||||
}
|
||||
}*/
|
||||
OSG_WARN << "getParameter invalid param " << pname << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@ -484,7 +516,8 @@ const Program::ActiveVarInfoMap& Program::getActiveAttribs(unsigned int contextI
|
||||
|
||||
Program::PerContextProgram::PerContextProgram(const Program* program, unsigned int contextID ) :
|
||||
osg::Referenced(),
|
||||
_contextID( contextID )
|
||||
_contextID( contextID ),
|
||||
_loadedBinary(false)
|
||||
{
|
||||
_program = program;
|
||||
_extensions = GL2Extensions::Get( _contextID, true );
|
||||
@ -515,6 +548,20 @@ void Program::PerContextProgram::linkProgram(osg::State& state)
|
||||
<< " contextID=" << _contextID
|
||||
<< std::endl;
|
||||
|
||||
const ProgramBinary* programBinary = _program->getProgramBinary();
|
||||
|
||||
_loadedBinary = false;
|
||||
if (programBinary && programBinary->getSize())
|
||||
{
|
||||
GLint linked = GL_FALSE;
|
||||
_extensions->glProgramBinary( _glProgramHandle, programBinary->getFormat(),
|
||||
reinterpret_cast<const GLvoid*>(programBinary->getData()), programBinary->getSize() );
|
||||
_extensions->glGetProgramiv( _glProgramHandle, GL_LINK_STATUS, &linked );
|
||||
_loadedBinary = _isLinked = (linked == GL_TRUE);
|
||||
}
|
||||
|
||||
if (!_loadedBinary)
|
||||
{
|
||||
if (_extensions->isGeometryShader4Supported())
|
||||
{
|
||||
_extensions->glProgramParameteri( _glProgramHandle, GL_GEOMETRY_VERTICES_OUT_EXT, _program->_geometryVerticesOut );
|
||||
@ -533,19 +580,25 @@ void Program::PerContextProgram::linkProgram(osg::State& state)
|
||||
{
|
||||
_shadersToDetach[i]->detachShader( _contextID, _glProgramHandle );
|
||||
}
|
||||
}
|
||||
_shadersToDetach.clear();
|
||||
|
||||
if (!_loadedBinary)
|
||||
{
|
||||
// Attach new shaders
|
||||
for( unsigned int i=0; i < _shadersToAttach.size(); ++i )
|
||||
{
|
||||
_shadersToAttach[i]->attachShader( _contextID, _glProgramHandle );
|
||||
}
|
||||
}
|
||||
_shadersToAttach.clear();
|
||||
|
||||
_uniformInfoMap.clear();
|
||||
_attribInfoMap.clear();
|
||||
_lastAppliedUniformList.clear();
|
||||
|
||||
if (!_loadedBinary)
|
||||
{
|
||||
// set any explicit vertex attribute bindings
|
||||
const AttribBindingList& programBindlist = _program->getAttribBindingList();
|
||||
for( AttribBindingList::const_iterator itr = programBindlist.begin();
|
||||
@ -576,11 +629,19 @@ void Program::PerContextProgram::linkProgram(osg::State& state)
|
||||
_extensions->glBindFragDataLocation( _glProgramHandle, itr->second, reinterpret_cast<const GLchar*>(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);
|
||||
}
|
||||
|
||||
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<GLvoid*>(programBinary->getData()) );
|
||||
programBinary->setFormat(binaryFormat);
|
||||
return programBinary;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Program::PerContextProgram::useProgram() const
|
||||
{
|
||||
_extensions->glUseProgram( _glProgramHandle );
|
||||
|
Loading…
Reference in New Issue
Block a user