Move Vertex/Uniform buffer pools to hecl

This commit is contained in:
Jack Andersen 2016-12-12 10:10:32 -10:00
parent e9dc6787ce
commit 9162db2dcf
12 changed files with 26 additions and 402 deletions

View File

@ -60,9 +60,7 @@ list(APPEND SPECTER_HEADERS
include/specter/FileBrowser.hpp include/specter/FileBrowser.hpp
include/specter/Icon.hpp include/specter/Icon.hpp
include/specter/FontCache.hpp include/specter/FontCache.hpp
include/specter/Translator.hpp include/specter/Translator.hpp)
include/specter/UniformBufferPool.hpp
include/specter/VertexBufferPool.hpp)
atdna(atdna_FontCache.cpp include/specter/FontCache.hpp) atdna(atdna_FontCache.cpp include/specter/FontCache.hpp)

View File

@ -36,7 +36,7 @@ private:
zeus::CColor m_line2Clear; zeus::CColor m_line2Clear;
ViewBlock m_viewBlock; ViewBlock m_viewBlock;
std::experimental::optional<UniformBufferPool<ViewBlock>::Token> m_viewBlockBuf; std::experimental::optional<hecl::UniformBufferPool<ViewBlock>::Token> m_viewBlockBuf;
union union
{ {
struct struct

View File

@ -56,7 +56,7 @@ class RootView : public View
VertexBufferBindingSolid m_vertsBinding; VertexBufferBindingSolid m_vertsBinding;
ViewBlock m_viewBlock; ViewBlock m_viewBlock;
std::experimental::optional<UniformBufferPool<ViewBlock>::Token> m_viewVertBlockBuf; std::experimental::optional<hecl::UniformBufferPool<ViewBlock>::Token> m_viewVertBlockBuf;
SolidShaderVert m_verts[32]; SolidShaderVert m_verts[32];
void setArrowVerts(const boo::SWindowRect& rect, SplitView::ArrowDir dir); void setArrowVerts(const boo::SWindowRect& rect, SplitView::ArrowDir dir);
void setLineVerts(const boo::SWindowRect& rect, float split, SplitView::Axis axis); void setLineVerts(const boo::SWindowRect& rect, float split, SplitView::Axis axis);

View File

@ -43,7 +43,7 @@ private:
ViewChild<View*> m_views[2]; ViewChild<View*> m_views[2];
ViewBlock m_splitBlock; ViewBlock m_splitBlock;
std::experimental::optional<UniformBufferPool<ViewBlock>::Token> m_splitBlockBuf; std::experimental::optional<hecl::UniformBufferPool<ViewBlock>::Token> m_splitBlockBuf;
TexShaderVert m_splitVerts[4]; TexShaderVert m_splitVerts[4];
int m_clearanceA, m_clearanceB; int m_clearanceA, m_clearanceB;

View File

@ -44,7 +44,7 @@ public:
private: private:
size_t m_capacity; size_t m_capacity;
std::experimental::optional<VertexBufferPool<RenderGlyph>::Token> m_glyphBuf; std::experimental::optional<hecl::VertexBufferPool<RenderGlyph>::Token> m_glyphBuf;
boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */ boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */
boo::IShaderDataBinding* m_shaderBinding; boo::IShaderDataBinding* m_shaderBinding;
const FontAtlas& m_fontAtlas; const FontAtlas& m_fontAtlas;
@ -63,7 +63,7 @@ public:
friend class TextView; friend class TextView;
friend class MultiLineTextView; friend class MultiLineTextView;
VertexBufferPool<RenderGlyph> m_glyphPool; hecl::VertexBufferPool<RenderGlyph> m_glyphPool;
void updateBuffers() void updateBuffers()
{ {

View File

@ -31,7 +31,7 @@ private:
std::vector<std::vector<ViewChild<View*>>> m_children; std::vector<std::vector<ViewChild<View*>>> m_children;
ViewBlock m_tbBlock; ViewBlock m_tbBlock;
std::experimental::optional<UniformBufferPool<ViewBlock>::Token> m_tbBlockBuf; std::experimental::optional<hecl::UniformBufferPool<ViewBlock>::Token> m_tbBlockBuf;
TexShaderVert m_tbVerts[10]; TexShaderVert m_tbVerts[10];
int m_nomGauge = 25; int m_nomGauge = 25;
int m_padding = 10; int m_padding = 10;

View File

@ -10,7 +10,7 @@ namespace specter
class Tooltip : public View class Tooltip : public View
{ {
ViewBlock m_ttBlock; ViewBlock m_ttBlock;
std::experimental::optional<UniformBufferPool<ViewBlock>::Token> m_ttBlockBuf; std::experimental::optional<hecl::UniformBufferPool<ViewBlock>::Token> m_ttBlockBuf;
SolidShaderVert m_ttVerts[16]; SolidShaderVert m_ttVerts[16];
int m_nomWidth = 25; int m_nomWidth = 25;
int m_nomHeight = 25; int m_nomHeight = 25;

View File

@ -1,186 +0,0 @@
#ifndef SPECTER_UNIFORMBUFFERPOOL_HPP
#define SPECTER_UNIFORMBUFFERPOOL_HPP
#include <boo/boo.hpp>
#include <hecl/BitVector.hpp>
#include <vector>
#include <cstdlib>
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 <typename UniformStruct>
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<std::is_same<IndexTp, long long>::value, std::lldiv_t,
std::conditional_t<std::is_same<IndexTp, long>::value, std::ldiv_t,
std::conditional_t<std::is_same<IndexTp, int>::value, std::div_t, InvalidTp>>>;
static_assert(!std::is_same<DivTp, InvalidTp>::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<Bucket> 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<uint8_t*>(bucket.buffer->map(m_sizePerBucket));
bucket.dirty = true;
return reinterpret_cast<UniformStruct&>(bucket.cpuBuffer[m_div.rem * m_pool.m_stride]);
}
std::pair<boo::IGraphicsBufferD*, IndexTp> 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

View File

@ -1,189 +0,0 @@
#ifndef SPECTER_VERTEXBUFFERPOOL_HPP
#define SPECTER_VERTEXBUFFERPOOL_HPP
#include <boo/boo.hpp>
#include <hecl/BitVector.hpp>
#include <vector>
#include <cstdlib>
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 <typename VertStruct>
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<std::is_same<IndexTp, long long>::value, std::lldiv_t,
std::conditional_t<std::is_same<IndexTp, long>::value, std::ldiv_t,
std::conditional_t<std::is_same<IndexTp, int>::value, std::div_t, InvalidTp>>>;
static_assert(!std::is_same<DivTp, InvalidTp>::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<Bucket> 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<uint8_t*>(bucket.buffer->map(m_sizePerBucket));
bucket.dirty = true;
return reinterpret_cast<VertStruct*>(&bucket.cpuBuffer[m_div.rem * m_pool.m_stride]);
}
std::pair<boo::IGraphicsBufferD*, IndexTp> 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

View File

@ -8,8 +8,8 @@
#include "zeus/CTransform.hpp" #include "zeus/CTransform.hpp"
#include "zeus/CColor.hpp" #include "zeus/CColor.hpp"
#include "hecl/CVar.hpp" #include "hecl/CVar.hpp"
#include "UniformBufferPool.hpp" #include "hecl/UniformBufferPool.hpp"
#include "VertexBufferPool.hpp" #include "hecl/VertexBufferPool.hpp"
#include <boo/graphicsdev/GL.hpp> #include <boo/graphicsdev/GL.hpp>
#include <boo/graphicsdev/D3D.hpp> #include <boo/graphicsdev/D3D.hpp>
@ -110,7 +110,7 @@ public:
template <typename VertStruct> template <typename VertStruct>
struct VertexBufferBinding struct VertexBufferBinding
{ {
std::experimental::optional<typename VertexBufferPool<VertStruct>::Token> m_vertsBuf; std::experimental::optional<typename hecl::VertexBufferPool<VertStruct>::Token> m_vertsBuf;
boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */ boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */
boo::IShaderDataBinding* m_shaderBinding = nullptr; boo::IShaderDataBinding* m_shaderBinding = nullptr;
@ -143,13 +143,13 @@ public:
{ {
void init(boo::IGraphicsDataFactory::Context& ctx, void init(boo::IGraphicsDataFactory::Context& ctx,
ViewResources& res, size_t count, ViewResources& res, size_t count,
const UniformBufferPool<ViewBlock>::Token& viewBlockBuf); const hecl::UniformBufferPool<ViewBlock>::Token& viewBlockBuf);
}; };
struct VertexBufferBindingTex : VertexBufferBinding<TexShaderVert> struct VertexBufferBindingTex : VertexBufferBinding<TexShaderVert>
{ {
void init(boo::IGraphicsDataFactory::Context& ctx, void init(boo::IGraphicsDataFactory::Context& ctx,
ViewResources& res, size_t count, ViewResources& res, size_t count,
const UniformBufferPool<ViewBlock>::Token& viewBlockBuf, const hecl::UniformBufferPool<ViewBlock>::Token& viewBlockBuf,
boo::ITexture* texture); boo::ITexture* texture);
}; };
@ -184,14 +184,14 @@ protected:
" float4x4 mv;\n"\ " float4x4 mv;\n"\
" float4 mulColor;\n"\ " float4 mulColor;\n"\
"};\n" "};\n"
std::experimental::optional<UniformBufferPool<ViewBlock>::Token> m_viewVertBlockBuf; std::experimental::optional<hecl::UniformBufferPool<ViewBlock>::Token> m_viewVertBlockBuf;
public: public:
struct Resources struct Resources
{ {
UniformBufferPool<ViewBlock> m_bufPool; hecl::UniformBufferPool<ViewBlock> m_bufPool;
VertexBufferPool<SolidShaderVert> m_solidPool; hecl::VertexBufferPool<SolidShaderVert> m_solidPool;
VertexBufferPool<TexShaderVert> m_texPool; hecl::VertexBufferPool<TexShaderVert> m_texPool;
void updateBuffers() void updateBuffers()
{ {

View File

@ -365,9 +365,9 @@ TextView::TextView(ViewResources& res,
m_fontAtlas(font), m_fontAtlas(font),
m_align(align) m_align(align)
{ {
if (size_t(VertexBufferPool<RenderGlyph>::bucketCapacity()) < capacity) if (size_t(hecl::VertexBufferPool<RenderGlyph>::bucketCapacity()) < capacity)
Log.report(logvisor::Fatal, "bucket overflow [%" PRISize "/%" PRISize "]", Log.report(logvisor::Fatal, "bucket overflow [%" PRISize "/%" PRISize "]",
capacity, VertexBufferPool<RenderGlyph>::bucketCapacity()); capacity, hecl::VertexBufferPool<RenderGlyph>::bucketCapacity());
_commitResources(0); _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) void TextView::typesetGlyphs(const std::string& str, const zeus::CColor& defaultColor)
{ {
UTF8Iterator it(str.begin()); 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); _commitResources(charLen);
uint32_t lCh = -1; 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) 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; uint32_t lCh = -1;
m_glyphs.clear(); m_glyphs.clear();
m_glyphs.reserve(str.size()); m_glyphs.reserve(charLen);
m_glyphInfo.clear(); m_glyphInfo.clear();
m_glyphInfo.reserve(str.size()); m_glyphInfo.reserve(charLen);
int adv = 0; int adv = 0;
for (wchar_t ch : str) for (wchar_t ch : str)

View File

@ -387,7 +387,7 @@ void View::commitResources(ViewResources& res, const boo::FactoryCommitFunc& com
void View::VertexBufferBindingSolid::init(boo::IGraphicsDataFactory::Context& ctx, void View::VertexBufferBindingSolid::init(boo::IGraphicsDataFactory::Context& ctx,
ViewResources& res, size_t count, ViewResources& res, size_t count,
const UniformBufferPool<ViewBlock>::Token& viewBlockBuf) const hecl::UniformBufferPool<ViewBlock>::Token& viewBlockBuf)
{ {
m_vertsBuf.emplace(res.m_viewRes.m_solidPool.allocateBlock(res.m_factory, count)); m_vertsBuf.emplace(res.m_viewRes.m_solidPool.allocateBlock(res.m_factory, count));
auto vBufInfo = m_vertsBuf->getBufferInfo(); 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, void View::VertexBufferBindingTex::init(boo::IGraphicsDataFactory::Context& ctx,
ViewResources& res, size_t count, ViewResources& res, size_t count,
const UniformBufferPool<ViewBlock>::Token& viewBlockBuf, const hecl::UniformBufferPool<ViewBlock>::Token& viewBlockBuf,
boo::ITexture* texture) boo::ITexture* texture)
{ {
m_vertsBuf.emplace(res.m_viewRes.m_texPool.allocateBlock(res.m_factory, count)); m_vertsBuf.emplace(res.m_viewRes.m_texPool.allocateBlock(res.m_factory, count));