working on font system

This commit is contained in:
Kbz-8
2023-12-13 00:33:34 +01:00
parent 6ccf2a10d7
commit d56b224d42
11 changed files with 333 additions and 92 deletions

83
4 git.filemode.normal_file
View File

@@ -0,0 +1,83 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* text_library.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/10 11:59:57 by maldavid #+# #+# */
/* Updated: 2023/12/12 22:47:33 by kbz_8 ### ########.fr */
/* */
/* ************************************************************************** */
#include <renderer/text_library.h>
#include <core/errors.h>
#include <renderer/renderer.h>
#include <algorithm>
namespace mlx
{
void TextData::init(std::string text, Font const* font, std::vector<Vertex> vbo_data, std::vector<uint16_t> ibo_data)
{
_text = std::move(text);
_font = font;
#ifdef DEBUG
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_vbo[i].create(sizeof(Vertex) * vbo_data.size(), static_cast<const void*>(vbo_data.data()), _text.c_str());
_ibo.create(sizeof(uint16_t) * ibo_data.size(), ibo_data.data(), _text.c_str());
#else
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_vbo[i].create(sizeof(Vertex) * vbo_data.size(), static_cast<const void*>(vbo_data.data()), nullptr);
_ibo.create(sizeof(uint16_t) * ibo_data.size(), ibo_data.data(), nullptr);
#endif
}
void TextData::bind(Renderer& renderer) noexcept
{
_vbo[renderer.getActiveImageIndex()].bind(renderer);
_ibo.bind(renderer);
}
void TextData::destroy() noexcept
{
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_vbo[i].destroy();
_ibo.destroy();
}
std::shared_ptr<TextData> TextLibrary::getTextData(TextID id)
{
if(!_cache.count(id))
core::error::report(e_kind::fatal_error, "Text Library : wrong text ID '%d'", id);
return _cache[id];
}
TextID TextLibrary::addTextToLibrary(std::shared_ptr<TextData> text)
{
auto it = std::find_if(_cache.begin(), _cache.end(), [=](const std::pair<TextID, std::shared_ptr<TextData>>& v)
{
return v.second->getText() == text->getText();
});
if(it != _cache.end())
return it->first;
_cache[_current_id] = text;
_current_id++;
return _current_id - 1;
}
void TextLibrary::removeTextFromLibrary(TextID id)
{
if(_cache.count(id))
{
_cache[id]->destroy();
_cache.erase(id);
}
}
void TextLibrary::clearLibrary()
{
for(auto [id, text] : _cache)
text->destroy();
_cache.clear();
}
}

View File

