From 569bc144e75961e49b9321f2e4f497c9e3a91533 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Thu, 2 Apr 2026 01:06:40 +0800 Subject: [PATCH] refactor: split scene render request planning --- engine/CMakeLists.txt | 5 + .../Rendering/SceneRenderRequestPlanner.h | 29 ++++ .../XCEngine/Rendering/SceneRenderer.h | 3 +- .../Rendering/SceneRenderRequestPlanner.cpp | 69 ++++++++++ engine/src/Rendering/SceneRenderer.cpp | 44 +----- tests/Rendering/unit/CMakeLists.txt | 1 + .../test_scene_render_request_planner.cpp | 128 ++++++++++++++++++ 7 files changed, 235 insertions(+), 44 deletions(-) create mode 100644 engine/include/XCEngine/Rendering/SceneRenderRequestPlanner.h create mode 100644 engine/src/Rendering/SceneRenderRequestPlanner.cpp create mode 100644 tests/Rendering/unit/test_scene_render_request_planner.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 18f8ac2e..d833b6eb 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -272,6 +272,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Texture/Texture.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Texture/TextureLoader.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Texture/TextureImportSettings.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/BuiltinResources.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/Mesh.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/MeshLoader.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/MeshImportSettings.h @@ -284,6 +285,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Texture/Texture.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Texture/TextureLoader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Texture/TextureImportSettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/BuiltinResources.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/Mesh.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/MeshLoader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/MeshImportSettings.cpp @@ -344,6 +346,8 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/CameraRenderer.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/ObjectIdEncoding.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/ObjectIdPass.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/SceneRenderRequestPlanner.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/SceneRenderRequestUtils.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/SceneRenderer.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h @@ -358,6 +362,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSceneExtractor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSceneUtility.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderResourceCache.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/SceneRenderRequestPlanner.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/SceneRenderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp diff --git a/engine/include/XCEngine/Rendering/SceneRenderRequestPlanner.h b/engine/include/XCEngine/Rendering/SceneRenderRequestPlanner.h new file mode 100644 index 00000000..e48797f2 --- /dev/null +++ b/engine/include/XCEngine/Rendering/SceneRenderRequestPlanner.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include + +namespace XCEngine { +namespace Components { +class CameraComponent; +class Scene; +} // namespace Components + +namespace Rendering { + +class SceneRenderRequestPlanner { +public: + std::vector CollectCameras( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera) const; + + std::vector BuildRequests( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface) const; +}; + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/SceneRenderer.h b/engine/include/XCEngine/Rendering/SceneRenderer.h index ef816d6b..49c8646c 100644 --- a/engine/include/XCEngine/Rendering/SceneRenderer.h +++ b/engine/include/XCEngine/Rendering/SceneRenderer.h @@ -2,6 +2,7 @@ #include #include +#include #include @@ -42,7 +43,7 @@ public: const RenderSurface& surface); private: - RenderSceneExtractor m_sceneExtractor; + SceneRenderRequestPlanner m_requestPlanner; CameraRenderer m_cameraRenderer; }; diff --git a/engine/src/Rendering/SceneRenderRequestPlanner.cpp b/engine/src/Rendering/SceneRenderRequestPlanner.cpp new file mode 100644 index 00000000..2536b20e --- /dev/null +++ b/engine/src/Rendering/SceneRenderRequestPlanner.cpp @@ -0,0 +1,69 @@ +#include "Rendering/SceneRenderRequestPlanner.h" + +#include "Rendering/SceneRenderRequestUtils.h" + +#include "Scene/Scene.h" + +#include + +namespace XCEngine { +namespace Rendering { + +std::vector SceneRenderRequestPlanner::CollectCameras( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera) const { + std::vector cameras; + + if (SceneRenderRequestUtils::IsUsableCamera(overrideCamera)) { + cameras.push_back(overrideCamera); + return cameras; + } + + cameras = scene.FindObjectsOfType(); + cameras.erase( + std::remove_if( + cameras.begin(), + cameras.end(), + [](const Components::CameraComponent* camera) { + return !SceneRenderRequestUtils::IsUsableCamera(camera); + }), + cameras.end()); + + SceneRenderRequestUtils::SortSceneCamerasForRendering(cameras); + return cameras; +} + +std::vector SceneRenderRequestPlanner::BuildRequests( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface) const { + std::vector requests; + const std::vector cameras = + CollectCameras(scene, overrideCamera); + + size_t renderedBaseCameraCount = 0; + for (Components::CameraComponent* camera : cameras) { + CameraRenderRequest request; + if (!SceneRenderRequestUtils::BuildCameraRenderRequest( + scene, + *camera, + context, + surface, + renderedBaseCameraCount, + requests.size(), + request)) { + continue; + } + + requests.push_back(request); + if (camera->GetStackType() == Components::CameraStackType::Base) { + ++renderedBaseCameraCount; + } + } + + return requests; +} + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/SceneRenderer.cpp b/engine/src/Rendering/SceneRenderer.cpp index cd267a8f..4df60fc2 100644 --- a/engine/src/Rendering/SceneRenderer.cpp +++ b/engine/src/Rendering/SceneRenderer.cpp @@ -1,11 +1,6 @@ #include "Rendering/SceneRenderer.h" #include "Rendering/SceneRenderRequestUtils.h" -#include "Components/GameObject.h" -#include "Scene/Scene.h" - -#include - namespace XCEngine { namespace Rendering { @@ -32,44 +27,7 @@ std::vector SceneRenderer::BuildRenderRequests( Components::CameraComponent* overrideCamera, const RenderContext& context, const RenderSurface& surface) const { - std::vector requests; - - std::vector cameras; - if (SceneRenderRequestUtils::IsUsableCamera(overrideCamera)) { - cameras.push_back(overrideCamera); - } else { - cameras = scene.FindObjectsOfType(); - cameras.erase( - std::remove_if( - cameras.begin(), - cameras.end(), - [](const Components::CameraComponent* camera) { - return !SceneRenderRequestUtils::IsUsableCamera(camera); - }), - cameras.end()); - } - - SceneRenderRequestUtils::SortSceneCamerasForRendering(cameras); - - size_t renderedBaseCameraCount = 0; - for (Components::CameraComponent* camera : cameras) { - CameraRenderRequest request; - if (SceneRenderRequestUtils::BuildCameraRenderRequest( - scene, - *camera, - context, - surface, - renderedBaseCameraCount, - requests.size(), - request)) { - requests.push_back(request); - if (camera->GetStackType() == Components::CameraStackType::Base) { - ++renderedBaseCameraCount; - } - } - } - - return requests; + return m_requestPlanner.BuildRequests(scene, overrideCamera, context, surface); } bool SceneRenderer::Render(const CameraRenderRequest& request) { diff --git a/tests/Rendering/unit/CMakeLists.txt b/tests/Rendering/unit/CMakeLists.txt index 17f787a0..11396bd1 100644 --- a/tests/Rendering/unit/CMakeLists.txt +++ b/tests/Rendering/unit/CMakeLists.txt @@ -7,6 +7,7 @@ set(RENDERING_UNIT_TEST_SOURCES test_builtin_forward_pipeline.cpp test_builtin_scene_view_post_pass_plan.cpp test_camera_scene_renderer.cpp + test_scene_render_request_planner.cpp test_scene_render_request_utils.cpp test_render_scene_utility.cpp test_render_scene_extractor.cpp diff --git a/tests/Rendering/unit/test_scene_render_request_planner.cpp b/tests/Rendering/unit/test_scene_render_request_planner.cpp new file mode 100644 index 00000000..5ef78310 --- /dev/null +++ b/tests/Rendering/unit/test_scene_render_request_planner.cpp @@ -0,0 +1,128 @@ +#include + +#include +#include +#include + +using namespace XCEngine::Components; +using namespace XCEngine::Rendering; + +namespace { + +RenderContext CreateValidContext() { + RenderContext context; + context.device = reinterpret_cast(1); + context.commandList = reinterpret_cast(1); + context.commandQueue = reinterpret_cast(1); + return context; +} + +} // namespace + +TEST(SceneRenderRequestPlanner_Test, FallsBackToSceneCamerasWhenOverrideCameraIsNotUsable) { + Scene scene("SceneRenderRequestPlannerOverrideFallback"); + + GameObject* overrideObject = scene.CreateGameObject("Override"); + auto* overrideCamera = overrideObject->AddComponent(); + overrideCamera->SetDepth(-10.0f); + overrideCamera->SetEnabled(false); + + GameObject* sceneCameraObject = scene.CreateGameObject("SceneCamera"); + auto* sceneCamera = sceneCameraObject->AddComponent(); + sceneCamera->SetDepth(3.0f); + + SceneRenderRequestPlanner planner; + const std::vector cameras = planner.CollectCameras(scene, overrideCamera); + + ASSERT_EQ(cameras.size(), 1u); + EXPECT_EQ(cameras[0], sceneCamera); +} + +TEST(SceneRenderRequestPlanner_Test, UsesOverrideCameraExclusivelyWhenItIsUsable) { + Scene scene("SceneRenderRequestPlannerOverrideOnly"); + + GameObject* overrideObject = scene.CreateGameObject("Override"); + auto* overrideCamera = overrideObject->AddComponent(); + overrideCamera->SetDepth(9.0f); + + GameObject* sceneCameraObject = scene.CreateGameObject("SceneCamera"); + auto* sceneCamera = sceneCameraObject->AddComponent(); + sceneCamera->SetDepth(-5.0f); + + SceneRenderRequestPlanner planner; + const std::vector cameras = planner.CollectCameras(scene, overrideCamera); + + ASSERT_EQ(cameras.size(), 1u); + EXPECT_EQ(cameras[0], overrideCamera); +} + +TEST(SceneRenderRequestPlanner_Test, CollectsOnlyUsableSceneCamerasInStableRenderOrder) { + Scene scene("SceneRenderRequestPlannerCollectScene"); + + GameObject* inactiveObject = scene.CreateGameObject("Inactive"); + auto* inactiveCamera = inactiveObject->AddComponent(); + inactiveObject->SetActive(false); + + GameObject* highBaseObject = scene.CreateGameObject("HighBase"); + auto* highBase = highBaseObject->AddComponent(); + highBase->SetDepth(10.0f); + highBase->SetStackType(CameraStackType::Base); + + GameObject* firstTieObject = scene.CreateGameObject("FirstTie"); + auto* firstTie = firstTieObject->AddComponent(); + firstTie->SetDepth(2.0f); + firstTie->SetStackType(CameraStackType::Base); + + GameObject* secondTieObject = scene.CreateGameObject("SecondTie"); + auto* secondTie = secondTieObject->AddComponent(); + secondTie->SetDepth(2.0f); + secondTie->SetStackType(CameraStackType::Base); + + GameObject* overlayObject = scene.CreateGameObject("Overlay"); + auto* overlay = overlayObject->AddComponent(); + overlay->SetDepth(-10.0f); + overlay->SetStackType(CameraStackType::Overlay); + + SceneRenderRequestPlanner planner; + const std::vector cameras = planner.CollectCameras(scene, nullptr); + + (void)inactiveCamera; + ASSERT_EQ(cameras.size(), 4u); + EXPECT_EQ(cameras[0], firstTie); + EXPECT_EQ(cameras[1], secondTie); + EXPECT_EQ(cameras[2], highBase); + EXPECT_EQ(cameras[3], overlay); +} + +TEST(SceneRenderRequestPlanner_Test, BuildsRequestsAndDropsZeroSizedViewportsWithoutBreakingClearFlow) { + Scene scene("SceneRenderRequestPlannerBuildRequests"); + + GameObject* firstBaseObject = scene.CreateGameObject("FirstBase"); + auto* firstBase = firstBaseObject->AddComponent(); + firstBase->SetStackType(CameraStackType::Base); + firstBase->SetDepth(1.0f); + + GameObject* skippedBaseObject = scene.CreateGameObject("SkippedBase"); + auto* skippedBase = skippedBaseObject->AddComponent(); + skippedBase->SetStackType(CameraStackType::Base); + skippedBase->SetDepth(2.0f); + skippedBase->SetViewportRect(XCEngine::Math::Rect(0.5f, 0.5f, 0.0f, 1.0f)); + + GameObject* overlayObject = scene.CreateGameObject("Overlay"); + auto* overlay = overlayObject->AddComponent(); + overlay->SetStackType(CameraStackType::Overlay); + overlay->SetDepth(3.0f); + + SceneRenderRequestPlanner planner; + const std::vector requests = planner.BuildRequests( + scene, + nullptr, + CreateValidContext(), + RenderSurface(640, 360)); + + ASSERT_EQ(requests.size(), 2u); + EXPECT_EQ(requests[0].camera, firstBase); + EXPECT_EQ(requests[0].clearFlags, RenderClearFlags::All); + EXPECT_EQ(requests[1].camera, overlay); + EXPECT_EQ(requests[1].clearFlags, RenderClearFlags::Depth); +}