diff --git a/include/boo/graphicsdev/GL.hpp b/include/boo/graphicsdev/GL.hpp index 845f98d..6e43b98 100644 --- a/include/boo/graphicsdev/GL.hpp +++ b/include/boo/graphicsdev/GL.hpp @@ -15,7 +15,7 @@ class GLDataFactory : public IGraphicsDataFactory { friend struct GLCommandQueue; IGraphicsContext* m_parent; - static thread_local struct GLData* m_deferredData; + static ThreadLocalPtr m_deferredData; std::unordered_set m_committedData; std::mutex m_committedMutex; std::vector m_texUnis; diff --git a/include/boo/graphicsdev/IGraphicsDataFactory.hpp b/include/boo/graphicsdev/IGraphicsDataFactory.hpp index c13d3f1..14f90e0 100644 --- a/include/boo/graphicsdev/IGraphicsDataFactory.hpp +++ b/include/boo/graphicsdev/IGraphicsDataFactory.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "boo/System.hpp" namespace boo @@ -226,6 +227,28 @@ private: virtual void destroyAllData()=0; }; +/** Multiplatform TLS-pointer wrapper (for compilers without proper thread_local support) */ +template +class ThreadLocalPtr +{ +#if _WIN32 + DWORD m_key; +public: + ThreadLocalPtr() {m_key = TlsAlloc();} + ~ThreadLocalPtr() {TlsFree(m_key);} + T* get() {return static_cast(TlsGetValue(m_key));} + void reset(T* v=nullptr) {TlsSetValue(m_key, v);} +#else + pthread_key_t m_key; +public: + ThreadLocalPtr() {pthread_key_create(&m_key, nullptr);} + ~ThreadLocalPtr() {pthread_key_delete(m_key);} + T* get() {return static_cast(pthread_getspecific(m_key));} + void reset(T* v=nullptr) {pthread_setspecific(m_key, v);} +#endif + T* operator->() {return get();} +}; + /** Ownership token for maintaining lifetime of factory-created resources * deletion of this token triggers mass-deallocation of the factory's * IGraphicsData (please don't delete and draw contained resources in the same frame). */ diff --git a/include/boo/graphicsdev/Metal.hpp b/include/boo/graphicsdev/Metal.hpp index fec21eb..d08f9de 100644 --- a/include/boo/graphicsdev/Metal.hpp +++ b/include/boo/graphicsdev/Metal.hpp @@ -11,6 +11,7 @@ #include "IGraphicsCommandQueue.hpp" #include "boo/IGraphicsContext.hpp" #include +#include #include #include @@ -22,8 +23,9 @@ class MetalDataFactory : public IGraphicsDataFactory { friend struct MetalCommandQueue; IGraphicsContext* m_parent; - struct MetalData* m_deferredData = nullptr; + static ThreadLocalPtr m_deferredData; std::unordered_set m_committedData; + std::mutex m_committedMutex; struct MetalContext* m_ctx; void destroyData(IGraphicsData*); @@ -64,7 +66,7 @@ public: size_t texCount, ITexture** texs); void reset(); - IGraphicsDataToken commit(); + GraphicsDataToken commit(); }; } diff --git a/lib/graphicsdev/GL.cpp b/lib/graphicsdev/GL.cpp index 4270dc7..9596b30 100644 --- a/lib/graphicsdev/GL.cpp +++ b/lib/graphicsdev/GL.cpp @@ -16,7 +16,7 @@ namespace boo { static LogVisor::LogModule Log("boo::GL"); -thread_local struct GLData* GLDataFactory::m_deferredData; +ThreadLocalPtr GLDataFactory::m_deferredData; struct GLData : IGraphicsData { std::vector> m_SPs; @@ -94,9 +94,9 @@ IGraphicsBufferS* GLDataFactory::newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count) { GLGraphicsBufferS* retval = new GLGraphicsBufferS(use, data, stride * count); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_SBufs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_SBufs.emplace_back(retval); return retval; } @@ -105,9 +105,9 @@ GLDataFactory::newStaticBuffer(BufferUse use, std::unique_ptr&& data, { std::unique_ptr d = std::move(data); GLGraphicsBufferS* retval = new GLGraphicsBufferS(use, d.get(), stride * count); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_SBufs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_SBufs.emplace_back(retval); return retval; } @@ -249,9 +249,9 @@ GLDataFactory::newStaticTexture(size_t width, size_t height, size_t mips, Textur const void* data, size_t sz) { GLTextureS* retval = new GLTextureS(width, height, mips, fmt, data, sz); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_STexs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_STexs.emplace_back(retval); return retval; } @@ -261,9 +261,9 @@ GLDataFactory::newStaticTexture(size_t width, size_t height, size_t mips, Textur { std::unique_ptr d = std::move(data); GLTextureS* retval = new GLTextureS(width, height, mips, fmt, d.get(), sz); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_STexs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_STexs.emplace_back(retval); return retval; } @@ -272,9 +272,9 @@ GLDataFactory::newStaticArrayTexture(size_t width, size_t height, size_t layers, const void *data, size_t sz) { GLTextureSA* retval = new GLTextureSA(width, height, layers, fmt, data, sz); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_SATexs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_SATexs.emplace_back(retval); return retval; } @@ -473,9 +473,9 @@ IShaderPipeline* GLDataFactory::newShaderPipeline } GLShaderPipeline* retval = new GLShaderPipeline(std::move(shader)); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_SPs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_SPs.emplace_back(retval); return retval; } @@ -569,9 +569,9 @@ GLDataFactory::newShaderDataBinding(IShaderPipeline* pipeline, { GLShaderDataBinding* retval = new GLShaderDataBinding(pipeline, vtxFormat, ubufCount, ubufs, texCount, texs); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_SBinds.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_SBinds.emplace_back(retval); return retval; } @@ -580,21 +580,21 @@ GLDataFactory::GLDataFactory(IGraphicsContext* parent) void GLDataFactory::reset() { - delete static_cast(m_deferredData); - m_deferredData = nullptr; + delete m_deferredData.get(); + m_deferredData.reset(); } GraphicsDataToken GLDataFactory::commit() { - if (!m_deferredData) + if (!m_deferredData.get()) return GraphicsDataToken(this, nullptr); std::unique_lock lk(m_committedMutex); - GLData* retval = m_deferredData; + GLData* retval = m_deferredData.get(); #ifndef NDEBUG for (std::unique_ptr& b : retval->m_SBinds) b->m_committed = true; #endif - m_deferredData = nullptr; + m_deferredData.reset(); m_committedData.insert(retval); /* Let's go ahead and flush to ensure our data gets to the GPU While this isn't strictly required, some drivers might behave @@ -1158,9 +1158,9 @@ GLDataFactory::newDynamicBuffer(BufferUse use, size_t stride, size_t count) { GLCommandQueue* q = static_cast(m_parent->getCommandQueue()); GLGraphicsBufferD* retval = new GLGraphicsBufferD(q, use, stride * count); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_DBufs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_DBufs.emplace_back(retval); return retval; } @@ -1235,9 +1235,9 @@ GLDataFactory::newDynamicTexture(size_t width, size_t height, TextureFormat fmt) { GLCommandQueue* q = static_cast(m_parent->getCommandQueue()); GLTextureD* retval = new GLTextureD(q, width, height, fmt); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_DTexs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_DTexs.emplace_back(retval); return retval; } @@ -1259,9 +1259,9 @@ GLDataFactory::newRenderTexture(size_t width, size_t height, size_t samples) GLCommandQueue* q = static_cast(m_parent->getCommandQueue()); GLTextureR* retval = new GLTextureR(q, width, height, samples); q->resizeRenderTexture(retval, width, height); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_RTexs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_RTexs.emplace_back(retval); return retval; } @@ -1282,9 +1282,9 @@ IVertexFormat* GLDataFactory::newVertexFormat { GLCommandQueue* q = static_cast(m_parent->getCommandQueue()); GLVertexFormat* retval = new struct GLVertexFormat(q, elementCount, elements); - if (!m_deferredData) - m_deferredData = new struct GLData(); - static_cast(m_deferredData)->m_VFmts.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct GLData()); + m_deferredData->m_VFmts.emplace_back(retval); return retval; } diff --git a/lib/graphicsdev/Metal.mm b/lib/graphicsdev/Metal.mm index f73cfcb..ec38360 100644 --- a/lib/graphicsdev/Metal.mm +++ b/lib/graphicsdev/Metal.mm @@ -13,6 +13,7 @@ namespace boo static LogVisor::LogModule Log("boo::Metal"); struct MetalCommandQueue; +ThreadLocalPtr MetalDataFactory::m_deferredData; struct MetalData : IGraphicsData { std::vector> m_SPs; @@ -717,6 +718,7 @@ struct MetalCommandQueue : IGraphicsCommandQueue /* Update dynamic data here */ MetalDataFactory* gfxF = static_cast(m_parent->getDataFactory()); + std::unique_lock datalk(gfxF->m_committedMutex); for (MetalData* d : gfxF->m_committedData) { for (std::unique_ptr& b : d->m_DBufs) @@ -724,10 +726,7 @@ struct MetalCommandQueue : IGraphicsCommandQueue for (std::unique_ptr& t : d->m_DTexs) t->update(m_fillBuf); } - for (std::unique_ptr& b : gfxF->m_deferredData->m_DBufs) - b->update(m_fillBuf); - for (std::unique_ptr& t : gfxF->m_deferredData->m_DTexs) - t->update(m_fillBuf); + datalk.unlock(); @autoreleasepool { @@ -815,26 +814,32 @@ void MetalTextureD::unmap() } MetalDataFactory::MetalDataFactory(IGraphicsContext* parent, MetalContext* ctx) -: m_parent(parent), m_deferredData(new struct MetalData()), m_ctx(ctx) {} +: m_parent(parent), m_ctx(ctx) {} IGraphicsBufferS* MetalDataFactory::newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count) { MetalGraphicsBufferS* retval = new MetalGraphicsBufferS(use, m_ctx, data, stride, count); - static_cast(m_deferredData)->m_SBufs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_SBufs.emplace_back(retval); return retval; } IGraphicsBufferS* MetalDataFactory::newStaticBuffer(BufferUse use, std::unique_ptr&& data, size_t stride, size_t count) { std::unique_ptr d = std::move(data); MetalGraphicsBufferS* retval = new MetalGraphicsBufferS(use, m_ctx, d.get(), stride, count); - static_cast(m_deferredData)->m_SBufs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_SBufs.emplace_back(retval); return retval; } IGraphicsBufferD* MetalDataFactory::newDynamicBuffer(BufferUse use, size_t stride, size_t count) { MetalCommandQueue* q = static_cast(m_parent->getCommandQueue()); MetalGraphicsBufferD* retval = new MetalGraphicsBufferD(q, use, m_ctx, stride, count); - static_cast(m_deferredData)->m_DBufs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_DBufs.emplace_back(retval); return retval; } @@ -842,7 +847,9 @@ ITextureS* MetalDataFactory::newStaticTexture(size_t width, size_t height, size_ const void* data, size_t sz) { MetalTextureS* retval = new MetalTextureS(m_ctx, width, height, mips, fmt, data, sz); - static_cast(m_deferredData)->m_STexs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_STexs.emplace_back(retval); return retval; } ITextureS* MetalDataFactory::newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, @@ -850,34 +857,44 @@ ITextureS* MetalDataFactory::newStaticTexture(size_t width, size_t height, size_ { std::unique_ptr d = std::move(data); MetalTextureS* retval = new MetalTextureS(m_ctx, width, height, mips, fmt, d.get(), sz); - static_cast(m_deferredData)->m_STexs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_STexs.emplace_back(retval); return retval; } ITextureSA* MetalDataFactory::newStaticArrayTexture(size_t width, size_t height, size_t layers, TextureFormat fmt, const void* data, size_t sz) { MetalTextureSA* retval = new MetalTextureSA(m_ctx, width, height, layers, fmt, data, sz); - static_cast(m_deferredData)->m_SATexs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_SATexs.emplace_back(retval); return retval; } ITextureD* MetalDataFactory::newDynamicTexture(size_t width, size_t height, TextureFormat fmt) { MetalCommandQueue* q = static_cast(m_parent->getCommandQueue()); MetalTextureD* retval = new MetalTextureD(q, m_ctx, width, height, fmt); - static_cast(m_deferredData)->m_DTexs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_DTexs.emplace_back(retval); return retval; } ITextureR* MetalDataFactory::newRenderTexture(size_t width, size_t height, size_t samples) { MetalTextureR* retval = new MetalTextureR(m_ctx, width, height, samples); - static_cast(m_deferredData)->m_RTexs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_RTexs.emplace_back(retval); return retval; } IVertexFormat* MetalDataFactory::newVertexFormat(size_t elementCount, const VertexElementDescriptor* elements) { MetalVertexFormat* retval = new struct MetalVertexFormat(elementCount, elements); - static_cast(m_deferredData)->m_VFmts.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_VFmts.emplace_back(retval); return retval; } @@ -907,7 +924,9 @@ IShaderPipeline* MetalDataFactory::newShaderPipeline(const char* vertSource, con MetalShaderPipeline* retval = new MetalShaderPipeline(m_ctx, vertFunc.get(), fragFunc.get(), static_cast(vtxFmt), targetSamples, srcFac, dstFac, depthTest, depthWrite, backfaceCulling); - static_cast(m_deferredData)->m_SPs.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_SPs.emplace_back(retval); return retval; } @@ -920,30 +939,37 @@ MetalDataFactory::newShaderDataBinding(IShaderPipeline* pipeline, { MetalShaderDataBinding* retval = new MetalShaderDataBinding(m_ctx, pipeline, vbuf, instVbo, ibuf, ubufCount, ubufs, texCount, texs); - static_cast(m_deferredData)->m_SBinds.emplace_back(retval); + if (!m_deferredData.get()) + m_deferredData.reset(new struct MetalData()); + m_deferredData->m_SBinds.emplace_back(retval); return retval; } void MetalDataFactory::reset() { - delete static_cast(m_deferredData); - m_deferredData = new struct MetalData(); + delete m_deferredData.get(); + m_deferredData.reset(); } -IGraphicsDataToken MetalDataFactory::commit() +GraphicsDataToken MetalDataFactory::commit() { - MetalData* retval = static_cast(m_deferredData); - m_deferredData = new struct MetalData(); + if (!m_deferredData.get()) + return GraphicsDataToken(this, nullptr); + std::unique_lock lk(m_committedMutex); + MetalData* retval = m_deferredData.get(); + m_deferredData.reset(); m_committedData.insert(retval); - return IGraphicsDataToken(this, retval); + return GraphicsDataToken(this, retval); } void MetalDataFactory::destroyData(IGraphicsData* d) { + std::unique_lock lk(m_committedMutex); MetalData* data = static_cast(d); m_committedData.erase(data); delete data; } void MetalDataFactory::destroyAllData() { + std::unique_lock lk(m_committedMutex); for (IGraphicsData* data : m_committedData) delete static_cast(data); m_committedData.clear();