diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 2e0136f2..56abd473 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -505,6 +505,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Extraction/RenderSceneUtility.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphTypes.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraph.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphBlackboard.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphCompiler.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphExecutor.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h diff --git a/engine/include/XCEngine/Rendering/Graph/RenderGraphBlackboard.h b/engine/include/XCEngine/Rendering/Graph/RenderGraphBlackboard.h new file mode 100644 index 00000000..9ad26e3e --- /dev/null +++ b/engine/include/XCEngine/Rendering/Graph/RenderGraphBlackboard.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace XCEngine { +namespace Rendering { + +class RenderGraphBlackboard { +public: + template + T& Emplace(Args&&... args) { + using StorageType = std::remove_cv_t>; + auto value = std::make_shared(std::forward(args)...); + StorageType& reference = *value; + m_entries[std::type_index(typeid(StorageType))] = std::move(value); + return reference; + } + + template + void Set(const T& value) { + Emplace(value); + } + + template + T* TryGet() { + using StorageType = std::remove_cv_t>; + const auto entryIt = m_entries.find(std::type_index(typeid(StorageType))); + return entryIt != m_entries.end() + ? static_cast(entryIt->second.get()) + : nullptr; + } + + template + const T* TryGet() const { + using StorageType = std::remove_cv_t>; + const auto entryIt = m_entries.find(std::type_index(typeid(StorageType))); + return entryIt != m_entries.end() + ? static_cast(entryIt->second.get()) + : nullptr; + } + + template + bool Contains() const { + return TryGet() != nullptr; + } + + void Clear() { + m_entries.clear(); + } + +private: + std::unordered_map> m_entries; +}; + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/RenderPipeline.h b/engine/include/XCEngine/Rendering/RenderPipeline.h index 5133cdcb..cd4c2779 100644 --- a/engine/include/XCEngine/Rendering/RenderPipeline.h +++ b/engine/include/XCEngine/Rendering/RenderPipeline.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,13 @@ namespace Rendering { class RenderGraphBuilder; +struct CameraFrameRenderGraphResources { + RenderGraphTextureHandle mainSceneColor = {}; + RenderGraphTextureHandle mainSceneDepth = {}; + RenderGraphTextureHandle mainDirectionalShadow = {}; + RenderGraphTextureHandle objectIdColor = {}; +}; + struct RenderPipelineMainSceneRenderGraphContext { RenderGraphBuilder& graphBuilder; Containers::String passName = {}; @@ -27,6 +35,7 @@ struct RenderPipelineMainSceneRenderGraphContext { RenderGraphTextureHandle depthTarget = {}; RenderGraphTextureHandle mainDirectionalShadowTexture = {}; bool* executionSucceeded = nullptr; + RenderGraphBlackboard* blackboard = nullptr; }; class RenderPipeline { diff --git a/engine/include/XCEngine/Rendering/SceneRenderFeaturePass.h b/engine/include/XCEngine/Rendering/SceneRenderFeaturePass.h index 2b351098..c3525a0d 100644 --- a/engine/include/XCEngine/Rendering/SceneRenderFeaturePass.h +++ b/engine/include/XCEngine/Rendering/SceneRenderFeaturePass.h @@ -12,6 +12,7 @@ namespace XCEngine { namespace Rendering { class RenderGraphBuilder; +class RenderGraphBlackboard; using SceneRenderFeaturePassBeginCallback = std::function; using SceneRenderFeaturePassEndCallback = std::function; @@ -32,6 +33,7 @@ struct SceneRenderFeaturePassRenderGraphContext { bool* executionSucceeded = nullptr; SceneRenderFeaturePassBeginCallback beginPassCallback = {}; SceneRenderFeaturePassEndCallback endPassCallback = {}; + RenderGraphBlackboard* blackboard = nullptr; }; class SceneRenderFeaturePass : public RenderPass { diff --git a/engine/src/Rendering/Execution/CameraRenderer.cpp b/engine/src/Rendering/Execution/CameraRenderer.cpp index fd8dd0f3..d02a52d4 100644 --- a/engine/src/Rendering/Execution/CameraRenderer.cpp +++ b/engine/src/Rendering/Execution/CameraRenderer.cpp @@ -771,7 +771,10 @@ bool ExecuteRenderGraphPlan( CameraFrameExecutionState& executionState) { RenderGraph graph = {}; RenderGraphBuilder graphBuilder(graph); + RenderGraphBlackboard blackboard = {}; RenderGraphImportedTextureRegistry importedTextures = {}; + CameraFrameRenderGraphResources& frameResources = + blackboard.Emplace(); bool stageExecutionSucceeded = true; RenderGraphTextureHandle mainDirectionalShadowTexture = {}; @@ -896,6 +899,14 @@ bool ExecuteRenderGraphPlan( shadowState.HasShadowSampling() && outputSurface.depthTexture.IsValid()) { mainDirectionalShadowTexture = outputSurface.depthTexture; + frameResources.mainDirectionalShadow = outputSurface.depthTexture; + } + if (stage == CameraFrameStage::MainScene) { + frameResources.mainSceneColor = GetPrimaryColorTexture(outputSurface); + frameResources.mainSceneDepth = outputSurface.depthTexture; + } + if (stage == CameraFrameStage::ObjectId) { + frameResources.objectIdColor = GetPrimaryColorTexture(outputSurface); } const RenderSurface stageSurfaceTemplate = stagePassContext.surface; const bool hasStageSourceSurface = stagePassContext.sourceSurface != nullptr; @@ -921,7 +932,8 @@ bool ExecuteRenderGraphPlan( outputSurface.colorTextures, outputSurface.depthTexture, mainDirectionalShadowTexture, - &stageExecutionSucceeded + &stageExecutionSucceeded, + &blackboard }; if (!executionState.pipeline->RecordMainSceneRenderGraph(mainSceneContext)) { Debug::Logger::Get().Error( diff --git a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineFrame.cpp b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineFrame.cpp index 063d989d..caa239c7 100644 --- a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineFrame.cpp +++ b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineFrame.cpp @@ -189,7 +189,8 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph( clearAttachments, executionSucceeded, beginRecordedPass, - endRecordedPass + endRecordedPass, + context.blackboard }; bool recordedAnyPass = false; if (!m_forwardSceneFeatureHost.Record( diff --git a/engine/src/Rendering/SceneRenderFeatureHost.cpp b/engine/src/Rendering/SceneRenderFeatureHost.cpp index 79213236..4977a5c3 100644 --- a/engine/src/Rendering/SceneRenderFeatureHost.cpp +++ b/engine/src/Rendering/SceneRenderFeatureHost.cpp @@ -137,7 +137,8 @@ bool SceneRenderFeatureHost::Record( clearAttachments, context.executionSucceeded, context.beginPassCallback, - context.endPassCallback + context.endPassCallback, + context.blackboard }; if (!featurePass->RecordRenderGraph(featureContext)) { Debug::Logger::Get().Error( diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index 1772808b..a81d54d0 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -57,6 +57,11 @@ struct MockPipelineState { bool lastHasSkybox = false; const XCEngine::Resources::Material* lastSkyboxMaterial = nullptr; bool lastRecordedMainSceneShadowHandleValid = false; + bool lastReceivedRenderGraphBlackboard = false; + bool lastBlackboardMainSceneColorValid = false; + bool lastBlackboardMainSceneDepthValid = false; + bool lastBlackboardMainDirectionalShadowValid = false; + bool lastBlackboardObjectIdColorValid = false; std::vector renderedCameras; std::vector renderedClearFlags; std::vector renderedClearColors; @@ -414,6 +419,20 @@ public: ++m_state->recordMainSceneCalls; m_state->lastRecordedMainSceneShadowHandleValid = context.mainDirectionalShadowTexture.IsValid(); + m_state->lastReceivedRenderGraphBlackboard = context.blackboard != nullptr; + if (context.blackboard != nullptr) { + if (const CameraFrameRenderGraphResources* frameResources = + context.blackboard->TryGet()) { + m_state->lastBlackboardMainSceneColorValid = + frameResources->mainSceneColor.IsValid(); + m_state->lastBlackboardMainSceneDepthValid = + frameResources->mainSceneDepth.IsValid(); + m_state->lastBlackboardMainDirectionalShadowValid = + frameResources->mainDirectionalShadow.IsValid(); + m_state->lastBlackboardObjectIdColorValid = + frameResources->objectIdColor.IsValid(); + } + } if (!m_state->recordMainSceneResult) { return false; } @@ -1585,6 +1604,11 @@ TEST(CameraRenderer_Test, UsesPipelineRecordedMainSceneWhenSupported) { EXPECT_EQ(state->executeRecordedMainSceneCalls, 1); EXPECT_EQ(state->renderCalls, 0); EXPECT_EQ(state->eventLog, (std::vector{ "pipelineGraph" })); + EXPECT_TRUE(state->lastReceivedRenderGraphBlackboard); + EXPECT_TRUE(state->lastBlackboardMainSceneColorValid); + EXPECT_TRUE(state->lastBlackboardMainSceneDepthValid); + EXPECT_FALSE(state->lastBlackboardMainDirectionalShadowValid); + EXPECT_FALSE(state->lastBlackboardObjectIdColorValid); EXPECT_FALSE(state->lastSurfaceAutoTransitionEnabled); EXPECT_EQ(state->lastSurfaceWidth, 320u); EXPECT_EQ(state->lastSurfaceHeight, 180u); @@ -1723,6 +1747,11 @@ TEST(CameraRenderer_Test, PassesShadowDependencyToPipelineRecordedMainScene) { EXPECT_EQ(pipelineState->recordMainSceneCalls, 1); EXPECT_EQ(pipelineState->executeRecordedMainSceneCalls, 1); EXPECT_TRUE(pipelineState->lastRecordedMainSceneShadowHandleValid); + EXPECT_TRUE(pipelineState->lastReceivedRenderGraphBlackboard); + EXPECT_TRUE(pipelineState->lastBlackboardMainSceneColorValid); + EXPECT_TRUE(pipelineState->lastBlackboardMainSceneDepthValid); + EXPECT_TRUE(pipelineState->lastBlackboardMainDirectionalShadowValid); + EXPECT_FALSE(pipelineState->lastBlackboardObjectIdColorValid); EXPECT_EQ( pipelineState->eventLog, (std::vector{