Integrate XCUI shell state and runtime frame seams

This commit is contained in:
2026-04-05 12:50:55 +08:00
parent ec97445071
commit e5e9f348a3
29 changed files with 3183 additions and 102 deletions

View File

@@ -13,6 +13,7 @@ namespace {
using XCEngine::Editor::XCUIBackend::CreateImGuiXCUIHostedPreviewPresenter;
using XCEngine::Editor::XCUIBackend::CreateQueuedNativeXCUIHostedPreviewPresenter;
using XCEngine::Editor::XCUIBackend::IImGuiXCUIHostedPreviewTargetBinding;
using XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter;
using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewFrame;
using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats;
@@ -36,6 +37,19 @@ public:
}
};
class RecordingImGuiHostedPreviewTargetBinding final : public IImGuiXCUIHostedPreviewTargetBinding {
public:
ImDrawList* ResolveTargetDrawList(const XCUIHostedPreviewFrame& frame) const override {
++resolveCallCount;
lastFrame = &frame;
return resolvedDrawList;
}
mutable std::size_t resolveCallCount = 0u;
mutable const XCUIHostedPreviewFrame* lastFrame = nullptr;
ImDrawList* resolvedDrawList = nullptr;
};
void PrepareImGui(float width = 1024.0f, float height = 768.0f) {
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2(width, height);
@@ -78,13 +92,10 @@ TEST(XCUIHostedPreviewPresenterTest, PresentReturnsFalseAndClearsStatsWhenFrameH
EXPECT_EQ(stats.flushedCommandCount, 0u);
}
TEST(XCUIHostedPreviewPresenterTest, PresentFlushesDrawDataIntoProvidedImGuiDrawList) {
TEST(XCUIHostedPreviewPresenterTest, PresentFlushesDrawDataIntoExplicitBindingResolvedImGuiDrawList) {
ImGuiContextScope contextScope;
PrepareImGui(800.0f, 600.0f);
std::unique_ptr<IXCUIHostedPreviewPresenter> presenter = CreateImGuiXCUIHostedPreviewPresenter();
ASSERT_NE(presenter, nullptr);
XCEngine::UI::UIDrawData drawData = {};
XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("HostedPreview");
drawList.AddFilledRect(
@@ -101,6 +112,15 @@ TEST(XCUIHostedPreviewPresenterTest, PresentFlushesDrawDataIntoProvidedImGuiDraw
ImDrawList* targetDrawList = ImGui::GetWindowDrawList();
ASSERT_NE(targetDrawList, nullptr);
std::unique_ptr<RecordingImGuiHostedPreviewTargetBinding> targetBinding =
std::make_unique<RecordingImGuiHostedPreviewTargetBinding>();
RecordingImGuiHostedPreviewTargetBinding* targetBindingPtr = targetBinding.get();
targetBindingPtr->resolvedDrawList = targetDrawList;
std::unique_ptr<IXCUIHostedPreviewPresenter> presenter =
CreateImGuiXCUIHostedPreviewPresenter(std::move(targetBinding));
ASSERT_NE(presenter, nullptr);
XCUIHostedPreviewFrame frame = {};
frame.drawData = &drawData;
@@ -116,6 +136,129 @@ TEST(XCUIHostedPreviewPresenterTest, PresentFlushesDrawDataIntoProvidedImGuiDraw
EXPECT_EQ(stats.submittedCommandCount, 2u);
EXPECT_EQ(stats.flushedDrawListCount, 1u);
EXPECT_EQ(stats.flushedCommandCount, 2u);
EXPECT_EQ(targetBindingPtr->resolveCallCount, 1u);
EXPECT_EQ(targetBindingPtr->lastFrame, &frame);
EXPECT_GT(targetDrawList->VtxBuffer.Size, 0);
EXPECT_GT(targetDrawList->CmdBuffer.Size, 0);
}
TEST(XCUIHostedPreviewPresenterTest, PresentReturnsFalseWhenExplicitBindingDoesNotResolveTargetDrawList) {
ImGuiContextScope contextScope;
PrepareImGui(800.0f, 600.0f);
XCEngine::UI::UIDrawData drawData = {};
drawData.EmplaceDrawList("HostedPreviewMissingTarget").AddFilledRect(
XCEngine::UI::UIRect(10.0f, 12.0f, 44.0f, 28.0f),
XCEngine::UI::UIColor(0.9f, 0.3f, 0.2f, 1.0f));
std::unique_ptr<RecordingImGuiHostedPreviewTargetBinding> targetBinding =
std::make_unique<RecordingImGuiHostedPreviewTargetBinding>();
RecordingImGuiHostedPreviewTargetBinding* targetBindingPtr = targetBinding.get();
std::unique_ptr<IXCUIHostedPreviewPresenter> presenter =
CreateImGuiXCUIHostedPreviewPresenter(std::move(targetBinding));
ASSERT_NE(presenter, nullptr);
XCUIHostedPreviewFrame frame = {};
frame.drawData = &drawData;
frame.debugName = "HostedPreviewMissingTarget";
const bool presented = presenter->Present(frame);
const XCUIHostedPreviewStats& stats = presenter->GetLastStats();
EXPECT_FALSE(presented);
EXPECT_FALSE(stats.presented);
EXPECT_EQ(stats.submittedDrawListCount, 1u);
EXPECT_EQ(stats.submittedCommandCount, 1u);
EXPECT_EQ(stats.flushedDrawListCount, 0u);
EXPECT_EQ(stats.flushedCommandCount, 0u);
EXPECT_EQ(targetBindingPtr->resolveCallCount, 1u);
EXPECT_EQ(targetBindingPtr->lastFrame, &frame);
}
TEST(XCUIHostedPreviewPresenterTest, PresentFlushesDrawDataIntoExplicitBindingResolvedForegroundDrawListWithoutWindow) {
ImGuiContextScope contextScope;
PrepareImGui(800.0f, 600.0f);
XCEngine::UI::UIDrawData drawData = {};
XCEngine::UI::UIDrawList& drawList = drawData.EmplaceDrawList("HostedPreviewForegroundTarget");
drawList.AddFilledRect(
XCEngine::UI::UIRect(16.0f, 18.0f, 52.0f, 34.0f),
XCEngine::UI::UIColor(0.15f, 0.75f, 0.45f, 1.0f));
drawList.AddText(
XCEngine::UI::UIPoint(24.0f, 28.0f),
"foreground",
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
15.0f);
ImGui::NewFrame();
ImDrawList* targetDrawList = ImGui::GetForegroundDrawList();
ASSERT_NE(targetDrawList, nullptr);
const int baselineVertexCount = targetDrawList->VtxBuffer.Size;
const int baselineIndexCount = targetDrawList->IdxBuffer.Size;
const int baselineCommandCount = targetDrawList->CmdBuffer.Size;
std::unique_ptr<RecordingImGuiHostedPreviewTargetBinding> targetBinding =
std::make_unique<RecordingImGuiHostedPreviewTargetBinding>();
RecordingImGuiHostedPreviewTargetBinding* targetBindingPtr = targetBinding.get();
targetBindingPtr->resolvedDrawList = targetDrawList;
std::unique_ptr<IXCUIHostedPreviewPresenter> presenter =
CreateImGuiXCUIHostedPreviewPresenter(std::move(targetBinding));
ASSERT_NE(presenter, nullptr);
XCUIHostedPreviewFrame frame = {};
frame.drawData = &drawData;
frame.debugName = "HostedPreviewForegroundTarget";
const bool presented = presenter->Present(frame);
const XCUIHostedPreviewStats& stats = presenter->GetLastStats();
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_EQ(targetBindingPtr->resolveCallCount, 1u);
EXPECT_EQ(targetBindingPtr->lastFrame, &frame);
EXPECT_GT(targetDrawList->VtxBuffer.Size, baselineVertexCount);
EXPECT_GT(targetDrawList->IdxBuffer.Size, baselineIndexCount);
EXPECT_GE(targetDrawList->CmdBuffer.Size, baselineCommandCount);
}
TEST(XCUIHostedPreviewPresenterTest, DefaultFactoryStillUsesCurrentWindowBindingForLegacyImGuiPath) {
ImGuiContextScope contextScope;
PrepareImGui(800.0f, 600.0f);
std::unique_ptr<IXCUIHostedPreviewPresenter> presenter = CreateImGuiXCUIHostedPreviewPresenter();
ASSERT_NE(presenter, nullptr);
XCEngine::UI::UIDrawData drawData = {};
drawData.EmplaceDrawList("HostedPreviewDefaultBinding").AddFilledRect(
XCEngine::UI::UIRect(8.0f, 10.0f, 36.0f, 22.0f),
XCEngine::UI::UIColor(0.2f, 0.6f, 0.9f, 1.0f));
ImGui::NewFrame();
ASSERT_TRUE(ImGui::Begin("HostedPreviewPresenterDefaultBindingWindow"));
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.flushedDrawListCount, 1u);
EXPECT_EQ(stats.flushedCommandCount, 1u);
EXPECT_GT(targetDrawList->VtxBuffer.Size, 0);
EXPECT_GT(targetDrawList->CmdBuffer.Size, 0);
}