Add CameraRenderer scene rendering boundary

This commit is contained in:
2026-03-27 16:22:59 +08:00
parent 9e66a81514
commit 26035e3940
7 changed files with 236 additions and 46 deletions

View File

@@ -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

View File

@@ -0,0 +1,38 @@
#pragma once
#include <XCEngine/Rendering/RenderPipeline.h>
#include <memory>
namespace XCEngine {
namespace Components {
class CameraComponent;
class Scene;
} // namespace Components
namespace Rendering {
class RenderSurface;
class CameraRenderer {
public:
CameraRenderer();
explicit CameraRenderer(std::unique_ptr<RenderPipeline> pipeline);
~CameraRenderer();
void SetPipeline(std::unique_ptr<RenderPipeline> 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<RenderPipeline> m_pipeline;
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -1,8 +1,6 @@
#pragma once
#include <XCEngine/Rendering/RenderPipeline.h>
#include <memory>
#include <XCEngine/Rendering/CameraRenderer.h>
namespace XCEngine {
namespace Components {
@@ -16,10 +14,10 @@ class SceneRenderer {
public:
SceneRenderer();
explicit SceneRenderer(std::unique_ptr<RenderPipeline> pipeline);
~SceneRenderer();
~SceneRenderer() = default;
void SetPipeline(std::unique_ptr<RenderPipeline> 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<RenderPipeline> m_pipeline;
CameraRenderer m_cameraRenderer;
};
} // namespace Rendering

View File

@@ -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<Pipelines::BuiltinForwardPipeline>()) {
}
CameraRenderer::CameraRenderer(std::unique_ptr<RenderPipeline> pipeline)
: m_pipeline(std::move(pipeline)) {
if (!m_pipeline) {
m_pipeline = std::make_unique<Pipelines::BuiltinForwardPipeline>();
}
}
CameraRenderer::~CameraRenderer() {
if (m_pipeline) {
m_pipeline->Shutdown();
}
}
void CameraRenderer::SetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
if (m_pipeline) {
m_pipeline->Shutdown();
}
m_pipeline = std::move(pipeline);
if (!m_pipeline) {
m_pipeline = std::make_unique<Pipelines::BuiltinForwardPipeline>();
}
}
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

View File

@@ -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<Pipelines::BuiltinForwardPipeline>()) {
}
SceneRenderer::SceneRenderer() = default;
SceneRenderer::SceneRenderer(std::unique_ptr<RenderPipeline> pipeline)
: m_pipeline(std::move(pipeline)) {
if (!m_pipeline) {
m_pipeline = std::make_unique<Pipelines::BuiltinForwardPipeline>();
}
}
SceneRenderer::~SceneRenderer() {
if (m_pipeline) {
m_pipeline->Shutdown();
}
: m_cameraRenderer(std::move(pipeline)) {
}
void SceneRenderer::SetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
if (m_pipeline) {
m_pipeline->Shutdown();
}
m_pipeline = std::move(pipeline);
if (!m_pipeline) {
m_pipeline = std::make_unique<Pipelines::BuiltinForwardPipeline>();
}
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

View File

@@ -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
)

View File

@@ -0,0 +1,127 @@
#include <gtest/gtest.h>
#include <XCEngine/Components/CameraComponent.h>
#include <XCEngine/Rendering/CameraRenderer.h>
#include <XCEngine/Rendering/RenderSurface.h>
#include <XCEngine/Rendering/SceneRenderer.h>
#include <XCEngine/Scene/Scene.h>
#include <memory>
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<MockPipelineState> 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<MockPipelineState> m_state;
};
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(CameraRenderer_Test, UsesOverrideCameraAndSurfaceSizeWhenSubmittingScene) {
Scene scene("CameraRendererScene");
GameObject* primaryCameraObject = scene.CreateGameObject("PrimaryCamera");
auto* primaryCamera = primaryCameraObject->AddComponent<CameraComponent>();
primaryCamera->SetPrimary(true);
primaryCamera->SetDepth(10.0f);
GameObject* overrideCameraObject = scene.CreateGameObject("OverrideCamera");
auto* overrideCamera = overrideCameraObject->AddComponent<CameraComponent>();
overrideCamera->SetPrimary(false);
overrideCamera->SetDepth(-1.0f);
auto state = std::make_shared<MockPipelineState>();
CameraRenderer renderer(std::make_unique<MockPipeline>(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<CameraComponent>();
camera->SetPrimary(true);
camera->SetDepth(2.0f);
auto initialState = std::make_shared<MockPipelineState>();
auto replacementState = std::make_shared<MockPipelineState>();
{
auto initialPipeline = std::make_unique<MockPipeline>(initialState);
MockPipeline* initialPipelineRaw = initialPipeline.get();
SceneRenderer renderer(std::move(initialPipeline));
EXPECT_EQ(renderer.GetPipeline(), initialPipelineRaw);
auto replacementPipeline = std::make_unique<MockPipeline>(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);
}