Text Rendering WIP

This commit is contained in:
Brandon Mak 2022-11-14 10:11:47 +08:00
parent 25841c6899
commit 898b7fbf2c
16 changed files with 256 additions and 55 deletions

View File

@ -38,7 +38,9 @@ project "SHADE_Application"
"%{IncludeDir.spdlog}/include",
"%{IncludeDir.tinyddsloader}",
"%{IncludeDir.reactphysics3d}\\include",
"%{IncludeDir.yamlcpp}"
"%{IncludeDir.yamlcpp}",
"%{IncludeDir.msdf_atlas_gen}",
"%{IncludeDir.msdfgen}"
}
externalwarnings "Off"

View File

@ -31,8 +31,8 @@ namespace SHADE
//! Data containing character and uv transformation data and other misc data
std::vector<GlyphData> glyphTransformations;
//! The actual data of the atlas to go into the binary
std::unique_ptr<unsigned char[]> bitmapData;
//! The actual data of the atlas to go into the binary. Was unique_ptr, vector easier to copy.
std::vector<unsigned char> bitmapData;
//! Width of the bitmap
uint32_t bitmapWidth;
@ -42,5 +42,6 @@ namespace SHADE
//! Font geometry required to get kerning from
msdf_atlas::FontGeometry fontGeometry;
};
}

View File

@ -51,8 +51,8 @@ namespace SHADE
uint32_t bytesRequired = newFontAsset->bitmapWidth * newFontAsset->bitmapHeight * SHFontAsset::BYTES_PER_CHANNEL * SHFontAsset::NUM_CHANNELS;
// Read the bitmap
newFontAsset->bitmapData = std::make_unique<unsigned char[]>(bytesRequired);
file.read (reinterpret_cast<char*>(newFontAsset->bitmapData.get()), bytesRequired);
newFontAsset->bitmapData.resize(bytesRequired);
file.read (reinterpret_cast<char*>(newFontAsset->bitmapData.data()), bytesRequired);
file.close();
@ -68,7 +68,6 @@ namespace SHADE
- 4x4 Matrix data (stores UV transform + extra data)
- Actual atlas texture
*/
}
}

View File

