From 26035e3940f642d27b266b4c7bcf42ffa1c7d759 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Fri, 27 Mar 2026 16:22:59 +0800 Subject: [PATCH] Add CameraRenderer scene rendering boundary --- engine/CMakeLists.txt | 2 + .../XCEngine/Rendering/CameraRenderer.h | 38 ++++++ .../XCEngine/Rendering/SceneRenderer.h | 11 +- engine/src/Rendering/CameraRenderer.cpp | 60 +++++++++ engine/src/Rendering/SceneRenderer.cpp | 43 +----- tests/Rendering/unit/CMakeLists.txt | 1 + .../unit/test_camera_scene_renderer.cpp | 127 ++++++++++++++++++ 7 files changed, 236 insertions(+), 46 deletions(-) create mode 100644 engine/include/XCEngine/Rendering/CameraRenderer.h create mode 100644 engine/src/Rendering/CameraRenderer.cpp create mode 100644 tests/Rendering/unit/test_camera_scene_renderer.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index c905dba2..1e3cee57 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -330,8 +330,10 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPipelineAsset.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderSurface.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderResourceCache.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/CameraRenderer.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/SceneRenderer.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/CameraRenderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSurface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSceneExtractor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderResourceCache.cpp diff --git a/engine/include/XCEngine/Rendering/CameraRenderer.h b/engine/include/XCEngine/Rendering/CameraRenderer.h new file mode 100644 index 00000000..4604c6b9 --- /dev/null +++ b/engine/include/XCEngine/Rendering/CameraRenderer.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include + +namespace XCEngine { +namespace Components { +class CameraComponent; +class Scene; +} // namespace Components + +namespace Rendering { + +class RenderSurface; + +class CameraRenderer { +public: + CameraRenderer(); + explicit CameraRenderer(std::unique_ptr pipeline); + ~CameraRenderer(); + + void SetPipeline(std::unique_ptr pipeline); + RenderPipeline* GetPipeline() const { return m_pipeline.get(); } + + bool Render( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface); + +private: + RenderSceneExtractor m_sceneExtractor; + std::unique_ptr m_pipeline; +}; + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/SceneRenderer.h b/engine/include/XCEngine/Rendering/SceneRenderer.h index b6b35dc1..7b02089a 100644 --- a/engine/include/XCEngine/Rendering/SceneRenderer.h +++ b/engine/include/XCEngine/Rendering/SceneRenderer.h @@ -1,8 +1,6 @@ #pragma once -#include - -#include +#include namespace XCEngine { namespace Components { @@ -16,10 +14,10 @@ class SceneRenderer { public: SceneRenderer(); explicit SceneRenderer(std::unique_ptr pipeline); - ~SceneRenderer(); + ~SceneRenderer() = default; void SetPipeline(std::unique_ptr pipeline); - RenderPipeline* GetPipeline() const { return m_pipeline.get(); } + RenderPipeline* GetPipeline() const { return m_cameraRenderer.GetPipeline(); } bool Render( const Components::Scene& scene, @@ -28,8 +26,7 @@ public: const RenderSurface& surface); private: - RenderSceneExtractor m_sceneExtractor; - std::unique_ptr m_pipeline; + CameraRenderer m_cameraRenderer; }; } // namespace Rendering diff --git a/engine/src/Rendering/CameraRenderer.cpp b/engine/src/Rendering/CameraRenderer.cpp new file mode 100644 index 00000000..686a5f9a --- /dev/null +++ b/engine/src/Rendering/CameraRenderer.cpp @@ -0,0 +1,60 @@ +#include "Rendering/CameraRenderer.h" + +#include "Rendering/Pipelines/BuiltinForwardPipeline.h" +#include "Rendering/RenderSurface.h" +#include "Scene/Scene.h" + +namespace XCEngine { +namespace Rendering { + +CameraRenderer::CameraRenderer() + : m_pipeline(std::make_unique()) { +} + +CameraRenderer::CameraRenderer(std::unique_ptr pipeline) + : m_pipeline(std::move(pipeline)) { + if (!m_pipeline) { + m_pipeline = std::make_unique(); + } +} + +CameraRenderer::~CameraRenderer() { + if (m_pipeline) { + m_pipeline->Shutdown(); + } +} + +void CameraRenderer::SetPipeline(std::unique_ptr pipeline) { + if (m_pipeline) { + m_pipeline->Shutdown(); + } + + m_pipeline = std::move(pipeline); + if (!m_pipeline) { + m_pipeline = std::make_unique(); + } +} + +bool CameraRenderer::Render( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface) { + if (!context.IsValid() || m_pipeline == nullptr) { + return false; + } + + RenderSceneData sceneData = m_sceneExtractor.Extract( + scene, + overrideCamera, + surface.GetWidth(), + surface.GetHeight()); + if (!sceneData.HasCamera()) { + return false; + } + + return m_pipeline->Render(context, surface, sceneData); +} + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/SceneRenderer.cpp b/engine/src/Rendering/SceneRenderer.cpp index fc262e43..a3a61811 100644 --- a/engine/src/Rendering/SceneRenderer.cpp +++ b/engine/src/Rendering/SceneRenderer.cpp @@ -1,38 +1,16 @@ #include "Rendering/SceneRenderer.h" -#include "Rendering/Pipelines/BuiltinForwardPipeline.h" -#include "Rendering/RenderSurface.h" -#include "Scene/Scene.h" - namespace XCEngine { namespace Rendering { -SceneRenderer::SceneRenderer() - : m_pipeline(std::make_unique()) { -} +SceneRenderer::SceneRenderer() = default; SceneRenderer::SceneRenderer(std::unique_ptr pipeline) - : m_pipeline(std::move(pipeline)) { - if (!m_pipeline) { - m_pipeline = std::make_unique(); - } -} - -SceneRenderer::~SceneRenderer() { - if (m_pipeline) { - m_pipeline->Shutdown(); - } + : m_cameraRenderer(std::move(pipeline)) { } void SceneRenderer::SetPipeline(std::unique_ptr pipeline) { - if (m_pipeline) { - m_pipeline->Shutdown(); - } - - m_pipeline = std::move(pipeline); - if (!m_pipeline) { - m_pipeline = std::make_unique(); - } + m_cameraRenderer.SetPipeline(std::move(pipeline)); } bool SceneRenderer::Render( @@ -40,20 +18,7 @@ bool SceneRenderer::Render( Components::CameraComponent* overrideCamera, const RenderContext& context, const RenderSurface& surface) { - if (!context.IsValid() || m_pipeline == nullptr) { - return false; - } - - RenderSceneData sceneData = m_sceneExtractor.Extract( - scene, - overrideCamera, - surface.GetWidth(), - surface.GetHeight()); - if (!sceneData.HasCamera()) { - return false; - } - - return m_pipeline->Render(context, surface, sceneData); + return m_cameraRenderer.Render(scene, overrideCamera, context, surface); } } // namespace Rendering diff --git a/tests/Rendering/unit/CMakeLists.txt b/tests/Rendering/unit/CMakeLists.txt index 29fc5050..57a53d09 100644 --- a/tests/Rendering/unit/CMakeLists.txt +++ b/tests/Rendering/unit/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.15) project(XCEngine_RenderingUnitTests) set(RENDERING_UNIT_TEST_SOURCES + test_camera_scene_renderer.cpp test_render_scene_extractor.cpp ) diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp new file mode 100644 index 00000000..5c7b2513 --- /dev/null +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -0,0 +1,127 @@ +#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; +}; + +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(); + 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)); + + const RenderSurface surface(640, 480); + ASSERT_TRUE(renderer.Render(scene, overrideCamera, CreateValidContext(), surface)); + 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, 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); +}