#ifndef BOO_GRAPHICSDEV_COMMON_HPP #define BOO_GRAPHICSDEV_COMMON_HPP /* Private header for managing shader data * binding lifetimes through rendering cycle */ #include #include #include #include "boo/graphicsdev/IGraphicsDataFactory.hpp" namespace boo { struct BaseGraphicsData; struct BaseGraphicsPool; template struct GraphicsDataNode; /** Inherited by data factory implementations to track the head data and pool nodes */ struct GraphicsDataFactoryHead { std::mutex m_dataMutex; BaseGraphicsData* m_dataHead = nullptr; 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 */ struct BaseGraphicsData : IObj { GraphicsDataFactoryHead& m_head; BaseGraphicsData* m_next; BaseGraphicsData* m_prev = nullptr; GraphicsDataNode* m_SPs = nullptr; GraphicsDataNode* m_SBinds = nullptr; GraphicsDataNode* m_SBufs = nullptr; GraphicsDataNode* m_DBufs = nullptr; GraphicsDataNode* m_STexs = nullptr; GraphicsDataNode* m_SATexs = nullptr; GraphicsDataNode* m_DTexs = nullptr; GraphicsDataNode* m_RTexs = nullptr; GraphicsDataNode* m_VFmts = nullptr; template GraphicsDataNode*& getHead(); template size_t countForward() { auto* head = getHead(); return head ? head->countForward() : 0; } explicit BaseGraphicsData(GraphicsDataFactoryHead& head) : m_head(head) { 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() { std::lock_guard lk(m_head.m_dataMutex); if (m_prev) { if (m_next) m_next->m_prev = m_prev; m_prev->m_next = m_next; } else { if (m_next) m_next->m_prev = nullptr; m_head.m_dataHead = m_next; } } using iterator = DataIterator; iterator begin() { return iterator(this); } iterator end() { return iterator(nullptr); } }; template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_SPs; } template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_SBinds; } template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_SBufs; } template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_DBufs; } template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_STexs; } template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_SATexs; } template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_DTexs; } template <> inline GraphicsDataNode*& BaseGraphicsData::getHead() { return m_RTexs; } 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 BaseGraphicsData */ struct BaseGraphicsPool : IObj { GraphicsDataFactoryHead& m_head; BaseGraphicsPool* m_next; BaseGraphicsPool* m_prev = nullptr; GraphicsDataNode* m_DBufs = nullptr; template GraphicsDataNode*& getHead(); template size_t countForward() { auto* head = getHead(); return head ? head->countForward() : 0; } explicit BaseGraphicsPool(GraphicsDataFactoryHead& head) : m_head(head) { 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() { std::lock_guard lk(m_head.m_dataMutex); if (m_prev) { if (m_next) m_next->m_prev = m_prev; m_prev->m_next = m_next; } else { if (m_next) m_next->m_prev = nullptr; m_head.m_poolHead = m_next; } } using iterator = DataIterator; iterator begin() { return iterator(this); } iterator end() { return iterator(nullptr); } }; template <> inline GraphicsDataNode*& BaseGraphicsPool::getHead() { return m_DBufs; } /** Private generalised graphics object node. * Keeps a strong reference to the data pool that it's a member of; * as well as doubly-linked pointers to same-type sibling objects */ template struct GraphicsDataNode : NodeCls { ObjToken m_data; GraphicsDataNode* m_next; GraphicsDataNode* m_prev = nullptr; explicit GraphicsDataNode(const ObjToken& data) : m_data(data) { 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() { std::lock_guard lk(m_data->m_head.m_dataMutex); if (m_prev) { if (m_next) m_next->m_prev = m_prev; m_prev->m_next = m_next; } else { if (m_next) m_next->m_prev = nullptr; m_data->template getHead() = m_next; } } class iterator { GraphicsDataNode* m_node; public: using value_type = NodeCls; using pointer = NodeCls*; using reference = NodeCls&; using iterator_category = std::bidirectional_iterator_tag; explicit iterator(GraphicsDataNode* node) : m_node(node) {} NodeCls& 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; } }; iterator begin() { return iterator(this); } iterator end() { return iterator(nullptr); } size_t countForward() { size_t ret = 0; for (auto& n : *this) ++ret; return ret; } }; /** Hash table entry for owning sharable shader objects */ template class IShareableShader { std::atomic_int m_refCount = {0}; FactoryImpl& m_factory; uint64_t m_srckey, m_binKey; public: IShareableShader(FactoryImpl& factory, uint64_t srcKey, uint64_t binKey) : m_factory(factory), m_srckey(srcKey), m_binKey(binKey) {} void increment() { m_refCount++; } void decrement() { if (m_refCount.fetch_sub(1) == 1) m_factory._unregisterShareableShader(m_srckey, m_binKey); } class Token { IShareableShader* m_parent = nullptr; public: Token() = default; Token(IShareableShader* p) : m_parent(p) { m_parent->increment(); } Token& operator=(const Token&) = delete; Token(const Token&) = delete; Token& operator=(Token&& other) { m_parent = other.m_parent; other.m_parent = nullptr; return *this; } Token(Token&& other) { m_parent = other.m_parent; other.m_parent = nullptr; } void reset() { if (m_parent) m_parent->decrement(); m_parent = nullptr; } ~Token() { if (m_parent) m_parent->decrement(); } operator bool() const { return m_parent != nullptr; } ShaderImpl& get() const { return static_cast(*m_parent); } }; Token lock() { return Token(this); } }; } #endif // BOO_GRAPHICSDEV_COMMON_HPP