#include #include "Viewport/ViewportHostRenderFlowUtils.h" #include #include namespace { using XCEngine::Editor::ApplySceneViewportRenderRequestSetup; using XCEngine::Editor::ApplyViewportFailureStatus; using XCEngine::Editor::BuildGameViewportRenderFailurePolicy; using XCEngine::Editor::BuildSceneViewportRenderFailurePolicy; using XCEngine::Editor::BuildViewportRenderTargetUnavailablePolicy; using XCEngine::Editor::GameViewportRenderFailure; using XCEngine::Editor::MarkGameViewportRenderSuccess; using XCEngine::Editor::MarkSceneViewportRenderSuccess; using XCEngine::Editor::SceneViewportRenderFailure; using XCEngine::Editor::ViewportRenderTargets; using XCEngine::RHI::Format; using XCEngine::RHI::RHIResourceView; using XCEngine::RHI::ResourceStates; using XCEngine::RHI::ResourceViewDimension; using XCEngine::RHI::ResourceViewType; using XCEngine::Rendering::RenderPass; using XCEngine::Rendering::RenderPassContext; using XCEngine::Rendering::RenderPassSequence; using XCEngine::Rendering::RenderSurface; class DummyResourceView final : public RHIResourceView { public: explicit DummyResourceView( ResourceViewType viewType = ResourceViewType::RenderTarget, Format format = Format::R8G8B8A8_UNorm) : m_viewType(viewType) , m_format(format) { } void Shutdown() override { } void* GetNativeHandle() override { return nullptr; } bool IsValid() const override { return true; } ResourceViewType GetViewType() const override { return m_viewType; } ResourceViewDimension GetDimension() const override { return ResourceViewDimension::Texture2D; } Format GetFormat() const override { return m_format; } private: ResourceViewType m_viewType = ResourceViewType::RenderTarget; Format m_format = Format::R8G8B8A8_UNorm; }; class NoopRenderPass final : public RenderPass { public: const char* GetName() const override { return "NoopRenderPass"; } bool Execute(const RenderPassContext&) override { return true; } }; TEST(ViewportRenderFlowUtilsTest, BuildFailurePoliciesExposeExpectedStatusAndClearBehavior) { const auto targetUnavailable = BuildViewportRenderTargetUnavailablePolicy(); EXPECT_STREQ(targetUnavailable.statusText, "Viewport render target is unavailable"); EXPECT_FALSE(targetUnavailable.shouldClear); EXPECT_TRUE(targetUnavailable.invalidateObjectIdFrame); const auto missingSceneCamera = BuildSceneViewportRenderFailurePolicy(SceneViewportRenderFailure::MissingSceneViewCamera); EXPECT_STREQ(missingSceneCamera.statusText, "Scene view camera is unavailable"); EXPECT_TRUE(missingSceneCamera.shouldClear); EXPECT_FLOAT_EQ(missingSceneCamera.clearColor.r, 0.18f); EXPECT_FLOAT_EQ(missingSceneCamera.clearColor.g, 0.07f); EXPECT_FLOAT_EQ(missingSceneCamera.clearColor.b, 0.07f); EXPECT_FALSE(missingSceneCamera.setStatusIfEmpty); const auto sceneRendererFailed = BuildSceneViewportRenderFailurePolicy(SceneViewportRenderFailure::SceneRendererFailed); EXPECT_STREQ(sceneRendererFailed.statusText, "Scene renderer failed"); EXPECT_TRUE(sceneRendererFailed.shouldClear); EXPECT_TRUE(sceneRendererFailed.setStatusIfEmpty); const auto noGameCamera = BuildGameViewportRenderFailurePolicy(GameViewportRenderFailure::NoCameraInScene); EXPECT_STREQ(noGameCamera.statusText, "No camera in scene"); EXPECT_TRUE(noGameCamera.shouldClear); EXPECT_FLOAT_EQ(noGameCamera.clearColor.r, 0.10f); EXPECT_FLOAT_EQ(noGameCamera.clearColor.g, 0.09f); EXPECT_FLOAT_EQ(noGameCamera.clearColor.b, 0.08f); } TEST(ViewportRenderFlowUtilsTest, ApplyViewportFailureStatusRespectsSetIfEmptyBehavior) { std::string statusText; ApplyViewportFailureStatus( statusText, BuildSceneViewportRenderFailurePolicy(SceneViewportRenderFailure::SceneRendererFailed)); EXPECT_EQ(statusText, "Scene renderer failed"); statusText = "Scene object id shader view is unavailable"; ApplyViewportFailureStatus( statusText, BuildSceneViewportRenderFailurePolicy(SceneViewportRenderFailure::SceneRendererFailed)); EXPECT_EQ(statusText, "Scene object id shader view is unavailable"); ApplyViewportFailureStatus( statusText, BuildGameViewportRenderFailurePolicy(GameViewportRenderFailure::NoActiveScene)); EXPECT_EQ(statusText, "No active scene"); } TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupAttachesOptionalPassesAndObjectIdSurface) { DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt); DummyResourceView objectIdView(ResourceViewType::RenderTarget); ViewportRenderTargets targets = {}; targets.width = 800; targets.height = 600; targets.depthView = &depthView; targets.objectIdView = &objectIdView; targets.objectIdState = ResourceStates::Common; RenderPassSequence postPasses; postPasses.AddPass(std::make_unique()); XCEngine::Rendering::CameraRenderRequest request = {}; request.surface = RenderSurface(800, 600); request.surface.SetRenderArea(XCEngine::Math::RectInt(64, 32, 320, 240)); ApplySceneViewportRenderRequestSetup(targets, &postPasses, request); EXPECT_EQ(request.postScenePasses, &postPasses); EXPECT_TRUE(request.objectId.IsRequested()); ASSERT_EQ(request.objectId.surface.GetColorAttachments().size(), 1u); EXPECT_EQ(request.objectId.surface.GetColorAttachments()[0], &objectIdView); EXPECT_EQ(request.objectId.surface.GetDepthAttachment(), &depthView); const auto requestArea = request.surface.GetRenderArea(); const auto objectIdArea = request.objectId.surface.GetRenderArea(); EXPECT_EQ(objectIdArea.x, requestArea.x); EXPECT_EQ(objectIdArea.y, requestArea.y); EXPECT_EQ(objectIdArea.width, requestArea.width); EXPECT_EQ(objectIdArea.height, requestArea.height); } TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupSkipsUnavailableOptionalAttachments) { ViewportRenderTargets targets = {}; targets.width = 800; targets.height = 600; RenderPassSequence postPasses; XCEngine::Rendering::CameraRenderRequest request = {}; request.postScenePasses = reinterpret_cast(static_cast(0x1)); request.objectId.surface = RenderSurface(1, 1); request.objectId.surface.SetColorAttachment( reinterpret_cast(static_cast(0x2))); ApplySceneViewportRenderRequestSetup(targets, &postPasses, request); EXPECT_EQ(request.postScenePasses, nullptr); EXPECT_FALSE(request.objectId.IsRequested()); } TEST(ViewportRenderFlowUtilsTest, MarkSceneRenderSuccessMovesTargetsToShaderResourceState) { DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt); DummyResourceView objectIdView(ResourceViewType::RenderTarget); ViewportRenderTargets targets = {}; targets.width = 640; targets.height = 360; targets.depthView = &depthView; targets.objectIdView = &objectIdView; targets.colorState = ResourceStates::Common; targets.objectIdState = ResourceStates::Common; XCEngine::Rendering::CameraRenderRequest request = {}; request.surface = RenderSurface(640, 360); ApplySceneViewportRenderRequestSetup(targets, nullptr, request); MarkSceneViewportRenderSuccess(targets, request); EXPECT_EQ(targets.colorState, ResourceStates::PixelShaderResource); EXPECT_EQ(targets.objectIdState, ResourceStates::PixelShaderResource); EXPECT_TRUE(targets.hasValidObjectIdFrame); ViewportRenderTargets noObjectIdTargets = {}; noObjectIdTargets.colorState = ResourceStates::Common; noObjectIdTargets.objectIdState = ResourceStates::Common; XCEngine::Rendering::CameraRenderRequest noObjectIdRequest = {}; MarkSceneViewportRenderSuccess(noObjectIdTargets, noObjectIdRequest); EXPECT_EQ(noObjectIdTargets.colorState, ResourceStates::PixelShaderResource); EXPECT_EQ(noObjectIdTargets.objectIdState, ResourceStates::PixelShaderResource); EXPECT_FALSE(noObjectIdTargets.hasValidObjectIdFrame); } TEST(ViewportRenderFlowUtilsTest, MarkGameRenderSuccessClearsObjectIdFrameAndUpdatesColorState) { ViewportRenderTargets targets = {}; targets.colorState = ResourceStates::Common; targets.hasValidObjectIdFrame = true; MarkGameViewportRenderSuccess(targets); EXPECT_EQ(targets.colorState, ResourceStates::PixelShaderResource); EXPECT_FALSE(targets.hasValidObjectIdFrame); } } // namespace