diff --git a/include/boo/BooObject.hpp b/include/boo/BooObject.hpp index 89ab27c..048c3c4 100644 --- a/include/boo/BooObject.hpp +++ b/include/boo/BooObject.hpp @@ -25,13 +25,13 @@ class ObjToken SubCls* m_obj = nullptr; public: ObjToken() = default; - ObjToken(SubCls* obj) : m_obj(obj) { m_obj->increment(); } - ObjToken(const ObjToken& other) : m_obj(other.m_obj) { m_obj->increment(); } + ObjToken(SubCls* obj) : m_obj(obj) { if (m_obj) m_obj->increment(); } + ObjToken(const ObjToken& other) : m_obj(other.m_obj) { if (m_obj) m_obj->increment(); } ObjToken(ObjToken&& other) : m_obj(other.m_obj) { other.m_obj = nullptr; } ObjToken& operator=(SubCls* obj) - { if (m_obj) m_obj->decrement(); m_obj = obj; m_obj->increment(); return *this; } + { if (m_obj) m_obj->decrement(); m_obj = obj; if (m_obj) m_obj->increment(); return *this; } ObjToken& operator=(const ObjToken& other) - { if (m_obj) m_obj->decrement(); m_obj = other.m_obj; m_obj->increment(); return *this; } + { if (m_obj) m_obj->decrement(); m_obj = other.m_obj; if (m_obj) m_obj->increment(); return *this; } ObjToken& operator=(ObjToken&& other) { if (m_obj) m_obj->decrement(); m_obj = other.m_obj; other.m_obj = nullptr; return *this; } ~ObjToken() { if (m_obj) m_obj->decrement(); } @@ -40,6 +40,7 @@ public: SubCls& operator*() const { return *m_obj; } template T* cast() const { return static_cast(m_obj); } operator bool() const { return m_obj != nullptr; } + void reset() { if (m_obj) m_obj->decrement(); m_obj = nullptr; } }; } diff --git a/include/boo/graphicsdev/GL.hpp b/include/boo/graphicsdev/GL.hpp index bcdf1b2..8cce3c9 100644 --- a/include/boo/graphicsdev/GL.hpp +++ b/include/boo/graphicsdev/GL.hpp @@ -8,7 +8,7 @@ namespace boo { -class BaseGraphicsData; +struct BaseGraphicsData; class GLDataFactory : public IGraphicsDataFactory { diff --git a/include/boo/graphicsdev/IGraphicsDataFactory.hpp b/include/boo/graphicsdev/IGraphicsDataFactory.hpp index 780a576..eb18649 100644 --- a/include/boo/graphicsdev/IGraphicsDataFactory.hpp +++ b/include/boo/graphicsdev/IGraphicsDataFactory.hpp @@ -139,12 +139,13 @@ ENABLE_BITWISE_ENUM(VertexSemantic) /** Used to create IVertexFormat */ struct VertexElementDescriptor { - IGraphicsBuffer* vertBuffer = nullptr; - IGraphicsBuffer* indexBuffer = nullptr; + ObjToken vertBuffer; + ObjToken indexBuffer; VertexSemantic semantic; int semanticIdx = 0; VertexElementDescriptor() = default; - VertexElementDescriptor(IGraphicsBuffer* v, IGraphicsBuffer* i, VertexSemantic s, int idx=0) + VertexElementDescriptor(const ObjToken& v, const ObjToken& i, + VertexSemantic s, int idx=0) : vertBuffer(v), indexBuffer(i), semantic(s), semanticIdx(idx) {} }; diff --git a/include/boo/graphicsdev/Metal.hpp b/include/boo/graphicsdev/Metal.hpp index c5a3a6b..9a78e82 100644 --- a/include/boo/graphicsdev/Metal.hpp +++ b/include/boo/graphicsdev/Metal.hpp @@ -9,6 +9,7 @@ namespace boo { +struct BaseGraphicsData; class MetalDataFactory : public IGraphicsDataFactory { @@ -17,7 +18,9 @@ public: { friend class MetalDataFactoryImpl; MetalDataFactory& m_parent; - Context(MetalDataFactory& parent) : m_parent(parent) {} + ObjToken m_data; + Context(MetalDataFactory& parent); + ~Context(); public: Platform platform() const { return Platform::Metal; } const SystemChar* platformName() const { return _S("Metal"); } @@ -40,7 +43,9 @@ public: size_t baseVert = 0, size_t baseInst = 0); ObjToken newShaderPipeline(const char* vertSource, const char* fragSource, - IVertexFormat* vtxFmt, unsigned targetSamples, + std::vector* vertBlobOut, + std::vector* fragBlobOut, + const ObjToken& vtxFmt, unsigned targetSamples, BlendFactor srcFac, BlendFactor dstFac, Primitive prim, ZTest depthTest, bool depthWrite, bool colorWrite, bool alphaWrite, CullMode culling); diff --git a/lib/graphicsdev/Common.hpp b/lib/graphicsdev/Common.hpp index d31c5a2..2609edf 100644 --- a/lib/graphicsdev/Common.hpp +++ b/lib/graphicsdev/Common.hpp @@ -26,6 +26,24 @@ struct GraphicsDataFactoryHead BaseGraphicsPool* m_poolHead = nullptr; }; +/** Linked-list iterator shareable by data container types */ +template +class DataIterator +{ + T* m_node; +public: + using value_type = T; + using pointer = T*; + using reference = T&; + using iterator_category = std::bidirectional_iterator_tag; + + explicit DataIterator(T* node) : m_node(node) {} + T& operator*() const { return *m_node; } + bool operator!=(const DataIterator& other) const { return m_node != other.m_node; } + DataIterator& operator++() { m_node = m_node->m_next; return *this; } + DataIterator& operator--() { m_node = m_node->m_prev; return *this; } +}; + /** Private generalized data container class. * Keeps head pointers to all graphics objects by type */ @@ -50,6 +68,8 @@ struct BaseGraphicsData : IObj { std::lock_guard lk(m_head.m_dataMutex); m_next = head.m_dataHead; + if (m_next) + m_next->m_prev = this; head.m_dataHead = this; } ~BaseGraphicsData() @@ -64,52 +84,37 @@ struct BaseGraphicsData : IObj else { if (m_next) - m_next->m_prev = m_head.m_dataHead; + m_next->m_prev = nullptr; m_head.m_dataHead = m_next; } } - class iterator - { - BaseGraphicsData* m_node; - public: - using value_type = BaseGraphicsData; - using pointer = BaseGraphicsData*; - using reference = BaseGraphicsData&; - using iterator_category = std::bidirectional_iterator_tag; - - explicit iterator(BaseGraphicsData* node) : m_node(node) {} - BaseGraphicsData& operator*() const { return *m_node; } - bool operator!=(const iterator& other) const { return m_node != other.m_node; } - iterator& operator++() { m_node = m_node->m_next; return *this; } - iterator& operator--() { m_node = m_node->m_prev; return *this; } - }; - + using iterator = DataIterator; iterator begin() { return iterator(this); } iterator end() { return iterator(nullptr); } }; -template <> GraphicsDataNode*& +template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_SPs; } -template <> GraphicsDataNode*& +template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_SBinds; } -template <> GraphicsDataNode*& +template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_SBufs; } -template <> GraphicsDataNode*& +template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_DBufs; } -template <> GraphicsDataNode*& +template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_STexs; } -template <> GraphicsDataNode*& +template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_SATexs; } -template <> GraphicsDataNode*& +template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_DTexs; } -template <> GraphicsDataNode*& +template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_RTexs; } -template <> GraphicsDataNode*& +template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_VFmts; } /** Private generalized pool container class. - * Keeps head pointer to exactly one dynamic buffer while otherwise conforming to IGraphicsData + * Keeps head pointer to exactly one dynamic buffer while otherwise conforming to BaseGraphicsData */ struct BaseGraphicsPool : IObj { @@ -124,6 +129,8 @@ struct BaseGraphicsPool : IObj { std::lock_guard lk(m_head.m_dataMutex); m_next = head.m_poolHead; + if (m_next) + m_next->m_prev = this; head.m_poolHead = this; } ~BaseGraphicsPool() @@ -138,32 +145,17 @@ struct BaseGraphicsPool : IObj else { if (m_next) - m_next->m_prev = m_head.m_poolHead; + m_next->m_prev = nullptr; m_head.m_poolHead = m_next; } } - class iterator - { - BaseGraphicsPool* m_node; - public: - using value_type = BaseGraphicsPool; - using pointer = BaseGraphicsPool*; - using reference = BaseGraphicsPool&; - using iterator_category = std::bidirectional_iterator_tag; - - explicit iterator(BaseGraphicsPool* node) : m_node(node) {} - BaseGraphicsPool& operator*() const { return *m_node; } - bool operator!=(const iterator& other) const { return m_node != other.m_node; } - iterator& operator++() { m_node = m_node->m_next; return *this; } - iterator& operator--() { m_node = m_node->m_prev; return *this; } - }; - + using iterator = DataIterator; iterator begin() { return iterator(this); } iterator end() { return iterator(nullptr); } }; -template <> GraphicsDataNode*& +template <> inline GraphicsDataNode*& BaseGraphicsPool::getHead() { return m_DBufs; } /** Private generalised graphics object node. @@ -182,6 +174,8 @@ struct GraphicsDataNode : NodeCls { std::lock_guard lk(m_data->m_head.m_dataMutex); m_next = data->template getHead(); + if (m_next) + m_next->m_prev = this; data->template getHead() = this; } ~GraphicsDataNode() @@ -196,7 +190,7 @@ struct GraphicsDataNode : NodeCls else { if (m_next) - m_next->m_prev = m_data->template getHead(); + m_next->m_prev = nullptr; m_data->template getHead() = m_next; } } @@ -221,6 +215,7 @@ struct GraphicsDataNode : NodeCls iterator end() { return iterator(nullptr); } }; +/** Hash table entry for owning sharable shader objects */ template class IShareableShader { diff --git a/lib/graphicsdev/GL.cpp b/lib/graphicsdev/GL.cpp index 5810352..82c09de 100644 --- a/lib/graphicsdev/GL.cpp +++ b/lib/graphicsdev/GL.cpp @@ -34,7 +34,8 @@ class GLDataFactoryImpl : public GLDataFactory, public GraphicsDataFactoryHead uint32_t m_drawSamples; std::unordered_map> m_sharedShaders; public: - GLDataFactoryImpl(IGraphicsContext* parent, uint32_t drawSamples); + GLDataFactoryImpl(IGraphicsContext* parent, uint32_t drawSamples) + : m_parent(parent), m_drawSamples(drawSamples) {} Platform platform() const { return Platform::OpenGL; } const SystemChar* platformName() const { return _S("OpenGL"); } @@ -60,14 +61,13 @@ class GLGraphicsBufferS : public GraphicsDataNode GLGraphicsBufferS(const ObjToken& parent, BufferUse use, const void* data, size_t sz) : GraphicsDataNode(parent) { - Log.report(logvisor::Info, "Create static buffer %p\n", this); m_target = USE_TABLE[int(use)]; glGenBuffers(1, &m_buf); glBindBuffer(m_target, m_buf); glBufferData(m_target, sz, data, GL_STATIC_DRAW); } public: - ~GLGraphicsBufferS() {glDeleteBuffers(1, &m_buf); Log.report(logvisor::Info, "Delete static buffer %p\n", this); } + ~GLGraphicsBufferS() { glDeleteBuffers(1, &m_buf); } void bindVertex() const {glBindBuffer(GL_ARRAY_BUFFER, m_buf);} @@ -94,7 +94,6 @@ class GLGraphicsBufferD : public GraphicsDataNode : GraphicsDataNode(parent), m_target(USE_TABLE[int(use)]), m_cpuBuf(new uint8_t[sz]), m_cpuSz(sz) { - Log.report(logvisor::Info, "Create dynamic buffer %p\n", this); glGenBuffers(3, m_bufs); for (int i=0 ; i<3 ; ++i) { @@ -102,18 +101,44 @@ class GLGraphicsBufferD : public GraphicsDataNode glBufferData(m_target, m_cpuSz, nullptr, GL_STREAM_DRAW); } } - void update(int b); public: - ~GLGraphicsBufferD() {glDeleteBuffers(3, m_bufs); Log.report(logvisor::Info, "Delete dynamic buffer %p\n", this);} + ~GLGraphicsBufferD() { glDeleteBuffers(3, m_bufs); } - void load(const void* data, size_t sz); - void* map(size_t sz); - void unmap(); + void update(int b) + { + int slot = 1 << b; + if ((slot & m_validMask) == 0) + { + glBindBuffer(m_target, m_bufs[b]); + glBufferSubData(m_target, 0, m_cpuSz, m_cpuBuf.get()); + m_validMask |= slot; + } + } - void bindVertex(int b); - void bindIndex(int b); - void bindUniform(size_t idx, int b); - void bindUniformRange(size_t idx, GLintptr off, GLsizeiptr size, int b); + void load(const void* data, size_t sz) + { + size_t bufSz = std::min(sz, m_cpuSz); + memcpy(m_cpuBuf.get(), data, bufSz); + m_validMask = 0; + } + void* map(size_t sz) + { + if (sz < m_cpuSz) + return nullptr; + return m_cpuBuf.get(); + } + void unmap() + { + m_validMask = 0; + } + void bindVertex(int b) + {glBindBuffer(GL_ARRAY_BUFFER, m_bufs[b]);} + void bindIndex(int b) + {glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bufs[b]);} + void bindUniform(size_t idx, int b) + {glBindBufferBase(GL_UNIFORM_BUFFER, idx, m_bufs[b]);} + void bindUniformRange(size_t idx, GLintptr off, GLsizeiptr size, int b) + {glBindBufferRange(GL_UNIFORM_BUFFER, idx, m_bufs[b], off, size);} }; ObjToken @@ -217,7 +242,7 @@ class GLTextureS : public GraphicsDataNode } } public: - ~GLTextureS() {glDeleteTextures(1, &m_tex);} + ~GLTextureS() { glDeleteTextures(1, &m_tex); } void bind(size_t idx) const { @@ -274,7 +299,7 @@ class GLTextureSA : public GraphicsDataNode } } public: - ~GLTextureSA() {glDeleteTextures(1, &m_tex);} + ~GLTextureSA() { glDeleteTextures(1, &m_tex); } void bind(size_t idx) const { @@ -294,16 +319,76 @@ class GLTextureD : public GraphicsDataNode size_t m_width = 0; size_t m_height = 0; int m_validMask = 0; - GLTextureD(const ObjToken& parent, size_t width, size_t height, TextureFormat fmt, TextureClampMode clampMode); - void update(int b); + GLTextureD(const ObjToken& parent, size_t width, size_t height, + TextureFormat fmt, TextureClampMode clampMode) + : GraphicsDataNode(parent), m_width(width), m_height(height) + { + int pxPitch = 4; + switch (fmt) + { + case TextureFormat::RGBA8: + m_intFormat = GL_RGBA8; + m_format = GL_RGBA; + pxPitch = 4; + break; + case TextureFormat::I8: + m_intFormat = GL_R8; + m_format = GL_RED; + pxPitch = 1; + break; + default: + Log.report(logvisor::Fatal, "unsupported tex format"); + } + m_cpuSz = width * height * pxPitch; + m_cpuBuf.reset(new uint8_t[m_cpuSz]); + + glGenTextures(3, m_texs); + for (int i=0 ; i<3 ; ++i) + { + glBindTexture(GL_TEXTURE_2D, m_texs[i]); + glTexImage2D(GL_TEXTURE_2D, 0, m_intFormat, width, height, 0, m_format, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + SetClampMode(GL_TEXTURE_2D, clampMode); + } + } + public: - ~GLTextureD(); + ~GLTextureD() { glDeleteTextures(3, m_texs); } - void load(const void* data, size_t sz); - void* map(size_t sz); - void unmap(); + void update(int b) + { + int slot = 1 << b; + if ((slot & m_validMask) == 0) + { + glBindTexture(GL_TEXTURE_2D, m_texs[b]); + glTexImage2D(GL_TEXTURE_2D, 0, m_intFormat, m_width, m_height, 0, m_format, GL_UNSIGNED_BYTE, m_cpuBuf.get()); + m_validMask |= slot; + } + } - void bind(size_t idx, int b); + void load(const void* data, size_t sz) + { + size_t bufSz = std::min(sz, m_cpuSz); + memcpy(m_cpuBuf.get(), data, bufSz); + m_validMask = 0; + } + void* map(size_t sz) + { + if (sz > m_cpuSz) + return nullptr; + return m_cpuBuf.get(); + } + void unmap() + { + m_validMask = 0; + } + + void bind(size_t idx, int b) + { + glActiveTexture(GL_TEXTURE0 + idx); + glBindTexture(GL_TEXTURE_2D, m_texs[b]); + } }; #define MAX_BIND_TEXS 4 @@ -323,7 +408,12 @@ class GLTextureR : public GraphicsDataNode GLTextureR(const ObjToken& parent, GLCommandQueue* q, size_t width, size_t height, size_t samples, TextureClampMode clampMode, size_t colorBindCount, size_t depthBindCount); public: - ~GLTextureR(); + ~GLTextureR() + { + glDeleteTextures(2, m_texs); + glDeleteTextures(MAX_BIND_TEXS * 2, m_bindTexs[0]); + glDeleteFramebuffers(1, &m_fbo); + } void bind(size_t idx, int bindIdx, bool depth) const { @@ -673,16 +763,14 @@ ObjToken GLDataFactory::Context::newShaderPipeline struct GLVertexFormat : GraphicsDataNode { - GLCommandQueue* m_q; GLuint m_vao[3] = {}; - size_t m_elementCount; GLuint m_baseVert, m_baseInst; - std::unique_ptr m_elements; - GLVertexFormat(const ObjToken& parent, GLCommandQueue* q, size_t elementCount, - const VertexElementDescriptor* elements, + std::vector m_elements; + GLVertexFormat(const ObjToken& parent, GLCommandQueue* q, + size_t elementCount, const VertexElementDescriptor* elements, size_t baseVert, size_t baseInst); - ~GLVertexFormat(); - void bind(int idx) const {glBindVertexArray(m_vao[idx]);} + ~GLVertexFormat() { glDeleteVertexArrays(3, m_vao); } + void bind(int idx) const { glBindVertexArray(m_vao[idx]); } }; struct GLShaderDataBinding : GraphicsDataNode @@ -735,7 +823,7 @@ struct GLShaderDataBinding : GraphicsDataNode m_texs.reserve(texCount); for (size_t i=0 ; i& pi ubufOffs, ubufSizes, texCount, texs, texBindIdx, depthBind)}; } -GLDataFactoryImpl::GLDataFactoryImpl(IGraphicsContext* parent, uint32_t drawSamples) -: m_parent(parent), m_drawSamples(drawSamples) {} - GLDataFactory::Context::Context(GLDataFactory& parent) : m_parent(parent), m_data(new BaseGraphicsData(static_cast(parent))) {} @@ -974,13 +1059,13 @@ struct GLCommandQueue : IGraphicsCommandQueue size_t stride = 0; size_t instStride = 0; - for (size_t i=0 ; im_elementCount ; ++i) + for (size_t i=0 ; im_elements.size() ; ++i) { - const VertexElementDescriptor* desc = &fmt->m_elements[i]; - if ((desc->semantic & VertexSemantic::Instanced) != VertexSemantic::None) - instStride += SEMANTIC_SIZE_TABLE[int(desc->semantic & VertexSemantic::SemanticMask)]; + const VertexElementDescriptor& desc = fmt->m_elements[i]; + if ((desc.semantic & VertexSemantic::Instanced) != VertexSemantic::None) + instStride += SEMANTIC_SIZE_TABLE[int(desc.semantic & VertexSemantic::SemanticMask)]; else - stride += SEMANTIC_SIZE_TABLE[int(desc->semantic & VertexSemantic::SemanticMask)]; + stride += SEMANTIC_SIZE_TABLE[int(desc.semantic & VertexSemantic::SemanticMask)]; } for (int b=0 ; b<3 ; ++b) @@ -990,28 +1075,28 @@ struct GLCommandQueue : IGraphicsCommandQueue glBindVertexArray(fmt->m_vao[b]); IGraphicsBuffer* lastVBO = nullptr; IGraphicsBuffer* lastEBO = nullptr; - for (size_t i=0 ; im_elementCount ; ++i) + for (size_t i=0 ; im_elements.size() ; ++i) { - const VertexElementDescriptor* desc = &fmt->m_elements[i]; - if (desc->vertBuffer != lastVBO) + const VertexElementDescriptor& desc = fmt->m_elements[i]; + if (desc.vertBuffer.get() != lastVBO) { - lastVBO = desc->vertBuffer; + lastVBO = desc.vertBuffer.get(); if (lastVBO->dynamic()) static_cast*>(lastVBO)->bindVertex(b); else static_cast(lastVBO)->bindVertex(); } - if (desc->indexBuffer != lastEBO) + if (desc.indexBuffer.get() != lastEBO) { - lastEBO = desc->indexBuffer; + lastEBO = desc.indexBuffer.get(); if (lastEBO->dynamic()) static_cast*>(lastEBO)->bindIndex(b); else static_cast(lastEBO)->bindIndex(); } glEnableVertexAttribArray(i); - int maskedSem = int(desc->semantic & VertexSemantic::SemanticMask); - if ((desc->semantic & VertexSemantic::Instanced) != VertexSemantic::None) + int maskedSem = int(desc.semantic & VertexSemantic::SemanticMask); + if ((desc.semantic & VertexSemantic::Instanced) != VertexSemantic::None) { glVertexAttribPointer(i, SEMANTIC_COUNT_TABLE[maskedSem], SEMANTIC_TYPE_TABLE[maskedSem], GL_TRUE, instStride, (void*)instOffset); @@ -1350,39 +1435,12 @@ struct GLCommandQueue : IGraphicsCommandQueue m_pendingFmtAdds.push_back(fmt); } - void delVertexFormat(const ObjToken& fmt) - { -#if 0 - std::unique_lock lk(m_mt); - bool foundAdd = false; - for (GLVertexFormat*& afmt : m_pendingFmtAdds) - if (afmt == fmt) - { - foundAdd = true; - afmt = nullptr; - break; - } - if (!foundAdd) - m_pendingFmtDels.push_back({fmt->m_vao[0], fmt->m_vao[1], fmt->m_vao[2]}); -#endif - glDeleteVertexArrays(3, fmt->m_vao); - } - void addFBO(const ObjToken& tex) { std::unique_lock lk(m_mt); m_pendingFboAdds.push_back(tex); } - void delFBO(const ObjToken& tex) - { -#if 0 - std::unique_lock lk(m_mt); - m_pendingFboDels.push_back(tex->m_fbo); -#endif - glDeleteFramebuffers(1, &tex.cast()->m_fbo); - } - void execute() { std::unique_lock lk(m_mt); @@ -1432,125 +1490,12 @@ struct GLCommandQueue : IGraphicsCommandQueue } }; -template -void GLGraphicsBufferD::update(int b) -{ - int slot = 1 << b; - if ((slot & m_validMask) == 0) - { - glBindBuffer(m_target, m_bufs[b]); - glBufferSubData(m_target, 0, m_cpuSz, m_cpuBuf.get()); - m_validMask |= slot; - } -} - -template -void GLGraphicsBufferD::load(const void* data, size_t sz) -{ - size_t bufSz = std::min(sz, m_cpuSz); - memcpy(m_cpuBuf.get(), data, bufSz); - m_validMask = 0; -} -template -void* GLGraphicsBufferD::map(size_t sz) -{ - if (sz < m_cpuSz) - return nullptr; - return m_cpuBuf.get(); -} -template -void GLGraphicsBufferD::unmap() -{ - m_validMask = 0; -} -template -void GLGraphicsBufferD::bindVertex(int b) -{glBindBuffer(GL_ARRAY_BUFFER, m_bufs[b]);} -template -void GLGraphicsBufferD::bindIndex(int b) -{glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bufs[b]);} -template -void GLGraphicsBufferD::bindUniform(size_t idx, int b) -{glBindBufferBase(GL_UNIFORM_BUFFER, idx, m_bufs[b]);} -template -void GLGraphicsBufferD::bindUniformRange(size_t idx, GLintptr off, GLsizeiptr size, int b) -{glBindBufferRange(GL_UNIFORM_BUFFER, idx, m_bufs[b], off, size);} - ObjToken GLDataFactory::Context::newDynamicBuffer(BufferUse use, size_t stride, size_t count) { return {new GLGraphicsBufferD(m_data, use, stride * count)}; } -GLTextureD::GLTextureD(const ObjToken& parent, size_t width, size_t height, TextureFormat fmt, - TextureClampMode clampMode) -: GraphicsDataNode(parent), m_width(width), m_height(height) -{ - int pxPitch = 4; - switch (fmt) - { - case TextureFormat::RGBA8: - m_intFormat = GL_RGBA8; - m_format = GL_RGBA; - pxPitch = 4; - break; - case TextureFormat::I8: - m_intFormat = GL_R8; - m_format = GL_RED; - pxPitch = 1; - break; - default: - Log.report(logvisor::Fatal, "unsupported tex format"); - } - m_cpuSz = width * height * pxPitch; - m_cpuBuf.reset(new uint8_t[m_cpuSz]); - - glGenTextures(3, m_texs); - for (int i=0 ; i<3 ; ++i) - { - glBindTexture(GL_TEXTURE_2D, m_texs[i]); - glTexImage2D(GL_TEXTURE_2D, 0, m_intFormat, width, height, 0, m_format, GL_UNSIGNED_BYTE, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - SetClampMode(GL_TEXTURE_2D, clampMode); - } -} -GLTextureD::~GLTextureD() { glDeleteTextures(3, m_texs); } - -void GLTextureD::update(int b) -{ - int slot = 1 << b; - if ((slot & m_validMask) == 0) - { - glBindTexture(GL_TEXTURE_2D, m_texs[b]); - glTexImage2D(GL_TEXTURE_2D, 0, m_intFormat, m_width, m_height, 0, m_format, GL_UNSIGNED_BYTE, m_cpuBuf.get()); - m_validMask |= slot; - } -} - -void GLTextureD::load(const void* data, size_t sz) -{ - size_t bufSz = std::min(sz, m_cpuSz); - memcpy(m_cpuBuf.get(), data, bufSz); - m_validMask = 0; -} -void* GLTextureD::map(size_t sz) -{ - if (sz > m_cpuSz) - return nullptr; - return m_cpuBuf.get(); -} -void GLTextureD::unmap() -{ - m_validMask = 0; -} - -void GLTextureD::bind(size_t idx, int b) -{ - glActiveTexture(GL_TEXTURE0 + idx); - glBindTexture(GL_TEXTURE_2D, m_texs[b]); -} - ObjToken GLDataFactory::Context::newDynamicTexture(size_t width, size_t height, TextureFormat fmt, TextureClampMode clampMode) { @@ -1612,13 +1557,6 @@ GLTextureR::GLTextureR(const ObjToken& parent, GLCommandQueue* m_q->addFBO(this); } -GLTextureR::~GLTextureR() -{ - glDeleteTextures(2, m_texs); - glDeleteTextures(MAX_BIND_TEXS * 2, m_bindTexs[0]); - m_q->delFBO(this); -} - ObjToken GLDataFactory::Context::newRenderTexture(size_t width, size_t height, TextureClampMode clampMode, size_t colorBindingCount, size_t depthBindingCount) @@ -1631,20 +1569,17 @@ GLDataFactory::Context::newRenderTexture(size_t width, size_t height, TextureCla return retval; } -GLVertexFormat::GLVertexFormat(const ObjToken& parent, GLCommandQueue* q, size_t elementCount, - const VertexElementDescriptor* elements, +GLVertexFormat::GLVertexFormat(const ObjToken& parent, GLCommandQueue* q, + size_t elementCount, const VertexElementDescriptor* elements, size_t baseVert, size_t baseInst) : GraphicsDataNode(parent), - m_q(q), - m_elementCount(elementCount), - m_elements(new VertexElementDescriptor[elementCount]), m_baseVert(baseVert), m_baseInst(baseInst) { + m_elements.reserve(elementCount); for (size_t i=0 ; iaddVertexFormat(this); + m_elements.push_back(elements[i]); + q->addVertexFormat(this); } -GLVertexFormat::~GLVertexFormat() { m_q->delVertexFormat(this); } ObjToken GLDataFactory::Context::newVertexFormat (size_t elementCount, const VertexElementDescriptor* elements, diff --git a/lib/graphicsdev/Metal.mm b/lib/graphicsdev/Metal.mm index 6e5e00e..83a0d26 100644 --- a/lib/graphicsdev/Metal.mm +++ b/lib/graphicsdev/Metal.mm @@ -1,6 +1,7 @@ #include "../mac/CocoaCommon.hpp" #if BOO_HAS_METAL #include "logvisor/logvisor.hpp" +#include "boo/IApplication.hpp" #include "boo/graphicsdev/Metal.hpp" #include "boo/IGraphicsContext.hpp" #include "Common.hpp" @@ -25,8 +26,8 @@ class MetalDataFactoryImpl; struct MetalShareableShader : IShareableShader { id m_shader; - MetalShareableShader(MetalDataFactoryImpl& fac, uint64_t srcKey, id s) - : IShareableShader(fac, srcKey, 0), m_shader(s) {} + MetalShareableShader(MetalDataFactoryImpl& fac, uint64_t srcKey, uint64_t binKey, id s) + : IShareableShader(fac, srcKey, binKey), m_shader(s) {} }; class MetalDataFactoryImpl : public MetalDataFactory, public GraphicsDataFactoryHead @@ -39,7 +40,21 @@ class MetalDataFactoryImpl : public MetalDataFactory, public GraphicsDataFactory uint32_t m_sampleCount; public: - MetalDataFactoryImpl(const ObjToken& parent, MetalContext* ctx, uint32_t sampleCount); + std::unordered_map m_sourceToBinary; + char m_libfile[MAXPATHLEN]; + bool m_hasCompiler = false; + + MetalDataFactoryImpl(IGraphicsContext* parent, MetalContext* ctx, uint32_t sampleCount) + : m_parent(parent), m_ctx(ctx), m_sampleCount(sampleCount) + { + snprintf(m_libfile, MAXPATHLEN, "%sboo_metal_shader.metallib", getenv("TMPDIR")); + for (auto& arg : APP->getArgs()) + if (arg == "--metal-compile") + { + m_hasCompiler = CheckForMetalCompiler(); + break; + } + } ~MetalDataFactoryImpl() = default; Platform platform() const { return Platform::Metal; } @@ -47,6 +62,146 @@ public: void commitTransaction(const std::function&); ObjToken newPoolBuffer(BufferUse use, size_t stride, size_t count); void _unregisterShareableShader(uint64_t srcKey, uint64_t binKey) { m_sharedShaders.erase(srcKey); } + + static bool CheckForMetalCompiler() + { + pid_t pid = fork(); + if (!pid) + { + execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", NULL); + /* xcrun returns 72 if metal command not found; + * emulate that if xcrun not found */ + exit(72); + } + + int status, ret; + while ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR) {} + if (ret < 0) + return false; + return WEXITSTATUS(status) == 1; + } + + uint64_t CompileLib(std::vector& blobOut, const char* source, uint64_t srcKey) + { + if (!m_hasCompiler) + { + /* Cache the source if there's no compiler */ + size_t sourceLen = strlen(source); + + /* First byte unset to indicate source data */ + blobOut.resize(sourceLen + 2); + memcpy(&blobOut[1], source, sourceLen); + } + else + { + /* Cache the binary otherwise */ + int compilerOut[2]; + int compilerIn[2]; + pipe(compilerOut); + pipe(compilerIn); + + /* Pipe source write to compiler */ + pid_t compilerPid = fork(); + if (!compilerPid) + { + dup2(compilerIn[0], STDIN_FILENO); + dup2(compilerOut[1], STDOUT_FILENO); + + close(compilerOut[0]); + close(compilerOut[1]); + close(compilerIn[0]); + close(compilerIn[1]); + + execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", "-o", "/dev/stdout", "-Wno-unused-variable", + "-Wno-unused-const-variable", "-Wno-unused-function", "-x", "metal", "-", NULL); + fprintf(stderr, "execlp fail %s\n", strerror(errno)); + exit(1); + } + close(compilerIn[0]); + close(compilerOut[1]); + + /* Pipe compiler to linker */ + pid_t linkerPid = fork(); + if (!linkerPid) + { + dup2(compilerOut[0], STDIN_FILENO); + + close(compilerOut[0]); + close(compilerIn[1]); + + /* metallib doesn't like outputting to a pipe, so temp file will have to do */ + execlp("xcrun", "xcrun", "-sdk", "macosx", "metallib", "-", "-o", m_libfile, NULL); + fprintf(stderr, "execlp fail %s\n", strerror(errno)); + exit(1); + } + close(compilerOut[0]); + + /* Stream in source */ + const char* inPtr = source; + size_t inRem = strlen(source); + while (inRem) + { + ssize_t writeRes = write(compilerIn[1], inPtr, inRem); + if (writeRes < 0) + { + fprintf(stderr, "write fail %s\n", strerror(errno)); + break; + } + inPtr += writeRes; + inRem -= writeRes; + } + close(compilerIn[1]); + + /* Wait for completion */ + int compilerStat, linkerStat; + if (waitpid(compilerPid, &compilerStat, 0) < 0 || waitpid(linkerPid, &linkerStat, 0) < 0) + { + fprintf(stderr, "waitpid fail %s\n", strerror(errno)); + return 0; + } + + if (WEXITSTATUS(compilerStat) || WEXITSTATUS(linkerStat)) + return 0; + + /* Copy temp file into buffer with first byte set to indicate binary data */ + FILE* fin = fopen(m_libfile, "rb"); + fseek(fin, 0, SEEK_END); + long libLen = ftell(fin); + fseek(fin, 0, SEEK_SET); + blobOut.resize(libLen + 1); + blobOut[0] = 1; + fread(&blobOut[1], 1, libLen, fin); + fclose(fin); + } + + XXH64_state_t hashState; + XXH64_reset(&hashState, 0); + XXH64_update(&hashState, blobOut.data(), blobOut.size()); + uint64_t binKey = XXH64_digest(&hashState); + m_sourceToBinary[srcKey] = binKey; + return binKey; + } + + uint64_t CompileLib(__strong id& libOut, const char* source, uint64_t srcKey, + MTLCompileOptions* compOpts, NSError * _Nullable *err) + { + libOut = [m_ctx->m_dev newLibraryWithSource:@(source) + options:compOpts + error:err]; + + if (srcKey) + { + XXH64_state_t hashState; + XXH64_reset(&hashState, 0); + uint8_t zero = 0; + XXH64_update(&hashState, &zero, 1); + XXH64_update(&hashState, source, strlen(source) + 1); + uint64_t binKey = XXH64_digest(&hashState); + m_sourceToBinary[srcKey] = binKey; + return binKey; + } + return 0; + } }; #define MTL_STATIC MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared @@ -79,7 +234,7 @@ class MetalGraphicsBufferD : public GraphicsDataNode MetalCommandQueue* m_q; std::unique_ptr m_cpuBuf; int m_validSlots = 0; - MetalGraphicsBufferD(const ObjToken& parent, MetalCommandQueue* q, BufferUse use, + MetalGraphicsBufferD(const ObjToken& parent, MetalCommandQueue* q, BufferUse use, MetalContext* ctx, size_t stride, size_t count) : GraphicsDataNode(parent), m_q(q), m_stride(stride), m_count(count), m_sz(stride * count) @@ -88,7 +243,6 @@ class MetalGraphicsBufferD : public GraphicsDataNode m_bufs[0] = [ctx->m_dev newBufferWithLength:m_sz options:MTL_DYNAMIC]; m_bufs[1] = [ctx->m_dev newBufferWithLength:m_sz options:MTL_DYNAMIC]; } - void update(int b); public: size_t m_stride; size_t m_count; @@ -96,16 +250,39 @@ public: id m_bufs[2]; MetalGraphicsBufferD() = default; - void load(const void* data, size_t sz); - void* map(size_t sz); - void unmap(); + void update(int b) + { + int slot = 1 << b; + if ((slot & m_validSlots) == 0) + { + id res = m_bufs[b]; + memcpy(res.contents, m_cpuBuf.get(), m_sz); + m_validSlots |= slot; + } + } + void load(const void* data, size_t sz) + { + size_t bufSz = std::min(sz, m_sz); + memcpy(m_cpuBuf.get(), data, bufSz); + m_validSlots = 0; + } + void* map(size_t sz) + { + if (sz > m_sz) + return nullptr; + return m_cpuBuf.get(); + } + void unmap() + { + m_validSlots = 0; + } }; class MetalTextureS : public GraphicsDataNode { friend class MetalDataFactory; - MetalTextureS(BaseGraphicsData* parent, MetalContext* ctx, size_t width, size_t height, size_t mips, - TextureFormat fmt, const void* data, size_t sz) + MetalTextureS(const ObjToken& parent, MetalContext* ctx, size_t width, size_t height, + size_t mips, TextureFormat fmt, const void* data, size_t sz) : GraphicsDataNode(parent) { MTLPixelFormat pfmt = MTLPixelFormatRGBA8Unorm; @@ -156,7 +333,7 @@ public: class MetalTextureSA : public GraphicsDataNode { friend class MetalDataFactory; - MetalTextureSA(BaseGraphicsData* parent, MetalContext* ctx, size_t width, + MetalTextureSA(const ObjToken& parent, MetalContext* ctx, size_t width, size_t height, size_t layers, size_t mips, TextureFormat fmt, const void* data, size_t sz) : GraphicsDataNode(parent) @@ -219,7 +396,7 @@ class MetalTextureD : public GraphicsDataNode size_t m_cpuSz; size_t m_pxPitch; int m_validSlots = 0; - MetalTextureD(BaseGraphicsData* parent, MetalCommandQueue* q, MetalContext* ctx, + MetalTextureD(const ObjToken& parent, MetalCommandQueue* q, MetalContext* ctx, size_t width, size_t height, TextureFormat fmt) : GraphicsDataNode(parent), m_q(q), m_width(width), m_height(height) { @@ -252,14 +429,37 @@ class MetalTextureD : public GraphicsDataNode m_texs[1] = [ctx->m_dev newTextureWithDescriptor:desc]; } } - void update(int b); public: id m_texs[2]; ~MetalTextureD() = default; - void load(const void* data, size_t sz); - void* map(size_t sz); - void unmap(); + void update(int b) + { + int slot = 1 << b; + if ((slot & m_validSlots) == 0) + { + id res = m_texs[b]; + [res replaceRegion:MTLRegionMake2D(0, 0, m_width, m_height) + mipmapLevel:0 withBytes:m_cpuBuf.get() bytesPerRow:m_width*m_pxPitch]; + m_validSlots |= slot; + } + } + void load(const void* data, size_t sz) + { + size_t bufSz = std::min(sz, m_cpuSz); + memcpy(m_cpuBuf.get(), data, bufSz); + m_validSlots = 0; + } + void* map(size_t sz) + { + if (sz > m_cpuSz) + return nullptr; + return m_cpuBuf.get(); + } + void unmap() + { + m_validSlots = 0; + } }; #define MAX_BIND_TEXS 4 @@ -383,8 +583,8 @@ class MetalTextureR : public GraphicsDataNode } } - MetalTextureR(BaseGraphicsData* parent, MetalContext* ctx, size_t width, size_t height, size_t samples, - size_t colorBindCount, size_t depthBindCount) + MetalTextureR(const ObjToken& parent, MetalContext* ctx, size_t width, size_t height, + size_t samples, size_t colorBindCount, size_t depthBindCount) : GraphicsDataNode(parent), m_width(width), m_height(height), m_samples(samples), m_colorBindCount(colorBindCount), m_depthBindCount(depthBindCount) @@ -452,8 +652,9 @@ struct MetalVertexFormat : GraphicsDataNode MTLVertexDescriptor* m_vdesc; size_t m_stride = 0; size_t m_instStride = 0; - MetalVertexFormat(BaseGraphicsData* parent, size_t elementCount, const VertexElementDescriptor* elements) - : GraphicsDataNode(parent), m_elementCount(elementCount) + MetalVertexFormat(const ObjToken& parent, + size_t elementCount, const VertexElementDescriptor* elements) + : GraphicsDataNode(parent), m_elementCount(elementCount) { for (size_t i=0 ; i friend struct MetalShaderDataBinding; MTLCullMode m_cullMode = MTLCullModeNone; MTLPrimitiveType m_drawPrim; - const MetalVertexFormat* m_vtxFmt; MetalShareableShader::Token m_vert; MetalShareableShader::Token m_frag; - MetalShaderPipeline(BaseGraphicsData* parent, + MetalShaderPipeline(const ObjToken& parent, MetalContext* ctx, MetalShareableShader::Token&& vert, MetalShareableShader::Token&& frag, - const MetalVertexFormat* vtxFmt, NSUInteger targetSamples, + const ObjToken& vtxFmt, NSUInteger targetSamples, BlendFactor srcFac, BlendFactor dstFac, Primitive prim, ZTest depthTest, bool depthWrite, bool colorWrite, bool alphaWrite, CullMode culling) : GraphicsDataNode(parent), - m_drawPrim(PRIMITIVE_TABLE[int(prim)]), m_vtxFmt(vtxFmt), + m_drawPrim(PRIMITIVE_TABLE[int(prim)]), m_vert(std::move(vert)), m_frag(std::move(frag)) { switch (culling) @@ -569,7 +769,7 @@ class MetalShaderPipeline : public GraphicsDataNode MTLRenderPipelineDescriptor* desc = [MTLRenderPipelineDescriptor new]; desc.vertexFunction = m_vert.get().m_shader; desc.fragmentFunction = m_frag.get().m_shader; - desc.vertexDescriptor = vtxFmt->m_vdesc; + desc.vertexDescriptor = vtxFmt.cast()->m_vdesc; desc.sampleCount = targetSamples; desc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; desc.colorAttachments[0].writeMask = (colorWrite ? COLOR_WRITE_MASK : 0) | @@ -636,58 +836,58 @@ public: } }; -static id GetBufferGPUResource(const IGraphicsBuffer* buf, int idx) +static id GetBufferGPUResource(const ObjToken& buf, int idx) { if (buf->dynamic()) { - const MetalGraphicsBufferD* cbuf = static_cast(buf); + const MetalGraphicsBufferD* cbuf = buf.cast>(); return cbuf->m_bufs[idx]; } else { - const MetalGraphicsBufferS* cbuf = static_cast(buf); + const MetalGraphicsBufferS* cbuf = buf.cast(); return cbuf->m_buf; } } -static id GetBufferGPUResource(const IGraphicsBuffer* buf, int idx, size_t& strideOut) +static id GetBufferGPUResource(const ObjToken& buf, int idx, size_t& strideOut) { if (buf->dynamic()) { - const MetalGraphicsBufferD* cbuf = static_cast(buf); + const MetalGraphicsBufferD* cbuf = buf.cast>(); strideOut = cbuf->m_stride; return cbuf->m_bufs[idx]; } else { - const MetalGraphicsBufferS* cbuf = static_cast(buf); + const MetalGraphicsBufferS* cbuf = buf.cast(); strideOut = cbuf->m_stride; return cbuf->m_buf; } } -static id GetTextureGPUResource(const ITexture* tex, int idx, int bindIdx, bool depth) +static id GetTextureGPUResource(const ObjToken& tex, int idx, int bindIdx, bool depth) { switch (tex->type()) { case TextureType::Dynamic: { - const MetalTextureD* ctex = static_cast(tex); + const MetalTextureD* ctex = tex.cast(); return ctex->m_texs[idx]; } case TextureType::Static: { - const MetalTextureS* ctex = static_cast(tex); + const MetalTextureS* ctex = tex.cast(); return ctex->m_tex; } case TextureType::StaticArray: { - const MetalTextureSA* ctex = static_cast(tex); + const MetalTextureSA* ctex = tex.cast(); return ctex->m_tex; } case TextureType::Render: { - const MetalTextureR* ctex = static_cast(tex); + const MetalTextureR* ctex = tex.cast(); return depth ? ctex->m_depthBindTex[bindIdx] : ctex->m_colorBindTex[bindIdx]; } default: break; @@ -697,88 +897,80 @@ static id GetTextureGPUResource(const ITexture* tex, int idx, int bi struct MetalShaderDataBinding : GraphicsDataNode { - MetalShaderPipeline* m_pipeline; - IGraphicsBuffer* m_vbuf; - IGraphicsBuffer* m_instVbo; - IGraphicsBuffer* m_ibuf; - size_t m_ubufCount; - std::unique_ptr m_ubufs; - std::unique_ptr m_ubufOffs; - std::unique_ptr m_fubufs; - size_t m_texCount; + ObjToken m_pipeline; + ObjToken m_vbuf; + ObjToken m_instVbo; + ObjToken m_ibuf; + std::vector> m_ubufs; + std::vector m_ubufOffs; + std::vector m_fubufs; struct BoundTex { - ITexture* tex; + ObjToken tex; int idx; bool depth; }; - std::unique_ptr m_texs; + std::vector m_texs; size_t m_baseVert; size_t m_baseInst; - MetalShaderDataBinding(MetalData* d, + MetalShaderDataBinding(const ObjToken& d, MetalContext* ctx, - IShaderPipeline* pipeline, - IGraphicsBuffer* vbuf, IGraphicsBuffer* instVbo, IGraphicsBuffer* ibuf, - size_t ubufCount, IGraphicsBuffer** ubufs, const PipelineStage* ubufStages, + const ObjToken& pipeline, + const ObjToken& vbuf, + const ObjToken& instVbo, + const ObjToken& ibuf, + size_t ubufCount, const ObjToken* ubufs, const PipelineStage* ubufStages, const size_t* ubufOffs, const size_t* ubufSizes, - size_t texCount, ITexture** texs, + size_t texCount, const ObjToken* texs, const int* texBindIdxs, const bool* depthBind, size_t baseVert, size_t baseInst) : GraphicsDataNode(d), - m_pipeline(static_cast(pipeline)), + m_pipeline(pipeline), m_vbuf(vbuf), m_instVbo(instVbo), m_ibuf(ibuf), - m_ubufCount(ubufCount), - m_ubufs(new IGraphicsBuffer*[ubufCount]), - m_texCount(texCount), - m_texs(new BoundTex[texCount]), m_baseVert(baseVert), m_baseInst(baseInst) { - addDepData(m_pipeline->m_parentData); - if (ubufCount && ubufStages) { - m_fubufs.reset(new bool[ubufCount]); + m_fubufs.reserve(ubufCount); for (size_t i=0 ; im_parentData); + m_ubufs.push_back(ubufs[i]); } + m_texs.reserve(texCount); for (size_t i=0 ; im_parentData); + m_texs.push_back({texs[i], texBindIdxs ? texBindIdxs[i] : 0, depthBind ? depthBind[i] : false}); } } void bind(id enc, int b) { - m_pipeline->bind(enc); + m_pipeline.cast()->bind(enc); size_t stride; if (m_vbuf) @@ -791,23 +983,23 @@ struct MetalShaderDataBinding : GraphicsDataNode id buf = GetBufferGPUResource(m_instVbo, b, stride); [enc setVertexBuffer:buf offset:stride * m_baseInst atIndex:1]; } - if (m_ubufOffs) - for (size_t i=0 ; i(binding); + MetalShaderDataBinding* cbind = binding.cast(); cbind->bind(m_enc, m_fillBuf); m_boundData = cbind; - m_currentPrimitive = cbind->m_pipeline->m_drawPrim; + m_currentPrimitive = cbind->m_pipeline.cast()->m_drawPrim; } } @@ -975,10 +1167,10 @@ struct MetalCommandQueue : IGraphicsCommandQueue instanceCount:instCount]; } - void resolveBindTexture(ITextureR* texture, const SWindowRect& rect, bool tlOrigin, + void resolveBindTexture(const ObjToken& texture, const SWindowRect& rect, bool tlOrigin, int bindIdx, bool color, bool depth) { - MetalTextureR* tex = static_cast(texture); + MetalTextureR* tex = texture.cast(); @autoreleasepool { [m_enc endEncoding]; @@ -1025,10 +1217,10 @@ struct MetalCommandQueue : IGraphicsCommandQueue } } - MetalTextureR* m_needsDisplay = nullptr; - void resolveDisplay(ITextureR* source) + ObjToken m_needsDisplay; + void resolveDisplay(const ObjToken& source) { - m_needsDisplay = static_cast(source); + m_needsDisplay = source; } bool m_inProgress = false; @@ -1095,19 +1287,20 @@ struct MetalCommandQueue : IGraphicsCommandQueue { w.m_metalLayer.drawableSize = w.m_size; w.m_needsResize = NO; - m_needsDisplay = nullptr; + m_needsDisplay.reset(); return; } } id drawable = [w.m_metalLayer nextDrawable]; if (drawable) { + MetalTextureR* src = m_needsDisplay.cast(); id dest = drawable.texture; - if (m_needsDisplay->m_colorTex.width == dest.width && - m_needsDisplay->m_colorTex.height == dest.height) + if (src->m_colorTex.width == dest.width && + src->m_colorTex.height == dest.height) { id blitEnc = [m_cmdBuf blitCommandEncoder]; - [blitEnc copyFromTexture:m_needsDisplay->m_colorTex + [blitEnc copyFromTexture:src->m_colorTex sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(0, 0, 0) @@ -1120,7 +1313,7 @@ struct MetalCommandQueue : IGraphicsCommandQueue [m_cmdBuf presentDrawable:drawable]; } } - m_needsDisplay = nullptr; + m_needsDisplay.reset(); } m_drawBuf = m_fillBuf; @@ -1134,187 +1327,167 @@ struct MetalCommandQueue : IGraphicsCommandQueue } }; -void MetalGraphicsBufferD::update(int b) -{ - int slot = 1 << b; - if ((slot & m_validSlots) == 0) - { - id res = m_bufs[b]; - memcpy(res.contents, m_cpuBuf.get(), m_sz); - m_validSlots |= slot; - } -} -void MetalGraphicsBufferD::load(const void* data, size_t sz) -{ - size_t bufSz = std::min(sz, m_sz); - memcpy(m_cpuBuf.get(), data, bufSz); - m_validSlots = 0; -} -void* MetalGraphicsBufferD::map(size_t sz) -{ - if (sz > m_sz) - return nullptr; - return m_cpuBuf.get(); -} -void MetalGraphicsBufferD::unmap() -{ - m_validSlots = 0; -} +MetalDataFactory::Context::Context(MetalDataFactory& parent) +: m_parent(parent), m_data(new BaseGraphicsData(static_cast(parent))) {} -void MetalTextureD::update(int b) -{ - int slot = 1 << b; - if ((slot & m_validSlots) == 0) - { - id res = m_texs[b]; - [res replaceRegion:MTLRegionMake2D(0, 0, m_width, m_height) - mipmapLevel:0 withBytes:m_cpuBuf.get() bytesPerRow:m_width*m_pxPitch]; - m_validSlots |= slot; - } -} -void MetalTextureD::load(const void* data, size_t sz) -{ - size_t bufSz = std::min(sz, m_cpuSz); - memcpy(m_cpuBuf.get(), data, bufSz); - m_validSlots = 0; -} -void* MetalTextureD::map(size_t sz) -{ - if (sz > m_cpuSz) - return nullptr; - return m_cpuBuf.get(); -} -void MetalTextureD::unmap() -{ - m_validSlots = 0; -} +MetalDataFactory::Context::~Context() {} -MetalDataFactoryImpl::MetalDataFactoryImpl(IGraphicsContext* parent, MetalContext* ctx, uint32_t sampleCount) -: m_parent(parent), m_ctx(ctx), m_sampleCount(sampleCount) {} - -IGraphicsBufferS* MetalDataFactory::Context::newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count) +ObjToken +MetalDataFactory::Context::newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count) { @autoreleasepool { - MetalData* d = MetalDataFactoryImpl::m_deferredData.get(); MetalDataFactoryImpl& factory = static_cast(m_parent); - MetalGraphicsBufferS* retval = new MetalGraphicsBufferS(d, use, factory.m_ctx, data, stride, count); - d->m_SBufs.emplace_back(retval); - return retval; + return {new MetalGraphicsBufferS(m_data, use, factory.m_ctx, data, stride, count)}; } } -IGraphicsBufferD* MetalDataFactory::Context::newDynamicBuffer(BufferUse use, size_t stride, size_t count) +ObjToken +MetalDataFactory::Context::newDynamicBuffer(BufferUse use, size_t stride, size_t count) { @autoreleasepool { - MetalData* d = MetalDataFactoryImpl::m_deferredData.get(); MetalDataFactoryImpl& factory = static_cast(m_parent); MetalCommandQueue* q = static_cast(factory.m_parent->getCommandQueue()); - MetalGraphicsBufferD* retval = new MetalGraphicsBufferD(d, q, use, factory.m_ctx, stride, count); - d->m_DBufs.emplace_back(retval); - return retval; + return {new MetalGraphicsBufferD(m_data, q, use, factory.m_ctx, stride, count)}; } } -ITextureS* MetalDataFactory::Context::newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, - TextureClampMode clampMode, const void* data, size_t sz) +ObjToken +MetalDataFactory::Context::newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, + TextureClampMode clampMode, const void* data, size_t sz) { @autoreleasepool { - MetalData* d = MetalDataFactoryImpl::m_deferredData.get(); MetalDataFactoryImpl& factory = static_cast(m_parent); - MetalTextureS* retval = new MetalTextureS(d, factory.m_ctx, width, height, mips, fmt, data, sz); - d->m_STexs.emplace_back(retval); - return retval; + return {new MetalTextureS(m_data, factory.m_ctx, width, height, mips, fmt, data, sz)}; } } -ITextureSA* MetalDataFactory::Context::newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips, - TextureFormat fmt, TextureClampMode clampMode, - const void* data, size_t sz) +ObjToken +MetalDataFactory::Context::newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips, + TextureFormat fmt, TextureClampMode clampMode, + const void* data, size_t sz) { @autoreleasepool { - MetalData* d = MetalDataFactoryImpl::m_deferredData.get(); MetalDataFactoryImpl& factory = static_cast(m_parent); - MetalTextureSA* retval = new MetalTextureSA(d, factory.m_ctx, width, height, layers, mips, fmt, data, sz); - d->m_SATexs.emplace_back(retval); - return retval; + return {new MetalTextureSA(m_data, factory.m_ctx, width, height, layers, mips, fmt, data, sz)}; } } -ITextureD* MetalDataFactory::Context::newDynamicTexture(size_t width, size_t height, TextureFormat fmt, - TextureClampMode clampMode) +ObjToken +MetalDataFactory::Context::newDynamicTexture(size_t width, size_t height, TextureFormat fmt, + TextureClampMode clampMode) { @autoreleasepool { - MetalData* d = MetalDataFactoryImpl::m_deferredData.get(); MetalDataFactoryImpl& factory = static_cast(m_parent); MetalCommandQueue* q = static_cast(factory.m_parent->getCommandQueue()); - MetalTextureD* retval = new MetalTextureD(d, q, factory.m_ctx, width, height, fmt); - d->m_DTexs.emplace_back(retval); - return retval; + return {new MetalTextureD(m_data, q, factory.m_ctx, width, height, fmt)}; } } -ITextureR* MetalDataFactory::Context::newRenderTexture(size_t width, size_t height, TextureClampMode clampMode, - size_t colorBindCount, size_t depthBindCount) +ObjToken +MetalDataFactory::Context::newRenderTexture(size_t width, size_t height, TextureClampMode clampMode, + size_t colorBindCount, size_t depthBindCount) { @autoreleasepool { - MetalData* d = MetalDataFactoryImpl::m_deferredData.get(); MetalDataFactoryImpl& factory = static_cast(m_parent); - MetalTextureR* retval = new MetalTextureR(d, factory.m_ctx, width, height, factory.m_sampleCount, - colorBindCount, depthBindCount); - d->m_RTexs.emplace_back(retval); - return retval; + return {new MetalTextureR(m_data, factory.m_ctx, width, height, factory.m_sampleCount, + colorBindCount, depthBindCount)}; } } -IVertexFormat* MetalDataFactory::Context::newVertexFormat(size_t elementCount, const VertexElementDescriptor* elements, - size_t baseVert, size_t baseInst) +ObjToken +MetalDataFactory::Context::newVertexFormat(size_t elementCount, const VertexElementDescriptor* elements, + size_t baseVert, size_t baseInst) { @autoreleasepool { - MetalData* d = MetalDataFactoryImpl::m_deferredData.get(); - MetalVertexFormat* retval = new struct MetalVertexFormat(d, elementCount, elements); - d->m_VFmts.emplace_back(retval); - return retval; + return {new struct MetalVertexFormat(m_data, elementCount, elements)}; } } -IShaderPipeline* MetalDataFactory::Context::newShaderPipeline(const char* vertSource, const char* fragSource, - IVertexFormat* vtxFmt, unsigned targetSamples, - BlendFactor srcFac, BlendFactor dstFac, Primitive prim, - ZTest depthTest, bool depthWrite, bool colorWrite, - bool alphaWrite, CullMode culling) +ObjToken +MetalDataFactory::Context::newShaderPipeline(const char* vertSource, const char* fragSource, + std::vector* vertBlobOut, + std::vector* fragBlobOut, + const ObjToken& vtxFmt, unsigned targetSamples, + BlendFactor srcFac, BlendFactor dstFac, Primitive prim, + ZTest depthTest, bool depthWrite, bool colorWrite, + bool alphaWrite, CullMode culling) { @autoreleasepool { - MetalData* d = MetalDataFactoryImpl::m_deferredData.get(); MetalDataFactoryImpl& factory = static_cast(m_parent); MTLCompileOptions* compOpts = [MTLCompileOptions new]; compOpts.languageVersion = MTLLanguageVersion1_2; NSError* err = nullptr; XXH64_state_t hashState; - uint64_t hashes[2]; + uint64_t srcHashes[2] = {}; + uint64_t binHashes[2] = {}; XXH64_reset(&hashState, 0); - XXH64_update(&hashState, vertSource, strlen(vertSource)); - hashes[0] = XXH64_digest(&hashState); + if (vertSource) + { + XXH64_update(&hashState, vertSource, strlen(vertSource)); + srcHashes[0] = XXH64_digest(&hashState); + auto binSearch = factory.m_sourceToBinary.find(srcHashes[0]); + if (binSearch != factory.m_sourceToBinary.cend()) + binHashes[0] = binSearch->second; + } + else if (vertBlobOut && !vertBlobOut->empty()) + { + XXH64_update(&hashState, vertBlobOut->data(), vertBlobOut->size()); + binHashes[0] = XXH64_digest(&hashState); + } XXH64_reset(&hashState, 0); - XXH64_update(&hashState, fragSource, strlen(fragSource)); - hashes[1] = XXH64_digest(&hashState); + if (fragSource) + { + XXH64_update(&hashState, fragSource, strlen(fragSource)); + srcHashes[1] = XXH64_digest(&hashState); + auto binSearch = factory.m_sourceToBinary.find(srcHashes[1]); + if (binSearch != factory.m_sourceToBinary.cend()) + binHashes[1] = binSearch->second; + } + else if (fragBlobOut && !fragBlobOut->empty()) + { + XXH64_update(&hashState, fragBlobOut->data(), fragBlobOut->size()); + binHashes[1] = XXH64_digest(&hashState); + } + + if (vertBlobOut && vertBlobOut->empty()) + binHashes[0] = factory.CompileLib(*vertBlobOut, vertSource, srcHashes[0]); + + if (fragBlobOut && fragBlobOut->empty()) + binHashes[1] = factory.CompileLib(*fragBlobOut, fragSource, srcHashes[1]); MetalShareableShader::Token vertShader; MetalShareableShader::Token fragShader; - auto vertFind = factory.m_sharedShaders.find(hashes[0]); + auto vertFind = binHashes[0] ? factory.m_sharedShaders.find(binHashes[0]) : + factory.m_sharedShaders.end(); if (vertFind != factory.m_sharedShaders.end()) { vertShader = vertFind->second->lock(); } else { - id vertShaderLib = [factory.m_ctx->m_dev newLibraryWithSource:@(vertSource) - options:compOpts - error:&err]; + id vertShaderLib; + if (vertBlobOut && !vertBlobOut->empty()) + { + if ((*vertBlobOut)[0] == 1) + { + dispatch_data_t vertData = dispatch_data_create(vertBlobOut->data() + 1, vertBlobOut->size() - 1, nullptr, nullptr); + vertShaderLib = [factory.m_ctx->m_dev newLibraryWithData:vertData error:&err]; + if (!vertShaderLib) + Log.report(logvisor::Fatal, "error loading vert library: %s", [[err localizedDescription] UTF8String]); + } + else + { + factory.CompileLib(vertShaderLib, (char*)vertBlobOut->data() + 1, 0, compOpts, &err); + } + } + else + binHashes[0] = factory.CompileLib(vertShaderLib, vertSource, srcHashes[0], compOpts, &err); + if (!vertShaderLib) { printf("%s\n", vertSource); @@ -1323,20 +1496,36 @@ IShaderPipeline* MetalDataFactory::Context::newShaderPipeline(const char* vertSo id vertFunc = [vertShaderLib newFunctionWithName:@"vmain"]; auto it = - factory.m_sharedShaders.emplace(std::make_pair(hashes[0], - std::make_unique(factory, hashes[0], vertFunc))).first; + factory.m_sharedShaders.emplace(std::make_pair(binHashes[0], + std::make_unique(factory, srcHashes[0], binHashes[0], vertFunc))).first; vertShader = it->second->lock(); } - auto fragFind = factory.m_sharedShaders.find(hashes[1]); + auto fragFind = binHashes[1] ? factory.m_sharedShaders.find(binHashes[1]) : + factory.m_sharedShaders.end(); if (fragFind != factory.m_sharedShaders.end()) { fragShader = fragFind->second->lock(); } else { - id fragShaderLib = [factory.m_ctx->m_dev newLibraryWithSource:@(fragSource) - options:compOpts - error:&err]; + id fragShaderLib; + if (fragBlobOut && !fragBlobOut->empty()) + { + if ((*fragBlobOut)[0] == 1) + { + dispatch_data_t fragData = dispatch_data_create(fragBlobOut->data() + 1, fragBlobOut->size() - 1, nullptr, nullptr); + fragShaderLib = [factory.m_ctx->m_dev newLibraryWithData:fragData error:&err]; + if (!fragShaderLib) + Log.report(logvisor::Fatal, "error loading frag library: %s", [[err localizedDescription] UTF8String]); + } + else + { + factory.CompileLib(fragShaderLib, (char*)fragBlobOut->data() + 1, 0, compOpts, &err); + } + } + else + binHashes[1] = factory.CompileLib(fragShaderLib, fragSource, srcHashes[1], compOpts, &err); + if (!fragShaderLib) { printf("%s\n", fragSource); @@ -1345,17 +1534,14 @@ IShaderPipeline* MetalDataFactory::Context::newShaderPipeline(const char* vertSo id fragFunc = [fragShaderLib newFunctionWithName:@"fmain"]; auto it = - factory.m_sharedShaders.emplace(std::make_pair(hashes[1], - std::make_unique(factory, hashes[1], fragFunc))).first; + factory.m_sharedShaders.emplace(std::make_pair(binHashes[1], + std::make_unique(factory, srcHashes[1], binHashes[1], fragFunc))).first; fragShader = it->second->lock(); } - MetalShaderPipeline* retval = new MetalShaderPipeline(d, factory.m_ctx, std::move(vertShader), std::move(fragShader), - static_cast(vtxFmt), targetSamples, - srcFac, dstFac, prim, depthTest, depthWrite, - colorWrite, alphaWrite, culling); - d->m_SPs.emplace_back(retval); - return retval; + return {new MetalShaderPipeline(m_data, factory.m_ctx, std::move(vertShader), std::move(fragShader), + vtxFmt, targetSamples, srcFac, dstFac, prim, depthTest, depthWrite, + colorWrite, alphaWrite, culling)}; } } @@ -1374,43 +1560,25 @@ MetalDataFactory::Context::newShaderDataBinding(const ObjToken& @autoreleasepool { MetalDataFactoryImpl& factory = static_cast(m_parent); - MetalShaderDataBinding* retval = - new MetalShaderDataBinding(MetalDataFactoryImpl::m_deferredData.get(), - factory.m_ctx, pipeline, vbuf, instVbo, ibuf, - ubufCount, ubufs, ubufStages, ubufOffs, - ubufSizes, texCount, texs, texBindIdxs, - depthBind, baseVert, baseInst); - MetalDataFactoryImpl::m_deferredData->m_SBinds.emplace_back(retval); - return retval; + return {new MetalShaderDataBinding(m_data, + factory.m_ctx, pipeline, vbo, instVbo, ibo, + ubufCount, ubufs, ubufStages, ubufOffs, + ubufSizes, texCount, texs, texBindIdxs, + depthBind, baseVert, baseInst)}; } } void MetalDataFactoryImpl::commitTransaction(const FactoryCommitFunc& trans) { - if (m_deferredData.get()) - Log.report(logvisor::Fatal, "nested commitTransaction usage detected"); - m_deferredData.reset(new MetalData()); - MetalDataFactory::Context ctx(*this); - if (!trans(ctx)) - { - delete m_deferredData.get(); - m_deferredData.reset(); - return GraphicsDataToken(this, nullptr); - } - - std::unique_lock lk(m_committedMutex); - MetalData* retval = m_deferredData.get(); - m_deferredData.reset(); - m_committedData.insert(retval); - return GraphicsDataToken(this, retval); + trans(ctx); } ObjToken MetalDataFactoryImpl::newPoolBuffer(BufferUse use, size_t stride, size_t count) { - ObjToken pool(new BaseGraphicsPool(*this)); + ObjToken pool(new BaseGraphicsPool(*this)); MetalCommandQueue* q = static_cast(m_parent->getCommandQueue()); - return {MetalGraphicsBufferD(pool, q, use, m_ctx, stride, count)}; + return {new MetalGraphicsBufferD(pool, q, use, m_ctx, stride, count)}; } IGraphicsCommandQueue* _NewMetalCommandQueue(MetalContext* ctx, IWindow* parentWindow, diff --git a/lib/mac/WindowCocoa.mm b/lib/mac/WindowCocoa.mm index 1e96375..4de567b 100644 --- a/lib/mac/WindowCocoa.mm +++ b/lib/mac/WindowCocoa.mm @@ -215,7 +215,6 @@ public: ~GraphicsContextCocoaGL() { m_commandQueue->stopRenderer(); - m_dataFactory->destroyAllData(); delete m_commandQueue; delete m_dataFactory; printf("CONTEXT DESTROYED\n"); @@ -374,7 +373,6 @@ public: ~GraphicsContextCocoaMetal() { m_commandQueue->stopRenderer(); - m_dataFactory->destroyAllData(); delete m_commandQueue; delete m_dataFactory; m_metalCtx->m_windows.erase(m_parentWindow); diff --git a/test/main.cpp b/test/main.cpp index 24cd0be..8e75c87 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -157,7 +157,7 @@ struct CTestWindowCallback : IWindowCallback bool m_rectDirty = false; bool m_windowInvalid = false; - void resized(const SWindowRect& rect) + void resized(const SWindowRect& rect, bool sync) { m_lastRect = rect; m_rectDirty = true; @@ -247,8 +247,8 @@ struct TestApplicationCallback : IApplicationCallback CTestWindowCallback windowCallback; bool running = true; - IShaderDataBinding* m_binding = nullptr; - ITextureR* m_renderTarget = nullptr; + boo::ObjToken m_binding; + boo::ObjToken m_renderTarget; std::mutex m_mt; std::condition_variable m_cv; @@ -256,13 +256,13 @@ struct TestApplicationCallback : IApplicationCallback std::mutex m_initmt; std::condition_variable m_initcv; - static GraphicsDataToken LoaderProc(TestApplicationCallback* self) + static void LoaderProc(TestApplicationCallback* self) { std::unique_lock lk(self->m_initmt); IGraphicsDataFactory* factory = self->mainWindow->getLoadContextDataFactory(); - GraphicsDataToken data = factory->commitTransaction([&](IGraphicsDataFactory::Context& ctx) -> bool + factory->commitTransaction([&](IGraphicsDataFactory::Context& ctx) -> bool { /* Create render target */ int x, y, w, h; @@ -282,16 +282,15 @@ struct TestApplicationCallback : IApplicationCallback {{0.5,-0.5},{1.0,0.0}}, {{-0.5,-0.5},{0.0,0.0}} }; - IGraphicsBuffer* vbo = - ctx.newStaticBuffer(BufferUse::Vertex, quad, sizeof(Vert), 4); + auto vbo = ctx.newStaticBuffer(BufferUse::Vertex, quad, sizeof(Vert), 4); /* Make vertex format */ VertexElementDescriptor descs[2] = { - {vbo, nullptr, VertexSemantic::Position3}, - {vbo, nullptr, VertexSemantic::UV2} + {vbo.get(), nullptr, VertexSemantic::Position3}, + {vbo.get(), nullptr, VertexSemantic::UV2} }; - IVertexFormat* vfmt = ctx.newVertexFormat(2, descs); + auto vfmt = ctx.newVertexFormat(2, descs); /* Make ramp texture */ using Pixel = uint8_t[4]; @@ -304,11 +303,11 @@ struct TestApplicationCallback : IApplicationCallback tex[i][j][2] = 0; tex[i][j][3] = 0xff; } - ITexture* texture = - ctx.newStaticTexture(256, 256, 1, TextureFormat::RGBA8, boo::TextureClampMode::Repeat, tex, 256*256*4); + boo::ObjToken texture = ctx.newStaticTexture(256, 256, 1, TextureFormat::RGBA8, + boo::TextureClampMode::Repeat, tex, 256*256*4).get(); /* Make shader pipeline */ - IShaderPipeline* pipeline = nullptr; + boo::ObjToken pipeline; auto plat = ctx.platform(); if (plat == IGraphicsDataFactory::Platform::OpenGL) { @@ -437,7 +436,7 @@ struct TestApplicationCallback : IApplicationCallback " return tex.sample(samp, d.out_uv);\n" "}\n"; - pipeline = metalF.newShaderPipeline(VS, FS, vfmt, 1, + pipeline = metalF.newShaderPipeline(VS, FS, nullptr, nullptr, vfmt, 1, BlendFactor::One, BlendFactor::Zero, Primitive::TriStrips, boo::ZTest::LEqual, true, true, true, boo::CullMode::None); } @@ -445,7 +444,7 @@ struct TestApplicationCallback : IApplicationCallback /* Make shader data binding */ self->m_binding = - ctx.newShaderDataBinding(pipeline, vfmt, vbo, nullptr, nullptr, 0, nullptr, nullptr, + ctx.newShaderDataBinding(pipeline, vfmt, vbo.get(), nullptr, nullptr, 0, nullptr, nullptr, 1, &texture, nullptr, nullptr); return true; @@ -463,7 +462,6 @@ struct TestApplicationCallback : IApplicationCallback if (!self->running) break; } - return data; } int appMain(IApplication* app)