1
0
forked from 0ad/0ad
0ad/source/renderer/VertexBuffer.cpp

226 lines
6.8 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// Name: VertexBuffer.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#include "precompiled.h"
#include "ps/Errors.h"
#include "ogl.h"
#include "Renderer.h"
#include "VertexBuffer.h"
#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)
{
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).
glGetError(); // clear the error state
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();
} else {
m_SysMem=new u8[size];
}
// store max/free vertex counts
m_MaxVertices=m_FreeVertices=size/vertexSize;
// create sole free chunk
VBChunk* chunk=new VBChunk;
chunk->m_Owner=this;
chunk->m_Count=m_FreeVertices;
chunk->m_Index=0;
m_FreeList.push_front(chunk);
}
///////////////////////////////////////////////////////////////////////////////
// CVertexBuffer destructor
CVertexBuffer::~CVertexBuffer()
{
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)
delete *iter;
}
///////////////////////////////////////////////////////////////////////////////
// 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)
{
// check this is the right kind of buffer
if (dynamic!=m_Dynamic || vertexSize!=m_VertexSize) return 0;
// quick check there's enough vertices spare to allocate
if (numVertices>m_FreeVertices) return 0;
// trawl free list looking for first free chunk with enough space
VBChunk* chunk=0;
typedef std::list<VBChunk*>::iterator Iter;
for (Iter iter=m_FreeList.begin();iter!=m_FreeList.end();++iter) {
if (numVertices<=(*iter)->m_Count) {
chunk=*iter;
// remove this chunk from the free list
// size_t size1=m_FreeList.size();
m_FreeList.erase(iter);
// size_t size2=m_FreeList.size();
// no need to search further ..
break;
}
}
if (!chunk) {
// no big enough spare chunk available
return 0;
}
// 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;
m_FreeList.push_front(newchunk);
// resize given chunk, resize total available free vertices
chunk->m_Count=numVertices;
m_FreeVertices-=numVertices;
}
// return found chunk
return chunk;
}
///////////////////////////////////////////////////////////////////////////////
// Release: return given chunk to this buffer
void CVertexBuffer::Release(VBChunk* chunk)
{
// add to free list
// 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;
}
///////////////////////////////////////////////////////////////////////////////
// ClearBatchIndices: clear lists of all batches
void CVertexBuffer::ClearBatchIndices()
{
for (uint 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));
// memcpy2(&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) {
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);
if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed();
} else {
debug_assert(m_SysMem);
memcpy2(m_SysMem+chunk->m_Index*m_VertexSize,data,chunk->m_Count*m_VertexSize);
}
}
///////////////////////////////////////////////////////////////////////////////
// Bind: bind to this buffer; return pointer to address required as parameter
// 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;
}
return base;
}