Add CameraRenderer scene rendering boundary
This commit is contained in:
@@ -330,8 +330,10 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPipelineAsset.h
|
${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/RenderSurface.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderResourceCache.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/SceneRenderer.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.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/RenderSurface.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSceneExtractor.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSceneExtractor.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderResourceCache.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderResourceCache.cpp
|
||||||
|
|||||||
38
engine/include/XCEngine/Rendering/CameraRenderer.h
Normal file
38
engine/include/XCEngine/Rendering/CameraRenderer.h
Normal 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
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <XCEngine/Rendering/RenderPipeline.h>
|
#include <XCEngine/Rendering/CameraRenderer.h>
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Components {
|
namespace Components {
|
||||||
@@ -16,10 +14,10 @@ class SceneRenderer {
|
|||||||
public:
|
public:
|
||||||
SceneRenderer();
|
SceneRenderer();
|
||||||
explicit SceneRenderer(std::unique_ptr<RenderPipeline> pipeline);
|
explicit SceneRenderer(std::unique_ptr<RenderPipeline> pipeline);
|
||||||
~SceneRenderer();
|
~SceneRenderer() = default;
|
||||||
|
|
||||||
void SetPipeline(std::unique_ptr<RenderPipeline> pipeline);
|
void SetPipeline(std::unique_ptr<RenderPipeline> pipeline);
|
||||||
RenderPipeline* GetPipeline() const { return m_pipeline.get(); }
|
RenderPipeline* GetPipeline() const { return m_cameraRenderer.GetPipeline(); }
|
||||||
|
|
||||||
bool Render(
|
bool Render(
|
||||||
const Components::Scene& scene,
|
const Components::Scene& scene,
|
||||||
@@ -28,8 +26,7 @@ public:
|
|||||||
const RenderSurface& surface);
|
const RenderSurface& surface);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RenderSceneExtractor m_sceneExtractor;
|
CameraRenderer m_cameraRenderer;
|
||||||
std::unique_ptr<RenderPipeline> m_pipeline;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Rendering
|
} // namespace Rendering
|
||||||
|
|||||||
60
engine/src/Rendering/CameraRenderer.cpp
Normal file
60
engine/src/Rendering/CameraRenderer.cpp
Normal 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
|
||||||
@@ -1,38 +1,16 @@
|
|||||||
#include "Rendering/SceneRenderer.h"
|
#include "Rendering/SceneRenderer.h"
|
||||||
|
|
||||||
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
|
|
||||||
#include "Rendering/RenderSurface.h"
|
|
||||||
#include "Scene/Scene.h"
|
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Rendering {
|
namespace Rendering {
|
||||||
|
|
||||||
SceneRenderer::SceneRenderer()
|
SceneRenderer::SceneRenderer() = default;
|
||||||
: m_pipeline(std::make_unique<Pipelines::BuiltinForwardPipeline>()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SceneRenderer::SceneRenderer(std::unique_ptr<RenderPipeline> pipeline)
|
SceneRenderer::SceneRenderer(std::unique_ptr<RenderPipeline> pipeline)
|
||||||
: m_pipeline(std::move(pipeline)) {
|
: m_cameraRenderer(std::move(pipeline)) {
|
||||||
if (!m_pipeline) {
|
|
||||||
m_pipeline = std::make_unique<Pipelines::BuiltinForwardPipeline>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SceneRenderer::~SceneRenderer() {
|
|
||||||
if (m_pipeline) {
|
|
||||||
m_pipeline->Shutdown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneRenderer::SetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
|
void SceneRenderer::SetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
|
||||||
if (m_pipeline) {
|
m_cameraRenderer.SetPipeline(std::move(pipeline));
|
||||||
m_pipeline->Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_pipeline = std::move(pipeline);
|
|
||||||
if (!m_pipeline) {
|
|
||||||
m_pipeline = std::make_unique<Pipelines::BuiltinForwardPipeline>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneRenderer::Render(
|
bool SceneRenderer::Render(
|
||||||
@@ -40,20 +18,7 @@ bool SceneRenderer::Render(
|
|||||||
Components::CameraComponent* overrideCamera,
|
Components::CameraComponent* overrideCamera,
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
const RenderSurface& surface) {
|
const RenderSurface& surface) {
|
||||||
if (!context.IsValid() || m_pipeline == nullptr) {
|
return m_cameraRenderer.Render(scene, overrideCamera, context, surface);
|
||||||
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 Rendering
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.15)
|
|||||||
project(XCEngine_RenderingUnitTests)
|
project(XCEngine_RenderingUnitTests)
|
||||||
|
|
||||||
set(RENDERING_UNIT_TEST_SOURCES
|
set(RENDERING_UNIT_TEST_SOURCES
|
||||||
|
test_camera_scene_renderer.cpp
|
||||||
test_render_scene_extractor.cpp
|
test_render_scene_extractor.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
127
tests/Rendering/unit/test_camera_scene_renderer.cpp
Normal file
127
tests/Rendering/unit/test_camera_scene_renderer.cpp
Normal 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user