#include #include "XCUIBackend/XCUIRHICommandCompiler.h" namespace { using XCEngine::Editor::XCUIBackend::XCUIRHICommandCompiler; using XCEngine::UI::UIColor; using XCEngine::UI::UIDrawData; using XCEngine::UI::UIDrawList; using XCEngine::UI::UIPoint; using XCEngine::UI::UIRect; using XCEngine::UI::UITextureHandle; using XCEngine::UI::UITextureHandleKind; class StubTextGlyphProvider final : public XCUIRHICommandCompiler::TextGlyphProvider { public: bool BeginText( float requestedFontSize, XCUIRHICommandCompiler::TextRunContext& outContext) const override { outContext.requestedFontSize = requestedFontSize; outContext.resolvedFontSize = requestedFontSize > 0.0f ? requestedFontSize : 14.0f; outContext.lineHeight = 12.0f; outContext.texture = UITextureHandle{ 99u, 256u, 256u, UITextureHandleKind::ShaderResourceView }; return true; } bool ResolveGlyph( const XCUIRHICommandCompiler::TextRunContext&, std::uint32_t codepoint, XCUIRHICommandCompiler::TextGlyph& outGlyph) const override { switch (codepoint) { case 'A': outGlyph.x0 = 0.0f; outGlyph.y0 = 0.0f; outGlyph.x1 = 8.0f; outGlyph.y1 = 10.0f; outGlyph.u0 = 0.0f; outGlyph.v0 = 0.0f; outGlyph.u1 = 0.25f; outGlyph.v1 = 0.5f; outGlyph.advanceX = 8.0f; outGlyph.visible = true; return true; case 'B': outGlyph.x0 = 0.0f; outGlyph.y0 = 0.0f; outGlyph.x1 = 7.0f; outGlyph.y1 = 10.0f; outGlyph.u0 = 0.25f; outGlyph.v0 = 0.0f; outGlyph.u1 = 0.5f; outGlyph.v1 = 0.5f; outGlyph.advanceX = 7.0f; outGlyph.visible = true; return true; default: return false; } } }; TEST(XCUIRHICommandCompilerTest, CompileMergesAdjacentColorAndTexturedCommandsPerDrawList) { XCUIRHICommandCompiler compiler = {}; UIDrawData drawData = {}; UIDrawList& drawList = drawData.EmplaceDrawList("Batches"); drawList.AddFilledRect(UIRect(0.0f, 0.0f, 10.0f, 10.0f), UIColor(1.0f, 0.0f, 0.0f, 1.0f)); drawList.AddFilledRect(UIRect(10.0f, 0.0f, 10.0f, 10.0f), UIColor(0.0f, 1.0f, 0.0f, 1.0f)); const UITextureHandle texture{ 7u, 64u, 64u, UITextureHandleKind::ShaderResourceView }; drawList.AddImage(UIRect(0.0f, 20.0f, 10.0f, 10.0f), texture, UIColor(1.0f, 1.0f, 1.0f, 1.0f)); drawList.AddImage(UIRect(10.0f, 20.0f, 10.0f, 10.0f), texture, UIColor(0.5f, 0.5f, 0.5f, 1.0f)); XCUIRHICommandCompiler::CompileConfig config = {}; config.surfaceClipRect = UIRect(0.0f, 0.0f, 100.0f, 100.0f); XCUIRHICommandCompiler::CompiledDrawData compiled = {}; compiler.Compile(drawData, config, compiled); ASSERT_EQ(compiled.batches.size(), 2u); EXPECT_EQ(compiled.batches[0].kind, XCUIRHICommandCompiler::BatchKind::Color); EXPECT_EQ(compiled.batches[0].commandCount, 2u); EXPECT_EQ(compiled.batches[0].vertexCount, 12u); EXPECT_EQ(compiled.batches[1].kind, XCUIRHICommandCompiler::BatchKind::Textured); EXPECT_EQ(compiled.batches[1].commandCount, 2u); EXPECT_EQ(compiled.batches[1].vertexCount, 12u); EXPECT_EQ(compiled.batches[1].texture.nativeHandle, texture.nativeHandle); EXPECT_EQ(compiled.stats.compiledCommandCount, 4u); EXPECT_EQ(compiled.stats.batchCount, 2u); EXPECT_EQ(compiled.stats.colorVertexCount, 12u); EXPECT_EQ(compiled.stats.texturedVertexCount, 12u); } TEST(XCUIRHICommandCompilerTest, CompileTracksClipStackTransitionsAndUnderflow) { XCUIRHICommandCompiler compiler = {}; UIDrawData drawData = {}; UIDrawList& drawList = drawData.EmplaceDrawList("ClipStack"); drawList.PushClipRect(UIRect(0.0f, 0.0f, 50.0f, 50.0f)); drawList.PushClipRect(UIRect(10.0f, 10.0f, 40.0f, 40.0f)); drawList.AddFilledRect(UIRect(0.0f, 0.0f, 30.0f, 30.0f), UIColor(1.0f, 0.0f, 0.0f, 1.0f)); drawList.PopClipRect(); drawList.AddFilledRect(UIRect(0.0f, 0.0f, 20.0f, 20.0f), UIColor(0.0f, 1.0f, 0.0f, 1.0f)); drawList.PopClipRect(); drawList.PopClipRect(); XCUIRHICommandCompiler::CompileConfig config = {}; config.surfaceClipRect = UIRect(0.0f, 0.0f, 100.0f, 100.0f); XCUIRHICommandCompiler::CompiledDrawData compiled = {}; compiler.Compile(drawData, config, compiled); ASSERT_EQ(compiled.batches.size(), 2u); EXPECT_EQ(compiled.stats.compiledCommandCount, 7u); EXPECT_EQ(compiled.stats.clipPushCommandCount, 2u); EXPECT_EQ(compiled.stats.clipPopCommandCount, 3u); EXPECT_EQ(compiled.stats.clipStackUnderflowCount, 1u); EXPECT_EQ(compiled.stats.maxClipDepth, 2u); EXPECT_EQ(compiled.stats.danglingClipDepth, 0u); EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.x, 10.0f); EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.y, 10.0f); EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.width, 40.0f); EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.height, 40.0f); ASSERT_GE(compiled.colorVertices.size(), 12u); EXPECT_FLOAT_EQ(compiled.colorVertices[0].position[0], 10.0f); EXPECT_FLOAT_EQ(compiled.colorVertices[0].position[1], 10.0f); EXPECT_FLOAT_EQ(compiled.colorVertices[6].position[0], 0.0f); EXPECT_FLOAT_EQ(compiled.colorVertices[6].position[1], 0.0f); } TEST(XCUIRHICommandCompilerTest, CompileUsesTextGlyphProviderOutputForTextBatches) { XCUIRHICommandCompiler compiler = {}; StubTextGlyphProvider glyphProvider = {}; UIDrawData drawData = {}; UIDrawList& drawList = drawData.EmplaceDrawList("Text"); drawList.AddText(UIPoint(4.0f, 6.0f), "A\nB", UIColor(1.0f, 1.0f, 1.0f, 1.0f), 16.0f); XCUIRHICommandCompiler::CompileConfig config = {}; config.surfaceClipRect = UIRect(0.0f, 0.0f, 100.0f, 100.0f); config.textGlyphProvider = &glyphProvider; XCUIRHICommandCompiler::CompiledDrawData compiled = {}; compiler.Compile(drawData, config, compiled); ASSERT_EQ(compiled.batches.size(), 1u); EXPECT_EQ(compiled.batches[0].kind, XCUIRHICommandCompiler::BatchKind::Textured); EXPECT_EQ(compiled.batches[0].vertexCount, 12u); EXPECT_EQ(compiled.batches[0].texture.nativeHandle, 99u); EXPECT_EQ(compiled.stats.textCommandCount, 1u); EXPECT_EQ(compiled.stats.compiledCommandCount, 1u); ASSERT_EQ(compiled.texturedVertices.size(), 12u); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[0], 4.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[1], 6.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[6].position[0], 4.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[6].position[1], 18.0f); } TEST(XCUIRHICommandCompilerTest, CompileReportsUnsupportedTextWhenNoGlyphProviderIsAvailable) { XCUIRHICommandCompiler compiler = {}; UIDrawData drawData = {}; UIDrawList& drawList = drawData.EmplaceDrawList("UnsupportedText"); drawList.AddText(UIPoint(0.0f, 0.0f), "xcui", UIColor(1.0f, 1.0f, 1.0f, 1.0f), 12.0f); XCUIRHICommandCompiler::CompileConfig config = {}; config.surfaceClipRect = UIRect(0.0f, 0.0f, 32.0f, 32.0f); XCUIRHICommandCompiler::CompiledDrawData compiled = {}; compiler.Compile(drawData, config, compiled); EXPECT_TRUE(compiled.Empty()); EXPECT_EQ(compiled.stats.textCommandCount, 1u); EXPECT_EQ(compiled.stats.compiledCommandCount, 0u); EXPECT_EQ(compiled.stats.skippedCommandCount, 1u); EXPECT_EQ(compiled.stats.unsupportedCommandCount, 1u); } TEST(XCUIRHICommandCompilerTest, CompileClipsImageCommandsAndAdjustsUvCoordinates) { XCUIRHICommandCompiler compiler = {}; UIDrawData drawData = {}; UIDrawList& drawList = drawData.EmplaceDrawList("ClippedImage"); drawList.PushClipRect(UIRect(10.0f, 8.0f, 12.0f, 10.0f)); drawList.AddImage( UIRect(4.0f, 4.0f, 20.0f, 20.0f), UITextureHandle{ 77u, 32u, 32u, UITextureHandleKind::ShaderResourceView }, UIColor(0.7f, 0.8f, 0.9f, 1.0f)); XCUIRHICommandCompiler::CompileConfig config = {}; config.surfaceClipRect = UIRect(0.0f, 0.0f, 64.0f, 64.0f); XCUIRHICommandCompiler::CompiledDrawData compiled = {}; compiler.Compile(drawData, config, compiled); ASSERT_EQ(compiled.batches.size(), 1u); ASSERT_EQ(compiled.texturedVertices.size(), 6u); EXPECT_EQ(compiled.batches[0].kind, XCUIRHICommandCompiler::BatchKind::Textured); EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.x, 10.0f); EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.y, 8.0f); EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.width, 12.0f); EXPECT_FLOAT_EQ(compiled.batches[0].clipRect.height, 10.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[0], 10.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[1], 8.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].uv[0], 0.3f); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].uv[1], 0.2f); EXPECT_FLOAT_EQ(compiled.texturedVertices[1].position[0], 22.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[1].uv[0], 0.9f); EXPECT_FLOAT_EQ(compiled.texturedVertices[2].position[1], 18.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[2].uv[1], 0.7f); EXPECT_EQ(compiled.stats.imageCommandCount, 1u); EXPECT_EQ(compiled.stats.compiledCommandCount, 2u); } TEST(XCUIRHICommandCompilerTest, CompilePreservesMirroredImageUvForNegativeRectExtents) { XCUIRHICommandCompiler compiler = {}; UIDrawData drawData = {}; UIDrawList& drawList = drawData.EmplaceDrawList("MirroredImage"); drawList.AddImage( UIRect(24.0f, 18.0f, -16.0f, -12.0f), UITextureHandle{ 91u, 64u, 64u, UITextureHandleKind::ShaderResourceView }, UIColor(1.0f, 1.0f, 1.0f, 1.0f)); XCUIRHICommandCompiler::CompileConfig config = {}; config.surfaceClipRect = UIRect(0.0f, 0.0f, 64.0f, 64.0f); XCUIRHICommandCompiler::CompiledDrawData compiled = {}; compiler.Compile(drawData, config, compiled); ASSERT_EQ(compiled.batches.size(), 1u); ASSERT_EQ(compiled.texturedVertices.size(), 6u); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[0], 8.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].position[1], 6.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].uv[0], 1.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].uv[1], 1.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[1].position[0], 24.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[1].uv[0], 0.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[2].position[1], 18.0f); EXPECT_FLOAT_EQ(compiled.texturedVertices[2].uv[1], 0.0f); EXPECT_EQ(compiled.stats.imageCommandCount, 1u); EXPECT_EQ(compiled.stats.compiledCommandCount, 1u); } TEST(XCUIRHICommandCompilerTest, CompileUsesExplicitImageUvRectWhenProvided) { XCUIRHICommandCompiler compiler = {}; UIDrawData drawData = {}; UIDrawList& drawList = drawData.EmplaceDrawList("ExplicitUvImage"); drawList.AddImage( UIRect(8.0f, 6.0f, 16.0f, 12.0f), UITextureHandle{ 123u, 64u, 64u, UITextureHandleKind::ShaderResourceView }, UIColor(0.9f, 0.8f, 0.7f, 1.0f), UIPoint(0.25f, 0.40f), UIPoint(0.75f, 0.90f)); XCUIRHICommandCompiler::CompileConfig config = {}; config.surfaceClipRect = UIRect(0.0f, 0.0f, 64.0f, 64.0f); XCUIRHICommandCompiler::CompiledDrawData compiled = {}; compiler.Compile(drawData, config, compiled); ASSERT_EQ(compiled.batches.size(), 1u); ASSERT_EQ(compiled.texturedVertices.size(), 6u); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].uv[0], 0.25f); EXPECT_FLOAT_EQ(compiled.texturedVertices[0].uv[1], 0.40f); EXPECT_FLOAT_EQ(compiled.texturedVertices[1].uv[0], 0.75f); EXPECT_FLOAT_EQ(compiled.texturedVertices[1].uv[1], 0.40f); EXPECT_FLOAT_EQ(compiled.texturedVertices[2].uv[0], 0.75f); EXPECT_FLOAT_EQ(compiled.texturedVertices[2].uv[1], 0.90f); EXPECT_EQ(compiled.stats.imageCommandCount, 1u); EXPECT_EQ(compiled.stats.compiledCommandCount, 1u); } } // namespace