Use pool allocator to avoid apparently expensive dynamic allocations when rendering terrain.

Add function to get pool usage, for debugging.

This was SVN commit r9132.
This commit is contained in:
Ykkrosh 2011-03-30 21:42:35 +00:00
parent 98fa860199
commit 1014da1f88
4 changed files with 106 additions and 21 deletions

View File

@ -119,3 +119,8 @@ void pool_free_all(Pool* p)
da_set_size(&p->da, 0);
}
size_t pool_committed(Pool* p)
{
return p->da.cur_size;
}

View File

@ -133,6 +133,17 @@ LIB_API void pool_free(Pool* p, void* el);
**/
LIB_API void pool_free_all(Pool* p);
/**
* Return the number of bytes committed in the pool's backing array.
*
* This is roughly the number of bytes allocated in this pool plus the
* unused freelist entries.
*
* @param p Pool*
* @return number of bytes
**/
LIB_API size_t pool_committed(Pool* p);
/**
* C++ wrapper on top of pool_alloc for fixed-size allocations (determined by sizeof(T))
@ -204,6 +215,11 @@ public:
return t;
}
size_t GetCommittedSize()
{
return pool_committed(&m_pool);
}
private:
Pool m_pool;
};

View File

@ -20,12 +20,11 @@
#include <set>
#include <algorithm>
#include <boost/tuple/tuple.hpp>
#include "graphics/GameView.h"
#include "graphics/LightEnv.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "lib/allocators/pool.h"
#include "lib/res/graphics/unifont.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
@ -663,21 +662,53 @@ void CPatchRData::Update()
// Types used for glMultiDrawElements batching:
// To minimise the cost of memory allocations, everything used for computing
// batches uses a pool allocator. (All allocations are short-lived so we can
// just throw away the whole pool at the end of each frame.)
// std::map types with appropriate pool allocators and default comparison operator
#define POOLED_BATCH_MAP(Key, Value) \
std::map<Key, Value, std::less<Key>, pool_allocator<std::pair<Key const, Value> > >
// Equivalent to "m[k]", when it returns a pool-allocated std::map (since we can't
// use the default constructor in that case)
template<typename M>
typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, RawPoolAllocator& pool)
{
return m.insert(std::make_pair(k,
typename M::mapped_type(typename M::mapped_type::key_compare(), typename M::mapped_type::allocator_type(pool))
)).first->second;
}
// Equivalent to "m[k]", when it returns a std::pair of pool-allocated std::vectors
template<typename M>
typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, RawPoolAllocator& pool)
{
return m.insert(std::make_pair(k, std::make_pair(
typename M::mapped_type::first_type(typename M::mapped_type::first_type::allocator_type(pool)),
typename M::mapped_type::second_type(typename M::mapped_type::second_type::allocator_type(pool))
))).first->second;
}
static const size_t POOL_SIZE = 4*MiB; // this should be enough for fairly huge maps
// Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
typedef std::pair<std::vector<GLint>, std::vector<void*> > BatchElements;
typedef std::pair<std::vector<GLint, pool_allocator<GLint> >, std::vector<void*, pool_allocator<void*> > > BatchElements;
// Group batches by index buffer
typedef std::map<CVertexBuffer*, BatchElements> IndexBufferBatches;
typedef POOLED_BATCH_MAP(CVertexBuffer*, BatchElements) IndexBufferBatches;
// Group batches by vertex buffer
typedef std::map<CVertexBuffer*, IndexBufferBatches> VertexBufferBatches;
typedef POOLED_BATCH_MAP(CVertexBuffer*, IndexBufferBatches) VertexBufferBatches;
// Group batches by texture
typedef std::map<CTerrainTextureEntry*, VertexBufferBatches> TextureBatches;
typedef POOLED_BATCH_MAP(CTerrainTextureEntry*, VertexBufferBatches) TextureBatches;
void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches)
{
TextureBatches batches;
RawPoolAllocator pool(POOL_SIZE);
TextureBatches batches (TextureBatches::key_compare(), (TextureBatches::allocator_type(pool)));
PROFILE_START("compute batches");
@ -689,7 +720,13 @@ void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches)
{
SSplat& splat = patch->m_Splats[j];
BatchElements& batch = batches[splat.m_Texture][patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
BatchElements& batch = PooledPairGet(
PooledMapGet(
PooledMapGet(batches, splat.m_Texture, pool),
patch->m_VBBase->m_Owner, pool
),
patch->m_VBBaseIndices->m_Owner, pool
);
batch.first.push_back(splat.m_IndexCount);
@ -745,13 +782,38 @@ void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches)
*/
struct SBlendBatch
{
SBlendBatch(RawPoolAllocator& pool) :
m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(pool))
{
}
CTerrainTextureEntry* m_Texture;
VertexBufferBatches m_Batches;
};
/**
* Helper structure for RenderBlends.
*/
struct SBlendStackItem
{
SBlendStackItem(CVertexBuffer::VBChunk* v, CVertexBuffer::VBChunk* i,
const std::vector<CPatchRData::SSplat>& s, RawPoolAllocator& pool) :
vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(pool))
{
}
typedef std::vector<CPatchRData::SSplat, pool_allocator<CPatchRData::SSplat*> > SplatStack;
CVertexBuffer::VBChunk* vertices;
CVertexBuffer::VBChunk* indices;
SplatStack splats;
};
void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches)
{
std::vector<SBlendBatch> batches;
RawPoolAllocator pool(POOL_SIZE);
typedef std::vector<SBlendBatch, pool_allocator<SBlendBatch*> > BatchesStack;
BatchesStack batches((BatchesStack::allocator_type(pool)));
PROFILE_START("compute batches");
@ -759,8 +821,8 @@ void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches)
// to avoid heavy reallocations
batches.reserve(256);
// Each patch has a vertex buffer, index buffer, and stack of splats
std::vector<boost::tuple<CVertexBuffer::VBChunk*, CVertexBuffer::VBChunk*, std::vector<SSplat> > > blendStacks;
typedef std::vector<SBlendStackItem, pool_allocator<SBlendStackItem*> > BlendStacks;
BlendStacks blendStacks((BlendStacks::allocator_type(pool)));
blendStacks.reserve(patches.size());
// Extract all the blend splats from each patch
@ -769,9 +831,10 @@ void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches)
CPatchRData* patch = patches[i];
if (!patch->m_BlendSplats.empty())
{
blendStacks.push_back(boost::make_tuple(patch->m_VBBlends, patch->m_VBBlendIndices, patch->m_BlendSplats));
blendStacks.push_back(SBlendStackItem(patch->m_VBBlends, patch->m_VBBlendIndices, patch->m_BlendSplats, pool));
// Reverse the splats so the first to be rendered is at the back of the list
std::reverse(blendStacks.back().get<2>().begin(), blendStacks.back().get<2>().end());
std::reverse(blendStacks.back().splats.begin(), blendStacks.back().splats.end());
}
}
@ -788,14 +851,13 @@ void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches)
for (size_t k = 0; k < blendStacks.size(); ++k)
{
std::vector<SSplat>& splats = blendStacks[k].get<2>();
SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
if (!splats.empty() && splats.back().m_Texture == tex)
{
CVertexBuffer::VBChunk* vertices = blendStacks[k].get<0>();
CVertexBuffer::VBChunk* indices = blendStacks[k].get<1>();
BatchElements& batch = batches.back().m_Batches[vertices->m_Owner][indices->m_Owner];
CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
BatchElements& batch = PooledPairGet(PooledMapGet(batches.back().m_Batches, vertices->m_Owner, pool), indices->m_Owner, pool);
batch.first.push_back(splats.back().m_IndexCount);
u8* indexBase = indices->m_Owner->GetBindAddress();
@ -811,7 +873,7 @@ void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches)
for (size_t k = 0; k < blendStacks.size(); ++k)
{
std::vector<SSplat>& splats = blendStacks[k].get<2>();
SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
if (splats.size() > bestStackSize)
{
bestStackSize = splats.size();
@ -822,7 +884,7 @@ void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches)
if (bestStackSize == 0)
break;
SBlendBatch layer;
SBlendBatch layer(pool);
layer.m_Texture = bestTex;
batches.push_back(layer);
}
@ -831,7 +893,7 @@ void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches)
CVertexBuffer* lastVB = NULL;
for (std::vector<SBlendBatch>::iterator itt = batches.begin(); itt != batches.end(); ++itt)
for (BatchesStack::iterator itt = batches.begin(); itt != batches.end(); ++itt)
{
if (itt->m_Texture)
itt->m_Texture->GetTexture()->Bind();

View File

@ -48,6 +48,8 @@ public:
CPatch* GetPatch() { return m_Patch; }
private:
friend struct SBlendStackItem;
struct SSplat {
SSplat() : m_Texture(0), m_IndexCount(0) {}