#include #include #include #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; bool renderResult = true; uint32_t lastSurfaceWidth = 0; uint32_t lastSurfaceHeight = 0; int32_t lastRenderAreaX = 0; int32_t lastRenderAreaY = 0; int32_t lastRenderAreaWidth = 0; int32_t lastRenderAreaHeight = 0; uint32_t lastCameraViewportWidth = 0; uint32_t lastCameraViewportHeight = 0; CameraComponent* lastCamera = nullptr; size_t lastVisibleItemCount = 0; RenderClearFlags lastClearFlags = RenderClearFlags::All; std::vector renderedCameras; std::vector renderedClearFlags; std::vector eventLog; }; struct MockPipelineAssetState { int createCalls = 0; std::shared_ptr lastCreatedPipelineState; }; 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->eventLog.push_back("pipeline"); ++m_state->renderCalls; m_state->lastSurfaceWidth = surface.GetWidth(); m_state->lastSurfaceHeight = surface.GetHeight(); const XCEngine::Math::RectInt renderArea = surface.GetRenderArea(); m_state->lastRenderAreaX = renderArea.x; m_state->lastRenderAreaY = renderArea.y; m_state->lastRenderAreaWidth = renderArea.width; m_state->lastRenderAreaHeight = renderArea.height; m_state->lastCamera = sceneData.camera; m_state->lastCameraViewportWidth = sceneData.cameraData.viewportWidth; m_state->lastCameraViewportHeight = sceneData.cameraData.viewportHeight; m_state->lastVisibleItemCount = sceneData.visibleItems.size(); m_state->lastClearFlags = sceneData.cameraData.clearFlags; m_state->renderedCameras.push_back(sceneData.camera); m_state->renderedClearFlags.push_back(sceneData.cameraData.clearFlags); return m_state->renderResult; } private: std::shared_ptr m_state; }; class MockPipelineAsset final : public RenderPipelineAsset { public: explicit MockPipelineAsset(std::shared_ptr state) : m_state(std::move(state)) { } std::unique_ptr CreatePipeline() const override { ++m_state->createCalls; m_state->lastCreatedPipelineState = std::make_shared(); return std::make_unique(m_state->lastCreatedPipelineState); } private: std::shared_ptr m_state; }; class MockObjectIdPass final : public ObjectIdPass { public: MockObjectIdPass( std::shared_ptr state, bool renderResult = true) : m_state(std::move(state)) , m_renderResult(renderResult) { } bool Render( const RenderContext&, const RenderSurface&, const RenderSceneData&) override { m_state->eventLog.push_back("objectId"); return m_renderResult; } void Shutdown() override { m_state->eventLog.push_back("shutdown:objectId"); } private: std::shared_ptr m_state; bool m_renderResult = true; }; class TrackingPass final : public RenderPass { public: TrackingPass( std::shared_ptr state, const char* label, bool initializeResult = true, bool executeResult = true) : m_state(std::move(state)) , m_label(label) , m_initializeResult(initializeResult) , m_executeResult(executeResult) { } const char* GetName() const override { return m_label; } bool Initialize(const RenderContext&) override { m_state->eventLog.push_back(std::string("init:") + m_label); return m_initializeResult; } bool Execute(const RenderPassContext&) override { m_state->eventLog.push_back(m_label); return m_executeResult; } void Shutdown() override { m_state->eventLog.push_back(std::string("shutdown:") + m_label); } private: std::shared_ptr m_state; const char* m_label = ""; bool m_initializeResult = true; bool m_executeResult = true; }; 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(); request.clearFlags = RenderClearFlags::None; ASSERT_TRUE(renderer.Render(request)); EXPECT_EQ(state->renderCalls, 1); EXPECT_EQ(state->lastSurfaceWidth, 640u); EXPECT_EQ(state->lastSurfaceHeight, 480u); EXPECT_EQ(state->lastRenderAreaX, 0); EXPECT_EQ(state->lastRenderAreaY, 0); EXPECT_EQ(state->lastRenderAreaWidth, 640); EXPECT_EQ(state->lastRenderAreaHeight, 480); EXPECT_EQ(state->lastCameraViewportWidth, 640u); EXPECT_EQ(state->lastCameraViewportHeight, 480u); EXPECT_EQ(state->lastCamera, overrideCamera); EXPECT_NE(state->lastCamera, primaryCamera); EXPECT_EQ(state->lastVisibleItemCount, 0u); EXPECT_EQ(state->lastClearFlags, RenderClearFlags::None); } TEST(CameraRenderer_Test, ExecutesInjectedPreAndPostPassSequencesAroundPipelineRender) { Scene scene("CameraRendererPassScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(3.0f); auto state = std::make_shared(); CameraRenderer renderer(std::make_unique(state)); RenderPassSequence prePasses; prePasses.AddPass(std::make_unique(state, "pre")); RenderPassSequence postPasses; postPasses.AddPass(std::make_unique(state, "post")); CameraRenderRequest request; request.scene = &scene; request.camera = camera; request.context = CreateValidContext(); request.surface = RenderSurface(320, 180); request.cameraDepth = camera->GetDepth(); request.preScenePasses = &prePasses; request.postScenePasses = &postPasses; ASSERT_TRUE(renderer.Render(request)); EXPECT_EQ( state->eventLog, (std::vector{ "init:pre", "pre", "pipeline", "init:post", "post", "shutdown:post", "shutdown:pre" })); } TEST(CameraRenderer_Test, ExecutesObjectIdPassBetweenPipelineAndPostPassesWhenRequested) { Scene scene("CameraRendererObjectIdPassScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(3.0f); auto state = std::make_shared(); CameraRenderer renderer( std::make_unique(state), std::make_unique(state)); RenderPassSequence prePasses; prePasses.AddPass(std::make_unique(state, "pre")); RenderPassSequence postPasses; postPasses.AddPass(std::make_unique(state, "post")); CameraRenderRequest request; request.scene = &scene; request.camera = camera; request.context = CreateValidContext(); request.surface = RenderSurface(320, 180); request.cameraDepth = camera->GetDepth(); request.preScenePasses = &prePasses; request.postScenePasses = &postPasses; request.objectId.surface = RenderSurface(320, 180); request.objectId.surface.SetColorAttachment(reinterpret_cast(1)); request.objectId.surface.SetDepthAttachment(reinterpret_cast(2)); ASSERT_TRUE(renderer.Render(request)); EXPECT_EQ( state->eventLog, (std::vector{ "init:pre", "pre", "pipeline", "objectId", "init:post", "post", "shutdown:post", "shutdown:pre" })); } TEST(CameraRenderer_Test, ShutsDownInitializedPassesWhenPipelineRenderFails) { Scene scene("CameraRendererFailureScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(2.0f); auto state = std::make_shared(); state->renderResult = false; CameraRenderer renderer(std::make_unique(state)); RenderPassSequence prePasses; prePasses.AddPass(std::make_unique(state, "pre")); CameraRenderRequest request; request.scene = &scene; request.camera = camera; request.context = CreateValidContext(); request.surface = RenderSurface(320, 180); request.cameraDepth = camera->GetDepth(); request.preScenePasses = &prePasses; EXPECT_FALSE(renderer.Render(request)); EXPECT_EQ( state->eventLog, (std::vector{ "init:pre", "pre", "pipeline", "shutdown:pre" })); } TEST(CameraRenderer_Test, StopsRenderingWhenObjectIdPassFails) { Scene scene("CameraRendererObjectIdFailureScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(2.0f); auto state = std::make_shared(); CameraRenderer renderer( std::make_unique(state), std::make_unique(state, false)); RenderPassSequence prePasses; prePasses.AddPass(std::make_unique(state, "pre")); RenderPassSequence postPasses; postPasses.AddPass(std::make_unique(state, "post")); CameraRenderRequest request; request.scene = &scene; request.camera = camera; request.context = CreateValidContext(); request.surface = RenderSurface(320, 180); request.cameraDepth = camera->GetDepth(); request.preScenePasses = &prePasses; request.postScenePasses = &postPasses; request.objectId.surface = RenderSurface(320, 180); request.objectId.surface.SetColorAttachment(reinterpret_cast(1)); request.objectId.surface.SetDepthAttachment(reinterpret_cast(2)); EXPECT_FALSE(renderer.Render(request)); EXPECT_EQ( state->eventLog, (std::vector{ "init:pre", "pre", "pipeline", "objectId", "shutdown:pre" })); } TEST(CameraRenderer_Test, ShutsDownSequencesWhenPostPassInitializationFails) { Scene scene("CameraRendererPostPassInitFailureScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(4.0f); auto state = std::make_shared(); CameraRenderer renderer(std::make_unique(state)); RenderPassSequence prePasses; prePasses.AddPass(std::make_unique(state, "pre")); RenderPassSequence postPasses; postPasses.AddPass(std::make_unique(state, "post", false, true)); CameraRenderRequest request; request.scene = &scene; request.camera = camera; request.context = CreateValidContext(); request.surface = RenderSurface(512, 512); request.cameraDepth = camera->GetDepth(); request.preScenePasses = &prePasses; request.postScenePasses = &postPasses; EXPECT_FALSE(renderer.Render(request)); EXPECT_EQ( state->eventLog, (std::vector{ "init:pre", "pre", "pipeline", "init:post", "shutdown:post", "shutdown:pre" })); } TEST(SceneRenderer_Test, BuildsSortedRequestsForAllUsableCamerasAndHonorsOverrideCamera) { 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); highCamera->SetClearMode(CameraClearMode::None); 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(), 2u); EXPECT_EQ(defaultRequests[0].camera, lowCamera); EXPECT_EQ(defaultRequests[0].cameraDepth, 1.0f); EXPECT_EQ(defaultRequests[0].clearFlags, RenderClearFlags::All); EXPECT_EQ(defaultRequests[0].surface.GetWidth(), 320u); EXPECT_EQ(defaultRequests[0].surface.GetHeight(), 180u); EXPECT_EQ(defaultRequests[1].camera, highCamera); EXPECT_EQ(defaultRequests[1].cameraDepth, 5.0f); EXPECT_EQ(defaultRequests[1].clearFlags, RenderClearFlags::None); const std::vector overrideRequests = renderer.BuildRenderRequests(scene, lowCamera, context, surface); ASSERT_EQ(overrideRequests.size(), 1u); EXPECT_EQ(overrideRequests[0].camera, lowCamera); EXPECT_EQ(overrideRequests[0].clearFlags, RenderClearFlags::All); } TEST(SceneRenderer_Test, RendersBaseCamerasBeforeOverlayCamerasAndResolvesAutoClearPerStackType) { Scene scene("SceneRendererCameraStackScene"); GameObject* lateBaseCameraObject = scene.CreateGameObject("LateBaseCamera"); auto* lateBaseCamera = lateBaseCameraObject->AddComponent(); lateBaseCamera->SetDepth(10.0f); lateBaseCamera->SetStackType(CameraStackType::Base); GameObject* earlyBaseCameraObject = scene.CreateGameObject("EarlyBaseCamera"); auto* earlyBaseCamera = earlyBaseCameraObject->AddComponent(); earlyBaseCamera->SetDepth(1.0f); earlyBaseCamera->SetStackType(CameraStackType::Base); GameObject* overlayCameraObject = scene.CreateGameObject("OverlayCamera"); auto* overlayCamera = overlayCameraObject->AddComponent(); overlayCamera->SetDepth(-10.0f); overlayCamera->SetStackType(CameraStackType::Overlay); SceneRenderer renderer; const std::vector requests = renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), RenderSurface(640, 360)); ASSERT_EQ(requests.size(), 3u); EXPECT_EQ(requests[0].camera, earlyBaseCamera); EXPECT_EQ(requests[0].cameraStackOrder, 0u); EXPECT_EQ(requests[0].clearFlags, RenderClearFlags::All); EXPECT_EQ(requests[1].camera, lateBaseCamera); EXPECT_EQ(requests[1].cameraStackOrder, 0u); EXPECT_EQ(requests[1].clearFlags, RenderClearFlags::Depth); EXPECT_EQ(requests[2].camera, overlayCamera); EXPECT_EQ(requests[2].cameraStackOrder, 1u); EXPECT_EQ(requests[2].clearFlags, RenderClearFlags::Depth); } TEST(SceneRenderer_Test, FallsBackToColorClearForFirstOverlayCameraWhenNoBaseCameraExists) { Scene scene("SceneRendererOverlayOnlyScene"); GameObject* firstOverlayObject = scene.CreateGameObject("FirstOverlay"); auto* firstOverlay = firstOverlayObject->AddComponent(); firstOverlay->SetDepth(1.0f); firstOverlay->SetStackType(CameraStackType::Overlay); GameObject* secondOverlayObject = scene.CreateGameObject("SecondOverlay"); auto* secondOverlay = secondOverlayObject->AddComponent(); secondOverlay->SetDepth(2.0f); secondOverlay->SetStackType(CameraStackType::Overlay); SceneRenderer renderer; const std::vector requests = renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), RenderSurface(320, 180)); ASSERT_EQ(requests.size(), 2u); EXPECT_EQ(requests[0].camera, firstOverlay); EXPECT_EQ(requests[0].clearFlags, RenderClearFlags::All); EXPECT_EQ(requests[1].camera, secondOverlay); EXPECT_EQ(requests[1].clearFlags, RenderClearFlags::Depth); } TEST(SceneRenderer_Test, HonorsExplicitOverrideCameraClearMode) { Scene scene("SceneRendererOverrideClearModeScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(2.0f); camera->SetClearMode(CameraClearMode::DepthOnly); SceneRenderer renderer; const std::vector requests = renderer.BuildRenderRequests(scene, camera, CreateValidContext(), RenderSurface(640, 360)); ASSERT_EQ(requests.size(), 1u); EXPECT_EQ(requests[0].camera, camera); EXPECT_EQ(requests[0].clearFlags, RenderClearFlags::Depth); } TEST(SceneRenderer_Test, ResolvesNormalizedCameraViewportRectToPerRequestRenderArea) { Scene scene("SceneRendererViewportRectScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(2.0f); camera->SetViewportRect(XCEngine::Math::Rect(0.25f, 0.1f, 0.5f, 0.4f)); SceneRenderer renderer; const std::vector requests = renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), RenderSurface(800, 600)); ASSERT_EQ(requests.size(), 1u); const XCEngine::Math::RectInt renderArea = requests[0].surface.GetRenderArea(); EXPECT_EQ(renderArea.x, 200); EXPECT_EQ(renderArea.y, 60); EXPECT_EQ(renderArea.width, 400); EXPECT_EQ(renderArea.height, 240); } TEST(SceneRenderer_Test, ComposesCameraViewportRectWithinExistingSurfaceRenderArea) { Scene scene("SceneRendererNestedViewportRectScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(2.0f); camera->SetViewportRect(XCEngine::Math::Rect(0.25f, 0.1f, 0.5f, 0.4f)); RenderSurface surface(800, 600); surface.SetRenderArea(XCEngine::Math::RectInt(100, 50, 400, 300)); SceneRenderer renderer; const std::vector requests = renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), surface); ASSERT_EQ(requests.size(), 1u); const XCEngine::Math::RectInt renderArea = requests[0].surface.GetRenderArea(); EXPECT_EQ(renderArea.x, 200); EXPECT_EQ(renderArea.y, 80); EXPECT_EQ(renderArea.width, 200); EXPECT_EQ(renderArea.height, 120); } TEST(SceneRenderer_Test, PreservesExistingSurfaceRenderAreaForFullViewportCamera) { Scene scene("SceneRendererFullViewportNestedSurfaceScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(2.0f); camera->SetViewportRect(XCEngine::Math::Rect(0.0f, 0.0f, 1.0f, 1.0f)); RenderSurface surface(1024, 768); surface.SetRenderArea(XCEngine::Math::RectInt(80, 120, 320, 240)); SceneRenderer renderer; const std::vector requests = renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), surface); ASSERT_EQ(requests.size(), 1u); const XCEngine::Math::RectInt renderArea = requests[0].surface.GetRenderArea(); EXPECT_EQ(renderArea.x, 80); EXPECT_EQ(renderArea.y, 120); EXPECT_EQ(renderArea.width, 320); EXPECT_EQ(renderArea.height, 240); } TEST(CameraRenderer_Test, UsesResolvedRenderAreaForCameraViewportDimensions) { Scene scene("CameraRendererViewportRectScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetViewportRect(XCEngine::Math::Rect(0.125f, 0.25f, 0.5f, 0.5f)); auto state = std::make_shared(); CameraRenderer renderer(std::make_unique(state)); CameraRenderRequest request; request.scene = &scene; request.camera = camera; request.context = CreateValidContext(); request.surface = RenderSurface(640, 480); request.surface.SetRenderArea(XCEngine::Math::RectInt(80, 120, 320, 240)); ASSERT_TRUE(renderer.Render(request)); EXPECT_EQ(state->lastRenderAreaX, 80); EXPECT_EQ(state->lastRenderAreaY, 120); EXPECT_EQ(state->lastRenderAreaWidth, 320); EXPECT_EQ(state->lastRenderAreaHeight, 240); EXPECT_EQ(state->lastCameraViewportWidth, 320u); EXPECT_EQ(state->lastCameraViewportHeight, 240u); } 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, CreatesPipelineInstancesFromPipelineAssetsAndShutsDownReplacedPipelines) { Scene scene("SceneRendererAssetScene"); GameObject* cameraObject = scene.CreateGameObject("Camera"); auto* camera = cameraObject->AddComponent(); camera->SetPrimary(true); camera->SetDepth(2.0f); auto initialAssetState = std::make_shared(); auto replacementAssetState = std::make_shared(); { SceneRenderer renderer(std::make_shared(initialAssetState)); ASSERT_NE(renderer.GetPipeline(), nullptr); ASSERT_NE(renderer.GetPipelineAsset(), nullptr); EXPECT_EQ(initialAssetState->createCalls, 1); const RenderSurface surface(800, 600); ASSERT_TRUE(renderer.Render(scene, nullptr, CreateValidContext(), surface)); ASSERT_NE(initialAssetState->lastCreatedPipelineState, nullptr); EXPECT_EQ(initialAssetState->lastCreatedPipelineState->renderCalls, 1); EXPECT_EQ(initialAssetState->lastCreatedPipelineState->lastCamera, camera); renderer.SetPipelineAsset(std::make_shared(replacementAssetState)); ASSERT_NE(initialAssetState->lastCreatedPipelineState, nullptr); EXPECT_EQ(initialAssetState->lastCreatedPipelineState->shutdownCalls, 1); EXPECT_EQ(replacementAssetState->createCalls, 1); ASSERT_TRUE(renderer.Render(scene, nullptr, CreateValidContext(), surface)); ASSERT_NE(replacementAssetState->lastCreatedPipelineState, nullptr); EXPECT_EQ(replacementAssetState->lastCreatedPipelineState->renderCalls, 1); EXPECT_EQ(replacementAssetState->lastCreatedPipelineState->lastCamera, camera); } ASSERT_NE(replacementAssetState->lastCreatedPipelineState, nullptr); EXPECT_EQ(replacementAssetState->lastCreatedPipelineState->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(); farRequest.cameraStackOrder = 1; farRequest.clearFlags = RenderClearFlags::None; CameraRenderRequest nearRequest = farRequest; nearRequest.camera = nearCamera; nearRequest.cameraDepth = nearCamera->GetDepth(); nearRequest.cameraStackOrder = 0; nearRequest.clearFlags = RenderClearFlags::Depth; const std::vector requests = { farRequest, nearRequest }; ASSERT_TRUE(renderer.Render(requests)); ASSERT_EQ(state->renderedCameras.size(), 2u); ASSERT_EQ(state->renderedClearFlags.size(), 2u); EXPECT_EQ(state->renderedCameras[0], nearCamera); EXPECT_EQ(state->renderedClearFlags[0], RenderClearFlags::Depth); EXPECT_EQ(state->renderedCameras[1], farCamera); EXPECT_EQ(state->renderedClearFlags[1], RenderClearFlags::None); }