From 66cb556fb6f87d195aacf8a25ffafb86d524da19 Mon Sep 17 00:00:00 2001 From: Frederico Linhares Date: Thu, 20 Apr 2023 16:17:49 -0300 Subject: feat Create text rendering system --- src/vk/texture.cpp | 299 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 236 insertions(+), 63 deletions(-) (limited to 'src/vk/texture.cpp') diff --git a/src/vk/texture.cpp b/src/vk/texture.cpp index 020dd21..93bfe61 100644 --- a/src/vk/texture.cpp +++ b/src/vk/texture.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2022 Frederico de Oliveira Linhares + * Copyright 2022-2023 Frederico de Oliveira Linhares * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,57 +27,55 @@ namespace { -struct TextureBuilder +inline void +create_vulkan_image( + VkImage *image, VkDeviceMemory *device_memory, int width, int height, + uint32_t mip_levels) +{ + VkExtent3D vk_extent3d{}; + vk_extent3d.width = width; + vk_extent3d.height = height; + vk_extent3d.depth = 1; + + VK::Image::create( + cg_core.vk_device_with_swapchain, + image, + device_memory, + VK_FORMAT_R8G8B8A8_UNORM, + vk_extent3d, + mip_levels, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); +} + +struct ImageBuilder { VK::Texture *texture; +}; + +struct ImageTextureBuilder: public ImageBuilder +{ std::string texture_path; - TextureBuilder(VK::Texture *t, std::string tp); - TextureBuilder(VK::Texture *t, const char* tp); + ImageTextureBuilder(VK::Texture *t, std::string tp); + ImageTextureBuilder(VK::Texture *t, const char* tp); }; -TextureBuilder::TextureBuilder(VK::Texture *t, std::string tp): - texture{t}, +ImageTextureBuilder::ImageTextureBuilder(VK::Texture *t, std::string tp): texture_path{tp} { + this->texture = t; } -TextureBuilder::TextureBuilder(VK::Texture *t, const char* tp): - TextureBuilder{t, std::string(tp)} +ImageTextureBuilder::ImageTextureBuilder(VK::Texture *t, const char* tp): + ImageTextureBuilder{t, std::string(tp)} { } -void move_image_state( - VkCommandBuffer vk_command_buffer, VkImage vk_image, VkFormat format, - VkAccessFlags src_access_mask, VkAccessFlags dst_access_mask, - VkImageLayout old_layout, VkImageLayout new_layout, - VkPipelineStageFlags source_stage, VkPipelineStageFlags destination_stage) -{ - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.pNext = nullptr; - barrier.srcAccessMask = src_access_mask; - barrier.dstAccessMask = dst_access_mask; - barrier.oldLayout = old_layout; - barrier.newLayout = new_layout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = vk_image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier( - vk_command_buffer, source_stage, destination_stage, 0, 0, nullptr, - 0, nullptr, 1, &barrier); -} - void load_image(void *obj) { - auto self = static_cast(obj); + auto self = static_cast(obj); qoi_desc img_desc; const int num_channels = 4; // all images are converted to RGBA @@ -104,20 +102,12 @@ load_image(void *obj) { // Create vulkan image. try { - VkExtent3D vk_extent3d{}; - vk_extent3d.width = self->texture->width; - vk_extent3d.height = self->texture->height; - vk_extent3d.depth = 1; - - VK::Image::create( - cg_core.vk_device_with_swapchain, - &self->texture->image, - &self->texture->device_memory, - VK_FORMAT_R8G8B8A8_UNORM, - vk_extent3d, - self->texture->mip_levels, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + create_vulkan_image( + &self->texture->image, + &self->texture->device_memory, + self->texture->width, + self->texture->height, + self->texture->mip_levels); } catch(VK::Image::Error error) { @@ -134,7 +124,7 @@ load_image(void *obj) VkCommandBuffer vk_command_buffer{command_pool.command_buffers[0]}; queue.submit_one_time_command(vk_command_buffer, [&](){ - move_image_state( + VK::Image::move_image_state( vk_command_buffer, self->texture->image, VK_FORMAT_R8G8B8A8_UNORM, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, @@ -149,13 +139,14 @@ load_image(void *obj) image_copy.imageSubresource.baseArrayLayer = 0; image_copy.imageSubresource.layerCount = 1; image_copy.imageOffset = {0, 0, 0}; - image_copy.imageExtent = {self->texture->width, self->texture->height, 1}; + image_copy.imageExtent = { + self->texture->width, self->texture->height, 1}; vkCmdCopyBufferToImage( vk_command_buffer, source_image_buffer.buffer, self->texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); - move_image_state( + VK::Image::move_image_state( vk_command_buffer, self->texture->image, VK_FORMAT_R8G8B8A8_UNORM, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, @@ -172,7 +163,7 @@ load_image(void *obj) void unload_image(void *obj) { - auto self = static_cast(obj); + auto self = static_cast(obj); vkDestroyImage( cg_core.vk_device_with_swapchain->device, self->texture->image, nullptr); @@ -184,7 +175,7 @@ unload_image(void *obj) void load_sampler(void *obj) { - auto self = static_cast(obj); + auto self = static_cast(obj); VkSamplerCreateInfo sampler_info{}; sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; @@ -215,7 +206,7 @@ load_sampler(void *obj) void unload_sampler(void *obj) { - auto self = static_cast(obj); + auto self = static_cast(obj); vkDestroySampler( cg_core.vk_device_with_swapchain->device, self->texture->sampler, nullptr); @@ -224,7 +215,7 @@ unload_sampler(void *obj) void load_view(void *obj) { - auto self = static_cast(obj); + auto self = static_cast(obj); try { @@ -242,27 +233,209 @@ load_view(void *obj) void unload_view(void *obj) { - auto self = static_cast(obj); + auto self = static_cast(obj); vkDestroyImageView( cg_core.vk_device_with_swapchain->device, self->texture->view, nullptr); } -const CommandChain loader{ +const CommandChain image_loader{ {&load_image, &unload_image}, {&load_sampler, &unload_sampler}, {&load_view, &unload_view} }; +struct CharacterToDraw +{ + int pos_x, pos_y; + std::shared_ptr character; + + CharacterToDraw(int x, int y, std::shared_ptr character): + pos_x{x}, + pos_y{y}, + character{character} + {}; +}; + +struct TextTextureBuilder: public ImageBuilder +{ + VK::Font *font; + const char* str; + std::vector chars_to_draw; + + TextTextureBuilder(VK::Texture *texture, VK::Font *font, const char* str): + font{font}, + str{str} + { + this->texture = texture; + } +}; + +void +load_text_proportions(void *obj) +{ + auto self = static_cast(obj); + + uint32_t texture_width, texture_height; + auto unicode_text{VK::Character::str_to_unicode(self->str)}; + + texture_width = 0; + texture_height = 0; + self->chars_to_draw.reserve(unicode_text.size()); + { // Calculate image size + int max_height; + for(auto char_code : unicode_text) + { + auto char_image = self->font->character(char_code); + uint32_t pos_x{texture_width + char_image->bearing_x}; + uint32_t pos_y{char_image->height - char_image->bearing_y}; + + self->chars_to_draw.emplace_back(pos_x, pos_y, char_image); + + if(char_image->height > texture_height) + texture_height = char_image->height; + + texture_width += char_image->advance; + } + } + + self->texture->width = texture_width; + self->texture->height = texture_height; + self->texture->mip_levels = 1; +} + +void +load_text_image(void *obj) +{ + auto self = static_cast(obj); + + const int NumChannels = 4; + + size_t image_size{static_cast( + self->texture->width * self->texture->height * NumChannels)}; + std::vector pixels(image_size); + for(auto x{0}; x < self->texture->width; x++) + { + for(auto y{0}; y < self->texture->height; y++) + { + auto image_coord = y * self->font->face->glyph->bitmap.width + + x * NumChannels; + pixels[image_coord] = 0; // Red + pixels[image_coord + 1] = 0; // Green + pixels[image_coord + 2] = 0; // Blue + pixels[image_coord + 3] = 255; // Alpha + } + } + VK::SourceBuffer source_image_buffer{ + cg_core.vk_device_with_swapchain, pixels.data(), image_size}; + + { // Create vulkan image. + try + { + create_vulkan_image( + &self->texture->image, + &self->texture->device_memory, + self->texture->width, + self->texture->height, + self->texture->mip_levels); + } + catch(VK::Image::Error error) + { + throw CommandError{error.what()}; + } + } + + { // Render text + auto queue_family{ + cg_core.vk_device_with_swapchain->get_queue_family_with_presentation()}; + auto queue{queue_family->get_queue()}; + VK::CommandPool command_pool{queue_family, 1}; + VkCommandBuffer vk_command_buffer{command_pool.command_buffers[0]}; + + queue.submit_one_time_command(vk_command_buffer, [&](){ + VK::Image::move_image_state( + vk_command_buffer, self->texture->image, VK_FORMAT_R8G8B8A8_UNORM, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + + VkBufferImageCopy image_copy{}; + image_copy.bufferOffset = 0; + image_copy.bufferRowLength = 0; + image_copy.bufferImageHeight = 0; + image_copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_copy.imageSubresource.mipLevel = 0; + image_copy.imageSubresource.baseArrayLayer = 0; + image_copy.imageSubresource.layerCount = 1; + image_copy.imageOffset = {0, 0, 0}; + image_copy.imageExtent = {self->texture->width, self->texture->height, 1}; + + vkCmdCopyBufferToImage( + vk_command_buffer, source_image_buffer.buffer, self->texture->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + + for(auto &to_draw: self->chars_to_draw) + { + VkImageSubresourceLayers + source_subresources{}, destination_subresources{}; + source_subresources.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + source_subresources.mipLevel = 0; + source_subresources.baseArrayLayer = 0; + source_subresources.layerCount = 1; + + destination_subresources.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + destination_subresources.mipLevel = 0; + destination_subresources.baseArrayLayer = 0; + destination_subresources.layerCount = 1; + + VkImageCopy image_copy{}; + image_copy.srcSubresource = source_subresources; + image_copy.srcOffset = {0, 0, 0}; + image_copy.dstSubresource = destination_subresources; + image_copy.dstOffset = {to_draw.pos_x, to_draw.pos_y, 0}; + image_copy.extent = { + to_draw.character->width, to_draw.character->height, 1}; + + vkCmdCopyImage( + vk_command_buffer, + to_draw.character->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + self->texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &image_copy); + } + + VK::Image::move_image_state( + vk_command_buffer, self->texture->image, VK_FORMAT_R8G8B8A8_UNORM, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + }); + } +} + +const CommandChain text_loader{ + {&load_text_proportions, nullptr}, + {&load_text_image, &unload_image}, + {&load_sampler, &unload_sampler}, + {&load_view, &unload_view} +}; + } namespace VK { +Texture::Texture(Font *font, const char* str) +{ + TextTextureBuilder text_builder(this, font, str); + text_loader.execute(&text_builder); +} + Texture::Texture(std::string texture_path) { - TextureBuilder texture_builder(this, texture_path); - loader.execute(&texture_builder); + ImageTextureBuilder texture_builder(this, texture_path); + image_loader.execute(&texture_builder); } Texture::Texture(const char* texture_path): @@ -272,8 +445,8 @@ Texture::Texture(const char* texture_path): Texture::~Texture() { - TextureBuilder texture_builder(this, ""); - loader.revert(&texture_builder); + ImageTextureBuilder texture_builder(this, ""); + image_loader.revert(&texture_builder); } } -- cgit v1.2.3