From 572700fbb3d9f2b480669388612ba76d98568ae2 Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Tue, 8 Nov 2022 10:57:07 +0800 Subject: [PATCH] Text Rendering WIP --- .../src/Assets/Asset Types/SHFontAsset.h | 26 +- .../Libraries/Compilers/SHFontCompiler.cpp | 253 ------------------ .../Libraries/Compilers/SHFontCompiler.h | 22 -- .../Assets/Libraries/Loaders/SHFontLoader.cpp | 13 +- .../GlobalData/SHGraphicsGlobalData.cpp | 21 +- .../MiddleEnd/Interface/SHGraphicsConstants.h | 30 ++- .../MiddleEnd/TextRendering/SHFont.cpp | 60 +++++ .../Graphics/MiddleEnd/TextRendering/SHFont.h | 46 ++++ 8 files changed, 175 insertions(+), 296 deletions(-) delete mode 100644 SHADE_Engine/src/Assets/Libraries/Compilers/SHFontCompiler.cpp delete mode 100644 SHADE_Engine/src/Assets/Libraries/Compilers/SHFontCompiler.h create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.cpp create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.h diff --git a/SHADE_Engine/src/Assets/Asset Types/SHFontAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHFontAsset.h index c2fcb20c..7db0bc75 100644 --- a/SHADE_Engine/src/Assets/Asset Types/SHFontAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/SHFontAsset.h @@ -1,13 +1,3 @@ -/*************************************************************************//** - * \file SHFontAsset.h - * \author Brandon Mak - * \date 5 November 2022 - * \brief - * - * Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. - *****************************************************************************/ #pragma once #include "SHAssetData.h" @@ -22,12 +12,14 @@ namespace SHADE struct SH_API SHFontAsset : SHAssetData { using GlyphData = std::tuple; + static constexpr uint32_t NUM_CHANNELS = 3; + static constexpr uint32_t BYTES_PER_CHANNEL = 3; /*-----------------------------------------------------------------------*/ /* MEMBER VARIABLES */ /*-----------------------------------------------------------------------*/ //! Name of the shader file (without parent path) - //std::string fontName; + std::string fontName; //! Data containing character and uv transformation data and other misc data std::vector glyphTransformations; @@ -35,7 +27,13 @@ namespace SHADE //! The actual data of the atlas to go into the binary std::unique_ptr bitmapData; - //! size of the bitmap - uint32_t bitmapSize; + //! Width of the bitmap + uint32_t bitmapWidth; + + //! Height of the bitmap + uint32_t bitmapHeight; + + //! Font geometry required to get kerning from + msdf_atlas::FontGeometry fontGeometry; }; -} +} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Libraries/Compilers/SHFontCompiler.cpp b/SHADE_Engine/src/Assets/Libraries/Compilers/SHFontCompiler.cpp deleted file mode 100644 index 804ec994..00000000 --- a/SHADE_Engine/src/Assets/Libraries/Compilers/SHFontCompiler.cpp +++ /dev/null @@ -1,253 +0,0 @@ -#include "SHpch.h" -#include "SHFontCompiler.h" -#include "Graphics/MiddleEnd/TextRendering/SHFreetypeInstance.h" -#include "Assets/Asset Types/SHFontAsset.h" -#include "Math/Vector/SHVec2.h" - -#include -#include - -namespace SHADE -{ - /***************************************************************************/ - /*! - - \brief - Given a valid ptr to a font asset and relevant data, initialize the data - inside the font asset. See SHFontAsset for details. - - \param fontAsset - The ptr to the font asset. - - \param glyphData - Individual glyph data. - - \param fontBitmap - Actual bitmap data - - \param fontGeometry - Font geometry required to get advance - - */ - /***************************************************************************/ - void SHFontCompiler::WriteToFontAsset(SHFontAsset* fontAsset, std::vector const& glyphData, msdfgen::Bitmap const& fontBitmap, msdf_atlas::FontGeometry const& fontGeometry) noexcept - { - if (!fontAsset) - return; - - uint32_t numGlyphs = static_cast(glyphData.size()); - for (uint32_t i = 0; i < numGlyphs; ++i) - { - // bounding box of the glyph in atlas - double atlasL = 0.0, atlasR = 0.0, atlasT = 0.0, atlasB = 0.0; - - // bounding box of glyph as it should be placed on the baseline - double atlasPL = 0.0, atlasPR = 0.0, atlasPT = 0.0, atlasPB = 0.0; - - // initialize the bounding boxes - glyphData[i].getQuadAtlasBounds(atlasL, atlasB, atlasR, atlasT); - glyphData[i].getQuadPlaneBounds(atlasPL, atlasPB, atlasPR, atlasPT); - - // normalize the bounding box to (0 - 1). - atlasL /= fontBitmap.width(); - atlasR /= fontBitmap.width(); - atlasT /= fontBitmap.height(); - atlasB /= fontBitmap.height(); - - // Normalized texture dimensions - SHVec2 const NORMALIZED_TEX_DIMS{ static_cast (atlasR - atlasL), static_cast (atlasT - atlasB) }; - - // When we render the quad, it has to correctly scale depending on what letter/glyph we are rendering. This is for that scale. - SHVec2 const QUAD_SCALE { static_cast (atlasPR - atlasL), static_cast (atlasT - atlasB) }; - - // initialize a matrix for uv and quad transformation data - SHMatrix transformData - { - // For scaling the tex coords - NORMALIZED_TEX_DIMS[0], 0.0f, 0.0f, 0.0f, - 0.0f, NORMALIZED_TEX_DIMS[1], 0.0f, 0.0f, - - // For translating the tex coords - static_cast(atlasL), static_cast(atlasB), 1.0f, 0.0f, - - // Stores the transformation for a quad to correctly shape the glyph (first 2 values) and the bearing (last 2) - QUAD_SCALE[0], QUAD_SCALE[1], static_cast(atlasPL), static_cast(atlasPB) - }; - - // Initialize new data (we want the matrix transposed for shader use) - SHFontAsset::GlyphData newData = std::make_tuple(glyphData[i].getCodepoint(), SHMatrix::Transpose(transformData)); - - // Push 1 set of data for a character/glyph into the asset. - fontAsset->glyphTransformations.push_back(newData); - } - - uint32_t bytesRequired = fontBitmap.width() * fontBitmap.height() * 3 * sizeof(float); - - // copy data from bitmap to asset. Each channel is a 32 bit float and there are 3 channels. - fontAsset->bitmapData = std::make_unique(bytesRequired); - - fontAsset->bitmapSize = bytesRequired; - } - - /***************************************************************************/ - /*! - - \brief - Loads and compiles a font to binary format. Returns a path to the binary - data (XQ please confirm kor kor thanks <3). - - \param path - Path to the font file (truetype font file) to load. - - \return - Path to newly created binary data. - - */ - /***************************************************************************/ - std::optional SHFontCompiler::LoadAndCompileFont(AssetPath path) noexcept - { - msdfgen::FontHandle* fontHandle = nullptr; - - // XQ I need your help for path manipulation to actually load the msdfgen::FontHandle here. Am I doing this correctly? - fontHandle = msdfgen::loadFont(SHFreetypeInstance::GetFreetypeHandle(), path.string().c_str()); - - if (fontHandle) - { - // Compile a font asset - auto* fontAsset = CompileFontToMemory(fontHandle); - - // No path to binary format - if (!fontAsset) - return {}; - - return CompileFontToBinary(path, *fontAsset); - } - - SHLOG_ERROR("Unable to open font file: {}", path.string()); - - return {}; - } - - /***************************************************************************/ - /*! - - \brief - This function takes in a font handle and generates a font asset from it. - It first generates an atlas and all relevant data before creating the - asset. - - \param fontHandle - MSDF font handle required to initialize member variables in SHFontAsset. - - \return - A pointer to a brand new font asset. - - */ - /***************************************************************************/ - SHADE::SHFontAsset const* SHFontCompiler::CompileFontToMemory(msdfgen::FontHandle* fontHandle) noexcept - { - // Individual glyph geometry - std::vector glyphData; - - // Actual bitmap data - msdfgen::Bitmap fontBitmap; - - // Font geometry required to get advance - msdf_atlas::FontGeometry fontGeometry (&glyphData); - - // Load char set - fontGeometry.loadCharset(fontHandle, 1.0, msdf_atlas::Charset::ASCII); - - // Apply MSDF edge coloring - const double maxCornerAngle = 3.0; - for (msdf_atlas::GlyphGeometry& glyph : glyphData) - glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, maxCornerAngle, 0); - - // configure parameters for atlas generation - msdf_atlas::TightAtlasPacker atlasPacker; - atlasPacker.setDimensionsConstraint(msdf_atlas::TightAtlasPacker::DimensionsConstraint::SQUARE); - - atlasPacker.setMinimumScale(64.0); - atlasPacker.setPixelRange(2.0); - atlasPacker.setMiterLimit(1.0); - atlasPacker.pack(glyphData.data(), static_cast(glyphData.size())); - - // Get the dimensions after applying parameters - int width = 0, height = 0; - atlasPacker.getDimensions(width, height); - - // generate the atlas - msdf_atlas::ImmediateAtlasGenerator> generator(width, height); - msdf_atlas::GeneratorAttributes genAttribs; - generator.setAttributes(genAttribs); - generator.setThreadCount(4); - generator.generate(glyphData.data(), static_cast(glyphData.size())); - - fontBitmap = std::move(((msdfgen::Bitmap&&)generator.atlasStorage())); - - // at this point we have all the required data to initialize a font asset. - - // Dynamically allocate new asset - SHFontAsset* newAsset = new SHFontAsset(); - - // Now we populate it with data - WriteToFontAsset(newAsset, glyphData, fontBitmap, fontGeometry); - - return newAsset; - } - - /***************************************************************************/ - /*! - - \brief - After generating the asset we call this function to serialize the font - data into binary data. - - \param path - path to font file (?). - - \param asset - Asset to write. - - \return - Path the asset. - - */ - /***************************************************************************/ - std::string SHFontCompiler::CompileFontToBinary(AssetPath path, SHFontAsset const& asset) noexcept - { - std::string newPath{ path.string() }; - newPath = newPath.substr(0, newPath.find_last_of('.')); - newPath += SHADER_BUILT_IN_EXTENSION.data(); - - std::ofstream file{ newPath, std::ios::binary | std::ios::out | std::ios::trunc }; - - uint32_t numGlyphs = asset.glyphTransformations.size(); - - // Write number of glyphs first - file.write(reinterpret_cast(&numGlyphs), sizeof (uint32_t)); - - for (uint32_t i = 0; i < numGlyphs; ++i) - { - auto const& [glyph, data] = asset.glyphTransformations[i]; - - // write the glyph first - file.write (reinterpret_cast(&glyph), sizeof (msdfgen::unicode_t)); - - // then write the data next to it - file.write (reinterpret_cast(&data), sizeof (SHMatrix)); - } - - // Write bytes required for bitmap - file.write(reinterpret_cast(asset.bitmapSize), sizeof (uint32_t)); - - // now we write the actual bitmap - file.write (reinterpret_cast(asset.bitmapData.get()), asset.bitmapSize); - - - file.close(); - - return newPath; - } - -} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Libraries/Compilers/SHFontCompiler.h b/SHADE_Engine/src/Assets/Libraries/Compilers/SHFontCompiler.h deleted file mode 100644 index c5e68aa6..00000000 --- a/SHADE_Engine/src/Assets/Libraries/Compilers/SHFontCompiler.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "Assets/SHAssetMacros.h" -#include "msdf-atlas-gen/msdf-atlas-gen.h" - - -namespace SHADE -{ - class SHFontAsset; - - class SHFontCompiler - { - private: - static void WriteToFontAsset (SHFontAsset* fontAsset, std::vector const& glyphData, msdfgen::Bitmap const& fontBitmap, msdf_atlas::FontGeometry const& fontGeometry) noexcept; - - public: - static std::optional LoadAndCompileFont (AssetPath path) noexcept; - static SHFontAsset const* CompileFontToMemory (msdfgen::FontHandle* fontHandle) noexcept; - static std::string CompileFontToBinary (AssetPath path, SHFontAsset const& asset) noexcept; - - }; -} diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHFontLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHFontLoader.cpp index 36482d09..52db00e1 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHFontLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHFontLoader.cpp @@ -37,12 +37,17 @@ namespace SHADE file.read(reinterpret_cast(&std::get<1>(newFontAsset->glyphTransformations[i])), sizeof(SHMatrix)); } - // Read bytes required for the bitmap - file.read(reinterpret_cast(&newFontAsset->bitmapSize), sizeof(uint32_t)); + // read the width + file.read(reinterpret_cast(&newFontAsset->bitmapWidth), sizeof(SHFontAsset::bitmapWidth)); + + // read the height + file.read(reinterpret_cast(&newFontAsset->bitmapHeight), sizeof(SHFontAsset::bitmapHeight)); + + uint32_t bytesRequired = newFontAsset->bitmapWidth * newFontAsset->bitmapHeight * SHFontAsset::BYTES_PER_CHANNEL * SHFontAsset::NUM_CHANNELS; // Read the bitmap - newFontAsset->bitmapData = std::make_unique(newFontAsset->bitmapSize); - file.read (reinterpret_cast(newFontAsset->bitmapData.get()), newFontAsset->bitmapSize); + newFontAsset->bitmapData = std::make_unique(bytesRequired); + file.read (reinterpret_cast(newFontAsset->bitmapData.get()), bytesRequired); file.close(); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp index 9717889d..d67ccafd 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp @@ -45,7 +45,7 @@ namespace SHADE }; // For global data (generic data and textures) - Handle staticGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS,{ genericDataBinding, texturesBinding }); + Handle staticGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS,{ genericDataBinding, texturesBinding}); std::vector lightBindings{}; @@ -98,6 +98,25 @@ namespace SHADE // For High frequency global data (camera) Handle materialDataPerInstanceLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, { materialDataBinding }); + SHVkDescriptorSetLayout::Binding fontBitmapBinding + { + .Type = vk::DescriptorType::eCombinedImageSampler, + .Stage = vk::ShaderStageFlagBits::eFragment, + .BindPoint = SHGraphicsConstants::DescriptorSetBindings::FONT_BITMAP_DATA, + .DescriptorCount = 1, + }; + + SHVkDescriptorSetLayout::Binding fontMatrixBinding + { + .Type = vk::DescriptorType::eStorageBuffer, + .Stage = vk::ShaderStageFlagBits::eVertex, + .BindPoint = SHGraphicsConstants::DescriptorSetBindings::FONT_MATRIX_DATA, + .DescriptorCount = 1, + }; + + Handle fontDataLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::FONT_DATA, { fontBitmapBinding, fontMatrixBinding }); + + globalDescSetLayouts.push_back(staticGlobalLayout); globalDescSetLayouts.push_back(dynamicGlobalLayout); globalDescSetLayouts.push_back(cameraDataGlobalLayout); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h index 0a67cd9f..fd8566d7 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h @@ -82,6 +82,14 @@ namespace SHADE /***************************************************************************/ static constexpr uint32_t RENDERGRAPH_NODE_COMPUTE_RESOURCE = 5; + /***************************************************************************/ + /*! + \brief + To store font data. + + */ + /***************************************************************************/ + static constexpr uint32_t FONT_DATA = 6; }; struct DescriptorSetBindings @@ -107,7 +115,7 @@ namespace SHADE /***************************************************************************/ /*! \brief - DescriptorSet binding for combined image sampler data. + DescriptorSet binding for light data. */ /***************************************************************************/ @@ -116,7 +124,7 @@ namespace SHADE /***************************************************************************/ /*! \brief - DescriptorSet binding for lights. + DescriptorSet binding for camera data. */ /***************************************************************************/ @@ -130,6 +138,24 @@ namespace SHADE /***************************************************************************/ static constexpr uint32_t BATCHED_PER_INST_DATA = 0; + /***************************************************************************/ + /*! + \brief + Descriptor set binding for font bitmaps. + + */ + /***************************************************************************/ + static constexpr uint32_t FONT_BITMAP_DATA = 0; + + /***************************************************************************/ + /*! + \brief + Descriptor set binding for font matrix data. + + */ + /***************************************************************************/ + static constexpr uint32_t FONT_MATRIX_DATA = 1; + }; struct VertexBufferBindings diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.cpp new file mode 100644 index 00000000..7d03d86f --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.cpp @@ -0,0 +1,60 @@ +#include "SHpch.h" +#include "SHFont.h" +#include "Graphics/Devices/SHVkLogicalDevice.h" +#include "Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.h" +#include "Graphics/Descriptors/SHVkDescriptorSetGroup.h" + +namespace SHADE +{ + SHFont::SHFont(Handle const& inLogicalDeviceHdl, Handle commandPool, Handle descPool, SHFontAsset const& asset) noexcept + { + // assign device for convenient usage + logicalDevice = inLogicalDeviceHdl; + + SHImageCreateParams imageParams + { + .imageType = vk::ImageType::e2D, + .width = asset.bitmapWidth, + .height = asset.bitmapHeight, + .depth = 1, + .levels = 1, + .arrayLayers = 1, + .imageFormat = vk::Format::eR32G32B32Sfloat, + .usageFlags = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst, + .createFlags = {} + }; + + uint32_t bytesRequired = asset.bitmapWidth * asset.bitmapHeight * SHFontAsset::BYTES_PER_CHANNEL * SHFontAsset::NUM_CHANNELS; + uint32_t mipOffset = 0; + + // Create the image + bitmapDataImage = logicalDevice->CreateImage(imageParams, asset.bitmapData.get(), bytesRequired, { &mipOffset, 1 }, VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, {}); + + //SHImageViewDetails viewDetails + //{ + // .viewType = vk::ImageViewType::e2D, + // .format = vk::Format::eR32G32B32Sfloat, + // .imageAspectFlags = vk::ImageAspectFlagBits::eColor, + // .baseMipLevel = 0, + // .mipLevelCount = 1, + // .baseArrayLayer = 0, + // .layerCount = 1, + //}; + //bitmapDataImageView = bitmapDataImage->CreateImageView(logicalDevice, bitmapDataImage, ) + + uint32_t glyphDataSize = asset.glyphTransformations.size() * sizeof (SHMatrix); + + // allocate GPU buffer for matrices + matrixDataBuffer = logicalDevice->CreateBuffer(glyphDataSize, nullptr, glyphDataSize, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, {}); + + // Font data desc set layout + auto fontDataLayout = SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetBindings::FONT_BITMAP_DATA]; + + // allocate desc set for the bitmap and matrix data + descSet = descPool->Allocate({fontDataLayout}, {1, 1}); + + //auto viewLayoutSampler = std::make_tuple() + descSet->ModifyWriteDescImage(SHGraphicsConstants::DescriptorSetIndex::FONT_DATA, SHGraphicsConstants::DescriptorSetBindings::FONT_BITMAP_DATA, {}); + } + +} \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.h b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.h new file mode 100644 index 00000000..c1a8d30a --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.h @@ -0,0 +1,46 @@ +#pragma once + +#include "Resource/SHHandle.h" +#include "msdf-atlas-gen/msdf-atlas-gen.h" +#include "Assets/Asset Types/SHFontAsset.h" + +namespace SHADE +{ + class SHVkLogicalDevice; + class SHVkDescriptorPool; + class SHVkDescriptorSetGroup; + class SHVkCommandBuffer; + class SHVkCommandPool; + class SHVkImage; + class SHVkImageView; + class SHVkBuffer; + + class SHFont + { + private: + //! Device for creation and destruction + Handle logicalDevice; + + //! Font asset contains exactly what we need, so we'll use it + Handle fontAsset; + + //! Device memory that stores bitmap data + Handle bitmapDataImage; + + //! View to device memory + Handle bitmapDataImageView; + + //! Device memory that stores matrix data + Handle matrixDataBuffer; + + //! Descriptor set required to store the bitmap AND matrix data for the UV and quad transformation + Handle descSet; + + //! Used for getting the correct indices into the matrix data buffer + std::unordered_map unicodeIndexing; + + public: + SHFont (Handle const& inLogicalDeviceHdl, Handle commandPool, Handle descPool, SHFontAsset const& asset) noexcept; + + }; +}