diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 328db6b2..7299f654 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -553,6 +553,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Features/BuiltinGaussianSplatPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Features/BuiltinVolumetricPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/BuiltinForwardSceneRecorder.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/CameraFramePlan.cpp @@ -616,6 +617,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Shadow/DirectionalShadowRuntime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/SceneRenderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardSceneRecorder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/ScriptableRenderPipelineHost.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.cpp diff --git a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h index c38eba72..34ab8cbf 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h +++ b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h @@ -46,6 +46,8 @@ struct BuiltinForwardStageGraphBuilder; namespace Pipelines { +class BuiltinForwardSceneRecorder; + class BuiltinForwardPipeline : public RenderPipeline { public: BuiltinForwardPipeline(); @@ -385,6 +387,7 @@ private: RHI::RHIResourceView* m_skyboxBoundCubemapTextureView = nullptr; SceneRenderFeatureHost m_forwardSceneFeatureHost; + friend class BuiltinForwardSceneRecorder; friend struct Internal::BuiltinForwardStageGraphBuilder; }; diff --git a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardSceneRecorder.h b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardSceneRecorder.h new file mode 100644 index 00000000..ef1c732b --- /dev/null +++ b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardSceneRecorder.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace XCEngine { +namespace Rendering { + +struct RenderPipelineStageRenderGraphContext; + +namespace Pipelines { + +class BuiltinForwardPipeline; +struct BuiltinForwardSceneRecorderState; + +class BuiltinForwardSceneRecorder { +public: + BuiltinForwardSceneRecorder( + BuiltinForwardPipeline& pipeline, + const RenderPipelineStageRenderGraphContext& context); + ~BuiltinForwardSceneRecorder(); + + bool RecordDefaultScene(); + bool RecordScenePhase(ScenePhase scenePhase); + bool RecordInjectionPoint(SceneRenderInjectionPoint injectionPoint); + +private: + SceneRenderFeaturePassBeginCallback BuildBeginRecordedPassCallback( + bool* executionSucceeded) const; + SceneRenderFeaturePassEndCallback BuildEndRecordedPassCallback() const; + RenderPipelineStageRenderGraphContext BuildGraphContext() const; + + BuiltinForwardPipeline& m_pipeline; + const RenderPipelineStageRenderGraphContext& m_context; + RenderSurface m_graphManagedSurface = {}; + RenderGraphTextureHandle m_mainDirectionalShadowTexture = {}; + bool m_clearAttachments = true; + std::shared_ptr m_state; +}; + +} // namespace Pipelines +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardSceneRecorder.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardSceneRecorder.cpp new file mode 100644 index 00000000..df75c71f --- /dev/null +++ b/engine/src/Rendering/Pipelines/BuiltinForwardSceneRecorder.cpp @@ -0,0 +1,243 @@ +#include + +#include "Debug/Logger.h" +#include "Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace XCEngine { +namespace Rendering { +namespace Pipelines { + +struct BuiltinForwardSceneRecorderState { + bool initialized = false; +}; + +namespace { + +bool ScenePhaseSamplesMainDirectionalShadow(ScenePhase scenePhase) { + return scenePhase == ScenePhase::Opaque || + scenePhase == ScenePhase::Transparent; +} + +} // namespace + +BuiltinForwardSceneRecorder::BuiltinForwardSceneRecorder( + BuiltinForwardPipeline& pipeline, + const RenderPipelineStageRenderGraphContext& context) + : m_pipeline(pipeline) + , m_context(context) + , m_graphManagedSurface( + BuildRenderGraphManagedSurfaceTemplate(context.surfaceTemplate)) + , m_state(std::make_shared()) { + const CameraFrameRenderGraphResources* const frameResources = + TryGetCameraFrameRenderGraphResources(context.blackboard); + if (frameResources != nullptr) { + m_mainDirectionalShadowTexture = frameResources->mainDirectionalShadow; + } +} + +BuiltinForwardSceneRecorder::~BuiltinForwardSceneRecorder() = default; + +SceneRenderFeaturePassBeginCallback +BuiltinForwardSceneRecorder::BuildBeginRecordedPassCallback( + bool* executionSucceeded) const { + BuiltinForwardPipeline& pipeline = m_pipeline; + const std::shared_ptr state = m_state; + return [&pipeline, state, executionSucceeded]( + const RenderPassContext& passContext, + bool clearAttachments) -> bool { + if (executionSucceeded != nullptr && + !(*executionSucceeded)) { + return false; + } + + if (!state->initialized) { + if (!pipeline.Initialize(passContext.renderContext)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardSceneRecorder failed during execution: Initialize returned false"); + if (executionSucceeded != nullptr) { + *executionSucceeded = false; + } + return false; + } + + const FrameExecutionContext executionContext( + passContext.renderContext, + passContext.surface, + passContext.sceneData, + passContext.sourceSurface, + passContext.sourceColorView, + passContext.sourceColorState); + if (!pipeline.m_forwardSceneFeatureHost.Prepare(executionContext)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardSceneRecorder failed during execution: SceneRenderFeatureHost::Prepare returned false"); + if (executionSucceeded != nullptr) { + *executionSucceeded = false; + } + return false; + } + + state->initialized = true; + } + + if (!pipeline.BeginForwardScenePass( + passContext, + clearAttachments)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinForwardSceneRecorder failed during execution: BeginForwardScenePass returned false"); + if (executionSucceeded != nullptr) { + *executionSucceeded = false; + } + return false; + } + + return true; + }; +} + +SceneRenderFeaturePassEndCallback +BuiltinForwardSceneRecorder::BuildEndRecordedPassCallback() const { + BuiltinForwardPipeline& pipeline = m_pipeline; + return [&pipeline](const RenderPassContext& passContext) { + pipeline.EndForwardScenePass(passContext); + }; +} + +bool BuiltinForwardSceneRecorder::RecordDefaultScene() { + if (m_context.stage != CameraFrameStage::MainScene) { + return false; + } + + for (const Internal::ForwardSceneStep& step : + Internal::GetBuiltinForwardSceneSteps()) { + if (step.type == Internal::ForwardSceneStepType::InjectionPoint) { + if (!RecordInjectionPoint(step.injectionPoint)) { + return false; + } + continue; + } + + if (!RecordScenePhase(step.scenePhase)) { + return false; + } + } + + return true; +} + +bool BuiltinForwardSceneRecorder::RecordScenePhase( + ScenePhase scenePhase) { + if (m_context.stage != CameraFrameStage::MainScene) { + return false; + } + + const RenderPipelineStageRenderGraphContext graphContext = + BuildGraphContext(); + const bool clearAttachments = m_clearAttachments; + const SceneRenderFeaturePassBeginCallback beginRecordedPass = + BuildBeginRecordedPassCallback(graphContext.executionSucceeded); + const SceneRenderFeaturePassEndCallback endRecordedPass = + BuildEndRecordedPassCallback(); + const RenderPassGraphBeginCallback beginPhasePass = + [beginRecordedPass, clearAttachments]( + const RenderPassContext& passContext) { + return beginRecordedPass( + passContext, + clearAttachments); + }; + const std::vector additionalReadTextures = + ScenePhaseSamplesMainDirectionalShadow(scenePhase) && + m_mainDirectionalShadowTexture.IsValid() + ? std::vector{ m_mainDirectionalShadowTexture } + : std::vector{}; + if (!::XCEngine::Rendering::RecordRenderPipelineStagePhasePass( + graphContext, + scenePhase, + [&pipeline = m_pipeline, scenePhase]( + const RenderPassContext& passContext) { + const FrameExecutionContext executionContext( + passContext.renderContext, + passContext.surface, + passContext.sceneData, + passContext.sourceSurface, + passContext.sourceColorView, + passContext.sourceColorState); + const ScenePhaseExecutionContext scenePhaseExecutionContext = + pipeline.BuildScenePhaseExecutionContext( + executionContext, + scenePhase); + return pipeline.ExecuteBuiltinScenePhase( + scenePhaseExecutionContext); + }, + beginPhasePass, + endRecordedPass, + additionalReadTextures)) { + return false; + } + + m_clearAttachments = false; + return true; +} + +bool BuiltinForwardSceneRecorder::RecordInjectionPoint( + SceneRenderInjectionPoint injectionPoint) { + if (m_context.stage != CameraFrameStage::MainScene) { + return false; + } + + const RenderPipelineStageRenderGraphContext graphContext = + BuildGraphContext(); + const SceneRenderFeaturePassBeginCallback beginRecordedPass = + BuildBeginRecordedPassCallback(graphContext.executionSucceeded); + const SceneRenderFeaturePassEndCallback endRecordedPass = + BuildEndRecordedPassCallback(); + bool recordedAnyPass = false; + if (!::XCEngine::Rendering::RecordRenderPipelineStageFeaturePasses( + graphContext, + m_pipeline.m_forwardSceneFeatureHost, + injectionPoint, + m_clearAttachments, + beginRecordedPass, + endRecordedPass, + &recordedAnyPass)) { + return false; + } + + if (recordedAnyPass) { + m_clearAttachments = false; + } + return true; +} + +RenderPipelineStageRenderGraphContext +BuiltinForwardSceneRecorder::BuildGraphContext() const { + const RenderGraphRecordingContext baseRecordingContext = + BuildRenderGraphRecordingContext(m_context); + RenderGraphRecordingContextBuildParams recordingParams = {}; + recordingParams.surface = &m_graphManagedSurface; + recordingParams.overrideSourceBinding = true; + recordingParams.sourceBinding = + BuildRenderGraphRecordingSourceBinding(baseRecordingContext); + const RenderGraphRecordingContext recordingContext = + BuildRenderGraphRecordingContext( + baseRecordingContext, + std::move(recordingParams)); + return BuildRenderPipelineStageRenderGraphContext( + recordingContext, + m_context.stage); +} + +} // namespace Pipelines +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardStageGraphBuilder.cpp b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardStageGraphBuilder.cpp index 3f1802fb..5d3a1247 100644 --- a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardStageGraphBuilder.cpp +++ b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardStageGraphBuilder.cpp @@ -1,175 +1,18 @@ #include "Rendering/Pipelines/Internal/BuiltinForwardStageGraphBuilder.h" -#include "Debug/Logger.h" -#include "Rendering/Graph/RenderGraph.h" #include "Rendering/Pipelines/BuiltinForwardPipeline.h" -#include "Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.h" -#include "Rendering/RenderSurface.h" -#include -#include - -#include +#include namespace XCEngine { namespace Rendering { namespace Pipelines { namespace Internal { -namespace { - -struct ForwardSceneGraphExecutionState { - bool initialized = false; -}; - -bool ScenePhaseSamplesMainDirectionalShadow(ScenePhase scenePhase) { - return scenePhase == ScenePhase::Opaque || - scenePhase == ScenePhase::Transparent; -} - -} // namespace - bool BuiltinForwardStageGraphBuilder::Record( BuiltinForwardPipeline& pipeline, const RenderPipelineStageRenderGraphContext& context) { - const RenderSurface graphManagedSurface = - BuildRenderGraphManagedSurfaceTemplate(context.surfaceTemplate); - const RenderGraphRecordingContext baseRecordingContext = - BuildRenderGraphRecordingContext(context); - RenderGraphRecordingContextBuildParams recordingParams = {}; - recordingParams.surface = &graphManagedSurface; - recordingParams.overrideSourceBinding = true; - recordingParams.sourceBinding = - BuildRenderGraphRecordingSourceBinding(baseRecordingContext); - RenderGraphRecordingContext recordingContext = - BuildRenderGraphRecordingContext( - baseRecordingContext, - std::move(recordingParams)); - - const CameraFrameRenderGraphResources* const frameResources = - TryGetCameraFrameRenderGraphResources(recordingContext.blackboard); - const RenderGraphTextureHandle mainDirectionalShadowTexture = - frameResources != nullptr - ? frameResources->mainDirectionalShadow - : RenderGraphTextureHandle{}; - bool* const executionSucceeded = recordingContext.executionSucceeded; - const RenderPipelineStageRenderGraphContext graphContext = - BuildRenderPipelineStageRenderGraphContext( - recordingContext, - CameraFrameStage::MainScene); - const std::shared_ptr graphExecutionState = - std::make_shared(); - const SceneRenderFeaturePassBeginCallback beginRecordedPass = - [&pipeline, - graphExecutionState, - executionSucceeded]( - const RenderPassContext& passContext, - bool clearAttachments) -> bool { - if (executionSucceeded != nullptr && !(*executionSucceeded)) { - return false; - } - - if (!graphExecutionState->initialized) { - if (!pipeline.Initialize(passContext.renderContext)) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline::RecordStageRenderGraph failed during execution: Initialize returned false"); - if (executionSucceeded != nullptr) { - *executionSucceeded = false; - } - return false; - } - - const FrameExecutionContext executionContext( - passContext.renderContext, - passContext.surface, - passContext.sceneData, - passContext.sourceSurface, - passContext.sourceColorView, - passContext.sourceColorState); - if (!pipeline.m_forwardSceneFeatureHost.Prepare(executionContext)) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline::RecordStageRenderGraph failed during execution: SceneRenderFeatureHost::Prepare returned false"); - if (executionSucceeded != nullptr) { - *executionSucceeded = false; - } - return false; - } - - graphExecutionState->initialized = true; - } - - if (!pipeline.BeginForwardScenePass(passContext, clearAttachments)) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline::RecordStageRenderGraph failed during execution: BeginForwardScenePass returned false"); - if (executionSucceeded != nullptr) { - *executionSucceeded = false; - } - return false; - } - - return true; - }; - const SceneRenderFeaturePassEndCallback endRecordedPass = - [&pipeline](const RenderPassContext& passContext) { - pipeline.EndForwardScenePass(passContext); - }; - - bool clearAttachments = true; - for (const ForwardSceneStep& step : GetBuiltinForwardSceneSteps()) { - if (step.type == ForwardSceneStepType::InjectionPoint) { - bool recordedAnyPass = false; - if (!::XCEngine::Rendering::RecordRenderPipelineStageFeaturePasses( - graphContext, - pipeline.m_forwardSceneFeatureHost, - step.injectionPoint, - clearAttachments, - beginRecordedPass, - endRecordedPass, - &recordedAnyPass)) { - return false; - } - - if (recordedAnyPass) { - clearAttachments = false; - } - continue; - } - - const RenderPassGraphBeginCallback beginPhasePass = - [beginRecordedPass, clearAttachments](const RenderPassContext& passContext) { - return beginRecordedPass(passContext, clearAttachments); - }; - const std::vector additionalReadTextures = - ScenePhaseSamplesMainDirectionalShadow(step.scenePhase) && - mainDirectionalShadowTexture.IsValid() - ? std::vector{ mainDirectionalShadowTexture } - : std::vector{}; - if (!::XCEngine::Rendering::RecordRenderPipelineStagePhasePass( - graphContext, - step.scenePhase, - [&pipeline, scenePhase = step.scenePhase](const RenderPassContext& passContext) { - const FrameExecutionContext executionContext( - passContext.renderContext, - passContext.surface, - passContext.sceneData, - passContext.sourceSurface, - passContext.sourceColorView, - passContext.sourceColorState); - const ScenePhaseExecutionContext scenePhaseExecutionContext = - pipeline.BuildScenePhaseExecutionContext(executionContext, scenePhase); - return pipeline.ExecuteBuiltinScenePhase(scenePhaseExecutionContext); - }, - beginPhasePass, - endRecordedPass, - additionalReadTextures)) { - return false; - } - clearAttachments = false; - } - - return true; + BuiltinForwardSceneRecorder recorder(pipeline, context); + return recorder.RecordDefaultScene(); } } // namespace Internal diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index 4e8bf1fe..d2249974 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -11,6 +11,7 @@ #include "Input/InputManager.h" #include "Physics/PhysicsWorld.h" #include "Rendering/Pipelines/BuiltinForwardPipeline.h" +#include "Rendering/Pipelines/BuiltinForwardSceneRecorder.h" #include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h" #include "Scene/Scene.h" #include "Scripting/ScriptComponent.h" @@ -88,7 +89,8 @@ struct ManagedScriptableRenderContextState { uint64_t handle = 0; Rendering::CameraFrameStage stage = Rendering::CameraFrameStage::MainScene; const Rendering::RenderPipelineStageRenderGraphContext* graphContext = nullptr; - Rendering::Pipelines::BuiltinForwardPipeline builtinForwardPipeline; + Rendering::Pipelines::BuiltinForwardSceneRecorder* builtinForwardSceneRecorder = + nullptr; }; uint64_t& GetManagedScriptableRenderContextNextHandle() { @@ -233,6 +235,7 @@ public: } void Shutdown() override { + m_builtinForwardPipeline.Shutdown(); ReleaseManagedObjects(); m_supportsStageMethod = nullptr; m_recordStageMethod = nullptr; @@ -283,6 +286,11 @@ public: ManagedScriptableRenderContextState managedContextState = {}; managedContextState.stage = context.stage; managedContextState.graphContext = &context; + Rendering::Pipelines::BuiltinForwardSceneRecorder builtinForwardSceneRecorder( + m_builtinForwardPipeline, + context); + managedContextState.builtinForwardSceneRecorder = + &builtinForwardSceneRecorder; const uint64_t managedContextHandle = RegisterManagedScriptableRenderContextState(managedContextState); MonoObject* const managedContextObject = @@ -441,6 +449,7 @@ private: mutable MonoMethod* m_supportsStageMethod = nullptr; mutable MonoMethod* m_recordStageMethod = nullptr; mutable bool m_pipelineCreationAttempted = false; + Rendering::Pipelines::BuiltinForwardPipeline m_builtinForwardPipeline; }; class MonoManagedRenderPipelineBridge final @@ -2169,18 +2178,56 @@ int32_t InternalCall_Rendering_ScriptableRenderContext_GetStage( : static_cast(Rendering::CameraFrameStage::MainScene); } -mono_bool InternalCall_Rendering_ScriptableRenderContext_RenderBuiltinForwardMainScene( +mono_bool InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinForwardMainScene( uint64_t nativeHandle) { ManagedScriptableRenderContextState* const state = FindManagedScriptableRenderContextState(nativeHandle); if (state == nullptr || state->graphContext == nullptr || + state->builtinForwardSceneRecorder == nullptr || state->stage != Rendering::CameraFrameStage::MainScene) { return 0; } - return state->builtinForwardPipeline.RecordStageRenderGraph( - *state->graphContext) + return state->builtinForwardSceneRecorder->RecordDefaultScene() + ? 1 + : 0; +} + +mono_bool +InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinForwardScenePhase( + uint64_t nativeHandle, + int32_t scenePhase) { + ManagedScriptableRenderContextState* const state = + FindManagedScriptableRenderContextState(nativeHandle); + if (state == nullptr || + state->graphContext == nullptr || + state->builtinForwardSceneRecorder == nullptr || + state->stage != Rendering::CameraFrameStage::MainScene) { + return 0; + } + + return state->builtinForwardSceneRecorder->RecordScenePhase( + static_cast(scenePhase)) + ? 1 + : 0; +} + +mono_bool +InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinForwardInjectionPoint( + uint64_t nativeHandle, + int32_t injectionPoint) { + ManagedScriptableRenderContextState* const state = + FindManagedScriptableRenderContextState(nativeHandle); + if (state == nullptr || + state->graphContext == nullptr || + state->builtinForwardSceneRecorder == nullptr || + state->stage != Rendering::CameraFrameStage::MainScene) { + return 0; + } + + return state->builtinForwardSceneRecorder->RecordInjectionPoint( + static_cast(injectionPoint)) ? 1 : 0; } @@ -2314,7 +2361,9 @@ void RegisterInternalCalls() { mono_add_internal_call("XCEngine.InternalCalls::Rendering_SetRenderPipelineAssetType", reinterpret_cast(&InternalCall_Rendering_SetRenderPipelineAssetType)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_GetRenderPipelineAssetTypeName", reinterpret_cast(&InternalCall_Rendering_GetRenderPipelineAssetTypeName)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_GetStage", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_GetStage)); - mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RenderBuiltinForwardMainScene", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RenderBuiltinForwardMainScene)); + mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordBuiltinForwardMainScene", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinForwardMainScene)); + mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordBuiltinForwardScenePhase", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinForwardScenePhase)); + mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordBuiltinForwardInjectionPoint", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinForwardInjectionPoint)); GetInternalCallRegistrationState() = true; } diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index 01d4e062..1f337a32 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -120,6 +120,8 @@ set(XCENGINE_SCRIPT_CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Quaternion.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/RaycastHit.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/RenderPipelineAsset.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScenePhase.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/SceneRenderInjectionPoint.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableRenderPipeline.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableRenderContext.cs diff --git a/managed/GameScripts/RenderPipelineApiProbe.cs b/managed/GameScripts/RenderPipelineApiProbe.cs index e08181ab..914e8e24 100644 --- a/managed/GameScripts/RenderPipelineApiProbe.cs +++ b/managed/GameScripts/RenderPipelineApiProbe.cs @@ -39,7 +39,21 @@ namespace Gameplay RecordStageCallCount++; return context != null && context.stage == CameraFrameStage.MainScene && - context.RenderBuiltinForwardMainScene(); + context.RecordBuiltinForwardInjectionPoint( + SceneRenderInjectionPoint.BeforeOpaque) && + context.RecordBuiltinForwardScenePhase(ScenePhase.Opaque) && + context.RecordBuiltinForwardInjectionPoint( + SceneRenderInjectionPoint.AfterOpaque) && + context.RecordBuiltinForwardInjectionPoint( + SceneRenderInjectionPoint.BeforeSkybox) && + context.RecordBuiltinForwardScenePhase(ScenePhase.Skybox) && + context.RecordBuiltinForwardInjectionPoint( + SceneRenderInjectionPoint.AfterSkybox) && + context.RecordBuiltinForwardInjectionPoint( + SceneRenderInjectionPoint.BeforeTransparent) && + context.RecordBuiltinForwardScenePhase(ScenePhase.Transparent) && + context.RecordBuiltinForwardInjectionPoint( + SceneRenderInjectionPoint.AfterTransparent); } } diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index 9725773d..b1e9a0af 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -388,7 +388,19 @@ namespace XCEngine [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool - Rendering_ScriptableRenderContext_RenderBuiltinForwardMainScene( + Rendering_ScriptableRenderContext_RecordBuiltinForwardMainScene( ulong nativeHandle); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool + Rendering_ScriptableRenderContext_RecordBuiltinForwardScenePhase( + ulong nativeHandle, + int scenePhase); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool + Rendering_ScriptableRenderContext_RecordBuiltinForwardInjectionPoint( + ulong nativeHandle, + int injectionPoint); } } diff --git a/managed/XCEngine.ScriptCore/ScenePhase.cs b/managed/XCEngine.ScriptCore/ScenePhase.cs new file mode 100644 index 00000000..a65aecf9 --- /dev/null +++ b/managed/XCEngine.ScriptCore/ScenePhase.cs @@ -0,0 +1,12 @@ +namespace XCEngine +{ + public enum ScenePhase + { + Opaque = 0, + Skybox = 1, + Transparent = 3, + EditorExtension = 4, + PostProcess = 5, + FinalOutput = 6 + } +} diff --git a/managed/XCEngine.ScriptCore/SceneRenderInjectionPoint.cs b/managed/XCEngine.ScriptCore/SceneRenderInjectionPoint.cs new file mode 100644 index 00000000..6faa8cb5 --- /dev/null +++ b/managed/XCEngine.ScriptCore/SceneRenderInjectionPoint.cs @@ -0,0 +1,12 @@ +namespace XCEngine +{ + public enum SceneRenderInjectionPoint + { + BeforeOpaque = 0, + AfterOpaque = 1, + BeforeSkybox = 2, + AfterSkybox = 3, + BeforeTransparent = 4, + AfterTransparent = 5 + } +} diff --git a/managed/XCEngine.ScriptCore/ScriptableRenderContext.cs b/managed/XCEngine.ScriptCore/ScriptableRenderContext.cs index f5171d7a..4b430fbb 100644 --- a/managed/XCEngine.ScriptCore/ScriptableRenderContext.cs +++ b/managed/XCEngine.ScriptCore/ScriptableRenderContext.cs @@ -13,11 +13,33 @@ namespace XCEngine (CameraFrameStage)InternalCalls.Rendering_ScriptableRenderContext_GetStage( m_nativeHandle); - public bool RenderBuiltinForwardMainScene() + public bool RecordBuiltinForwardMainScene() { return InternalCalls - .Rendering_ScriptableRenderContext_RenderBuiltinForwardMainScene( + .Rendering_ScriptableRenderContext_RecordBuiltinForwardMainScene( m_nativeHandle); } + + public bool RenderBuiltinForwardMainScene() + { + return RecordBuiltinForwardMainScene(); + } + + public bool RecordBuiltinForwardScenePhase(ScenePhase scenePhase) + { + return InternalCalls + .Rendering_ScriptableRenderContext_RecordBuiltinForwardScenePhase( + m_nativeHandle, + (int)scenePhase); + } + + public bool RecordBuiltinForwardInjectionPoint( + SceneRenderInjectionPoint injectionPoint) + { + return InternalCalls + .Rendering_ScriptableRenderContext_RecordBuiltinForwardInjectionPoint( + m_nativeHandle, + (int)injectionPoint); + } } } diff --git a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp index c9c4433c..9afd9ead 100644 --- a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp +++ b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp @@ -16,6 +16,7 @@ #define private public #include #undef private +#include #include #include #include @@ -684,6 +685,105 @@ TEST(BuiltinForwardPipeline_Test, RecordsMainSceneGraphPassWithSampledShadowDepe EXPECT_EQ(depthPlan.lastAccessState, ResourceStates::DepthWrite); } +TEST(BuiltinForwardSceneRecorder_Test, RecordsExplicitSceneStepsInCallerOrder) { + BuiltinForwardPipeline pipeline; + pipeline.m_forwardSceneFeatureHost.m_featurePasses.clear(); + + auto beforeOpaqueFeature = std::make_unique( + SceneRenderInjectionPoint::BeforeOpaque, + true, + nullptr, + "BeforeOpaqueFeature"); + auto afterOpaqueFeature = std::make_unique( + SceneRenderInjectionPoint::AfterOpaque, + true, + nullptr, + "AfterOpaqueFeature"); + pipeline.AddForwardSceneFeaturePass(std::move(beforeOpaqueFeature)); + pipeline.AddForwardSceneFeaturePass(std::move(afterOpaqueFeature)); + + RenderGraph graph = {}; + RenderGraphBuilder graphBuilder(graph); + + RenderGraphTextureDesc colorDesc = {}; + colorDesc.width = 320u; + colorDesc.height = 180u; + colorDesc.format = static_cast(Format::R8G8B8A8_UNorm); + colorDesc.textureType = static_cast(XCEngine::RHI::TextureType::Texture2D); + colorDesc.sampleCount = 1u; + + RenderGraphTextureDesc depthDesc = {}; + depthDesc.width = 320u; + depthDesc.height = 180u; + depthDesc.format = static_cast(Format::D24_UNorm_S8_UInt); + depthDesc.textureType = static_cast(XCEngine::RHI::TextureType::Texture2D); + depthDesc.sampleCount = 1u; + + TestResourceView colorView( + ResourceViewType::RenderTarget, + ResourceViewDimension::Texture2D, + Format::R8G8B8A8_UNorm); + TestResourceView depthView( + ResourceViewType::DepthStencil, + ResourceViewDimension::Texture2D, + Format::D24_UNorm_S8_UInt); + + const RenderGraphImportedTextureOptions graphManagedImport = { + ResourceStates::Common, + ResourceStates::Common, + true + }; + const RenderGraphTextureHandle colorTarget = + graphBuilder.ImportTexture("MainColor", colorDesc, &colorView, graphManagedImport); + const RenderGraphTextureHandle depthTarget = + graphBuilder.ImportTexture("MainDepth", depthDesc, &depthView, graphManagedImport); + + RenderSurface surface(320u, 180u); + surface.SetColorAttachment(&colorView); + surface.SetDepthAttachment(&depthView); + + RenderContext renderContext = {}; + RenderSceneData sceneData = {}; + RenderGraphBlackboard blackboard = {}; + bool executionSucceeded = true; + const RenderPipelineStageRenderGraphContext context = { + graphBuilder, + "MainScene", + CameraFrameStage::MainScene, + renderContext, + sceneData, + surface, + nullptr, + nullptr, + ResourceStates::Common, + {}, + { colorTarget }, + depthTarget, + &executionSucceeded, + &blackboard + }; + + BuiltinForwardSceneRecorder recorder(pipeline, context); + ASSERT_TRUE(recorder.RecordInjectionPoint(SceneRenderInjectionPoint::BeforeOpaque)); + ASSERT_TRUE(recorder.RecordScenePhase(ScenePhase::Opaque)); + ASSERT_TRUE(recorder.RecordInjectionPoint(SceneRenderInjectionPoint::AfterOpaque)); + + CompiledRenderGraph compiledGraph = {}; + String errorMessage; + ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage)) + << errorMessage.CStr(); + ASSERT_EQ(compiledGraph.GetPassCount(), 3u); + EXPECT_STREQ( + compiledGraph.GetPassName(0).CStr(), + "MainScene.BeforeOpaque.BeforeOpaqueFeature.0"); + EXPECT_STREQ( + compiledGraph.GetPassName(1).CStr(), + "MainScene.Opaque"); + EXPECT_STREQ( + compiledGraph.GetPassName(2).CStr(), + "MainScene.AfterOpaque.AfterOpaqueFeature.1"); +} + TEST(SceneRenderFeaturePass_Test, SupportsExplicitInjectionPointContract) { TestSceneRenderFeaturePass feature(SceneRenderInjectionPoint::AfterOpaque); diff --git a/tests/scripting/test_mono_script_runtime.cpp b/tests/scripting/test_mono_script_runtime.cpp index 590bd8c3..6702c0c4 100644 --- a/tests/scripting/test_mono_script_runtime.cpp +++ b/tests/scripting/test_mono_script_runtime.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -410,7 +411,25 @@ TEST_F( }; EXPECT_TRUE(recorder->RecordStageRenderGraph(graphContext)); - EXPECT_GT(graph.GetPassCount(), 0u); + + XCEngine::Rendering::CompiledRenderGraph compiledGraph = {}; + XCEngine::Containers::String errorMessage; + ASSERT_TRUE( + XCEngine::Rendering::RenderGraphCompiler::Compile( + graph, + compiledGraph, + &errorMessage)) + << errorMessage.CStr(); + ASSERT_EQ(compiledGraph.GetPassCount(), 3u); + EXPECT_STREQ( + compiledGraph.GetPassName(0).CStr(), + "ManagedMainScene.Opaque"); + EXPECT_STREQ( + compiledGraph.GetPassName(1).CStr(), + "ManagedMainScene.Skybox"); + EXPECT_STREQ( + compiledGraph.GetPassName(2).CStr(), + "ManagedMainScene.Transparent"); recorder->Shutdown(); }