diff --git a/specter/CMakeLists.txt b/specter/CMakeLists.txt index 8f4a11178..be652b9af 100644 --- a/specter/CMakeLists.txt +++ b/specter/CMakeLists.txt @@ -60,9 +60,7 @@ list(APPEND SPECTER_HEADERS include/specter/FileBrowser.hpp include/specter/Icon.hpp include/specter/FontCache.hpp - include/specter/Translator.hpp - include/specter/UniformBufferPool.hpp - include/specter/VertexBufferPool.hpp) + include/specter/Translator.hpp) atdna(atdna_FontCache.cpp include/specter/FontCache.hpp) diff --git a/specter/include/specter/ModalWindow.hpp b/specter/include/specter/ModalWindow.hpp index 4c1040b66..e0a51342d 100644 --- a/specter/include/specter/ModalWindow.hpp +++ b/specter/include/specter/ModalWindow.hpp @@ -36,7 +36,7 @@ private: zeus::CColor m_line2Clear; ViewBlock m_viewBlock; - std::experimental::optional::Token> m_viewBlockBuf; + std::experimental::optional::Token> m_viewBlockBuf; union { struct diff --git a/specter/include/specter/RootView.hpp b/specter/include/specter/RootView.hpp index 7de51d443..20bc4d38e 100644 --- a/specter/include/specter/RootView.hpp +++ b/specter/include/specter/RootView.hpp @@ -56,7 +56,7 @@ class RootView : public View VertexBufferBindingSolid m_vertsBinding; ViewBlock m_viewBlock; - std::experimental::optional::Token> m_viewVertBlockBuf; + std::experimental::optional::Token> m_viewVertBlockBuf; SolidShaderVert m_verts[32]; void setArrowVerts(const boo::SWindowRect& rect, SplitView::ArrowDir dir); void setLineVerts(const boo::SWindowRect& rect, float split, SplitView::Axis axis); diff --git a/specter/include/specter/SplitView.hpp b/specter/include/specter/SplitView.hpp index a22fa085c..f7d97f8a6 100644 --- a/specter/include/specter/SplitView.hpp +++ b/specter/include/specter/SplitView.hpp @@ -43,7 +43,7 @@ private: ViewChild m_views[2]; ViewBlock m_splitBlock; - std::experimental::optional::Token> m_splitBlockBuf; + std::experimental::optional::Token> m_splitBlockBuf; TexShaderVert m_splitVerts[4]; int m_clearanceA, m_clearanceB; diff --git a/specter/include/specter/TextView.hpp b/specter/include/specter/TextView.hpp index 3af867144..b46605cb6 100644 --- a/specter/include/specter/TextView.hpp +++ b/specter/include/specter/TextView.hpp @@ -44,7 +44,7 @@ public: private: size_t m_capacity; - std::experimental::optional::Token> m_glyphBuf; + std::experimental::optional::Token> m_glyphBuf; boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */ boo::IShaderDataBinding* m_shaderBinding; const FontAtlas& m_fontAtlas; @@ -63,7 +63,7 @@ public: friend class TextView; friend class MultiLineTextView; - VertexBufferPool m_glyphPool; + hecl::VertexBufferPool m_glyphPool; void updateBuffers() { diff --git a/specter/include/specter/Toolbar.hpp b/specter/include/specter/Toolbar.hpp index 52ed2e465..51bc27461 100644 --- a/specter/include/specter/Toolbar.hpp +++ b/specter/include/specter/Toolbar.hpp @@ -31,7 +31,7 @@ private: std::vector>> m_children; ViewBlock m_tbBlock; - std::experimental::optional::Token> m_tbBlockBuf; + std::experimental::optional::Token> m_tbBlockBuf; TexShaderVert m_tbVerts[10]; int m_nomGauge = 25; int m_padding = 10; diff --git a/specter/include/specter/Tooltip.hpp b/specter/include/specter/Tooltip.hpp index b4e19f5fb..44ac98bc4 100644 --- a/specter/include/specter/Tooltip.hpp +++ b/specter/include/specter/Tooltip.hpp @@ -10,7 +10,7 @@ namespace specter class Tooltip : public View { ViewBlock m_ttBlock; - std::experimental::optional::Token> m_ttBlockBuf; + std::experimental::optional::Token> m_ttBlockBuf; SolidShaderVert m_ttVerts[16]; int m_nomWidth = 25; int m_nomHeight = 25; diff --git a/specter/include/specter/UniformBufferPool.hpp b/specter/include/specter/UniformBufferPool.hpp deleted file mode 100644 index b375f191c..000000000 --- a/specter/include/specter/UniformBufferPool.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef SPECTER_UNIFORMBUFFERPOOL_HPP -#define SPECTER_UNIFORMBUFFERPOOL_HPP - -#include -#include -#include -#include - -namespace specter -{ - -#define SPECTER_UBUFPOOL_ALLOCATION_BLOCK 262144 - -/** This class provides a uniform structure for packing instanced uniform-buffer - * data with consistent stride into a vector of 256K 'Buckets'. - * - * This results in a space-efficient way of managing GPU data of things like UI - * widgets. These can potentially have numerous binding instances, so this avoids - * allocating a full GPU buffer object for each. */ -template -class UniformBufferPool -{ - /* Resolve div_t type using ssize_t as basis */ -#if _WIN32 - using IndexTp = SSIZE_T; -#else - using IndexTp = ssize_t; -#endif - struct InvalidTp {}; - using DivTp = std::conditional_t::value, std::lldiv_t, - std::conditional_t::value, std::ldiv_t, - std::conditional_t::value, std::div_t, InvalidTp>>>; - static_assert(!std::is_same::value, "unsupported IndexTp for DivTp resolution"); - - /** Size of single element, rounded up to 256-multiple */ - static constexpr IndexTp m_stride = ROUND_UP_256(sizeof(UniformStruct)); - static_assert(m_stride <= SPECTER_UBUFPOOL_ALLOCATION_BLOCK, "Stride too large for uniform pool"); - - /** Number of rounded elements per 256K bucket */ - static constexpr IndexTp m_countPerBucket = SPECTER_UBUFPOOL_ALLOCATION_BLOCK / m_stride; - - /** Buffer size per bucket (ideally 256K) */ - static constexpr IndexTp m_sizePerBucket = m_stride * m_countPerBucket; - - /** BitVector indicating free allocation blocks */ - hecl::llvm::BitVector m_freeBlocks; - - /** Efficient way to get bucket and block simultaneously */ - DivTp getBucketDiv(IndexTp idx) const { return std::div(idx, m_countPerBucket); } - - /** Buffer pool token */ - boo::GraphicsBufferPoolToken m_token; - - /** Private bucket info */ - struct Bucket - { - boo::IGraphicsBufferD* buffer; - uint8_t* cpuBuffer = nullptr; - size_t useCount = 0; - bool dirty = false; - Bucket() = default; - Bucket(const Bucket& other) = delete; - Bucket& operator=(const Bucket& other) = delete; - Bucket(Bucket&& other) = default; - Bucket& operator=(Bucket&& other) = default; - - void updateBuffer() - { - if (buffer) - buffer->unmap(); - cpuBuffer = nullptr; - dirty = false; - } - - void increment(UniformBufferPool& pool) - { - if (!useCount) - buffer = pool.m_token.newPoolBuffer(boo::BufferUse::Uniform, - pool.m_stride, pool.m_countPerBucket); - ++useCount; - } - - void decrement(UniformBufferPool& pool) - { - --useCount; - if (!useCount) - { - pool.m_token.deletePoolBuffer(buffer); - buffer = nullptr; - } - } - }; - std::vector m_buckets; - -public: - /** User block-owning token */ - class Token - { - friend class UniformBufferPool; - UniformBufferPool& m_pool; - IndexTp m_index; - DivTp m_div; - Token(UniformBufferPool& pool) - : m_pool(pool) - { - auto& freeSpaces = pool.m_freeBlocks; - auto& buckets = pool.m_buckets; - int idx = freeSpaces.find_first(); - if (idx == -1) - { - buckets.emplace_back(); - m_index = freeSpaces.size(); - freeSpaces.resize(freeSpaces.size() + pool.m_countPerBucket, true); - } - else - { - m_index = idx; - } - freeSpaces.reset(m_index); - m_div = pool.getBucketDiv(m_index); - - Bucket& bucket = m_pool.m_buckets[m_div.quot]; - bucket.increment(m_pool); - } - - public: - Token(const Token& other) = delete; - Token& operator=(const Token& other) = delete; - Token& operator=(Token&& other) = delete; - Token(Token&& other) - : m_pool(other.m_pool), m_index(other.m_index), - m_div(other.m_div) - { - other.m_index = -1; - } - - ~Token() - { - if (m_index != -1) - { - m_pool.m_freeBlocks.set(m_index); - Bucket& bucket = m_pool.m_buckets[m_div.quot]; - bucket.decrement(m_pool); - } - } - - UniformStruct& access() - { - Bucket& bucket = m_pool.m_buckets[m_div.quot]; - if (!bucket.cpuBuffer) - bucket.cpuBuffer = reinterpret_cast(bucket.buffer->map(m_sizePerBucket)); - bucket.dirty = true; - return reinterpret_cast(bucket.cpuBuffer[m_div.rem * m_pool.m_stride]); - } - - std::pair getBufferInfo() const - { - Bucket& bucket = m_pool.m_buckets[m_div.quot]; - return {bucket.buffer, m_div.rem * m_pool.m_stride}; - } - }; - - UniformBufferPool() = default; - UniformBufferPool(const UniformBufferPool& other) = delete; - UniformBufferPool& operator=(const UniformBufferPool& other) = delete; - - /** Load dirty buffer data into GPU */ - void updateBuffers() - { - for (Bucket& bucket : m_buckets) - if (bucket.dirty) - bucket.updateBuffer(); - } - - /** Allocate free block into client-owned Token */ - Token allocateBlock(boo::IGraphicsDataFactory* factory) - { - if (!m_token) - m_token = factory->newBufferPool(); - return Token(*this); - } -}; - -} - -#endif // SPECTER_UNIFORMBUFFERPOOL_HPP diff --git a/specter/include/specter/VertexBufferPool.hpp b/specter/include/specter/VertexBufferPool.hpp deleted file mode 100644 index 4f079fbc7..000000000 --- a/specter/include/specter/VertexBufferPool.hpp +++ /dev/null @@ -1,189 +0,0 @@ -#ifndef SPECTER_VERTEXBUFFERPOOL_HPP -#define SPECTER_VERTEXBUFFERPOOL_HPP - -#include -#include -#include -#include - -namespace specter -{ - -#define SPECTER_VBUFPOOL_ALLOCATION_BLOCK 262144 - -/** This class provides a uniform structure for packing instanced vertex-buffer - * data with consistent stride into a vector of 256K 'Buckets'. - * - * This results in a space-efficient way of managing GPU data of things like UI - * widgets. These can potentially have numerous binding instances, so this avoids - * allocating a full GPU buffer object for each. */ -template -class VertexBufferPool -{ - /* Resolve div_t type using ssize_t as basis */ -#if _WIN32 - using IndexTp = SSIZE_T; -#else - using IndexTp = ssize_t; -#endif - struct InvalidTp {}; - using DivTp = std::conditional_t::value, std::lldiv_t, - std::conditional_t::value, std::ldiv_t, - std::conditional_t::value, std::div_t, InvalidTp>>>; - static_assert(!std::is_same::value, "unsupported IndexTp for DivTp resolution"); - - /** Size of single element */ - static constexpr IndexTp m_stride = sizeof(VertStruct); - static_assert(m_stride <= SPECTER_VBUFPOOL_ALLOCATION_BLOCK, "Stride too large for vertex pool"); - - /** Number of elements per 256K bucket */ - static constexpr IndexTp m_countPerBucket = SPECTER_VBUFPOOL_ALLOCATION_BLOCK / m_stride; - - /** Buffer size per bucket (ideally 256K) */ - static constexpr IndexTp m_sizePerBucket = m_stride * m_countPerBucket; - - /** BitVector indicating free allocation elements */ - hecl::llvm::BitVector m_freeElements; - - /** Efficient way to get bucket and element simultaneously */ - DivTp getBucketDiv(IndexTp idx) const { return std::div(idx, m_countPerBucket); } - - /** Buffer pool token */ - boo::GraphicsBufferPoolToken m_token; - - /** Private bucket info */ - struct Bucket - { - boo::IGraphicsBufferD* buffer; - uint8_t* cpuBuffer = nullptr; - size_t useCount = 0; - bool dirty = false; - Bucket() = default; - Bucket(const Bucket& other) = delete; - Bucket& operator=(const Bucket& other) = delete; - Bucket(Bucket&& other) = default; - Bucket& operator=(Bucket&& other) = default; - - void updateBuffer() - { - if (buffer) - buffer->unmap(); - cpuBuffer = nullptr; - dirty = false; - } - - void increment(VertexBufferPool& pool) - { - if (!useCount) - buffer = pool.m_token.newPoolBuffer(boo::BufferUse::Vertex, - pool.m_stride, pool.m_countPerBucket); - ++useCount; - } - - void decrement(VertexBufferPool& pool) - { - --useCount; - if (!useCount) - { - pool.m_token.deletePoolBuffer(buffer); - buffer = nullptr; - } - } - }; - std::vector m_buckets; - -public: - /** User element-owning token */ - class Token - { - friend class VertexBufferPool; - VertexBufferPool& m_pool; - IndexTp m_index; - IndexTp m_count; - DivTp m_div; - Token(VertexBufferPool& pool, IndexTp count) - : m_pool(pool), m_count(count) - { - assert(count <= pool.m_countPerBucket && "unable to fit in bucket"); - auto& freeSpaces = pool.m_freeElements; - auto& buckets = pool.m_buckets; - int idx = freeSpaces.find_first_contiguous(count, pool.m_countPerBucket); - if (idx == -1) - { - buckets.emplace_back(); - m_index = freeSpaces.size(); - freeSpaces.resize(freeSpaces.size() + pool.m_countPerBucket, true); - } - else - { - m_index = idx; - } - freeSpaces.reset(m_index, m_index + count); - m_div = pool.getBucketDiv(m_index); - - Bucket& bucket = m_pool.m_buckets[m_div.quot]; - bucket.increment(pool); - } - public: - Token(const Token& other) = delete; - Token& operator=(const Token& other) = delete; - Token& operator=(Token&& other) = delete; - Token(Token&& other) - : m_pool(other.m_pool), m_index(other.m_index), - m_count(other.m_count), m_div(other.m_div) - { - other.m_index = -1; - } - - ~Token() - { - if (m_index != -1) - { - m_pool.m_freeElements.set(m_index, m_index + m_count); - Bucket& bucket = m_pool.m_buckets[m_div.quot]; - bucket.decrement(m_pool); - } - } - - VertStruct* access() - { - Bucket& bucket = m_pool.m_buckets[m_div.quot]; - if (!bucket.cpuBuffer) - bucket.cpuBuffer = reinterpret_cast(bucket.buffer->map(m_sizePerBucket)); - bucket.dirty = true; - return reinterpret_cast(&bucket.cpuBuffer[m_div.rem * m_pool.m_stride]); - } - - std::pair getBufferInfo() const - { - Bucket& bucket = m_pool.m_buckets[m_div.quot]; - return {bucket.buffer, m_div.rem}; - } - }; - - VertexBufferPool() = default; - VertexBufferPool(const VertexBufferPool& other) = delete; - VertexBufferPool& operator=(const VertexBufferPool& other) = delete; - - /** Load dirty buffer data into GPU */ - void updateBuffers() - { - for (Bucket& bucket : m_buckets) - if (bucket.dirty) - bucket.updateBuffer(); - } - - /** Allocate free block into client-owned Token */ - Token allocateBlock(boo::IGraphicsDataFactory* factory, IndexTp count) - { - if (!m_token) - m_token = factory->newBufferPool(); - return Token(*this, count); - } - - static constexpr IndexTp bucketCapacity() { return m_countPerBucket; } -}; - -} - -#endif // SPECTER_VERTEXBUFFERPOOL_HPP diff --git a/specter/include/specter/View.hpp b/specter/include/specter/View.hpp index e8251bc88..f3bb91aa6 100644 --- a/specter/include/specter/View.hpp +++ b/specter/include/specter/View.hpp @@ -8,8 +8,8 @@ #include "zeus/CTransform.hpp" #include "zeus/CColor.hpp" #include "hecl/CVar.hpp" -#include "UniformBufferPool.hpp" -#include "VertexBufferPool.hpp" +#include "hecl/UniformBufferPool.hpp" +#include "hecl/VertexBufferPool.hpp" #include #include @@ -110,7 +110,7 @@ public: template struct VertexBufferBinding { - std::experimental::optional::Token> m_vertsBuf; + std::experimental::optional::Token> m_vertsBuf; boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */ boo::IShaderDataBinding* m_shaderBinding = nullptr; @@ -143,13 +143,13 @@ public: { void init(boo::IGraphicsDataFactory::Context& ctx, ViewResources& res, size_t count, - const UniformBufferPool::Token& viewBlockBuf); + const hecl::UniformBufferPool::Token& viewBlockBuf); }; struct VertexBufferBindingTex : VertexBufferBinding { void init(boo::IGraphicsDataFactory::Context& ctx, ViewResources& res, size_t count, - const UniformBufferPool::Token& viewBlockBuf, + const hecl::UniformBufferPool::Token& viewBlockBuf, boo::ITexture* texture); }; @@ -184,14 +184,14 @@ protected: " float4x4 mv;\n"\ " float4 mulColor;\n"\ "};\n" - std::experimental::optional::Token> m_viewVertBlockBuf; + std::experimental::optional::Token> m_viewVertBlockBuf; public: struct Resources { - UniformBufferPool m_bufPool; - VertexBufferPool m_solidPool; - VertexBufferPool m_texPool; + hecl::UniformBufferPool m_bufPool; + hecl::VertexBufferPool m_solidPool; + hecl::VertexBufferPool m_texPool; void updateBuffers() { diff --git a/specter/lib/TextView.cpp b/specter/lib/TextView.cpp index 721afe796..501312465 100644 --- a/specter/lib/TextView.cpp +++ b/specter/lib/TextView.cpp @@ -365,9 +365,9 @@ TextView::TextView(ViewResources& res, m_fontAtlas(font), m_align(align) { - if (size_t(VertexBufferPool::bucketCapacity()) < capacity) + if (size_t(hecl::VertexBufferPool::bucketCapacity()) < capacity) Log.report(logvisor::Fatal, "bucket overflow [%" PRISize "/%" PRISize "]", - capacity, VertexBufferPool::bucketCapacity()); + capacity, hecl::VertexBufferPool::bucketCapacity()); _commitResources(0); } @@ -408,7 +408,7 @@ int TextView::DoKern(FT_Pos val, const FontAtlas& atlas) void TextView::typesetGlyphs(const std::string& str, const zeus::CColor& defaultColor) { UTF8Iterator it(str.begin()); - size_t charLen = str.size() ? std::min(it.countTo(str.end()), size_t(1024)) : 0; + size_t charLen = str.size() ? std::min(it.countTo(str.end()), m_capacity) : 0; _commitResources(charLen); uint32_t lCh = -1; @@ -477,13 +477,14 @@ void TextView::typesetGlyphs(const std::string& str, const zeus::CColor& default void TextView::typesetGlyphs(const std::wstring& str, const zeus::CColor& defaultColor) { - _commitResources(str.size()); + size_t charLen = std::min(str.size(), m_capacity); + _commitResources(charLen); uint32_t lCh = -1; m_glyphs.clear(); - m_glyphs.reserve(str.size()); + m_glyphs.reserve(charLen); m_glyphInfo.clear(); - m_glyphInfo.reserve(str.size()); + m_glyphInfo.reserve(charLen); int adv = 0; for (wchar_t ch : str) diff --git a/specter/lib/View.cpp b/specter/lib/View.cpp index 639259509..485f70b89 100644 --- a/specter/lib/View.cpp +++ b/specter/lib/View.cpp @@ -387,7 +387,7 @@ void View::commitResources(ViewResources& res, const boo::FactoryCommitFunc& com void View::VertexBufferBindingSolid::init(boo::IGraphicsDataFactory::Context& ctx, ViewResources& res, size_t count, - const UniformBufferPool::Token& viewBlockBuf) + const hecl::UniformBufferPool::Token& viewBlockBuf) { m_vertsBuf.emplace(res.m_viewRes.m_solidPool.allocateBlock(res.m_factory, count)); auto vBufInfo = m_vertsBuf->getBufferInfo(); @@ -422,7 +422,7 @@ void View::VertexBufferBindingSolid::init(boo::IGraphicsDataFactory::Context& ct void View::VertexBufferBindingTex::init(boo::IGraphicsDataFactory::Context& ctx, ViewResources& res, size_t count, - const UniformBufferPool::Token& viewBlockBuf, + const hecl::UniformBufferPool::Token& viewBlockBuf, boo::ITexture* texture) { m_vertsBuf.emplace(res.m_viewRes.m_texPool.allocateBlock(res.m_factory, count));