Commit 863b846e authored by Christopher Tenter's avatar Christopher Tenter
Browse files

add support for instancing from vbo via VertexDeclaration

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@20108 383ad7c9-94d9-4d36-a494-682f7c89f535
parent 47c2e19e
......@@ -2040,9 +2040,8 @@ void DrawMeshT<Mesh>::updatePickingFaces_opt(ACG::GLState& _state )
template <class Mesh>
bool ACG::DrawMeshT<Mesh>::supportsPickingFaces_opt()
{
#ifndef GL_ARB_texture_buffer_object
return 0;
#endif
if (!ACG::Texture::supportsTextureBuffer())
return false;
// fetch picking shader from cache
pickFaceShader_ = ShaderCache::getInstance()->getProgram("Picking/vertex.glsl", "Picking/pick_face.glsl", 0, false);
......@@ -2390,27 +2389,16 @@ void ACG::DrawMeshT<Mesh>::createVertexDeclaration()
template <class Mesh>
void ACG::DrawMeshT<Mesh>::updateEdgeHalfedgeVertexDeclarations()
{
VertexElement elemArrayEC[] = { {GL_FLOAT, 3, VERTEX_USAGE_POSITION, 0, 0 },
{GL_UNSIGNED_BYTE, 4, VERTEX_USAGE_COLOR, 0, 0 } };
elemArrayEC[0].pointer_ = perEdgeVertexBuffer();
elemArrayEC[1].pointer_ = perEdgeColorBuffer();
VertexElement elemArrayH = {GL_FLOAT, 3, VERTEX_USAGE_POSITION, 0, 0 };
elemArrayH.pointer_ = perHalfedgeVertexBuffer();
VertexElement elemArrayHC[] = { {GL_FLOAT, 3, VERTEX_USAGE_POSITION, 0, 0 },
{GL_UNSIGNED_BYTE, 4, VERTEX_USAGE_COLOR, 0, 0 } };
elemArrayHC[0].pointer_ = perHalfedgeVertexBuffer();
elemArrayHC[1].pointer_ = perHalfedgeColorBuffer();
vertexDeclEdgeCol_->clear();
vertexDeclEdgeCol_->addElement(GL_FLOAT, 3, VERTEX_USAGE_POSITION, perEdgeVertexBuffer());
vertexDeclEdgeCol_->addElement(GL_UNSIGNED_BYTE, 4, VERTEX_USAGE_COLOR, perEdgeColorBuffer());
vertexDeclHalfedgeCol_->clear();
vertexDeclHalfedgePos_->clear();
vertexDeclHalfedgeCol_->addElement(GL_FLOAT, 3, VERTEX_USAGE_POSITION, perHalfedgeVertexBuffer());
vertexDeclHalfedgeCol_->addElement(GL_UNSIGNED_BYTE, 4, VERTEX_USAGE_COLOR, perHalfedgeColorBuffer());
vertexDeclEdgeCol_->addElements(2, elemArrayEC);
vertexDeclHalfedgeCol_->addElements(2, elemArrayHC);
vertexDeclHalfedgePos_->addElement(&elemArrayH);
vertexDeclHalfedgePos_->clear();
vertexDeclHalfedgePos_->addElement(GL_FLOAT, 3, VERTEX_USAGE_POSITION, perHalfedgeVertexBuffer());
vertexDeclEdgeCol_->setVertexStride(0);
vertexDeclHalfedgeCol_->setVertexStride(0);
......
......@@ -55,6 +55,11 @@
namespace ACG
{
VertexElement::VertexElement()
: type_(0), numElements_(0), usage_(VERTEX_USAGE_SHADER_INPUT), shaderInputName_(0), pointer_(0), divisor_(0), vbo_(0)
{
}
void VertexElement::setByteOffset(unsigned int _offset)
{
......@@ -106,7 +111,9 @@ void VertexDeclaration::addElement(unsigned int _type,
unsigned int _numElements,
VERTEX_USAGE _usage,
const void* _pointer,
const char* _shaderInputName)
const char* _shaderInputName,
unsigned int _divisor,
unsigned int _vbo)
{
VertexElement* ve = new VertexElement();
......@@ -115,6 +122,8 @@ void VertexDeclaration::addElement(unsigned int _type,
ve->usage_ = _usage;
ve->shaderInputName_ = _shaderInputName;
ve->pointer_ = _pointer;
ve->divisor_ = _divisor;
ve->vbo_ = _vbo;
addElement(ve);
delete ve;
......@@ -124,7 +133,9 @@ void VertexDeclaration::addElement(unsigned int _type,
unsigned int _numElements,
VERTEX_USAGE _usage,
size_t _byteOffset,
const char* _shaderInputName)
const char* _shaderInputName,
unsigned int _divisor,
unsigned int _vbo)
{
VertexElement* ve = new VertexElement();
......@@ -133,6 +144,8 @@ void VertexDeclaration::addElement(unsigned int _type,
ve->usage_ = _usage;
ve->shaderInputName_ = _shaderInputName;
ve->pointer_ = (void*)_byteOffset;
ve->divisor_ = _divisor;
ve->vbo_ = _vbo;
addElement(ve);
......@@ -170,18 +183,42 @@ void VertexDeclaration::addElements(unsigned int _numElements, const VertexEleme
const void* p;
} lastOffset, firstOffset;
firstOffset.p = getElement(0)->pointer_;
const VertexElement* pLastElement = getElement(n-1);
lastOffset.p = pLastElement->pointer_;
vertexStride_ = static_cast<unsigned int>(lastOffset.u + getElementSize( pLastElement ) - firstOffset.u);
std::map<unsigned int, VertexElement*> vboFirstElements;
std::map<unsigned int, VertexElement*> vboLastElements;
for (unsigned int i = 0; i < n; ++i)
{
if (vboFirstElements.find(elements_[i].vbo_) == vboFirstElements.end())
vboFirstElements[elements_[i].vbo_] = &elements_[i];
vboLastElements[elements_[i].vbo_] = &elements_[i];
}
for (std::map<unsigned int, VertexElement*>::iterator it = vboFirstElements.begin(); it != vboFirstElements.end(); ++it)
{
VertexElement* lastElement = vboLastElements[it->first];
firstOffset.p = it->second->pointer_;
lastOffset.p = lastElement->pointer_;
vertexStridesVBO_[it->first] = static_cast<unsigned int>(lastOffset.u + getElementSize( lastElement ) - firstOffset.u);
}
vertexStride_ = vertexStridesVBO_.begin()->second;
}
}
}
// union instead of reinterpret_cast for cross-platform compatibility, must be global for use in std::map
union VertexDeclaration_ptr2uint
{
unsigned long u;
const void* p;
};
void VertexDeclaration::updateOffsets()
{
......@@ -190,25 +227,44 @@ void VertexDeclaration::updateOffsets()
if (!numElements) return;
// union instead of reinterpret_cast for cross-platform compatibility
union ptr2uint
{
unsigned long u;
const void* p;
};
// separate offsets for each vbo
ptr2uint curOffset;
curOffset.p = elements_[0].pointer_;
std::map<unsigned int, VertexDeclaration_ptr2uint> vboOffsets;
std::map<unsigned int, VertexElement*> vboPrevElements;
for (unsigned int i = 1; i < numElements; ++i)
for (unsigned int i = 0; i < numElements; ++i)
{
if (elements_[i].pointer_)
curOffset.p = elements_[i].pointer_;
if (vboOffsets.find(elements_[i].vbo_) == vboOffsets.end())
{
vboOffsets[elements_[i].vbo_].p = elements_[i].pointer_;
vboPrevElements[elements_[i].vbo_] = &elements_[i];
}
}
for (unsigned int i = 0; i < numElements; ++i)
{
VertexElement* el = &elements_[i];
bool updateOffset = false;
if (el->pointer_)
{
vboOffsets[el->vbo_].p = el->pointer_;
vboPrevElements[el->vbo_] = el;
}
else
curOffset.u += getElementSize(&elements_[i-1]);
{
VertexElement* prevEl = vboPrevElements[el->vbo_];
if (prevEl != el)
{
updateOffset = true;
vboOffsets[el->vbo_].u += getElementSize(prevEl);
}
vboPrevElements[el->vbo_] = el;
}
elements_[i].pointer_ = curOffset.p;
if (updateOffset)
el->pointer_ = vboOffsets[el->vbo_].p;
}
}
......@@ -282,7 +338,7 @@ unsigned int VertexDeclaration::getGLTypeSize(unsigned int _type)
unsigned int VertexDeclaration::getElementSize(const VertexElement* _pElement)
{
return getGLTypeSize(_pElement->type_) * _pElement->numElements_;
return _pElement ? getGLTypeSize(_pElement->type_) * _pElement->numElements_ : 0;
}
......@@ -367,10 +423,11 @@ void VertexDeclaration::activateShaderPipeline(GLSL::Program* _prog) const
// setup correct attribute locations as specified
unsigned int numElements = getNumElements();
unsigned int vertexStride = getVertexStride();
for (unsigned int i = 0; i < numElements; ++i)
{
unsigned int vertexStride = getVertexStride(i);
const VertexElement* pElem = &elements_[i];
int loc = _prog->getAttributeLocation(pElem->shaderInputName_);
......@@ -384,9 +441,28 @@ void VertexDeclaration::activateShaderPipeline(GLSL::Program* _prog) const
if (pElem->usage_ == VERTEX_USAGE_BLENDINDICES)
normalizeElem = GL_FALSE;
GLint curVBO = 0;
if (pElem->vbo_)
{
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &curVBO);
glBindBuffer(GL_ARRAY_BUFFER, pElem->vbo_);
}
glVertexAttribPointer(loc, pElem->numElements_, pElem->type_, normalizeElem, vertexStride, pElem->pointer_);
if (supportsInstancedArrays())
{
#ifdef GL_ARB_instanced_arrays
glVertexAttribDivisor(loc, pElem->divisor_);
#endif
}
else if (pElem->divisor_)
std::cerr << "error: VertexDeclaration::activateShaderPipeline - instanced arrays not supported by gpu!" << std::endl;
glEnableVertexAttribArray(loc);
if (curVBO)
glBindBuffer(GL_ARRAY_BUFFER, curVBO);
}
}
}
......@@ -438,9 +514,13 @@ const VertexElement* VertexDeclaration::findElementByUsage(VERTEX_USAGE _usage)
}
unsigned int VertexDeclaration::getVertexStride() const
unsigned int VertexDeclaration::getVertexStride(unsigned int i) const
{
return vertexStride_;
unsigned int vbo = getElement(i)->vbo_;
std::map<unsigned int, unsigned int>::const_iterator it = vertexStridesVBO_.find(vbo);
return (it != vertexStridesVBO_.end()) ? it->second : vertexStride_;
// return vertexStride_;
}
......@@ -509,12 +589,28 @@ QString VertexDeclaration::toString() const
<< ", count: " << el->numElements_
<< ", usage: " << usage
<< ", shader-input: " << el->shaderInputName_
<< ", offset: " << el->pointer_ << "]\n";
<< ", offset: " << el->pointer_
<< ", divisor: " << el->divisor_
<< ", vbo: " << el->vbo_
<< ", stride: " << getVertexStride(i)
<< "]\n";
}
return result;
}
bool VertexDeclaration::supportsInstancedArrays()
{
static int status_ = -1;
#ifdef GL_ARB_instanced_arrays
if (status_ < 0) // core in 3.3
status_ = (checkExtensionSupported("GL_ARB_instanced_arrays") || openGLVersion(3,3)) ? 1 : 0;
#endif
return status_ > 0;
}
//=============================================================================
} // namespace ACG
//=============================================================================
......@@ -46,6 +46,7 @@
#include <vector>
#include <list>
#include <map>
#include <QString>
#include <ACG/Config/ACGDefines.hh>
......@@ -75,11 +76,15 @@ enum VERTEX_USAGE
*/
struct ACGDLLEXPORT VertexElement
{
VertexElement();
unsigned int type_; //!< GL_FLOAT, GL_UNSIGNED_BYTE, GL_INT, ...
unsigned int numElements_; //!< how many elements of type_
VERTEX_USAGE usage_; //!< position, normal, shader input ..
const char* shaderInputName_; //!< set shader input name, if usage_ = VERTEX_USAGE_USER_DEFINED otherwise this is set automatically, if usage_ != VERTEX_USAGE_USER_DEFINED
const void* pointer_; //!< Offset in bytes to the first instance of this element in vertex buffer; Or address to vertex data in system memory
const void* pointer_; //!< Offset in bytes to the first occurrence of this element in vertex buffer; Or address to vertex data in system memory
unsigned int divisor_; //!< For instanced rendering: Step rate describing how many instances are drawn before advancing to the next element. Must be 0 if this element is not per instance data.
unsigned int vbo_; //!< Explicit vbo source of this element, set to 0 if the buffer bound at the time of activating the declaration should be used instead.
/*! interpret pointer_ as byte offset
*/
......@@ -90,21 +95,24 @@ struct ACGDLLEXPORT VertexElement
unsigned int getByteOffset() const;
};
/** \brief Class to define the layout of a vertex element
/** \brief Class to define the vertex input layout
*
* This class is used to specify how the elements (normals,positions,...) of a vertex element are setup.
* This class is used to specify vertex data layout (normals,positions,...).
*
* A vertex declaration is needed to describe the data layout of a vertex buffer:
* The layout of a vertex buffer typically includes:
* - number of vertex elements
* - element usage (position, normal, ... )
* - element types (float, int, ...)
* - memory alignment
* - data pointers (only in case of system memory buffers)
* - source vertex buffers
* - divisor (step rate) for instance data
* - vertex stride
*
*
* Example usage:
* Example usage: Interleaved vertex data
*
* Suppose you have an interleaved vertex buffer without memory alignment:
* Create a vertex declaration given the following vertex data:
* \code
* Vertex
* {
......@@ -117,28 +125,21 @@ struct ACGDLLEXPORT VertexElement
* }
* \endcode
*
* You would create the following VertexElements:
* The accompanying vertex declaration is created as follows:
* \code
* VertexElement elemArray[] = { {GL_FLOAT, 4, VERTEX_USAGE_POSITION, 0, {0} },
* {GL_FLOAT, 3, VERTEX_USAGE_NORMAL, 0, {0} },
* {GL_FLOAT, 2, VERTEX_USAGE_TEXCOORD, 0, {0} },
* {GL_UNSIGNED_BYTE, 4, VERTEX_USAGE_COLOR, 0, {0} },
* {GL_FLOAT, 4, VERTEX_USAGE_SHADER_INPUT, "inTangent", {0} },
* {GL_FLOAT, 2, VERTEX_USAGE_SHADER_INPUT, "inTexcoord2", {0}}
* };
* \endcode
* VertexDeclaration decl;
* decl.addElement(GL_FLOAT, 4, VERTEX_USAGE_POSITION);
* decl.addElement(GL_FLOAT, 3, VERTEX_USAGE_NORMAL);
* decl.addElement(GL_FLOAT, 2, VERTEX_USAGE_TEXCOORD);
* decl.addElement(GL_UNSIGNED_BYTE, 4, VERTEX_USAGE_COLOR);
* decl.addElement(GL_FLOAT, 4, VERTEX_USAGE_SHADER_INPUT, 0U, "inTangent");
* decl.addElement(GL_FLOAT, 2, VERTEX_USAGE_SHADER_INPUT, 0U, "inTexcoord2");
*
* equivalent to
* \code
* VertexElement elemArray[] = { {GL_FLOAT, 4, VERTEX_USAGE_POSITION, 0, {0} },
* {GL_FLOAT, 3, VERTEX_USAGE_NORMAL, 0, {4*4} },
* {GL_FLOAT, 2, VERTEX_USAGE_TEXCOORD, 0, {4*4 + 4*3} },
* {GL_UNSIGNED_BYTE, 4, VERTEX_USAGE_COLOR, 0, {4*4 + 4*3 + 4*2} },
* ..
* };
* \endcode
*
* in the vertex shader:
* Note that the element offsets are automatically computed, but it is possible to provide them manually of course.
*
* These elements are then available in a vertex shader with the following input semantics:
* \code
* in vec4 inPosition;
* ...
......@@ -151,38 +152,110 @@ struct ACGDLLEXPORT VertexElement
*
* ---------------------------------------------------------------------------------
*
* Example use: multiple buffer source in system memory
*
* Suppose you have the following arrays in system memory
* Example usage: Multiple source buffers in system memory
*
* Given are system memory buffers as vertex data source for drawing:
* \code
* float4* pVertexPositions;
* ubyte4* pVertexColors;
* float3* pVertexNormals;
* \endcode
*
* important is, that you define a vertex stride of 0 and define the data pointer in the vertex elements instead
*
* The correct vertex declaration to use is:
* It is important that the vertex stride is manually set to 0!! and that the element data pointers are set accordingly:
*
* \code
* VertexElement elemArray[] = { {GL_FLOAT, 4, VERTEX_USAGE_POSITION, 0, pVertexPositions},
* {GL_FLOAT, 3, VERTEX_USAGE_NORMAL, 0, pVertexNormals},
* {GL_UNSIGNED_BYTE, 4, VERTEX_USAGE_COLOR, 0, pVertexColors}
* };
*
* VertexDeclaration* pDecl = new VertexDeclaration();
*
* pDecl->addElements(3, elemArray);
* pDecl->setVertexStride(0);
* VertexDeclaration decl;
* decl.addElement(GL_FLOAT, 4, VERTEX_USAGE_POSITION, pVertexPositions);
* decl.addElement(GL_FLOAT, 3, VERTEX_USAGE_NORMAL, pVertexNormals);
* decl.addElement(GL_UNSIGNED_BYTE, 4, VERTEX_USAGE_COLOR, pVertexColors);
* decl.setVertexStride(0);
* \endcode
*
*
* Note:
* - vertex stride = 0
* - assign data address in vertex elements and don't change the data location later
* - order of elements is not important in this case
* - mixing interleaved vbo and system memory elements in the same declaration is forbidden
*
* ---------------------------------------------------------------------------------
*
* Example usage: Instancing from vbo
*
* Geometry instancing can be implemented in several ways:
* - compute instance transform procedurally from gl_InstanceID in shader
* - store per instance data in a texture and use vertex texture fetch via gl_InstanceID
* - store per instance data in a vbo and stream these as input to a vertex shader (ARB_instanced_arrays).
*
* The first two methods can be implemented without modifying the vertex declaration,
* so this example focuses on the last method, in which instance data is streamed from a vbo.
*
* Given a mesh with the following vertex elements:
* \code
* Vertex
* {
* float4 pos
* float3 normal
* float2 texcoord
* }
* \endcode
*
* Each instance of this mesh should have a different world transform and a different color.
* So the per instance data is:
* \code
* InstanceData
* {
* mat4 modelview
* uint color
* }
* \endcode
*
* This per instance data is stored in a separate GL_ARRAY_BUFFER vbo.
*
* So there are two vbos involved in the draw call:
* geometryVBO - stores static Vertex data of the mesh
* instanceVBO - stores InstanceData for each instance
*
* The vertex declaration for the instanced draw call is initialized as follows:
*
* \code
* VertexDeclaration decl;
* decl.addElement(GL_FLOAT, 4, VERTEX_USAGE_POSITION, 0U, 0, 0, geometryVBO.id()); // mesh vertex data layout
* decl.addElement(GL_FLOAT, 3, VERTEX_USAGE_NORMAL, 0U, 0, 0, geometryVBO.id());
* decl.addElement(GL_FLOAT, 2, VERTEX_USAGE_TEXCOORD, 0U, 0, 0, geometryVBO.id());
*
* decl.addElement(GL_FLOAT, 4, VERTEX_USAGE_SHADER_INPUT, 0U, "inModelView0", 1, instanceVBO.id()); // instance data layout
* decl.addElement(GL_FLOAT, 4, VERTEX_USAGE_SHADER_INPUT, 0U, "inModelView1", 1, instanceVBO.id());
* decl.addElement(GL_FLOAT, 4, VERTEX_USAGE_SHADER_INPUT, 0U, "inModelView2", 1, instanceVBO.id());
* decl.addElement(GL_FLOAT, 4, VERTEX_USAGE_SHADER_INPUT, 0U, "inModelView3", 1, instanceVBO.id());
* decl.addElement(GL_FLOAT, 4, VERTEX_USAGE_COLOR, 0U, 0, 1, instanceVBO.id());
* \endcode
*
* The 4x4 modelview matrix has to be split up into float4 elements.
*
* Using this setup, a call to decl.activateShaderPipeline() is enough to prepare an instanced draw call.
*
* Finally, instancing requires a customized vertex shader that makes use of the per instance data:
* \code
* ..
* in vec4 inModelView0;
* in vec4 inModelView1;
* in vec4 inModelView2;
* in vec4 inModelView3;
* ..
*
* void main()
* {
* ..
* vec4 posVS;
* posVS.x = dot(inModelView0, inPosition);
* posVS.y = dot(inModelView1, inPosition);
* ..
* gl_Position = projection * posVS;
* }
* \endcode
*/
class ACGDLLEXPORT VertexDeclaration
{
public:
......@@ -196,11 +269,11 @@ public:
/*! append one element to declarations, direct method
*/
void addElement(unsigned int _type, unsigned int _numElements, VERTEX_USAGE _usage, const void* _pointer, const char* _shaderInputName = 0);
void addElement(unsigned int _type, unsigned int _numElements, VERTEX_USAGE _usage, const void* _pointer, const char* _shaderInputName = 0, unsigned int _divisor = 0, unsigned int _vbo = 0);
/*! append one element to declarations, direct method
*/
void addElement(unsigned int _type, unsigned int _numElements, VERTEX_USAGE _usage, size_t _byteOffset = 0, const char* _shaderInputName = 0);
void addElement(unsigned int _type, unsigned int _numElements, VERTEX_USAGE _usage, size_t _byteOffset = 0, const char* _shaderInputName = 0, unsigned int _divisor = 0, unsigned int _vbo = 0);
/*! append multiple element declarations
*/
......@@ -237,9 +310,9 @@ public:
*/
void setVertexStride(unsigned int _stride);
/*! get vertex size
/*! get vertex size (for element i, for multi vbo support)
*/
unsigned int getVertexStride() const;
unsigned int getVertexStride(unsigned int i = 0) const;
/*! get num of vertex elements
*/
......@@ -265,6 +338,9 @@ public:
*/
static unsigned int getElementSize(const VertexElement* _pElement);
/*! Check hw support for streaming instance data from vbo
*/
static bool supportsInstancedArrays();
/*! Returns a string describing the vertex format for debugging purpose.
*/
......@@ -289,6 +365,9 @@ private:
/// Offset in bytes between each vertex
unsigned int vertexStride_;
/// Map vbo to offset in bytes between each vertex in that vbo
std::map<unsigned int, unsigned int> vertexStridesVBO_;
/// Flag that indicates, whether the stride was set by user or derived automatically
int strideUserDefined_;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment