diff --git a/source/renderer/FixedFunctionModelRenderer.cpp b/source/renderer/FixedFunctionModelRenderer.cpp index 6e9a4a7319..f884f50c20 100644 --- a/source/renderer/FixedFunctionModelRenderer.cpp +++ b/source/renderer/FixedFunctionModelRenderer.cpp @@ -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 diff --git a/source/renderer/HWLightingModelRenderer.cpp b/source/renderer/HWLightingModelRenderer.cpp index b37475240f..953f1013a1 100644 --- a/source/renderer/HWLightingModelRenderer.cpp +++ b/source/renderer/HWLightingModelRenderer.cpp @@ -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 diff --git a/source/renderer/InstancingModelRenderer.cpp b/source/renderer/InstancingModelRenderer.cpp index 7f08e202a9..cdacc7102c 100644 --- a/source/renderer/InstancingModelRenderer.cpp +++ b/source/renderer/InstancingModelRenderer.cpp @@ -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 diff --git a/source/renderer/ModelRenderer.cpp b/source/renderer/ModelRenderer.cpp index bb9ad1bfad..57b264d3a4 100644 --- a/source/renderer/ModelRenderer.cpp +++ b/source/renderer/ModelRenderer.cpp @@ -161,7 +161,7 @@ void ModelRenderer::BuildUV( // Build default indices array. void ModelRenderer::BuildIndices( const CModelDefPtr& mdef, - u16* Indices) + const VertexArrayIterator& Indices) { size_t idxidx = 0; SModelFace* faces = mdef->GetFaces(); diff --git a/source/renderer/ModelRenderer.h b/source/renderer/ModelRenderer.h index 33f8ad0061..0f0df247a5 100644 --- a/source/renderer/ModelRenderer.h +++ b/source/renderer/ModelRenderer.h @@ -243,7 +243,7 @@ public: */ static void BuildIndices( const CModelDefPtr& mdef, - u16* Indices); + const VertexArrayIterator& Indices); }; diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp index a48415438f..5e70e93502 100644 --- a/source/renderer/PatchRData.cpp +++ b/source/renderer/PatchRData.cpp @@ -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]); } diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index aa3d4c08f0..b16f94a96a 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -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 "???"; } diff --git a/source/renderer/TransparencyRenderer.cpp b/source/renderer/TransparencyRenderer.cpp index 7bd90589ca..b9dff6968f 100644 --- a/source/renderer/TransparencyRenderer.cpp +++ b/source/renderer/TransparencyRenderer.cpp @@ -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(); diff --git a/source/renderer/VertexArray.cpp b/source/renderer/VertexArray.cpp index 511fb1ef60..edcf7e4c57 100644 --- a/source/renderer/VertexArray.cpp +++ b/source/renderer/VertexArray.cpp @@ -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 VertexArray::Attribute::GetIterator() return vertexArray->MakeIterator(this); } +template<> +VertexArrayIterator VertexArray::Attribute::GetIterator() const +{ + debug_assert(vertexArray); + debug_assert(type == GL_UNSIGNED_SHORT); + debug_assert(elems >= 1); + + return vertexArray->MakeIterator(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 VertexIndexArray::GetIterator() const +{ + return m_Attr.GetIterator(); +} diff --git a/source/renderer/VertexArray.h b/source/renderer/VertexArray.h index 6b6ebb908e..935d4b9b4e 100644 --- a/source/renderer/VertexArray.h +++ b/source/renderer/VertexArray.h @@ -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 @@ -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(m_BackingStore + attr->offset, m_Stride); } - bool m_Dynamic; + GLenum m_Usage; + GLenum m_Target; size_t m_NumVertices; std::vector 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 GetIterator() const; + +private: + Attribute m_Attr; +}; #endif // INCLUDED_VERTEXARRAY diff --git a/source/renderer/VertexBuffer.cpp b/source/renderer/VertexBuffer.cpp index fc8d87d045..8953f88513 100644 --- a/source/renderer/VertexBuffer.cpp +++ b/source/renderer/VertexBuffer.cpp @@ -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::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::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::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;im_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;im_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(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; +} diff --git a/source/renderer/VertexBuffer.h b/source/renderer/VertexBuffer.h index 30396aa18f..88db0e3a1e 100644 --- a/source/renderer/VertexBuffer.h +++ b/source/renderer/VertexBuffer.h @@ -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 -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 > 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& 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 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 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 diff --git a/source/renderer/VertexBufferManager.cpp b/source/renderer/VertexBufferManager.cpp index 9ff46e858d..d2c730a4c5 100644 --- a/source/renderer/VertexBufferManager.cpp +++ b/source/renderer/VertexBufferManager.cpp @@ -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::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::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::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::iterator Iter; + for (Iter iter = m_Buffers.begin(); iter != m_Buffers.end(); ++iter) + total += (*iter)->GetBytesAllocated(); + + return total; } diff --git a/source/renderer/VertexBufferManager.h b/source/renderer/VertexBufferManager.h index 88aa44d61b..2cff433933 100644 --- a/source/renderer/VertexBufferManager.h +++ b/source/renderer/VertexBufferManager.h @@ -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& GetBufferList() const { return m_Buffers; } + size_t GetBytesReserved(); + size_t GetBytesAllocated(); + private: // list of all known vertex buffers std::list m_Buffers; @@ -56,5 +61,4 @@ private: extern CVertexBufferManager g_VBMan; - #endif