@@ -6,7 +6,7 @@
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */ /* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/08 18:55:57 by maldavid #+# #+# */ /* Created: 2022/10/08 18:55:57 by maldavid #+# #+# */
/* Updated: 2023/12/10 23:05:14 by kbz_8 ### ########.fr */ /* Updated: 2023/12/12 22:11:47 by kbz_8 ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@@ -23,9 +23,9 @@ namespace mlx
void Buffer::create(Buffer::kind type, VkDeviceSize size, VkBufferUsageFlags usage, const char* name, const void* data) void Buffer::create(Buffer::kind type, VkDeviceSize size, VkBufferUsageFlags usage, const char* name, const void* data)
{ {
_usage = usage; _usage = usage;
if(type == Buffer::kind::constant) if(type == Buffer::kind::constant || type == Buffer::kind::dynamic_device_local)
{ {
if(data == nullptr) if(data == nullptr && type == Buffer::kind::constant)
{ {
core::error::report(e_kind::warning, "Vulkan : trying to create constant buffer without data (constant buffers cannot be modified after creation)"); core::error::report(e_kind::warning, "Vulkan : trying to create constant buffer without data (constant buffers cannot be modified after creation)");
return; return;
@@ -45,7 +45,7 @@ namespace mlx
mapMem(&mapped); mapMem(&mapped);
std::memcpy(mapped, data, size); std::memcpy(mapped, data, size);
unmapMem(); unmapMem();
if(type == Buffer::kind::constant) if(type == Buffer::kind::constant || type == Buffer::kind::dynamic_device_local)
pushToGPU(); pushToGPU();
} }
} }
@@ -83,6 +83,40 @@ namespace mlx
_size = size; _size = size;
} }
bool Buffer::copyFromBuffer(const Buffer& buffer) noexcept
{
if(!(_usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT))
{
core::error::report(e_kind::error, "Vulkan : buffer cannot be the destination of a copy because it does not have the correct usage flag");
return false;
}
if(!(buffer._usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT))
{
core::error::report(e_kind::error, "Vulkan : buffer cannot be the source of a copy because it does not have the correct usage flag");
return false;
}
// TODO, use global cmd buffer pool to manage resources
CmdPool cmdpool;
cmdpool.init();
CmdBuffer cmdBuffer;
cmdBuffer.init(&cmdpool);
cmdBuffer.beginRecord(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
VkBufferCopy copyRegion{};
copyRegion.size = _size;
vkCmdCopyBuffer(cmdBuffer.get(), buffer._buffer, _buffer, 1, &copyRegion);
cmdBuffer.endRecord();
cmdBuffer.submitIdle();
cmdBuffer.destroy();
cmdpool.destroy();
return true;
}
void Buffer::pushToGPU() noexcept void Buffer::pushToGPU() noexcept
{ {
VmaAllocationCreateInfo alloc_info{}; VmaAllocationCreateInfo alloc_info{};
@@ -97,25 +131,8 @@ namespace mlx
newBuffer.createBuffer(newBuffer._usage, alloc_info, _size, nullptr); newBuffer.createBuffer(newBuffer._usage, alloc_info, _size, nullptr);
#endif #endif
CmdPool cmdpool; if(newBuffer.copyFromBuffer(*this)) // if the copy succeded we swap the buffers, else the new one is deleted
cmdpool.init(); this->swap(newBuffer);
CmdBuffer cmdBuffer;
cmdBuffer.init(&cmdpool);
cmdBuffer.beginRecord(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
VkBufferCopy copyRegion{};
copyRegion.size = _size;
vkCmdCopyBuffer(cmdBuffer.get(), _buffer, newBuffer._buffer, 1, &copyRegion);
cmdBuffer.endRecord();
cmdBuffer.submitIdle();
cmdBuffer.destroy();
cmdpool.destroy();
this->swap(newBuffer);
newBuffer.destroy(); newBuffer.destroy();
} }

View File

@@ -6,7 +6,7 @@
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */ /* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/06 23:18:52 by maldavid #+# #+# */ /* Created: 2022/10/06 23:18:52 by maldavid #+# #+# */
/* Updated: 2023/12/11 19:47:39 by kbz_8 ### ########.fr */ /* Updated: 2023/12/12 21:12:44 by kbz_8 ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@@ -22,7 +22,7 @@ namespace mlx
class Buffer class Buffer
{ {
public: public:
enum class kind { dynamic, uniform, constant }; enum class kind { dynamic, dynamic_device_local, uniform, constant };
void create(kind type, VkDeviceSize size, VkBufferUsageFlags usage, const char* name, const void* data = nullptr); void create(kind type, VkDeviceSize size, VkBufferUsageFlags usage, const char* name, const void* data = nullptr);
void destroy() noexcept; void destroy() noexcept;
@@ -32,6 +32,7 @@ namespace mlx
inline void unmapMem() noexcept { Render_Core::get().getAllocator().unmapMemory(_allocation); _is_mapped = false; } inline void unmapMem() noexcept { Render_Core::get().getAllocator().unmapMemory(_allocation); _is_mapped = false; }
void flush(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); void flush(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0);
bool copyFromBuffer(const Buffer& buffer) noexcept;
inline VkBuffer& operator()() noexcept { return _buffer; } inline VkBuffer& operator()() noexcept { return _buffer; }
inline VkBuffer& get() noexcept { return _buffer; } inline VkBuffer& get() noexcept { return _buffer; }

View File

@@ -6,7 +6,7 @@
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */ /* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/06 18:28:08 by maldavid #+# #+# */ /* Created: 2022/10/06 18:28:08 by maldavid #+# #+# */
/* Updated: 2023/11/10 08:33:52 by maldavid ### ########.fr */ /* Updated: 2023/12/12 22:17:14 by kbz_8 ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@@ -18,7 +18,10 @@ namespace mlx
void VBO::setData(uint32_t size, const void* data) void VBO::setData(uint32_t size, const void* data)
{ {
if(size > getSize()) if(size > getSize())
{
core::error::report(e_kind::error, "Vulkan : trying to store to much data in a vertex buffer (%d bytes in %d bytes)", size, getSize()); core::error::report(e_kind::error, "Vulkan : trying to store to much data in a vertex buffer (%d bytes in %d bytes)", size, getSize());
return;
}
if(data == nullptr) if(data == nullptr)
core::error::report(e_kind::warning, "Vulkan : mapping null data in a vertex buffer"); core::error::report(e_kind::warning, "Vulkan : mapping null data in a vertex buffer");
@@ -28,4 +31,25 @@ namespace mlx
std::memcpy(temp, data, static_cast<size_t>(size)); std::memcpy(temp, data, static_cast<size_t>(size));
unmapMem(); unmapMem();
} }
void D_VBO::setData(uint32_t size, const void* data)
{
if(size > getSize())
{
core::error::report(e_kind::error, "Vulkan : trying to store to much data in a vertex buffer (%d bytes in %d bytes)", size, getSize());
return;
}
if(data == nullptr)
core::error::report(e_kind::warning, "Vulkan : mapping null data in a vertex buffer");
Buffer tmp_buf;
#ifdef DEBUG
tmp_buf.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, "tmp_buffer", data);
#else
tmp_buf.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, nullptr, data);
#endif
copyFromBuffer(tmp_buf);
tmp_buf.destroy();
}
} }

View File

@@ -6,7 +6,7 @@
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */ /* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/06 18:27:38 by maldavid #+# #+# */ /* Created: 2022/10/06 18:27:38 by maldavid #+# #+# */
/* Updated: 2023/12/08 19:06:45 by kbz_8 ### ########.fr */ /* Updated: 2023/12/12 22:46:21 by kbz_8 ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@@ -22,7 +22,15 @@ namespace mlx
class VBO : public Buffer class VBO : public Buffer
{ {
public: public:
inline void create(uint32_t size, const char* name) { Buffer::create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, name); } inline void create(uint32_t size, const void* data, const char* name) { Buffer::create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, name, data); }
void setData(uint32_t size, const void* data);
inline void bind(Renderer& renderer) noexcept { vkCmdBindVertexBuffers(renderer.getActiveCmdBuffer().get(), 0, 1, &_buffer, &_offset); }
};
class D_VBO : public Buffer
{
public:
inline void create(uint32_t size, const void* data, const char* name) { Buffer::create(Buffer::kind::dynamic_device_local, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, name, data); }
void setData(uint32_t size, const void* data); void setData(uint32_t size, const void* data);
inline void bind(Renderer& renderer) noexcept { vkCmdBindVertexBuffers(renderer.getActiveCmdBuffer().get(), 0, 1, &_buffer, &_offset); } inline void bind(Renderer& renderer) noexcept { vkCmdBindVertexBuffers(renderer.getActiveCmdBuffer().get(), 0, 1, &_buffer, &_offset); }
}; };

84
src/renderer/font.cpp git.filemode.normal_file
View File

@@ -0,0 +1,84 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* font.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/12/11 22:06:09 by kbz_8 #+# #+# */
/* Updated: 2023/12/12 23:57:53 by kbz_8 ### ########.fr */
/* */
/* ************************************************************************** */
#include <renderer/font.h>
#include <renderer/renderer.h>
#include <fstream>
constexpr const int RANGE = 1024;
namespace mlx
{
Font::Font(Renderer& renderer, const std::filesystem::path& path, float scale) : non_copyable(), _name(path.string())
{
std::vector<uint8_t> tmp_bitmap(RANGE * RANGE);
std::vector<uint8_t> vulkan_bitmap(RANGE * RANGE * 4);
std::ifstream file(path, std::ios::binary);
if(!file.is_open())
{
core::error::report(e_kind::error, "Font load : cannot open font file, %s", _name.c_str());
return;
}
std::ifstream::pos_type fileSize = std::filesystem::file_size(path);
file.seekg(0, std::ios::beg);
std::vector<uint8_t> bytes(fileSize);
file.read(reinterpret_cast<char*>(bytes.data()), fileSize);
file.close();
stbtt_pack_context pc;
stbtt_PackBegin(&pc, tmp_bitmap.data(), RANGE, RANGE, RANGE, 1, nullptr);
stbtt_PackFontRange(&pc, bytes.data(), 0, scale, 32, 96, _cdata.data());
stbtt_PackEnd(&pc);
for(int i = 0, j = 0; i < RANGE * RANGE; i++, j += 4)
{
vulkan_bitmap[j + 0] = tmp_bitmap[i];
vulkan_bitmap[j + 1] = tmp_bitmap[i];
vulkan_bitmap[j + 2] = tmp_bitmap[i];
vulkan_bitmap[j + 3] = tmp_bitmap[i];
}
#ifdef DEBUG
_atlas.create(vulkan_bitmap.data(), RANGE, RANGE, VK_FORMAT_R8G8B8A8_UNORM, std::string(name + "_font_altas").c_str(), true);
#else
_atlas.create(vulkan_bitmap.data(), RANGE, RANGE, VK_FORMAT_R8G8B8A8_UNORM, nullptr, true);
#endif
_atlas.setDescriptor(renderer.getFragDescriptorSet().duplicate());
}
Font::Font(class Renderer& renderer, const std::string& name, const std::vector<uint8_t>& ttf_data, float scale)
{
std::vector<uint8_t> tmp_bitmap(RANGE * RANGE);
std::vector<uint8_t> vulkan_bitmap(RANGE * RANGE * 4);
stbtt_pack_context pc;
stbtt_PackBegin(&pc, tmp_bitmap.data(), RANGE, RANGE, RANGE, 1, nullptr);
stbtt_PackFontRange(&pc, ttf_data.data(), 0, 6.0, 32, 96, _cdata.data());
stbtt_PackEnd(&pc);
for(int i = 0, j = 0; i < RANGE * RANGE; i++, j += 4)
{
vulkan_bitmap[j + 0] = tmp_bitmap[i];
vulkan_bitmap[j + 1] = tmp_bitmap[i];
vulkan_bitmap[j + 2] = tmp_bitmap[i];
vulkan_bitmap[j + 3] = tmp_bitmap[i];
}
#ifdef DEBUG
_atlas.create(vulkan_bitmap.data(), RANGE, RANGE, VK_FORMAT_R8G8B8A8_UNORM, std::string(name + "_font_altas").c_str(), true);
#else
_atlas.create(vulkan_bitmap.data(), RANGE, RANGE, VK_FORMAT_R8G8B8A8_UNORM, nullptr, true);
#endif
_atlas.setDescriptor(renderer.getFragDescriptorSet().duplicate());
}
Font::~Font()
{
_atlas.destroy();
}
}

