#include #include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h" #include "XCUIBackend/XCUIHostedPreviewPresenter.h" #include #include #include namespace { using XCEngine::Editor::XCUIBackend::CreateImGuiXCUIHostedPreviewPresenter; using XCEngine::Editor::XCUIBackend::CreateQueuedNativeXCUIHostedPreviewPresenter; using XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter; using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewFrame; using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats; using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewQueue; using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewQueuedFrame; using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor; using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceImage; using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceRegistry; using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats; class ImGuiContextScope { public: ImGuiContextScope() { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGui::StyleColorsDark(); } ~ImGuiContextScope() { ImGui::DestroyContext(); } }; void PrepareImGui(float width = 1024.0f, float height = 768.0f) { ImGuiIO& io = ImGui::GetIO(); io.DisplaySize = ImVec2(width, height); io.DeltaTime = 1.0f / 60.0f; unsigned char* fontPixels = nullptr; int fontWidth = 0; int fontHeight = 0; io.Fonts->GetTexDataAsRGBA32(&fontPixels, &fontWidth, &fontHeight); io.Fonts->SetTexID(static_cast(1)); } XCEngine::UI::UITextureHandle MakeHostedPreviewTextureHandle( std::uintptr_t nativeHandle, std::uint32_t width, std::uint32_t height) { XCEngine::UI::UITextureHandle texture = {}; texture.nativeHandle = nativeHandle; texture.width = width; texture.height = height; texture.kind = XCEngine::UI::UITextureHandleKind::ImGuiDescriptor; return texture; } TEST(XCUIHostedPreviewPresenterTest, PresentReturnsFalseAndClearsStatsWhenFrameHasNoDrawData) { ImGuiContextScope contextScope; PrepareImGui(); std::unique_ptr presenter = CreateImGuiXCUIHostedPreviewPresenter(); ASSERT_NE(presenter, nullptr); XCUIHostedPreviewFrame frame = {}; const bool presented = presenter->Present(frame); const XCUIHostedPreviewStats& stats = presenter->GetLastStats(); EXPECT_FALSE(presented); EXPECT_FALSE(stats.presented); EXPECT_EQ(stats.submittedDrawListCount, 0u); EXPECT_EQ(stats.submittedCommandCount, 0u); EXPECT_EQ(stats.flushedDrawListCount, 0u); EXPECT_EQ(stats.flushedCommandCount, 0u); } TEST(XCUIHostedPreviewPresenterTest, PresentFlushesDrawDataIntoProvidedImGuiDrawList) { ImGuiContextScope contextScope; PrepareImGui(800.0f, 600.0f); std::unique_ptr presenter = CreateImGuiXCUIHostedPreviewPresenter(); ASSERT_NE(presenter, nullptr); XCEngine::UI::UIDrawData drawData = {}; XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("HostedPreview"); drawList.AddFilledRect( XCEngine::UI::UIRect(12.0f, 14.0f, 48.0f, 30.0f), XCEngine::UI::UIColor(0.25f, 0.5f, 0.8f, 1.0f)); drawList.AddText( XCEngine::UI::UIPoint(18.0f, 24.0f), "xcui", XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f), 14.0f); ImGui::NewFrame(); ASSERT_TRUE(ImGui::Begin("HostedPreviewPresenterWindow")); ImDrawList* targetDrawList = ImGui::GetWindowDrawList(); ASSERT_NE(targetDrawList, nullptr); XCUIHostedPreviewFrame frame = {}; frame.drawData = &drawData; const bool presented = presenter->Present(frame); const XCUIHostedPreviewStats& stats = presenter->GetLastStats(); ImGui::End(); ImGui::EndFrame(); EXPECT_TRUE(presented); EXPECT_TRUE(stats.presented); EXPECT_EQ(stats.submittedDrawListCount, 1u); EXPECT_EQ(stats.submittedCommandCount, 2u); EXPECT_EQ(stats.flushedDrawListCount, 1u); EXPECT_EQ(stats.flushedCommandCount, 2u); EXPECT_GT(targetDrawList->VtxBuffer.Size, 0); EXPECT_GT(targetDrawList->CmdBuffer.Size, 0); } TEST(XCUIHostedPreviewPresenterTest, QueuedNativePresenterCopiesFrameIntoQueue) { XCUIHostedPreviewQueue queue = {}; XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {}; queue.BeginFrame(); std::unique_ptr presenter = CreateQueuedNativeXCUIHostedPreviewPresenter(queue, surfaceRegistry); ASSERT_NE(presenter, nullptr); EXPECT_TRUE(presenter->IsNativeQueued()); XCEngine::UI::UIDrawData drawData = {}; XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("HostedPreviewNative"); drawList.AddFilledRect( XCEngine::UI::UIRect(20.0f, 24.0f, 56.0f, 32.0f), XCEngine::UI::UIColor(0.2f, 0.35f, 0.9f, 1.0f)); drawList.AddText( XCEngine::UI::UIPoint(28.0f, 36.0f), "native", XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f), 16.0f); XCUIHostedPreviewFrame frame = {}; frame.drawData = &drawData; frame.debugName = "Hosted Preview Native Queue"; frame.debugSource = "tests.hosted_preview.native_queue"; frame.canvasRect = XCEngine::UI::UIRect(32.0f, 48.0f, 320.0f, 180.0f); frame.logicalSize = XCEngine::UI::UISize(640.0f, 360.0f); EXPECT_TRUE(presenter->Present(frame)); const XCUIHostedPreviewStats& stats = presenter->GetLastStats(); EXPECT_TRUE(stats.presented); EXPECT_TRUE(stats.queuedToNativePass); EXPECT_EQ(stats.submittedDrawListCount, 1u); EXPECT_EQ(stats.submittedCommandCount, 2u); EXPECT_EQ(stats.flushedDrawListCount, 0u); EXPECT_EQ(stats.flushedCommandCount, 0u); drawData.Clear(); const auto& queuedFrames = queue.GetQueuedFrames(); ASSERT_EQ(queuedFrames.size(), 1u); EXPECT_EQ(queuedFrames[0].debugName, "Hosted Preview Native Queue"); EXPECT_EQ(queuedFrames[0].debugSource, "tests.hosted_preview.native_queue"); EXPECT_FLOAT_EQ(queuedFrames[0].canvasRect.x, 32.0f); EXPECT_FLOAT_EQ(queuedFrames[0].canvasRect.y, 48.0f); EXPECT_FLOAT_EQ(queuedFrames[0].canvasRect.width, 320.0f); EXPECT_FLOAT_EQ(queuedFrames[0].canvasRect.height, 180.0f); EXPECT_FLOAT_EQ(queuedFrames[0].logicalSize.width, 640.0f); EXPECT_FLOAT_EQ(queuedFrames[0].logicalSize.height, 360.0f); EXPECT_EQ(queuedFrames[0].drawData.GetDrawListCount(), 1u); EXPECT_EQ(queuedFrames[0].drawData.GetTotalCommandCount(), 2u); } TEST(XCUIHostedPreviewPresenterTest, QueuedNativePresenterFallsBackLogicalSizeToCanvasRectAndDelegatesSurfaceQueries) { XCUIHostedPreviewQueue queue = {}; XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {}; queue.BeginFrame(); surfaceRegistry.BeginFrame(); std::unique_ptr presenter = CreateQueuedNativeXCUIHostedPreviewPresenter(queue, surfaceRegistry); ASSERT_NE(presenter, nullptr); XCEngine::UI::UIDrawData drawData = {}; XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("HostedPreviewFallback"); drawList.AddFilledRect( XCEngine::UI::UIRect(6.0f, 10.0f, 40.0f, 24.0f), XCEngine::UI::UIColor(0.85f, 0.35f, 0.2f, 1.0f)); XCUIHostedPreviewFrame frame = {}; frame.drawData = &drawData; frame.debugName = "XCUI Demo"; frame.debugSource = "tests.hosted_preview.logical_size_fallback"; frame.canvasRect = XCEngine::UI::UIRect(18.0f, 22.0f, 320.0f, 180.0f); ASSERT_TRUE(presenter->Present(frame)); ASSERT_EQ(queue.GetQueuedFrames().size(), 1u); const XCUIHostedPreviewQueuedFrame& queuedFrame = queue.GetQueuedFrames().front(); EXPECT_FLOAT_EQ(queuedFrame.logicalSize.width, 320.0f); EXPECT_FLOAT_EQ(queuedFrame.logicalSize.height, 180.0f); XCUIHostedPreviewSurfaceDescriptor descriptor = {}; EXPECT_FALSE(presenter->TryGetSurfaceDescriptor("XCUI Demo", descriptor)); EXPECT_TRUE(descriptor.debugName.empty()); surfaceRegistry.RecordQueuedFrame(queuedFrame, 0u); ASSERT_TRUE(presenter->TryGetSurfaceDescriptor("XCUI Demo", descriptor)); EXPECT_EQ(descriptor.debugName, "XCUI Demo"); EXPECT_EQ(descriptor.debugSource, "tests.hosted_preview.logical_size_fallback"); EXPECT_FLOAT_EQ(descriptor.canvasRect.x, 18.0f); EXPECT_FLOAT_EQ(descriptor.canvasRect.y, 22.0f); EXPECT_FLOAT_EQ(descriptor.canvasRect.width, 320.0f); EXPECT_FLOAT_EQ(descriptor.canvasRect.height, 180.0f); EXPECT_FLOAT_EQ(descriptor.logicalSize.width, 320.0f); EXPECT_FLOAT_EQ(descriptor.logicalSize.height, 180.0f); EXPECT_EQ(descriptor.submittedDrawListCount, 1u); EXPECT_EQ(descriptor.submittedCommandCount, 1u); EXPECT_TRUE(descriptor.queuedThisFrame); XCUIHostedPreviewSurfaceImage image = {}; EXPECT_FALSE(presenter->TryGetSurfaceImage("XCUI Demo", image)); EXPECT_FALSE(image.IsValid()); surfaceRegistry.UpdateSurface( "XCUI Demo", MakeHostedPreviewTextureHandle(11u, 640u, 360u), XCEngine::UI::UIRect(0.0f, 0.0f, 320.0f, 180.0f)); ASSERT_TRUE(presenter->TryGetSurfaceImage("XCUI Demo", image)); EXPECT_TRUE(image.IsValid()); EXPECT_EQ(image.texture.nativeHandle, 11u); EXPECT_EQ(image.texture.width, 640u); EXPECT_EQ(image.texture.height, 360u); EXPECT_FLOAT_EQ(image.uvMin.x, 0.0f); EXPECT_FLOAT_EQ(image.uvMin.y, 0.0f); EXPECT_FLOAT_EQ(image.uvMax.x, 0.5f); EXPECT_FLOAT_EQ(image.uvMax.y, 0.5f); } TEST(XCUIHostedPreviewPresenterTest, QueuedNativePresenterFallsBackWhenLogicalSizeIsPartiallySpecified) { XCUIHostedPreviewQueue queue = {}; XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {}; queue.BeginFrame(); std::unique_ptr presenter = CreateQueuedNativeXCUIHostedPreviewPresenter(queue, surfaceRegistry); ASSERT_NE(presenter, nullptr); XCEngine::UI::UIDrawData drawData = {}; drawData.EmplaceDrawList("HostedPreviewPartialLogicalSize").AddFilledRect( XCEngine::UI::UIRect(2.0f, 3.0f, 18.0f, 9.0f), XCEngine::UI::UIColor(0.1f, 0.2f, 0.3f, 1.0f)); XCUIHostedPreviewFrame frame = {}; frame.drawData = &drawData; frame.debugName = "XCUI Partial Logical Size"; frame.canvasRect = XCEngine::UI::UIRect(10.0f, 20.0f, 300.0f, 140.0f); frame.logicalSize = XCEngine::UI::UISize(512.0f, 0.0f); ASSERT_TRUE(presenter->Present(frame)); ASSERT_EQ(queue.GetQueuedFrames().size(), 1u); const XCUIHostedPreviewQueuedFrame& queuedFrame = queue.GetQueuedFrames().front(); EXPECT_FLOAT_EQ(queuedFrame.logicalSize.width, 300.0f); EXPECT_FLOAT_EQ(queuedFrame.logicalSize.height, 140.0f); } TEST(XCUIHostedPreviewPresenterTest, QueuedNativePresenterRejectsMissingDrawDataAndLeavesQueueUntouched) { XCUIHostedPreviewQueue queue = {}; XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {}; queue.BeginFrame(); std::unique_ptr presenter = CreateQueuedNativeXCUIHostedPreviewPresenter(queue, surfaceRegistry); ASSERT_NE(presenter, nullptr); XCUIHostedPreviewFrame frame = {}; frame.debugName = "Missing DrawData"; EXPECT_FALSE(presenter->Present(frame)); const XCUIHostedPreviewStats& stats = presenter->GetLastStats(); EXPECT_FALSE(stats.presented); EXPECT_FALSE(stats.queuedToNativePass); EXPECT_EQ(stats.submittedDrawListCount, 0u); EXPECT_EQ(stats.submittedCommandCount, 0u); EXPECT_EQ(stats.flushedDrawListCount, 0u); EXPECT_EQ(stats.flushedCommandCount, 0u); EXPECT_TRUE(queue.GetQueuedFrames().empty()); } TEST(XCUIHostedPreviewPresenterTest, HostedPreviewQueuePreservesSubmissionOrderAndPayloadMetadata) { XCUIHostedPreviewQueue queue = {}; queue.BeginFrame(); XCEngine::UI::UIDrawData firstDrawData = {}; XCEngine::UI::UIDrawList& firstDrawList = firstDrawData.EmplaceDrawList("FirstPreview"); firstDrawList.AddFilledRect( XCEngine::UI::UIRect(0.0f, 0.0f, 20.0f, 10.0f), XCEngine::UI::UIColor(1.0f, 0.2f, 0.2f, 1.0f)); XCEngine::UI::UIDrawData secondDrawData = {}; XCEngine::UI::UIDrawList& secondDrawList = secondDrawData.EmplaceDrawList("SecondPreview"); secondDrawList.AddFilledRect( XCEngine::UI::UIRect(10.0f, 8.0f, 24.0f, 14.0f), XCEngine::UI::UIColor(0.2f, 0.7f, 1.0f, 1.0f)); secondDrawList.AddText( XCEngine::UI::UIPoint(12.0f, 12.0f), "queued", XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f), 13.0f); XCUIHostedPreviewStats firstStats = {}; XCUIHostedPreviewFrame firstFrame = {}; firstFrame.drawData = &firstDrawData; firstFrame.debugName = "First Native Preview"; ASSERT_TRUE(queue.Submit(firstFrame, &firstStats)); XCUIHostedPreviewStats secondStats = {}; XCUIHostedPreviewFrame secondFrame = {}; secondFrame.drawData = &secondDrawData; ASSERT_TRUE(queue.Submit(secondFrame, &secondStats)); firstDrawData.Clear(); secondDrawData.Clear(); const auto& queuedFrames = queue.GetQueuedFrames(); ASSERT_EQ(queuedFrames.size(), 2u); EXPECT_EQ(firstStats.submittedDrawListCount, 1u); EXPECT_EQ(firstStats.submittedCommandCount, 1u); EXPECT_TRUE(firstStats.queuedToNativePass); EXPECT_EQ(secondStats.submittedDrawListCount, 1u); EXPECT_EQ(secondStats.submittedCommandCount, 2u); EXPECT_TRUE(secondStats.queuedToNativePass); EXPECT_EQ(queuedFrames[0].debugName, "First Native Preview"); EXPECT_EQ(queuedFrames[0].drawData.GetDrawListCount(), 1u); EXPECT_EQ(queuedFrames[0].drawData.GetTotalCommandCount(), 1u); EXPECT_EQ(queuedFrames[1].debugName, ""); EXPECT_EQ(queuedFrames[1].drawData.GetDrawListCount(), 1u); EXPECT_EQ(queuedFrames[1].drawData.GetTotalCommandCount(), 2u); } TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryIgnoresUnnamedQueuedFramesAndKeepsDescriptorListStable) { XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {}; XCUIHostedPreviewQueuedFrame unnamedFrame = {}; unnamedFrame.debugSource = "tests.hosted_preview.unnamed"; unnamedFrame.canvasRect = XCEngine::UI::UIRect(10.0f, 12.0f, 120.0f, 80.0f); unnamedFrame.logicalSize = XCEngine::UI::UISize(120.0f, 80.0f); unnamedFrame.drawData.EmplaceDrawList("Unnamed").AddFilledRect( XCEngine::UI::UIRect(0.0f, 0.0f, 16.0f, 16.0f), XCEngine::UI::UIColor(0.4f, 0.7f, 0.9f, 1.0f)); surfaceRegistry.RecordQueuedFrame(unnamedFrame, 5u); EXPECT_TRUE(surfaceRegistry.GetDescriptors().empty()); surfaceRegistry.UpdateSurface( "XCUI Demo", MakeHostedPreviewTextureHandle(13u, 800u, 600u), XCEngine::UI::UIRect(0.0f, 0.0f, 400.0f, 300.0f)); ASSERT_EQ(surfaceRegistry.GetDescriptors().size(), 1u); surfaceRegistry.RecordQueuedFrame(unnamedFrame, 9u); ASSERT_EQ(surfaceRegistry.GetDescriptors().size(), 1u); EXPECT_EQ(surfaceRegistry.GetDescriptors().front().debugName, "XCUI Demo"); EXPECT_TRUE(surfaceRegistry.GetDescriptors().front().image.IsValid()); } TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryExposesImageUvForRenderedCanvasRect) { XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {}; XCUIHostedPreviewSurfaceImage image = {}; surfaceRegistry.UpdateSurface( "XCUI Demo", MakeHostedPreviewTextureHandle(7u, 1024u, 768u), XCEngine::UI::UIRect(128.0f, 96.0f, 512.0f, 384.0f)); ASSERT_TRUE(surfaceRegistry.TryGetSurfaceImage("XCUI Demo", image)); EXPECT_TRUE(image.IsValid()); EXPECT_EQ(image.texture.nativeHandle, 7u); EXPECT_EQ(image.texture.width, 1024u); EXPECT_EQ(image.texture.height, 768u); EXPECT_EQ(image.surfaceWidth, 1024u); EXPECT_EQ(image.surfaceHeight, 768u); EXPECT_FLOAT_EQ(image.uvMin.x, 0.125f); EXPECT_FLOAT_EQ(image.uvMin.y, 0.125f); EXPECT_FLOAT_EQ(image.uvMax.x, 0.625f); EXPECT_FLOAT_EQ(image.uvMax.y, 0.625f); } TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryTracksQueuedFrameMetadataAlongsideLatestSurfaceImage) { XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {}; surfaceRegistry.BeginFrame(); XCEngine::UI::UIDrawData drawData = {}; XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("RegistryPreview"); drawList.AddFilledRect( XCEngine::UI::UIRect(8.0f, 6.0f, 30.0f, 18.0f), XCEngine::UI::UIColor(0.3f, 0.7f, 0.9f, 1.0f)); drawList.AddText( XCEngine::UI::UIPoint(16.0f, 12.0f), "meta", XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f), 12.0f); XCUIHostedPreviewQueuedFrame queuedFrame = {}; queuedFrame.debugName = "XCUI Demo"; queuedFrame.debugSource = "tests.hosted_preview.registry"; queuedFrame.canvasRect = XCEngine::UI::UIRect(24.0f, 32.0f, 300.0f, 160.0f); queuedFrame.logicalSize = XCEngine::UI::UISize(600.0f, 320.0f); queuedFrame.drawData = drawData; surfaceRegistry.RecordQueuedFrame(queuedFrame, 2u); XCUIHostedPreviewSurfaceDescriptor descriptor = {}; ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor)); EXPECT_EQ(descriptor.debugName, "XCUI Demo"); EXPECT_EQ(descriptor.debugSource, "tests.hosted_preview.registry"); EXPECT_FLOAT_EQ(descriptor.canvasRect.x, 24.0f); EXPECT_FLOAT_EQ(descriptor.canvasRect.y, 32.0f); EXPECT_FLOAT_EQ(descriptor.canvasRect.width, 300.0f); EXPECT_FLOAT_EQ(descriptor.canvasRect.height, 160.0f); EXPECT_FLOAT_EQ(descriptor.logicalSize.width, 600.0f); EXPECT_FLOAT_EQ(descriptor.logicalSize.height, 320.0f); EXPECT_EQ(descriptor.queuedFrameIndex, 2u); EXPECT_EQ(descriptor.submittedDrawListCount, 1u); EXPECT_EQ(descriptor.submittedCommandCount, 2u); EXPECT_TRUE(descriptor.queuedThisFrame); EXPECT_FALSE(descriptor.image.IsValid()); surfaceRegistry.UpdateSurface( "XCUI Demo", MakeHostedPreviewTextureHandle(9u, 1024u, 512u), XCEngine::UI::UIRect(128.0f, 64.0f, 320.0f, 160.0f)); ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor)); EXPECT_TRUE(descriptor.queuedThisFrame); EXPECT_TRUE(descriptor.image.IsValid()); EXPECT_EQ(descriptor.image.texture.nativeHandle, 9u); EXPECT_EQ(descriptor.image.texture.width, 1024u); EXPECT_EQ(descriptor.image.texture.height, 512u); EXPECT_FLOAT_EQ(descriptor.image.uvMin.x, 0.125f); EXPECT_FLOAT_EQ(descriptor.image.uvMin.y, 0.125f); EXPECT_FLOAT_EQ(descriptor.image.uvMax.x, 0.4375f); EXPECT_FLOAT_EQ(descriptor.image.uvMax.y, 0.4375f); surfaceRegistry.BeginFrame(); ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor)); EXPECT_FALSE(descriptor.queuedThisFrame); EXPECT_TRUE(descriptor.image.IsValid()); } TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryReusesDescriptorForRepeatedQueuedFrameKeys) { XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {}; surfaceRegistry.BeginFrame(); surfaceRegistry.UpdateSurface( "XCUI Demo", MakeHostedPreviewTextureHandle(31u, 400u, 200u), XCEngine::UI::UIRect(0.0f, 0.0f, 200.0f, 100.0f)); XCUIHostedPreviewQueuedFrame firstQueuedFrame = {}; firstQueuedFrame.debugName = "XCUI Demo"; firstQueuedFrame.debugSource = "tests.hosted_preview.first"; firstQueuedFrame.canvasRect = XCEngine::UI::UIRect(8.0f, 12.0f, 320.0f, 180.0f); firstQueuedFrame.logicalSize = XCEngine::UI::UISize(640.0f, 360.0f); firstQueuedFrame.drawData.EmplaceDrawList("First").AddFilledRect( XCEngine::UI::UIRect(0.0f, 0.0f, 16.0f, 16.0f), XCEngine::UI::UIColor(1.0f, 0.0f, 0.0f, 1.0f)); XCUIHostedPreviewQueuedFrame secondQueuedFrame = {}; secondQueuedFrame.debugName = "XCUI Demo"; secondQueuedFrame.debugSource = "tests.hosted_preview.second"; secondQueuedFrame.canvasRect = XCEngine::UI::UIRect(20.0f, 24.0f, 256.0f, 144.0f); secondQueuedFrame.logicalSize = XCEngine::UI::UISize(512.0f, 288.0f); XCEngine::UI::UIDrawList& secondDrawList = secondQueuedFrame.drawData.EmplaceDrawList("Second"); secondDrawList.AddFilledRect( XCEngine::UI::UIRect(1.0f, 2.0f, 20.0f, 10.0f), XCEngine::UI::UIColor(0.0f, 1.0f, 0.0f, 1.0f)); secondDrawList.AddText( XCEngine::UI::UIPoint(4.0f, 6.0f), "second", XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f), 12.0f); surfaceRegistry.RecordQueuedFrame(firstQueuedFrame, 1u); surfaceRegistry.RecordQueuedFrame(secondQueuedFrame, 7u); ASSERT_EQ(surfaceRegistry.GetDescriptors().size(), 1u); XCUIHostedPreviewSurfaceDescriptor descriptor = {}; ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor)); EXPECT_EQ(descriptor.debugSource, "tests.hosted_preview.second"); EXPECT_FLOAT_EQ(descriptor.canvasRect.x, 20.0f); EXPECT_FLOAT_EQ(descriptor.canvasRect.y, 24.0f); EXPECT_FLOAT_EQ(descriptor.canvasRect.width, 256.0f); EXPECT_FLOAT_EQ(descriptor.canvasRect.height, 144.0f); EXPECT_FLOAT_EQ(descriptor.logicalSize.width, 512.0f); EXPECT_FLOAT_EQ(descriptor.logicalSize.height, 288.0f); EXPECT_EQ(descriptor.queuedFrameIndex, 7u); EXPECT_EQ(descriptor.submittedDrawListCount, 1u); EXPECT_EQ(descriptor.submittedCommandCount, 2u); EXPECT_TRUE(descriptor.queuedThisFrame); EXPECT_TRUE(descriptor.image.IsValid()); EXPECT_EQ(descriptor.image.texture.nativeHandle, 31u); } TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryRejectsInvalidSurfaceUpdatesWithoutClobberingExistingImage) { XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {}; XCUIHostedPreviewSurfaceDescriptor descriptor = {}; surfaceRegistry.UpdateSurface( "XCUI Demo", MakeHostedPreviewTextureHandle(17u, 512u, 256u), XCEngine::UI::UIRect(64.0f, 32.0f, 256.0f, 128.0f)); ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor)); ASSERT_TRUE(descriptor.image.IsValid()); const XCUIHostedPreviewSurfaceImage originalImage = descriptor.image; surfaceRegistry.UpdateSurface( "XCUI Demo", XCEngine::UI::UITextureHandle{}, XCEngine::UI::UIRect(0.0f, 0.0f, 128.0f, 64.0f)); surfaceRegistry.UpdateSurface( "", MakeHostedPreviewTextureHandle(19u, 512u, 256u), XCEngine::UI::UIRect(0.0f, 0.0f, 128.0f, 64.0f)); surfaceRegistry.UpdateSurface( "XCUI Demo", MakeHostedPreviewTextureHandle(21u, 0u, 256u), XCEngine::UI::UIRect(0.0f, 0.0f, 128.0f, 64.0f)); ASSERT_TRUE(surfaceRegistry.TryGetSurfaceDescriptor("XCUI Demo", descriptor)); EXPECT_EQ(descriptor.image.texture.nativeHandle, originalImage.texture.nativeHandle); EXPECT_EQ(descriptor.image.texture.width, originalImage.texture.width); EXPECT_EQ(descriptor.image.texture.height, originalImage.texture.height); EXPECT_EQ(descriptor.image.surfaceWidth, originalImage.surfaceWidth); EXPECT_EQ(descriptor.image.surfaceHeight, originalImage.surfaceHeight); EXPECT_FLOAT_EQ(descriptor.image.uvMin.x, originalImage.uvMin.x); EXPECT_FLOAT_EQ(descriptor.image.uvMin.y, originalImage.uvMin.y); EXPECT_FLOAT_EQ(descriptor.image.uvMax.x, originalImage.uvMax.x); EXPECT_FLOAT_EQ(descriptor.image.uvMax.y, originalImage.uvMax.y); } TEST(XCUIHostedPreviewPresenterTest, BeginFrameClearsQueuedFramesButKeepsLastDrainStatsUntilReplaced) { XCUIHostedPreviewQueue queue = {}; XCEngine::UI::UIDrawData drawData = {}; XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("Preview"); drawList.AddFilledRect( XCEngine::UI::UIRect(4.0f, 4.0f, 18.0f, 12.0f), XCEngine::UI::UIColor(0.4f, 0.6f, 0.9f, 1.0f)); XCUIHostedPreviewFrame frame = {}; frame.drawData = &drawData; ASSERT_TRUE(queue.Submit(frame)); ASSERT_EQ(queue.GetQueuedFrames().size(), 1u); XCUIHostedPreviewDrainStats initialDrainStats = {}; initialDrainStats.queuedFrameCount = 3u; initialDrainStats.queuedCommandCount = 7u; initialDrainStats.renderedFrameCount = 2u; initialDrainStats.skippedFrameCount = 1u; queue.SetLastDrainStats(initialDrainStats); queue.BeginFrame(); EXPECT_TRUE(queue.GetQueuedFrames().empty()); const XCUIHostedPreviewDrainStats& drainStats = queue.GetLastDrainStats(); EXPECT_EQ(drainStats.queuedFrameCount, 3u); EXPECT_EQ(drainStats.queuedCommandCount, 7u); EXPECT_EQ(drainStats.renderedFrameCount, 2u); EXPECT_EQ(drainStats.skippedFrameCount, 1u); } TEST(XCUIHostedPreviewPresenterTest, SurfaceRegistryQueriesClearOutputForMissingOrInvalidNames) { XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {}; XCUIHostedPreviewSurfaceDescriptor descriptor = {}; descriptor.debugName = "stale"; descriptor.debugSource = "stale"; descriptor.queuedThisFrame = true; XCUIHostedPreviewSurfaceImage image = {}; image.texture = MakeHostedPreviewTextureHandle(23u, 64u, 64u); image.surfaceWidth = 64u; image.surfaceHeight = 64u; EXPECT_FALSE(surfaceRegistry.TryGetSurfaceDescriptor(nullptr, descriptor)); EXPECT_TRUE(descriptor.debugName.empty()); EXPECT_TRUE(descriptor.debugSource.empty()); EXPECT_FALSE(descriptor.queuedThisFrame); EXPECT_FALSE(descriptor.image.IsValid()); EXPECT_FALSE(surfaceRegistry.TryGetSurfaceImage("", image)); EXPECT_FALSE(image.IsValid()); EXPECT_EQ(image.surfaceWidth, 0u); EXPECT_EQ(image.surfaceHeight, 0u); } } // namespace