From c2583e42da37c92d54571402b8340512fd30ebe8 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Sun, 29 Sep 2013 14:48:11 +0000 Subject: [PATCH] Reduce memory allocations in the renderer Use an arena allocator in ShaderModelRenderer::Render, to reduce the allocation cost in STL containers. Avoid unnecessary copying of std::vectors. This was SVN commit r13911. --- source/graphics/ShaderDefines.cpp | 16 +++++++- source/graphics/ShaderDefines.h | 4 ++ source/lib/allocators/allocator_adapters.h | 3 ++ source/renderer/DecalRData.cpp | 4 +- source/renderer/ModelRenderer.cpp | 48 +++++++++++++++------- source/renderer/PatchRData.cpp | 8 ++-- 6 files changed, 61 insertions(+), 22 deletions(-) diff --git a/source/graphics/ShaderDefines.cpp b/source/graphics/ShaderDefines.cpp index c414fbff32..b07d9b9079 100644 --- a/source/graphics/ShaderDefines.cpp +++ b/source/graphics/ShaderDefines.cpp @@ -104,10 +104,21 @@ typename CShaderParams::SItems* CShaderParams::GetInterned(con template CShaderParams::CShaderParams() +{ + *this = s_Empty; +} + +template +CShaderParams::CShaderParams(SItems* items) : m_Items(items) +{ +} + +template +CShaderParams CShaderParams::CreateEmpty() { SItems items; items.RecalcHash(); - m_Items = GetInterned(items); + return CShaderParams(GetInterned(items)); } template @@ -267,5 +278,8 @@ void CShaderConditionalDefines::Add(const char* defname, const char* defvalue, i template<> CShaderParams::InternedItems_t CShaderParams::s_InternedItems = CShaderParams::InternedItems_t(); template<> CShaderParams::InternedItems_t CShaderParams::s_InternedItems = CShaderParams::InternedItems_t(); +template<> CShaderParams CShaderParams::s_Empty = CShaderParams::CreateEmpty(); +template<> CShaderParams CShaderParams::s_Empty = CShaderParams::CreateEmpty(); + template class CShaderParams; template class CShaderParams; diff --git a/source/graphics/ShaderDefines.h b/source/graphics/ShaderDefines.h index b6fa4bb03f..c235b6f8cf 100644 --- a/source/graphics/ShaderDefines.h +++ b/source/graphics/ShaderDefines.h @@ -118,6 +118,10 @@ private: * for any subsequent requests for an equal items list. */ static SItems* GetInterned(const SItems& items); + + CShaderParams(SItems* items); + static CShaderParams CreateEmpty(); + static CShaderParams s_Empty; }; /** diff --git a/source/lib/allocators/allocator_adapters.h b/source/lib/allocators/allocator_adapters.h index 0e9690d34f..f09bced85d 100644 --- a/source/lib/allocators/allocator_adapters.h +++ b/source/lib/allocators/allocator_adapters.h @@ -115,6 +115,9 @@ public: typedef ProxyAllocator other; }; + // (required to be declared by boost::unordered_map, but should never be called) + explicit NOTHROW_DEFINE ProxyAllocator(); + explicit NOTHROW_DEFINE ProxyAllocator(Allocator& allocator) : allocator(&allocator) { diff --git a/source/renderer/DecalRData.cpp b/source/renderer/DecalRData.cpp index e57dfff8c2..0c85902ad7 100644 --- a/source/renderer/DecalRData.cpp +++ b/source/renderer/DecalRData.cpp @@ -121,12 +121,12 @@ void CDecalRData::RenderDecals(std::vector& decals, const CShaderD if (material.GetSamplers().size() != 0) { - CMaterial::SamplersVector samplers = material.GetSamplers(); + const CMaterial::SamplersVector& samplers = material.GetSamplers(); size_t samplersNum = samplers.size(); for (size_t s = 0; s < samplersNum; ++s) { - CMaterial::TextureSampler &samp = samplers[s]; + const CMaterial::TextureSampler& samp = samplers[s]; shader->BindTexture(samp.Name, samp.Sampler); } diff --git a/source/renderer/ModelRenderer.cpp b/source/renderer/ModelRenderer.cpp index 965eefc533..2579497a30 100644 --- a/source/renderer/ModelRenderer.cpp +++ b/source/renderer/ModelRenderer.cpp @@ -17,6 +17,8 @@ #include "precompiled.h" +#include "lib/allocators/allocator_adapters.h" +#include "lib/allocators/arena.h" #include "lib/ogl.h" #include "maths/Vector3D.h" #include "maths/Vector4D.h" @@ -417,8 +419,13 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade * list in each, rebinding the GL state whenever it changes. */ - typedef boost::unordered_map, SMRMaterialBucketKeyHash> MaterialBuckets_t; - MaterialBuckets_t materialBuckets; + Allocators::Arena<> arena(1*MiB); + typedef ProxyAllocator > ArenaProxyAllocator; + typedef std::vector ModelList_t; + typedef boost::unordered_map, ProxyAllocator > + > MaterialBuckets_t; + MaterialBuckets_t materialBuckets((MaterialBuckets_t::allocator_type(arena))); { PROFILE3("bucketing by material"); @@ -432,7 +439,7 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade const CShaderConditionalDefines& condefs = model->GetMaterial().GetConditionalDefines(); for (size_t j = 0; j < condefs.GetSize(); ++j) { - const CShaderConditionalDefines::CondDefine &item = condefs.GetItem(j); + const CShaderConditionalDefines::CondDefine& item = condefs.GetItem(j); int type = item.m_CondType; switch (type) { @@ -454,21 +461,32 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade CShaderDefines defs = model->GetMaterial().GetShaderDefines(condFlags); SMRMaterialBucketKey key(model->GetMaterial().GetShaderEffect(), defs); - std::vector& bucketItems = materialBuckets[key]; - bucketItems.push_back(model); + + MaterialBuckets_t::iterator it = materialBuckets.find(key); + if (it == materialBuckets.end()) + { + std::pair inserted = materialBuckets.insert( + std::make_pair(key, ModelList_t(ModelList_t::allocator_type(arena)))); + inserted.first->second.reserve(32); + inserted.first->second.push_back(model); + } + else + { + it->second.push_back(model); + } } } - std::vector sortByDistItems; + std::vector sortByDistItems((ArenaProxyAllocator(arena))); - std::vector sortByDistTechs; + std::vector sortByDistTechs((ArenaProxyAllocator(arena))); // indexed by sortByDistItems[i].techIdx // (which stores indexes instead of CShaderTechniquePtr directly // to avoid the shared_ptr copy cost when sorting; maybe it'd be better // if we just stored raw CShaderTechnique* and assumed the shader manager // will keep it alive long enough) - std::vector techBuckets; + std::vector techBuckets((ArenaProxyAllocator(arena))); { PROFILE3("processing material buckets"); @@ -528,7 +546,7 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade // (This exists primarily because techBuckets wants a CModel**; // we could avoid the cost of copying into this list by adding // a stride length into techBuckets and not requiring contiguous CModel*s) - std::vector sortByDistModels; + std::vector sortByDistModels((ArenaProxyAllocator(arena))); if (!sortByDistItems.empty()) { @@ -577,15 +595,15 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade // This vector keeps track of texture changes during rendering. It is kept outside the // loops to avoid excessive reallocations. The token allocation of 64 elements // should be plenty, though it is reallocated below (at a cost) if necessary. - std::vector currentTexs; + std::vector currentTexs((ArenaProxyAllocator(arena))); currentTexs.reserve(64); // texBindings holds the identifier bindings in the shader, which can no longer be defined // statically in the ShaderRenderModifier class. texBindingNames uses interned strings to // keep track of when bindings need to be reevaluated. - std::vector texBindings; + std::vector texBindings((ArenaProxyAllocator(arena))); texBindings.reserve(64); - std::vector texBindingNames; + std::vector texBindingNames((ArenaProxyAllocator(arena))); texBindingNames.reserve(64); while (idxTechStart < techBuckets.size()) @@ -633,7 +651,7 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade if (flags && !(model->GetFlags() & flags)) continue; - CMaterial::SamplersVector samplers = model->GetMaterial().GetSamplers(); + const CMaterial::SamplersVector& samplers = model->GetMaterial().GetSamplers(); size_t samplersNum = samplers.size(); // make sure the vectors are the right virtual sizes, and also @@ -653,7 +671,7 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade // bind the samplers to the shader for (size_t s = 0; s < samplersNum; ++s) { - CMaterial::TextureSampler &samp = samplers[s]; + const CMaterial::TextureSampler& samp = samplers[s]; CShaderProgram::Binding bind = texBindings[s]; // check that the handles are current @@ -694,7 +712,7 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade currentStaticUniforms.BindUniforms(shader); } - CShaderRenderQueries renderQueries = model->GetMaterial().GetRenderQueries(); + const CShaderRenderQueries& renderQueries = model->GetMaterial().GetRenderQueries(); for (size_t q = 0; q < renderQueries.GetSize(); q++) { diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp index 56d34ad952..e623bdf075 100644 --- a/source/renderer/PatchRData.cpp +++ b/source/renderer/PatchRData.cpp @@ -787,12 +787,12 @@ void CPatchRData::RenderBases(const std::vector& patches, const CS if (itt->first->GetMaterial().GetSamplers().size() != 0) { - CMaterial::SamplersVector samplers = itt->first->GetMaterial().GetSamplers(); + const CMaterial::SamplersVector& samplers = itt->first->GetMaterial().GetSamplers(); size_t samplersNum = samplers.size(); for (size_t s = 0; s < samplersNum; ++s) { - CMaterial::TextureSampler &samp = samplers[s]; + const CMaterial::TextureSampler& samp = samplers[s]; shader->BindTexture(samp.Name, samp.Sampler); } @@ -1017,12 +1017,12 @@ void CPatchRData::RenderBlends(const std::vector& patches, const C if (itt->m_Texture) { - CMaterial::SamplersVector samplers = itt->m_Texture->GetMaterial().GetSamplers(); + const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers(); size_t samplersNum = samplers.size(); for (size_t s = 0; s < samplersNum; ++s) { - CMaterial::TextureSampler &samp = samplers[s]; + const CMaterial::TextureSampler& samp = samplers[s]; shader->BindTexture(samp.Name, samp.Sampler); }