locking and unlocking GL buffers on the main thread to support GL buffers in PartialRenderPolicy.

This commit is contained in:
rachytski 2011-11-12 18:33:57 +04:00 committed by Alex Zolotarev
parent 4fcc52f3aa
commit fca2a020cb
12 changed files with 238 additions and 53 deletions

View file

@ -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 <typename TElem, typename TElemFactory>
struct BasePoolTraits
{
TElemFactory m_factory;
ThreadedList<TElem> m_pool;
BasePoolTraits(TElemFactory const & factory) : m_factory(factory)
typedef TElem elem_t;
BasePoolTraits(TElemFactory const & factory)
: m_factory(factory)
{}
void Free(ThreadedList<TElem> & 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 <typename TElem, typename TElemFactory>
struct FixedSizePoolTraits : BasePoolTraits<TElem, TElemFactory>
/// 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 <typename TElemFactory, typename TBase>
struct SeparateFreePoolTraits : TBase
{
typedef BasePoolTraits<TElem, TElemFactory> base_t;
typedef TBase base_t;
typedef typename base_t::elem_t elem_t;
ThreadedList<elem_t> m_freePool;
SeparateFreePoolTraits(TElemFactory const & factory)
: base_t(factory)
{}
void Free(elem_t const & elem)
{
m_freePool.PushBack(elem);
}
void MergeImpl(list<elem_t> & l)
{
for (typename list<elem_t>::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<TElemFactory, TBase>::MergeImpl, this, _1));
}
};
/// This traits maintains a fixed-size of pre-allocated resources.
template <typename TElemFactory, typename TBase >
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<TElem, TElemFactory>
m_isAllocated(false)
{}
TElem const Reserve(ThreadedList<TElem> & elems)
elem_t const Reserve()
{
if (!m_isAllocated)
{
@ -50,17 +115,19 @@ struct FixedSizePoolTraits : BasePoolTraits<TElem, TElemFactory>
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 <typename TElem, typename TElemFactory>
struct AllocateOnDemandPoolTraits : BasePoolTraits<TElem, TElemFactory>
/// This traits allocates resources on demand.
template <typename TElemFactory, typename TBase>
struct AllocateOnDemandPoolTraits : TBase
{
typedef BasePoolTraits<TElem, TElemFactory> 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<TElem, TElemFactory>
m_poolSize(0)
{}
void ReserveImpl(list<TElem> & l, TElem & elem)
void ReserveImpl(list<elem_t> & l, elem_t & elem)
{
if (l.empty())
{
@ -82,27 +149,28 @@ struct AllocateOnDemandPoolTraits : BasePoolTraits<TElem, TElemFactory>
l.pop_back();
}
TElem const Reserve(ThreadedList<TElem> & elems)
elem_t const Reserve()
{
TElem elem;
elems.ProcessList(bind(&AllocateOnDemandPoolTraits<TElem, TElemFactory>::ReserveImpl, this, _1, ref(elem)));
elem_t elem;
base_t::m_pool.ProcessList(bind(&AllocateOnDemandPoolTraits<elem_t, TElemFactory>::ReserveImpl, this, _1, ref(elem)));
return elem;
}
};
// This class tracks OpenGL resources allocation in
// a multithreaded environment.
template <typename TElem, typename TPoolTraits>
template <typename TPoolTraits>
class ResourcePool
{
private:
TPoolTraits m_traits;
ThreadedList<TElem> m_pool;
scoped_ptr<TPoolTraits> 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();
}
};

View file

@ -32,6 +32,7 @@ void PartialRenderPolicy::ProcessRenderQueue(list<yg::gl::Renderer::Packet> & re
void PartialRenderPolicy::DrawFrame(shared_ptr<PaintEvent> const & paintEvent,
ScreenBase const & screenBase)
{
m_resourceManager->mergeFreeResources();
/// blitting from the current surface onto screen
// RenderPolicyMT::DrawFrame(paintEvent, screenBase);

View file

@ -116,6 +116,8 @@ void RenderPolicyMT::EndFrame(shared_ptr<PaintEvent> const & e,
void RenderPolicyMT::DrawFrame(shared_ptr<PaintEvent> const & e,
ScreenBase const & s)
{
m_resourceManager->mergeFreeResources();
if (m_DoAddCommand && (s != m_renderQueue->renderState().m_actualScreen))
m_renderQueue->AddCommand(m_renderFn, s);

View file

@ -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();

View file

@ -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<UnlockStorage> command(new UnlockStorage());
command->m_storage = storage;
processCommand(command);
}
void GeometryBatcher::flushPipeline(shared_ptr<SkinPage> 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);

View file

@ -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<SkinPage> const & skinPage);
void flushPipeline(shared_ptr<SkinPage> const & skinPage, int pipelineID);
void unlockPipeline(int pipelineID);
public:

View file

@ -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;

View file

@ -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();
};

View file

@ -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<gl::BaseTexture> 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();
}
}

View file

@ -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<shared_ptr<gl::BaseTexture>, TTextureFactory> TTexturePoolTraits;
typedef ResourcePool<shared_ptr<gl::BaseTexture>, TTexturePoolTraits> TTexturePool;
typedef BasePoolTraits<shared_ptr<gl::BaseTexture>, TTextureFactory> TBaseTexturePoolTraits;
typedef FixedSizePoolTraits<TTextureFactory, TBaseTexturePoolTraits > TTexturePoolTraits;
typedef ResourcePool<TTexturePoolTraits> TTexturePool;
typedef FixedSizePoolTraits<gl::Storage, TStorageFactory> TStoragePoolTraits;
typedef ResourcePool<gl::Storage, TStoragePoolTraits> TStoragePool;
typedef BasePoolTraits<gl::Storage, TStorageFactory> TBaseStoragePoolTraits;
typedef SeparateFreePoolTraits<TStorageFactory, TBaseStoragePoolTraits> TSeparateFreeStoragePoolTraits;
typedef FixedSizePoolTraits<TStorageFactory, TSeparateFreeStoragePoolTraits> TStoragePoolTraits;
typedef ResourcePool<TStoragePoolTraits> TStoragePool;
private:
@ -166,6 +170,8 @@ namespace yg
void enterBackground();
void enterForeground();
void mergeFreeResources();
shared_ptr<yg::gl::BaseTexture> createRenderTarget(unsigned w, unsigned h);
};

View file

@ -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)

View file

@ -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();
};