From 4042bbd62362831529beb672e49792f1006b019a Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Wed, 20 May 2026 19:12:09 -0500 Subject: [PATCH] font: render font texture --- Makefile | 3 +- data/font/outline/uncial_antiqua_36.data | Bin 0 -> 67484 bytes filenames.txt | 3 + include/font/outline.h | 79 ++++ include/font/outline_types.h | 49 +++ include/minecraft/vulkan.h | 8 +- include/vulkan_helper.h | 39 ++ shader/font.hlsl | 32 ++ src/font/outline.cpp | 481 +++++++++++++++++++++++ src/main.cpp | 27 +- src/minecraft/vulkan.cpp | 53 +-- src/vulkan_helper.cpp | 62 +++ 12 files changed, 777 insertions(+), 59 deletions(-) create mode 100644 data/font/outline/uncial_antiqua_36.data create mode 100644 include/font/outline.h create mode 100644 include/font/outline_types.h create mode 100644 shader/font.hlsl create mode 100644 src/font/outline.cpp diff --git a/Makefile b/Makefile index eeda30c..f44f3b4 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,8 @@ OBJS = \ src/minecraft/world.o \ src/minecraft/entry_table.o \ src/minecraft/vulkan.o \ - src/minecraft/vulkan/per_world.o + src/minecraft/vulkan/per_world.o \ + src/font/outline.o WORLDS = \ data/minecraft/midnightmeadow/inthash.o \ diff --git a/data/font/outline/uncial_antiqua_36.data b/data/font/outline/uncial_antiqua_36.data new file mode 100644 index 0000000000000000000000000000000000000000..90e468ec7191858217d774d08728957ce4cf178e GIT binary patch literal 67484 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 Njq?7lsCrD4{67;E(d7UD literal 0 HcmV?d00001 diff --git a/filenames.txt b/filenames.txt index fb4c619..de3e097 100644 --- a/filenames.txt +++ b/filenames.txt @@ -35,3 +35,6 @@ data/minecraft/midnightmeadow/global.dump data/minecraft/midnightmeadow/global.lights.vtx data/minecraft/terrain2.dds + +shader/font.spv +data/font/outline/uncial_antiqua_36.data diff --git a/include/font/outline.h b/include/font/outline.h new file mode 100644 index 0000000..7295189 --- /dev/null +++ b/include/font/outline.h @@ -0,0 +1,79 @@ +#pragma once + +#include "outline_types.h" +#include "vulkan_helper.h" + +namespace font::outline { + + struct font_desc { + char const * const path; + }; + + font_desc const uncial_antiqua[] = { + { + .path = "data/font/outline/uncial_antiqua_36.data", + }, + }; + int const uncial_antiqua_length = (sizeof (uncial_antiqua)) / (sizeof (font_desc)); + + struct AllocatedImage { + VkImage image; + VkDeviceMemory memory; + VkImageView imageView; + }; + + struct LoadedFont { + types::font * font; + types::glyph * glyphs; + AllocatedImage allocatedImage; + }; + + struct font { + static constexpr int perVertexSize = (4) * 2; + static constexpr int perInstanceSize = (0) * 2; + + VkInstance instance; + VkDevice device; + VkQueue queue; + VkCommandPool commandPool; + VkPhysicalDeviceProperties physicalDeviceProperties; + VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties; + VkFormat colorFormat; + VkFormat depthFormat; + VkSampler linearSampler; + + // font-specific state + VkPipelineLayout pipelineLayout; + VkShaderModule shaderModule; + VkPipeline pipeline; + VertexIndex vertexIndex; + LoadedFont loadedFont; + + VkDescriptorPool descriptorPool{ VK_NULL_HANDLE }; + static constexpr int descriptorSetLayoutCount = 1; + VkDescriptorSetLayout descriptorSetLayouts[descriptorSetLayoutCount]; // unrelated to maxFrames, unrelated to descriptorCount + VkDescriptorSet descriptorSet0; + + void initial_state(VkInstance instance, + VkDevice device, + VkQueue queue, + VkCommandPool commandPool, + VkPhysicalDeviceProperties physicalDeviceProperties, + VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties, + VkFormat colorFormat, + VkFormat depthFormat, + VkSampler linearSampler); + + void init(); + + void load_vertex_index_buffer(); + void load_shader(); + void create_descriptor_sets(); + void write_descriptor_sets(VkImageView fontImageView); + void create_pipeline(); + void draw(VkCommandBuffer commandBuffer, + uint32_t frameIndex); + + LoadedFont load_font(font_desc const& desc); + }; +} diff --git a/include/font/outline_types.h b/include/font/outline_types.h new file mode 100644 index 0000000..de4a3c7 --- /dev/null +++ b/include/font/outline_types.h @@ -0,0 +1,49 @@ +// this file is designed to be platform-agnostic +#pragma once + +#include + +namespace font::outline::types { + + // metrics are 26.6 fixed point + struct glyph_metrics { + int32_t horiBearingX; + int32_t horiBearingY; + int32_t horiAdvance; + }; + + static_assert((sizeof (struct glyph_metrics)) == ((sizeof (int32_t)) * 3)); + + struct glyph_bitmap { + uint16_t x; + uint16_t y; + uint16_t width; + uint16_t height; + }; + + static_assert((sizeof (struct glyph_bitmap)) == ((sizeof (uint16_t)) * 4)); + + struct glyph { + struct glyph_bitmap bitmap; + struct glyph_metrics metrics; + }; + + static_assert((sizeof (struct glyph)) == ((sizeof (struct glyph_bitmap)) + (sizeof (struct glyph_metrics)))); + + struct font { + uint32_t first_char_code; + uint32_t last_char_code; + struct face_metrics { + int32_t height; // 26.6 fixed point + int32_t max_advance; // 26.6 fixed point + } face_metrics; + uint16_t glyph_count; + uint16_t _texture_stride; + uint16_t texture_width; + uint16_t texture_height; + uint32_t max_z_curve_ix; + }; + + static_assert((sizeof (struct font)) == ((sizeof (uint32_t)) * 7)); + +} diff --git a/include/minecraft/vulkan.h b/include/minecraft/vulkan.h index 46b2625..83c11db 100644 --- a/include/minecraft/vulkan.h +++ b/include/minecraft/vulkan.h @@ -3,6 +3,7 @@ #include "directxmath/directxmath.h" #include "volk/volk.h" +#include "vulkan_helper.h" #include "minecraft/vulkan/per_world.h" namespace minecraft::vulkan { @@ -24,12 +25,7 @@ namespace minecraft::vulkan { static constexpr int perVertexSize = (3 + 3 + 2) * 2; static constexpr int perInstanceSize = (3 + 1 + 3 + 1) * 2; - struct { - VkDeviceSize jointWeightOffset; - VkDeviceSize indexOffset; - VkBuffer buffer; - VkDeviceMemory memory; - } vertexIndex; + VertexIndex vertexIndex; // externally initialized, opaque handle VkInstance instance; diff --git a/include/vulkan_helper.h b/include/vulkan_helper.h index 9b3be79..78e3f0e 100644 --- a/include/vulkan_helper.h +++ b/include/vulkan_helper.h @@ -73,3 +73,42 @@ void createImageFromFilenameTGA(VkDevice device, VkImage * outImage, VkDeviceMemory * outMemory, VkImageView * outImageView); + +void createImage(VkDevice device, + VkDeviceSize nonCoherentAtomSize, + VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, + VkFormat format, + uint32_t width, + uint32_t height, + uint32_t levelCount, + VkImage * outImage, + VkDeviceMemory * outMemory, + VkImageView * outImageView); + +void textureTransfer(VkDevice device, + VkQueue queue, + VkCommandBuffer commandBuffer, + VkFence fence, + VkDeviceSize nonCoherentAtomSize, + VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, + uint32_t imageDataSize, + void * imageData, + VkImage image, + uint32_t width, + uint32_t height, + uint32_t levelCount, + uint32_t * levelOffsets); + +struct VertexIndex { + VkDeviceSize indexOffset; + VkBuffer buffer; + VkDeviceMemory memory; +}; + +VertexIndex createVertexIndexBuffer(VkDevice device, + VkPhysicalDeviceProperties const& physicalDeviceProperties, + VkPhysicalDeviceMemoryProperties const& physicalDeviceMemoryProperties, + void const * vertexStart, + uint32_t vertexSize, + void const * indexStart, + uint32_t indexSize); diff --git a/shader/font.hlsl b/shader/font.hlsl new file mode 100644 index 0000000..035e729 --- /dev/null +++ b/shader/font.hlsl @@ -0,0 +1,32 @@ +// set 1: constant +[[vk::binding(0, 0)]] SamplerState ClosestSampler; +[[vk::binding(1, 0)]] Texture2D FontTexture; + +struct VSInput +{ + float2 Position : POSITION0; + float2 Texture : TEXCOORD0; +}; + +struct VSOutput +{ + float4 Position : SV_POSITION; + float2 Texture : NORMAL0; +}; + +[shader("vertex")] +VSOutput VSMain(VSInput input) +{ + VSOutput output = (VSOutput)0; + output.Position = float4(input.Position, 0, 1); + output.Texture = input.Texture; + + return output; +} + +[shader("pixel")] +float4 PSMain(VSOutput input) : SV_TARGET +{ + float4 color = FontTexture.Sample(ClosestSampler, input.Texture); + return float4(color.xxx, 1.0); +} diff --git a/src/font/outline.cpp b/src/font/outline.cpp new file mode 100644 index 0000000..27defea --- /dev/null +++ b/src/font/outline.cpp @@ -0,0 +1,481 @@ +#include +#include +#include + +#include "volk/volk.h" +#include "vulkan/vk_enum_string_helper.h" + +#include "directxmath/directxmath.h" +#include "vulkan_helper.h" +#include "check.h" +#include "file.h" + +#include "font/outline.h" +#include "font/outline_types.h" + +namespace font::outline { + static const _Float16 vertexData[] = { + // x y u v + (_Float16)-1.0, (_Float16)-1.0, (_Float16)0.0, (_Float16)0.0, + (_Float16)1.0, (_Float16)-1.0, (_Float16)1.0, (_Float16)0.0, + (_Float16)-1.0, (_Float16)1.0, (_Float16)0.0, (_Float16)1.0, + (_Float16)1.0, (_Float16)1.0, (_Float16)1.0, (_Float16)1.0, + }; + static const uint32_t vertexSize = (sizeof (vertexData)); + + static const uint16_t indexData[] = { + 0, 1, 2, 3, + }; + static const uint32_t indexSize = (sizeof (indexData)); + + void font::initial_state(VkInstance instance, + VkDevice device, + VkQueue queue, + VkCommandPool commandPool, + VkPhysicalDeviceProperties physicalDeviceProperties, + VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties, + VkFormat colorFormat, + VkFormat depthFormat, + VkSampler linearSampler) + { + this->instance = instance; + this->device = device; + this->queue = queue; + this->commandPool = commandPool; + + this->physicalDeviceProperties = physicalDeviceProperties; + this->physicalDeviceMemoryProperties = physicalDeviceMemoryProperties; + + this->colorFormat = colorFormat; + this->depthFormat = depthFormat; + + this->linearSampler = linearSampler; + } + + void font::init() + { + load_vertex_index_buffer(); + load_shader(); + create_descriptor_sets(); + loadedFont = load_font(uncial_antiqua[0]); + write_descriptor_sets(loadedFont.allocatedImage.imageView); + create_pipeline(); + } + + ////////////////////////////////////////////////////////////////////// + // vertex index buffer + ////////////////////////////////////////////////////////////////////// + + void font::load_vertex_index_buffer() + { + void const * vertexStart = (void const *)vertexData; + void const * indexStart = (void const *)indexData; + + vertexIndex = createVertexIndexBuffer(device, + physicalDeviceProperties, + physicalDeviceMemoryProperties, + vertexStart, vertexSize, + indexStart, indexSize); + } + + ////////////////////////////////////////////////////////////////////// + // shader + ////////////////////////////////////////////////////////////////////// + + void font::load_shader() + { + uint32_t shaderSize; + void const * shaderStart = file::open("shader/font.spv", &shaderSize); + + VkShaderModuleCreateInfo shaderModuleCreateInfo{ + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .codeSize = shaderSize, + .pCode = (uint32_t *)shaderStart + }; + VK_CHECK(vkCreateShaderModule(device, &shaderModuleCreateInfo, nullptr, &shaderModule)); + } + + ////////////////////////////////////////////////////////////////////// + // pipeline + ////////////////////////////////////////////////////////////////////// + + void font::create_pipeline() + { + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = descriptorSetLayoutCount, + .pSetLayouts = descriptorSetLayouts, + .pushConstantRangeCount = 0, + .pPushConstantRanges = nullptr + }; + VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP + }; + + VkPipelineShaderStageCreateInfo shaderStages[2]{ + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = shaderModule, + .pName = "VSMain" + }, + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = shaderModule, + .pName = "PSMain" + } + }; + + VkPipelineViewportStateCreateInfo viewportState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .scissorCount = 1 + }; + + constexpr uint32_t dynamicStateCount = 2; + VkDynamicState dynamicStates[dynamicStateCount]{ + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + VkPipelineDynamicStateCreateInfo dynamicState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .dynamicStateCount = dynamicStateCount, + .pDynamicStates = dynamicStates + }; + + VkPipelineDepthStencilStateCreateInfo depthStencilState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .depthTestEnable = VK_FALSE, + .depthWriteEnable = VK_FALSE, + .depthCompareOp = VK_COMPARE_OP_ALWAYS, + .stencilTestEnable = VK_FALSE, + .front = { + .failOp = VK_STENCIL_OP_REPLACE, + .passOp = VK_STENCIL_OP_REPLACE, + .depthFailOp = VK_STENCIL_OP_REPLACE, + .compareOp = VK_COMPARE_OP_ALWAYS, + .compareMask = 0x01, + .writeMask = 0x01, + .reference = 1, + }, + .back = { + .failOp = VK_STENCIL_OP_REPLACE, + .passOp = VK_STENCIL_OP_REPLACE, + .depthFailOp = VK_STENCIL_OP_REPLACE, + .compareOp = VK_COMPARE_OP_ALWAYS, + .compareMask = 0x01, + .writeMask = 0x01, + .reference = 1, + }, + }; + + VkPipelineRenderingCreateInfo renderingCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + .colorAttachmentCount = 1, + .pColorAttachmentFormats = &colorFormat, + .depthAttachmentFormat = depthFormat, + .stencilAttachmentFormat = depthFormat + }; + + VkPipelineColorBlendAttachmentState blendAttachment{ + .colorWriteMask = 0xF + }; + VkPipelineColorBlendStateCreateInfo colorBlendState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .attachmentCount = 1, + .pAttachments = &blendAttachment + }; + VkPipelineRasterizationStateCreateInfo rasterizationState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + //.cullMode = VK_CULL_MODE_BACK_BIT, + .cullMode = VK_CULL_MODE_NONE, + .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, + .lineWidth = 1.0f + }; + VkPipelineMultisampleStateCreateInfo multisampleState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT + }; + + VkVertexInputBindingDescription vertexBindingDescriptions[2]{ + { + .binding = 0, + .stride = perVertexSize, + .inputRate = VK_VERTEX_INPUT_RATE_VERTEX + }, + { + .binding = 1, + .stride = perInstanceSize, + .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE + } + }; + + VkVertexInputAttributeDescription vertexAttributeDescriptions[2]{ + // per-vertex + { // position + .location = 0, + .binding = 0, + .format = VK_FORMAT_R16G16_SFLOAT, + .offset = 0, + }, + { // texture + .location = 1, + .binding = 0, + .format = VK_FORMAT_R16G16_SFLOAT, + .offset = 4, + }, + }; + + VkPipelineVertexInputStateCreateInfo vertexInputState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + //.vertexBindingDescriptionCount = 2, + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = vertexBindingDescriptions, + .vertexAttributeDescriptionCount = 2, + .pVertexAttributeDescriptions = vertexAttributeDescriptions, + }; + + VkGraphicsPipelineCreateInfo pipelineCreateInfos[1]{ + { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = &renderingCreateInfo, + .stageCount = 2, + .pStages = shaderStages, + .pVertexInputState = &vertexInputState, + .pInputAssemblyState = &inputAssemblyState, + .pViewportState = &viewportState, + .pRasterizationState = &rasterizationState, + .pMultisampleState = &multisampleState, + .pDepthStencilState = &depthStencilState, + .pColorBlendState = &colorBlendState, + .pDynamicState = &dynamicState, + .layout = pipelineLayout + }, + }; + + VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pipelineCreateInfos, nullptr, &pipeline)); + } + + ////////////////////////////////////////////////////////////////////// + // load font + ////////////////////////////////////////////////////////////////////// + + LoadedFont font::load_font(font_desc const& desc) + { + uint32_t font_data_size; + void const * font_data = file::open(desc.path, &font_data_size); + assert(font_data != nullptr); + + types::font * font = (types::font *)font_data; + types::glyph * glyphs = (types::glyph *)(((ptrdiff_t)font_data) + (sizeof (types::font))); + + void * texture_data = (void *)(((ptrdiff_t)glyphs) + (sizeof (types::glyph)) * font->glyph_count); + + ptrdiff_t font_end = ((ptrdiff_t)font_data) + font_data_size; + int texture_size = font->texture_width * font->texture_height; + assert(font_end - ((ptrdiff_t)texture_data) == texture_size); + + // transfer texture + + VkCommandBuffer commandBuffer{}; + VkCommandBufferAllocateInfo commandBufferAllocateInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = commandPool, + .commandBufferCount = 1 + }; + VK_CHECK(vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffer)); + + VkFenceCreateInfo fenceCreateInfo{ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO + }; + VkFence fence{}; + VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &fence)); + + void * imageData = texture_data; + uint32_t imageDataSize = texture_size; + VkFormat format = VK_FORMAT_R8_UNORM; + uint32_t width = font->texture_width; + uint32_t height = font->texture_height; + uint32_t levelCount = 1; + uint32_t levelOffset = 0; + VkImage outImage; + VkDeviceMemory outMemory; + VkImageView outImageView; + + createImage(device, + physicalDeviceProperties.limits.nonCoherentAtomSize, + physicalDeviceMemoryProperties, + format, + width, + height, + levelCount, + &outImage, + &outMemory, + &outImageView); + + textureTransfer(device, + queue, + commandBuffer, + fence, + physicalDeviceProperties.limits.nonCoherentAtomSize, + physicalDeviceMemoryProperties, + imageDataSize, + imageData, + outImage, + width, + height, + levelCount, + &levelOffset); + + vkDestroyFence(device, fence, nullptr); + vkFreeCommandBuffers(device, + commandPool, + 1, + &commandBuffer); + + // return + return { + .font = font, + .glyphs = glyphs, + .allocatedImage = { + .image = outImage, + .memory = outMemory, + .imageView = outImageView, + }, + }; + } + + ////////////////////////////////////////////////////////////////////// + // descriptor sets + ////////////////////////////////////////////////////////////////////// + + void font::create_descriptor_sets() + { + // + // pool + // + constexpr int descriptorPoolSizesCount = 2; + VkDescriptorPoolSize descriptorPoolSizes[descriptorPoolSizesCount]{ + { // linear sampler + .type = VK_DESCRIPTOR_TYPE_SAMPLER, + .descriptorCount = 1, + }, + { + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .descriptorCount = 1, + } + }; + VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = 1, + .poolSizeCount = descriptorPoolSizesCount, + .pPoolSizes = descriptorPoolSizes + }; + VK_CHECK(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool)); + + // + // (set 0, constant) + // + { + constexpr int bindingCount = 2; + VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[bindingCount]{ + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT + }, + { // font image + .binding = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT + } + }; + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = bindingCount, + .pBindings = descriptorSetLayoutBindings + }; + VK_CHECK(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayouts[0])); + + VkDescriptorSetAllocateInfo descriptorSetAllocateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = descriptorPool, + .descriptorSetCount = 1, + .pSetLayouts = &descriptorSetLayouts[0] + }; + VK_CHECK(vkAllocateDescriptorSets(device, &descriptorSetAllocateInfo, &descriptorSet0)); + } + } + + ////////////////////////////////////////////////////////////////////// + // descriptor set writes + ////////////////////////////////////////////////////////////////////// + + void font::write_descriptor_sets(VkImageView fontImageView) + { + constexpr uint32_t writeCount = 2; + VkWriteDescriptorSet writeDescriptorSets[writeCount]; + uint32_t writeIndex = 0; + + // set1 bindings + VkDescriptorImageInfo samplerDescriptorImageInfo = { + .sampler = linearSampler, + }; + writeDescriptorSets[writeIndex++] = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptorSet0, + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, + .pImageInfo = &samplerDescriptorImageInfo + }; + VkDescriptorImageInfo terrainDescriptorImageInfo = { + .imageView = fontImageView, + .imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL + }; + writeDescriptorSets[writeIndex++] = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptorSet0, + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .pImageInfo = &terrainDescriptorImageInfo + }; + + assert(writeIndex == writeCount); + vkUpdateDescriptorSets(device, writeIndex, writeDescriptorSets, 0, nullptr); + } + + ////////////////////////////////////////////////////////////////////// + // draw + ////////////////////////////////////////////////////////////////////// + + void font::draw(VkCommandBuffer commandBuffer, + uint32_t frameIndex) + { + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + VkDescriptorSet descriptorSets[1] = { + descriptorSet0, + }; + vkCmdBindDescriptorSets(commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + pipelineLayout, + 0, 1, descriptorSets, + 0, nullptr); + + vkCmdBindIndexBuffer(commandBuffer, vertexIndex.buffer, vertexIndex.indexOffset, VK_INDEX_TYPE_UINT16); + + VkDeviceSize vertexOffset{ 0 }; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexIndex.buffer, &vertexOffset); + + vkCmdDrawIndexed(commandBuffer, 4, 1, 0, 0, 0); + } +} diff --git a/src/main.cpp b/src/main.cpp index 2c72290..d4e59af 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,7 @@ #include "collada/scene/vulkan.h" #include "minecraft/vulkan.h" +#include "font/outline.h" #include "scenes/shadow_test/shadow_test.h" #include "scenes/eidelwind/eidelwind.h" @@ -678,6 +679,22 @@ int main() shadowDepthImageViewDepth); minecraft_state.init(); + ////////////////////////////////////////////////////////////////////// + // initialize font + ////////////////////////////////////////////////////////////////////// + + font::outline::font font_state; + font_state.initial_state(instance, + device, + queue, + commandPool, + physicalDeviceProperties, + physicalDeviceMemoryProperties, + surfaceFormat.format, + depthFormat, + textureSamplers[2]); + font_state.init(); + ////////////////////////////////////////////////////////////////////// // initialize view ////////////////////////////////////////////////////////////////////// @@ -798,7 +815,7 @@ int main() // transfer ////////////////////////////////////////////////////////////////////// - { + if (0) { collada_state.vulkan.change_frame(commandBuffer, frameIndex); XMMATRIX projection = currentProjection(); @@ -901,7 +918,7 @@ int main() collada_state.vulkan.excludeMaterialIndex = lightMaterialIndex; collada_state.vulkan.pipelineIndex = 0; // shadow pipeline - collada_state.draw(); + //collada_state.draw(); vkCmdEndRendering(commandBuffer); @@ -1022,11 +1039,13 @@ int main() collada_state.vulkan.excludeMaterialIndex = -1; collada_state.vulkan.pipelineIndex = 1; // non-shadow pipeline - collada_state.draw(); + //collada_state.draw(); //collada_state.vulkan.pipelineIndex = 2; // geometry shader pipeline //collada_state.draw(); - minecraft_state.draw(commandBuffer, frameIndex); + //minecraft_state.draw(commandBuffer, frameIndex); + + font_state.draw(commandBuffer, frameIndex); vkCmdEndRendering(commandBuffer); diff --git a/src/minecraft/vulkan.cpp b/src/minecraft/vulkan.cpp index db390a7..163bfe8 100644 --- a/src/minecraft/vulkan.cpp +++ b/src/minecraft/vulkan.cpp @@ -72,54 +72,11 @@ namespace minecraft::vulkan { uint32_t indexSize; void const * indexStart = file::open(index_filename, &indexSize); - vertexIndex.indexOffset = vertexSize; - - // create buffer - - VkDeviceSize bufferSize{ vertexSize + indexSize }; - VkBufferCreateInfo vertexIndexBufferCreateInfo{ - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .size = bufferSize, - .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE - }; - VK_CHECK(vkCreateBuffer(device, &vertexIndexBufferCreateInfo, nullptr, &vertexIndex.buffer)); - - // allocate memory - - VkMemoryRequirements memoryRequirements; - vkGetBufferMemoryRequirements(device, vertexIndex.buffer, &memoryRequirements); - VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; - VkMemoryAllocateFlags memoryAllocateFlags{}; - VkDeviceSize stride; - allocateFromMemoryRequirements(device, - physicalDeviceProperties.limits.nonCoherentAtomSize, - physicalDeviceMemoryProperties, - memoryRequirements, - memoryPropertyFlags, - memoryAllocateFlags, - 1, - &vertexIndex.memory, - &stride); - - VK_CHECK(vkBindBufferMemory(device, vertexIndex.buffer, vertexIndex.memory, 0)); - - // copy data - - void * vertexIndexMappedData; - VK_CHECK(vkMapMemory(device, vertexIndex.memory, 0, VK_WHOLE_SIZE, 0, &vertexIndexMappedData)); - memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + 0), vertexStart, vertexSize); - memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + vertexSize), indexStart, indexSize); - - VkMappedMemoryRange mappedMemoryRange{ - .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, - .memory = vertexIndex.memory, - .offset = 0, - .size = VK_WHOLE_SIZE, - }; - vkFlushMappedMemoryRanges(device, 1, &mappedMemoryRange); - - vkUnmapMemory(device, vertexIndex.memory); + vertexIndex = createVertexIndexBuffer(device, + physicalDeviceProperties, + physicalDeviceMemoryProperties, + vertexStart, vertexSize, + indexStart, indexSize); } ////////////////////////////////////////////////////////////////////// diff --git a/src/vulkan_helper.cpp b/src/vulkan_helper.cpp index 7669599..824fa67 100644 --- a/src/vulkan_helper.cpp +++ b/src/vulkan_helper.cpp @@ -418,3 +418,65 @@ void createImageFromFilenameTGA(VkDevice device, free(imageStart); } + +VertexIndex createVertexIndexBuffer(VkDevice device, + VkPhysicalDeviceProperties const& physicalDeviceProperties, + VkPhysicalDeviceMemoryProperties const& physicalDeviceMemoryProperties, + void const * vertexStart, + uint32_t vertexSize, + void const * indexStart, + uint32_t indexSize) +{ + VertexIndex vertexIndex{}; + + vertexIndex.indexOffset = vertexSize; + + // create buffer + + VkDeviceSize bufferSize{ vertexSize + indexSize }; + VkBufferCreateInfo vertexIndexBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = bufferSize, + .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + VK_CHECK(vkCreateBuffer(device, &vertexIndexBufferCreateInfo, nullptr, &vertexIndex.buffer)); + + // allocate memory + + VkMemoryRequirements memoryRequirements; + vkGetBufferMemoryRequirements(device, vertexIndex.buffer, &memoryRequirements); + VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; + VkMemoryAllocateFlags memoryAllocateFlags{}; + VkDeviceSize stride; + allocateFromMemoryRequirements(device, + physicalDeviceProperties.limits.nonCoherentAtomSize, + physicalDeviceMemoryProperties, + memoryRequirements, + memoryPropertyFlags, + memoryAllocateFlags, + 1, + &vertexIndex.memory, + &stride); + + VK_CHECK(vkBindBufferMemory(device, vertexIndex.buffer, vertexIndex.memory, 0)); + + // copy data + + void * vertexIndexMappedData; + VK_CHECK(vkMapMemory(device, vertexIndex.memory, 0, VK_WHOLE_SIZE, 0, &vertexIndexMappedData)); + memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + 0), vertexStart, vertexSize); + memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + vertexSize), indexStart, indexSize); + + VkMappedMemoryRange mappedMemoryRange{ + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = vertexIndex.memory, + .offset = 0, + .size = VK_WHOLE_SIZE, + }; + vkFlushMappedMemoryRanges(device, 1, &mappedMemoryRange); + + vkUnmapMemory(device, vertexIndex.memory); + + return vertexIndex; +}