From e5a9eaa711d82ffcd60f0f6f6470728d321038a4 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 29 Nov 2010 17:43:27 +0000 Subject: [PATCH] From Tim Moore, "Here is initial support for uniform buffer objects. The binding between a buffer object and an indexed target is implemented as a new StateAttribute, UniformBufferBinding. I've included an example program based on the code in the ARB_uniform_buffer_object specification. A few things remain to do: * The binding between a uniform block in a shader program and a buffer indexed target number is fixed, like a vertex attribute binding. This is too restrictive because that binding can be changed without relinking the program. This mapping should be done by name in the same way that uniform values are handled i.e., like a pseudo state attribute; * There's no direct way yet to query for the offset of uniforms in uniform block, so only the std140 layout is really usable. A helper class that implemented the std140 rules would be quite helpful for setting up uniform blocks without having to link a program first; * There's no direct support for querying parameters such as the maximum block length, minimum offset alignment, etc. Having that information available outside of the draw thread would make certain instancing techniques easier to implement." --- examples/CMakeLists.txt | 1 + examples/osguniformbuffer/CMakeLists.txt | 4 + .../osguniformbuffer/osguniformbuffer.cpp | 206 ++++++++++++++++++ include/osg/BufferIndexBinding | 129 +++++++++++ include/osg/BufferObject | 19 ++ include/osg/GL2Extensions | 64 ++++++ include/osg/Program | 28 ++- include/osg/StateAttribute | 5 +- src/osg/BufferIndexBinding.cpp | 98 +++++++++ src/osg/BufferObject.cpp | 35 +++ src/osg/CMakeLists.txt | 2 + src/osg/GL2Extensions.cpp | 134 ++++++++++++ src/osg/Program.cpp | 67 ++++++ 13 files changed, 789 insertions(+), 3 deletions(-) create mode 100644 examples/osguniformbuffer/CMakeLists.txt create mode 100644 examples/osguniformbuffer/osguniformbuffer.cpp create mode 100644 include/osg/BufferIndexBinding create mode 100644 src/osg/BufferIndexBinding.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index eb54ffe3b..b8e62cea1 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -115,6 +115,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgtexturerectangle) ADD_SUBDIRECTORY(osgtexturecompression) ADD_SUBDIRECTORY(osgthirdpersonview) + ADD_SUBDIRECTORY(osguniformbuffer) ADD_SUBDIRECTORY(osguserstats) ADD_SUBDIRECTORY(osgvertexprogram) ADD_SUBDIRECTORY(osgvertexattributes) diff --git a/examples/osguniformbuffer/CMakeLists.txt b/examples/osguniformbuffer/CMakeLists.txt new file mode 100644 index 000000000..8b60bf455 --- /dev/null +++ b/examples/osguniformbuffer/CMakeLists.txt @@ -0,0 +1,4 @@ +SET(TARGET_SRC osguniformbuffer.cpp ) + +#### end var setup ### +SETUP_EXAMPLE(osguniformbuffer) diff --git a/examples/osguniformbuffer/osguniformbuffer.cpp b/examples/osguniformbuffer/osguniformbuffer.cpp new file mode 100644 index 000000000..06d2b85b3 --- /dev/null +++ b/examples/osguniformbuffer/osguniformbuffer.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +using namespace std; +using namespace osg; + +// This example is based on the sample code in the +// ARB_uniform_buffer_object extension specification. + +GLfloat colors1[] = { + // block + 0.45,0.45,1,1, + 0.45,0.45,1,1, + 0.75,0.75,0.75,1, + 0.0,0.0,1.0,1, + 0.0,1.0,0.0,1 +}; +GLfloat colors2[] = { + // block + 0.45,0.45,1,1, + 0.45,0.45,1,1, + 0.75,0.75,0.75,1, + 1.0,0.0,0.0,1, + 0.0,1.0,0.0,1, +}; + +char vertexShaderSource[] = + "// Vertex shader for Gooch shading\n" + "// Author: Randi Rost\n" + "// Copyright (c) 2002-2006 3Dlabs Inc. Ltd.\n" + "// See 3Dlabs-License.txt for license information\n" + + "vec3 LightPosition = vec3(0.0, 10.0, 4.0); \n" + + "varying float NdotL;\n" + "varying vec3 ReflectVec;\n" + "varying vec3 ViewVec;\n" + + "void main(void)\n" + "{\n" + "vec3 ecPos = vec3 (gl_ModelViewMatrix * gl_Vertex);\n" + "vec3 tnorm = normalize(gl_NormalMatrix * gl_Normal);\n" + "vec3 lightVec = normalize(LightPosition - ecPos);\n" + "ReflectVec = normalize(reflect(-lightVec, tnorm));\n" + "ViewVec = normalize(-ecPos);\n" + "NdotL = (dot(lightVec, tnorm) + 1.0) * 0.5;\n" + "gl_Position = ftransform();\n" + "}\n"; + +char fragmentShaderSource[] = + "// Fragment shader for Gooch shading, adapted for ARB_uniform_buffer_object\n" + + "#extension GL_ARB_uniform_buffer_object : enable\n" + + "layout(std140) uniform colors0\n" + "{\n" + "float DiffuseCool;\n" + "float DiffuseWarm;\n" + "vec3 SurfaceColor;\n" + "vec3 WarmColor;\n" + "vec3 CoolColor;\n" + "};\n" + + "varying float NdotL;\n" + "varying vec3 ReflectVec;\n" + "varying vec3 ViewVec;\n" + + "void main (void)\n" + "{\n" + "vec3 kcool = min(CoolColor + DiffuseCool * SurfaceColor, 1.0);\n" + "vec3 kwarm = min(WarmColor + DiffuseWarm * SurfaceColor, 1.0); \n" + "vec3 kfinal = mix(kcool, kwarm, NdotL);\n" + + "vec3 nreflect = normalize(ReflectVec);\n" + "vec3 nview = normalize(ViewVec);\n" + + "float spec = max(dot(nreflect, nview), 0.0);\n" + "spec = pow(spec, 32.0);\n" + + "gl_FragColor = vec4 (min(kfinal + spec, 1.0), 1.0);\n" + "}\n"; + +// Callback for animating the WarmColor + +class UniformCallback : public StateAttributeCallback +{ +public: + void operator() (StateAttribute* attr, NodeVisitor* nv) + { + UniformBufferBinding* ubb = static_cast(attr); + UniformBufferObject* ubo + = static_cast(ubb->getBufferObject()); + FloatArray* array = static_cast(ubo->getBufferData(0)); + double time = nv->getFrameStamp()->getSimulationTime(); + double frac = fmod(time, 1.0); + Vec4f warmColor = (Vec4f(0.0, 0.0, 1.0 ,1) * frac + + Vec4f(1.0, 0.0, 0.0, 1) * (1 - frac)); + // Since we're using the std140 layout, we know where the + // warmColor variable is located in the buffer. + for (int i = 0; i < 4; ++i) + (*array)[12 + i] = warmColor[i]; + array->dirty(); + } +}; + +int main(int argc, char** argv) +{ + osg::ArgumentParser arguments(&argc,argv); + osgViewer::Viewer viewer(arguments); + + if (arguments.argc() <= 1) { + cerr << "Need a scene.\n"; + return 1; + } + + osg::ref_ptr loadedModel = osgDB::readNodeFiles(arguments); + if (!loadedModel) { + cerr << "couldn't load " << argv[1] << "\n"; + return 1; + } + osgUtil::Optimizer optimizer; + optimizer.optimize(loadedModel.get()); + const BoundingSphere bound = loadedModel->getBound(); + const float displacement = 2.25 * bound.radius(); + Group* scene = new Group; + StateSet* rootSS = scene->getOrCreateStateSet(); + + Shader* vertexShader = new Shader(Shader::VERTEX); + vertexShader->setShaderSource(vertexShaderSource); + Shader* fragmentShader = new Shader(Shader::FRAGMENT); + fragmentShader->setShaderSource(fragmentShaderSource); + Program* prog = new Program; + prog->addShader(vertexShader); + prog->addShader(fragmentShader); + prog->addBindUniformBlock("colors0", 0); + rootSS->setAttributeAndModes(prog, StateAttribute::ON); + // Place 3 instances of the loaded model with different uniform + // blocks for each. + // + // The blocksize is known because of the std140 format. + const unsigned blockSize = 20 * sizeof(GLfloat); + ref_ptr colorArray + = new FloatArray(&colors1[0], + &colors1[sizeof(colors1) / sizeof(GLfloat)]); + ref_ptr ubo = new UniformBufferObject; + colorArray->setBufferObject(ubo.get()); + Group* group1 = new Group; + StateSet* ss1 = group1->getOrCreateStateSet(); + group1->addChild(loadedModel.get()); + scene->addChild(group1); + ref_ptr ubb1 + = new UniformBufferBinding(0, ubo.get(), 0, blockSize); + ss1->setAttributeAndModes(ubb1.get(), StateAttribute::ON); + + ref_ptr colorArray2 + = new FloatArray(&colors2[0], + &colors2[sizeof(colors2) / sizeof(GLfloat)]); + ref_ptr ubo2 = new UniformBufferObject; + colorArray2->setBufferObject(ubo2.get()); + MatrixTransform* group2 = new MatrixTransform; + Matrix mat2 = Matrix::translate(-displacement, 0.0, 0.0); + group2->setMatrix(mat2); + StateSet* ss2 = group2->getOrCreateStateSet(); + group2->addChild(loadedModel.get()); + scene->addChild(group2); + ref_ptr ubb2 + = new UniformBufferBinding(0, ubo2.get(), 0, blockSize); + ss2->setAttributeAndModes(ubb2.get(), StateAttribute::ON); + + ref_ptr colorArray3 + = new FloatArray(&colors2[0], + &colors2[sizeof(colors2) / sizeof(GLfloat)]); + ref_ptr ubo3 = new UniformBufferObject; + colorArray3->setBufferObject(ubo3.get()); + MatrixTransform* group3 = new MatrixTransform; + Matrix mat3 = Matrix::translate(displacement, 0.0, 0.0); + group3->setMatrix(mat3); + StateSet* ss3 = group3->getOrCreateStateSet(); + group3->addChild(loadedModel.get()); + scene->addChild(group3); + ref_ptr ubb3 + = new UniformBufferBinding(0, ubo3.get(), 0, blockSize); + ubb3->setUpdateCallback(new UniformCallback); + ubb3->setDataVariance(Object::DYNAMIC); + ss3->setAttributeAndModes(ubb3.get(), StateAttribute::ON); + + viewer.setSceneData(scene); + viewer.realize(); + return viewer.run(); +} diff --git a/include/osg/BufferIndexBinding b/include/osg/BufferIndexBinding new file mode 100644 index 000000000..2e9e1273b --- /dev/null +++ b/include/osg/BufferIndexBinding @@ -0,0 +1,129 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * Copyright (C) 2010 Tim Moore + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#ifndef OSG_BUFFERINDEXBINDING +#define OSG_BUFFERINDEXBINDING 1 +#include +#include +#include + +namespace osg { + +class State; + +/** Encapsulate binding buffer objects to index targets. This + * specifically supports the uniform buffer and transform feedback + * targets. + */ + +// Common implementation superclass +class OSG_EXPORT BufferIndexBinding : public StateAttribute +{ + protected: + BufferIndexBinding(GLenum target, GLuint index); + BufferIndexBinding(GLenum target, GLuint index, BufferObject* bo, GLintptr offset, + GLsizeiptr size); + BufferIndexBinding(const BufferIndexBinding& rhs, const CopyOp& copyop=CopyOp::SHALLOW_COPY); + public: + // The member value is part of the key to this state attribute in + // the State class. Using the index target, we can seperately + // track the bindings for many different index targets. + virtual unsigned getMember() const { return static_cast(_index); } + + GLenum getTarget() const { return _target; } + /** Get the index target. + */ + GLuint getIndex() const { return _index; } + /** Set the buffer object that will be bound to the index target. + */ + void setBufferObject(BufferObject *bo) { _bufferObject = bo; } + /** Get the buffer object to be bound. + */ + BufferObject* getBufferObject() const { return _bufferObject.get(); } + /** Set the starting offset into the buffer object for data for + the indexed target. Note: the required alignment on the offset + may be quite large (e.g., 256 bytes on NVidia 8600M). This + should be checked with glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT...). + */ + void setOffset(GLintptr offset) { _offset = offset; } + GLintptr getOffset() const { return _offset; } + /** Set the size of data for the indexed target. + */ + void setSize(GLsizeiptr size) { _size = size; } + GLsizeiptr getSize() const { return _size; } + virtual void apply(State& state) const; + protected: + virtual ~BufferIndexBinding(); + const GLenum _target; + const GLuint _index; + ref_ptr _bufferObject; + GLintptr _offset; + GLsizeiptr _size; +}; + +/** StateAttribute for binding a uniform buffer index target. + */ +class OSG_EXPORT UniformBufferBinding : public BufferIndexBinding +{ + public: + UniformBufferBinding(); + UniformBufferBinding(GLuint index); + /** Create a binding for a uniform buffer index target. + * @param index the index target + * @param bo associated buffer object + * @param offset offset into buffer object + * @param size size of data in buffer object + */ + UniformBufferBinding(GLuint index, BufferObject* bo, GLintptr offset, GLsizeiptr size); + UniformBufferBinding(const UniformBufferBinding& rhs, const CopyOp& copyop=CopyOp::SHALLOW_COPY); + META_StateAttribute(osg, UniformBufferBinding, UNIFORMBUFFERBINDING); + + virtual int compare(const StateAttribute& bb) const + { + COMPARE_StateAttribute_Types(UniformBufferBinding, bb) + + COMPARE_StateAttribute_Parameter(_target) + COMPARE_StateAttribute_Parameter(_index) + COMPARE_StateAttribute_Parameter(_bufferObject) + COMPARE_StateAttribute_Parameter(_offset) + COMPARE_StateAttribute_Parameter(_size) + return 0; + } +}; + +/** StateAttribute for binding a transform feedback index target. + */ +class OSG_EXPORT TransformFeedbackBufferBinding : public BufferIndexBinding +{ + public: + TransformFeedbackBufferBinding(GLuint index = 0); + TransformFeedbackBufferBinding(GLuint index, BufferObject* bo, GLintptr offset, GLsizeiptr size); + TransformFeedbackBufferBinding(const TransformFeedbackBufferBinding& rhs, const CopyOp& copyop=CopyOp::SHALLOW_COPY); + META_StateAttribute(osg, TransformFeedbackBufferBinding, TRANSFORMFEEDBACKBUFFERBINDING); + + virtual int compare(const StateAttribute& bb) const + { + COMPARE_StateAttribute_Types(TransformFeedbackBufferBinding, bb) + + COMPARE_StateAttribute_Parameter(_target) + COMPARE_StateAttribute_Parameter(_index) + COMPARE_StateAttribute_Parameter(_bufferObject) + COMPARE_StateAttribute_Parameter(_offset) + COMPARE_StateAttribute_Parameter(_size) + return 0; + } +}; +} + +#endif diff --git a/include/osg/BufferObject b/include/osg/BufferObject index 68681c9d5..563598275 100644 --- a/include/osg/BufferObject +++ b/include/osg/BufferObject @@ -254,6 +254,7 @@ class OSG_EXPORT GLBufferObject : public Referenced bool isBufferObjectSupported() const { return _glGenBuffers!=0; } bool isPBOSupported() const { return _isPBOSupported; } + bool isUniformBufferObjectSupported() const { return _isUniformBufferObjectSupported; } void glGenBuffers (GLsizei n, GLuint *buffers) const; void glBindBuffer (GLenum target, GLuint buffer) const; @@ -266,6 +267,8 @@ class OSG_EXPORT GLBufferObject : public Referenced GLboolean glUnmapBuffer (GLenum target) const; void glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params) const; void glGetBufferPointerv (GLenum target, GLenum pname, GLvoid* *params) const; + void glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + void glBindBufferBase (GLenum target, GLuint index, GLuint buffer); protected: @@ -280,6 +283,9 @@ class OSG_EXPORT GLBufferObject : public Referenced typedef GLboolean (GL_APIENTRY * UnmapBufferProc) (GLenum target); typedef void (GL_APIENTRY * GetBufferParameterivProc) (GLenum target, GLenum pname, GLint *params); typedef void (GL_APIENTRY * GetBufferPointervProc) (GLenum target, GLenum pname, GLvoid* *params); + typedef void (GL_APIENTRY * BindBufferRangeProc) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); + typedef void (GL_APIENTRY * BindBufferBaseProc) (GLenum target, GLuint index, GLuint buffer); + GenBuffersProc _glGenBuffers; BindBufferProc _glBindBuffer; @@ -292,8 +298,11 @@ class OSG_EXPORT GLBufferObject : public Referenced UnmapBufferProc _glUnmapBuffer; GetBufferParameterivProc _glGetBufferParameteriv; GetBufferPointervProc _glGetBufferPointerv; + BindBufferRangeProc _glBindBufferRange; + BindBufferBaseProc _glBindBufferBase; bool _isPBOSupported; + bool _isUniformBufferObjectSupported; }; /** Function to call to get the extension of a specified context. @@ -753,6 +762,16 @@ class OSG_EXPORT PixelDataBufferObject : public BufferObject }; +class OSG_EXPORT UniformBufferObject : public BufferObject +{ + public: + UniformBufferObject(); + UniformBufferObject(const UniformBufferObject& ubo, const CopyOp& copyop=CopyOp::SHALLOW_COPY); + META_Object(osg, UniformBufferObject); + protected: + virtual ~UniformBufferObject(); +}; + inline void GLBufferObject::bindBuffer() { _extensions->glBindBuffer(_profile._target,_glObjectID); diff --git a/include/osg/GL2Extensions b/include/osg/GL2Extensions index 5135403c4..b999288ac 100644 --- a/include/osg/GL2Extensions +++ b/include/osg/GL2Extensions @@ -247,6 +247,43 @@ typedef char GLchar; #define GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905 #endif +// ARB_uniform_buffer_object +#ifndef GL_UNIFORM_BUFFER +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +#endif + namespace osg { @@ -287,6 +324,8 @@ class OSG_EXPORT GL2Extensions : public osg::Referenced void setGpuShader4Supported(bool flag) { _isGpuShader4Supported = flag; } bool isGpuShader4Supported() const { return _isGpuShader4Supported; } + void setUniformBufferObjectSupported(bool flag) { _isUniformBufferObjectSupported = flag; } + bool isUniformBufferObjectSupported() {return _isUniformBufferObjectSupported; } /** 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. @@ -433,6 +472,14 @@ class OSG_EXPORT GL2Extensions : public osg::Referenced void glUniform3uiv( GLint location, GLsizei count, const GLuint *value ) const; void glUniform4uiv( GLint location, GLsizei count, const GLuint *value ) const; + // ARB_uniform_buffer_object + void glGetUniformIndices(GLuint program, GLsizei uniformCount, const GLchar* *uniformNames, GLuint *uniformIndices) const; + void glGetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params) const; + void glGetActiveUniformName(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName) const; + GLuint glGetUniformBlockIndex(GLuint program, const GLchar *uniformBlockName) const; + 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; protected: ~GL2Extensions() {} @@ -446,6 +493,7 @@ class OSG_EXPORT GL2Extensions : public osg::Referenced bool _isGeometryShader4Supported; bool _areTessellationShadersSupported; bool _isGpuShader4Supported; + bool _isUniformBufferObjectSupported; typedef void (GL_APIENTRY * BlendEquationSeparateProc)(GLenum modeRGB, GLenum modeAlpha); typedef void (GL_APIENTRY * DrawBuffersProc)(GLsizei n, const GLenum *bufs); @@ -567,6 +615,13 @@ class OSG_EXPORT GL2Extensions : public osg::Referenced typedef void (GL_APIENTRY * Uniform3uivProc)( GLint location, GLsizei count, const GLuint *value ); typedef void (GL_APIENTRY * Uniform4uivProc)( GLint location, GLsizei count, const GLuint *value ); typedef GLuint (GL_APIENTRY * GetHandleProc) (GLenum pname); + typedef void (GL_APIENTRY * GetUniformIndicesProc)(GLuint program, GLsizei uniformCount, const GLchar* *uniformNames, GLuint *uniformIndices); + typedef void (GL_APIENTRY * GetActiveUniformsivProc)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); + typedef void (GL_APIENTRY * GetActiveUniformNameProc)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); + typedef GLuint (GL_APIENTRY * GetUniformBlockIndexProc)(GLuint program, const GLchar *uniformBlockName); + 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); BlendEquationSeparateProc _glBlendEquationSeparate; DrawBuffersProc _glDrawBuffers; @@ -697,6 +752,15 @@ class OSG_EXPORT GL2Extensions : public osg::Referenced Uniform2uivProc _glUniform2uiv; Uniform3uivProc _glUniform3uiv; Uniform4uivProc _glUniform4uiv; + + // ARB_uniform_buffer_object + GetUniformIndicesProc _glGetUniformIndices; + GetActiveUniformsivProc _glGetActiveUniformsiv; + GetActiveUniformNameProc _glGetActiveUniformName; + GetUniformBlockIndexProc _glGetUniformBlockIndex; + GetActiveUniformBlockivProc _glGetActiveUniformBlockiv; + GetActiveUniformBlockNameProc _glGetActiveUniformBlockName; + UniformBlockBindingProc _glUniformBlockBinding; }; } diff --git a/include/osg/Program b/include/osg/Program index f57d0130c..b4979f477 100644 --- a/include/osg/Program +++ b/include/osg/Program @@ -113,11 +113,22 @@ class OSG_EXPORT Program : public osg::StateAttribute /** Remove an frag data location binding. */ void removeBindFragDataLocation( const std::string& name ); + /** Add a uniform block binding to an index target. XXX This + * should not be an attribute of the program. It should be a + * pseudo-uniform that can live in StateSet objects because + * it is cheap to set. */ + void addBindUniformBlock(const std::string& name, GLuint index); + + /** Remove a uniform block binding. */ + void removeBindUniformBlock(const std::string& name); + typedef std::map AttribBindingList; typedef std::map FragDataBindingList; + typedef std::map UniformBlockBindingList; const AttribBindingList& getAttribBindingList() const { return _attribBindingList; } const FragDataBindingList& getFragDataBindingList() const { return _fragDataBindingList; } + const UniformBlockBindingList& getUniformBlockBindingList() const { return _uniformBlockBindingList; } /** Return true if this Program represents "fixed-functionality" rendering */ bool isFixedFunction() const; @@ -151,7 +162,18 @@ class OSG_EXPORT Program : public osg::StateAttribute typedef std::map< std::string, ActiveVarInfo > ActiveVarInfoMap; const ActiveUniformMap& getActiveUniforms(unsigned int contextID) const; const ActiveVarInfoMap& getActiveAttribs(unsigned int contextID) const; - + struct UniformBlockInfo + { + UniformBlockInfo() : _index(GL_INVALID_INDEX), _size(0) {} + UniformBlockInfo(GLuint index, GLsizei size) + : _index(index), _size(size) + { + } + GLuint _index; + GLsizei _size; + }; + typedef std::map UniformBlockMap; + const UniformBlockMap& getUniformBlocks(unsigned contextID) const; public: // make PerContextProgram a friend to allow it access Program's protected @@ -214,7 +236,7 @@ class OSG_EXPORT Program : public osg::StateAttribute const ActiveUniformMap& getActiveUniforms() const {return _uniformInfoMap;} const ActiveVarInfoMap& getActiveAttribs() const {return _attribInfoMap;} - + const UniformBlockMap& getUniformBlocks() const {return _uniformBlockMap; } inline GLint getUniformLocation( unsigned int uniformNameID ) const { ActiveUniformMap::const_iterator itr = _uniformInfoMap.find(uniformNameID); return (itr!=_uniformInfoMap.end()) ? itr->second._location : -1; } inline GLint getAttribLocation( const std::string& name ) const { ActiveVarInfoMap::const_iterator itr = _attribInfoMap.find(name); return (itr!=_attribInfoMap.end()) ? itr->second._location : -1; } @@ -246,6 +268,7 @@ class OSG_EXPORT Program : public osg::StateAttribute ActiveUniformMap _uniformInfoMap; ActiveVarInfoMap _attribInfoMap; + UniformBlockMap _uniformBlockMap; typedef std::pair, unsigned int> UniformModifiedCountPair; typedef std::vector LastAppliedUniformList; @@ -272,6 +295,7 @@ class OSG_EXPORT Program : public osg::StateAttribute mutable osg::buffered_value< osg::ref_ptr > _pcpList; AttribBindingList _attribBindingList; FragDataBindingList _fragDataBindingList; + UniformBlockBindingList _uniformBlockBindingList; typedef std::vector< ref_ptr > ShaderList; ShaderList _shaderList; diff --git a/include/osg/StateAttribute b/include/osg/StateAttribute index 97136c3e4..b867b774a 100644 --- a/include/osg/StateAttribute +++ b/include/osg/StateAttribute @@ -184,7 +184,10 @@ class OSG_EXPORT StateAttribute : public Object OSGNVSLANG_PROGRAM, // osgNVParse - OSGNVPARSE_PROGRAM_PARSER + OSGNVPARSE_PROGRAM_PARSER, + + UNIFORMBUFFERBINDING, + TRANSFORMFEEDBACKBUFFERBINDING }; /** Simple pairing between an attribute type and the member within that attribute type group.*/ diff --git a/src/osg/BufferIndexBinding.cpp b/src/osg/BufferIndexBinding.cpp new file mode 100644 index 000000000..f4b1fa4d9 --- /dev/null +++ b/src/osg/BufferIndexBinding.cpp @@ -0,0 +1,98 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * Copyright (C) 2010 Tim Moore + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#include +#include + +namespace osg { + +BufferIndexBinding::BufferIndexBinding(GLenum target, GLuint index) + : _target(target), _index(index), _offset(0), _size(0) +{ +} + +BufferIndexBinding::BufferIndexBinding(GLenum target, GLuint index, BufferObject* bo, + GLintptr offset, GLsizeiptr size) + : _target(target), _index(index), _bufferObject(bo), _offset(offset), _size(size) +{ +} + +BufferIndexBinding::BufferIndexBinding(const BufferIndexBinding& rhs, const CopyOp& copyop) + : StateAttribute(rhs, copyop), + _target(rhs._target), _index(rhs._index), + _bufferObject(static_cast(copyop(rhs._bufferObject.get()))), + _offset(rhs._offset), + _size(rhs._size) +{ +} + +BufferIndexBinding::~BufferIndexBinding() +{ +} + +void BufferIndexBinding::apply(State& state) const +{ + if (_bufferObject.valid()) + { + GLBufferObject* glObject + = _bufferObject->getOrCreateGLBufferObject(state.getContextID()); + if (!glObject->_extensions->isUniformBufferObjectSupported()) + return; + if (glObject->isDirty()) glObject->compileBuffer(); + glObject->_extensions->glBindBufferRange(_target, _index, + glObject->getGLObjectID(), _offset, _size); + } +} + +UniformBufferBinding::UniformBufferBinding() + : BufferIndexBinding(GL_UNIFORM_BUFFER, 0) +{ +} + +UniformBufferBinding::UniformBufferBinding(GLuint index) + : BufferIndexBinding(GL_UNIFORM_BUFFER, index) +{ +} + +UniformBufferBinding::UniformBufferBinding(GLuint index, BufferObject* bo, GLintptr offset, + GLsizeiptr size) + : BufferIndexBinding(GL_UNIFORM_BUFFER, index, bo, offset, size) +{ + +} + +UniformBufferBinding::UniformBufferBinding(const UniformBufferBinding& rhs, + const CopyOp& copyop) + : BufferIndexBinding(rhs, copyop) +{ +} + + +TransformFeedbackBufferBinding::TransformFeedbackBufferBinding(GLuint index) + : BufferIndexBinding(GL_TRANSFORM_FEEDBACK_BUFFER, index) +{ +} + +TransformFeedbackBufferBinding::TransformFeedbackBufferBinding(GLuint index, BufferObject* bo, GLintptr offset, GLsizeiptr size) + : BufferIndexBinding(GL_TRANSFORM_FEEDBACK_BUFFER, index, bo, offset, size) +{ + +} + +TransformFeedbackBufferBinding::TransformFeedbackBufferBinding(const TransformFeedbackBufferBinding& rhs, const CopyOp& copyop) + : BufferIndexBinding(rhs, copyop) +{ +} + +} diff --git a/src/osg/BufferObject.cpp b/src/osg/BufferObject.cpp index 78e3a1b52..bba71b187 100644 --- a/src/osg/BufferObject.cpp +++ b/src/osg/BufferObject.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -275,6 +276,9 @@ GLBufferObject::Extensions::Extensions(const Extensions& rhs): _glUnmapBuffer = rhs._glUnmapBuffer; _glGetBufferParameteriv = rhs._glGetBufferParameteriv; _glGetBufferPointerv = rhs._glGetBufferPointerv; + _glBindBufferRange = rhs._glBindBufferRange; + _glBindBufferBase = rhs._glBindBufferBase; + } @@ -291,6 +295,8 @@ void GLBufferObject::Extensions::lowestCommonDenominator(const Extensions& rhs) if (!rhs._glUnmapBuffer) _glUnmapBuffer = rhs._glUnmapBuffer; if (!rhs._glGetBufferParameteriv) _glGetBufferParameteriv = rhs._glGetBufferParameteriv; if (!rhs._glGetBufferParameteriv) _glGetBufferPointerv = rhs._glGetBufferPointerv; + if (!rhs._glBindBufferRange) _glBindBufferRange = rhs._glBindBufferRange; + if (!rhs._glBindBufferBase) _glBindBufferBase = rhs._glBindBufferBase; } void GLBufferObject::Extensions::setupGLExtensions(unsigned int contextID) @@ -307,6 +313,10 @@ void GLBufferObject::Extensions::setupGLExtensions(unsigned int contextID) setGLExtensionFuncPtr(_glGetBufferParameteriv, "glGetBufferParameteriv","glGetBufferParameterivARB"); setGLExtensionFuncPtr(_glGetBufferPointerv, "glGetBufferPointerv","glGetBufferPointervARB"); _isPBOSupported = OSG_GL3_FEATURES || osg::isGLExtensionSupported(contextID,"GL_ARB_pixel_buffer_object"); + setGLExtensionFuncPtr(_glBindBufferRange, "glBindBufferRange"); + setGLExtensionFuncPtr(_glBindBufferBase, "glBindBufferBase"); + _isUniformBufferObjectSupported + = osg::isGLExtensionSupported(contextID, "GL_ARB_uniform_buffer_object"); } void GLBufferObject::Extensions::glGenBuffers(GLsizei n, GLuint *buffers) const @@ -387,6 +397,17 @@ void GLBufferObject::Extensions::glGetBufferPointerv (GLenum target, GLenum pnam else OSG_WARN<<"Error: glGetBufferPointerv not supported by OpenGL driver"<isUniformBufferObjectSupported()) + { + GLuint activeUniformBlocks = 0; + GLsizei maxBlockNameLen = 0; + _extensions->glGetProgramiv(_glProgramHandle, GL_ACTIVE_UNIFORM_BLOCKS, + reinterpret_cast(&activeUniformBlocks)); + _extensions->glGetProgramiv(_glProgramHandle, + GL_ACTIVE_UNIFORM_MAX_LENGTH, + &maxBlockNameLen); + if (maxBlockNameLen > 0) + { + std::vector blockName(maxBlockNameLen); + for (GLuint i = 0; i < activeUniformBlocks; ++i) + { + GLsizei len = 0; + GLint blockSize = 0; + _extensions->glGetActiveUniformBlockName(_glProgramHandle, i, + maxBlockNameLen, &len, + &blockName[0]); + _extensions->glGetActiveUniformBlockiv(_glProgramHandle, i, + GL_UNIFORM_BLOCK_DATA_SIZE, + &blockSize); + _uniformBlockMap + .insert(UniformBlockMap::value_type(&blockName[0], + UniformBlockInfo(i, blockSize))); + } + } + // Bind any uniform blocks + const UniformBlockBindingList& bindingList = _program->getUniformBlockBindingList(); + for (UniformBlockMap::iterator itr = _uniformBlockMap.begin(), + end = _uniformBlockMap.end(); + itr != end; + ++itr) + { + const std::string& blockName = itr->first; + UniformBlockBindingList::const_iterator bitr = bindingList.find(blockName); + if (bitr != bindingList.end()) + { + _extensions->glUniformBlockBinding(_glProgramHandle, itr->second._index, + bitr->second); + OSG_INFO << "uniform block " << blockName << ": " << itr->second._index + << " binding: " << bitr->second << "\n"; + } + else + { + OSG_WARN << "uniform block " << blockName << " has no binding.\n"; + } + + } + + } + // build _uniformInfoMap GLint numUniforms = 0; GLsizei maxLen = 0;