From dd013580a0504f2df888033392f4cbb7003fb69c Mon Sep 17 00:00:00 2001 From: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:16:20 +0300 Subject: [PATCH] Initial SDF font generation work. --- .gitmodules | 3 + UnleashedRecomp/CMakeLists.txt | 2 + UnleashedRecomp/gpu/imgui_font_builder.cpp | 86 ++++++++++++++++++++++ UnleashedRecomp/gpu/imgui_font_builder.h | 3 + UnleashedRecomp/gpu/imgui_snapshot.h | 3 +- UnleashedRecomp/gpu/video.cpp | 74 +++++++++---------- thirdparty/CMakeLists.txt | 6 ++ thirdparty/msdf-atlas-gen | 1 + vcpkg.json | 3 +- 9 files changed, 140 insertions(+), 41 deletions(-) create mode 100644 UnleashedRecomp/gpu/imgui_font_builder.cpp create mode 100644 UnleashedRecomp/gpu/imgui_font_builder.h create mode 160000 thirdparty/msdf-atlas-gen diff --git a/.gitmodules b/.gitmodules index 2d13581c..1accc7a0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "UnleashedRecompResources"] path = UnleashedRecompResources url = https://github.com/hedge-dev/UnleashedRecompResources.git +[submodule "thirdparty/msdf-atlas-gen"] + path = thirdparty/msdf-atlas-gen + url = https://github.com/Chlumsky/msdf-atlas-gen.git diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 00455941..8ffdb1c8 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -88,6 +88,7 @@ set(SWA_CPU_CXX_SOURCES set(SWA_GPU_CXX_SOURCES "gpu/video.cpp" "gpu/imgui_common.cpp" + "gpu/imgui_font_builder.cpp" "gpu/imgui_snapshot.cpp" "gpu/rhi/plume_d3d12.cpp" "gpu/rhi/plume_vulkan.cpp" @@ -258,6 +259,7 @@ target_link_libraries(UnleashedRecomp PRIVATE magic_enum::magic_enum unofficial::tiny-aes-c::tiny-aes-c nfd::nfd + msdf-atlas-gen::msdf-atlas-gen ) target_include_directories(UnleashedRecomp PRIVATE diff --git a/UnleashedRecomp/gpu/imgui_font_builder.cpp b/UnleashedRecomp/gpu/imgui_font_builder.cpp new file mode 100644 index 00000000..f844e910 --- /dev/null +++ b/UnleashedRecomp/gpu/imgui_font_builder.cpp @@ -0,0 +1,86 @@ +#include "imgui_font_builder.h" + +#include + +static bool FontBuilder_Build(ImFontAtlas* atlas) +{ + auto freeType = msdfgen::initializeFreetype(); + + std::vector glyphs; + std::vector> ranges; + + for (auto& configData : atlas->ConfigData) + { + msdf_atlas::Charset charset; + const ImWchar* glyphRanges = configData.GlyphRanges; + while (*glyphRanges != NULL) + { + for (ImWchar i = glyphRanges[0]; i <= glyphRanges[1]; i++) + charset.add(i); + + glyphRanges += 2; + } + + size_t index = glyphs.size(); + + auto font = msdfgen::loadFontData(freeType, reinterpret_cast(configData.FontData), configData.FontDataSize); + + msdf_atlas::FontGeometry fontGeometry(&glyphs); + fontGeometry.loadCharset(font, 1.0, charset); + + msdfgen::destroyFont(font); + + ranges.emplace_back(index, glyphs.size() - index); + } + + for (auto& glyph : glyphs) + glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, 3.0, 0); + + msdf_atlas::TightAtlasPacker packer; + packer.setMinimumScale(32.0); + packer.pack(glyphs.data(), glyphs.size()); + + int width = 0, height = 0; + packer.getDimensions(width, height); + + msdf_atlas::ImmediateAtlasGenerator> generator(width, height); + generator.generate(glyphs.data(), glyphs.size()); + + for (size_t i = 0; i < atlas->ConfigData.size(); i++) + { + auto& config = atlas->ConfigData[i]; + config.DstFont->FontSize = config.SizePixels; + config.DstFont->ConfigData = &config; + config.DstFont->ConfigDataCount = 1; + config.DstFont->ContainerAtlas = atlas; + // TODO: ascent? descent? wat do they mean + + auto& [index, count] = ranges[i]; + for (size_t j = 0; j < count; j++) + { + auto& glyph = glyphs[index + j]; + double x0, y0, x1, y1, u0, v0, u1, v1; + glyph.getQuadPlaneBounds(x0, y0, x1, y1); + glyph.getQuadAtlasBounds(u0, v0, u1, v1); + config.DstFont->AddGlyph(&config, glyph.getCodepoint(), x0, y0, x1, y1, u0, v0, u1, v1, glyph.getAdvance()); + } + + config.DstFont->BuildLookupTable(); + } + + atlas->TexReady = true; + atlas->TexPixelsUseColors = true; + atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(width * height * 4); + atlas->TexWidth = width; + atlas->TexHeight = height; + atlas->TexUvScale = { 1.0f / width, 1.0f / height }; + + auto bitmapRef = (msdfgen::BitmapConstRef)generator.atlasStorage(); + memcpy(atlas->TexPixelsRGBA32, bitmapRef.pixels, width * height * 4); + + msdfgen::deinitializeFreetype(freeType); + + return true; +} + +ImFontBuilderIO g_fontBuilderIO = { FontBuilder_Build }; diff --git a/UnleashedRecomp/gpu/imgui_font_builder.h b/UnleashedRecomp/gpu/imgui_font_builder.h new file mode 100644 index 00000000..e0def5d2 --- /dev/null +++ b/UnleashedRecomp/gpu/imgui_font_builder.h @@ -0,0 +1,3 @@ +#pragma once + +extern ImFontBuilderIO g_fontBuilderIO; diff --git a/UnleashedRecomp/gpu/imgui_snapshot.h b/UnleashedRecomp/gpu/imgui_snapshot.h index 26b60d0e..8613637e 100644 --- a/UnleashedRecomp/gpu/imgui_snapshot.h +++ b/UnleashedRecomp/gpu/imgui_snapshot.h @@ -34,8 +34,7 @@ struct ImDrawDataSnapshot // Undefine this to generate a font atlas file in working directory. // You also need to do this if you are testing localization, as only // characters in the locale get added to the atlas. -// Don't forget to compress the generated atlas texture to BC4 with no mips! -#define ENABLE_IM_FONT_ATLAS_SNAPSHOT +//#define ENABLE_IM_FONT_ATLAS_SNAPSHOT struct ImFontAtlasSnapshot { diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index be7ec78d..7a0883de 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -1,35 +1,37 @@ -#include +#include "video.h" -#include -#include +#include "imgui_common.h" +#include "imgui_snapshot.h" +#include "imgui_font_builder.h" + +#include #include #include #include +#include +#include +#include #include #include -#include +#include +#include #include +#include #include #include #include #include +#include #include #include -#include - -#include "imgui_snapshot.h" -#include "imgui_common.h" -#include "video.h" #include #include #include +#include -#include -#include -#include -#include - -#include +#if defined(ASYNC_PSO_DEBUG) || defined(PSO_CACHING) +#include +#endif #include "../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h" #include "shader/copy_vs.hlsl.dxil.h" @@ -38,6 +40,8 @@ #include "shader/csd_filter_ps.hlsl.spirv.h" #include "shader/enhanced_motion_blur_ps.hlsl.dxil.h" #include "shader/enhanced_motion_blur_ps.hlsl.spirv.h" +#include "shader/gamma_correction_ps.hlsl.dxil.h" +#include "shader/gamma_correction_ps.hlsl.spirv.h" #include "shader/gaussian_blur_3x3.hlsl.dxil.h" #include "shader/gaussian_blur_3x3.hlsl.spirv.h" #include "shader/gaussian_blur_5x5.hlsl.dxil.h" @@ -46,16 +50,14 @@ #include "shader/gaussian_blur_7x7.hlsl.spirv.h" #include "shader/gaussian_blur_9x9.hlsl.dxil.h" #include "shader/gaussian_blur_9x9.hlsl.spirv.h" -#include "shader/gamma_correction_ps.hlsl.dxil.h" -#include "shader/gamma_correction_ps.hlsl.spirv.h" #include "shader/imgui_ps.hlsl.dxil.h" #include "shader/imgui_ps.hlsl.spirv.h" #include "shader/imgui_vs.hlsl.dxil.h" #include "shader/imgui_vs.hlsl.spirv.h" -#include "shader/movie_vs.hlsl.dxil.h" -#include "shader/movie_vs.hlsl.spirv.h" #include "shader/movie_ps.hlsl.dxil.h" #include "shader/movie_ps.hlsl.spirv.h" +#include "shader/movie_vs.hlsl.dxil.h" +#include "shader/movie_vs.hlsl.spirv.h" #include "shader/resolve_msaa_depth_2x.hlsl.dxil.h" #include "shader/resolve_msaa_depth_2x.hlsl.spirv.h" #include "shader/resolve_msaa_depth_4x.hlsl.dxil.h" @@ -63,10 +65,6 @@ #include "shader/resolve_msaa_depth_8x.hlsl.dxil.h" #include "shader/resolve_msaa_depth_8x.hlsl.spirv.h" -#if defined(ASYNC_PSO_DEBUG) || defined(PSO_CACHING) -#include -#endif - extern "C" { __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; @@ -1112,6 +1110,8 @@ struct ImGuiPushConstants ImVec2 scale{ 1.0f, 1.0f }; }; +extern ImFontBuilderIO g_fontBuilderIO; + static void CreateImGuiBackend() { ImGuiIO& io = ImGui::GetIO(); @@ -1123,8 +1123,8 @@ static void CreateImGuiBackend() IM_DELETE(io.Fonts); io.Fonts = ImFontAtlasSnapshot::Load(); #else - io.Fonts->TexDesiredWidth = 4096; io.Fonts->AddFontDefault(); + io.Fonts->FontBuilderIO = &g_fontBuilderIO; ImFontAtlasSnapshot::GenerateGlyphRanges(); #endif @@ -1137,17 +1137,16 @@ static void CreateImGuiBackend() ImGui_ImplSDL2_InitForOther(Window::s_pWindow); - RenderComponentMapping componentMapping(RenderSwizzle::ONE, RenderSwizzle::ONE, RenderSwizzle::ONE, RenderSwizzle::R); - #ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT - g_imFontTexture = LoadTexture(decompressZstd(g_im_font_atlas_texture, g_im_font_atlas_texture_uncompressed_size).get(), - g_im_font_atlas_texture_uncompressed_size, componentMapping); + g_imFontTexture = LoadTexture( + decompressZstd(g_im_font_atlas_texture, g_im_font_atlas_texture_uncompressed_size).get(), g_im_font_atlas_texture_uncompressed_size); #else g_imFontTexture = std::make_unique(ResourceType::Texture); + io.Fonts->Build(); uint8_t* pixels; int width, height; - io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height); + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); RenderTextureDesc textureDesc; textureDesc.dimension = RenderTextureDimension::TEXTURE_2D; @@ -1156,17 +1155,17 @@ static void CreateImGuiBackend() textureDesc.depth = 1; textureDesc.mipLevels = 1; textureDesc.arraySize = 1; - textureDesc.format = RenderFormat::R8_UNORM; + textureDesc.format = RenderFormat::R8G8B8A8_UNORM; g_imFontTexture->textureHolder = g_device->createTexture(textureDesc); g_imFontTexture->texture = g_imFontTexture->textureHolder.get(); - uint32_t rowPitch = (width + PITCH_ALIGNMENT - 1) & ~(PITCH_ALIGNMENT - 1); + uint32_t rowPitch = (width * 4 + PITCH_ALIGNMENT - 1) & ~(PITCH_ALIGNMENT - 1); uint32_t slicePitch = (rowPitch * height + PLACEMENT_ALIGNMENT - 1) & ~(PLACEMENT_ALIGNMENT - 1); auto uploadBuffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(slicePitch)); uint8_t* mappedMemory = reinterpret_cast(uploadBuffer->map()); - if (rowPitch == width) + if (rowPitch == (width * 4)) { memcpy(mappedMemory, pixels, slicePitch); } @@ -1174,8 +1173,8 @@ static void CreateImGuiBackend() { for (size_t i = 0; i < height; i++) { - memcpy(mappedMemory, pixels, width); - pixels += width; + memcpy(mappedMemory, pixels, width * 4); + pixels += width * 4; mappedMemory += rowPitch; } } @@ -1188,7 +1187,7 @@ static void CreateImGuiBackend() g_copyCommandList->copyTextureRegion( RenderTextureCopyLocation::Subresource(g_imFontTexture->texture, 0), - RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), RenderFormat::R8_UNORM, width, height, 1, rowPitch, 0)); + RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), RenderFormat::R8G8B8A8_UNORM, width, height, 1, rowPitch / 4, 0)); }); g_imFontTexture->layout = RenderTextureLayout::COPY_DEST; @@ -1197,7 +1196,6 @@ static void CreateImGuiBackend() textureViewDesc.format = textureDesc.format; textureViewDesc.dimension = RenderTextureViewDimension::TEXTURE_2D; textureViewDesc.mipLevels = 1; - textureViewDesc.componentMapping = componentMapping; g_imFontTexture->textureView = g_imFontTexture->texture->createTextureView(textureViewDesc); g_imFontTexture->descriptorIndex = g_textureDescriptorAllocator.allocate(); @@ -1261,7 +1259,7 @@ static void CreateImGuiBackend() ddspp::Header header; ddspp::HeaderDXT10 headerDX10; - ddspp::encode_header(ddspp::R8_UNORM, width, height, 1, ddspp::Texture2D, 1, 1, header, headerDX10); + ddspp::encode_header(ddspp::R8G8B8A8_UNORM, width, height, 1, ddspp::Texture2D, 1, 1, header, headerDX10); file = fopen("im_font_atlas.dds", "wb"); if (file) @@ -1269,7 +1267,7 @@ static void CreateImGuiBackend() fwrite(&ddspp::DDS_MAGIC, 4, 1, file); fwrite(&header, sizeof(header), 1, file); fwrite(&headerDX10, sizeof(headerDX10), 1, file); - fwrite(pixels, 1, width * height, file); + fwrite(pixels, 4, width * height, file); fclose(file); } #endif diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 9f43e072..696296ca 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -1,4 +1,10 @@ +set(MSDF_ATLAS_BUILD_STANDALONE OFF) +set(MSDF_ATLAS_USE_SKIA OFF) +set(MSDF_ATLAS_NO_ARTERY_FONT ON) +set(MSDFGEN_DISABLE_PNG ON) + add_subdirectory(${SWA_THIRDPARTY_ROOT}/PowerRecomp) add_subdirectory(${SWA_THIRDPARTY_ROOT}/ShaderRecomp) add_subdirectory(${SWA_THIRDPARTY_ROOT}/o1heap) add_subdirectory(${SWA_THIRDPARTY_ROOT}/fshasher) +add_subdirectory(${SWA_THIRDPARTY_ROOT}/msdf-atlas-gen) diff --git a/thirdparty/msdf-atlas-gen b/thirdparty/msdf-atlas-gen new file mode 160000 index 00000000..7e8d3464 --- /dev/null +++ b/thirdparty/msdf-atlas-gen @@ -0,0 +1 @@ +Subproject commit 7e8d34645a67c6e4def5d281f7adec1c2501dc49 diff --git a/vcpkg.json b/vcpkg.json index d778cb96..e93e1219 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -23,6 +23,7 @@ }, "magic-enum", "nativefiledialog-extended", - "miniaudio" + "miniaudio", + "freetype" ] }