@ -25,12 +25,9 @@ namespace SHADE
{
if (cmdBufferHdl && (bufferUsageFlags & vk::BufferUsageFlagBits::eTransferDst))
{
vk::BufferCopy copyRegion
{
.srcOffset = 0,
.dstOffset = 0,
.size = sizeStored,
};
vk::BufferCopy copyRegion{};
PrepareBufferCopy(copyRegion);
cmdBufferHdl->GetVkCommandBuffer().copyBuffer(stagingBuffer, vkBuffer, 1, &copyRegion);
}
}
@ -54,6 +51,13 @@ namespace SHADE
vmaFlushAllocation(vmaAllocator, alloc, srcOffset, dstOffset);
}
void SHVkBuffer::PrepareBufferCopy(vk::BufferCopy& bufferCopy) noexcept
{
bufferCopy.srcOffset = 0;
bufferCopy.dstOffset = 0;
bufferCopy.size = sizeStored;
}
vk::Buffer SHVkBuffer::GetVkBuffer(void) const noexcept
{
return vkBuffer;

View File

@ -98,6 +98,7 @@ namespace SHADE
void ResizeNoCopy (uint32_t newSize);
void ResizeReplace (uint32_t newSize, void* data, uint32_t srcSize);
void FlushAllocation (uint32_t srcOffset, uint32_t dstOffset) noexcept;
void PrepareBufferCopy (vk::BufferCopy& bufferCopy) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */

View File

@ -303,6 +303,22 @@ namespace SHADE
CreateFramebufferImage();
}
void SHVkImage::PrepareImageCopy(std::vector<vk::BufferImageCopy>& bufferImageCopy) noexcept
{
for (uint32_t i = 0; i < mipOffsets.size(); ++i)
{
bufferImageCopy[i].bufferOffset = mipOffsets[i];
bufferImageCopy[i].bufferRowLength = 0; // for padding
bufferImageCopy[i].bufferImageHeight = 0; // for padding
bufferImageCopy[i].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor; // TODO: Need to change this to base it off image format.
bufferImageCopy[i].imageSubresource.mipLevel = i;
bufferImageCopy[i].imageSubresource.baseArrayLayer = 0; // TODO: Array textures not supported yet
bufferImageCopy[i].imageSubresource.layerCount = layerCount;
bufferImageCopy[i].imageOffset = vk::Offset3D{ 0,0,0 };
bufferImageCopy[i].imageExtent = vk::Extent3D{ width >> i, height >> i, 1 };
}
}
void SHVkImage::LinkWithExteriorImage(vk::Image inVkImage, vk::ImageType type, uint32_t inWidth, uint32_t inHeight, uint32_t inDepth, uint32_t layers, uint8_t levels, vk::Format format, vk::ImageUsageFlags flags) noexcept
{
vkImage = inVkImage;

View File

@ -138,6 +138,7 @@ namespace SHADE
void TransferToDeviceResource (Handle<SHVkCommandBuffer> cmdBufferHdl) noexcept;
void PrepareImageTransitionInfo (vk::ImageLayout oldLayout, vk::ImageLayout newLayout, vk::ImageMemoryBarrier& barrier) noexcept;
void HandleResizeFramebufferImage(uint32_t newWidth, uint32_t newHeight) noexcept;
void PrepareImageCopy (std::vector<vk::BufferImageCopy>& bufferImageCopy) noexcept;
/*-----------------------------------------------------------------------*/
/* GETTERS AND SETTERS */

View File

@ -831,6 +831,11 @@ namespace SHADE
return texLibrary.GetTextureHandle(textureId);
}
Handle<SHFont> SHGraphicsSystem::AddFont(SHFontAsset const& fontAsset) const noexcept
{
}
#pragma endregion ADD_REMOVE
#pragma region ROUTINES
@ -911,6 +916,7 @@ namespace SHADE
#pragma region MISC
void SHGraphicsSystem::PrepareResize(uint32_t newWidth, uint32_t newHeight) noexcept
{
resizeWidth = newWidth;

View File

@ -33,6 +33,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Graphics/MiddleEnd/Lights/SHLightingSubSystem.h"
#include "Graphics/MiddleEnd/PostProcessing/SHSSAO.h"
#include "Camera/SHCameraDirector.h"
#include "Graphics/MiddleEnd/TextRendering/SHFontLibrary.h"
namespace SHADE
{
@ -321,6 +322,21 @@ namespace SHADE
*/
/***************************************************************************/
Handle<SHTexture> GetDefaultTexture() const noexcept { return defaultTexture; }
/***************************************************************************/
/*!
\brief
Adds a font into the font library.
\param fontAsset
Font asset to add.
\return
Handle to the new font.
*/
/***************************************************************************/
Handle<SHFont> AddFont (SHFontAsset const& fontAsset) const noexcept;
void PrepareResize(uint32_t newWidth, uint32_t newHeight) noexcept;
void HandleResize(void) noexcept;
@ -386,6 +402,7 @@ namespace SHADE
SHResourceHub resourceManager;
SHMeshLibrary meshLibrary;
SHTextureLibrary texLibrary;
SHFontLibrary fontLibrary;
SHSamplerCache samplerCache;
SHMaterialInstanceCache materialInstanceCache;

View File

@ -7,7 +7,22 @@
namespace SHADE
{
SHFont::SHFont(Handle<SHVkLogicalDevice> inLogicalDeviceHdl, Handle<SHVkQueue> queue, Handle<SHVkCommandPool> commandPool, Handle<SHVkDescriptorPool> descPool, SHFontAsset& asset) noexcept
/***************************************************************************/
/*!
\brief
Non-default ctor. Prepares objects in staging buffers with data from
SHFontAsset.
\param inLogicalDeviceHdl
Logical device required for vulkan object creation.
\param asset
Font asset to copy data from.
*/
/***************************************************************************/
SHFont::SHFont(Handle<SHVkLogicalDevice> inLogicalDeviceHdl, SHFontAsset& asset) noexcept
{
/*-----------------------------------------------------------------------*/
/* PREPARE GPU DATA */
@ -16,11 +31,14 @@ namespace SHADE
// assign device for convenient usage
logicalDevice = inLogicalDeviceHdl;
// Copy the font data
fontAsset = asset;
SHImageCreateParams imageParams
{
.imageType = vk::ImageType::e2D,
.width = asset.bitmapWidth,
.height = asset.bitmapHeight,
.width = fontAsset.bitmapWidth,
.height = fontAsset.bitmapHeight,
.depth = 1,
.levels = 1,
.arrayLayers = 1,
@ -29,53 +47,58 @@ namespace SHADE
.createFlags = {}
};
uint32_t bytesRequired = asset.bitmapWidth * asset.bitmapHeight * SHFontAsset::BYTES_PER_CHANNEL * SHFontAsset::NUM_CHANNELS;
uint32_t bytesRequired = fontAsset.bitmapWidth * fontAsset.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, {});
bitmapDataImage = logicalDevice->CreateImage(imageParams, fontAsset.bitmapData.data(), bytesRequired, { &mipOffset, 1 }, VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, {});
// Amount of data required to hold matrices for all glyphs
uint32_t glyphDataSize = asset.glyphTransformations.size() * sizeof (SHMatrix);
uint32_t glyphDataSize = fontAsset.glyphTransformations.size() * sizeof (SHMatrix);
// For indexing
for (uint32_t i = 0; i < fontAsset->glyphs.size(); ++i)
unicodeIndexing.emplace(fontAsset->glyphs[i], i);
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, {});
matrixDataBuffer = logicalDevice->CreateBuffer(glyphDataSize, fontAsset.glyphTransformations.data(), glyphDataSize, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, {});
//// Prepare pre and post transfer barrier
//bitmapDataImage->PrepareImageTransitionInfo(vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, preTransferBarrier);
//bitmapDataImage->PrepareImageTransitionInfo(vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, postTransferBarrier);
}
/***************************************************************************/
/*!
\brief
Transfers data from staging buffers to GPU. Does not issue any of the
barriers (we want this done outside).
\param commandBuffer
Command buffer used to
\return
*/
/***************************************************************************/
void SHFont::TransferToGPU(Handle<SHVkCommandBuffer> commandBuffer) noexcept
{
/*-----------------------------------------------------------------------*/
/* COMMANDS TO TRANSFER TO DEVICE MEMORY */
/*-----------------------------------------------------------------------*/
// Create command buffer to record transfer from host to device
Handle<SHVkCommandBuffer> 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();
void SHFont::DoPostTransfer(Handle<SHVkDescriptorPool> descPool) noexcept
{
/*-----------------------------------------------------------------------*/
/* CREATE IMAGE VIEW */
/*-----------------------------------------------------------------------*/
@ -92,7 +115,6 @@ namespace SHADE
};
bitmapDataImageView = bitmapDataImage->CreateImageView(logicalDevice, bitmapDataImage, viewDetails);
/*-----------------------------------------------------------------------*/
/* DESCRIPTORS */
/*-----------------------------------------------------------------------*/
@ -106,14 +128,12 @@ namespace SHADE
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);
SHGraphicsConstants::DescriptorSetBindings::FONT_MATRIX_DATA, { &matrixDataBuffer, 1 }, 0, fontAsset.glyphTransformations.size() * sizeof(SHMatrix));
// 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<msdfgen::unicode_t, uint32_t> SHFont::GetUnicodeIndexing(void) const noexcept
@ -121,9 +141,14 @@ namespace SHADE
return unicodeIndexing;
}
SHADE::Handle<SHADE::SHFontAsset> SHFont::GetFontAsset(void) const noexcept
SHFontAsset const& SHFont::GetFontAsset(void) const noexcept
{
return fontAsset;
}
Handle<SHVkImage> SHFont::GetImage(void) const noexcept
{
return bitmapDataImage;
}
}

