From 0f84e52c213ec96df14becfad9574dd1d9b10ed5 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Tue, 21 Apr 2026 21:07:06 +0800 Subject: [PATCH] refactor(new_editor): unify d3d12 text run caching --- .../Win32/EditorWindowRuntimeController.cpp | 10 +- .../Win32/EditorWindowRuntimeController.h | 1 + .../app/Rendering/D3D12/D3D12UiRenderer.cpp | 370 +++++------------- .../app/Rendering/D3D12/D3D12UiRenderer.h | 73 +--- .../app/Rendering/D3D12/D3D12UiTextSystem.cpp | 31 +- .../app/Rendering/D3D12/D3D12UiTextSystem.h | 12 +- 6 files changed, 131 insertions(+), 366 deletions(-) diff --git a/new_editor/app/Platform/Win32/EditorWindowRuntimeController.cpp b/new_editor/app/Platform/Win32/EditorWindowRuntimeController.cpp index 82bd5153..d167f5c4 100644 --- a/new_editor/app/Platform/Win32/EditorWindowRuntimeController.cpp +++ b/new_editor/app/Platform/Win32/EditorWindowRuntimeController.cpp @@ -74,9 +74,10 @@ void EditorWindowRuntimeController::ClearExternalDockHostDropPreview() { } void EditorWindowRuntimeController::SetDpiScale(float dpiScale) { + m_dpiScale = dpiScale > 0.0f ? dpiScale : 1.0f; m_renderer.SetDpiScale(dpiScale); - m_textSystem.SetDpiScale(dpiScale); - m_uiRenderer.SetDpiScale(dpiScale); + m_textSystem.SetDpiScale(m_dpiScale); + m_uiRenderer.SetDpiScale(m_dpiScale); } ::XCEngine::UI::Editor::UIEditorTextMeasurer& EditorWindowRuntimeController::GetTextMeasurer() { @@ -124,7 +125,7 @@ bool EditorWindowRuntimeController::Initialize( m_windowRenderer.Shutdown(); return false; } - m_textSystem.SetDpiScale(m_renderer.GetDpiScale()); + m_textSystem.SetDpiScale(m_dpiScale); if (!m_uiRenderer.Initialize(m_windowRenderer, m_textureHost, m_textSystem)) { LogRuntimeTrace("app", "d3d12 ui renderer initialization failed"); @@ -133,7 +134,7 @@ bool EditorWindowRuntimeController::Initialize( m_windowRenderer.Shutdown(); return false; } - m_uiRenderer.SetDpiScale(m_renderer.GetDpiScale()); + m_uiRenderer.SetDpiScale(m_dpiScale); const Host::D3D12WindowRenderLoopAttachResult attachResult = m_windowRenderLoop.Attach(m_uiRenderer, m_windowRenderer); @@ -189,6 +190,7 @@ void EditorWindowRuntimeController::Shutdown() { m_textureHost.Shutdown(); m_windowRenderer.Shutdown(); m_renderer.Shutdown(); + m_dpiScale = 1.0f; } void EditorWindowRuntimeController::ResetInteractionState() { diff --git a/new_editor/app/Platform/Win32/EditorWindowRuntimeController.h b/new_editor/app/Platform/Win32/EditorWindowRuntimeController.h index f84ebad2..a527869a 100644 --- a/new_editor/app/Platform/Win32/EditorWindowRuntimeController.h +++ b/new_editor/app/Platform/Win32/EditorWindowRuntimeController.h @@ -114,6 +114,7 @@ private: float m_frameStatsDisplayAccumulatorSeconds = 0.0f; float m_displayFps = 0.0f; float m_displayFrameTimeMs = 0.0f; + float m_dpiScale = 1.0f; std::string m_frameRateText = {}; bool m_ready = false; }; diff --git a/new_editor/app/Rendering/D3D12/D3D12UiRenderer.cpp b/new_editor/app/Rendering/D3D12/D3D12UiRenderer.cpp index e942dff9..7cccd752 100644 --- a/new_editor/app/Rendering/D3D12/D3D12UiRenderer.cpp +++ b/new_editor/app/Rendering/D3D12/D3D12UiRenderer.cpp @@ -66,9 +66,6 @@ using ::XCEngine::UI::UITextureHandle; constexpr std::uint64_t kMinDynamicVertexBufferBytes = 16u * 1024u; constexpr std::uint64_t kMinDynamicIndexBufferBytes = 16u * 1024u; constexpr std::uint64_t kMinDynamicInstanceBufferBytes = 16u * 1024u; -constexpr std::uint32_t kGlyphAtlasPageWidth = 1024u; -constexpr std::uint32_t kGlyphAtlasPageHeight = 1024u; -constexpr std::uint32_t kGlyphAtlasPadding = 1u; enum class UiQuadKind : int { Textured = 0, @@ -913,25 +910,6 @@ GraphicsPipelineDesc BuildQuadUiPipelineDesc( } // namespace -bool D3D12UiRenderer::GlyphAtlasKey::operator==(const GlyphAtlasKey& other) const { - return fontFaceKey == other.fontFaceKey && - glyphIndex == other.glyphIndex && - fontEmSizeTenths == other.fontEmSizeTenths && - isSideways == other.isSideways; -} - -std::size_t D3D12UiRenderer::GlyphAtlasKeyHash::operator()( - const GlyphAtlasKey& key) const { - const std::size_t faceHash = std::hash{}(key.fontFaceKey); - const std::size_t glyphHash = std::hash{}(key.glyphIndex); - const std::size_t fontHash = std::hash{}(key.fontEmSizeTenths); - const std::size_t sidewaysHash = std::hash{}(key.isSideways); - return faceHash ^ - (glyphHash << 1u) ^ - (fontHash << 2u) ^ - (sidewaysHash << 3u); -} - bool D3D12UiRenderer::TextRunCacheKey::operator==(const TextRunCacheKey& other) const { return text == other.text && fontSizeTenths == other.fontSizeTenths && @@ -983,7 +961,6 @@ bool D3D12UiRenderer::Initialize( void D3D12UiRenderer::Shutdown() { ReleaseTextRunCache(); - ReleaseGlyphAtlas(); ReleaseCompiledDrawListCache(); if (m_textureHost != nullptr) { m_textureHost->ReleaseTexture(m_whiteTexture); @@ -996,6 +973,7 @@ void D3D12UiRenderer::Shutdown() { m_textureHost = nullptr; m_textSystem = nullptr; m_dpiScale = 1.0f; + m_compiledDrawListFrameCounter = 0u; m_lastError.clear(); } @@ -1003,7 +981,6 @@ void D3D12UiRenderer::SetDpiScale(float dpiScale) { const float resolvedScale = ClampDpiScale(dpiScale); if (std::abs(m_dpiScale - resolvedScale) > 0.0001f) { ReleaseTextRunCache(); - ReleaseGlyphAtlas(); ReleaseCompiledDrawListCache(); } m_dpiScale = resolvedScale; @@ -1044,201 +1021,10 @@ bool D3D12UiRenderer::EnsureWhiteTexture() { return true; } -bool D3D12UiRenderer::EnsureGlyphAtlasPage() { - if (m_textureHost == nullptr) { - m_lastError = "EnsureGlyphAtlasPage requires an initialized texture host."; - return false; - } - - if (!m_glyphAtlasPages.empty()) { - return true; - } - - GlyphAtlasPage page = {}; - page.width = kGlyphAtlasPageWidth; - page.height = kGlyphAtlasPageHeight; - - std::vector initialPixels( - static_cast(page.width) * static_cast(page.height) * 4u, - 0u); - std::string error = {}; - if (!m_textureHost->LoadTextureFromRgba( - initialPixels.data(), - page.width, - page.height, - page.texture, - error)) { - m_lastError = error.empty() - ? "Failed to create the glyph atlas page texture." - : error; - return false; - } - - m_glyphAtlasPages.push_back(std::move(page)); - return true; -} - -bool D3D12UiRenderer::AllocateGlyphAtlasRegion( - std::uint32_t width, - std::uint32_t height, - std::size_t& outPageIndex, - std::uint32_t& outPageX, - std::uint32_t& outPageY) { - outPageIndex = 0u; - outPageX = 0u; - outPageY = 0u; - - if (width == 0u || height == 0u) { - return false; - } - - if (!EnsureGlyphAtlasPage()) { - return false; - } - - const std::uint32_t paddedWidth = width + kGlyphAtlasPadding * 2u; - const std::uint32_t paddedHeight = height + kGlyphAtlasPadding * 2u; - if (paddedWidth > kGlyphAtlasPageWidth || paddedHeight > kGlyphAtlasPageHeight) { - m_lastError = "Glyph atlas entry exceeds the atlas page size."; - return false; - } - - auto tryAllocateInPage = - [&](GlyphAtlasPage& page, std::size_t pageIndex) -> bool { - if (page.cursorX + paddedWidth > page.width) { - page.cursorX = 0u; - page.cursorY += page.rowHeight; - page.rowHeight = 0u; - } - - if (page.cursorY + paddedHeight > page.height) { - return false; - } - - outPageIndex = pageIndex; - outPageX = page.cursorX + kGlyphAtlasPadding; - outPageY = page.cursorY + kGlyphAtlasPadding; - page.cursorX += paddedWidth; - page.rowHeight = (std::max)(page.rowHeight, paddedHeight); - return true; - }; - - for (std::size_t pageIndex = 0u; pageIndex < m_glyphAtlasPages.size(); ++pageIndex) { - if (tryAllocateInPage(m_glyphAtlasPages[pageIndex], pageIndex)) { - return true; - } - } - - GlyphAtlasPage newPage = {}; - newPage.width = kGlyphAtlasPageWidth; - newPage.height = kGlyphAtlasPageHeight; - - std::vector initialPixels( - static_cast(newPage.width) * static_cast(newPage.height) * 4u, - 0u); - std::string error = {}; - if (!m_textureHost->LoadTextureFromRgba( - initialPixels.data(), - newPage.width, - newPage.height, - newPage.texture, - error)) { - m_lastError = error.empty() - ? "Failed to create an additional glyph atlas page." - : error; - return false; - } - - m_glyphAtlasPages.push_back(std::move(newPage)); - return tryAllocateInPage(m_glyphAtlasPages.back(), m_glyphAtlasPages.size() - 1u); -} - -const D3D12UiRenderer::GlyphAtlasEntry* D3D12UiRenderer::ResolveGlyphAtlasEntry( - const D3D12UiTextSystem::ShapedGlyph& glyph) { - if (m_textureHost == nullptr || m_textSystem == nullptr || glyph.fontFace == nullptr) { - return nullptr; - } - - GlyphAtlasKey key = {}; - key.fontFaceKey = reinterpret_cast(glyph.fontFace.Get()); - key.glyphIndex = glyph.glyphIndex; - key.fontEmSizeTenths = static_cast(std::lround(glyph.fontEmSize * 10.0f)); - key.isSideways = glyph.isSideways; - - const auto found = m_glyphAtlas.find(key); - if (found != m_glyphAtlas.end()) { - return &found->second; - } - - D3D12UiTextSystem::RasterizedGlyph rasterizedGlyph = {}; - std::string error = {}; - if (!m_textSystem->RasterizeGlyph(glyph, rasterizedGlyph, error)) { - m_lastError = error.empty() - ? "Failed to rasterize the glyph atlas entry." - : error; - return nullptr; - } - - GlyphAtlasEntry entry = {}; - entry.boundsLeft = rasterizedGlyph.boundsLeft; - entry.boundsTop = rasterizedGlyph.boundsTop; - entry.boundsRight = rasterizedGlyph.boundsRight; - entry.boundsBottom = rasterizedGlyph.boundsBottom; - entry.width = rasterizedGlyph.width; - entry.height = rasterizedGlyph.height; - entry.hasPixels = - !rasterizedGlyph.rgbaPixels.empty() && - rasterizedGlyph.width > 0u && - rasterizedGlyph.height > 0u; - - if (entry.hasPixels) { - std::size_t pageIndex = 0u; - std::uint32_t pageX = 0u; - std::uint32_t pageY = 0u; - if (!AllocateGlyphAtlasRegion( - rasterizedGlyph.width, - rasterizedGlyph.height, - pageIndex, - pageX, - pageY)) { - return nullptr; - } - - GlyphAtlasPage& page = m_glyphAtlasPages[pageIndex]; - if (!page.texture.IsValid()) { - m_lastError = "Glyph atlas page texture is invalid."; - return nullptr; - } - - if (!m_textureHost->UpdateTextureRegionRgba( - page.texture, - pageX, - pageY, - rasterizedGlyph.rgbaPixels.data(), - rasterizedGlyph.width, - rasterizedGlyph.height, - rasterizedGlyph.width * 4u, - error)) { - m_lastError = error.empty() - ? "Failed to upload the glyph atlas region." - : error; - return nullptr; - } - - entry.pageIndex = pageIndex; - entry.pageX = pageX; - entry.pageY = pageY; - entry.textureHandle.ptr = static_cast(page.texture.nativeHandle); - } - - const auto [insertedIt, inserted] = m_glyphAtlas.emplace(std::move(key), std::move(entry)); - (void)inserted; - return &insertedIt->second; -} - const D3D12UiRenderer::CachedTextRun* D3D12UiRenderer::ResolveTextRun( std::string_view text, - float fontSize) { + float fontSize, + std::uint64_t currentFrameId) { if (m_textureHost == nullptr || m_textSystem == nullptr) { return nullptr; } @@ -1252,51 +1038,43 @@ const D3D12UiRenderer::CachedTextRun* D3D12UiRenderer::ResolveTextRun( const auto found = m_textRunCache.find(cacheKey); if (found != m_textRunCache.end()) { + found->second.lastUsedFrame = currentFrameId; return &found->second; } std::string error = {}; - D3D12UiTextSystem::ShapedTextRun shapedText = {}; - if (!m_textSystem->ShapeTextRun(text, fontSize, shapedText, error)) { + D3D12UiTextSystem::RasterizedTextRun rasterizedText = {}; + if (!m_textSystem->RasterizeTextMask(text, fontSize, rasterizedText, error)) { m_lastError = error.empty() - ? "Failed to shape the UI text run." + ? "Failed to rasterize the UI text run." : error; return nullptr; } CachedTextRun cachedText = {}; - cachedText.glyphs.reserve(shapedText.glyphs.size()); - for (const D3D12UiTextSystem::ShapedGlyph& glyph : shapedText.glyphs) { - const GlyphAtlasEntry* atlasEntry = ResolveGlyphAtlasEntry(glyph); - if (atlasEntry == nullptr) { + cachedText.offsetX = rasterizedText.offsetX; + cachedText.offsetY = rasterizedText.offsetY; + cachedText.width = static_cast(rasterizedText.width); + cachedText.height = static_cast(rasterizedText.height); + cachedText.lastUsedFrame = currentFrameId; + cachedText.hasPixels = + rasterizedText.width > 0u && + rasterizedText.height > 0u && + !rasterizedText.rgbaPixels.empty(); + + if (cachedText.hasPixels) { + if (!m_textureHost->LoadTextureFromRgba( + rasterizedText.rgbaPixels.data(), + rasterizedText.width, + rasterizedText.height, + cachedText.texture, + error)) { + m_lastError = error.empty() + ? "Failed to upload the UI text run texture." + : error; return nullptr; } - - if (!atlasEntry->hasPixels || atlasEntry->textureHandle.ptr == 0u) { - continue; - } - - const GlyphAtlasPage& page = m_glyphAtlasPages[atlasEntry->pageIndex]; - if (!page.texture.IsValid() || page.width == 0u || page.height == 0u) { - m_lastError = "Glyph atlas page metadata is invalid."; - return nullptr; - } - - GlyphQuadPlan glyphQuad = {}; - glyphQuad.left = glyph.originX + static_cast(atlasEntry->boundsLeft); - glyphQuad.top = glyph.originY + static_cast(atlasEntry->boundsTop); - glyphQuad.right = glyph.originX + static_cast(atlasEntry->boundsRight); - glyphQuad.bottom = glyph.originY + static_cast(atlasEntry->boundsBottom); - glyphQuad.uvMinX = static_cast(atlasEntry->pageX) / static_cast(page.width); - glyphQuad.uvMinY = static_cast(atlasEntry->pageY) / static_cast(page.height); - glyphQuad.uvMaxX = - static_cast(atlasEntry->pageX + atlasEntry->width) / - static_cast(page.width); - glyphQuad.uvMaxY = - static_cast(atlasEntry->pageY + atlasEntry->height) / - static_cast(page.height); - glyphQuad.textureHandle = atlasEntry->textureHandle; - cachedText.glyphs.push_back(glyphQuad); + cachedText.textureHandle.ptr = static_cast(cachedText.texture.nativeHandle); } const auto [insertedIt, inserted] = @@ -1305,24 +1083,48 @@ const D3D12UiRenderer::CachedTextRun* D3D12UiRenderer::ResolveTextRun( return &insertedIt->second; } -void D3D12UiRenderer::ReleaseGlyphAtlas() { +void D3D12UiRenderer::ReleaseTextRunCache() { if (m_textureHost != nullptr) { - for (GlyphAtlasPage& page : m_glyphAtlasPages) { - m_textureHost->ReleaseTexture(page.texture); + for (auto& [key, cachedText] : m_textRunCache) { + m_textureHost->ReleaseTexture(cachedText.texture); + cachedText.textureHandle = {}; + } + } + m_textRunCache.clear(); +} + +void D3D12UiRenderer::PruneTextRunCache(std::uint64_t currentFrameId) { + constexpr std::size_t kTextRunCacheMaxEntries = 512u; + constexpr std::uint64_t kTextRunCacheRetentionFrames = 240u; + if (m_textRunCache.size() <= kTextRunCacheMaxEntries) { + return; + } + + const std::uint64_t pruneBeforeFrame = + currentFrameId > kTextRunCacheRetentionFrames + ? currentFrameId - kTextRunCacheRetentionFrames + : 0u; + + bool evictedAny = false; + for (auto it = m_textRunCache.begin(); it != m_textRunCache.end();) { + if (it->second.lastUsedFrame <= pruneBeforeFrame) { + if (m_textureHost != nullptr) { + m_textureHost->ReleaseTexture(it->second.texture); + } + it = m_textRunCache.erase(it); + evictedAny = true; + } else { + ++it; } } - m_glyphAtlasPages.clear(); - m_glyphAtlas.clear(); -} - -void D3D12UiRenderer::ReleaseTextRunCache() { - m_textRunCache.clear(); + if (evictedAny) { + ReleaseCompiledDrawListCache(); + } } void D3D12UiRenderer::ReleaseCompiledDrawListCache() { m_compiledDrawListCache.clear(); - m_compiledDrawListFrameCounter = 0u; } bool D3D12UiRenderer::EnsureInitialized( @@ -1829,6 +1631,7 @@ bool D3D12UiRenderer::BuildDrawBatches( }; const float dpiScale = ClampDpiScale(m_dpiScale); + const std::uint64_t currentFrameId = ++m_compiledDrawListFrameCounter; const D3D12_GPU_DESCRIPTOR_HANDLE whiteTextureHandle = { static_cast(m_whiteTexture.nativeHandle) }; @@ -2118,31 +1921,33 @@ bool D3D12UiRenderer::BuildDrawBatches( } const CachedTextRun* textRun = - ResolveTextRun(command.text, command.fontSize); - if (textRun == nullptr || textRun->glyphs.empty()) { + ResolveTextRun(command.text, command.fontSize, currentFrameId); + if (textRun == nullptr || + !textRun->hasPixels || + textRun->textureHandle.ptr == 0u || + textRun->width <= 0.0f || + textRun->height <= 0.0f) { break; } const FloatPoint position = ToPixelPoint(command.position, dpiScale); - for (const GlyphQuadPlan& glyphQuad : textRun->glyphs) { - appendAxisAlignedQuad( - FloatRect{ - position.x + glyphQuad.left, - position.y + glyphQuad.top, - position.x + glyphQuad.right, - position.y + glyphQuad.bottom - }, - UIPoint(glyphQuad.uvMinX, glyphQuad.uvMinY), - UIPoint(glyphQuad.uvMaxX, glyphQuad.uvMaxY), - command.color, - command.color, - UiQuadKind::Textured, - 0.0f, - 0.0f, - 0.0f, - glyphQuad.textureHandle, - currentClip.scissor); - } + appendAxisAlignedQuad( + FloatRect{ + position.x + textRun->offsetX, + position.y + textRun->offsetY, + position.x + textRun->offsetX + textRun->width, + position.y + textRun->offsetY + textRun->height + }, + UIPoint(0.0f, 0.0f), + UIPoint(1.0f, 1.0f), + command.color, + command.color, + UiQuadKind::Textured, + 0.0f, + 0.0f, + 0.0f, + textRun->textureHandle, + currentClip.scissor); break; } case UIDrawCommandType::Image: { @@ -2227,7 +2032,6 @@ bool D3D12UiRenderer::BuildDrawBatches( } }; - const std::uint64_t currentFrameId = ++m_compiledDrawListFrameCounter; const std::uint32_t renderWidth = renderArea.width > 0 ? renderArea.width : 0u; const std::uint32_t renderHeight = renderArea.height > 0 ? renderArea.height : 0u; const int dpiScaleMilli = @@ -2285,6 +2089,8 @@ bool D3D12UiRenderer::BuildDrawBatches( } } + PruneTextRunCache(currentFrameId); + return true; } diff --git a/new_editor/app/Rendering/D3D12/D3D12UiRenderer.h b/new_editor/app/Rendering/D3D12/D3D12UiRenderer.h index 06a9b382..36c2997c 100644 --- a/new_editor/app/Rendering/D3D12/D3D12UiRenderer.h +++ b/new_editor/app/Rendering/D3D12/D3D12UiRenderer.h @@ -88,42 +88,6 @@ private: std::uint64_t quadInstanceCapacityBytes = 0u; }; - struct GlyphAtlasKey { - std::uintptr_t fontFaceKey = 0u; - std::uint16_t glyphIndex = 0u; - int fontEmSizeTenths = 0; - bool isSideways = false; - - bool operator==(const GlyphAtlasKey& other) const; - }; - - struct GlyphAtlasKeyHash { - std::size_t operator()(const GlyphAtlasKey& key) const; - }; - - struct GlyphAtlasEntry { - std::size_t pageIndex = 0u; - std::uint32_t pageX = 0u; - std::uint32_t pageY = 0u; - std::uint32_t width = 0u; - std::uint32_t height = 0u; - LONG boundsLeft = 0; - LONG boundsTop = 0; - LONG boundsRight = 0; - LONG boundsBottom = 0; - D3D12_GPU_DESCRIPTOR_HANDLE textureHandle = {}; - bool hasPixels = false; - }; - - struct GlyphAtlasPage { - ::XCEngine::UI::UITextureHandle texture = {}; - std::uint32_t width = 0u; - std::uint32_t height = 0u; - std::uint32_t cursorX = 0u; - std::uint32_t cursorY = 0u; - std::uint32_t rowHeight = 0u; - }; - struct TextRunCacheKey { std::string text = {}; int fontSizeTenths = 0; @@ -136,20 +100,15 @@ private: std::size_t operator()(const TextRunCacheKey& key) const; }; - struct GlyphQuadPlan { - float left = 0.0f; - float top = 0.0f; - float right = 0.0f; - float bottom = 0.0f; - float uvMinX = 0.0f; - float uvMinY = 0.0f; - float uvMaxX = 0.0f; - float uvMaxY = 0.0f; - D3D12_GPU_DESCRIPTOR_HANDLE textureHandle = {}; - }; - struct CachedTextRun { - std::vector glyphs = {}; + ::XCEngine::UI::UITextureHandle texture = {}; + D3D12_GPU_DESCRIPTOR_HANDLE textureHandle = {}; + float offsetX = 0.0f; + float offsetY = 0.0f; + float width = 0.0f; + float height = 0.0f; + bool hasPixels = false; + std::uint64_t lastUsedFrame = 0u; }; struct CompiledDrawListKey { @@ -187,20 +146,12 @@ private: std::size_t quadInstanceBytes); void DestroyFrameResources(FrameResources& frameResources); bool EnsureWhiteTexture(); - bool EnsureGlyphAtlasPage(); - bool AllocateGlyphAtlasRegion( - std::uint32_t width, - std::uint32_t height, - std::size_t& outPageIndex, - std::uint32_t& outPageX, - std::uint32_t& outPageY); - const GlyphAtlasEntry* ResolveGlyphAtlasEntry( - const D3D12UiTextSystem::ShapedGlyph& glyph); const CachedTextRun* ResolveTextRun( std::string_view text, - float fontSize); - void ReleaseGlyphAtlas(); + float fontSize, + std::uint64_t currentFrameId); void ReleaseTextRunCache(); + void PruneTextRunCache(std::uint64_t currentFrameId); void ReleaseCompiledDrawListCache(); bool BuildDrawBatches( const ::XCEngine::UI::UIDrawData& drawData, @@ -230,8 +181,6 @@ private: ::XCEngine::RHI::RHIBuffer* m_quadIndexBuffer = nullptr; ::XCEngine::RHI::RHIResourceView* m_quadIndexBufferView = nullptr; std::array m_frameResources = {}; - std::vector m_glyphAtlasPages = {}; - std::unordered_map m_glyphAtlas = {}; std::unordered_map m_textRunCache = {}; std::unordered_map m_compiledDrawListCache = {}; diff --git a/new_editor/app/Rendering/D3D12/D3D12UiTextSystem.cpp b/new_editor/app/Rendering/D3D12/D3D12UiTextSystem.cpp index e54dbdc3..af0406a6 100644 --- a/new_editor/app/Rendering/D3D12/D3D12UiTextSystem.cpp +++ b/new_editor/app/Rendering/D3D12/D3D12UiTextSystem.cpp @@ -224,10 +224,13 @@ void CompositeCoverage( const std::size_t srcOffset = static_cast(row) * textureStride + static_cast(column) * channelCount; - std::uint8_t coverage = 0u; + std::uint32_t coverageSum = 0u; for (std::size_t channel = 0u; channel < channelCount; ++channel) { - coverage = (std::max)(coverage, alphaTexture[srcOffset + channel]); + coverageSum += static_cast(alphaTexture[srcOffset + channel]); } + const std::uint8_t coverage = static_cast( + (coverageSum + static_cast(channelCount / 2u)) / + static_cast(channelCount)); if (coverage == 0u) { continue; @@ -533,13 +536,9 @@ float D3D12UiTextSystem::MeasureTextWidth( bool D3D12UiTextSystem::RasterizeTextMask( std::string_view text, float fontSize, - std::vector& outRgbaPixels, - UINT& outWidth, - UINT& outHeight, + RasterizedTextRun& outRun, std::string& outError) { - outRgbaPixels.clear(); - outWidth = 0u; - outHeight = 0u; + outRun = {}; outError.clear(); if (!m_dwriteFactory) { @@ -593,16 +592,18 @@ bool D3D12UiTextSystem::RasterizeTextMask( const float topPad = (std::max)(overhangMetrics.top, 0.0f); const float rightPad = (std::max)(overhangMetrics.right, 0.0f); const float bottomPad = (std::max)(overhangMetrics.bottom, 0.0f); - outWidth = (std::max)( + outRun.width = (std::max)( 1u, static_cast(std::ceil( textMetrics.widthIncludingTrailingWhitespace + leftPad + rightPad))); - outHeight = (std::max)( + outRun.height = (std::max)( 1u, static_cast(std::ceil( (std::max)(textMetrics.height, scaledFontSize * 1.6f) + topPad + bottomPad))); - outRgbaPixels.assign( - static_cast(outWidth) * static_cast(outHeight) * 4u, + outRun.offsetX = -leftPad; + outRun.offsetY = -topPad; + outRun.rgbaPixels.assign( + static_cast(outRun.width) * static_cast(outRun.height) * 4u, 0u); GlyphRunCollector* collector = new GlyphRunCollector(dpiScale); @@ -675,9 +676,9 @@ bool D3D12UiTextSystem::RasterizeTextMask( } CompositeCoverage( - outRgbaPixels, - outWidth, - outHeight, + outRun.rgbaPixels, + outRun.width, + outRun.height, bounds, alphaTexture.data(), static_cast(boundsWidth) * 3u, diff --git a/new_editor/app/Rendering/D3D12/D3D12UiTextSystem.h b/new_editor/app/Rendering/D3D12/D3D12UiTextSystem.h index 2887c2b2..2d292c20 100644 --- a/new_editor/app/Rendering/D3D12/D3D12UiTextSystem.h +++ b/new_editor/app/Rendering/D3D12/D3D12UiTextSystem.h @@ -20,6 +20,14 @@ namespace XCEngine::UI::Editor::Host { class D3D12UiTextSystem final : public ::XCEngine::UI::Editor::UIEditorTextMeasurer { public: + struct RasterizedTextRun { + std::vector rgbaPixels = {}; + UINT width = 0u; + UINT height = 0u; + float offsetX = 0.0f; + float offsetY = 0.0f; + }; + struct ShapedGlyph { Microsoft::WRL::ComPtr fontFace = {}; std::uint16_t glyphIndex = 0u; @@ -67,9 +75,7 @@ public: bool RasterizeTextMask( std::string_view text, float fontSize, - std::vector& outRgbaPixels, - UINT& outWidth, - UINT& outHeight, + RasterizedTextRun& outRun, std::string& outError); private: