#include #include "XCUIBackend/IWindowUICompositor.h" #include "XCUIBackend/NativeWindowUICompositor.h" #include "XCUIBackend/UITextureRegistration.h" #include #include #include #include namespace { using XCEngine::Editor::Platform::D3D12WindowRenderer; using XCEngine::Editor::XCUIBackend::CreateNativeWindowUICompositor; using XCEngine::Editor::XCUIBackend::IXCUITextAtlasProvider; using XCEngine::Editor::XCUIBackend::IWindowUICompositor; using XCEngine::Editor::XCUIBackend::NativeWindowUICompositor; using XCEngine::Editor::XCUIBackend::UITextureRegistration; using XCEngine::Editor::XCUIBackend::XCUINativeWindowPresentStats; using XCEngine::Editor::XCUIBackend::XCUINativeWindowRenderPacket; HWND MakeFakeHwnd() { return reinterpret_cast(static_cast(0x2345u)); } class StubTextAtlasProvider final : public IXCUITextAtlasProvider { public: bool GetAtlasTextureView(PixelFormat preferredFormat, AtlasTextureView& outView) const override { (void)preferredFormat; outView = {}; return false; } std::size_t GetFontCount() const override { return 0u; } FontHandle GetFont(std::size_t index) const override { (void)index; return {}; } FontHandle GetDefaultFont() const override { return {}; } bool GetFontInfo(FontHandle font, FontInfo& outInfo) const override { (void)font; outInfo = {}; return false; } bool GetBakedFontInfo(FontHandle font, float fontSize, BakedFontInfo& outInfo) const override { (void)font; (void)fontSize; outInfo = {}; return false; } bool FindGlyph(FontHandle font, float fontSize, std::uint32_t codepoint, GlyphInfo& outInfo) const override { (void)font; (void)fontSize; (void)codepoint; outInfo = {}; return false; } }; XCEngine::UI::UIDrawData MakeDrawData() { XCEngine::UI::UIDrawData drawData = {}; drawData.EmplaceDrawList("NativeOverlay").AddFilledRect( XCEngine::UI::UIRect(10.0f, 12.0f, 48.0f, 24.0f), XCEngine::UI::UIColor(0.2f, 0.4f, 0.8f, 1.0f)); return drawData; } TEST(NativeWindowUICompositorTest, RenderPacketReportsDrawDataPresenceAndClearResetsPayload) { XCUINativeWindowRenderPacket packet = {}; EXPECT_FALSE(packet.HasDrawData()); EXPECT_EQ(packet.textAtlasProvider, nullptr); StubTextAtlasProvider atlasProvider = {}; packet.drawData = MakeDrawData(); packet.textAtlasProvider = &atlasProvider; EXPECT_TRUE(packet.HasDrawData()); EXPECT_EQ(packet.drawData.GetDrawListCount(), 1u); EXPECT_EQ(packet.drawData.GetTotalCommandCount(), 1u); EXPECT_EQ(packet.textAtlasProvider, &atlasProvider); packet.Clear(); EXPECT_FALSE(packet.HasDrawData()); EXPECT_EQ(packet.drawData.GetDrawListCount(), 0u); EXPECT_EQ(packet.drawData.GetTotalCommandCount(), 0u); EXPECT_EQ(packet.textAtlasProvider, nullptr); } TEST(NativeWindowUICompositorTest, SubmitAndClearPendingPacketTracksCopiedDrawDataAndAtlasProvider) { NativeWindowUICompositor compositor = {}; StubTextAtlasProvider atlasProvider = {}; const XCEngine::UI::UIDrawData drawData = MakeDrawData(); compositor.SubmitRenderPacket(drawData, &atlasProvider); ASSERT_TRUE(compositor.HasPendingRenderPacket()); const XCUINativeWindowRenderPacket& packet = compositor.GetPendingRenderPacket(); EXPECT_TRUE(packet.HasDrawData()); EXPECT_EQ(packet.drawData.GetDrawListCount(), 1u); EXPECT_EQ(packet.drawData.GetTotalCommandCount(), 1u); EXPECT_EQ(packet.textAtlasProvider, &atlasProvider); compositor.ClearPendingRenderPacket(); EXPECT_FALSE(compositor.HasPendingRenderPacket()); EXPECT_FALSE(compositor.GetPendingRenderPacket().HasDrawData()); EXPECT_EQ(compositor.GetPendingRenderPacket().textAtlasProvider, nullptr); } TEST(NativeWindowUICompositorTest, InitializeAndShutdownResetStateAlongSafePaths) { NativeWindowUICompositor compositor = {}; D3D12WindowRenderer renderer = {}; compositor.SubmitRenderPacket(MakeDrawData(), nullptr); ASSERT_TRUE(compositor.HasPendingRenderPacket()); bool configureFontsCalled = false; EXPECT_FALSE(compositor.Initialize( nullptr, renderer, [&configureFontsCalled]() { configureFontsCalled = true; })); EXPECT_FALSE(configureFontsCalled); EXPECT_FALSE(compositor.HasPendingRenderPacket()); compositor.SubmitRenderPacket(MakeDrawData(), nullptr); EXPECT_TRUE(compositor.Initialize( MakeFakeHwnd(), renderer, [&configureFontsCalled]() { configureFontsCalled = true; })); EXPECT_FALSE(configureFontsCalled); EXPECT_FALSE(compositor.HasPendingRenderPacket()); compositor.Shutdown(); EXPECT_FALSE(compositor.HasPendingRenderPacket()); const XCUINativeWindowPresentStats& stats = compositor.GetLastPresentStats(); EXPECT_FALSE(stats.hadPendingPacket); EXPECT_FALSE(stats.renderedNativeOverlay); EXPECT_EQ(stats.submittedDrawListCount, 0u); EXPECT_EQ(stats.submittedCommandCount, 0u); } TEST(NativeWindowUICompositorTest, RenderFrameWithUnpreparedRendererSkipsCallbacksAndKeepsPendingPacket) { NativeWindowUICompositor compositor = {}; D3D12WindowRenderer renderer = {}; ASSERT_TRUE(compositor.Initialize(MakeFakeHwnd(), renderer, {})); compositor.SubmitRenderPacket(MakeDrawData(), nullptr); ASSERT_TRUE(compositor.HasPendingRenderPacket()); bool uiRendered = false; bool beforeUiRendered = false; bool afterUiRendered = false; compositor.RenderFrame( std::array{ 0.1f, 0.2f, 0.3f, 1.0f }.data(), [&uiRendered]() { uiRendered = true; }, [&beforeUiRendered](const ::XCEngine::Rendering::RenderContext&, const ::XCEngine::Rendering::RenderSurface&) { beforeUiRendered = true; }, [&afterUiRendered](const ::XCEngine::Rendering::RenderContext&, const ::XCEngine::Rendering::RenderSurface&) { afterUiRendered = true; }); EXPECT_FALSE(uiRendered); EXPECT_FALSE(beforeUiRendered); EXPECT_FALSE(afterUiRendered); EXPECT_TRUE(compositor.HasPendingRenderPacket()); const XCUINativeWindowPresentStats& stats = compositor.GetLastPresentStats(); EXPECT_FALSE(stats.hadPendingPacket); EXPECT_FALSE(stats.renderedNativeOverlay); EXPECT_EQ(stats.submittedDrawListCount, 0u); EXPECT_EQ(stats.submittedCommandCount, 0u); } TEST(NativeWindowUICompositorTest, InterfaceFactoryReturnsSafeNativeCompositorDefaults) { std::unique_ptr compositor = CreateNativeWindowUICompositor(); ASSERT_NE(compositor, nullptr); D3D12WindowRenderer renderer = {}; bool configureFontsCalled = false; EXPECT_FALSE(compositor->Initialize( nullptr, renderer, [&configureFontsCalled]() { configureFontsCalled = true; })); EXPECT_FALSE(configureFontsCalled); EXPECT_FALSE(compositor->HandleWindowMessage(MakeFakeHwnd(), WM_CLOSE, 0u, 0u)); UITextureRegistration registration = {}; EXPECT_FALSE(compositor->CreateTextureDescriptor(nullptr, nullptr, registration)); EXPECT_EQ(registration.texture.nativeHandle, 0u); bool uiRendered = false; compositor->RenderFrame( std::array{ 0.0f, 0.0f, 0.0f, 1.0f }.data(), [&uiRendered]() { uiRendered = true; }, {}, {}); EXPECT_FALSE(uiRendered); compositor->Shutdown(); } } // namespace