View File

@ -23,7 +23,7 @@ namespace SHADE
Handle<SHVkLogicalDevice> logicalDevice;
//! Font asset contains exactly what we need, so we'll use it
Handle<SHFontAsset> fontAsset;
SHFontAsset fontAsset;
//! Device memory that stores bitmap data
Handle<SHVkImage> bitmapDataImage;
@ -40,11 +40,27 @@ namespace SHADE
//! Used for getting the correct indices into the matrix data buffer
std::unordered_map<msdfgen::unicode_t, uint32_t> unicodeIndexing;
public:
SHFont (Handle<SHVkLogicalDevice> inLogicalDeviceHdl, Handle<SHVkQueue> queue, Handle<SHVkCommandPool> commandPool, Handle<SHVkDescriptorPool> descPool, SHFontAsset& asset) noexcept;
////! To transition images for transfer ops
//vk::ImageMemoryBarrier preTransferBarrier;
////! To transition images for shader reads
//vk::ImageMemoryBarrier postTransferBarrier;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
SHFont (Handle<SHVkLogicalDevice> inLogicalDeviceHdl, SHFontAsset& asset) noexcept;
void TransferToGPU (Handle<SHVkCommandBuffer> commandBuffer) noexcept;
void DoPostTransfer (Handle<SHVkDescriptorPool> descPool) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
std::unordered_map<msdfgen::unicode_t, uint32_t> GetUnicodeIndexing (void) const noexcept;
Handle<SHFontAsset> GetFontAsset (void) const noexcept;
SHFontAsset const& GetFontAsset (void) const noexcept;
Handle<SHVkImage> GetImage (void) const noexcept;
};
}

