Formalize chained fullscreen post-process execution

This commit is contained in:
2026-04-06 13:58:17 +08:00
parent 2b70a2e309
commit 6a1ed4be68
10 changed files with 635 additions and 32 deletions

View File

@@ -461,6 +461,7 @@ add_library(XCEngine STATIC
${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/Execution/CameraRenderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/CameraRenderer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/DirectionalShadowSurfaceCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/DirectionalShadowSurfaceCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/FullscreenPassSurfaceCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthOnlyPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthOnlyPass.cpp

View File

@@ -20,6 +20,7 @@ class RHIResourceView;
namespace Rendering { namespace Rendering {
class DirectionalShadowSurfaceCache; class DirectionalShadowSurfaceCache;
class FullscreenPassSurfaceCache;
class RenderSurface; class RenderSurface;
class RenderPipelineAsset; class RenderPipelineAsset;
struct RenderContext; struct RenderContext;
@@ -71,6 +72,8 @@ private:
std::unique_ptr<RenderPass> m_depthOnlyPass; std::unique_ptr<RenderPass> m_depthOnlyPass;
std::unique_ptr<RenderPass> m_shadowCasterPass; std::unique_ptr<RenderPass> m_shadowCasterPass;
std::unique_ptr<DirectionalShadowSurfaceCache> m_directionalShadowSurface; std::unique_ptr<DirectionalShadowSurfaceCache> m_directionalShadowSurface;
std::unique_ptr<FullscreenPassSurfaceCache> m_postProcessSurfaceCache;
std::unique_ptr<FullscreenPassSurfaceCache> m_finalOutputSurfaceCache;
}; };
} // namespace Rendering } // namespace Rendering

View File

@@ -14,6 +14,10 @@ class CameraComponent;
class Scene; class Scene;
} // namespace Components } // namespace Components
namespace RHI {
class RHIResourceView;
} // namespace RHI
namespace Rendering { namespace Rendering {
enum class CameraFrameStage : uint8_t { enum class CameraFrameStage : uint8_t {
@@ -143,16 +147,26 @@ struct ObjectIdRenderRequest {
struct FullscreenPassRenderRequest { struct FullscreenPassRenderRequest {
RenderSurface sourceSurface; RenderSurface sourceSurface;
RHI::RHIResourceView* sourceColorView = nullptr;
RenderSurface destinationSurface; RenderSurface destinationSurface;
RenderPassSequence* passes = nullptr; RenderPassSequence* passes = nullptr;
size_t GetPassCount() const {
return passes != nullptr ? passes->GetPassCount() : 0u;
}
bool IsRequested() const { bool IsRequested() const {
return passes != nullptr; return passes != nullptr;
} }
bool RequiresIntermediateSurface() const {
return GetPassCount() > 1u;
}
bool IsValid() const { bool IsValid() const {
return passes != nullptr && return passes != nullptr &&
HasValidColorTarget(sourceSurface) && HasValidColorTarget(sourceSurface) &&
sourceColorView != nullptr &&
HasValidColorTarget(destinationSurface); HasValidColorTarget(destinationSurface);
} }
}; };
@@ -295,6 +309,17 @@ struct CameraRenderRequest {
} }
} }
RHI::RHIResourceView* GetSourceColorView(CameraFrameStage stage) const {
switch (stage) {
case CameraFrameStage::PostProcess:
return postProcess.IsRequested() ? postProcess.sourceColorView : nullptr;
case CameraFrameStage::FinalOutput:
return finalOutput.IsRequested() ? finalOutput.sourceColorView : nullptr;
default:
return nullptr;
}
}
bool RequiresIntermediateSceneColor() const { bool RequiresIntermediateSceneColor() const {
return postProcess.IsRequested() || finalOutput.IsRequested(); return postProcess.IsRequested() || finalOutput.IsRequested();
} }

View File

@@ -7,6 +7,10 @@
#include <vector> #include <vector>
namespace XCEngine { namespace XCEngine {
namespace RHI {
class RHIResourceView;
} // namespace RHI
namespace Rendering { namespace Rendering {
struct RenderSceneData; struct RenderSceneData;
@@ -17,6 +21,7 @@ struct RenderPassContext {
const RenderSurface& surface; const RenderSurface& surface;
const RenderSceneData& sceneData; const RenderSceneData& sceneData;
const RenderSurface* sourceSurface = nullptr; const RenderSurface* sourceSurface = nullptr;
RHI::RHIResourceView* sourceColorView = nullptr;
}; };
class RenderPass { class RenderPass {
@@ -47,6 +52,10 @@ public:
return m_passes.size(); return m_passes.size();
} }
bool Empty() const {
return m_passes.empty();
}
bool Initialize(const RenderContext& context) { bool Initialize(const RenderContext& context) {
for (const std::unique_ptr<RenderPass>& pass : m_passes) { for (const std::unique_ptr<RenderPass>& pass : m_passes) {
if (pass == nullptr) { if (pass == nullptr) {
@@ -83,6 +92,14 @@ public:
return true; return true;
} }
bool ExecutePass(size_t index, const RenderPassContext& context) {
if (index >= m_passes.size() || m_passes[index] == nullptr) {
return false;
}
return m_passes[index]->Execute(context);
}
private: private:
std::vector<std::unique_ptr<RenderPass>> m_passes; std::vector<std::unique_ptr<RenderPass>> m_passes;
}; };

View File

@@ -0,0 +1,156 @@
#include "Rendering/Caches/FullscreenPassSurfaceCache.h"
#include "Rendering/RenderContext.h"
#include "RHI/RHIDevice.h"
#include "RHI/RHIResourceView.h"
#include "RHI/RHITexture.h"
namespace XCEngine {
namespace Rendering {
namespace {
void DestroySurfaceEntry(FullscreenPassSurfaceCache::SurfaceEntry& entry) {
if (entry.renderTargetView != nullptr) {
entry.renderTargetView->Shutdown();
delete entry.renderTargetView;
entry.renderTargetView = nullptr;
}
if (entry.shaderResourceView != nullptr) {
entry.shaderResourceView->Shutdown();
delete entry.shaderResourceView;
entry.shaderResourceView = nullptr;
}
if (entry.texture != nullptr) {
entry.texture->Shutdown();
delete entry.texture;
entry.texture = nullptr;
}
entry.surface = RenderSurface();
entry.currentColorState = RHI::ResourceStates::Common;
}
} // namespace
FullscreenPassSurfaceCache::~FullscreenPassSurfaceCache() {
Reset();
}
bool FullscreenPassSurfaceCache::EnsureSurfaces(
const RenderContext& context,
uint32_t width,
uint32_t height,
RHI::Format format,
size_t surfaceCount) {
if (!context.IsValid() ||
width == 0 ||
height == 0 ||
format == RHI::Format::Unknown ||
surfaceCount == 0) {
return false;
}
if (Matches(context, width, height, format, surfaceCount)) {
return true;
}
std::vector<SurfaceEntry> newEntries(surfaceCount);
for (size_t index = 0; index < surfaceCount; ++index) {
SurfaceEntry& entry = newEntries[index];
RHI::TextureDesc textureDesc = {};
textureDesc.width = width;
textureDesc.height = height;
textureDesc.depth = 1;
textureDesc.mipLevels = 1;
textureDesc.arraySize = 1;
textureDesc.format = static_cast<uint32_t>(format);
textureDesc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D);
textureDesc.sampleCount = 1;
textureDesc.sampleQuality = 0;
textureDesc.flags = 0;
entry.texture = context.device->CreateTexture(textureDesc);
if (entry.texture == nullptr) {
for (SurfaceEntry& createdEntry : newEntries) {
DestroySurfaceEntry(createdEntry);
}
return false;
}
RHI::ResourceViewDesc viewDesc = {};
viewDesc.format = static_cast<uint32_t>(format);
viewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
viewDesc.mipLevel = 0;
entry.renderTargetView = context.device->CreateRenderTargetView(entry.texture, viewDesc);
if (entry.renderTargetView == nullptr) {
for (SurfaceEntry& createdEntry : newEntries) {
DestroySurfaceEntry(createdEntry);
}
return false;
}
entry.shaderResourceView = context.device->CreateShaderResourceView(entry.texture, viewDesc);
if (entry.shaderResourceView == nullptr) {
for (SurfaceEntry& createdEntry : newEntries) {
DestroySurfaceEntry(createdEntry);
}
return false;
}
entry.surface = RenderSurface(width, height);
entry.surface.SetColorAttachment(entry.renderTargetView);
entry.surface.SetAutoTransitionEnabled(true);
entry.surface.SetColorStateBefore(RHI::ResourceStates::Common);
entry.surface.SetColorStateAfter(RHI::ResourceStates::PixelShaderResource);
entry.currentColorState = RHI::ResourceStates::Common;
}
Reset();
m_device = context.device;
m_width = width;
m_height = height;
m_format = format;
m_entries = std::move(newEntries);
return true;
}
FullscreenPassSurfaceCache::SurfaceEntry* FullscreenPassSurfaceCache::GetSurfaceEntry(size_t index) {
return index < m_entries.size() ? &m_entries[index] : nullptr;
}
const FullscreenPassSurfaceCache::SurfaceEntry* FullscreenPassSurfaceCache::GetSurfaceEntry(size_t index) const {
return index < m_entries.size() ? &m_entries[index] : nullptr;
}
bool FullscreenPassSurfaceCache::Matches(
const RenderContext& context,
uint32_t width,
uint32_t height,
RHI::Format format,
size_t surfaceCount) const {
return m_device == context.device &&
m_width == width &&
m_height == height &&
m_format == format &&
m_entries.size() == surfaceCount;
}
void FullscreenPassSurfaceCache::Reset() {
for (SurfaceEntry& entry : m_entries) {
DestroySurfaceEntry(entry);
}
m_entries.clear();
m_device = nullptr;
m_width = 0;
m_height = 0;
m_format = RHI::Format::Unknown;
}
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,63 @@
#pragma once
#include "Rendering/RenderSurface.h"
#include "RHI/RHIEnums.h"
#include <cstddef>
#include <vector>
namespace XCEngine {
namespace RHI {
class RHIDevice;
class RHIResourceView;
class RHITexture;
} // namespace RHI
namespace Rendering {
struct RenderContext;
class FullscreenPassSurfaceCache {
public:
struct SurfaceEntry {
RenderSurface surface = {};
RHI::RHITexture* texture = nullptr;
RHI::RHIResourceView* renderTargetView = nullptr;
RHI::RHIResourceView* shaderResourceView = nullptr;
RHI::ResourceStates currentColorState = RHI::ResourceStates::Common;
};
FullscreenPassSurfaceCache() = default;
FullscreenPassSurfaceCache(const FullscreenPassSurfaceCache&) = delete;
FullscreenPassSurfaceCache& operator=(const FullscreenPassSurfaceCache&) = delete;
~FullscreenPassSurfaceCache();
bool EnsureSurfaces(
const RenderContext& context,
uint32_t width,
uint32_t height,
RHI::Format format,
size_t surfaceCount);
size_t GetSurfaceCount() const { return m_entries.size(); }
SurfaceEntry* GetSurfaceEntry(size_t index);
const SurfaceEntry* GetSurfaceEntry(size_t index) const;
private:
bool Matches(
const RenderContext& context,
uint32_t width,
uint32_t height,
RHI::Format format,
size_t surfaceCount) const;
void Reset();
RHI::RHIDevice* m_device = nullptr;
uint32_t m_width = 0;
uint32_t m_height = 0;
RHI::Format m_format = RHI::Format::Unknown;
std::vector<SurfaceEntry> m_entries;
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -2,6 +2,7 @@
#include "Components/CameraComponent.h" #include "Components/CameraComponent.h"
#include "Rendering/Caches/DirectionalShadowSurfaceCache.h" #include "Rendering/Caches/DirectionalShadowSurfaceCache.h"
#include "Rendering/Caches/FullscreenPassSurfaceCache.h"
#include "Rendering/Passes/BuiltinDepthOnlyPass.h" #include "Rendering/Passes/BuiltinDepthOnlyPass.h"
#include "Rendering/Passes/BuiltinObjectIdPass.h" #include "Rendering/Passes/BuiltinObjectIdPass.h"
#include "Rendering/Passes/BuiltinShadowCasterPass.h" #include "Rendering/Passes/BuiltinShadowCasterPass.h"
@@ -11,6 +12,8 @@
#include "RHI/RHIResourceView.h" #include "RHI/RHIResourceView.h"
#include "Scene/Scene.h" #include "Scene/Scene.h"
#include <algorithm>
namespace XCEngine { namespace XCEngine {
namespace Rendering { namespace Rendering {
@@ -135,9 +138,10 @@ public:
ScopedInitializedPassSequence& operator=(const ScopedInitializedPassSequence&) = delete; ScopedInitializedPassSequence& operator=(const ScopedInitializedPassSequence&) = delete;
~ScopedInitializedPassSequence() { ~ScopedInitializedPassSequence() {
if (m_sequence != nullptr && m_initialized) { // Request-owned pass sequences may record GPU work that executes only after the
m_sequence->Shutdown(); // caller closes/submits the command list. Do not destroy sequence-owned GPU
} // resources here; the sequence owner must shut them down explicitly when the
// sequence is no longer needed.
} }
bool IsReady() const { bool IsReady() const {
@@ -159,6 +163,8 @@ struct CameraFrameExecutionState {
RenderPass* objectIdPass = nullptr; RenderPass* objectIdPass = nullptr;
RenderPass* depthOnlyPass = nullptr; RenderPass* depthOnlyPass = nullptr;
RenderPass* shadowCasterPass = nullptr; RenderPass* shadowCasterPass = nullptr;
FullscreenPassSurfaceCache* postProcessSurfaceCache = nullptr;
FullscreenPassSurfaceCache* finalOutputSurfaceCache = nullptr;
std::unique_ptr<ScopedInitializedPassSequence> preScenePasses; std::unique_ptr<ScopedInitializedPassSequence> preScenePasses;
std::unique_ptr<ScopedInitializedPassSequence> postProcessPasses; std::unique_ptr<ScopedInitializedPassSequence> postProcessPasses;
std::unique_ptr<ScopedInitializedPassSequence> finalOutputPasses; std::unique_ptr<ScopedInitializedPassSequence> finalOutputPasses;
@@ -175,6 +181,94 @@ bool ExecutePassSequenceStage(
return activeSequence->IsReady() && activeSequence->Execute(passContext); return activeSequence->IsReady() && activeSequence->Execute(passContext);
} }
void CopyIntermediateSurfaceLayout(
const RenderSurface& templateSurface,
RenderSurface& destinationSurface) {
destinationSurface.SetSize(templateSurface.GetWidth(), templateSurface.GetHeight());
if (templateSurface.HasCustomRenderArea()) {
destinationSurface.SetRenderArea(templateSurface.GetRenderArea());
} else {
destinationSurface.ResetRenderArea();
}
destinationSurface.ClearClearColorOverride();
destinationSurface.SetAutoTransitionEnabled(true);
}
bool ExecuteFullscreenPassSequenceStage(
std::unique_ptr<ScopedInitializedPassSequence>& activeSequence,
RenderPassSequence* sequence,
const RenderContext& context,
const RenderPassContext& passContext,
FullscreenPassSurfaceCache* surfaceCache) {
activeSequence = std::make_unique<ScopedInitializedPassSequence>(sequence, context);
if (!activeSequence->IsReady()) {
return false;
}
if (sequence == nullptr || sequence->GetPassCount() <= 1u) {
return activeSequence->Execute(passContext);
}
if (surfaceCache == nullptr ||
passContext.sourceSurface == nullptr ||
passContext.sourceColorView == nullptr ||
!HasValidColorTarget(passContext.surface)) {
return false;
}
const std::vector<RHI::RHIResourceView*>& colorAttachments = passContext.surface.GetColorAttachments();
const RHI::Format outputFormat = colorAttachments[0]->GetFormat();
const size_t intermediateSurfaceCount = std::min<size_t>(2u, sequence->GetPassCount() - 1u);
if (!surfaceCache->EnsureSurfaces(
context,
passContext.surface.GetWidth(),
passContext.surface.GetHeight(),
outputFormat,
intermediateSurfaceCount)) {
return false;
}
const RenderSurface* currentSourceSurface = passContext.sourceSurface;
RHI::RHIResourceView* currentSourceColorView = passContext.sourceColorView;
for (size_t passIndex = 0; passIndex < sequence->GetPassCount(); ++passIndex) {
const bool isLastPass = (passIndex + 1u) == sequence->GetPassCount();
const RenderSurface* outputSurface = &passContext.surface;
FullscreenPassSurfaceCache::SurfaceEntry* intermediateEntry = nullptr;
if (!isLastPass) {
intermediateEntry = surfaceCache->GetSurfaceEntry(passIndex % intermediateSurfaceCount);
if (intermediateEntry == nullptr) {
return false;
}
CopyIntermediateSurfaceLayout(passContext.surface, intermediateEntry->surface);
intermediateEntry->surface.SetColorStateBefore(intermediateEntry->currentColorState);
intermediateEntry->surface.SetColorStateAfter(RHI::ResourceStates::PixelShaderResource);
outputSurface = &intermediateEntry->surface;
}
const RenderPassContext chainedContext = {
context,
*outputSurface,
passContext.sceneData,
currentSourceSurface,
currentSourceColorView
};
if (!sequence->ExecutePass(passIndex, chainedContext)) {
return false;
}
if (intermediateEntry != nullptr) {
intermediateEntry->currentColorState = RHI::ResourceStates::PixelShaderResource;
currentSourceSurface = &intermediateEntry->surface;
currentSourceColorView = intermediateEntry->shaderResourceView;
}
}
return true;
}
RenderPassContext BuildFrameStagePassContext( RenderPassContext BuildFrameStagePassContext(
CameraFrameStage stage, CameraFrameStage stage,
const CameraRenderRequest& request, const CameraRenderRequest& request,
@@ -184,7 +278,8 @@ RenderPassContext BuildFrameStagePassContext(
request.context, request.context,
outputSurface != nullptr ? *outputSurface : request.surface, outputSurface != nullptr ? *outputSurface : request.surface,
sceneData, sceneData,
request.GetSourceSurface(stage) request.GetSourceSurface(stage),
request.GetSourceColorView(stage)
}; };
} }
@@ -219,17 +314,19 @@ bool ExecuteFrameStage(
return executionState.pipeline != nullptr && return executionState.pipeline != nullptr &&
executionState.pipeline->Render(request.context, passContext.surface, sceneData); executionState.pipeline->Render(request.context, passContext.surface, sceneData);
case CameraFrameStage::PostProcess: case CameraFrameStage::PostProcess:
return ExecutePassSequenceStage( return ExecuteFullscreenPassSequenceStage(
executionState.postProcessPasses, executionState.postProcessPasses,
request.GetPassSequence(stage), request.GetPassSequence(stage),
request.context, request.context,
passContext); passContext,
executionState.postProcessSurfaceCache);
case CameraFrameStage::FinalOutput: case CameraFrameStage::FinalOutput:
return ExecutePassSequenceStage( return ExecuteFullscreenPassSequenceStage(
executionState.finalOutputPasses, executionState.finalOutputPasses,
request.GetPassSequence(stage), request.GetPassSequence(stage),
request.context, request.context,
passContext); passContext,
executionState.finalOutputSurfaceCache);
case CameraFrameStage::ObjectId: case CameraFrameStage::ObjectId:
return !request.objectId.IsRequested() || return !request.objectId.IsRequested() ||
ExecuteStandalonePass( ExecuteStandalonePass(
@@ -319,7 +416,9 @@ CameraRenderer::CameraRenderer(
: m_pipelineAsset(nullptr) : m_pipelineAsset(nullptr)
, m_objectIdPass(std::move(objectIdPass)) , m_objectIdPass(std::move(objectIdPass))
, m_depthOnlyPass(std::move(depthOnlyPass)) , m_depthOnlyPass(std::move(depthOnlyPass))
, m_shadowCasterPass(std::move(shadowCasterPass)) { , m_shadowCasterPass(std::move(shadowCasterPass))
, m_postProcessSurfaceCache(std::make_unique<FullscreenPassSurfaceCache>())
, m_finalOutputSurfaceCache(std::make_unique<FullscreenPassSurfaceCache>()) {
if (m_objectIdPass == nullptr) { if (m_objectIdPass == nullptr) {
m_objectIdPass = std::make_unique<Passes::BuiltinObjectIdPass>(); m_objectIdPass = std::make_unique<Passes::BuiltinObjectIdPass>();
} }
@@ -336,7 +435,9 @@ CameraRenderer::CameraRenderer(std::shared_ptr<const RenderPipelineAsset> pipeli
: m_pipelineAsset(std::move(pipelineAsset)) : m_pipelineAsset(std::move(pipelineAsset))
, m_objectIdPass(std::make_unique<Passes::BuiltinObjectIdPass>()) , m_objectIdPass(std::make_unique<Passes::BuiltinObjectIdPass>())
, m_depthOnlyPass(CreateDefaultDepthOnlyPass()) , m_depthOnlyPass(CreateDefaultDepthOnlyPass())
, m_shadowCasterPass(CreateDefaultShadowCasterPass()) { , m_shadowCasterPass(CreateDefaultShadowCasterPass())
, m_postProcessSurfaceCache(std::make_unique<FullscreenPassSurfaceCache>())
, m_finalOutputSurfaceCache(std::make_unique<FullscreenPassSurfaceCache>()) {
SetPipelineAsset(m_pipelineAsset); SetPipelineAsset(m_pipelineAsset);
} }
@@ -481,6 +582,8 @@ bool CameraRenderer::ExecuteRenderPlan(
executionState.objectIdPass = m_objectIdPass.get(); executionState.objectIdPass = m_objectIdPass.get();
executionState.depthOnlyPass = m_depthOnlyPass.get(); executionState.depthOnlyPass = m_depthOnlyPass.get();
executionState.shadowCasterPass = m_shadowCasterPass.get(); executionState.shadowCasterPass = m_shadowCasterPass.get();
executionState.postProcessSurfaceCache = m_postProcessSurfaceCache.get();
executionState.finalOutputSurfaceCache = m_finalOutputSurfaceCache.get();
for (const CameraFrameStageInfo& stageInfo : kOrderedCameraFrameStages) { for (const CameraFrameStageInfo& stageInfo : kOrderedCameraFrameStages) {
if (!request.HasFrameStage(stageInfo.stage) && if (!request.HasFrameStage(stageInfo.stage) &&

File diff suppressed because one or more lines are too long

View File

@@ -167,7 +167,9 @@ void PostProcessSceneTest::SetUp() {
mTexture = CreateCheckerTexture(); mTexture = CreateCheckerTexture();
mMaterial = CreateQuadMaterial(mTexture); mMaterial = CreateQuadMaterial(mTexture);
mPostProcessPasses.AddPass(std::make_unique<Passes::BuiltinColorScalePostProcessPass>( mPostProcessPasses.AddPass(std::make_unique<Passes::BuiltinColorScalePostProcessPass>(
Vector4(0.55f, 0.72f, 1.0f, 1.0f))); Vector4(1.0f, 0.75f, 0.75f, 1.0f)));
mPostProcessPasses.AddPass(std::make_unique<Passes::BuiltinColorScalePostProcessPass>(
Vector4(0.55f, 0.95f, 1.0f, 1.0f)));
BuildScene(); BuildScene();

View File

@@ -57,6 +57,9 @@ struct MockShadowAllocationState {
int createTextureCalls = 0; int createTextureCalls = 0;
int shutdownTextureCalls = 0; int shutdownTextureCalls = 0;
int destroyTextureCalls = 0; int destroyTextureCalls = 0;
int createRenderTargetViewCalls = 0;
int shutdownRenderTargetViewCalls = 0;
int destroyRenderTargetViewCalls = 0;
int createDepthViewCalls = 0; int createDepthViewCalls = 0;
int shutdownDepthViewCalls = 0; int shutdownDepthViewCalls = 0;
int destroyDepthViewCalls = 0; int destroyDepthViewCalls = 0;
@@ -66,6 +69,7 @@ struct MockShadowAllocationState {
uint32_t lastTextureWidth = 0; uint32_t lastTextureWidth = 0;
uint32_t lastTextureHeight = 0; uint32_t lastTextureHeight = 0;
XCEngine::RHI::Format lastTextureFormat = XCEngine::RHI::Format::Unknown; XCEngine::RHI::Format lastTextureFormat = XCEngine::RHI::Format::Unknown;
XCEngine::RHI::Format lastRenderTargetViewFormat = XCEngine::RHI::Format::Unknown;
XCEngine::RHI::Format lastDepthViewFormat = XCEngine::RHI::Format::Unknown; XCEngine::RHI::Format lastDepthViewFormat = XCEngine::RHI::Format::Unknown;
XCEngine::RHI::Format lastShaderViewFormat = XCEngine::RHI::Format::Unknown; XCEngine::RHI::Format lastShaderViewFormat = XCEngine::RHI::Format::Unknown;
}; };
@@ -128,6 +132,8 @@ public:
~MockShadowView() override { ~MockShadowView() override {
if (m_viewType == XCEngine::RHI::ResourceViewType::ShaderResource) { if (m_viewType == XCEngine::RHI::ResourceViewType::ShaderResource) {
++m_state->destroyShaderViewCalls; ++m_state->destroyShaderViewCalls;
} else if (m_viewType == XCEngine::RHI::ResourceViewType::RenderTarget) {
++m_state->destroyRenderTargetViewCalls;
} else { } else {
++m_state->destroyDepthViewCalls; ++m_state->destroyDepthViewCalls;
} }
@@ -136,6 +142,8 @@ public:
void Shutdown() override { void Shutdown() override {
if (m_viewType == XCEngine::RHI::ResourceViewType::ShaderResource) { if (m_viewType == XCEngine::RHI::ResourceViewType::ShaderResource) {
++m_state->shutdownShaderViewCalls; ++m_state->shutdownShaderViewCalls;
} else if (m_viewType == XCEngine::RHI::ResourceViewType::RenderTarget) {
++m_state->shutdownRenderTargetViewCalls;
} else { } else {
++m_state->shutdownDepthViewCalls; ++m_state->shutdownDepthViewCalls;
} }
@@ -221,7 +229,15 @@ public:
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; } const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
XCEngine::RHI::RHIResourceView* CreateRenderTargetView( XCEngine::RHI::RHIResourceView* CreateRenderTargetView(
XCEngine::RHI::RHITexture*, XCEngine::RHI::RHITexture*,
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; } const XCEngine::RHI::ResourceViewDesc& desc) override {
++m_state->createRenderTargetViewCalls;
m_state->lastRenderTargetViewFormat = static_cast<XCEngine::RHI::Format>(desc.format);
return new MockShadowView(
m_state,
XCEngine::RHI::ResourceViewType::RenderTarget,
static_cast<XCEngine::RHI::Format>(desc.format),
desc.dimension);
}
XCEngine::RHI::RHIResourceView* CreateDepthStencilView( XCEngine::RHI::RHIResourceView* CreateDepthStencilView(
XCEngine::RHI::RHITexture*, XCEngine::RHI::RHITexture*,
@@ -395,6 +411,7 @@ public:
lastSourceSurfaceWidth = context.sourceSurface->GetRenderAreaWidth(); lastSourceSurfaceWidth = context.sourceSurface->GetRenderAreaWidth();
lastSourceSurfaceHeight = context.sourceSurface->GetRenderAreaHeight(); lastSourceSurfaceHeight = context.sourceSurface->GetRenderAreaHeight();
} }
lastSourceColorView = context.sourceColorView;
return m_executeResult; return m_executeResult;
} }
@@ -412,6 +429,7 @@ public:
bool lastHasSourceSurface = false; bool lastHasSourceSurface = false;
uint32_t lastSourceSurfaceWidth = 0; uint32_t lastSourceSurfaceWidth = 0;
uint32_t lastSourceSurfaceHeight = 0; uint32_t lastSourceSurfaceHeight = 0;
XCEngine::RHI::RHIResourceView* lastSourceColorView = nullptr;
private: private:
std::shared_ptr<MockPipelineState> m_state; std::shared_ptr<MockPipelineState> m_state;
@@ -451,6 +469,7 @@ public:
lastSourceSurfaceWidth = context.sourceSurface->GetRenderAreaWidth(); lastSourceSurfaceWidth = context.sourceSurface->GetRenderAreaWidth();
lastSourceSurfaceHeight = context.sourceSurface->GetRenderAreaHeight(); lastSourceSurfaceHeight = context.sourceSurface->GetRenderAreaHeight();
} }
lastSourceColorView = context.sourceColorView;
return m_executeResult; return m_executeResult;
} }
@@ -470,6 +489,7 @@ public:
bool lastHasSourceSurface = false; bool lastHasSourceSurface = false;
uint32_t lastSourceSurfaceWidth = 0; uint32_t lastSourceSurfaceWidth = 0;
uint32_t lastSourceSurfaceHeight = 0; uint32_t lastSourceSurfaceHeight = 0;
XCEngine::RHI::RHIResourceView* lastSourceColorView = nullptr;
}; };
RenderContext CreateValidContext() { RenderContext CreateValidContext() {
@@ -507,11 +527,13 @@ TEST(CameraRenderRequest_Test, ReportsFormalFrameStageContract) {
request.postProcess.sourceSurface = RenderSurface(256, 128); request.postProcess.sourceSurface = RenderSurface(256, 128);
request.postProcess.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2)); request.postProcess.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
request.postProcess.sourceColorView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(20);
request.postProcess.destinationSurface = RenderSurface(512, 256); request.postProcess.destinationSurface = RenderSurface(512, 256);
request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3)); request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3));
request.finalOutput.sourceSurface = RenderSurface(512, 256); request.finalOutput.sourceSurface = RenderSurface(512, 256);
request.finalOutput.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(4)); request.finalOutput.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(4));
request.finalOutput.sourceColorView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(40);
request.finalOutput.destinationSurface = RenderSurface(640, 360); request.finalOutput.destinationSurface = RenderSurface(640, 360);
request.finalOutput.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(5)); request.finalOutput.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(5));
@@ -563,8 +585,14 @@ TEST(CameraRenderRequest_Test, ReportsFormalFrameStageContract) {
EXPECT_EQ(request.GetOutputSurface(CameraFrameStage::PostProcess)->GetRenderAreaWidth(), 512u); EXPECT_EQ(request.GetOutputSurface(CameraFrameStage::PostProcess)->GetRenderAreaWidth(), 512u);
ASSERT_NE(request.GetSourceSurface(CameraFrameStage::PostProcess), nullptr); ASSERT_NE(request.GetSourceSurface(CameraFrameStage::PostProcess), nullptr);
EXPECT_EQ(request.GetSourceSurface(CameraFrameStage::PostProcess)->GetRenderAreaWidth(), 256u); EXPECT_EQ(request.GetSourceSurface(CameraFrameStage::PostProcess)->GetRenderAreaWidth(), 256u);
EXPECT_EQ(
request.GetSourceColorView(CameraFrameStage::PostProcess),
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(20));
ASSERT_NE(request.GetSourceSurface(CameraFrameStage::FinalOutput), nullptr); ASSERT_NE(request.GetSourceSurface(CameraFrameStage::FinalOutput), nullptr);
EXPECT_EQ(request.GetSourceSurface(CameraFrameStage::FinalOutput)->GetRenderAreaWidth(), 512u); EXPECT_EQ(request.GetSourceSurface(CameraFrameStage::FinalOutput)->GetRenderAreaWidth(), 512u);
EXPECT_EQ(
request.GetSourceColorView(CameraFrameStage::FinalOutput),
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(40));
} }
TEST(CameraRenderer_Test, UsesOverrideCameraAndSurfaceSizeWhenSubmittingScene) { TEST(CameraRenderer_Test, UsesOverrideCameraAndSurfaceSizeWhenSubmittingScene) {
@@ -699,9 +727,7 @@ TEST(CameraRenderer_Test, ExecutesInjectedPreAndPostPassSequencesAroundPipelineR
"pre", "pre",
"pipeline", "pipeline",
"init:post", "init:post",
"post", "post" }));
"shutdown:post",
"shutdown:pre" }));
} }
TEST(CameraRenderer_Test, ExecutesObjectIdPassBetweenPipelineAndPostPassesWhenRequested) { TEST(CameraRenderer_Test, ExecutesObjectIdPassBetweenPipelineAndPostPassesWhenRequested) {
@@ -744,9 +770,7 @@ TEST(CameraRenderer_Test, ExecutesObjectIdPassBetweenPipelineAndPostPassesWhenRe
"pipeline", "pipeline",
"objectId", "objectId",
"init:post", "init:post",
"post", "post" }));
"shutdown:post",
"shutdown:pre" }));
} }
TEST(CameraRenderer_Test, RoutesSceneColorThroughPostProcessAndFinalOutputStages) { TEST(CameraRenderer_Test, RoutesSceneColorThroughPostProcessAndFinalOutputStages) {
@@ -780,11 +804,13 @@ TEST(CameraRenderer_Test, RoutesSceneColorThroughPostProcessAndFinalOutputStages
request.postProcess.sourceSurface = RenderSurface(256, 128); request.postProcess.sourceSurface = RenderSurface(256, 128);
request.postProcess.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2)); request.postProcess.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
request.postProcess.sourceColorView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(20);
request.postProcess.destinationSurface = RenderSurface(512, 256); request.postProcess.destinationSurface = RenderSurface(512, 256);
request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3)); request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3));
request.postProcess.passes = &postProcessPasses; request.postProcess.passes = &postProcessPasses;
request.finalOutput.sourceSurface = request.postProcess.destinationSurface; request.finalOutput.sourceSurface = request.postProcess.destinationSurface;
request.finalOutput.sourceColorView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(30);
request.finalOutput.destinationSurface = request.surface; request.finalOutput.destinationSurface = request.surface;
request.finalOutput.passes = &finalOutputPasses; request.finalOutput.passes = &finalOutputPasses;
@@ -797,12 +823,18 @@ TEST(CameraRenderer_Test, RoutesSceneColorThroughPostProcessAndFinalOutputStages
EXPECT_TRUE(postProcessPassRaw->lastHasSourceSurface); EXPECT_TRUE(postProcessPassRaw->lastHasSourceSurface);
EXPECT_EQ(postProcessPassRaw->lastSourceSurfaceWidth, 256u); EXPECT_EQ(postProcessPassRaw->lastSourceSurfaceWidth, 256u);
EXPECT_EQ(postProcessPassRaw->lastSourceSurfaceHeight, 128u); EXPECT_EQ(postProcessPassRaw->lastSourceSurfaceHeight, 128u);
EXPECT_EQ(
postProcessPassRaw->lastSourceColorView,
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(20));
EXPECT_EQ(postProcessPassRaw->lastSurfaceWidth, 512u); EXPECT_EQ(postProcessPassRaw->lastSurfaceWidth, 512u);
EXPECT_EQ(postProcessPassRaw->lastSurfaceHeight, 256u); EXPECT_EQ(postProcessPassRaw->lastSurfaceHeight, 256u);
ASSERT_NE(finalOutputPassRaw, nullptr); ASSERT_NE(finalOutputPassRaw, nullptr);
EXPECT_TRUE(finalOutputPassRaw->lastHasSourceSurface); EXPECT_TRUE(finalOutputPassRaw->lastHasSourceSurface);
EXPECT_EQ(finalOutputPassRaw->lastSourceSurfaceWidth, 512u); EXPECT_EQ(finalOutputPassRaw->lastSourceSurfaceWidth, 512u);
EXPECT_EQ(finalOutputPassRaw->lastSourceSurfaceHeight, 256u); EXPECT_EQ(finalOutputPassRaw->lastSourceSurfaceHeight, 256u);
EXPECT_EQ(
finalOutputPassRaw->lastSourceColorView,
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(30));
EXPECT_EQ(finalOutputPassRaw->lastSurfaceWidth, 800u); EXPECT_EQ(finalOutputPassRaw->lastSurfaceWidth, 800u);
EXPECT_EQ(finalOutputPassRaw->lastSurfaceHeight, 600u); EXPECT_EQ(finalOutputPassRaw->lastSurfaceHeight, 600u);
EXPECT_EQ( EXPECT_EQ(
@@ -812,9 +844,214 @@ TEST(CameraRenderer_Test, RoutesSceneColorThroughPostProcessAndFinalOutputStages
"init:postProcess", "init:postProcess",
"postProcess", "postProcess",
"init:finalOutput", "init:finalOutput",
"finalOutput", "finalOutput" }));
"shutdown:finalOutput", }
"shutdown:postProcess" }));
TEST(CameraRenderer_Test, ChainsMultiPassPostProcessThroughIntermediateSurface) {
Scene scene("CameraRendererMultiPassPostProcessScene");
GameObject* cameraObject = scene.CreateGameObject("Camera");
auto* camera = cameraObject->AddComponent<CameraComponent>();
camera->SetPrimary(true);
camera->SetDepth(4.0f);
auto pipelineState = std::make_shared<MockPipelineState>();
auto allocationState = std::make_shared<MockShadowAllocationState>();
MockShadowDevice device(allocationState);
CameraRenderer renderer(std::make_unique<MockPipeline>(pipelineState));
auto firstPass = std::make_unique<MockScenePass>(pipelineState, "postProcessTint");
MockScenePass* firstPassRaw = firstPass.get();
auto secondPass = std::make_unique<MockScenePass>(pipelineState, "postProcessComposite");
MockScenePass* secondPassRaw = secondPass.get();
RenderPassSequence postProcessPasses;
postProcessPasses.AddPass(std::move(firstPass));
postProcessPasses.AddPass(std::move(secondPass));
auto* sourceColorAttachment = new MockShadowView(
allocationState,
XCEngine::RHI::ResourceViewType::RenderTarget,
XCEngine::RHI::Format::R8G8B8A8_UNorm,
XCEngine::RHI::ResourceViewDimension::Texture2D);
auto* sourceColorShaderView = new MockShadowView(
allocationState,
XCEngine::RHI::ResourceViewType::ShaderResource,
XCEngine::RHI::Format::R8G8B8A8_UNorm,
XCEngine::RHI::ResourceViewDimension::Texture2D);
auto* destinationColorAttachment = new MockShadowView(
allocationState,
XCEngine::RHI::ResourceViewType::RenderTarget,
XCEngine::RHI::Format::R8G8B8A8_UNorm,
XCEngine::RHI::ResourceViewDimension::Texture2D);
RenderContext context = CreateValidContext();
context.device = &device;
CameraRenderRequest request;
request.scene = &scene;
request.camera = camera;
request.context = context;
request.surface = RenderSurface(512, 256);
request.surface.SetColorAttachment(destinationColorAttachment);
request.cameraDepth = camera->GetDepth();
request.postProcess.sourceSurface = RenderSurface(256, 128);
request.postProcess.sourceSurface.SetColorAttachment(sourceColorAttachment);
request.postProcess.sourceColorView = sourceColorShaderView;
request.postProcess.destinationSurface = RenderSurface(512, 256);
request.postProcess.destinationSurface.SetColorAttachment(destinationColorAttachment);
request.postProcess.passes = &postProcessPasses;
ASSERT_TRUE(renderer.Render(request));
ASSERT_NE(firstPassRaw, nullptr);
EXPECT_TRUE(firstPassRaw->lastHasSourceSurface);
EXPECT_EQ(firstPassRaw->lastSourceSurfaceWidth, 256u);
EXPECT_EQ(firstPassRaw->lastSourceSurfaceHeight, 128u);
EXPECT_EQ(firstPassRaw->lastSourceColorView, sourceColorShaderView);
EXPECT_EQ(firstPassRaw->lastSurfaceWidth, 512u);
EXPECT_EQ(firstPassRaw->lastSurfaceHeight, 256u);
ASSERT_NE(secondPassRaw, nullptr);
EXPECT_TRUE(secondPassRaw->lastHasSourceSurface);
EXPECT_EQ(secondPassRaw->lastSourceSurfaceWidth, 512u);
EXPECT_EQ(secondPassRaw->lastSourceSurfaceHeight, 256u);
EXPECT_NE(secondPassRaw->lastSourceColorView, nullptr);
EXPECT_NE(secondPassRaw->lastSourceColorView, sourceColorShaderView);
EXPECT_EQ(secondPassRaw->lastSurfaceWidth, 512u);
EXPECT_EQ(secondPassRaw->lastSurfaceHeight, 256u);
EXPECT_EQ(allocationState->createTextureCalls, 1);
EXPECT_EQ(allocationState->createRenderTargetViewCalls, 1);
EXPECT_EQ(allocationState->createShaderViewCalls, 1);
EXPECT_EQ(
pipelineState->eventLog,
(std::vector<std::string>{
"pipeline",
"init:postProcessTint",
"init:postProcessComposite",
"postProcessTint",
"postProcessComposite" }));
delete destinationColorAttachment;
delete sourceColorShaderView;
delete sourceColorAttachment;
}
TEST(CameraRenderer_Test, KeepsPostProcessAndFinalOutputScratchSurfacesIndependentPerStage) {
Scene scene("CameraRendererIndependentFullscreenStagesScene");
GameObject* cameraObject = scene.CreateGameObject("Camera");
auto* camera = cameraObject->AddComponent<CameraComponent>();
camera->SetPrimary(true);
camera->SetDepth(5.0f);
auto pipelineState = std::make_shared<MockPipelineState>();
auto allocationState = std::make_shared<MockShadowAllocationState>();
MockShadowDevice device(allocationState);
CameraRenderer renderer(std::make_unique<MockPipeline>(pipelineState));
auto postFirstPass = std::make_unique<MockScenePass>(pipelineState, "postA");
auto postSecondPass = std::make_unique<MockScenePass>(pipelineState, "postB");
MockScenePass* postSecondPassRaw = postSecondPass.get();
RenderPassSequence postProcessPasses;
postProcessPasses.AddPass(std::move(postFirstPass));
postProcessPasses.AddPass(std::move(postSecondPass));
auto finalFirstPass = std::make_unique<MockScenePass>(pipelineState, "finalA");
MockScenePass* finalFirstPassRaw = finalFirstPass.get();
auto finalSecondPass = std::make_unique<MockScenePass>(pipelineState, "finalB");
MockScenePass* finalSecondPassRaw = finalSecondPass.get();
RenderPassSequence finalOutputPasses;
finalOutputPasses.AddPass(std::move(finalFirstPass));
finalOutputPasses.AddPass(std::move(finalSecondPass));
auto* sourceColorAttachment = new MockShadowView(
allocationState,
XCEngine::RHI::ResourceViewType::RenderTarget,
XCEngine::RHI::Format::R8G8B8A8_UNorm,
XCEngine::RHI::ResourceViewDimension::Texture2D);
auto* sourceColorShaderView = new MockShadowView(
allocationState,
XCEngine::RHI::ResourceViewType::ShaderResource,
XCEngine::RHI::Format::R8G8B8A8_UNorm,
XCEngine::RHI::ResourceViewDimension::Texture2D);
auto* postProcessDestination = new MockShadowView(
allocationState,
XCEngine::RHI::ResourceViewType::RenderTarget,
XCEngine::RHI::Format::R8G8B8A8_UNorm,
XCEngine::RHI::ResourceViewDimension::Texture2D);
auto* finalOutputSource = new MockShadowView(
allocationState,
XCEngine::RHI::ResourceViewType::ShaderResource,
XCEngine::RHI::Format::R8G8B8A8_UNorm,
XCEngine::RHI::ResourceViewDimension::Texture2D);
auto* finalOutputDestination = new MockShadowView(
allocationState,
XCEngine::RHI::ResourceViewType::RenderTarget,
XCEngine::RHI::Format::R8G8B8A8_UNorm,
XCEngine::RHI::ResourceViewDimension::Texture2D);
RenderContext context = CreateValidContext();
context.device = &device;
CameraRenderRequest request;
request.scene = &scene;
request.camera = camera;
request.context = context;
request.surface = RenderSurface(800, 600);
request.surface.SetColorAttachment(finalOutputDestination);
request.cameraDepth = camera->GetDepth();
request.postProcess.sourceSurface = RenderSurface(256, 128);
request.postProcess.sourceSurface.SetColorAttachment(sourceColorAttachment);
request.postProcess.sourceColorView = sourceColorShaderView;
request.postProcess.destinationSurface = RenderSurface(512, 256);
request.postProcess.destinationSurface.SetColorAttachment(postProcessDestination);
request.postProcess.passes = &postProcessPasses;
request.finalOutput.sourceSurface = request.postProcess.destinationSurface;
request.finalOutput.sourceColorView = finalOutputSource;
request.finalOutput.destinationSurface = RenderSurface(800, 600);
request.finalOutput.destinationSurface.SetColorAttachment(finalOutputDestination);
request.finalOutput.passes = &finalOutputPasses;
ASSERT_TRUE(renderer.Render(request));
ASSERT_NE(postSecondPassRaw, nullptr);
EXPECT_EQ(postSecondPassRaw->lastSourceSurfaceWidth, 512u);
EXPECT_EQ(postSecondPassRaw->lastSourceSurfaceHeight, 256u);
ASSERT_NE(finalFirstPassRaw, nullptr);
EXPECT_EQ(finalFirstPassRaw->lastSourceSurfaceWidth, 512u);
EXPECT_EQ(finalFirstPassRaw->lastSourceSurfaceHeight, 256u);
EXPECT_EQ(finalFirstPassRaw->lastSourceColorView, finalOutputSource);
ASSERT_NE(finalSecondPassRaw, nullptr);
EXPECT_EQ(finalSecondPassRaw->lastSourceSurfaceWidth, 800u);
EXPECT_EQ(finalSecondPassRaw->lastSourceSurfaceHeight, 600u);
EXPECT_NE(finalSecondPassRaw->lastSourceColorView, nullptr);
EXPECT_NE(finalSecondPassRaw->lastSourceColorView, finalOutputSource);
EXPECT_EQ(allocationState->createTextureCalls, 2);
EXPECT_EQ(allocationState->createRenderTargetViewCalls, 2);
EXPECT_EQ(allocationState->createShaderViewCalls, 2);
EXPECT_EQ(allocationState->shutdownTextureCalls, 0);
EXPECT_EQ(allocationState->shutdownRenderTargetViewCalls, 0);
EXPECT_EQ(allocationState->shutdownShaderViewCalls, 0);
EXPECT_EQ(
pipelineState->eventLog,
(std::vector<std::string>{
"pipeline",
"init:postA",
"init:postB",
"postA",
"postB",
"init:finalA",
"init:finalB",
"finalA",
"finalB" }));
delete finalOutputDestination;
delete finalOutputSource;
delete postProcessDestination;
delete sourceColorShaderView;
delete sourceColorAttachment;
} }
TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) { TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) {
@@ -869,9 +1106,11 @@ TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) {
request.depthOnly.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2)); request.depthOnly.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
request.postProcess.sourceSurface = RenderSurface(256, 128); request.postProcess.sourceSurface = RenderSurface(256, 128);
request.postProcess.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(4)); request.postProcess.sourceSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(4));
request.postProcess.sourceColorView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(40);
request.postProcess.destinationSurface = RenderSurface(320, 180); request.postProcess.destinationSurface = RenderSurface(320, 180);
request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(5)); request.postProcess.destinationSurface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(5));
request.finalOutput.sourceSurface = request.postProcess.destinationSurface; request.finalOutput.sourceSurface = request.postProcess.destinationSurface;
request.finalOutput.sourceColorView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(50);
request.finalOutput.destinationSurface = request.surface; request.finalOutput.destinationSurface = request.surface;
request.objectId.surface = RenderSurface(320, 180); request.objectId.surface = RenderSurface(320, 180);
request.objectId.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(6)); request.objectId.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(6));
@@ -896,12 +1135,7 @@ TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) {
"init:post", "init:post",
"post", "post",
"init:overlay", "init:overlay",
"overlay", "overlay" }));
"shutdown:overlay",
"shutdown:post",
"shutdown:finalOutput",
"shutdown:postProcess",
"shutdown:pre" }));
} }
TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipeline) { TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipeline) {
@@ -1253,7 +1487,7 @@ TEST(CameraRenderer_Test, ShutsDownInitializedPassesWhenPipelineRenderFails) {
EXPECT_FALSE(renderer.Render(request)); EXPECT_FALSE(renderer.Render(request));
EXPECT_EQ( EXPECT_EQ(
state->eventLog, state->eventLog,
(std::vector<std::string>{ "init:pre", "pre", "pipeline", "shutdown:pre" })); (std::vector<std::string>{ "init:pre", "pre", "pipeline" }));
} }
TEST(CameraRenderer_Test, StopsRenderingWhenObjectIdPassFails) { TEST(CameraRenderer_Test, StopsRenderingWhenObjectIdPassFails) {
@@ -1290,7 +1524,7 @@ TEST(CameraRenderer_Test, StopsRenderingWhenObjectIdPassFails) {
EXPECT_FALSE(renderer.Render(request)); EXPECT_FALSE(renderer.Render(request));
EXPECT_EQ( EXPECT_EQ(
state->eventLog, state->eventLog,
(std::vector<std::string>{ "init:pre", "pre", "pipeline", "objectId", "shutdown:pre" })); (std::vector<std::string>{ "init:pre", "pre", "pipeline", "objectId" }));
} }
TEST(CameraRenderer_Test, ShutsDownSequencesWhenPostPassInitializationFails) { TEST(CameraRenderer_Test, ShutsDownSequencesWhenPostPassInitializationFails) {
@@ -1327,8 +1561,7 @@ TEST(CameraRenderer_Test, ShutsDownSequencesWhenPostPassInitializationFails) {
"pre", "pre",
"pipeline", "pipeline",
"init:post", "init:post",
"shutdown:post", "shutdown:post" }));
"shutdown:pre" }));
} }
TEST(SceneRenderer_Test, BuildsSortedRequestsForAllUsableCamerasAndHonorsOverrideCamera) { TEST(SceneRenderer_Test, BuildsSortedRequestsForAllUsableCamerasAndHonorsOverrideCamera) {