diff --git a/SHADE_Engine/src/Assets/Asset Types/SHFontAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHFontAsset.h index 7db0bc75..cb21d9d5 100644 --- a/SHADE_Engine/src/Assets/Asset Types/SHFontAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/SHFontAsset.h @@ -11,9 +11,13 @@ namespace SHADE { struct SH_API SHFontAsset : SHAssetData { - using GlyphData = std::tuple; + using GlyphData = SHMatrix; static constexpr uint32_t NUM_CHANNELS = 3; static constexpr uint32_t BYTES_PER_CHANNEL = 3; + static constexpr uint8_t BASELINE_LEFT_MATRIX_INDEX_ROW = 3; + static constexpr uint8_t BASELINE_LEFT_MATRIX_INDEX_COL = 2; + static constexpr uint8_t BASELINE_BOTTOM_MATRIX_INDEX_ROW = 3; + static constexpr uint8_t BASELINE_BOTTOM_MATRIX_INDEX_COL = 3; /*-----------------------------------------------------------------------*/ /* MEMBER VARIABLES */ @@ -21,6 +25,9 @@ namespace SHADE //! Name of the shader file (without parent path) std::string fontName; + //! Glyphs. Corresponds to the transformation container below. + std::vector glyphs; + //! Data containing character and uv transformation data and other misc data std::vector glyphTransformations; diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHFontLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHFontLoader.cpp index 52db00e1..1a17ae38 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHFontLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHFontLoader.cpp @@ -27,14 +27,19 @@ namespace SHADE // read how many glyphs we have file.read (reinterpret_cast(&numGlyphs), sizeof (uint32_t)); + newFontAsset->glyphs.resize(numGlyphs); newFontAsset->glyphTransformations.resize(numGlyphs); + for (uint32_t i = 0; i < numGlyphs; ++i) { - // read the glyph - file.read(reinterpret_cast(&std::get<0>(newFontAsset->glyphTransformations[i])), sizeof(msdfgen::unicode_t)); + // Read the data for the glyph + file.read(reinterpret_cast(&newFontAsset->glyphs[i]), sizeof(msdf_atlas::unicode_t)); + } - // Read the data for the glyph - file.read(reinterpret_cast(&std::get<1>(newFontAsset->glyphTransformations[i])), sizeof(SHMatrix)); + for (uint32_t i = 0; i < numGlyphs; ++i) + { + // Read the data for the glyph transformations + file.read(reinterpret_cast(&newFontAsset->glyphTransformations[i]), sizeof(SHMatrix)); } // read the width diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.cpp index 7d03d86f..50641404 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.cpp @@ -3,11 +3,16 @@ #include "Graphics/Devices/SHVkLogicalDevice.h" #include "Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.h" #include "Graphics/Descriptors/SHVkDescriptorSetGroup.h" +#include "Graphics/Buffers/SHVkBuffer.h" namespace SHADE { - SHFont::SHFont(Handle const& inLogicalDeviceHdl, Handle commandPool, Handle descPool, SHFontAsset const& asset) noexcept + SHFont::SHFont(Handle inLogicalDeviceHdl, Handle queue, Handle commandPool, Handle descPool, SHFontAsset& asset) noexcept { + /*-----------------------------------------------------------------------*/ + /* PREPARE GPU DATA */ + /*-----------------------------------------------------------------------*/ + // assign device for convenient usage logicalDevice = inLogicalDeviceHdl; @@ -30,31 +35,95 @@ namespace SHADE // 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, ) - + // Amount of data required to hold matrices for all glyphs 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, {}); + // For indexing + for (uint32_t i = 0; i < fontAsset->glyphs.size(); ++i) + unicodeIndexing.emplace(fontAsset->glyphs[i], i); + // allocate GPU buffer for matrices + matrixDataBuffer = logicalDevice->CreateBuffer(glyphDataSize, asset.glyphTransformations.data(), glyphDataSize, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, {}); + + /*-----------------------------------------------------------------------*/ + /* COMMANDS TO TRANSFER TO DEVICE MEMORY */ + /*-----------------------------------------------------------------------*/ + // Create command buffer to record transfer from host to device + Handle commandBuffer = commandPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); + + commandBuffer->BeginRecording(); + + // Prepare an image barrier + vk::ImageMemoryBarrier stagingToDst{}; + bitmapDataImage->PrepareImageTransitionInfo(vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, stagingToDst); + + // Transition image to dst + commandBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, {stagingToDst}); + + // Transfer to device memory + bitmapDataImage->TransferToDeviceResource(commandBuffer); + + // Transfer to GPU buffer + matrixDataBuffer->TransferToDeviceResource(commandBuffer); + + vk::ImageMemoryBarrier dstToShaderRead{}; + commandBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {}, {dstToShaderRead}); + + commandBuffer->EndRecording(); + + queue->SubmitCommandBuffer({commandBuffer}); + + // wait for the command to finish + logicalDevice->WaitIdle(); + + /*-----------------------------------------------------------------------*/ + /* CREATE IMAGE VIEW */ + /*-----------------------------------------------------------------------*/ + // Create the image view to the device resource + 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, viewDetails); + + + /*-----------------------------------------------------------------------*/ + /* DESCRIPTORS */ + /*-----------------------------------------------------------------------*/ // 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}); + descSet = descPool->Allocate({ fontDataLayout }, { 1, 1 }); //auto viewLayoutSampler = std::make_tuple() descSet->ModifyWriteDescImage(SHGraphicsConstants::DescriptorSetIndex::FONT_DATA, SHGraphicsConstants::DescriptorSetBindings::FONT_BITMAP_DATA, {}); + + descSet->ModifyWriteDescBuffer(SHGraphicsConstants::DescriptorSetIndex::FONT_DATA, + SHGraphicsConstants::DescriptorSetBindings::FONT_MATRIX_DATA, {&matrixDataBuffer, 1}, 0, glyphDataSize); + + // Bind image and buffer to desc set. + descSet->UpdateDescriptorSetImages(SHGraphicsConstants::DescriptorSetIndex::FONT_DATA, SHGraphicsConstants::DescriptorSetBindings::FONT_BITMAP_DATA); + + descSet->UpdateDescriptorSetBuffer(SHGraphicsConstants::DescriptorSetIndex::FONT_DATA, SHGraphicsConstants::DescriptorSetBindings::FONT_MATRIX_DATA); + + + } + + std::unordered_map SHFont::GetUnicodeIndexing(void) const noexcept + { + return unicodeIndexing; + } + + SHADE::Handle SHFont::GetFontAsset(void) const noexcept + { + return fontAsset; } } \ 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 index c1a8d30a..215ef6fd 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHFont.h @@ -14,6 +14,7 @@ namespace SHADE class SHVkImage; class SHVkImageView; class SHVkBuffer; + class SHVkQueue; class SHFont { @@ -40,7 +41,10 @@ namespace SHADE std::unordered_map unicodeIndexing; public: - SHFont (Handle const& inLogicalDeviceHdl, Handle commandPool, Handle descPool, SHFontAsset const& asset) noexcept; + SHFont (Handle inLogicalDeviceHdl, Handle queue, Handle commandPool, Handle descPool, SHFontAsset& asset) noexcept; + + std::unordered_map GetUnicodeIndexing (void) const noexcept; + Handle GetFontAsset (void) const noexcept; }; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRendererComponent.h b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRendererComponent.h index 0b36ac9b..769665bc 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRendererComponent.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRendererComponent.h @@ -4,11 +4,22 @@ #include #include "ECS_Base/Components/SHComponent.h" #include "Math/SHColour.h" +#include "Resource/SHHandle.h" namespace SHADE { + class SHFont; + class SHVkDescriptorSetGroup; + class SHVkBuffer; + class SHTextRendererComponent final : public SHComponent { + public: + static constexpr uint32_t MAX_CHARACTERS = 500; + + private: + using TextIndexingType = uint32_t; + private: //! Text required to be rendered std::string text; @@ -19,6 +30,15 @@ namespace SHADE //! Requires to recompute the positions of each glyph/character bool requiresRecompute; + //! Handle to the font used to render the text + Handle fontHandle; + + //! We want to index into the buffer with matrices + Handle indexingDataBuffer; + + //! character position data for each letter in the text + Handle charPositionDataBuffer; + void MakeDirty (void) noexcept; void Clean (void) noexcept; @@ -31,6 +51,7 @@ namespace SHADE void SetText (std::string_view newText) noexcept; std::string const& GetText (void) const noexcept; + friend class SHTextRenderingSubSystem; }; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRenderingSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRenderingSubSystem.cpp index 81f51ada..f4705f32 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRenderingSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRenderingSubSystem.cpp @@ -1,7 +1,125 @@ #include "SHpch.h" #include "SHTextRenderingSubSystem.h" +#include "Graphics/MiddleEnd/TextRendering/SHTextRendererComponent.h" +#include "ECS_Base/Managers/SHComponentManager.h" +#include "Math/Vector/SHVec4.h" +#include "Graphics/Devices/SHVkLogicalDevice.h" +#include "Graphics/MiddleEnd/TextRendering/SHFont.h" +#include "Graphics/Buffers/SHVkBuffer.h" namespace SHADE { - + void SHTextRenderingSubSystem::RecomputePositions(SHTextRendererComponent& textComp) noexcept + { + if (textComp.text.empty() || textComp.fontHandle) + return; + + // Create the buffer + if (!textComp.indexingDataBuffer) + textComp.indexingDataBuffer = logicalDevice->CreateBuffer(SHTextRendererComponent::MAX_CHARACTERS * sizeof(uint32_t), nullptr, SHTextRendererComponent::MAX_CHARACTERS * sizeof(uint32_t), vk::BufferUsageFlagBits::eVertexBuffer, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT); + + if (!textComp.charPositionDataBuffer) + textComp.indexingDataBuffer = logicalDevice->CreateBuffer(SHTextRendererComponent::MAX_CHARACTERS * sizeof(SHVec4), nullptr, SHTextRendererComponent::MAX_CHARACTERS * sizeof(SHVec4), vk::BufferUsageFlagBits::eVertexBuffer, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT); + + // For indexing font transformation in the shader + std::vector indexingData; + + // For placing glyphs correctly + std::vector charPositionData; + + // Baseline relative to entity with this component + SHVec4 baselineOrigin{ 0.0f, 0.0f, 0.0f, 1.0f }; + + // Number of characters in the string + uint32_t numChars = static_cast(textComp.text.size()); + + // Get a ref to the transform indices + auto const& glyphTransformIndices = textComp.fontHandle->GetUnicodeIndexing(); + + // Get a ref to the glyph transformations + auto const& glyphTransforms = textComp.fontHandle->GetFontAsset()->glyphTransformations; + + bool dueNextLine = false; + + // for every character + for (uint32_t i = 0; i < numChars; ++i) + { + SHTextRendererComponent::TextIndexingType index = glyphTransformIndices.at(textComp.text[i]); + + // Copy baseline + SHVec4 characterPos = baselineOrigin; + + // Get where to draw the glyph relative to the baseline + characterPos[0] += glyphTransforms[index].m[SHFontAsset::BASELINE_LEFT_MATRIX_INDEX_ROW][SHFontAsset::BASELINE_LEFT_MATRIX_INDEX_COL]; // Inside the matrix but not used in the shader so this value has no positional value + characterPos[1] += glyphTransforms[index].m[SHFontAsset::BASELINE_BOTTOM_MATRIX_INDEX_ROW][SHFontAsset::BASELINE_BOTTOM_MATRIX_INDEX_COL]; // Inside the matrix but not used in the shader so this value has no positional value + + indexingData.push_back(index); + charPositionData.push_back(characterPos); + + // if not the last character + if (i != numChars - 1) + { + // Get the advance and move the baseline + double advance = 0.0; + textComp.fontHandle->GetFontAsset()->fontGeometry.getAdvance(advance, textComp.text[i], textComp.text[static_cast(i) + 1]); + baselineOrigin[0] += static_cast(advance); + + //if (baselineOrigin[0] >= textComp.estimatedLineLength) + //{ + // dueNextLine = true; + //} + } + + //if (dueNextLine && textComp.renderedText[i] == ' ') + //{ + // baselineOrigin[0] = 0.0f; + // baselineOrigin[1] -= textComp.lineSpacing; + // dueNextLine = false; + //} + } + + textComp.indexingDataBuffer->WriteToMemory(indexingData.data(), indexingData.size() * sizeof (SHTextRendererComponent::TextIndexingType),0, 0); + textComp.charPositionDataBuffer->WriteToMemory(charPositionData.data(), charPositionData.size() * sizeof (SHVec4), 0, 0); + + indexingData.clear(); + charPositionData.clear(); + + } + + void SHTextRenderingSubSystem::Init(Handle device, Handle descPool) noexcept + { + logicalDevice = device; + } + + void SHTextRenderingSubSystem::Run(uint32_t frameIndex) noexcept + { + auto& textRendererComps = SHComponentManager::GetDense(); + + for (auto& comp : textRendererComps) + { + // If the component is dirty + if (comp.requiresRecompute) + { + RecomputePositions(comp); + comp.Clean(); + } + + } + } + + void SHTextRenderingSubSystem::Render(void) noexcept + { + auto& textRendererComps = SHComponentManager::GetDense(); + for (auto& comp : textRendererComps) + { + // draw the component + + } + } + + void SHTextRenderingSubSystem::Exit(void) noexcept + { + + } + } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRenderingSubSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRenderingSubSystem.h index 90879f9d..ec43c85b 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRenderingSubSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TextRendering/SHTextRenderingSubSystem.h @@ -11,15 +11,22 @@ namespace SHADE class SHVkBuffer; class SHLightComponent; class SHVkCommandBuffer; - + class SHTextRendererComponent; class SHTextRenderingSubSystem { private: - + + //! Logical device for creation and destruction + Handle logicalDevice; + + private: + void RecomputePositions(SHTextRendererComponent& textComp) noexcept; + public: void Init(Handle device, Handle descPool) noexcept; void Run(uint32_t frameIndex) noexcept; + void Render (void) noexcept; void Exit(void) noexcept;