View File

@ -0,0 +1,72 @@
#include "SHpch.h"
#include "SHFontLibrary.h"
#include "Graphics/Images/SHVkImage.h"
#include "Graphics/Commands/SHVkCommandPool.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
For adding fonts to the library
\param asset
The asset we want to create an SHFont from.
*/
/***************************************************************************/
Handle<SHFont> SHFontLibrary::AddFont(Handle<SHVkLogicalDevice> logicalDevice, SHResourceHub& resourceHub, SHFontAsset const& asset) noexcept
{
// Create new font
Handle<SHFont> newFont = resourceHub.Create<SHFont>(logicalDevice, asset);
// emplace new barriers and prepare them for transfer ops
preTransferBarriers.emplace_back();
postTransferBarriers.emplace_back();
newFont->GetImage()->PrepareImageTransitionInfo(vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, preTransferBarriers[preTransferBarriers.size() - 1]);
newFont->GetImage()->PrepareImageTransitionInfo(vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, postTransferBarriers[postTransferBarriers.size() - 1]);
}
/***************************************************************************/
/*!
\brief
Transfers staging buffer font memory to the GPU for optimized access.
\param cmdBuffer
Command buffer to record the transfer operations.
\param descPool
\return
*/
/***************************************************************************/
void SHFontLibrary::BuildFonts(Handle<SHVkCommandPool> cmdPool, Handle<SHVkDescriptorPool> descPool, SHResourceHub& resourceHub) noexcept
{
//std::vector
// allocate new command buffer
Handle<SHVkCommandBuffer> transferCommandBuffer = cmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY);
// Begin recording transfer ops
transferCommandBuffer->BeginRecording();
// Transition image to dst
transferCommandBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, preTransferBarriers);
// Transition dst to shader read
transferCommandBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {}, postTransferBarriers);
// End recording for transfer ops
transferCommandBuffer->EndRecording();
// Free it
resourceHub.Free(transferCommandBuffer);
}
}

