diff --git a/specter/include/specter/TextView.hpp b/specter/include/specter/TextView.hpp index 6a7c74bc6..3af867144 100644 --- a/specter/include/specter/TextView.hpp +++ b/specter/include/specter/TextView.hpp @@ -54,6 +54,8 @@ private: friend class MultiLineTextView; static int DoKern(FT_Pos val, const FontAtlas& atlas); + void _commitResources(size_t capacity); + public: class Resources { diff --git a/specter/include/specter/UniformBufferPool.hpp b/specter/include/specter/UniformBufferPool.hpp index 7d63f2f89..689e27c42 100644 --- a/specter/include/specter/UniformBufferPool.hpp +++ b/specter/include/specter/UniformBufferPool.hpp @@ -56,21 +56,38 @@ class UniformBufferPool { 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; - Bucket(UniformBufferPool& pool) - { - buffer = pool.m_token.newPoolBuffer(boo::BufferUse::Uniform, pool.m_stride, pool.m_countPerBucket); - } + void updateBuffer() { 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; @@ -90,7 +107,7 @@ public: int idx = freeSpaces.find_first(); if (idx == -1) { - buckets.emplace_back(pool); + buckets.emplace_back(); m_index = freeSpaces.size(); freeSpaces.resize(freeSpaces.size() + pool.m_countPerBucket, true); } @@ -100,6 +117,9 @@ public: } freeSpaces.reset(m_index); m_div = pool.getBucketDiv(m_index); + + Bucket& bucket = m_pool.m_buckets[m_div.quot]; + bucket.increment(m_pool); } public: @@ -116,7 +136,11 @@ public: ~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() diff --git a/specter/include/specter/VertexBufferPool.hpp b/specter/include/specter/VertexBufferPool.hpp index befa3cbf1..b0a07c34e 100644 --- a/specter/include/specter/VertexBufferPool.hpp +++ b/specter/include/specter/VertexBufferPool.hpp @@ -56,21 +56,38 @@ class VertexBufferPool { 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; - Bucket(VertexBufferPool& pool) - { - buffer = pool.m_token.newPoolBuffer(boo::BufferUse::Vertex, pool.m_stride, pool.m_countPerBucket); - } + void updateBuffer() { 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; @@ -92,7 +109,7 @@ public: int idx = freeSpaces.find_first_contiguous(count); if (idx == -1) { - buckets.emplace_back(pool); + buckets.emplace_back(); m_index = freeSpaces.size(); freeSpaces.resize(freeSpaces.size() + pool.m_countPerBucket, true); } @@ -102,6 +119,9 @@ public: } 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; @@ -117,7 +137,11 @@ public: ~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() diff --git a/specter/include/specter/View.hpp b/specter/include/specter/View.hpp index 387a3b831..e8251bc88 100644 --- a/specter/include/specter/View.hpp +++ b/specter/include/specter/View.hpp @@ -221,6 +221,7 @@ public: protected: View(ViewResources& res, View& parentView); void buildResources(boo::IGraphicsDataFactory::Context& ctx, ViewResources& res); + void _commitResources(ViewResources& res, const boo::FactoryCommitFunc& commitFunc); void commitResources(ViewResources& res, const boo::FactoryCommitFunc& commitFunc); public: diff --git a/specter/lib/TextView.cpp b/specter/lib/TextView.cpp index c42e1889c..721afe796 100644 --- a/specter/lib/TextView.cpp +++ b/specter/lib/TextView.cpp @@ -1,3 +1,4 @@ +#include "specter/RootView.hpp" #include "specter/TextView.hpp" #include "specter/ViewResources.hpp" #include "utf8proc.h" @@ -296,6 +297,66 @@ void TextView::Resources::init(boo::VulkanDataFactory::Context& ctx, FontCache* #endif +void TextView::_commitResources(size_t capacity) +{ + auto& res = rootView().viewRes(); + View::_commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool + { + buildResources(ctx, res); + + if (capacity) + { + m_glyphBuf.emplace(res.m_textRes.m_glyphPool.allocateBlock(res.m_factory, capacity)); + + boo::IShaderPipeline* shader; + if (m_fontAtlas.subpixel()) + shader = res.m_textRes.m_subpixel; + else + shader = res.m_textRes.m_regular; + + auto vBufInfo = m_glyphBuf->getBufferInfo(); + auto uBufInfo = m_viewVertBlockBuf->getBufferInfo(); + boo::IGraphicsBuffer* uBufs[] = {uBufInfo.first}; + size_t uBufOffs[] = {size_t(uBufInfo.second)}; + size_t uBufSizes[] = {sizeof(ViewBlock)}; + boo::ITexture* texs[] = {m_fontAtlas.texture()}; + + if (!res.m_textRes.m_vtxFmt) + { + boo::VertexElementDescriptor vdescs[] = + { + {vBufInfo.first, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 0}, + {vBufInfo.first, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 1}, + {vBufInfo.first, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 2}, + {vBufInfo.first, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 3}, + {vBufInfo.first, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 0}, + {vBufInfo.first, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 1}, + {vBufInfo.first, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 2}, + {vBufInfo.first, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 3}, + {vBufInfo.first, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 0}, + {vBufInfo.first, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 1}, + {vBufInfo.first, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 2}, + {vBufInfo.first, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3}, + {vBufInfo.first, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced} + }; + m_vtxFmt = ctx.newVertexFormat(13, vdescs, 0, vBufInfo.second); + m_shaderBinding = ctx.newShaderDataBinding(shader, m_vtxFmt, + nullptr, vBufInfo.first, nullptr, 1, + uBufs, nullptr, uBufOffs, uBufSizes, + 1, texs, 0, vBufInfo.second); + } + else + { + m_shaderBinding = ctx.newShaderDataBinding(shader, res.m_textRes.m_vtxFmt, + nullptr, vBufInfo.first, nullptr, 1, + uBufs, nullptr, uBufOffs, uBufSizes, + 1, texs, 0, vBufInfo.second); + } + } + return true; + }); +} + TextView::TextView(ViewResources& res, View& parentView, const FontAtlas& font, Alignment align, size_t capacity) @@ -308,59 +369,7 @@ TextView::TextView(ViewResources& res, Log.report(logvisor::Fatal, "bucket overflow [%" PRISize "/%" PRISize "]", capacity, VertexBufferPool::bucketCapacity()); - m_glyphs.reserve(capacity); - commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool - { - buildResources(ctx, res); - - m_glyphBuf.emplace(res.m_textRes.m_glyphPool.allocateBlock(res.m_factory, capacity)); - - boo::IShaderPipeline* shader; - if (font.subpixel()) - shader = res.m_textRes.m_subpixel; - else - shader = res.m_textRes.m_regular; - - auto vBufInfo = m_glyphBuf->getBufferInfo(); - auto uBufInfo = m_viewVertBlockBuf->getBufferInfo(); - boo::IGraphicsBuffer* uBufs[] = {uBufInfo.first}; - size_t uBufOffs[] = {size_t(uBufInfo.second)}; - size_t uBufSizes[] = {sizeof(ViewBlock)}; - boo::ITexture* texs[] = {m_fontAtlas.texture()}; - - if (!res.m_textRes.m_vtxFmt) - { - boo::VertexElementDescriptor vdescs[] = - { - {vBufInfo.first, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 0}, - {vBufInfo.first, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 1}, - {vBufInfo.first, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 2}, - {vBufInfo.first, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 3}, - {vBufInfo.first, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 0}, - {vBufInfo.first, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 1}, - {vBufInfo.first, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 2}, - {vBufInfo.first, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 3}, - {vBufInfo.first, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 0}, - {vBufInfo.first, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 1}, - {vBufInfo.first, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 2}, - {vBufInfo.first, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3}, - {vBufInfo.first, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced} - }; - m_vtxFmt = ctx.newVertexFormat(13, vdescs, 0, vBufInfo.second); - m_shaderBinding = ctx.newShaderDataBinding(shader, m_vtxFmt, - nullptr, vBufInfo.first, nullptr, 1, - uBufs, nullptr, uBufOffs, uBufSizes, - 1, texs, 0, vBufInfo.second); - } - else - { - m_shaderBinding = ctx.newShaderDataBinding(shader, res.m_textRes.m_vtxFmt, - nullptr, vBufInfo.first, nullptr, 1, - uBufs, nullptr, uBufOffs, uBufSizes, - 1, texs, 0, vBufInfo.second); - } - return true; - }); + _commitResources(0); } TextView::TextView(ViewResources& res, View& parentView, FontTag font, Alignment align, size_t capacity) @@ -398,43 +407,44 @@ int TextView::DoKern(FT_Pos val, const FontAtlas& atlas) void TextView::typesetGlyphs(const std::string& str, const zeus::CColor& defaultColor) { - size_t rem = str.size(); - const utf8proc_uint8_t* it = reinterpret_cast(str.data()); + UTF8Iterator it(str.begin()); + size_t charLen = str.size() ? std::min(it.countTo(str.end()), size_t(1024)) : 0; + _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; - while (rem) + if (charLen) { - utf8proc_int32_t ch; - utf8proc_ssize_t sz = utf8proc_iterate(it, -1, &ch); - if (sz < 0) - Log.report(logvisor::Fatal, "invalid UTF-8 char"); - if (ch == '\n') - break; - - const FontAtlas::Glyph* glyph = m_fontAtlas.lookupGlyph(ch); - if (!glyph) + for (; it.iter() < str.end() ; ++it) { - rem -= sz; - it += sz; - continue; + utf8proc_int32_t ch = *it; + if (ch == -1) + { + Log.report(logvisor::Warning, "invalid UTF-8 char"); + break; + } + if (ch == '\n' || ch == '\0') + break; + + const FontAtlas::Glyph* glyph = m_fontAtlas.lookupGlyph(ch); + if (!glyph) + continue; + + if (lCh != -1) + adv += DoKern(m_fontAtlas.lookupKern(lCh, glyph->m_glyphIdx), m_fontAtlas); + m_glyphs.emplace_back(adv, *glyph, defaultColor); + m_glyphInfo.emplace_back(ch, glyph->m_width, glyph->m_height, adv); + + lCh = glyph->m_glyphIdx; + + if (m_glyphs.size() == m_capacity) + break; } - - if (lCh != -1) - adv += DoKern(m_fontAtlas.lookupKern(lCh, glyph->m_glyphIdx), m_fontAtlas); - m_glyphs.emplace_back(adv, *glyph, defaultColor); - m_glyphInfo.emplace_back(ch, glyph->m_width, glyph->m_height, adv); - - lCh = glyph->m_glyphIdx; - rem -= sz; - it += sz; - - if (m_glyphs.size() == m_capacity) - break; } if (m_align == Alignment::Right) @@ -467,6 +477,8 @@ 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()); + uint32_t lCh = -1; m_glyphs.clear(); m_glyphs.reserve(str.size()); @@ -535,10 +547,13 @@ void TextView::colorGlyphsTypeOn(const zeus::CColor& newColor, float startInterv void TextView::invalidateGlyphs() { - RenderGlyph* out = m_glyphBuf->access(); - size_t i = 0; - for (RenderGlyph& glyph : m_glyphs) - out[i++] = glyph; + if (m_glyphBuf) + { + RenderGlyph* out = m_glyphBuf->access(); + size_t i = 0; + for (RenderGlyph& glyph : m_glyphs) + out[i++] = glyph; + } } void TextView::think() diff --git a/specter/lib/View.cpp b/specter/lib/View.cpp index 749c4519e..639259509 100644 --- a/specter/lib/View.cpp +++ b/specter/lib/View.cpp @@ -373,14 +373,18 @@ void View::draw(boo::IGraphicsCommandQueue* gfxQ) } } +void View::_commitResources(ViewResources& res, const boo::FactoryCommitFunc& commitFunc) +{ + m_gfxData = res.m_factory->commitTransaction(commitFunc); +} + void View::commitResources(ViewResources& res, const boo::FactoryCommitFunc& commitFunc) { if (m_gfxData) Log.report(logvisor::Fatal, "multiple resource commits not allowed"); - m_gfxData = res.m_factory->commitTransaction(commitFunc); + _commitResources(res, commitFunc); } - void View::VertexBufferBindingSolid::init(boo::IGraphicsDataFactory::Context& ctx, ViewResources& res, size_t count, const UniformBufferPool::Token& viewBlockBuf)