#include #include #include #include #ifdef __APPLE__ #include "vulkan/vulkan.h" #else #include "volk/volk.h" #endif #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" #include "renpy/script.h" #include "audio.h" #include "poem.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(medieval_sharp[0]); create_glyphs_buffer(loadedFont.font, loadedFont.glyphs); write_descriptor_sets(loadedFont.allocatedImage.imageView); create_instance_buffers(); 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{ .blendEnable = VK_TRUE, .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, .colorBlendOp = VK_BLEND_OP_ADD, .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .alphaBlendOp = VK_BLEND_OP_ADD, .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 }; constexpr int vertexBindingDescriptionsCount = 2; VkVertexInputBindingDescription vertexBindingDescriptions[vertexBindingDescriptionsCount]{ { .binding = 0, .stride = perVertexSize, .inputRate = VK_VERTEX_INPUT_RATE_VERTEX }, { .binding = 1, .stride = perInstanceSize, .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE } }; constexpr int vertexAttributeDescriptionsCount = 5; VkVertexInputAttributeDescription vertexAttributeDescriptions[vertexAttributeDescriptionsCount]{ // 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, }, // per-instance { .location = 2, .binding = 1, .format = VK_FORMAT_R16G16_UINT, .offset = 0, }, { .location = 3, .binding = 1, .format = VK_FORMAT_R32_UINT, .offset = 4, }, { .location = 4, .binding = 1, .format = VK_FORMAT_R8G8B8A8_UNORM, .offset = 8, }, }; VkPipelineVertexInputStateCreateInfo vertexInputState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .vertexBindingDescriptionCount = vertexBindingDescriptionsCount, .pVertexBindingDescriptions = vertexBindingDescriptions, .vertexAttributeDescriptionCount = vertexAttributeDescriptionsCount, .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 = 3; VkDescriptorPoolSize descriptorPoolSizes[descriptorPoolSizesCount]{ { // linear sampler .type = VK_DESCRIPTOR_TYPE_SAMPLER, .descriptorCount = 1, }, { .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .descriptorCount = 1, }, { .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .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 = 3; 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 }, { .binding = 2, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_VERTEX_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 = 3; 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 }; VkDescriptorBufferInfo glyphsDescriptorBufferInfo{ .buffer = glyphsBuffer, .offset = 0, .range = glyphsBufferSize, }; writeDescriptorSets[writeIndex++] = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = descriptorSet0, .dstBinding = 2, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .pBufferInfo = &glyphsDescriptorBufferInfo }; assert(writeIndex == writeCount); vkUpdateDescriptorSets(device, writeIndex, writeDescriptorSets, 0, nullptr); } ////////////////////////////////////////////////////////////////////// // create instance buffer ////////////////////////////////////////////////////////////////////// void font::create_instance_buffers() { constexpr VkDeviceSize bufferSize{ maximumGlyphCount * (sizeof (GlyphInstance)) }; instanceMemorySize = bufferSize * 2; instanceBufferOffset[0] = bufferSize * 0; instanceBufferOffset[1] = bufferSize * 1; // create buffer VkBufferCreateInfo bufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = instanceMemorySize, .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE }; VK_CHECK(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &instanceBuffer)); // allocate memory VkMemoryRequirements memoryRequirements; vkGetBufferMemoryRequirements(device, instanceBuffer, &memoryRequirements); VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; VkMemoryAllocateFlags memoryAllocateFlags{}; VkDeviceSize stride; allocateFromMemoryRequirements(device, physicalDeviceProperties.limits.nonCoherentAtomSize, physicalDeviceMemoryProperties, memoryRequirements, memoryPropertyFlags, memoryAllocateFlags, 1, &instanceMemory, &stride); VK_CHECK(vkBindBufferMemory(device, instanceBuffer, instanceMemory, 0)); // map memory VK_CHECK(vkMapMemory(device, instanceMemory, 0, VK_WHOLE_SIZE, 0, (void **)&instanceMappedData)); } ////////////////////////////////////////////////////////////////////// // create glyphs buffer ////////////////////////////////////////////////////////////////////// void font::create_glyphs_buffer(types::font const * const font, types::glyph const * const glyphs) { glyphsBufferSize = (sizeof (Glyph)) * font->glyph_count; // create buffer VkBufferCreateInfo bufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = glyphsBufferSize, .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE }; VK_CHECK(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &glyphsBuffer)); // allocate memory VkMemoryRequirements memoryRequirements; vkGetBufferMemoryRequirements(device, glyphsBuffer, &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, &glyphsMemory, &stride); VK_CHECK(vkBindBufferMemory(device, glyphsBuffer, glyphsMemory, 0)); // map memory Glyph * glyphsMappedData; VK_CHECK(vkMapMemory(device, glyphsMemory, 0, VK_WHOLE_SIZE, 0, (void **)&glyphsMappedData)); for (int i = 0; i < font->glyph_count; i++) { glyphsMappedData[i].x = glyphs[i].bitmap.x; glyphsMappedData[i].y = glyphs[i].bitmap.y; glyphsMappedData[i].width = glyphs[i].bitmap.width; glyphsMappedData[i].height = glyphs[i].bitmap.height; } // flush constexpr int mappedMemoryRangesCount = 1; VkMappedMemoryRange mappedMemoryRanges[mappedMemoryRangesCount]{ { .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = glyphsMemory, .offset = 0, .size = VK_WHOLE_SIZE, } }; vkFlushMappedMemoryRanges(device, mappedMemoryRangesCount, mappedMemoryRanges); vkUnmapMemory(device, glyphsMemory); } void font::emit_line(int frameIndex, char const * const string, uint32_t x, uint32_t y, int& outputIndex, int startIndex, int endIndex, uint32_t color) const { for (int i = startIndex; i < endIndex; i++) { bool highlight = string[i] < 0; char c = abs(string[i]); types::glyph const& glyph = loadedFont.glyphs[c - 32]; if (c != ' ' && c != '\n') { instanceMappedData[maximumGlyphCount * frameIndex + outputIndex++] = { (uint16_t)((x + glyph.metrics.horiBearingX) >> 6), (uint16_t)((y - glyph.metrics.horiBearingY) >> 6), (uint32_t)(c - 32), highlight ? 0xa81524: color, }; } x += glyph.metrics.horiAdvance; } } void font::centered(int frameIndex, char const * const string, uint32_t& x, uint32_t& y, uint32_t minX, uint32_t maxWidth, int& outputIndex, uint32_t color) const { int stringIndex = 0; int lineStart = stringIndex; int lineWidth = 0; uint32_t leftOffset = 0; while (true) { char c = abs(string[stringIndex++]); if (c == 0) break; if (c == '\n') { continue; } types::glyph const& glyph = loadedFont.glyphs[c - 32]; if (lineWidth == 0) { leftOffset = glyph.metrics.horiBearingX; } lineWidth += glyph.metrics.horiAdvance; if ((lineWidth - leftOffset) + loadedFont.font->face_metrics.max_advance > (maxWidth << 6)) { while (abs(string[stringIndex]) != ' ') { char c = abs(string[--stringIndex]); types::glyph const& glyph = loadedFont.glyphs[c - 32]; lineWidth -= glyph.metrics.horiAdvance; } uint32_t center = (minX + (maxWidth / 2)) << 6; uint32_t left = center - (lineWidth / 2); emit_line(frameIndex, string, left - leftOffset, y, outputIndex, lineStart, stringIndex, color); while (abs(string[stringIndex]) == ' ') stringIndex++; lineStart = stringIndex; lineWidth = 0; y += loadedFont.font->face_metrics.height * 1.2; x = minX << 6; } }; if (stringIndex != lineStart) { uint32_t center = (minX + (maxWidth / 2)) << 6; uint32_t left = center - (lineWidth / 2); emit_line(frameIndex, string, left - leftOffset, y, outputIndex, lineStart, stringIndex, color); y += loadedFont.font->face_metrics.height * 1.2; x = minX << 6; } } static char line_buffer[1024]; char const * combine_words(char const * const * const words, int start, int count, int word_index) { assert(count > 0); int index = 0; for (int i = start; i < start + count; i++) { char const * word = words[i]; int sign = (word_index == i) ? -1 : 1; while (true) { int c = *word++; if (c == 0) break; line_buffer[index++] = (char)(c * sign); } line_buffer[index++] = ' '; } assert(index > 0); line_buffer[index - 1] = 0; return line_buffer; } ////////////////////////////////////////////////////////////////////// // draw ////////////////////////////////////////////////////////////////////// void font::draw(VkCommandBuffer commandBuffer, uint32_t frameIndex, renpy::interpreter const& state) const { // transfer int outputIndex = 0; if (state.pause.menu) { assert(state.menu.count != 0); for (uint32_t i = 0; i < state.menu.count; i++) { uint32_t x = 400 << 6; uint32_t y = (100 * i + 130) << 6; centered(frameIndex, renpy::script::options[state.menu.optionIndex + i].string, x, y, 400, 480, outputIndex, 0x000000); } } else { bool say_poem = state.say.stringIndex == (uint32_t)renpy::script::strings_say_poem_index; if ((audio::poem_playing != nullptr) && say_poem) { poem::poem const * const poem = audio::poem_playing; int word_index = poem->timestamps[audio::poem_timestamp_index].wordIndex; char const * const string = combine_words(poem->words, poem->lines[audio::poem_line_index].start, poem->lines[audio::poem_line_index].length, word_index); uint32_t x = textboxLeft << 6; uint32_t y = (600 + 20) << 6; centered(frameIndex, string, x, y, textboxLeft, textboxWidth, outputIndex, 0x000000); if ((audio::poem_line_index + 1) < poem->lines_length) { char const * const string = combine_words(poem->words, poem->lines[audio::poem_line_index + 1].start, poem->lines[audio::poem_line_index + 1].length, -1); uint32_t x = textboxLeft << 6; //uint32_t y = (600 + 20) << 6; y += (20 << 6); centered(frameIndex, string, x, y, textboxLeft, textboxWidth, outputIndex, 0x808080); } } else if (state.say.stringIndex != -1u) { char const * const string = renpy::script::strings[state.say.stringIndex]; uint32_t x = textboxLeft << 6; uint32_t y = 600 << 6; centered(frameIndex, string, x, y, textboxLeft, textboxWidth, outputIndex, 0x000000); } if (state.say.characterIndex != -1u) { const renpy::language::character & character = renpy::script::characters[state.say.characterIndex]; char const * const string = character.characterName; uint32_t x = 550 << 6; uint32_t y = 565 << 6; centered(frameIndex, string, x, y, 550, 180, outputIndex, character.color); } } // flush constexpr int mappedMemoryRangesCount = 1; VkMappedMemoryRange mappedMemoryRanges[mappedMemoryRangesCount]{ { .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = instanceMemory, .offset = 0, .size = (sizeof (GlyphInstance)) * outputIndex, } }; alignMappedMemoryRanges(physicalDeviceProperties.limits.nonCoherentAtomSize, instanceMemorySize, mappedMemoryRangesCount, mappedMemoryRanges); vkFlushMappedMemoryRanges(device, mappedMemoryRangesCount, mappedMemoryRanges); // bind/draw 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 vertexOffsets[2]{ 0, instanceBufferOffset[frameIndex] }; VkBuffer vertexBuffers[2]{ vertexIndex.buffer, instanceBuffer }; vkCmdBindVertexBuffers(commandBuffer, 0, 2, vertexBuffers, vertexOffsets); vkCmdDrawIndexed(commandBuffer, 4, outputIndex, 0, 0, 0); } }