1
0
forked from 0ad/0ad
0ad/source/renderer/VertexBuffer.cpp
Ykkrosh ee3243ff92 # Fixes for simulation speed in scenario edtior. Various code cleanups.
Game, Simulation, etc: Separated 'update' and 'interpolate', and made
'update' return whether it's going fast enough (so callers can decide to
do more updates per render). Changed some time variables to 'double' so
they have enough precision in long games.
Atlas: Added "Fast" playback button. Made simulation sometimes go at
real-time speed, if it's just slightly too slow at rendering.
VertexBuffer: Removed some non-useful glGetError calls.
Entity: Commented out redundant Tick code. Fixed syntax error in
disabled code that confused the IDE.
Aura: Changed string code again, to simply use ASCII instead of UTF-16.
(SpiderMonkey seems to handle it just as efficiently, for small
strings.)
Misc: Some more minor header-file cleanup.
SVN log: Added feed link.

This was SVN commit r4807.
2007-01-24 20:17:28 +00:00

231 lines
7.1 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// Name: VertexBuffer.cpp
// Author: Rich Cross
// Contact: rich@wildfiregames.com
//
///////////////////////////////////////////////////////////////////////////////
#include "precompiled.h"
#include "ps/Errors.h"
#include "lib/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).
// (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 oglCheck()
// 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
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;
}