diff --git a/source/lib/allocators/pool.cpp b/source/lib/allocators/pool.cpp index 032cb68359..22731b2fdd 100644 --- a/source/lib/allocators/pool.cpp +++ b/source/lib/allocators/pool.cpp @@ -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; +} diff --git a/source/lib/allocators/pool.h b/source/lib/allocators/pool.h index b57d957ec2..faa8149738 100644 --- a/source/lib/allocators/pool.h +++ b/source/lib/allocators/pool.h @@ -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; }; diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp index b7e56144e1..6dbf284fc9 100644 --- a/source/renderer/PatchRData.cpp +++ b/source/renderer/PatchRData.cpp @@ -20,12 +20,11 @@ #include #include -#include - #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, pool_allocator > > + +// 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::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::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 > BatchElements; +typedef std::pair >, std::vector > > BatchElements; // Group batches by index buffer -typedef std::map IndexBufferBatches; +typedef POOLED_BATCH_MAP(CVertexBuffer*, BatchElements) IndexBufferBatches; // Group batches by vertex buffer -typedef std::map VertexBufferBatches; +typedef POOLED_BATCH_MAP(CVertexBuffer*, IndexBufferBatches) VertexBufferBatches; // Group batches by texture -typedef std::map TextureBatches; +typedef POOLED_BATCH_MAP(CTerrainTextureEntry*, VertexBufferBatches) TextureBatches; void CPatchRData::RenderBases(const std::vector& 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& 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& 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& s, RawPoolAllocator& pool) : + vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(pool)) + { + } + + typedef std::vector > SplatStack; + CVertexBuffer::VBChunk* vertices; + CVertexBuffer::VBChunk* indices; + SplatStack splats; +}; + void CPatchRData::RenderBlends(const std::vector& patches) { - std::vector batches; + RawPoolAllocator pool(POOL_SIZE); + + typedef std::vector > BatchesStack; + BatchesStack batches((BatchesStack::allocator_type(pool))); PROFILE_START("compute batches"); @@ -759,8 +821,8 @@ void CPatchRData::RenderBlends(const std::vector& patches) // to avoid heavy reallocations batches.reserve(256); - // Each patch has a vertex buffer, index buffer, and stack of splats - std::vector > > blendStacks; + typedef std::vector > 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& 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& patches) for (size_t k = 0; k < blendStacks.size(); ++k) { - std::vector& 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& patches) for (size_t k = 0; k < blendStacks.size(); ++k) { - std::vector& 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& 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& patches) CVertexBuffer* lastVB = NULL; - for (std::vector::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(); diff --git a/source/renderer/PatchRData.h b/source/renderer/PatchRData.h index 7b8e381024..19f7b4be22 100644 --- a/source/renderer/PatchRData.h +++ b/source/renderer/PatchRData.h @@ -48,6 +48,8 @@ public: CPatch* GetPatch() { return m_Patch; } private: + friend struct SBlendStackItem; + struct SSplat { SSplat() : m_Texture(0), m_IndexCount(0) {}