summaryrefslogtreecommitdiff
path: root/src/font.cpp
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-02-04 19:17:59 -0500
committerJonathan Bradley <jcb@pikum.xyz>2025-02-04 19:17:59 -0500
commit61e179f9580c985cb5ca80ea732fc7572d31c489 (patch)
tree122ff15c6d94ce5aebff4ff6f1fc6d13317abe16 /src/font.cpp
parenta3fb454f9935960dc2d367455f891d8fedfb9016 (diff)
pke: chkpt: text rendering, no errors but blank
Diffstat (limited to 'src/font.cpp')
-rw-r--r--src/font.cpp430
1 files changed, 334 insertions, 96 deletions
diff --git a/src/font.cpp b/src/font.cpp
index 45569cb..6292039 100644
--- a/src/font.cpp
+++ b/src/font.cpp
@@ -1,12 +1,13 @@
#include "font.hpp"
#include "asset-manager.hpp"
-#include "helpers.hpp"
+#include "dynamic-array.hpp"
#include "pk.h"
#include "window.hpp"
#include "static-plane.hpp"
#include "vendor-stb-image-include.h"
#include "game-settings.hpp"
+#include <vulkan/vulkan_core.h>
// TODO threading
@@ -23,11 +24,62 @@ struct FontTypeData {
FontTypeIndex h_ft{0};
} ftd;
+struct FontInstanceBufferItem {
+ glm::vec4 bg_color;
+ glm::vec4 fg_color;
+ glm::ivec4 bounds;
+ float width;
+};
+
+const VkDeviceSize instance_buffer_item_size = (sizeof(glm::vec4) * 3) + sizeof(float);
+
// BucketContainer<FontType, TextRenderHandle, 2> bktFont;
+uint32_t utf8_to_unicode(const char* str, uint32_t &out) {
+ uint32_t i = 0;
+ out = 0;
+
+ // Check the leading byte to determine the number of bytes in the character
+ if ((str[i] & 0x80) == 0) {
+ // 1 byte character (ASCII)
+ out = str[i];
+ i = 1;
+ } else if ((str[i] & 0xE0) == 0xC0) {
+ // 2 byte character
+ out = str[i] & 0x1F;
+ out <<= 6;
+ out |= (str[i + 1] & 0x3F);
+ i = 2;
+ } else if ((str[i] & 0xF0) == 0xE0) {
+ // 3 byte character
+ out = str[i] & 0x0F;
+ out <<= 6;
+ out |= (str[i + 1] & 0x3F);
+ out <<= 6;
+ out |= (str[i + 2] & 0x3F);
+ i = 3;
+ } else if ((str[i] & 0xF8) == 0xF0) {
+ // 4 byte character
+ out = str[i] & 0x07;
+ out <<= 6;
+ out |= (str[i + 1] & 0x3F);
+ out <<= 6;
+ out |= (str[i + 2] & 0x3F);
+ out <<= 6;
+ out |= (str[i + 3] & 0x3F);
+ i = 4;
+ } else {
+ // Invalid UTF-8 byte sequence
+ out = 0;
+ return 0;
+ }
+ return i;
+}
+
void FontType_Init() {
- ftd.h_ft = FontTypeIndex{5};
+ ftd.n_ft = FontTypeIndex{5};
ftd.arr_ft = pk_new<FontType>(5);
+ // memset(ftd.arr_ft, 0, sizeof(FontType) * 5);
}
void FontType_Teardown() {
@@ -36,28 +88,33 @@ void FontType_Teardown() {
for (i = FontTypeIndex{0}; i < ftd.h_ft; ++i) {
FontType_Unload(i);
ft = &ftd.arr_ft[(FontTypeIndex_T)i];
- pk_delete<FontRender>(ft->renders, (FontTypeIndex_T)ft->n_render);
+ if (ft->renders != nullptr) pk_delete<FontRender>(ft->renders, (FontTypeIndex_T)ft->n_render);
}
pk_delete(ftd.arr_ft, (FontTypeIndex_T)ftd.n_ft);
}
void FontType_Serialize(std::ofstream &stream, FontType *ft) {
- NULL_CHAR_ARR(handleStr, 23);
+ NULL_CHAR_ARR(handleStr, AssetKeyLength + 2);
+ const Asset *txtr = AM_Get(ft->fontTextureAssetHandle);
+ const Asset *glyphs = AM_Get(ft->glyphDetailsAssetHandle);
stream << PKE_PROJECT_FONT_TITLE << ft->title.val << std::endl;
- snprintf(handleStr, 22, "0x%08X 0x%08X", ft->fontTextureAssetHandle.bucketIndex, ft->fontTextureAssetHandle.itemIndex);
+ snprintf(handleStr, AssetKeyLength + 1, "%s", txtr->key);
stream << PKE_PROJECT_FONT_TEXTURE_HANDLE << handleStr << std::endl;
- snprintf(handleStr, 22, "0x%08X 0x%08X", ft->fontTextureAssetHandle.bucketIndex, ft->glyphDetailsAssetHandle.itemIndex);
+ snprintf(handleStr, AssetKeyLength + 1, "%s", glyphs->key);
stream << PKE_PROJECT_FONT_GLYPH_DETAILS_HANDLE << handleStr << std::endl;
+ AM_Release(ft->glyphDetailsAssetHandle);
+ AM_Release(ft->fontTextureAssetHandle);
}
void FontType_Deserialize(std::ifstream &stream) {
+ uint64_t i;
NULL_CHAR_ARR(readLine, 128);
pk_cstr title;
- AssetHandle fontTextureHandle;
- AssetHandle glyphDetailsHandle;
+ AssetKey fontTextureKey;
+ AssetKey glyphDetailsKey;
while (memset(readLine, 0, 128), stream.getline(readLine, 128)) {
if (strcmp("", readLine) == 0) {
- FontType_RegisterFont(title, fontTextureHandle, glyphDetailsHandle);
+ FontType_RegisterFont(title, AM_GetHandle(fontTextureKey), AM_GetHandle(glyphDetailsKey));
return;
}
if (strstr(readLine, PKE_PROJECT_FONT_TITLE)) {
@@ -70,22 +127,16 @@ void FontType_Deserialize(std::ifstream &stream) {
}
if (strstr(readLine, PKE_PROJECT_FONT_TEXTURE_HANDLE)) {
uint64_t prefixLen = strlen(PKE_PROJECT_FONT_TEXTURE_HANDLE);
- // 0x00000000 0x00000000
- readLine[prefixLen + 10] = '\0';
- STR2NUM_ERROR result1 = str2num(fontTextureHandle.bucketIndex, readLine + prefixLen + 2, 16);
- STR2NUM_ERROR result2 = str2num(fontTextureHandle.itemIndex, readLine + prefixLen + 11, 16);
- assert(result1 == STR2NUM_ERROR::SUCCESS);
- assert(result2 == STR2NUM_ERROR::SUCCESS);
+ for (i = 0; i < AssetKeyLength; ++i) {
+ fontTextureKey[i] = readLine[prefixLen + i];
+ }
continue;
}
if (strstr(readLine, PKE_PROJECT_FONT_GLYPH_DETAILS_HANDLE)) {
uint64_t prefixLen = strlen(PKE_PROJECT_FONT_GLYPH_DETAILS_HANDLE);
- // 0x00000000 0x00000000
- readLine[prefixLen + 10] = '\0';
- STR2NUM_ERROR result1 = str2num(glyphDetailsHandle.bucketIndex, readLine + prefixLen + 2, 16);
- STR2NUM_ERROR result2 = str2num(glyphDetailsHandle.itemIndex, readLine + prefixLen + 11, 16);
- assert(result1 == STR2NUM_ERROR::SUCCESS);
- assert(result2 == STR2NUM_ERROR::SUCCESS);
+ for (i = 0; i < AssetKeyLength; ++i) {
+ glyphDetailsKey[i] = readLine[prefixLen + i];
+ }
continue;
}
}
@@ -108,23 +159,23 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle
VkDeviceSize index;
constexpr VkDeviceSize expectedBufferCount = 3;
constexpr VkDeviceSize startingGlyphCount = 4;
+ assert(fontTextureHandle != AssetHandle_MAX);
+ assert(glyphsHandle != AssetHandle_MAX);
VkMemoryRequirements combined_vert_mem_reqs;
- VkMemoryRequirements combined_inst_mem_reqs;
VkMemoryRequirements combined_texture_mem_reqs;
const Asset *fontTexture = AM_Get(fontTextureHandle);
- const Asset *glyphs = AM_Get(glyphsHandle);
- if (ftd.h_ft > ftd.n_ft) {
- index = (VkDeviceSize)ftd.n_ft;
+ FontTypeIndex idx = ftd.h_ft;
+ ftd.h_ft += FontTypeIndex{1};
+ if (ftd.h_ft > ftd.n_ft && idx > FontTypeIndex{0}) {
ftd.n_ft += FontTypeIndex{5};
FontType *arr = pk_new<FontType>((FontTypeIndex_T)ftd.n_ft);
- memcpy(arr, ftd.arr_ft, sizeof(FontType) * index);
- pk_delete<FontType>(ftd.arr_ft, index);
+ memcpy(arr, ftd.arr_ft, sizeof(FontType) * (FontTypeIndex_T)idx);
+ if (ftd.arr_ft != nullptr && ftd.arr_ft != CAFE_BABE(FontType)) pk_delete<FontType>(ftd.arr_ft, (FontTypeIndex_T)idx);
ftd.arr_ft = arr;
}
- FontTypeIndex idx = ftd.h_ft++;
FontTypeIndex_T idx_t = (FontTypeIndex_T)idx;
FontType *ft = &ftd.arr_ft[idx_t];
ft->title = title;
@@ -133,9 +184,15 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle
ft->renders = pk_new<FontRender>(startingGlyphCount);
ft->n_render = FontRenderIndex{startingGlyphCount};
+ const Asset *glyphs = AM_Get(glyphsHandle);
+ ft->n_glyphs = glyphs->size / sizeof(FontGlyphChar);
+ ft->glyphs = (FontGlyphChar*)glyphs->ptr;
+
int txtr_x, txtr_y, txtr_chan;
stbi_uc *txtr_bytes = stbi_load_from_memory((unsigned char*)fontTexture->ptr, fontTexture->size, &txtr_x, &txtr_y, &txtr_chan, 4);
+ assert(txtr_bytes != nullptr);
assert(txtr_chan == 4);
+ uint32_t imageSizeBytes = txtr_x * txtr_y * txtr_chan;
/*
* vulkan setup
@@ -154,15 +211,15 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle
*/
// vertex
{
- for (uint64_t index = 0; index < expectedBufferCount; ++index) {
- if (index == 0) {
+ for (uint64_t idx = 0; idx < expectedBufferCount; ++idx) {
+ if (idx == 0) {
bufferCI.size = sizeof(glm::vec2) * 4;
- } else if (index == 1) {
+ } else if (idx == 1) {
bufferCI.size = sizeof(glm::vec2) * 4;
- } else if (index == 2) {
+ } else if (idx == 2) {
bufferCI.size = sizeof(uint16_t) * 6;
}
- if (index == 2) {
+ if (idx == 2) {
bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
} else {
bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
@@ -203,7 +260,7 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle
imageCreateInfo.extent.depth = 1;
imageCreateInfo.mipLevels = 1;
imageCreateInfo.arrayLayers = 1;
- imageCreateInfo.samples = global_sample_count;
+ imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
@@ -217,21 +274,6 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle
vkImageSubresourceRange.baseArrayLayer = 0u;
vkImageSubresourceRange.layerCount = 1u;
- VkImageViewCreateInfo vkImageViewCreateInfo;
- vkImageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
- vkImageViewCreateInfo.pNext = nullptr;
- vkImageViewCreateInfo.flags = 0;
- vkImageViewCreateInfo.image = ft->textureImage;
- vkImageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
- vkImageViewCreateInfo.format = imageCreateInfo.format;
- vkImageViewCreateInfo.components = VkComponentMapping {
- .r = VK_COMPONENT_SWIZZLE_IDENTITY,
- .g = VK_COMPONENT_SWIZZLE_IDENTITY,
- .b = VK_COMPONENT_SWIZZLE_IDENTITY,
- .a = VK_COMPONENT_SWIZZLE_IDENTITY,
- };
- vkImageViewCreateInfo.subresourceRange = vkImageSubresourceRange;
-
VkImage tmpImage;
vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &tmpImage);
assert(vkResult == VK_SUCCESS);
@@ -239,29 +281,9 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle
VkMemoryRequirements imageMemoryRequirements;
vkGetImageMemoryRequirements(vkDevice, tmpImage, &imageMemoryRequirements);
- VkDeviceSize paddedImageSize = imageMemoryRequirements.size + (imageMemoryRequirements.alignment - (imageMemoryRequirements.size % imageMemoryRequirements.alignment));
- assert(paddedImageSize % imageMemoryRequirements.alignment == 0);
+ pk_arr_append(&arr_texture_mem_reqs, &imageMemoryRequirements);
vkDestroyImage(vkDevice, tmpImage, vkAllocator);
-
- VkMemoryAllocateInfo vkMemoryAllocateInfo{};
- vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
- vkMemoryAllocateInfo.pNext = nullptr;
- vkMemoryAllocateInfo.allocationSize = paddedImageSize * swapchainLength;
- vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(imageMemoryRequirements.memoryTypeBits,
- VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
-
- vkResult = vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &ft->deviceMemoryTexture);
- assert(vkResult == VK_SUCCESS);
-
- vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &ft->textureImage);
- assert(vkResult == VK_SUCCESS);
- vkResult = vkBindImageMemory(vkDevice, ft->textureImage, ft->deviceMemoryTexture, 0);
- assert(vkResult == VK_SUCCESS);
-
- vkImageViewCreateInfo.image = ft->textureImage;
- vkResult = vkCreateImageView(vkDevice, &vkImageViewCreateInfo, vkAllocator, &ft->textureImageView);
- assert(vkResult == VK_SUCCESS);
}
/*
@@ -278,14 +300,9 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle
vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_vert_mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &ft->deviceMemoryVert);
- // inst
- CalculateCombinedMemReqs(arr_inst_mem_reqs.next, (VkMemoryRequirements*)arr_inst_mem_reqs.data, combined_inst_mem_reqs);
- vkMemoryAllocateInfo.allocationSize = combined_inst_mem_reqs.size;
- vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_inst_mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &ft->deviceMemoryInst);
-
// texture
CalculateCombinedMemReqs(arr_texture_mem_reqs.next, (VkMemoryRequirements*)arr_texture_mem_reqs.data, combined_texture_mem_reqs);
+ // assert(combined_texture_mem_reqs.size == fontTexture->size && "Size of image texture shouldn't change?");
vkMemoryAllocateInfo.allocationSize = combined_texture_mem_reqs.size;
vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_texture_mem_reqs.memoryTypeBits, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
if (vkMemoryAllocateInfo.memoryTypeIndex == 0) {
@@ -420,18 +437,39 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle
vkQueueWaitIdle(tmpBufferDetails.queue);
}
PKVK_EndBuffer(tmpBufferDetails);
- // set up instance buffer
- {
- ft->bindings.instanceBufferMaxCount = startingGlyphCount;
- bufferCI.size = combined_inst_mem_reqs.size;
- bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
- vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &ft->bindings.instanceBD.buffer);
- vkBindBufferMemory(vkDevice, ft->bindings.instanceBD.buffer, ft->deviceMemoryInst, 0);
- }
// texture // transition image layout and copy to buffer
+ tmpBufferDetails = {};
PKVK_BeginBuffer(transferFamilyIndex, combined_texture_mem_reqs.size, tmpBufferDetails);
- memcpy(tmpBufferDetails.deviceData, txtr_bytes, combined_texture_mem_reqs.size);
+ memcpy(tmpBufferDetails.deviceData, txtr_bytes, imageSizeBytes);
+ free(txtr_bytes);
{
+ VkDeviceSize paddedImageSize = combined_texture_mem_reqs.size + (combined_texture_mem_reqs.alignment - (combined_texture_mem_reqs.size % combined_texture_mem_reqs.alignment));
+ assert(paddedImageSize % combined_texture_mem_reqs.alignment == 0);
+
+ VkImageViewCreateInfo vkImageViewCreateInfo;
+ vkImageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ vkImageViewCreateInfo.pNext = nullptr;
+ vkImageViewCreateInfo.flags = 0;
+ vkImageViewCreateInfo.image = ft->textureImage;
+ vkImageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ vkImageViewCreateInfo.format = imageCreateInfo.format;
+ vkImageViewCreateInfo.components = VkComponentMapping {
+ .r = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .g = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .b = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .a = VK_COMPONENT_SWIZZLE_IDENTITY,
+ };
+ vkImageViewCreateInfo.subresourceRange = vkImageSubresourceRange;
+
+ vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &ft->textureImage);
+ assert(vkResult == VK_SUCCESS);
+ vkResult = vkBindImageMemory(vkDevice, ft->textureImage, ft->deviceMemoryTexture, 0);
+ assert(vkResult == VK_SUCCESS);
+
+ vkImageViewCreateInfo.image = ft->textureImage;
+ vkResult = vkCreateImageView(vkDevice, &vkImageViewCreateInfo, vkAllocator, &ft->textureImageView);
+ assert(vkResult == VK_SUCCESS);
+
VkImageMemoryBarrier vkImageMemoryBarrier;
vkImageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
vkImageMemoryBarrier.pNext = nullptr;
@@ -587,11 +625,11 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle
assert(vkResult == VK_SUCCESS);
VkWriteDescriptorSet *writeDescriptorSets = pk_new<VkWriteDescriptorSet>(swapchainLength);
- for (long i = 0; i < 1 * swapchainLength; ++i) {
+ for (long i = 0; i < swapchainLength; ++i) {
writeDescriptorSets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSets[i].pNext = nullptr;
writeDescriptorSets[i].dstSet = nullptr;
- writeDescriptorSets[i].dstBinding = i;
+ writeDescriptorSets[i].dstBinding = 0;
writeDescriptorSets[i].dstArrayElement = 0;
writeDescriptorSets[i].descriptorCount = 1;
writeDescriptorSets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
@@ -612,20 +650,17 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle
vkDescriptorBufferInfo[i].offset = 0;
vkDescriptorBufferInfo[i].range = sizeof(UniformBufferObject);
- long uboIndex = i * 2;
- long samplerIndex = uboIndex + 1;
-
- writeDescriptorSets[uboIndex].pBufferInfo = &vkDescriptorBufferInfo[i];
- writeDescriptorSets[uboIndex].dstSet = ft->vkDescriptorSets[i];
-
- writeDescriptorSets[samplerIndex].pImageInfo = &textureDescriptorInfo;
- writeDescriptorSets[samplerIndex].dstSet = ft->vkDescriptorSets[i];
+ writeDescriptorSets[i].pImageInfo = &textureDescriptorInfo;
+ writeDescriptorSets[i].dstSet = ft->vkDescriptorSets[i];
}
- vkUpdateDescriptorSets(vkDevice, 2 * swapchainLength, writeDescriptorSets, 0, nullptr);
+ vkUpdateDescriptorSets(vkDevice, swapchainLength, writeDescriptorSets, 0, nullptr);
pk_delete<VkDescriptorBufferInfo>(vkDescriptorBufferInfo, swapchainLength);
pk_delete<VkWriteDescriptorSet>(writeDescriptorSets, swapchainLength);
+ AM_Release(fontTextureHandle);
+ AM_Release(glyphsHandle);
+
return idx;
}
@@ -698,3 +733,206 @@ void FontType_Unload(FontTypeIndex idx) {
pk_delete<char>(ft->title.val, ft->title.reserved);
}
}
+
+// TODO perf?
+// Every time new text is added, we're creating a new buffer and shuffling data
+// This could probably be shortened or deferred by creating a larger-than-needed
+// buffer.
+// TODO threading
+FontRender FontType_AddStringRender(FontTypeIndex idx_ft, pk_cstr cstr) {
+ VkResult vkResult;
+ FontType *ft = &ftd.arr_ft[(FontTypeIndex_T)idx_ft];
+ FontRender *fr;
+ FontGlyphChar *fgc;
+ FontRenderIndex idx_fr = ft->n_render;
+ DynArray<FontInstanceBufferItem> instance_buffer_items;
+ uint32_t i, ii, u, count;
+ int32_t l, m, r;
+
+ if (ft->n_render > ft->h_render) {
+ ft->n_render += FontRenderIndex{5};
+ FontRender *arr = pk_new<FontRender>((FontRenderIndex_T)ft->n_render);
+ memcpy(arr, ft->renders, sizeof(FontRender) * (FontRenderIndex_T)idx_fr);
+ pk_delete<FontRender>(ft->renders, (FontRenderIndex_T)idx_fr);
+ ft->renders = arr;
+ }
+ fr = &ft->renders[(FontRenderIndex_T)idx_fr];
+ fr->len = cstr.length;
+ fr->index_ft = idx_ft;
+ fr->index_fr = idx_fr;
+
+ // insert new characters into tmp buffer
+ instance_buffer_items.Reserve(cstr.length);
+ {
+ count = 0;
+ FontInstanceBufferItem *buf_item;
+ for (i = 0; i < cstr.length;) {
+ fgc = nullptr;
+ u = 0;
+ // determine unicode char
+ ii = utf8_to_unicode(&cstr.val[i], u);
+ if (ii == 0) {
+ fprintf(stderr, "failed to determine unicode for character: '%c'\n", cstr.val[i]);
+ i += 1;
+ continue;
+ }
+ i += ii;
+ count += 1;
+
+ // binary search for glyph details
+ l = 0;
+ r = ft->n_glyphs - 1;
+ do {
+ m = l + (r-l)/2;
+ fgc = &ft->glyphs[m];
+ if (fgc->unicode < u)
+ l = m + 1;
+ else
+ r = m - 1;
+ } while (fgc->unicode != u && l <= r);
+
+ if (fgc->unicode != u) {
+ fprintf(stderr, "font: '%s' doesn't contain unicode character: '%u'\n", ft->title.val, u);
+ continue;
+ }
+
+ buf_item = &instance_buffer_items.Push();
+ buf_item->fg_color = glm::vec4(0.4, 0.5, 0.4, 0);
+ buf_item->bg_color = glm::vec4(1.0, 1.0, 1.0, 0);
+ buf_item->bounds = fgc->bounds;
+ buf_item->width = 10;
+ }
+ }
+ VkDeviceSize item_count_orig = ft->bindings.instanceBufferMaxCount;
+ VkDeviceSize item_count_new = ft->bindings.instanceBufferMaxCount + count;
+
+ // copy existing buffer to new buffer
+ VkDeviceSize byteCount = instance_buffer_item_size * item_count_new;
+ PKVK_TmpBufferDetails tmpBufferDetails{};
+ {
+ VkBuffer newBuffer;
+ VkBufferCreateInfo bufferCI;
+ bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferCI.pNext = nullptr;
+ bufferCI.flags = {};
+ bufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ bufferCI.queueFamilyIndexCount = 1;
+ bufferCI.pQueueFamilyIndices = &graphicsFamilyIndex;
+ bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ bufferCI.size = byteCount;
+ vkResult = vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &newBuffer);
+ assert(vkResult == VK_SUCCESS);
+
+ VkMemoryRequirements vkMemReqs;
+ vkGetBufferMemoryRequirements(vkDevice, newBuffer, &vkMemReqs);
+
+ vkDestroyBuffer(vkDevice, newBuffer, vkAllocator);
+ newBuffer = VK_NULL_HANDLE;
+
+ bufferCI.size = vkMemReqs.size + (vkMemReqs.alignment - (vkMemReqs.size & vkMemReqs.alignment));
+ vkResult = vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &newBuffer);
+ assert(vkResult == VK_SUCCESS);
+
+ VkDeviceMemory new_memory;
+ VkMemoryAllocateInfo vkMemoryAllocateInfo;
+ vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ vkMemoryAllocateInfo.pNext = nullptr;
+ vkMemoryAllocateInfo.allocationSize = bufferCI.size;
+ vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(vkMemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+ vkResult = vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &new_memory);
+ assert(vkResult == VK_SUCCESS);
+
+ vkResult = vkBindBufferMemory(vkDevice, newBuffer, new_memory, 0);
+ assert(vkResult == VK_SUCCESS);
+
+ PKVK_BeginBuffer(transferFamilyIndex, byteCount, tmpBufferDetails, PKVK_TmpBufferFlags_NONE);
+ if (ft->bindings.instanceBD.buffer != VK_NULL_HANDLE) {
+
+ VkCommandBufferBeginInfo vkCommandBufferBeginInfo;
+ vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ vkCommandBufferBeginInfo.pNext = nullptr;
+ // TODO consider single-use?
+ vkCommandBufferBeginInfo.flags = 0;
+ vkCommandBufferBeginInfo.pInheritanceInfo = nullptr;
+ vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo);
+
+ VkBufferCopy vk_buffer_copy{};
+ vk_buffer_copy.srcOffset = 0;
+ vk_buffer_copy.dstOffset = 0;
+ vk_buffer_copy.size = instance_buffer_item_size * item_count_orig;
+ vkCmdCopyBuffer(tmpBufferDetails.cmdBuffer, ft->bindings.instanceBD.buffer, newBuffer, 1, &vk_buffer_copy);
+
+ vkEndCommandBuffer(tmpBufferDetails.cmdBuffer);
+
+ VkSubmitInfo submitInfo;
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.pNext = nullptr;
+ submitInfo.waitSemaphoreCount = 0;
+ submitInfo.pWaitSemaphores = nullptr;
+ submitInfo.pWaitDstStageMask = nullptr;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &tmpBufferDetails.cmdBuffer;
+ submitInfo.signalSemaphoreCount = 0;
+ submitInfo.pSignalSemaphores = nullptr;
+ vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr);
+ vkQueueWaitIdle(tmpBufferDetails.queue);
+
+ }
+ PKVK_EndBuffer(tmpBufferDetails, PKVK_TmpBufferFlags_NONE);
+ if (ft->bindings.instanceBD.buffer != VK_NULL_HANDLE)
+ vkDestroyBuffer(vkDevice, ft->bindings.instanceBD.buffer, vkAllocator);
+ if (ft->deviceMemoryInst != VK_NULL_HANDLE)
+ vkFreeMemory(vkDevice, ft->deviceMemoryInst, vkAllocator);
+
+ ft->deviceMemoryInst = new_memory;
+ ft->bindings.instanceBD.buffer = newBuffer;
+ ft->bindings.instanceBufferMaxCount = item_count_new;
+ }
+
+ // create tmp local buffer & copy data to graphics card
+ byteCount = instance_buffer_item_size * count;
+ PKVK_BeginBuffer(transferFamilyIndex, byteCount, tmpBufferDetails);
+ {
+
+ VkCommandBufferBeginInfo vkCommandBufferBeginInfo;
+ vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ vkCommandBufferBeginInfo.pNext = nullptr;
+ // TODO consider single-use?
+ vkCommandBufferBeginInfo.flags = 0;
+ vkCommandBufferBeginInfo.pInheritanceInfo = nullptr;
+ vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo);
+
+ memcpy(tmpBufferDetails.deviceData, instance_buffer_items.GetPtr(), instance_buffer_items.Count() * instance_buffer_item_size);
+
+ VkBufferCopy vk_buffer_copy{};
+ vk_buffer_copy.srcOffset = 0;
+ vk_buffer_copy.dstOffset = instance_buffer_item_size * item_count_orig;
+ vk_buffer_copy.size = instance_buffer_item_size * count;
+ vkCmdCopyBuffer(tmpBufferDetails.cmdBuffer, tmpBufferDetails.buffer, ft->bindings.instanceBD.buffer, 1, &vk_buffer_copy);
+
+ vkEndCommandBuffer(tmpBufferDetails.cmdBuffer);
+
+ VkSubmitInfo submitInfo;
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.pNext = nullptr;
+ submitInfo.waitSemaphoreCount = 0;
+ submitInfo.pWaitSemaphores = nullptr;
+ submitInfo.pWaitDstStageMask = nullptr;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &tmpBufferDetails.cmdBuffer;
+ submitInfo.signalSemaphoreCount = 0;
+ submitInfo.pSignalSemaphores = nullptr;
+ vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr);
+ vkQueueWaitIdle(tmpBufferDetails.queue);
+ }
+ PKVK_EndBuffer(tmpBufferDetails);
+
+ return *fr;
+}
+
+void FontType_RemoveStringRender(FontRender fr) {
+ (void)fr;
+ // TODO
+ // FontType *ft = &ftd.arr_ft[(FontTypeIndex_T)fr.index_ft];
+}