View File

@ -0,0 +1,34 @@
#pragma once
#include "SHFont.h"
#include <vector>
#include "Graphics/SHVulkanIncludes.h"
namespace SHADE
{
class SHVkLogicalDevice;
class SHVkDescriptorPool;
class SHVkCommandPool;
class SHVkCommandBuffer;
class SHVkQueue;
class SHFontLibrary
{
private:
//! Handles to all the fonts usable in SHTextRendererComponents
std::vector<Handle<SHFont>> fonts;
//! for indexing
std::unordered_map<decltype (std::declval<Handle<SHFont>>().GetId().Raw), uint32_t> fontIndexing;
//! For transitioning images for transfer operations
std::vector<vk::ImageMemoryBarrier> preTransferBarriers;
//! For transitioning images for shader reads
std::vector<vk::ImageMemoryBarrier> postTransferBarriers;
public:
Handle<SHFont> AddFont (Handle<SHVkLogicalDevice> logicalDevice, SHResourceHub& resourceHub, SHFontAsset const& asset) noexcept;
void BuildFonts (Handle<SHVkCommandPool> cmdPool, Handle<SHVkDescriptorPool> descPool, SHResourceHub& resourceHub) noexcept;
};
}

View File

@ -38,7 +38,7 @@ namespace SHADE
auto const& glyphTransformIndices = textComp.fontHandle->GetUnicodeIndexing();
// Get a ref to the glyph transformations
auto const& glyphTransforms = textComp.fontHandle->GetFontAsset()->glyphTransformations;
auto const& glyphTransforms = textComp.fontHandle->GetFontAsset().glyphTransformations;
bool dueNextLine = false;
@ -62,7 +62,7 @@ namespace SHADE
{
// Get the advance and move the baseline
double advance = 0.0;
textComp.fontHandle->GetFontAsset()->fontGeometry.getAdvance(advance, textComp.text[i], textComp.text[static_cast<uint64_t>(i) + 1]);
textComp.fontHandle->GetFontAsset().fontGeometry.getAdvance(advance, textComp.text[i], textComp.text[static_cast<uint64_t>(i) + 1]);
baselineOrigin[0] += static_cast<float>(advance);
//if (baselineOrigin[0] >= textComp.estimatedLineLength)

View File

@ -28,6 +28,7 @@ namespace SHADE { class SHMaterial; }
#include "Graphics/MiddleEnd/Interface/SHMaterial.h"
#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h"
#include "Assets/Asset Types/SHMaterialAsset.h"
#include "Graphics/MiddleEnd/TextRendering/SHFont.h"
namespace SHADE
{
@ -41,6 +42,7 @@ namespace SHADE
template<> struct SHResourceLoader<SHVkShaderModule> { using AssetType = SHShaderAsset; };
template<> struct SHResourceLoader<SHMaterialSpec> { using AssetType = SHMaterialAsset; };
template<> struct SHResourceLoader<SHMaterial> { using AssetType = SHMaterialSpec; };
template<> struct SHResourceLoader<SHFont> { using AssetType = SHFontAsset; };
/// <summary>
/// Static class responsible for loading and caching runtime resources from their
/// serialised Asset IDs.

View File

@ -39,6 +39,7 @@ namespace SHADE
!std::is_same_v<ResourceType, SHTexture> &&
!std::is_same_v<ResourceType, SHVkShaderModule> &&
!std::is_same_v<ResourceType, SHMaterialSpec> &&
!std::is_same_v<ResourceType, SHFont> &&
!std::is_same_v<ResourceType, SHMaterial>
)
{
@ -315,5 +316,9 @@ namespace SHADE
return matHandle;
}
else if constexpr (std::is_same_v<ResourceType, SHFontAsset>)
{
}
}
}