refactor: split scene render request planning

This commit is contained in:
2026-04-02 01:06:40 +08:00
parent 410b1e59c3
commit 569bc144e7
7 changed files with 235 additions and 44 deletions

View File

@@ -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/Texture.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Texture/TextureLoader.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/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/Mesh.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/MeshLoader.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/MeshLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/MeshImportSettings.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/Texture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Texture/TextureLoader.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/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/Mesh.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/MeshLoader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/MeshLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/MeshImportSettings.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/CameraRenderer.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/ObjectIdEncoding.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/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/SceneRenderer.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.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/RenderSceneExtractor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSceneUtility.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSceneUtility.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderResourceCache.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/SceneRenderer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp

View File

@@ -0,0 +1,29 @@
#pragma once
#include <XCEngine/Rendering/CameraRenderRequest.h>
#include <vector>
namespace XCEngine {
namespace Components {
class CameraComponent;
class Scene;
} // namespace Components
namespace Rendering {
class SceneRenderRequestPlanner {
public:
std::vector<Components::CameraComponent*> CollectCameras(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera) const;
std::vector<CameraRenderRequest> BuildRequests(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera,
const RenderContext& context,
const RenderSurface& surface) const;
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -2,6 +2,7 @@
#include <XCEngine/Rendering/CameraRenderRequest.h> #include <XCEngine/Rendering/CameraRenderRequest.h>
#include <XCEngine/Rendering/CameraRenderer.h> #include <XCEngine/Rendering/CameraRenderer.h>
#include <XCEngine/Rendering/SceneRenderRequestPlanner.h>
#include <vector> #include <vector>
@@ -42,7 +43,7 @@ public:
const RenderSurface& surface); const RenderSurface& surface);
private: private:
RenderSceneExtractor m_sceneExtractor; SceneRenderRequestPlanner m_requestPlanner;
CameraRenderer m_cameraRenderer; CameraRenderer m_cameraRenderer;
}; };

View File

