#include #include #include #include #include #include #include #include using namespace XCEngine::Components; using namespace XCEngine::Rendering; namespace { struct MockPipelineState { int initializeCalls = 0; int shutdownCalls = 0; int renderCalls = 0; uint32_t lastSurfaceWidth = 0; uint32_t lastSurfaceHeight = 0; CameraComponent* lastCamera = nullptr; size_t lastVisibleItemCount = 0; std::vector renderedCameras; }; class MockPipeline final : public RenderPipeline { public: explicit MockPipeline(std::shared_ptr state) : m_state(std::move(state)) { } bool Initialize(const RenderContext&) override { ++m_state->initializeCalls; return true; } void Shutdown() override { ++m_state->shutdownCalls; } bool Render( const RenderContext&, const RenderSurface& surface, const RenderSceneData& sceneData) override { ++m_state->renderCalls; m_state->lastSurfaceWidth = surface.GetWidth(); m_state->lastSurfaceHeight = surface.GetHeight(); m_state->lastCamera = sceneData.camera; m_state->lastVisibleItemCount = sceneData.visibleItems.size(); m_state->renderedCameras.push_back(sceneData.camera); return true; } private: std::shared_ptr m_state; }; RenderContext CreateValidContext() { RenderContext context; context.device = reinterpret_cast(1); context.commandList = reinterpret_cast(1); context.commandQueue = reinterpret_cast(1); return context; } } // namespace TEST(CameraRenderer_Test, UsesOverrideCameraAndSurfaceSizeWhenSubmittingScene) { Scene scene("CameraRendererScene"); GameObject* primaryCameraObject = scene.CreateGameObject("PrimaryCamera"); auto* primaryCamera = primaryCameraObject->AddComponent(); primaryCamera->SetPrimary(true); primaryCamera->SetDepth(10.0f); GameObject* overrideCameraObject = scene.CreateGameObject("OverrideCamera"); auto* overrideCamera = overrideCameraObject->AddComponent(); overrideCamera->SetPrimary(false); overrideCamera->SetDepth(-1.0f); auto state = std::make_shared(); CameraRenderer renderer(std::make_unique(state)); CameraRenderRequest request; request.scene = &scene; request.camera = overrideCamera; request.context = CreateValidContext(); request.surface = RenderSurface(640, 480); request.cameraDepth = overrideCamera->GetDepth(); ASSERT_TRUE(renderer.Render(request)); EXPECT_EQ(state->renderCalls, 1); EXPECT_EQ(state->lastSurfaceWidth, 640u); EXPECT_EQ(state->lastSurfaceHeight, 480u); EXPECT_EQ(state->lastCamera, overrideCamera); EXPECT_NE(state->lastCamera, primaryCamera); EXPECT_EQ(state->lastVisibleItemCount, 0u); } TEST(SceneRenderer_Test, BuildsSingleExplicitRequestFromSelectedCamera) { Scene scene("SceneRendererRequestScene"); GameObject* lowCameraObject = scene.CreateGameObject("LowCamera"); auto* lowCamera = lowCameraObject->AddComponent(); lowCamera->SetPrimary(true); lowCamera->SetDepth(1.0f); GameObject* highCameraObject = scene.CreateGameObject("HighCamera"); auto* highCamera = highCameraObject->AddComponent(); highCamera->SetPrimary(true); highCamera->SetDepth(5.0f); SceneRenderer renderer; const RenderContext context = CreateValidContext(); const RenderSurface surface(320, 180); const std::vector defaultRequests = renderer.BuildRenderRequests(scene, nullptr, context, surface); ASSERT_EQ(defaultRequests.size(), 1u); EXPECT_EQ(defaultRequests[0].camera, highCamera); EXPECT_EQ(defaultRequests[0].cameraDepth, 5.0f); EXPECT_EQ(defaultRequests[0].surface.GetWidth(), 320u); EXPECT_EQ(defaultRequests[0].surface.GetHeight(), 180u); const std::vector overrideRequests = renderer.BuildRenderRequests(scene, lowCamera, context, surface); ASSERT_EQ(overrideRequests.size(), 1u); EXPECT_EQ(overrideRequests[0].camera, lowCamera); } TEST(SceneRenderer_Test, ForwardsPipelineLifetimeAndRenderCallsToCameraRenderer) { Scene scene("SceneRendererScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(2.0f); auto initialState = std::make_shared(); auto replacementState = std::make_shared(); { auto initialPipeline = std::make_unique(initialState); MockPipeline* initialPipelineRaw = initialPipeline.get(); SceneRenderer renderer(std::move(initialPipeline)); EXPECT_EQ(renderer.GetPipeline(), initialPipelineRaw); auto replacementPipeline = std::make_unique(replacementState); MockPipeline* replacementPipelineRaw = replacementPipeline.get(); renderer.SetPipeline(std::move(replacementPipeline)); EXPECT_EQ(initialState->shutdownCalls, 1); EXPECT_EQ(renderer.GetPipeline(), replacementPipelineRaw); const RenderSurface surface(800, 600); ASSERT_TRUE(renderer.Render(scene, nullptr, CreateValidContext(), surface)); EXPECT_EQ(replacementState->renderCalls, 1); EXPECT_EQ(replacementState->lastSurfaceWidth, 800u); EXPECT_EQ(replacementState->lastSurfaceHeight, 600u); EXPECT_EQ(replacementState->lastCamera, camera); } EXPECT_EQ(initialState->shutdownCalls, 1); EXPECT_EQ(replacementState->shutdownCalls, 1); } TEST(SceneRenderer_Test, SortsManualCameraRequestsByDepthBeforeRendering) { Scene scene("SceneRendererManualRequests"); GameObject* farCameraObject = scene.CreateGameObject("FarCamera"); auto* farCamera = farCameraObject->AddComponent(); farCamera->SetPrimary(true); farCamera->SetDepth(10.0f); GameObject* nearCameraObject = scene.CreateGameObject("NearCamera"); auto* nearCamera = nearCameraObject->AddComponent(); nearCamera->SetPrimary(false); nearCamera->SetDepth(1.0f); auto state = std::make_shared(); SceneRenderer renderer(std::make_unique(state)); CameraRenderRequest farRequest; farRequest.scene = &scene; farRequest.camera = farCamera; farRequest.context = CreateValidContext(); farRequest.surface = RenderSurface(800, 600); farRequest.cameraDepth = farCamera->GetDepth(); CameraRenderRequest nearRequest = farRequest; nearRequest.camera = nearCamera; nearRequest.cameraDepth = nearCamera->GetDepth(); const std::vector requests = { farRequest, nearRequest }; ASSERT_TRUE(renderer.Render(requests)); ASSERT_EQ(state->renderedCameras.size(), 2u); EXPECT_EQ(state->renderedCameras[0], nearCamera); EXPECT_EQ(state->renderedCameras[1], farCamera); }