53
src/renderer/font.h git.filemode.normal_file
View File

@@ -0,0 +1,53 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* font.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/12/11 21:17:04 by kbz_8 #+# #+# */
/* Updated: 2023/12/13 00:25:30 by kbz_8 ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef __MLX_FONT__
#define __MLX_FONT__
#include <array>
#include <stb_truetype.h>
#include <utils/non_copyable.h>
#include <renderer/images/texture_atlas.h>
namespace mlx
{
class Font : public non_copyable
{
public:
Font(class Renderer& renderer, const std::filesystem::path& path, float scale);
Font(class Renderer& renderer, const std::string& name, const std::vector<uint8_t>& ttf_data, float scale);
inline const std::string& getName() const { return _name; }
inline const std::array<stbtt_packedchar, 96>& getCharData() const { return _cdata; }
inline VkDescriptorSet getDescriptorSet() noexcept { return _atlas.getSet(); }
inline bool operator==(const Font& rhs) { return rhs._name == _name; }
~Font();
private:
std::array<stbtt_packedchar, 96> _cdata;
TextureAtlas _atlas;
std::string _name;
};
}
namespace std
{
template <>
struct hash<mlx::Font>
{
std::size_t operator()(const mlx::Font& f) const noexcept
{
return std::hash<std::string>()(f.getName());
}
};
}
#endif

View File

@@ -6,7 +6,7 @@
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */ /* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/10 11:59:57 by maldavid #+# #+# */ /* Created: 2023/04/10 11:59:57 by maldavid #+# #+# */
/* Updated: 2023/11/16 13:45:31 by maldavid ### ########.fr */ /* Updated: 2023/12/12 23:03:33 by kbz_8 ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@@ -17,27 +17,36 @@
namespace mlx namespace mlx
{ {
void TextData::init(std::string text, std::vector<Vertex> vbo_data, std::vector<uint16_t> ibo_data) void TextData::init(std::string text, Font const* font, std::vector<Vertex> vbo_data, std::vector<uint16_t> ibo_data)
{ {
_text = std::move(text); _text = std::move(text);
_font = font;
#ifdef DEBUG #ifdef DEBUG
_vbo.create(sizeof(Vertex) * vbo_data.size(), vbo_data.data(), _text.c_str()); for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_vbo[i].create(sizeof(Vertex) * vbo_data.size(), static_cast<const void*>(vbo_data.data()), _text.c_str());
_ibo.create(sizeof(uint16_t) * ibo_data.size(), ibo_data.data(), _text.c_str()); _ibo.create(sizeof(uint16_t) * ibo_data.size(), ibo_data.data(), _text.c_str());
#else #else
_vbo.create(sizeof(Vertex) * vbo_data.size(), vbo_data.data(), nullptr); for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_vbo[i].create(sizeof(Vertex) * vbo_data.size(), static_cast<const void*>(vbo_data.data()), nullptr);
_ibo.create(sizeof(uint16_t) * ibo_data.size(), ibo_data.data(), nullptr); _ibo.create(sizeof(uint16_t) * ibo_data.size(), ibo_data.data(), nullptr);
#endif #endif
} }
void TextData::bind(Renderer& renderer) noexcept void TextData::bind(Renderer& renderer) noexcept
{ {
_vbo.bind(renderer); _vbo[renderer.getActiveImageIndex()].bind(renderer);
_ibo.bind(renderer); _ibo.bind(renderer);
} }
void TextData::updateVertexData(int frame, std::vector<Vertex> vbo_data)
{
_vbo[frame].setData(sizeof(Vertex) * vbo_data.size(), static_cast<const void*>(vbo_data.data()));
}
void TextData::destroy() noexcept void TextData::destroy() noexcept
{ {
_vbo.destroy(); for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_vbo[i].destroy();
_ibo.destroy(); _ibo.destroy();
} }

View File

@@ -6,7 +6,7 @@
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */ /* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/10 11:52:30 by maldavid #+# #+# */ /* Created: 2023/04/10 11:52:30 by maldavid #+# #+# */
/* Updated: 2023/12/08 19:12:24 by kbz_8 ### ########.fr */ /* Updated: 2023/12/12 23:07:13 by kbz_8 ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@@ -21,6 +21,8 @@
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
#include <mlx_profile.h> #include <mlx_profile.h>
#include <renderer/font.h>
#include <renderer/core/render_core.h>
namespace mlx namespace mlx
{ {
@@ -32,8 +34,10 @@ namespace mlx
public: public:
TextData() = default; TextData() = default;
void init(std::string text, std::vector<Vertex> vbo_data, std::vector<uint16_t> ibo_data); void init(std::string text, Font const* font, std::vector<Vertex> vbo_data, std::vector<uint16_t> ibo_data);
void bind(class Renderer& renderer) noexcept; void bind(class Renderer& renderer) noexcept;
inline const Font& getFontInUse() const noexcept { return *_font; }
void updateVertexData(int frame, std::vector<Vertex> vbo_data);
inline uint32_t getIBOsize() noexcept { return _ibo.getSize(); } inline uint32_t getIBOsize() noexcept { return _ibo.getSize(); }
inline const std::string& getText() const { return _text; } inline const std::string& getText() const { return _text; }
void destroy() noexcept; void destroy() noexcept;
@@ -41,9 +45,10 @@ namespace mlx
~TextData() = default; ~TextData() = default;
private: private:
C_VBO _vbo; std::array<D_VBO, MAX_FRAMES_IN_FLIGHT> _vbo;
C_IBO _ibo; C_IBO _ibo;
std::string _text; std::string _text;
Font const* _font = nullptr;
}; };
class TextLibrary class TextLibrary

View File

@@ -6,7 +6,7 @@
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */ /* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/06 16:41:13 by maldavid #+# #+# */ /* Created: 2023/04/06 16:41:13 by maldavid #+# #+# */
/* Updated: 2023/12/11 15:12:02 by kbz_8 ### ########.fr */ /* Updated: 2023/12/13 00:25:48 by kbz_8 ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@@ -36,7 +36,7 @@ namespace mlx
text(std::move(_text)) text(std::move(_text))
{} {}
void TextDrawData::init(TextLibrary& library, std::array<stbtt_packedchar, 96>& cdata) noexcept void TextDrawData::init(TextLibrary& library, Font* const font) noexcept
{ {
std::vector<Vertex> vertexData; std::vector<Vertex> vertexData;
std::vector<uint16_t> indexData; std::vector<uint16_t> indexData;
@@ -50,7 +50,7 @@ namespace mlx
continue; continue;
stbtt_aligned_quad q; stbtt_aligned_quad q;
stbtt_GetPackedQuad(cdata.data(), RANGE, RANGE, c - 32, &stb_x, &stb_y, &q, 1); stbtt_GetPackedQuad(font->getCharData().data(), RANGE, RANGE, c - 32, &stb_x, &stb_y, &q, 1);
std::size_t index = vertexData.size(); std::size_t index = vertexData.size();
@@ -67,7 +67,7 @@ namespace mlx
indexData.emplace_back(index + 0); indexData.emplace_back(index + 0);
} }
std::shared_ptr<TextData> text_data = std::make_shared<TextData>(); std::shared_ptr<TextData> text_data = std::make_shared<TextData>();
text_data->init(text, std::move(vertexData), std::move(indexData)); text_data->init(text, font, std::move(vertexData), std::move(indexData));
id = library.addTextToLibrary(text_data); id = library.addTextToLibrary(text_data);
#ifdef DEBUG #ifdef DEBUG
@@ -78,61 +78,19 @@ namespace mlx
void TextPutPipeline::init(Renderer* renderer) noexcept void TextPutPipeline::init(Renderer* renderer) noexcept
{ {
_renderer = renderer; _renderer = renderer;
std::vector<uint8_t> tmp_bitmap(RANGE * RANGE); _font_set.emplace(*_renderer, "default", dogica_ttf, 6.0f);
std::vector<uint8_t> vulkan_bitmap(RANGE * RANGE * 4);
stbtt_pack_context pc;
stbtt_PackBegin(&pc, tmp_bitmap.data(), RANGE, RANGE, RANGE, 1, nullptr);
stbtt_PackFontRange(&pc, dogica_ttf, 0, 6.0, 32, 96, _cdata.data());
stbtt_PackEnd(&pc);
for(int i = 0, j = 0; i < RANGE * RANGE; i++, j += 4)
{
vulkan_bitmap[j + 0] = tmp_bitmap[i];
vulkan_bitmap[j + 1] = tmp_bitmap[i];
vulkan_bitmap[j + 2] = tmp_bitmap[i];
vulkan_bitmap[j + 3] = tmp_bitmap[i];
}
_atlas.create(vulkan_bitmap.data(), RANGE, RANGE, VK_FORMAT_R8G8B8A8_UNORM, "__mlx_texts_pipeline_texture_atlas", true);
_atlas.setDescriptor(renderer->getFragDescriptorSet().duplicate());
} }
void TextPutPipeline::loadFont(const std::filesystem::path& filepath, float scale) void TextPutPipeline::loadFont(const std::filesystem::path& filepath, float scale)
{ {
std::vector<uint8_t> tmp_bitmap(RANGE * RANGE); _font_set.emplace(*_renderer, filepath, scale);
std::vector<uint8_t> vulkan_bitmap(RANGE * RANGE * 4);
std::ifstream file(filepath, std::ios::binary);
if(!file.is_open())
{
core::error::report(e_kind::error, "Font load : cannot open font file, %s", filepath.string().c_str());
return;
}
std::ifstream::pos_type fileSize = std::filesystem::file_size(filepath);
file.seekg(0, std::ios::beg);
std::vector<uint8_t> bytes(fileSize);
file.read(reinterpret_cast<char*>(bytes.data()), fileSize);
file.close();
stbtt_pack_context pc;
stbtt_PackBegin(&pc, tmp_bitmap.data(), RANGE, RANGE, RANGE, 1, nullptr);
stbtt_PackFontRange(&pc, bytes.data(), 0, scale, 32, 96, _cdata.data());
stbtt_PackEnd(&pc);
for(int i = 0, j = 0; i < RANGE * RANGE; i++, j += 4)
{
vulkan_bitmap[j + 0] = tmp_bitmap[i];
vulkan_bitmap[j + 1] = tmp_bitmap[i];
vulkan_bitmap[j + 2] = tmp_bitmap[i];
vulkan_bitmap[j + 3] = tmp_bitmap[i];
}
destroy();
_atlas.create(vulkan_bitmap.data(), RANGE, RANGE, VK_FORMAT_R8G8B8A8_UNORM, "__mlx_texts_pipeline_texture_atlas", true);
_atlas.setDescriptor(_renderer->getFragDescriptorSet().duplicate());
} }
void TextPutPipeline::put(int x, int y, int color, std::string str) void TextPutPipeline::put(int x, int y, int color, std::string str)
{ {
auto res = _drawlist.emplace(std::move(str), color, x, y); auto res = _drawlist.emplace(std::move(str), color, x, y);
if(res.second) if(res.second)
const_cast<TextDrawData&>(*res.first).init(_library, _cdata); const_cast<TextDrawData&>(*res.first).init(_library, _font_in_use);
} }
void TextPutPipeline::render() void TextPutPipeline::render()
@@ -150,6 +108,6 @@ namespace mlx
{ {
_library.clearLibrary(); _library.clearLibrary();
_drawlist.clear(); _drawlist.clear();
_atlas.destroy(); _font_set.clear();
} }
} }

View File

@@ -6,7 +6,7 @@
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */ /* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/06 16:24:11 by maldavid #+# #+# */ /* Created: 2023/04/06 16:24:11 by maldavid #+# #+# */
/* Updated: 2023/12/08 19:12:40 by kbz_8 ### ########.fr */ /* Updated: 2023/12/13 00:24:21 by kbz_8 ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@@ -33,7 +33,7 @@ namespace mlx
std::string text; std::string text;
TextDrawData(std::string text, int _color, int _x, int _y); TextDrawData(std::string text, int _color, int _x, int _y);
void init(TextLibrary& library, std::array<stbtt_packedchar, 96>& cdata) noexcept; void init(TextLibrary& library, Font* const font) noexcept;
bool operator==(const TextDrawData& rhs) const { return text == rhs.text && x == rhs.x && y == rhs.y && color == rhs.color; } bool operator==(const TextDrawData& rhs) const { return text == rhs.text && x == rhs.x && y == rhs.y && color == rhs.color; }
}; };
} }
@@ -59,7 +59,6 @@ namespace mlx
void init(Renderer* renderer) noexcept; void init(Renderer* renderer) noexcept;
void put(int x, int y, int color, std::string str); void put(int x, int y, int color, std::string str);
inline VkDescriptorSet getDescriptorSet() noexcept { return _atlas.getSet(); }
inline void clear() { _drawlist.clear(); } inline void clear() { _drawlist.clear(); }
void loadFont(const std::filesystem::path& filepath, float scale); void loadFont(const std::filesystem::path& filepath, float scale);
void render(); void render();
@@ -68,11 +67,11 @@ namespace mlx
~TextPutPipeline() = default; ~TextPutPipeline() = default;
private: private:
std::array<stbtt_packedchar, 96> _cdata; std::unordered_set<Font> _font_set;
TextureAtlas _atlas;
TextLibrary _library; TextLibrary _library;
std::unordered_set<TextDrawData> _drawlist; std::unordered_set<TextDrawData> _drawlist;
Renderer* _renderer = nullptr; Renderer* _renderer = nullptr;
Font* _font_in_use = nullptr;
}; };
} }