diff --git a/base/resource_pool.hpp b/base/resource_pool.hpp index 265ae44ba5..6cc0297968 100644 --- a/base/resource_pool.hpp +++ b/base/resource_pool.hpp @@ -3,6 +3,7 @@ #include "threaded_list.hpp" #include "logging.hpp" #include "../std/bind.hpp" +#include "../std/scoped_ptr.hpp" struct BasePoolElemFactory { @@ -14,24 +15,88 @@ struct BasePoolElemFactory size_t ElemSize() const; }; +/// basic traits maintains a list of free resources. template struct BasePoolTraits { TElemFactory m_factory; + ThreadedList m_pool; - BasePoolTraits(TElemFactory const & factory) : m_factory(factory) + typedef TElem elem_t; + + BasePoolTraits(TElemFactory const & factory) + : m_factory(factory) {} - void Free(ThreadedList & elems, TElem const & elem) + void Free(TElem const & elem) { - elems.PushBack(elem); + m_pool.PushBack(elem); + } + + size_t Size() const + { + return m_pool.Size(); + } + + void Cancel() + { + m_pool.Cancel(); + } + + bool IsCancelled() const + { + return m_pool.IsCancelled(); } }; -template -struct FixedSizePoolTraits : BasePoolTraits +/// This traits stores the free elements in a separate pool and has +/// a separate method to merge them all into a main pool. +/// For example should be used for resources where a certain preparation operation +/// should be performed on main thread before returning resource +/// to a free pool(p.e. @see resource_manager.cpp StorageFactory) +template +struct SeparateFreePoolTraits : TBase { - typedef BasePoolTraits base_t; + typedef TBase base_t; + typedef typename base_t::elem_t elem_t; + + ThreadedList m_freePool; + + SeparateFreePoolTraits(TElemFactory const & factory) + : base_t(factory) + {} + + void Free(elem_t const & elem) + { + m_freePool.PushBack(elem); + } + + void MergeImpl(list & l) + { + for (typename list::const_iterator it = l.begin(); + it != l.end(); + ++it) + { + base_t::m_factory.BeforeMerge(*it); + base_t::m_pool.PushBack(*it); + } + + l.clear(); + } + + void Merge() + { + m_freePool.ProcessList(bind(&SeparateFreePoolTraits::MergeImpl, this, _1)); + } +}; + +/// This traits maintains a fixed-size of pre-allocated resources. +template +struct FixedSizePoolTraits : TBase +{ + typedef TBase base_t; + typedef typename base_t::elem_t elem_t; + size_t m_count; bool m_isAllocated; @@ -41,7 +106,7 @@ struct FixedSizePoolTraits : BasePoolTraits m_isAllocated(false) {} - TElem const Reserve(ThreadedList & elems) + elem_t const Reserve() { if (!m_isAllocated) { @@ -50,17 +115,19 @@ struct FixedSizePoolTraits : BasePoolTraits LOG(LDEBUG, ("allocating ", base_t::m_factory.ElemSize() * m_count, "bytes for ", base_t::m_factory.ResName())); for (size_t i = 0; i < m_count; ++i) - elems.PushBack(base_t::m_factory.Create()); + base_t::m_pool.PushBack(base_t::m_factory.Create()); } - return elems.Front(true); + return base_t::m_pool.Front(true); } }; -template -struct AllocateOnDemandPoolTraits : BasePoolTraits +/// This traits allocates resources on demand. +template +struct AllocateOnDemandPoolTraits : TBase { - typedef BasePoolTraits base_t; + typedef TBase base_t; + typedef typename base_t::elem_t elem_t; size_t m_poolSize; AllocateOnDemandPoolTraits(TElemFactory const & factory, size_t ) @@ -68,7 +135,7 @@ struct AllocateOnDemandPoolTraits : BasePoolTraits m_poolSize(0) {} - void ReserveImpl(list & l, TElem & elem) + void ReserveImpl(list & l, elem_t & elem) { if (l.empty()) { @@ -82,27 +149,28 @@ struct AllocateOnDemandPoolTraits : BasePoolTraits l.pop_back(); } - TElem const Reserve(ThreadedList & elems) + elem_t const Reserve() { - TElem elem; - elems.ProcessList(bind(&AllocateOnDemandPoolTraits::ReserveImpl, this, _1, ref(elem))); + elem_t elem; + base_t::m_pool.ProcessList(bind(&AllocateOnDemandPoolTraits::ReserveImpl, this, _1, ref(elem))); return elem; } }; // This class tracks OpenGL resources allocation in // a multithreaded environment. -template +template class ResourcePool { private: - TPoolTraits m_traits; - ThreadedList m_pool; + scoped_ptr m_traits; public: - ResourcePool(TPoolTraits const & traits) + typedef typename TPoolTraits::elem_t elem_t; + + ResourcePool(TPoolTraits * traits) : m_traits(traits) { /// quick trick to perform lazy initialization @@ -110,19 +178,19 @@ public: Free(Reserve()); } - TElem const Reserve() + elem_t const Reserve() { - return m_traits.Reserve(m_pool); + return m_traits->Reserve(); } - void Free(TElem const & elem) + void Free(elem_t const & elem) { - m_traits.Free(m_pool, elem); + m_traits->Free(elem); } size_t Size() const { - return m_pool.Size(); + return m_traits->Size(); } void EnterForeground() @@ -133,11 +201,16 @@ public: void Cancel() { - m_pool.Cancel(); + return m_traits->Cancel(); } bool IsCancelled() const { - return m_pool.IsCancelled(); + return m_traits->IsCancelled(); + } + + void Merge() + { + m_traits->Merge(); } }; diff --git a/map/partial_render_policy.cpp b/map/partial_render_policy.cpp index fc5aafadd4..384b15cacf 100644 --- a/map/partial_render_policy.cpp +++ b/map/partial_render_policy.cpp @@ -32,6 +32,7 @@ void PartialRenderPolicy::ProcessRenderQueue(list & re void PartialRenderPolicy::DrawFrame(shared_ptr const & paintEvent, ScreenBase const & screenBase) { + m_resourceManager->mergeFreeResources(); /// blitting from the current surface onto screen // RenderPolicyMT::DrawFrame(paintEvent, screenBase); diff --git a/map/render_policy_mt.cpp b/map/render_policy_mt.cpp index 97f586825e..3ab4ca0703 100644 --- a/map/render_policy_mt.cpp +++ b/map/render_policy_mt.cpp @@ -116,6 +116,8 @@ void RenderPolicyMT::EndFrame(shared_ptr const & e, void RenderPolicyMT::DrawFrame(shared_ptr const & e, ScreenBase const & s) { + m_resourceManager->mergeFreeResources(); + if (m_DoAddCommand && (s != m_renderQueue->renderState().m_actualScreen)) m_renderQueue->AddCommand(m_renderFn, s); diff --git a/yg/blitter.cpp b/yg/blitter.cpp index 1f1c95f8a1..acb36f573f 100644 --- a/yg/blitter.cpp +++ b/yg/blitter.cpp @@ -318,7 +318,7 @@ namespace yg LOG(LINFO, ("performing IMMDrawTexturedPrimitives command")); yg::gl::Storage blitStorage = m_resourceManager->blitStorages()->Reserve(); - AuxVertex * pointsData = (AuxVertex*)blitStorage.m_vertices->lock(); + AuxVertex * pointsData = (AuxVertex*)blitStorage.m_vertices->data(); for (size_t i = 0; i < m_ptsCount; ++i) { @@ -338,7 +338,7 @@ namespace yg m_texture->makeCurrent(); unsigned short idxData[4] = {0, 1, 2, 3}; - memcpy(blitStorage.m_indices->lock(), idxData, sizeof(idxData)); + memcpy(blitStorage.m_indices->data(), idxData, sizeof(idxData)); blitStorage.m_indices->unlock(); blitStorage.m_indices->makeCurrent(); diff --git a/yg/geometry_batcher.cpp b/yg/geometry_batcher.cpp index e973b1435c..e69a106d41 100644 --- a/yg/geometry_batcher.cpp +++ b/yg/geometry_batcher.cpp @@ -69,8 +69,8 @@ namespace yg m_maxVertices = m_storage.m_vertices->size() / sizeof(Vertex); m_maxIndices = m_storage.m_indices->size() / sizeof(unsigned short); - m_vertices = (Vertex*)m_storage.m_vertices->lock(); - m_indices = (unsigned short *)m_storage.m_indices->lock(); + m_vertices = (Vertex*)m_storage.m_vertices->data(); + m_indices = (unsigned short *)m_storage.m_indices->data(); m_hasStorage = true; } } @@ -315,6 +315,26 @@ namespace yg } } + void GeometryBatcher::UnlockStorage::perform() + { + if (isDebugging()) + LOG(LINFO, ("performing UnlockPipeline command")); + m_storage.m_vertices->unlock(); + m_storage.m_indices->unlock(); + } + + void GeometryBatcher::unlockPipeline(int pipelineID) + { + GeometryPipeline & pipeline = m_pipelines[pipelineID]; + + Storage storage = pipeline.m_storage; + + shared_ptr command(new UnlockStorage()); + command->m_storage = storage; + + processCommand(command); + } + void GeometryBatcher::flushPipeline(shared_ptr const & skinPage, int pipelineID) { @@ -323,8 +343,7 @@ namespace yg { uploadData(skinPage); - pipeline.m_storage.m_vertices->unlock(); - pipeline.m_storage.m_indices->unlock(); + unlockPipeline(pipelineID); // base_t::applyStates(m_isAntiAliased); diff --git a/yg/geometry_batcher.hpp b/yg/geometry_batcher.hpp index e3338c37fc..3fbd7e4e9b 100644 --- a/yg/geometry_batcher.hpp +++ b/yg/geometry_batcher.hpp @@ -100,6 +100,13 @@ namespace yg void perform(); }; + struct UnlockStorage : public Command + { + Storage m_storage; + + void perform(); + }; + public: /// INTERNAL API! USE WITH CAUTION @@ -127,6 +134,7 @@ namespace yg void uploadData(shared_ptr const & skinPage); void flushPipeline(shared_ptr const & skinPage, int pipelineID); + void unlockPipeline(int pipelineID); public: diff --git a/yg/indexbuffer.cpp b/yg/indexbuffer.cpp index df0d29bcf9..addb298327 100644 --- a/yg/indexbuffer.cpp +++ b/yg/indexbuffer.cpp @@ -21,14 +21,14 @@ namespace yg } IndexBuffer::IndexBuffer(bool useVA) - : m_size(0), m_gpuData(0), m_useVA(useVA) + : m_size(0), m_gpuData(0), m_useVA(useVA), m_isLocked(false) { if (!m_useVA) OGLCHECK(glGenBuffers(1, &m_id)); } IndexBuffer::IndexBuffer(size_t size, bool useVA) - : m_size(0), m_gpuData(0), m_useVA(useVA) + : m_size(0), m_gpuData(0), m_useVA(useVA), m_isLocked(false) { if (!m_useVA) OGLCHECK(glGenBuffers(1, &m_id)); @@ -37,6 +37,7 @@ namespace yg void IndexBuffer::resize(size_t size) { + ASSERT(!m_isLocked, ()); if (size != m_size) { m_size = size; @@ -62,8 +63,22 @@ namespace yg OGLCHECK(glDeleteBuffers(1, &m_id)); } + bool IndexBuffer::isLocked() const + { + return m_isLocked; + } + + void * IndexBuffer::data() + { + ASSERT(m_isLocked, ("IndexBuffer is not locked")); + return m_gpuData; + } + void * IndexBuffer::lock() { + ASSERT(!m_isLocked, ()); + m_isLocked = true; + if (m_useVA) return m_gpuData; @@ -84,6 +99,9 @@ namespace yg void IndexBuffer::unlock() { + ASSERT(m_isLocked, ()); + m_isLocked = false; + if (m_useVA) return; diff --git a/yg/indexbuffer.hpp b/yg/indexbuffer.hpp index f6672bf21d..d2b3f52083 100644 --- a/yg/indexbuffer.hpp +++ b/yg/indexbuffer.hpp @@ -12,6 +12,7 @@ namespace yg unsigned int m_size; void * m_gpuData; bool m_useVA; + bool m_isLocked; public: @@ -27,6 +28,8 @@ namespace yg void unlock(); void * glPtr(); + void * data(); + bool isLocked() const; static int current(); }; diff --git a/yg/resource_manager.cpp b/yg/resource_manager.cpp index 6fbbcde839..d216f5cf41 100644 --- a/yg/resource_manager.cpp +++ b/yg/resource_manager.cpp @@ -5,6 +5,8 @@ #include "skin_loader.hpp" #include "storage.hpp" #include "texture.hpp" +#include "vertexbuffer.hpp" +#include "indexbuffer.hpp" #include "../coding/file_reader.hpp" #include "../coding/parse_xml.hpp" @@ -36,7 +38,25 @@ namespace yg gl::Storage const TStorageFactory::Create() { - return gl::Storage(m_vbSize, m_ibSize, m_useVA); + gl::Storage res(m_vbSize, m_ibSize, m_useVA); + + res.m_indices->lock(); + res.m_vertices->lock(); + + return res; + } + + void TStorageFactory::BeforeMerge(gl::Storage const & e) + { + if (e.m_indices->isLocked()) + e.m_indices->unlock(); + + e.m_indices->lock(); + + if (e.m_vertices->isLocked()) + e.m_vertices->unlock(); + + e.m_vertices->lock(); } ResourceManager::ResourceManager(size_t vbSize, size_t ibSize, size_t storagesCount, @@ -65,12 +85,12 @@ namespace yg if (useVA) LOG(LINFO, ("buffer objects are unsupported. using client vertex array instead.")); - m_storages.reset(new TStoragePool(TStoragePoolTraits(TStorageFactory(vbSize, ibSize, useVA, "primaryStorage"), storagesCount))); - m_smallStorages.reset(new TStoragePool(TStoragePoolTraits(TStorageFactory(smallVBSize, smallIBSize, useVA, "smallStorage"), smallStoragesCount))); - m_blitStorages.reset(new TStoragePool(TStoragePoolTraits(TStorageFactory(blitVBSize, blitIBSize, useVA, "blitStorage"), blitStoragesCount))); + m_storages.reset(new TStoragePool(new TStoragePoolTraits(TStorageFactory(vbSize, ibSize, useVA, "primaryStorage"), storagesCount))); + m_smallStorages.reset(new TStoragePool(new TStoragePoolTraits(TStorageFactory(smallVBSize, smallIBSize, useVA, "smallStorage"), smallStoragesCount))); + m_blitStorages.reset(new TStoragePool(new TStoragePoolTraits(TStorageFactory(blitVBSize, blitIBSize, useVA, "blitStorage"), blitStoragesCount))); - m_dynamicTextures.reset(new TTexturePool(TTexturePoolTraits(TTextureFactory(dynamicTexWidth, dynamicTexHeight, "dynamicTexture"), dynamicTexCount))); - m_fontTextures.reset(new TTexturePool(TTexturePoolTraits(TTextureFactory(fontTexWidth, fontTexHeight, "fontTexture"), fontTexCount))); + m_dynamicTextures.reset(new TTexturePool(new TTexturePoolTraits(TTextureFactory(dynamicTexWidth, dynamicTexHeight, "dynamicTexture"), dynamicTexCount))); + m_fontTextures.reset(new TTexturePool(new TTexturePoolTraits(TTextureFactory(fontTexWidth, fontTexHeight, "fontTexture"), fontTexCount))); } void ResourceManager::initMultiBlitStorage(size_t multiBlitVBSize, size_t multiBlitIBSize, size_t multiBlitStoragesCount) @@ -78,7 +98,7 @@ namespace yg m_multiBlitVBSize = multiBlitVBSize; m_multiBlitIBSize = multiBlitIBSize; - m_multiBlitStorages.reset(new TStoragePool(TStoragePoolTraits(TStorageFactory(multiBlitVBSize, multiBlitIBSize, m_useVA, "multiBlitStorage"), multiBlitStoragesCount))); + m_multiBlitStorages.reset(new TStoragePool(new TStoragePoolTraits(TStorageFactory(multiBlitVBSize, multiBlitIBSize, m_useVA, "multiBlitStorage"), multiBlitStoragesCount))); } void ResourceManager::initTinyStorage(size_t tinyVBSize, size_t tinyIBSize, size_t tinyStoragesCount) @@ -86,7 +106,7 @@ namespace yg m_tinyVBSize = tinyVBSize; m_tinyIBSize = tinyIBSize; - m_tinyStorages.reset(new TStoragePool(TStoragePoolTraits(TStorageFactory(tinyVBSize, tinyIBSize, m_useVA, "tinyStorage"), tinyStoragesCount))); + m_tinyStorages.reset(new TStoragePool(new TStoragePoolTraits(TStorageFactory(tinyVBSize, tinyIBSize, m_useVA, "tinyStorage"), tinyStoragesCount))); } void ResourceManager::initRenderTargets(size_t renderTargetWidth, size_t renderTargetHeight, size_t renderTargetsCount) @@ -94,7 +114,7 @@ namespace yg m_renderTargetWidth = renderTargetWidth; m_renderTargetHeight = renderTargetHeight; - m_renderTargets.reset(new TTexturePool(TTexturePoolTraits(TTextureFactory(renderTargetWidth, renderTargetHeight, "renderTargets"), renderTargetsCount))); + m_renderTargets.reset(new TTexturePool(new TTexturePoolTraits(TTextureFactory(renderTargetWidth, renderTargetHeight, "renderTargets"), renderTargetsCount))); } void ResourceManager::initStyleCacheTextures(size_t styleCacheTextureWidth, size_t styleCacheTextureHeight, size_t styleCacheTexturesCount) @@ -102,7 +122,7 @@ namespace yg m_styleCacheTextureWidth = styleCacheTextureWidth; m_styleCacheTextureHeight = styleCacheTextureHeight; - m_styleCacheTextures.reset(new TTexturePool(TTexturePoolTraits(TTextureFactory(styleCacheTextureWidth, styleCacheTextureHeight, "styleCacheTextures"), styleCacheTexturesCount))); + m_styleCacheTextures.reset(new TTexturePool(new TTexturePoolTraits(TTextureFactory(styleCacheTextureWidth, styleCacheTextureHeight, "styleCacheTextures"), styleCacheTexturesCount))); } shared_ptr const & ResourceManager::getTexture(string const & fileName) @@ -323,4 +343,18 @@ namespace yg { return m_styleCacheTextures.get(); } + + void ResourceManager::mergeFreeResources() + { + if (m_tinyStorages.get()) + m_tinyStorages->Merge(); + if (m_storages.get()) + m_storages->Merge(); + if (m_smallStorages.get()) + m_smallStorages->Merge(); + if (m_blitStorages.get()) + m_blitStorages->Merge(); + if (m_multiBlitStorages.get()) + m_multiBlitStorages->Merge(); + } } diff --git a/yg/resource_manager.hpp b/yg/resource_manager.hpp index 51b5cea441..5fa2c69f1d 100644 --- a/yg/resource_manager.hpp +++ b/yg/resource_manager.hpp @@ -45,17 +45,21 @@ namespace yg bool m_useVA; TStorageFactory(size_t vbSize, size_t ibSize, bool useVA, char const * resName); gl::Storage const Create(); + void BeforeMerge(gl::Storage const & e); }; class ResourceManager { public: - typedef FixedSizePoolTraits, TTextureFactory> TTexturePoolTraits; - typedef ResourcePool, TTexturePoolTraits> TTexturePool; + typedef BasePoolTraits, TTextureFactory> TBaseTexturePoolTraits; + typedef FixedSizePoolTraits TTexturePoolTraits; + typedef ResourcePool TTexturePool; - typedef FixedSizePoolTraits TStoragePoolTraits; - typedef ResourcePool TStoragePool; + typedef BasePoolTraits TBaseStoragePoolTraits; + typedef SeparateFreePoolTraits TSeparateFreeStoragePoolTraits; + typedef FixedSizePoolTraits TStoragePoolTraits; + typedef ResourcePool TStoragePool; private: @@ -166,6 +170,8 @@ namespace yg void enterBackground(); void enterForeground(); + void mergeFreeResources(); + shared_ptr createRenderTarget(unsigned w, unsigned h); }; diff --git a/yg/vertexbuffer.cpp b/yg/vertexbuffer.cpp index cef2e42ac0..26f77a5fbc 100644 --- a/yg/vertexbuffer.cpp +++ b/yg/vertexbuffer.cpp @@ -21,14 +21,14 @@ namespace yg } VertexBuffer::VertexBuffer(bool useVA) - : m_size(0), m_gpuData(0), m_useVA(useVA) + : m_size(0), m_gpuData(0), m_useVA(useVA), m_isLocked(false) { if (!m_useVA) OGLCHECK(glGenBuffers(1, &m_id)); } VertexBuffer::VertexBuffer(size_t size, bool useVA) - : m_size(0), m_gpuData(0), m_useVA(useVA) + : m_size(0), m_gpuData(0), m_useVA(useVA), m_isLocked(false) { if (!m_useVA) OGLCHECK(glGenBuffers(1, &m_id)); @@ -37,6 +37,7 @@ namespace yg void VertexBuffer::resize(size_t size) { + ASSERT(!m_isLocked, ()); if (size != m_size) { m_size = size; @@ -64,8 +65,17 @@ namespace yg OGLCHECK(glDeleteBuffers(1, &m_id)); } + void * VertexBuffer::data() + { + ASSERT(m_isLocked, ("IndexBuffer is not locked")); + return m_gpuData; + } + void * VertexBuffer::lock() { + ASSERT(!m_isLocked, ()); + m_isLocked = true; + if (m_useVA) return m_gpuData; @@ -86,6 +96,9 @@ namespace yg void VertexBuffer::unlock() { + ASSERT(m_isLocked, ()); + m_isLocked = false; + if (m_useVA) return; @@ -99,13 +112,18 @@ namespace yg m_gpuData = 0; } - void * VertexBuffer::glPtr() const + void * VertexBuffer::glPtr() { if (m_useVA) return m_gpuData; return 0; } + bool VertexBuffer::isLocked() const + { + return m_isLocked; + } + void VertexBuffer::makeCurrent() { if (m_useVA) diff --git a/yg/vertexbuffer.hpp b/yg/vertexbuffer.hpp index c85c992aba..0cf17e65f6 100644 --- a/yg/vertexbuffer.hpp +++ b/yg/vertexbuffer.hpp @@ -14,6 +14,7 @@ namespace yg /// using VA instead of buffer objects on some old GPU's bool m_useVA; + bool m_isLocked; public: @@ -27,7 +28,9 @@ namespace yg void makeCurrent(); void * lock(); void unlock(); - void * glPtr() const; + void * glPtr(); + void * data(); + bool isLocked() const; static unsigned current(); };