#include "font.hpp" #include "asset-manager.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 // TODO threading const char *PKE_PROJECT_FONT_TITLE = "Font::Title: "; const char *PKE_PROJECT_FONT_TEXTURE_HANDLE = "Font::TextureHandle: "; const char *PKE_PROJECT_FONT_GLYPH_DETAILS_HANDLE = "Font::GlyphDetailsHandle: "; TypeSafeInt_B(FontTypeIndex); TypeSafeInt_B(FontRenderIndex); struct FontTypeData { FontType *arr_ft; FontTypeIndex n_ft{0}; FontTypeIndex h_ft{0}; } ftd; struct FontInstanceBufferItem { glm::vec4 bg_color; glm::vec4 fg_color; glm::vec2 sprite_region_min; glm::vec2 sprite_region_max; float width; }; const VkDeviceSize instance_buffer_item_size = (sizeof(glm::vec4) * 2) + (sizeof(glm::vec2) * 2) + sizeof(float); // BucketContainer 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.n_ft = FontTypeIndex{5}; ftd.arr_ft = pk_new(5); // memset(ftd.arr_ft, 0, sizeof(FontType) * 5); } void FontType_Teardown() { FontTypeIndex i; FontType *ft; for (i = FontTypeIndex{0}; i < ftd.h_ft; ++i) { FontType_Unload(i); ft = &ftd.arr_ft[(FontTypeIndex_T)i]; if (ft->renders != nullptr) pk_delete(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, 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, AssetKeyLength + 1, "%s", txtr->key); stream << PKE_PROJECT_FONT_TEXTURE_HANDLE << handleStr << std::endl; 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; AssetKey fontTextureKey; AssetKey glyphDetailsKey; while (memset(readLine, 0, 128), stream.getline(readLine, 128)) { if (strcmp("", readLine) == 0) { FontType_RegisterFont(title, AM_GetHandle(fontTextureKey), AM_GetHandle(glyphDetailsKey)); return; } if (strstr(readLine, PKE_PROJECT_FONT_TITLE)) { uint64_t prefixLen = strlen(PKE_PROJECT_FONT_TITLE); title.length = strlen(readLine + prefixLen); title.reserved = title.length + 1; title.val = pk_new(title.reserved, pkeSettings.mem.bkt); strncpy(const_cast(title.val), readLine + prefixLen, title.reserved); continue; } if (strstr(readLine, PKE_PROJECT_FONT_TEXTURE_HANDLE)) { uint64_t prefixLen = strlen(PKE_PROJECT_FONT_TEXTURE_HANDLE); 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); for (i = 0; i < AssetKeyLength; ++i) { glyphDetailsKey[i] = readLine[prefixLen + i]; } continue; } } } FontType* FontType_GetFonts(FontTypeIndex &count) { count = ftd.h_ft; return ftd.arr_ft; } FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle, AssetHandle glyphsHandle) { PKVK_TmpBufferDetails tmpBufferDetails{}; VkResult vkResult; pk_arr arr_vert_mem_reqs; memset(&arr_vert_mem_reqs, 0, sizeof(pk_arr)); arr_vert_mem_reqs.alignment = alignof(VkMemoryRequirements); arr_vert_mem_reqs.stride = sizeof(VkMemoryRequirements); pk_arr arr_inst_mem_reqs(arr_vert_mem_reqs); pk_arr arr_texture_mem_reqs(arr_vert_mem_reqs); VkDeviceSize index; constexpr VkDeviceSize expectedBufferCount = 4; constexpr VkDeviceSize startingGlyphCount = 4; assert(fontTextureHandle != AssetHandle_MAX); assert(glyphsHandle != AssetHandle_MAX); VkMemoryRequirements combined_vert_mem_reqs; VkMemoryRequirements combined_texture_mem_reqs; const Asset *fontTexture = AM_Get(fontTextureHandle); 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((FontTypeIndex_T)ftd.n_ft); memcpy(arr, ftd.arr_ft, sizeof(FontType) * (FontTypeIndex_T)idx); if (ftd.arr_ft != nullptr && ftd.arr_ft != CAFE_BABE(FontType)) pk_delete(ftd.arr_ft, (FontTypeIndex_T)idx); ftd.arr_ft = arr; } FontTypeIndex_T idx_t = (FontTypeIndex_T)idx; FontType *ft = &ftd.arr_ft[idx_t]; ft->title = title; ft->fontTextureAssetHandle = fontTextureHandle; ft->glyphDetailsAssetHandle = glyphsHandle; ft->renders = pk_new(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; ft->atlas_size.x = (float)txtr_x; ft->atlas_size.y = (float)txtr_y; /* * vulkan setup */ VkBuffer buffer; 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; /* * Vulkan resource setup */ // vertex { for (uint64_t idx = 0; idx < expectedBufferCount; ++idx) { if (idx == 0) { // vertex bufferCI.size = sizeof(glm::vec2) * 4; } else if (idx == 1) { // uv bufferCI.size = sizeof(glm::vec2) * 4; } else if (idx == 2) { // atlas size bufferCI.size = sizeof(glm::vec2) * 4; } else if (idx == 3) { // index bufferCI.size = sizeof(uint16_t) * 6; } if (idx == 3) { 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; } vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &buffer); VkMemoryRequirements tmp; vkGetBufferMemoryRequirements(vkDevice, buffer, &tmp); pk_arr_append(&arr_vert_mem_reqs, &tmp); vkDestroyBuffer(vkDevice, buffer, vkAllocator); } } // instance { bufferCI.size = 0; bufferCI.size += sizeof(glm::vec4); // fgColor bufferCI.size += sizeof(glm::vec4); // bgColor bufferCI.size += sizeof(glm::vec2); // sprite_region_min bufferCI.size += sizeof(glm::vec2); // sprite_region_max bufferCI.size += sizeof(float); // width bufferCI.size *= startingGlyphCount; bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &buffer); VkMemoryRequirements tmp; vkGetBufferMemoryRequirements(vkDevice, buffer, &tmp); pk_arr_append(&arr_inst_mem_reqs, &tmp); vkDestroyBuffer(vkDevice, buffer, vkAllocator); } // texture VkImageCreateInfo imageCreateInfo{}; VkImageSubresourceRange vkImageSubresourceRange; { imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.pNext = nullptr; imageCreateInfo.flags = 0; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; imageCreateInfo.extent.width = txtr_x; imageCreateInfo.extent.height = txtr_y; imageCreateInfo.extent.depth = 1; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; 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; imageCreateInfo.queueFamilyIndexCount = 0; imageCreateInfo.pQueueFamilyIndices = nullptr; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; vkImageSubresourceRange.aspectMask = VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT; vkImageSubresourceRange.baseMipLevel = 0u; vkImageSubresourceRange.levelCount = 1u; vkImageSubresourceRange.baseArrayLayer = 0u; vkImageSubresourceRange.layerCount = 1u; VkImage tmpImage; vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &tmpImage); assert(vkResult == VK_SUCCESS); VkMemoryRequirements imageMemoryRequirements; vkGetImageMemoryRequirements(vkDevice, tmpImage, &imageMemoryRequirements); pk_arr_append(&arr_texture_mem_reqs, &imageMemoryRequirements); vkDestroyImage(vkDevice, tmpImage, vkAllocator); } /* * Vulkan memory allocation */ VkMemoryAllocateInfo vkMemoryAllocateInfo; vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; vkMemoryAllocateInfo.pNext = nullptr; // verts CalculateCombinedMemReqs(arr_vert_mem_reqs.next, (VkMemoryRequirements*)arr_vert_mem_reqs.data, combined_vert_mem_reqs); vkMemoryAllocateInfo.allocationSize = combined_vert_mem_reqs.size; vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_vert_mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &ft->deviceMemoryVert); // 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) { vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_texture_mem_reqs.memoryTypeBits, 0); } vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &ft->deviceMemoryTexture); /* * Vulkan data transfer */ VkDeviceSize runningOffset = 0; VkDeviceSize alignmentPadding = 0; // vert PKVK_BeginBuffer(transferFamilyIndex, combined_vert_mem_reqs.size, tmpBufferDetails); { index = 0; runningOffset = 0; uint32_t offsetVert = runningOffset; uint32_t sizeVert = sizeof(pkeIntrinsicsPlane.vert[0]) * 4; ft->bindings.vertexBD.firstBinding = index; ft->bindings.vertexBD.bindingCount = 1; alignmentPadding = sizeVert % combined_vert_mem_reqs.alignment; alignmentPadding = alignmentPadding == 0 ? 0 : combined_vert_mem_reqs.alignment - alignmentPadding; sizeVert += alignmentPadding; bufferCI.size = sizeVert; bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &ft->bindings.vertexBD.buffer); vkBindBufferMemory(vkDevice, ft->bindings.vertexBD.buffer, ft->deviceMemoryVert, offsetVert); runningOffset += sizeVert; index += 1; // uv uint32_t offsetUV = runningOffset; uint32_t sizeUV = sizeof(pkeIntrinsicsPlane.uv[0]) * 4; ft->bindings.uvBD.firstBinding = index; ft->bindings.uvBD.bindingCount = 1; alignmentPadding = sizeUV % combined_vert_mem_reqs.alignment; alignmentPadding = alignmentPadding == 0 ? 0 : combined_vert_mem_reqs.alignment - alignmentPadding; sizeUV += alignmentPadding; bufferCI.size = sizeUV; vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &ft->bindings.uvBD.buffer); vkBindBufferMemory(vkDevice, ft->bindings.uvBD.buffer, ft->deviceMemoryVert, offsetUV); runningOffset += sizeUV; index += 1; // atlas_size uint32_t offsetAtlasSize = runningOffset; uint32_t sizeAtlasSize = sizeof(ft->atlas_size) * 4; ft->bindings.atlasSizeBD.firstBinding = index; ft->bindings.atlasSizeBD.bindingCount = 1; alignmentPadding = sizeAtlasSize % combined_vert_mem_reqs.alignment; alignmentPadding = alignmentPadding == 0 ? 0 : combined_vert_mem_reqs.alignment - alignmentPadding; sizeAtlasSize += alignmentPadding; bufferCI.size = sizeAtlasSize; vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &ft->bindings.atlasSizeBD.buffer); vkBindBufferMemory(vkDevice, ft->bindings.atlasSizeBD.buffer, ft->deviceMemoryVert, offsetAtlasSize); runningOffset += sizeAtlasSize; index += 1; // 2023-09-27 - JCB // I don't know where else to put this ft->bindings.instanceBD.firstBinding = index; ft->bindings.instanceBD.bindingCount = 1; // no index += 1 because index just happens to be the right value here for // the binding index, whereas binding the IndexBuffer doesn't need a binding index. // index uint32_t offsetIndex = runningOffset; uint32_t sizeIndex = sizeof(pkeIntrinsicsPlane.index[0]) * 6; ft->bindings.indexBD.bindingCount = 1; ft->bindings.indexCount = 6; alignmentPadding = sizeIndex % combined_vert_mem_reqs.alignment; alignmentPadding = alignmentPadding == 0 ? 0 : combined_vert_mem_reqs.alignment - alignmentPadding; sizeIndex += alignmentPadding; bufferCI.size = sizeIndex; bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT; vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &ft->bindings.indexBD.buffer); vkBindBufferMemory(vkDevice, ft->bindings.indexBD.buffer, ft->deviceMemoryVert, offsetIndex); runningOffset += sizeIndex; // index += 1; assert(runningOffset == combined_vert_mem_reqs.size); runningOffset = 0; char *dstPtr = nullptr; char *srcPtr = nullptr; dstPtr = static_cast(tmpBufferDetails.deviceData) + runningOffset; srcPtr = reinterpret_cast(pkeIntrinsicsPlane.vert); memcpy(dstPtr, srcPtr, sizeVert); runningOffset += sizeVert; dstPtr = static_cast(tmpBufferDetails.deviceData) + runningOffset; srcPtr = reinterpret_cast(pkeIntrinsicsPlane.uv); memcpy(dstPtr, srcPtr, sizeUV); runningOffset += sizeUV; glm::vec2 atlas_size_buffer[4]; for (int i = 0; i < 4; ++i) { atlas_size_buffer[i] = ft->atlas_size; } dstPtr = static_cast(tmpBufferDetails.deviceData) + runningOffset; srcPtr = reinterpret_cast(atlas_size_buffer); memcpy(dstPtr, srcPtr, sizeAtlasSize); runningOffset += sizeAtlasSize; dstPtr = static_cast(tmpBufferDetails.deviceData) + runningOffset; srcPtr = reinterpret_cast(pkeIntrinsicsPlane.index); memcpy(dstPtr, srcPtr, sizeIndex); // runningOffset += sizeIndex; 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 bufferCopys[expectedBufferCount]; for (VkDeviceSize i = 0; i < expectedBufferCount; ++i) { bufferCopys[i].dstOffset = 0; } index = 0; bufferCopys[index].srcOffset = offsetVert; bufferCopys[index].size = sizeVert; vkCmdCopyBuffer(tmpBufferDetails.cmdBuffer, tmpBufferDetails.buffer, ft->bindings.vertexBD.buffer, 1, &bufferCopys[index]); index+=1; bufferCopys[index].srcOffset = offsetUV; bufferCopys[index].size = sizeUV; vkCmdCopyBuffer(tmpBufferDetails.cmdBuffer, tmpBufferDetails.buffer, ft->bindings.uvBD.buffer, 1, &bufferCopys[index]); index+=1; bufferCopys[index].srcOffset = offsetAtlasSize; bufferCopys[index].size = sizeAtlasSize; vkCmdCopyBuffer(tmpBufferDetails.cmdBuffer, tmpBufferDetails.buffer, ft->bindings.atlasSizeBD.buffer, 1, &bufferCopys[index]); index+=1; bufferCopys[index].srcOffset = offsetIndex; bufferCopys[index].size = sizeIndex; vkCmdCopyBuffer(tmpBufferDetails.cmdBuffer, tmpBufferDetails.buffer, ft->bindings.indexBD.buffer, 1, &bufferCopys[index]); // index+=1; 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); // texture // transition image layout and copy to buffer tmpBufferDetails = {}; PKVK_BeginBuffer(transferFamilyIndex, combined_texture_mem_reqs.size, tmpBufferDetails); 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; vkImageMemoryBarrier.srcAccessMask = {}; vkImageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; vkImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; vkImageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarrier.image = ft->textureImage; vkImageMemoryBarrier.subresourceRange = VkImageSubresourceRange { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, // TODO MipMap .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }; VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkCommandBufferBeginInfo.pNext = nullptr; vkCommandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); vkCmdPipelineBarrier(tmpBufferDetails.cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &vkImageMemoryBarrier); VkBufferImageCopy vkBufferImageCopy; vkBufferImageCopy.bufferOffset = 0; vkBufferImageCopy.bufferRowLength = txtr_x; vkBufferImageCopy.bufferImageHeight = txtr_y; vkBufferImageCopy.imageSubresource = VkImageSubresourceLayers { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1, }; vkBufferImageCopy.imageOffset = VkOffset3D { .x = 0, .y = 0, .z = 0, }; vkBufferImageCopy.imageExtent = VkExtent3D { .width = static_cast(txtr_x), .height = static_cast(txtr_y), .depth = 1, }; vkCmdCopyBufferToImage(tmpBufferDetails.cmdBuffer, tmpBufferDetails.buffer, ft->textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &vkBufferImageCopy); 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); vkResetCommandBuffer(tmpBufferDetails.cmdBuffer, 0); } PKVK_EndBuffer(tmpBufferDetails); PKVK_BeginBuffer(graphicsFamilyIndex, 0, tmpBufferDetails); { VkImageMemoryBarrier vkImageMemoryBarrier; vkImageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; vkImageMemoryBarrier.pNext = nullptr; vkImageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkImageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; vkImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; vkImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkImageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarrier.image = ft->textureImage; vkImageMemoryBarrier.subresourceRange = VkImageSubresourceRange { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, // TODO MipMap .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }; VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkCommandBufferBeginInfo.pNext = nullptr; vkCommandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); vkCmdPipelineBarrier(tmpBufferDetails.cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &vkImageMemoryBarrier); 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); /* * Vulkan descriptor sets */ VkDescriptorPoolSize descriptorPoolSizes[1]; descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptorPoolSizes[0].descriptorCount = swapchainLength; VkDescriptorPoolCreateInfo vkDescriptorPoolCreateInfo; vkDescriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; vkDescriptorPoolCreateInfo.pNext = nullptr; vkDescriptorPoolCreateInfo.flags = 0; vkDescriptorPoolCreateInfo.maxSets = swapchainLength; vkDescriptorPoolCreateInfo.poolSizeCount = (uint32_t)1; vkDescriptorPoolCreateInfo.pPoolSizes = descriptorPoolSizes; // consider making me a global pool vkResult = vkCreateDescriptorPool(vkDevice, &vkDescriptorPoolCreateInfo, vkAllocator, &ft->vkDescriptorPool); assert(vkResult == VK_SUCCESS); VkDescriptorSetLayout *descriptorSets = pk_new(swapchainLength); for (long i = 0; i < swapchainLength; ++i) { descriptorSets[i] = pkePipelines.descr_layouts.named.glyph; } VkDescriptorSetAllocateInfo vkDescriptorSetAllocateInfo; vkDescriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; vkDescriptorSetAllocateInfo.pNext = nullptr; vkDescriptorSetAllocateInfo.descriptorPool = ft->vkDescriptorPool; vkDescriptorSetAllocateInfo.descriptorSetCount = swapchainLength; vkDescriptorSetAllocateInfo.pSetLayouts = descriptorSets; ft->vkDescriptorSets = pk_new(swapchainLength); for (long i = 0; i < swapchainLength; ++i) { ft->vkDescriptorSets[i] = VkDescriptorSet{}; } vkResult = vkAllocateDescriptorSets(vkDevice, &vkDescriptorSetAllocateInfo, ft->vkDescriptorSets); pk_delete(descriptorSets, swapchainLength); assert(vkResult == VK_SUCCESS); VkWriteDescriptorSet *writeDescriptorSets = pk_new(swapchainLength); 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 = 0; writeDescriptorSets[i].dstArrayElement = 0; writeDescriptorSets[i].descriptorCount = 1; writeDescriptorSets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[i].pImageInfo = nullptr; writeDescriptorSets[i].pBufferInfo = nullptr; writeDescriptorSets[i].pTexelBufferView = nullptr; } VkDescriptorImageInfo textureDescriptorInfo; textureDescriptorInfo.sampler = global_sampler; textureDescriptorInfo.imageView = ft->textureImageView; textureDescriptorInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkDescriptorBufferInfo *vkDescriptorBufferInfo = pk_new(swapchainLength); for (long i = 0; i < swapchainLength; ++i) { vkDescriptorBufferInfo[i].buffer = UniformBuffers[i]; vkDescriptorBufferInfo[i].offset = 0; vkDescriptorBufferInfo[i].range = sizeof(UniformBufferObject); writeDescriptorSets[i].pImageInfo = &textureDescriptorInfo; writeDescriptorSets[i].dstSet = ft->vkDescriptorSets[i]; } vkUpdateDescriptorSets(vkDevice, swapchainLength, writeDescriptorSets, 0, nullptr); pk_delete(vkDescriptorBufferInfo, swapchainLength); pk_delete(writeDescriptorSets, swapchainLength); AM_Release(fontTextureHandle); AM_Release(glyphsHandle); return idx; } void FontType_Unload(FontTypeIndex idx) { FontType *ft = &ftd.arr_ft[(FontTypeIndex_T)idx]; if (ft->vkDescriptorSets != VK_NULL_HANDLE && ft->vkDescriptorPool != VK_NULL_HANDLE) { // 2023-09-27 - JCB (copied from entities.cpp) // We are not setting the pool flag for allowing freeing descriptor sets // so all we need to do is destroy the pool // If we switch to a global pool, we will need to free here, and // destroy the pool outside of this loop vkDestroyDescriptorPool(vkDevice, ft->vkDescriptorPool, vkAllocator); pk_delete(ft->vkDescriptorSets, swapchainLength); ft->vkDescriptorSets = CAFE_BABE(VkDescriptorSet); ft->vkDescriptorPool = VK_NULL_HANDLE; } if (ft->bindings.vertexBD.buffer != VK_NULL_HANDLE) vkDestroyBuffer(vkDevice, ft->bindings.vertexBD.buffer, vkAllocator); ft->bindings.vertexBD.buffer = VK_NULL_HANDLE; ft->bindings.vertexBD.firstBinding = 0; ft->bindings.vertexBD.bindingCount = 0; ft->bindings.vertexBD.offsets[0] = 0; if (ft->bindings.uvBD.buffer != VK_NULL_HANDLE) vkDestroyBuffer(vkDevice, ft->bindings.uvBD.buffer, vkAllocator); ft->bindings.uvBD.buffer = VK_NULL_HANDLE; ft->bindings.uvBD.firstBinding = 0; ft->bindings.uvBD.bindingCount = 0; ft->bindings.uvBD.offsets[0] = 0; if (ft->bindings.atlasSizeBD.buffer != VK_NULL_HANDLE) vkDestroyBuffer(vkDevice, ft->bindings.atlasSizeBD.buffer, vkAllocator); ft->bindings.atlasSizeBD.buffer = VK_NULL_HANDLE; ft->bindings.atlasSizeBD.firstBinding = 0; ft->bindings.atlasSizeBD.bindingCount = 0; ft->bindings.atlasSizeBD.offsets[0] = 0; if (ft->bindings.indexBD.buffer != VK_NULL_HANDLE) vkDestroyBuffer(vkDevice, ft->bindings.indexBD.buffer, vkAllocator); ft->bindings.indexBD.buffer = VK_NULL_HANDLE; ft->bindings.indexBD.bindingCount = 0; ft->bindings.indexBD.offsets[0] = 0; ft->bindings.indexCount = 0; if (ft->bindings.instanceBD.buffer != VK_NULL_HANDLE) vkDestroyBuffer(vkDevice, ft->bindings.instanceBD.buffer, vkAllocator); ft->bindings.instanceBD.buffer = VK_NULL_HANDLE; ft->bindings.instanceBD.firstBinding = 0; ft->bindings.instanceBD.bindingCount = 0; ft->bindings.instanceCounter = 0; ft->bindings.instanceBD.offsets[0] = 0; if (ft->textureImageView != VK_NULL_HANDLE) vkDestroyImageView(vkDevice, ft->textureImageView, vkAllocator); ft->textureImageView = VK_NULL_HANDLE; if (ft->textureImage != VK_NULL_HANDLE) vkDestroyImage(vkDevice, ft->textureImage, vkAllocator); ft->textureImage = VK_NULL_HANDLE; if (ft->deviceMemoryInst != VK_NULL_HANDLE) vkFreeMemory(vkDevice, ft->deviceMemoryInst, vkAllocator); ft->deviceMemoryInst = VK_NULL_HANDLE; if (ft->deviceMemoryVert != VK_NULL_HANDLE) vkFreeMemory(vkDevice, ft->deviceMemoryVert, vkAllocator); ft->deviceMemoryVert = VK_NULL_HANDLE; if (ft->deviceMemoryTexture != VK_NULL_HANDLE) vkFreeMemory(vkDevice, ft->deviceMemoryTexture, vkAllocator); ft->deviceMemoryTexture = VK_NULL_HANDLE; if (ft->title.reserved != 0 && ft->title.val != nullptr) { pk_delete(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 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((FontRenderIndex_T)ft->n_render); memcpy(arr, ft->renders, sizeof(FontRender) * (FontRenderIndex_T)idx_fr); pk_delete(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, 1); buf_item->bg_color = glm::vec4(0.0, 0.0, 0.0, 1); buf_item->sprite_region_min = fgc->sprite_region_min; buf_item->sprite_region_max = fgc->sprite_region_max; buf_item->width = 2; } } VkDeviceSize item_count_orig = ft->bindings.instanceCounter; VkDeviceSize item_count_new = ft->bindings.instanceCounter + 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.instanceCounter = 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]; }