Ykkrosh
7dca91f26b
Rewrite font builder tool to be much simpler and to support more text effects. Change GUI to use new set of fonts. Switch font textures from TGA to PNG so they're easier for the font builder to create. Support RGBA font textures (for e.g. stroked text). Greatly improve text rendering performance by using vertex arrays. Fix rendering code leaving vertex buffers bound. Add 'clip' property to GUI text objects, to disable clipping when rendering. Delete part of unused console function registration system. This was SVN commit r7595.
253 lines
7.8 KiB
C++
253 lines
7.8 KiB
C++
/* Copyright (C) 2010 Wildfire Games.
|
|
* This file is part of 0 A.D.
|
|
*
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* encapsulation of VBOs with batching and sharing
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
#include "ps/Errors.h"
|
|
#include "lib/ogl.h"
|
|
#include "lib/sysdep/cpu.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 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
|
|
|
|
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];
|
|
}
|
|
|
|
// 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
|
|
m_FreeList.erase(iter);
|
|
// 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 (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));
|
|
// cpu_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) {
|
|
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 {
|
|
debug_assert(m_SysMem);
|
|
cpu_memcpy(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;
|
|
}
|
|
|
|
void CVertexBuffer::Unbind()
|
|
{
|
|
if (g_Renderer.m_Caps.m_VBO) {
|
|
pglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
|
}
|
|
}
|