@@ -0,0 +1,69 @@
#include "Rendering/SceneRenderRequestPlanner.h"
#include "Rendering/SceneRenderRequestUtils.h"
#include "Scene/Scene.h"
#include <algorithm>
namespace XCEngine {
namespace Rendering {
std::vector<Components::CameraComponent*> SceneRenderRequestPlanner::CollectCameras(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera) const {
std::vector<Components::CameraComponent*> cameras;
if (SceneRenderRequestUtils::IsUsableCamera(overrideCamera)) {
cameras.push_back(overrideCamera);
return cameras;
}
cameras = scene.FindObjectsOfType<Components::CameraComponent>();
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<CameraRenderRequest> SceneRenderRequestPlanner::BuildRequests(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera,
const RenderContext& context,
const RenderSurface& surface) const {
std::vector<CameraRenderRequest> requests;
const std::vector<Components::CameraComponent*> 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

View File

@@ -1,11 +1,6 @@
#include "Rendering/SceneRenderer.h" #include "Rendering/SceneRenderer.h"
#include "Rendering/SceneRenderRequestUtils.h" #include "Rendering/SceneRenderRequestUtils.h"
#include "Components/GameObject.h"
#include "Scene/Scene.h"
#include <algorithm>
namespace XCEngine { namespace XCEngine {
namespace Rendering { namespace Rendering {
@@ -32,44 +27,7 @@ std::vector<CameraRenderRequest> SceneRenderer::BuildRenderRequests(
Components::CameraComponent* overrideCamera, Components::CameraComponent* overrideCamera,
const RenderContext& context, const RenderContext& context,
const RenderSurface& surface) const { const RenderSurface& surface) const {
std::vector<CameraRenderRequest> requests; return m_requestPlanner.BuildRequests(scene, overrideCamera, context, surface);
std::vector<Components::CameraComponent*> cameras;
if (SceneRenderRequestUtils::IsUsableCamera(overrideCamera)) {
cameras.push_back(overrideCamera);
} else {
cameras = scene.FindObjectsOfType<Components::CameraComponent>();
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;
} }
bool SceneRenderer::Render(const CameraRenderRequest& request) { bool SceneRenderer::Render(const CameraRenderRequest& request) {

View File

@@ -7,6 +7,7 @@ set(RENDERING_UNIT_TEST_SOURCES
test_builtin_forward_pipeline.cpp test_builtin_forward_pipeline.cpp
test_builtin_scene_view_post_pass_plan.cpp test_builtin_scene_view_post_pass_plan.cpp
test_camera_scene_renderer.cpp test_camera_scene_renderer.cpp
test_scene_render_request_planner.cpp
test_scene_render_request_utils.cpp test_scene_render_request_utils.cpp
test_render_scene_utility.cpp test_render_scene_utility.cpp
test_render_scene_extractor.cpp test_render_scene_extractor.cpp

View File

@@ -0,0 +1,128 @@
#include <gtest/gtest.h>
#include <XCEngine/Components/CameraComponent.h>
#include <XCEngine/Rendering/SceneRenderRequestPlanner.h>
#include <XCEngine/Scene/Scene.h>
using namespace XCEngine::Components;
using namespace XCEngine::Rendering;
namespace {
RenderContext CreateValidContext() {
RenderContext context;
context.device = reinterpret_cast<XCEngine::RHI::RHIDevice*>(1);
context.commandList = reinterpret_cast<XCEngine::RHI::RHICommandList*>(1);
context.commandQueue = reinterpret_cast<XCEngine::RHI::RHICommandQueue*>(1);
return context;
}
} // namespace
TEST(SceneRenderRequestPlanner_Test, FallsBackToSceneCamerasWhenOverrideCameraIsNotUsable) {
Scene scene("SceneRenderRequestPlannerOverrideFallback");
GameObject* overrideObject = scene.CreateGameObject("Override");
auto* overrideCamera = overrideObject->AddComponent<CameraComponent>();
overrideCamera->SetDepth(-10.0f);
overrideCamera->SetEnabled(false);
GameObject* sceneCameraObject = scene.CreateGameObject("SceneCamera");
auto* sceneCamera = sceneCameraObject->AddComponent<CameraComponent>();
sceneCamera->SetDepth(3.0f);
SceneRenderRequestPlanner planner;
const std::vector<CameraComponent*> 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<CameraComponent>();
overrideCamera->SetDepth(9.0f);
GameObject* sceneCameraObject = scene.CreateGameObject("SceneCamera");
auto* sceneCamera = sceneCameraObject->AddComponent<CameraComponent>();
sceneCamera->SetDepth(-5.0f);
SceneRenderRequestPlanner planner;
const std::vector<CameraComponent*> 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<CameraComponent>();
inactiveObject->SetActive(false);
GameObject* highBaseObject = scene.CreateGameObject("HighBase");
auto* highBase = highBaseObject->AddComponent<CameraComponent>();
highBase->SetDepth(10.0f);
highBase->SetStackType(CameraStackType::Base);
GameObject* firstTieObject = scene.CreateGameObject("FirstTie");
auto* firstTie = firstTieObject->AddComponent<CameraComponent>();
firstTie->SetDepth(2.0f);
firstTie->SetStackType(CameraStackType::Base);
GameObject* secondTieObject = scene.CreateGameObject("SecondTie");
auto* secondTie = secondTieObject->AddComponent<CameraComponent>();
secondTie->SetDepth(2.0f);
secondTie->SetStackType(CameraStackType::Base);
GameObject* overlayObject = scene.CreateGameObject("Overlay");
auto* overlay = overlayObject->AddComponent<CameraComponent>();
overlay->SetDepth(-10.0f);
overlay->SetStackType(CameraStackType::Overlay);
SceneRenderRequestPlanner planner;
const std::vector<CameraComponent*> 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<CameraComponent>();
firstBase->SetStackType(CameraStackType::Base);
firstBase->SetDepth(1.0f);
GameObject* skippedBaseObject = scene.CreateGameObject("SkippedBase");
auto* skippedBase = skippedBaseObject->AddComponent<CameraComponent>();
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<CameraComponent>();
overlay->SetStackType(CameraStackType::Overlay);
overlay->SetDepth(3.0f);
SceneRenderRequestPlanner planner;
const std::vector<CameraRenderRequest> 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);
}