Support storing index data in VBOs.

Use index VBOs in model renderers, for performance.
Be more explicit about static/dynamic VBOs.
Add VBO usage to renderer stats.
Clean up some obsolete unused code.

This was SVN commit r9052.
This commit is contained in:
Ykkrosh 2011-03-13 18:58:09 +00:00
parent b70a0a5b5a
commit 2f28b07356
14 changed files with 250 additions and 232 deletions

View File

@ -43,7 +43,7 @@
struct FFModelDef : public CModelDefRPrivate
{
/// Indices are the same for all models, so share them
u16* m_Indices;
VertexIndexArray m_IndexArray;
/// Static per-CModelDef vertex array
VertexArray m_Array;
@ -52,12 +52,11 @@ struct FFModelDef : public CModelDefRPrivate
VertexArray::Attribute m_UV;
FFModelDef(const CModelDefPtr& mdef);
~FFModelDef() { delete[] m_Indices; }
};
FFModelDef::FFModelDef(const CModelDefPtr& mdef)
: m_Array(false)
: m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW)
{
size_t numVertices = mdef->GetNumVertices();
@ -75,8 +74,11 @@ FFModelDef::FFModelDef(const CModelDefPtr& mdef)
m_Array.Upload();
m_Array.FreeBackingStore();
m_Indices = new u16[mdef->GetNumFaces()*3];
ModelRenderer::BuildIndices(mdef, m_Indices);
m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3);
m_IndexArray.Layout();
ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator());
m_IndexArray.Upload();
m_IndexArray.FreeBackingStore();
}
@ -89,7 +91,7 @@ struct FFModel
VertexArray::Attribute m_Position;
VertexArray::Attribute m_Color;
FFModel() : m_Array(true) { }
FFModel() : m_Array(GL_DYNAMIC_DRAW) { }
};
@ -267,6 +269,8 @@ void FixedFunctionModelRenderer::RenderModel(int streamflags, CModel* model, voi
u8* base = ffmodel->m_Array.Bind();
GLsizei stride = (GLsizei)ffmodel->m_Array.GetStride();
u8* indexBase = m->ffmodeldef->m_IndexArray.Bind();
glVertexPointer(3, GL_FLOAT, stride, base + ffmodel->m_Position.offset);
if (streamflags & STREAM_COLOR)
glColorPointer(3, ffmodel->m_Color.type, stride, base + ffmodel->m_Color.offset);
@ -284,7 +288,7 @@ void FixedFunctionModelRenderer::RenderModel(int streamflags, CModel* model, voi
if (!g_Renderer.m_SkipSubmit) {
pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1,
(GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->ffmodeldef->m_Indices);
(GLsizei)numFaces*3, GL_UNSIGNED_SHORT, indexBase);
}
// bump stats

View File

@ -46,18 +46,20 @@
struct HWLModelDef : public CModelDefRPrivate
{
/// Indices are the same for all models, so share them
u16* m_Indices;
VertexIndexArray m_IndexArray;
HWLModelDef(const CModelDefPtr& mdef);
~HWLModelDef() { delete[] m_Indices; }
};
HWLModelDef::HWLModelDef(const CModelDefPtr& mdef)
: m_IndexArray(GL_STATIC_DRAW)
{
m_Indices = new u16[mdef->GetNumFaces()*3];
ModelRenderer::BuildIndices(mdef, m_Indices);
m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3);
m_IndexArray.Layout();
ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator());
m_IndexArray.Upload();
m_IndexArray.FreeBackingStore();
}
@ -73,7 +75,7 @@ struct HWLModel
/// UV is stored per-CModel in order to avoid space wastage due to alignment
VertexArray::Attribute m_UV;
HWLModel() : m_Array(true) { }
HWLModel() : m_Array(GL_DYNAMIC_DRAW) { }
};
@ -301,6 +303,8 @@ void HWLightingModelRenderer::RenderModel(int streamflags, CModel* model, void*
u8* base = hwlmodel->m_Array.Bind();
GLsizei stride = (GLsizei)hwlmodel->m_Array.GetStride();
u8* indexBase = m->hwlmodeldef->m_IndexArray.Bind();
glVertexPointer(3, GL_FLOAT, stride, base + hwlmodel->m_Position.offset);
if (streamflags & STREAM_COLOR)
{
@ -319,7 +323,7 @@ void HWLightingModelRenderer::RenderModel(int streamflags, CModel* model, void*
if (!g_Renderer.m_SkipSubmit) {
pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1,
(GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->hwlmodeldef->m_Indices);
(GLsizei)numFaces*3, GL_UNSIGNED_SHORT, indexBase);
}
// bump stats

View File

@ -54,16 +54,15 @@ struct IModelDef : public CModelDefRPrivate
VertexArray::Attribute m_UV;
/// Indices are the same for all models, so share them
u16* m_Indices;
VertexIndexArray m_IndexArray;
IModelDef(const CModelDefPtr& mdef);
~IModelDef() { delete[] m_Indices; }
};
IModelDef::IModelDef(const CModelDefPtr& mdef)
: m_Array(false)
: m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW)
{
size_t numVertices = mdef->GetNumVertices();
@ -92,8 +91,11 @@ IModelDef::IModelDef(const CModelDefPtr& mdef)
m_Array.Upload();
m_Array.FreeBackingStore();
m_Indices = new u16[mdef->GetNumFaces()*3];
ModelRenderer::BuildIndices(mdef, m_Indices);
m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3);
m_IndexArray.Layout();
ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator());
m_IndexArray.Upload();
m_IndexArray.FreeBackingStore();
}
@ -105,6 +107,9 @@ struct InstancingModelRendererInternals
/// Previously prepared modeldef
IModelDef* imodeldef;
/// Index base for imodeldef
u8* imodeldefIndexBase;
/// If true, primary color will only contain the diffuse term
bool colorIsDiffuseOnly;
@ -243,6 +248,8 @@ void InstancingModelRenderer::PrepareModelDef(int streamflags, const CModelDefPt
u8* base = m->imodeldef->m_Array.Bind();
GLsizei stride = (GLsizei)m->imodeldef->m_Array.GetStride();
m->imodeldefIndexBase = m->imodeldef->m_IndexArray.Bind();
glVertexPointer(3, GL_FLOAT, stride, base + m->imodeldef->m_Position.offset);
if (streamflags & STREAM_COLOR)
{
@ -274,7 +281,7 @@ void InstancingModelRenderer::RenderModel(int streamflags, CModel* model, void*
if (!g_Renderer.m_SkipSubmit) {
pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1,
(GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldef->m_Indices);
(GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase);
}
// bump stats

View File

@ -161,7 +161,7 @@ void ModelRenderer::BuildUV(
// Build default indices array.
void ModelRenderer::BuildIndices(
const CModelDefPtr& mdef,
u16* Indices)
const VertexArrayIterator<u16>& Indices)
{
size_t idxidx = 0;
SModelFace* faces = mdef->GetFaces();

View File

@ -243,7 +243,7 @@ public:
*/
static void BuildIndices(
const CModelDefPtr& mdef,
u16* Indices);
const VertexArrayIterator<u16>& Indices);
};

View File

@ -283,7 +283,7 @@ void CPatchRData::BuildBlends()
{
// Construct vertex buffer
m_VBBlends = g_VBMan.Allocate(sizeof(SBlendVertex), m_BlendVertices.size(), true);
m_VBBlends = g_VBMan.Allocate(sizeof(SBlendVertex), m_BlendVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends, &m_BlendVertices[0]);
debug_assert(m_VBBlends->m_Index < 65536);
@ -470,9 +470,9 @@ void CPatchRData::BuildVertices()
}
// upload to vertex buffer
if (!m_VBBase) {
m_VBBase=g_VBMan.Allocate(sizeof(SBaseVertex),vsize*vsize,true);
}
if (!m_VBBase)
m_VBBase = g_VBMan.Allocate(sizeof(SBaseVertex), vsize * vsize, GL_STATIC_DRAW, GL_ARRAY_BUFFER);
m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices);
}
@ -551,7 +551,7 @@ void CPatchRData::BuildSides()
return;
if (!m_VBSides)
m_VBSides = g_VBMan.Allocate(sizeof(SSideVertex), sideVertices.size(), true);
m_VBSides = g_VBMan.Allocate(sizeof(SSideVertex), sideVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
m_VBSides->m_Owner->UpdateChunkVertices(m_VBSides, &sideVertices[0]);
}

