753 lines
31 KiB
C++
753 lines
31 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h"
|
|
#include "XCUIBackend/XCUIHostedPreviewPresenter.h"
|
|
|
|
#include <XCEngine/UI/DrawData.h>
|
|
|
|
#include <imgui.h>
|
|
|
|
#include <cstdint>
|
|
|
|
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;
|
|
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();
|
|
}
|
|
};
|
|
|
|
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);
|
|
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<ImTextureID>(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<IXCUIHostedPreviewPresenter> 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, PresentFlushesDrawDataIntoExplicitBindingResolvedImGuiDrawList) {
|
|
ImGuiContextScope contextScope;
|
|
PrepareImGui(800.0f, 600.0f);
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
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_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);
|
|
}
|
|
|
|
TEST(XCUIHostedPreviewPresenterTest, QueuedNativePresenterCopiesFrameIntoQueue) {
|
|
XCUIHostedPreviewQueue queue = {};
|
|
XCUIHostedPreviewSurfaceRegistry surfaceRegistry = {};
|
|
queue.BeginFrame();
|
|
|
|
std::unique_ptr<IXCUIHostedPreviewPresenter> 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<IXCUIHostedPreviewPresenter> 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<IXCUIHostedPreviewPresenter> 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<IXCUIHostedPreviewPresenter> 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
|