View File

@ -64,6 +64,7 @@
#include "renderer/TerrainOverlay.h"
#include "renderer/TerrainRenderer.h"
#include "renderer/TransparencyRenderer.h"
#include "renderer/VertexBufferManager.h"
#include "renderer/WaterManager.h"
@ -104,6 +105,8 @@ private:
Row_TerrainTris,
Row_ModelTris,
Row_BlendSplats,
Row_VBReserved,
Row_VBAllocated,
// Must be last to count number of rows
NumberRows
@ -175,6 +178,18 @@ CStr CRendererStatsTable::GetCellText(size_t row, size_t col)
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_BlendSplats);
return buf;
case Row_VBReserved:
if (col == 0)
return "VB bytes reserved";
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_VBMan.GetBytesReserved());
return buf;
case Row_VBAllocated:
if (col == 0)
return "VB bytes allocated";
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_VBMan.GetBytesAllocated());
return buf;
default:
return "???";
}

View File

@ -62,7 +62,7 @@ struct PSModelDef : public CModelDefRPrivate
};
PSModelDef::PSModelDef(const CModelDefPtr& mdef)
: m_Array(false)
: m_Array(GL_STATIC_DRAW)
{
m_UV.type = GL_FLOAT;
m_UV.elems = 2;
@ -113,7 +113,7 @@ struct PSModel
};
PSModel::PSModel(CModel* model)
: m_Model(model), m_Array(true)
: m_Model(model), m_Array(GL_DYNAMIC_DRAW)
{
CModelDefPtr mdef = m_Model->GetModelDef();

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -17,6 +17,7 @@
#include "precompiled.h"
#include "lib/bits.h"
#include "lib/ogl.h"
#include "maths/Vector3D.h"
#include "maths/Vector4D.h"
@ -26,9 +27,10 @@
#include "renderer/VertexBufferManager.h"
VertexArray::VertexArray(bool dynamic)
VertexArray::VertexArray(GLenum usage, GLenum target)
{
m_Dynamic = dynamic;
m_Usage = usage;
m_Target = target;
m_NumVertices = 0;
m_VB = 0;
@ -70,7 +72,7 @@ void VertexArray::SetNumVertices(size_t num)
// Add vertex attributes like Position, Normal, UV
void VertexArray::AddAttribute(Attribute* attr)
{
debug_assert((attr->type == GL_FLOAT || attr->type == GL_UNSIGNED_BYTE) && "Unsupported attribute type");
debug_assert((attr->type == GL_FLOAT || attr->type == GL_UNSIGNED_SHORT || attr->type == GL_UNSIGNED_BYTE) && "Unsupported attribute type");
debug_assert(attr->elems >= 1 && attr->elems <= 4);
attr->vertexArray = this;
@ -133,6 +135,16 @@ VertexArrayIterator<SColor4ub> VertexArray::Attribute::GetIterator<SColor4ub>()
return vertexArray->MakeIterator<SColor4ub>(this);
}
template<>
VertexArrayIterator<u16> VertexArray::Attribute::GetIterator<u16>() const
{
debug_assert(vertexArray);
debug_assert(type == GL_UNSIGNED_SHORT);
debug_assert(elems >= 1);
return vertexArray->MakeIterator<u16>(this);
}
static size_t RoundStride(size_t stride)
@ -146,7 +158,7 @@ static size_t RoundStride(size_t stride)
if (stride <= 16)
return 16;
return (stride + 31) & ~31;
return round_up(stride, (size_t)32);
}
// Re-layout by assigning offsets on a first-come first-serve basis,
@ -160,7 +172,7 @@ void VertexArray::Layout()
//debug_printf(L"Layouting VertexArray\n");
for(int idx = (int)m_Attributes.size()-1; idx >= 0; --idx)
for (ssize_t idx = m_Attributes.size()-1; idx >= 0; --idx)
{
Attribute* attr = m_Attributes[idx];
@ -173,23 +185,31 @@ void VertexArray::Layout()
case GL_UNSIGNED_BYTE:
attrSize = sizeof(GLubyte);
break;
case GL_UNSIGNED_SHORT:
attrSize = sizeof(GLushort);
break;
case GL_FLOAT:
attrSize = sizeof(GLfloat);
break;
default:
attrSize = 0;
debug_warn(L"Bad AttributeInfo::Type"); break;
debug_warn(L"Bad Attribute::type"); break;
}
attrSize *= attr->elems;
attr->offset = m_Stride;
m_Stride = (m_Stride + attrSize + 3) & ~3;
m_Stride += attrSize;
if (m_Target == GL_ARRAY_BUFFER)
m_Stride = round_up(m_Stride, (size_t)4);
//debug_printf(L"%i: offset: %u\n", idx, attr->offset);
}
m_Stride = RoundStride(m_Stride);
if (m_Target == GL_ARRAY_BUFFER)
m_Stride = RoundStride(m_Stride);
//debug_printf(L"Stride: %u\n", m_Stride);
@ -205,7 +225,7 @@ void VertexArray::Upload()
debug_assert(m_BackingStore);
if (!m_VB)
m_VB = g_VBMan.Allocate(m_Stride, m_NumVertices, m_Dynamic);
m_VB = g_VBMan.Allocate(m_Stride, m_NumVertices, m_Usage, m_Target);
if (!m_VB) // failed to allocate VBO
return;
@ -233,3 +253,17 @@ void VertexArray::FreeBackingStore()
m_BackingStore = 0;
}
VertexIndexArray::VertexIndexArray(GLenum usage) :
VertexArray(usage, GL_ELEMENT_ARRAY_BUFFER)
{
m_Attr.type = GL_UNSIGNED_SHORT;
m_Attr.elems = 1;
AddAttribute(&m_Attr);
}
VertexArrayIterator<u16> VertexIndexArray::GetIterator() const
{
return m_Attr.GetIterator<u16>();
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -116,8 +116,6 @@ private:
// support with hardcoded vertex structures.
// This class chooses the vertex layout at runtime, based on the attributes
// that are actually needed.
// Furthermore, it stores dynamic and static attributes in different
// vertex buffers, so that needless re-uploads of static data is avoided.
//
// Note that this class will not allocate any OpenGL resources until one
// of the Upload functions is called.
@ -126,7 +124,7 @@ class VertexArray
public:
struct Attribute
{
// Data type. Currently supported: GL_FLOAT
// Data type. Currently supported: GL_FLOAT, GL_UNSIGNED_BYTE
GLenum type;
// How many elements per vertex (e.g. 3 for RGB, 2 for UV)
GLuint elems;
@ -139,7 +137,7 @@ public:
Attribute() : type(0), elems(0), offset(0), vertexArray(0) { }
// Get an iterator for the given attribute that initially points at the first vertex.
// Supported types T: CVector3D, CVector4D, float[], SColor3ub, SColor4ub
// Supported types T: CVector3D, CVector4D, float[2], SColor3ub, SColor4ub
// This function verifies at runtime that the requested type T matches
// the attribute definition passed to AddAttribute().
template<typename T>
@ -147,7 +145,7 @@ public:
};
public:
VertexArray(bool dynamic);
VertexArray(GLenum usage, GLenum target = GL_ARRAY_BUFFER);
~VertexArray();
// Set the number of vertices stored in the array
@ -163,7 +161,7 @@ public:
// attributes.
// All vertex data is lost when a vertex array is re-layouted.
void Layout();
// (Re-)Upload the static/dynamic attributes of the vertex array.
// (Re-)Upload the attributes of the vertex array.
void Upload();
// Bind this array, returns the base address for calls to glVertexPointer etc.
u8* Bind();
@ -182,7 +180,8 @@ private:
return VertexArrayIterator<T>(m_BackingStore + attr->offset, m_Stride);
}
bool m_Dynamic;
GLenum m_Usage;
GLenum m_Target;
size_t m_NumVertices;
std::vector<Attribute*> m_Attributes;
@ -191,5 +190,21 @@ private:
char* m_BackingStore;
};
/**
* A VertexArray that is specialised to handle 16-bit array indices.
* Call Bind() and pass the return value to the indices parameter of
* glDrawElements/glDrawRangeElements/glMultiDrawElements.
* Use CVertexBuffer::Unbind() to unbind the array.
*/
class VertexIndexArray : public VertexArray
{
public:
VertexIndexArray(GLenum usage);
VertexArrayIterator<u16> GetIterator() const;
private:
Attribute m_Attr;
};
#endif // INCLUDED_VERTEXARRAY

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -16,7 +16,7 @@
*/
/*
* encapsulation of VBOs with batching and sharing
* encapsulation of VBOs with sharing
*/
#include "precompiled.h"
@ -28,59 +28,24 @@
#include "VertexBufferManager.h"
#include "ps/CLogger.h"
ERROR_GROUP(Renderer);
ERROR_TYPE(Renderer, VBOFailed);
///////////////////////////////////////////////////////////////////////////////
// shared list of all free batch objects
std::vector<CVertexBuffer::Batch *> CVertexBuffer::m_FreeBatches;
// NOTE: This global variable is here (as opposed to in VertexBufferManager.cpp,
// as would be logical) to make sure that m_FreeBatches is freed in the right
// order relative to the VertexBufferManager
CVertexBufferManager g_VBMan;
///////////////////////////////////////////////////////////////////////////////
// Call at shutdown to free memory
void CVertexBuffer::Shutdown()
{
for(std::vector<Batch*>::iterator iter=m_FreeBatches.begin();iter!=m_FreeBatches.end();++iter)
{
delete *iter;
}
}
///////////////////////////////////////////////////////////////////////////////
// CVertexBuffer constructor
CVertexBuffer::CVertexBuffer(size_t vertexSize,bool dynamic)
: m_VertexSize(vertexSize), m_Handle(0), m_SysMem(0), m_Dynamic(dynamic)
CVertexBuffer::CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target)
: m_VertexSize(vertexSize), m_Handle(0), m_SysMem(0), m_Usage(usage), m_Target(target)
{
size_t size = MAX_VB_SIZE_BYTES;
// allocate raw buffer
if (g_Renderer.m_Caps.m_VBO) {
// TODO: Detect when VBO failed, then fall back to system memory
// (and copy all old VBOs into there, because it needs to be
// consistent).
// (PT: Disabled the VBOFailed test because it's not very useful at the
// moment (it'll just cause the program to terminate, and I don't think
// it has ever helped to discover any problems, and a later ogl_WarnIfError()
// will tell us if there were any VBO issues anyway), and so it's a
// waste of time to call glGetError so frequently.)
// glGetError(); // clear the error state
if (g_Renderer.m_Caps.m_VBO)
{
pglGenBuffersARB(1, &m_Handle);
pglBindBufferARB(GL_ARRAY_BUFFER_ARB, m_Handle);
// if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed();
pglBufferDataARB(GL_ARRAY_BUFFER_ARB, size, 0, m_Dynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB);
// if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed();
pglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
} else {
m_SysMem=new u8[size];
pglBindBufferARB(m_Target, m_Handle);
pglBufferDataARB(m_Target, size, 0, m_Usage);
pglBindBufferARB(m_Target, 0);
}
else
{
m_SysMem = new u8[size];
}
// store max/free vertex counts
@ -98,15 +63,18 @@ CVertexBuffer::CVertexBuffer(size_t vertexSize,bool dynamic)
// CVertexBuffer destructor
CVertexBuffer::~CVertexBuffer()
{
if (m_Handle) {
pglDeleteBuffersARB(1,&m_Handle);
} else if (m_SysMem) {
if (m_Handle)
{
pglDeleteBuffersARB(1, &m_Handle);
}
else if (m_SysMem)
{
delete[] m_SysMem;
}
// janwas 2004-06-14: release freelist
typedef std::list<VBChunk*>::iterator Iter;
for(Iter iter=m_FreeList.begin();iter!=m_FreeList.end();++iter)
for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter)
delete *iter;
}
@ -114,13 +82,15 @@ CVertexBuffer::~CVertexBuffer()
// Allocate: try to allocate a buffer of given number of vertices (each of
// given size), with the given type, and using the given texture - return null
// if no free chunks available
CVertexBuffer::VBChunk* CVertexBuffer::Allocate(size_t vertexSize,size_t numVertices,bool dynamic)
CVertexBuffer::VBChunk* CVertexBuffer::Allocate(size_t vertexSize, size_t numVertices, GLenum usage, GLenum target)
{
// check this is the right kind of buffer
if (dynamic!=m_Dynamic || vertexSize!=m_VertexSize) return 0;
if (usage != m_Usage || target != m_Target || vertexSize != m_VertexSize)
return 0;
// quick check there's enough vertices spare to allocate
if (numVertices>m_FreeVertices) return 0;
if (numVertices > m_FreeVertices)
return 0;
// trawl free list looking for first free chunk with enough space
VBChunk* chunk=0;
@ -130,6 +100,7 @@ CVertexBuffer::VBChunk* CVertexBuffer::Allocate(size_t vertexSize,size_t numVert
chunk=*iter;
// remove this chunk from the free list
m_FreeList.erase(iter);
m_FreeVertices -= chunk->m_Count;
// no need to search further ..
break;
}
@ -142,16 +113,17 @@ CVertexBuffer::VBChunk* CVertexBuffer::Allocate(size_t vertexSize,size_t numVert
// split chunk into two; - allocate a new chunk using all unused vertices in the
// found chunk, and add it to the free list
if (chunk->m_Count > numVertices) {
VBChunk* newchunk=new VBChunk;
newchunk->m_Owner=this;
newchunk->m_Count=chunk->m_Count-numVertices;
newchunk->m_Index=chunk->m_Index+numVertices;
if (chunk->m_Count > numVertices)
{
VBChunk* newchunk = new VBChunk;
newchunk->m_Owner = this;
newchunk->m_Count = chunk->m_Count - numVertices;
newchunk->m_Index = chunk->m_Index + numVertices;
m_FreeList.push_front(newchunk);
m_FreeVertices += newchunk->m_Count;
// resize given chunk, resize total available free vertices
chunk->m_Count=numVertices;
m_FreeVertices-=numVertices;
// resize given chunk
chunk->m_Count = numVertices;
}
// return found chunk
@ -166,63 +138,22 @@ void CVertexBuffer::Release(VBChunk* chunk)
// TODO, RC - need to merge available chunks where possible to avoid
// excessive fragmentation of vertex buffer space
m_FreeList.push_front(chunk);
m_FreeVertices+=chunk->m_Count;
m_FreeVertices += chunk->m_Count;
}
///////////////////////////////////////////////////////////////////////////////
// ClearBatchIndices: clear lists of all batches
void CVertexBuffer::ClearBatchIndices()
{
for (size_t i=0;i<m_Batches.size();i++) {
m_Batches[i]->m_IndexData.clear();
m_FreeBatches.push_back(m_Batches[i]);
}
m_Batches.clear();
}
///////////////////////////////////////////////////////////////////////////////
// AppendBatch: add a batch to the render list for this buffer
void CVertexBuffer::AppendBatch(VBChunk* UNUSED(chunk),Handle texture,size_t numIndices,u16* indices)
{
// try and find a batch using this texture
size_t i;
Batch* batch=0;
for (i=0;i<m_Batches.size();++i) {
if (m_Batches[i]->m_Texture==texture) {
batch=m_Batches[i];
break;
}
}
if (!batch) {
if (m_FreeBatches.size()) {
batch=m_FreeBatches.back();
m_FreeBatches.pop_back();
} else {
batch=new Batch();
}
m_Batches.push_back(batch);
batch->m_Texture=texture;
}
// resize the chunk's batch to fit its indices
batch->m_IndexData.push_back(std::pair<size_t,u16*>(numIndices,indices));
// memcpy(&batch->m_Indices[0]+cursize,indices,sizeof(u16)*numIndices);
}
///////////////////////////////////////////////////////////////////////////////
// UpdateChunkVertices: update vertex data for given chunk
void CVertexBuffer::UpdateChunkVertices(VBChunk* chunk,void* data)
{
if (g_Renderer.m_Caps.m_VBO) {
if (g_Renderer.m_Caps.m_VBO)
{
debug_assert(m_Handle);
// glGetError(); // clear the error state
pglBindBufferARB(GL_ARRAY_BUFFER_ARB, m_Handle);
pglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, chunk->m_Index * m_VertexSize, chunk->m_Count * m_VertexSize, data);
pglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
// if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed();
} else {
pglBindBufferARB(m_Target, m_Handle);
pglBufferSubDataARB(m_Target, chunk->m_Index * m_VertexSize, chunk->m_Count * m_VertexSize, data);
pglBindBufferARB(m_Target, 0);
}
else
{
debug_assert(m_SysMem);
memcpy(m_SysMem + chunk->m_Index * m_VertexSize, data, chunk->m_Count * m_VertexSize);
}
@ -233,20 +164,32 @@ void CVertexBuffer::UpdateChunkVertices(VBChunk* chunk,void* data)
// to glVertexPointer ( + etc) calls
u8* CVertexBuffer::Bind()
{
u8* base;
if (g_Renderer.m_Caps.m_VBO) {
pglBindBufferARB(GL_ARRAY_BUFFER_ARB,m_Handle);
base=(u8*) 0;
} else {
base=(u8*) m_SysMem;
if (g_Renderer.m_Caps.m_VBO)
{
pglBindBufferARB(m_Target, m_Handle);
return (u8*)0;
}
else
{
return m_SysMem;
}
return base;
}
void CVertexBuffer::Unbind()
{
if (g_Renderer.m_Caps.m_VBO) {
pglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
if (g_Renderer.m_Caps.m_VBO)
{
pglBindBufferARB(GL_ARRAY_BUFFER, 0);
pglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
size_t CVertexBuffer::GetBytesReserved() const
{
return MAX_VB_SIZE_BYTES;
}
size_t CVertexBuffer::GetBytesAllocated() const
{
return (m_MaxVertices - m_FreeVertices) * m_VertexSize;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -30,25 +30,12 @@
// absolute maximum (bytewise) size of each GL vertex buffer object
#define MAX_VB_SIZE_BYTES (512*1024)
template <typename T>
struct ctor_dtor_logger;
///////////////////////////////////////////////////////////////////////////////
// CVertexBuffer: encapsulation of ARB_vertex_buffer_object, also supplying
// some additional functionality for batching and sharing buffers between
// multiple objects
// some additional functionality for sharing buffers between multiple objects
class CVertexBuffer
{
public:
// Batch: batch definition - defines indices into the VB to use when rendering,
// and the texture used when doing so
struct Batch {
// list of indices into the vertex buffer of primitives within the batch
std::vector<std::pair<size_t,u16*> > m_IndexData;
// texture to apply when rendering batch
Handle m_Texture;
};
// VBChunk: describes a portion of this vertex buffer
struct VBChunk
{
@ -62,7 +49,7 @@ public:
public:
// constructor, destructor
CVertexBuffer(size_t vertexSize, bool dynamic);
CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target);
~CVertexBuffer();
// bind to this buffer; return pointer to address required as parameter
@ -72,36 +59,25 @@ public:
// unbind any currently-bound buffer, so glVertexPointer etc calls will not attempt to use it
static void Unbind();
// clear lists of all batches
void ClearBatchIndices();
// add a batch to the render list for this buffer
void AppendBatch(VBChunk* chunk,Handle texture,size_t numIndices,u16* indices);
// update vertex data for given chunk
void UpdateChunkVertices(VBChunk* chunk,void* data);
// return this VBs batch list
const std::vector<Batch*>& GetBatches() const { return m_Batches; }
void UpdateChunkVertices(VBChunk* chunk, void* data);
size_t GetVertexSize() const { return m_VertexSize; }
// free memory
static void Shutdown();
size_t GetBytesReserved() const;
size_t GetBytesAllocated() const;
protected:
friend class CVertexBufferManager; // allow allocate only via CVertexBufferManager
// try to allocate a buffer of given number of vertices (each of given size),
// and with the given type - return null if no free chunks available
VBChunk* Allocate(size_t vertexSize,size_t numVertices,bool dynamic);
VBChunk* Allocate(size_t vertexSize, size_t numVertices, GLenum usage, GLenum target);
// return given chunk to this buffer
void Release(VBChunk* chunk);
private:
// set of all possible batches that can be used by this VB
std::vector<Batch*> m_Batches;
// vertex size of this vertex buffer
size_t m_VertexSize;
// number of vertices of above size in this buffer
@ -114,11 +90,10 @@ private:
GLuint m_Handle;
// raw system memory for systems not supporting VBOs
u8* m_SysMem;
// type of the buffer - dynamic?
bool m_Dynamic;
// list of all spare batches, shared between all vbs
static std::vector<Batch *> m_FreeBatches;
// usage type of the buffer (GL_STATIC_DRAW etc)
GLenum m_Usage;
// buffer target (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER)
GLenum m_Target;
};
#endif

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -21,10 +21,12 @@
#include "precompiled.h"
#include "lib/ogl.h"
#include "VertexBufferManager.h"
#include "lib/ogl.h"
#include "ps/CLogger.h"
CVertexBufferManager g_VBMan;
// janwas 2004-06-14: added dtor
@ -39,10 +41,9 @@ CVertexBufferManager::~CVertexBufferManager()
void CVertexBufferManager::Shutdown()
{
typedef std::list<CVertexBuffer*>::iterator Iter;
for (Iter iter=m_Buffers.begin();iter!=m_Buffers.end();++iter)
for (Iter iter = m_Buffers.begin(); iter != m_Buffers.end(); ++iter)
delete *iter;
CVertexBuffer::Shutdown();
m_Buffers.clear();
}
@ -50,10 +51,14 @@ void CVertexBufferManager::Shutdown()
// Allocate: try to allocate a buffer of given number of vertices (each of
// given size), with the given type, and using the given texture - return null
// if no free chunks available
CVertexBuffer::VBChunk* CVertexBufferManager::Allocate(size_t vertexSize, size_t numVertices, bool dynamic)
CVertexBuffer::VBChunk* CVertexBufferManager::Allocate(size_t vertexSize, size_t numVertices, GLenum usage, GLenum target)
{
CVertexBuffer::VBChunk* result=0;
debug_assert(usage == GL_STREAM_DRAW || usage == GL_STATIC_DRAW || usage == GL_DYNAMIC_DRAW);
debug_assert(target == GL_ARRAY_BUFFER || target == GL_ELEMENT_ARRAY_BUFFER);
// TODO, RC - run some sanity checks on allocation request
// iterate through all existing buffers testing for one that'll
@ -61,15 +66,15 @@ CVertexBuffer::VBChunk* CVertexBufferManager::Allocate(size_t vertexSize, size_t
typedef std::list<CVertexBuffer*>::iterator Iter;
for (Iter iter = m_Buffers.begin(); iter != m_Buffers.end(); ++iter) {
CVertexBuffer* buffer = *iter;
result = buffer->Allocate(vertexSize, numVertices, dynamic);
result = buffer->Allocate(vertexSize, numVertices, usage, target);
if (result)
return result;
}
// got this far; need to allocate a new buffer
CVertexBuffer* buffer = new CVertexBuffer(vertexSize, dynamic);
CVertexBuffer* buffer = new CVertexBuffer(vertexSize, usage, target);
m_Buffers.push_front(buffer);
result = buffer->Allocate(vertexSize, numVertices, dynamic);
result = buffer->Allocate(vertexSize, numVertices, usage, target);
if (!result)
{
@ -87,13 +92,25 @@ void CVertexBufferManager::Release(CVertexBuffer::VBChunk* chunk)
chunk->m_Owner->Release(chunk);
}
///////////////////////////////////////////////////////////////////////////////
// ClearBatchIndices: empty out the batch lists of all vertex buffers
void CVertexBufferManager::ClearBatchIndices()
size_t CVertexBufferManager::GetBytesReserved()
{
size_t total = 0;
typedef std::list<CVertexBuffer*>::iterator Iter;
for (Iter iter=m_Buffers.begin();iter!=m_Buffers.end();++iter) {
CVertexBuffer* buffer=*iter;
buffer->ClearBatchIndices();
}
for (Iter iter = m_Buffers.begin(); iter != m_Buffers.end(); ++iter)
total += (*iter)->GetBytesReserved();
return total;
}
size_t CVertexBufferManager::GetBytesAllocated()
{
size_t total = 0;
typedef std::list<CVertexBuffer*>::iterator Iter;
for (Iter iter = m_Buffers.begin(); iter != m_Buffers.end(); ++iter)
total += (*iter)->GetBytesAllocated();
return total;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -36,19 +36,24 @@ public:
// Explicit shutdown of the vertex buffer subsystem
void Shutdown();
// try to allocate a buffer of given number of vertices (each of given size),
// and with the given type - return null if no free chunks available
CVertexBuffer::VBChunk* Allocate(size_t vertexSize,size_t numVertices,bool dynamic);
/**
* Try to allocate a buffer of given number of vertices (each of given size),
* and with the given type.
* @param usage typically GL_STATIC_DRAW or GL_DYNAMIC_DRAW
* @param target typically GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER
* @return chunk, or NULL if no free chunks available
*/
CVertexBuffer::VBChunk* Allocate(size_t vertexSize, size_t numVertices, GLenum usage, GLenum target);
// return given chunk to it's owner
// return given chunk to its owner
void Release(CVertexBuffer::VBChunk* chunk);
// empty out the batch lists of all vertex buffers
void ClearBatchIndices();
// return list of all buffers
const std::list<CVertexBuffer*>& GetBufferList() const { return m_Buffers; }
size_t GetBytesReserved();
size_t GetBytesAllocated();
private:
// list of all known vertex buffers
std::list<CVertexBuffer*> m_Buffers;
@ -56,5 +61,4 @@ private:
extern CVertexBufferManager g_VBMan;
#endif