From 5fd474d08dac897446123c1e39ced70cee2237cd Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sat, 18 Apr 2026 16:08:01 +0800 Subject: [PATCH] feat(rendering): add shader-vector fullscreen SRP seam Generalize the native fullscreen pass and descriptor plumbing so managed SRP can request shader-driven fullscreen stages without being locked to the ColorScale path. Keep ColorScale as a convenience descriptor mapped to the builtin color-scale shader, and add native fullscreen factory coverage for the new shader-vector path. --- engine/CMakeLists.txt | 4 +- ...ssPass.h => BuiltinVectorFullscreenPass.h} | 21 ++-- .../Rendering/Planning/FullscreenPassDesc.h | 29 ++++- .../Planning/FullscreenPassFactory.h | 15 ++- ...ss.cpp => BuiltinVectorFullscreenPass.cpp} | 102 ++++++++++++------ .../src/Scripting/Mono/MonoScriptRuntime.cpp | 52 ++++++--- managed/GameScripts/RenderPipelineApiProbe.cs | 18 +++- .../FullscreenPassDescriptor.cs | 22 +++- managed/XCEngine.ScriptCore/InternalCalls.cs | 2 + .../ScriptableRenderContext.cs | 4 + .../integration/post_process_scene/main.cpp | 6 +- tests/Rendering/unit/CMakeLists.txt | 1 + .../unit/test_fullscreen_pass_factory.cpp | 78 ++++++++++++++ tests/scripting/test_mono_script_runtime.cpp | 2 +- 14 files changed, 286 insertions(+), 70 deletions(-) rename engine/include/XCEngine/Rendering/Passes/{BuiltinColorScalePostProcessPass.h => BuiltinVectorFullscreenPass.h} (75%) rename engine/src/Rendering/Passes/{BuiltinColorScalePostProcessPass.cpp => BuiltinVectorFullscreenPass.cpp} (83%) create mode 100644 tests/Rendering/unit/test_fullscreen_pass_factory.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 40f34987..05fc2c73 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -548,7 +548,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinDepthOnlyPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinVectorFullscreenPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinFinalColorPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Features/BuiltinGaussianSplatPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Features/BuiltinVolumetricPass.h @@ -590,7 +590,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinShadowCasterPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPassResources.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinVectorFullscreenPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinFinalColorPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Features/BuiltinGaussianSplatPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Features/BuiltinVolumetricPass.cpp diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinVectorFullscreenPass.h similarity index 75% rename from engine/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h rename to engine/include/XCEngine/Rendering/Passes/BuiltinVectorFullscreenPass.h index f41fb99e..0e7bce8e 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinVectorFullscreenPass.h @@ -20,17 +20,18 @@ class RHIDevice; namespace Rendering { namespace Passes { -class BuiltinColorScalePostProcessPass final : public RenderPass { +class BuiltinVectorFullscreenPass final : public RenderPass { public: struct OwnedDescriptorSet { RHI::RHIDescriptorPool* pool = nullptr; RHI::RHIDescriptorSet* set = nullptr; }; - explicit BuiltinColorScalePostProcessPass( - const Math::Vector4& colorScale = Math::Vector4(0.65f, 0.80f, 1.0f, 1.0f), - Containers::String shaderPath = {}); - ~BuiltinColorScalePostProcessPass() override; + explicit BuiltinVectorFullscreenPass( + const Math::Vector4& vectorPayload = Math::Vector4::One(), + Containers::String shaderPath = {}, + Containers::String preferredPassName = {}); + ~BuiltinVectorFullscreenPass() override; const char* GetName() const override; bool SupportsRenderGraph() const override; @@ -38,12 +39,15 @@ public: bool Execute(const RenderPassContext& context) override; void Shutdown() override; - void SetColorScale(const Math::Vector4& colorScale); - const Math::Vector4& GetColorScale() const; + void SetVectorPayload(const Math::Vector4& vectorPayload); + const Math::Vector4& GetVectorPayload() const; void SetShaderPath(const Containers::String& shaderPath); const Containers::String& GetShaderPath() const; + void SetPreferredPassName(const Containers::String& preferredPassName); + const Containers::String& GetPreferredPassName() const; + private: bool EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface); bool CreateResources(const RenderContext& renderContext, const RenderSurface& surface); @@ -51,7 +55,8 @@ private: void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet); Containers::String m_shaderPath; - Math::Vector4 m_colorScale = Math::Vector4(0.65f, 0.80f, 1.0f, 1.0f); + Containers::String m_preferredPassName; + Math::Vector4 m_vectorPayload = Math::Vector4::One(); RHI::RHIDevice* m_device = nullptr; RHI::RHIType m_backendType = RHI::RHIType::D3D12; RHI::Format m_renderTargetFormat = RHI::Format::Unknown; diff --git a/engine/include/XCEngine/Rendering/Planning/FullscreenPassDesc.h b/engine/include/XCEngine/Rendering/Planning/FullscreenPassDesc.h index edb0cef1..8acb4206 100644 --- a/engine/include/XCEngine/Rendering/Planning/FullscreenPassDesc.h +++ b/engine/include/XCEngine/Rendering/Planning/FullscreenPassDesc.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -9,16 +10,28 @@ namespace XCEngine { namespace Rendering { enum class FullscreenPassType : uint8_t { - ColorScale = 0 + ColorScale = 0, + ShaderVector = 1 }; struct FullscreenColorScaleDesc { Math::Vector4 scale = Math::Vector4::One(); }; +struct FullscreenShaderVectorDesc { + Containers::String shaderPath = {}; + Containers::String passName = {}; + Math::Vector4 vectorPayload = Math::Vector4::One(); + + bool IsValid() const { + return !shaderPath.Empty(); + } +}; + struct FullscreenPassDesc { FullscreenPassType type = FullscreenPassType::ColorScale; FullscreenColorScaleDesc colorScale = {}; + FullscreenShaderVectorDesc shaderVector = {}; static FullscreenPassDesc MakeColorScale(const Math::Vector4& scale) { FullscreenPassDesc desc = {}; @@ -27,10 +40,24 @@ struct FullscreenPassDesc { return desc; } + static FullscreenPassDesc MakeShaderVector( + const Containers::String& shaderPath, + const Math::Vector4& vectorPayload, + const Containers::String& passName = {}) { + FullscreenPassDesc desc = {}; + desc.type = FullscreenPassType::ShaderVector; + desc.shaderVector.shaderPath = shaderPath; + desc.shaderVector.passName = passName; + desc.shaderVector.vectorPayload = vectorPayload; + return desc; + } + bool IsValid() const { switch (type) { case FullscreenPassType::ColorScale: return true; + case FullscreenPassType::ShaderVector: + return shaderVector.IsValid(); default: return false; } diff --git a/engine/include/XCEngine/Rendering/Planning/FullscreenPassFactory.h b/engine/include/XCEngine/Rendering/Planning/FullscreenPassFactory.h index c2b5846c..8689b91f 100644 --- a/engine/include/XCEngine/Rendering/Planning/FullscreenPassFactory.h +++ b/engine/include/XCEngine/Rendering/Planning/FullscreenPassFactory.h @@ -1,8 +1,9 @@ #pragma once -#include +#include #include #include +#include #include @@ -23,8 +24,16 @@ inline std::unique_ptr BuildFullscreenPassSequence( switch (passDesc.type) { case FullscreenPassType::ColorScale: - sequence->AddPass(std::make_unique( - passDesc.colorScale.scale)); + sequence->AddPass(std::make_unique( + passDesc.colorScale.scale, + Resources::GetBuiltinColorScalePostProcessShaderPath(), + Containers::String("ColorScale"))); + break; + case FullscreenPassType::ShaderVector: + sequence->AddPass(std::make_unique( + passDesc.shaderVector.vectorPayload, + passDesc.shaderVector.shaderPath, + passDesc.shaderVector.passName)); break; default: break; diff --git a/engine/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp b/engine/src/Rendering/Passes/BuiltinVectorFullscreenPass.cpp similarity index 83% rename from engine/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp rename to engine/src/Rendering/Passes/BuiltinVectorFullscreenPass.cpp index c99eba23..0e2bae1d 100644 --- a/engine/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp +++ b/engine/src/Rendering/Passes/BuiltinVectorFullscreenPass.cpp @@ -1,4 +1,4 @@ -#include "Rendering/Passes/BuiltinColorScalePostProcessPass.h" +#include "Rendering/Passes/BuiltinVectorFullscreenPass.h" #include "Core/Asset/ResourceManager.h" #include "Debug/Logger.h" @@ -25,15 +25,32 @@ namespace Passes { namespace { struct PostProcessConstants { - Math::Vector4 colorScale = Math::Vector4::One(); + Math::Vector4 vectorPayload = Math::Vector4::One(); }; const Resources::ShaderPass* FindCompatiblePass( const Resources::Shader& shader, - Resources::ShaderBackend backend) { + Resources::ShaderBackend backend, + const Containers::String& preferredPassName) { + if (!preferredPassName.Empty()) { + const Resources::ShaderPass* preferredPass = + shader.FindPass(preferredPassName); + if (preferredPass != nullptr && + ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants( + shader, + preferredPass->name, + backend)) { + return preferredPass; + } + return nullptr; + } + const Resources::ShaderPass* colorScalePass = shader.FindPass("ColorScale"); if (colorScalePass != nullptr && - ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(shader, colorScalePass->name, backend)) { + ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants( + shader, + colorScalePass->name, + backend)) { return colorScalePass; } @@ -107,36 +124,38 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( } // namespace -BuiltinColorScalePostProcessPass::BuiltinColorScalePostProcessPass( - const Math::Vector4& colorScale, - Containers::String shaderPath) +BuiltinVectorFullscreenPass::BuiltinVectorFullscreenPass( + const Math::Vector4& vectorPayload, + Containers::String shaderPath, + Containers::String preferredPassName) : m_shaderPath(std::move(shaderPath)) - , m_colorScale(colorScale) { + , m_preferredPassName(std::move(preferredPassName)) + , m_vectorPayload(vectorPayload) { if (m_shaderPath.Empty()) { m_shaderPath = Resources::GetBuiltinColorScalePostProcessShaderPath(); } } -BuiltinColorScalePostProcessPass::~BuiltinColorScalePostProcessPass() { +BuiltinVectorFullscreenPass::~BuiltinVectorFullscreenPass() { Shutdown(); } -const char* BuiltinColorScalePostProcessPass::GetName() const { - return "BuiltinColorScalePostProcessPass"; +const char* BuiltinVectorFullscreenPass::GetName() const { + return "BuiltinVectorFullscreenPass"; } -bool BuiltinColorScalePostProcessPass::SupportsRenderGraph() const { +bool BuiltinVectorFullscreenPass::SupportsRenderGraph() const { return true; } -bool BuiltinColorScalePostProcessPass::RecordRenderGraph( +bool BuiltinVectorFullscreenPass::RecordRenderGraph( const RenderPassRenderGraphContext& context) { return RecordSourceColorFullscreenRasterPass( *this, context); } -bool BuiltinColorScalePostProcessPass::Execute(const RenderPassContext& context) { +bool BuiltinVectorFullscreenPass::Execute(const RenderPassContext& context) { if (!context.renderContext.IsValid() || context.sourceSurface == nullptr || context.sourceColorView == nullptr) { @@ -163,7 +182,7 @@ bool BuiltinColorScalePostProcessPass::Execute(const RenderPassContext& context) } PostProcessConstants constants = {}; - constants.colorScale = m_colorScale; + constants.vectorPayload = m_vectorPayload; m_constantsSet.set->WriteConstant(0, &constants, sizeof(constants)); if (m_boundSourceColorView != context.sourceColorView) { @@ -237,19 +256,19 @@ bool BuiltinColorScalePostProcessPass::Execute(const RenderPassContext& context) return true; } -void BuiltinColorScalePostProcessPass::Shutdown() { +void BuiltinVectorFullscreenPass::Shutdown() { DestroyResources(); } -void BuiltinColorScalePostProcessPass::SetColorScale(const Math::Vector4& colorScale) { - m_colorScale = colorScale; +void BuiltinVectorFullscreenPass::SetVectorPayload(const Math::Vector4& vectorPayload) { + m_vectorPayload = vectorPayload; } -const Math::Vector4& BuiltinColorScalePostProcessPass::GetColorScale() const { - return m_colorScale; +const Math::Vector4& BuiltinVectorFullscreenPass::GetVectorPayload() const { + return m_vectorPayload; } -void BuiltinColorScalePostProcessPass::SetShaderPath(const Containers::String& shaderPath) { +void BuiltinVectorFullscreenPass::SetShaderPath(const Containers::String& shaderPath) { if (m_shaderPath == shaderPath) { return; } @@ -258,11 +277,25 @@ void BuiltinColorScalePostProcessPass::SetShaderPath(const Containers::String& s m_shaderPath = shaderPath; } -const Containers::String& BuiltinColorScalePostProcessPass::GetShaderPath() const { +const Containers::String& BuiltinVectorFullscreenPass::GetShaderPath() const { return m_shaderPath; } -bool BuiltinColorScalePostProcessPass::EnsureInitialized( +void BuiltinVectorFullscreenPass::SetPreferredPassName( + const Containers::String& preferredPassName) { + if (m_preferredPassName == preferredPassName) { + return; + } + + DestroyResources(); + m_preferredPassName = preferredPassName; +} + +const Containers::String& BuiltinVectorFullscreenPass::GetPreferredPassName() const { + return m_preferredPassName; +} + +bool BuiltinVectorFullscreenPass::EnsureInitialized( const RenderContext& renderContext, const RenderSurface& surface) { const RHI::Format renderTargetFormat = @@ -289,7 +322,7 @@ bool BuiltinColorScalePostProcessPass::EnsureInitialized( return CreateResources(renderContext, surface); } -bool BuiltinColorScalePostProcessPass::CreateResources( +bool BuiltinVectorFullscreenPass::CreateResources( const RenderContext& renderContext, const RenderSurface& surface) { if (!renderContext.IsValid()) { @@ -300,14 +333,14 @@ bool BuiltinColorScalePostProcessPass::CreateResources( if (!::XCEngine::Rendering::Internal::TryResolveSingleColorAttachmentFormat(surface, renderTargetFormat)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, - "BuiltinColorScalePostProcessPass requires exactly one valid destination color attachment"); + "BuiltinVectorFullscreenPass requires exactly one valid destination color attachment"); return false; } if (m_shaderPath.Empty()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, - "BuiltinColorScalePostProcessPass requires a shader path before resource creation"); + "BuiltinVectorFullscreenPass requires a shader path before resource creation"); return false; } @@ -315,17 +348,20 @@ bool BuiltinColorScalePostProcessPass::CreateResources( if (!m_shader.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, - "BuiltinColorScalePostProcessPass failed to load configured post-process shader resource"); + "BuiltinVectorFullscreenPass failed to load configured fullscreen shader resource"); DestroyResources(); return false; } const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(renderContext.backendType); - const Resources::ShaderPass* colorScalePass = FindCompatiblePass(*m_shader.Get(), backend); - if (colorScalePass == nullptr) { + const Resources::ShaderPass* selectedPass = + FindCompatiblePass(*m_shader.Get(), backend, m_preferredPassName); + if (selectedPass == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, - "BuiltinColorScalePostProcessPass could not resolve a valid ColorScale shader pass"); + m_preferredPassName.Empty() + ? "BuiltinVectorFullscreenPass could not resolve a compatible fullscreen shader pass" + : "BuiltinVectorFullscreenPass could not resolve the configured fullscreen shader pass"); DestroyResources(); return false; } @@ -446,7 +482,7 @@ bool BuiltinColorScalePostProcessPass::CreateResources( m_backendType, m_pipelineLayout, *m_shader.Get(), - colorScalePass->name, + selectedPass->name, surface)); if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) { DestroyResources(); @@ -456,7 +492,7 @@ bool BuiltinColorScalePostProcessPass::CreateResources( return true; } -void BuiltinColorScalePostProcessPass::DestroyResources() { +void BuiltinVectorFullscreenPass::DestroyResources() { m_boundSourceColorView = nullptr; if (m_pipelineState != nullptr) { @@ -489,7 +525,7 @@ void BuiltinColorScalePostProcessPass::DestroyResources() { m_renderTargetSampleQuality = 0u; } -void BuiltinColorScalePostProcessPass::DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet) { +void BuiltinVectorFullscreenPass::DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet) { if (descriptorSet.set != nullptr) { descriptorSet.set->Shutdown(); delete descriptorSet.set; diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index feb40826..a1d19946 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -12,11 +12,12 @@ #include "Physics/PhysicsWorld.h" #include "Rendering/Execution/CameraFramePlan.h" #include "Rendering/Internal/RenderPipelineFactory.h" -#include "Rendering/Passes/BuiltinColorScalePostProcessPass.h" +#include "Rendering/Passes/BuiltinVectorFullscreenPass.h" #include "Rendering/Planning/FullscreenPassDesc.h" #include "Rendering/Pipelines/NativeSceneRecorder.h" #include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h" #include "Rendering/RenderPipelineStageGraphContract.h" +#include "Resources/BuiltinResources.h" #include "Scene/Scene.h" #include "Scripting/ScriptComponent.h" #include "Scripting/ScriptEngine.h" @@ -332,21 +333,30 @@ Rendering::RenderPass* ConfigureManagedFullscreenPass( } std::unique_ptr& passSlot = passPool[passIndex]; + Rendering::Passes::BuiltinVectorFullscreenPass* fullscreenPass = + dynamic_cast( + passSlot.get()); + if (fullscreenPass == nullptr) { + passSlot = + std::make_unique(); + fullscreenPass = + static_cast( + passSlot.get()); + } + switch (passDesc.type) { case Rendering::FullscreenPassType::ColorScale: { - Rendering::Passes::BuiltinColorScalePostProcessPass* colorScalePass = - dynamic_cast( - passSlot.get()); - if (colorScalePass == nullptr) { - passSlot = - std::make_unique(); - colorScalePass = - static_cast( - passSlot.get()); - } - - colorScalePass->SetColorScale(passDesc.colorScale.scale); - return colorScalePass; + fullscreenPass->SetShaderPath( + Resources::GetBuiltinColorScalePostProcessShaderPath()); + fullscreenPass->SetPreferredPassName("ColorScale"); + fullscreenPass->SetVectorPayload(passDesc.colorScale.scale); + return fullscreenPass; + } + case Rendering::FullscreenPassType::ShaderVector: { + fullscreenPass->SetShaderPath(passDesc.shaderVector.shaderPath); + fullscreenPass->SetPreferredPassName(passDesc.shaderVector.passName); + fullscreenPass->SetVectorPayload(passDesc.shaderVector.vectorPayload); + return fullscreenPass; } default: return nullptr; @@ -2652,6 +2662,8 @@ mono_bool InternalCall_Rendering_ScriptableRenderContext_RecordFullscreenPass( uint64_t nativeHandle, int32_t passType, + MonoString* shaderPath, + MonoString* passName, XCEngine::Math::Vector4* vectorPayload) { ManagedScriptableRenderContextState* const state = FindManagedScriptableRenderContextState(nativeHandle); @@ -2670,6 +2682,18 @@ InternalCall_Rendering_ScriptableRenderContext_RecordFullscreenPass( passDesc = Rendering::FullscreenPassDesc::MakeColorScale(*vectorPayload); break; + case Rendering::FullscreenPassType::ShaderVector: + if (vectorPayload == nullptr) { + return 0; + } + passDesc = Rendering::FullscreenPassDesc::MakeShaderVector( + Containers::String(MonoStringToUtf8(shaderPath).c_str()), + *vectorPayload, + Containers::String(MonoStringToUtf8(passName).c_str())); + if (!passDesc.IsValid()) { + return 0; + } + break; default: return 0; } diff --git a/managed/GameScripts/RenderPipelineApiProbe.cs b/managed/GameScripts/RenderPipelineApiProbe.cs index 129a02b1..7d12c5e6 100644 --- a/managed/GameScripts/RenderPipelineApiProbe.cs +++ b/managed/GameScripts/RenderPipelineApiProbe.cs @@ -2,6 +2,12 @@ using XCEngine; namespace Gameplay { + internal static class BuiltinShaderPaths + { + public const string ColorScalePostProcess = + "builtin://shaders/color-scale-post-process"; + } + public sealed class LegacyRenderPipelineApiProbeAsset : RenderPipelineAsset { } @@ -142,11 +148,15 @@ namespace Gameplay return context != null && context.stage == CameraFrameStage.PostProcess && context.RecordFullscreenPass( - FullscreenPassDescriptor.CreateColorScale( - new Vector4(1.10f, 0.95f, 0.90f, 1.0f))) && + FullscreenPassDescriptor.CreateShaderVector( + BuiltinShaderPaths.ColorScalePostProcess, + new Vector4(1.10f, 0.95f, 0.90f, 1.0f), + "ColorScale")) && context.RecordFullscreenPass( - FullscreenPassDescriptor.CreateColorScale( - new Vector4(0.95f, 1.05f, 1.10f, 1.0f))); + FullscreenPassDescriptor.CreateShaderVector( + BuiltinShaderPaths.ColorScalePostProcess, + new Vector4(0.95f, 1.05f, 1.10f, 1.0f), + "ColorScale")); } } diff --git a/managed/XCEngine.ScriptCore/FullscreenPassDescriptor.cs b/managed/XCEngine.ScriptCore/FullscreenPassDescriptor.cs index d969ab26..bbd13d7f 100644 --- a/managed/XCEngine.ScriptCore/FullscreenPassDescriptor.cs +++ b/managed/XCEngine.ScriptCore/FullscreenPassDescriptor.cs @@ -2,12 +2,15 @@ namespace XCEngine { public enum FullscreenPassType { - ColorScale = 0 + ColorScale = 0, + ShaderVector = 1 } public struct FullscreenPassDescriptor { public FullscreenPassType type; + public string shaderPath; + public string passName; public Vector4 vectorPayload; public static FullscreenPassDescriptor CreateColorScale( @@ -15,16 +18,33 @@ namespace XCEngine { FullscreenPassDescriptor descriptor = new FullscreenPassDescriptor(); descriptor.type = FullscreenPassType.ColorScale; + descriptor.shaderPath = string.Empty; + descriptor.passName = string.Empty; descriptor.vectorPayload = colorScale; return descriptor; } + public static FullscreenPassDescriptor CreateShaderVector( + string shaderPath, + Vector4 vectorPayload, + string passName = null) + { + FullscreenPassDescriptor descriptor = new FullscreenPassDescriptor(); + descriptor.type = FullscreenPassType.ShaderVector; + descriptor.shaderPath = shaderPath ?? string.Empty; + descriptor.passName = passName ?? string.Empty; + descriptor.vectorPayload = vectorPayload; + return descriptor; + } + public bool IsValid() { switch (type) { case FullscreenPassType.ColorScale: return true; + case FullscreenPassType.ShaderVector: + return !string.IsNullOrEmpty(shaderPath); default: return false; } diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index e0e2f29d..c29e3bc2 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -408,6 +408,8 @@ namespace XCEngine Rendering_ScriptableRenderContext_RecordFullscreenPass( ulong nativeHandle, int passType, + string shaderPath, + string passName, ref Vector4 vectorPayload); [MethodImpl(MethodImplOptions.InternalCall)] diff --git a/managed/XCEngine.ScriptCore/ScriptableRenderContext.cs b/managed/XCEngine.ScriptCore/ScriptableRenderContext.cs index 0900c613..2f28d3c9 100644 --- a/managed/XCEngine.ScriptCore/ScriptableRenderContext.cs +++ b/managed/XCEngine.ScriptCore/ScriptableRenderContext.cs @@ -54,11 +54,15 @@ namespace XCEngine nameof(pass)); } + string shaderPath = pass.shaderPath ?? string.Empty; + string passName = pass.passName ?? string.Empty; Vector4 vectorPayload = pass.vectorPayload; return InternalCalls .Rendering_ScriptableRenderContext_RecordFullscreenPass( m_nativeHandle, (int)pass.type, + shaderPath, + passName, ref vectorPayload); } } diff --git a/tests/Rendering/integration/post_process_scene/main.cpp b/tests/Rendering/integration/post_process_scene/main.cpp index 35980332..a997dc8e 100644 --- a/tests/Rendering/integration/post_process_scene/main.cpp +++ b/tests/Rendering/integration/post_process_scene/main.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -165,9 +165,9 @@ void PostProcessSceneTest::SetUp() { mMesh = CreateQuadMesh(); mTexture = CreateCheckerTexture(); mMaterial = CreateQuadMaterial(mTexture); - mPostProcessPasses.AddPass(std::make_unique( + mPostProcessPasses.AddPass(std::make_unique( Vector4(1.0f, 0.75f, 0.75f, 1.0f))); - mPostProcessPasses.AddPass(std::make_unique( + mPostProcessPasses.AddPass(std::make_unique( Vector4(0.55f, 0.95f, 1.0f, 1.0f))); BuildScene(); diff --git a/tests/Rendering/unit/CMakeLists.txt b/tests/Rendering/unit/CMakeLists.txt index 751b1280..fcd0f13c 100644 --- a/tests/Rendering/unit/CMakeLists.txt +++ b/tests/Rendering/unit/CMakeLists.txt @@ -18,6 +18,7 @@ set(RENDERING_UNIT_TEST_SOURCES test_render_resource_cache.cpp test_render_graph.cpp test_render_graph_recording_context.cpp + test_fullscreen_pass_factory.cpp ) add_executable(rendering_unit_tests ${RENDERING_UNIT_TEST_SOURCES}) diff --git a/tests/Rendering/unit/test_fullscreen_pass_factory.cpp b/tests/Rendering/unit/test_fullscreen_pass_factory.cpp new file mode 100644 index 00000000..b466589f --- /dev/null +++ b/tests/Rendering/unit/test_fullscreen_pass_factory.cpp @@ -0,0 +1,78 @@ +#include + +#include +#include +#include + +namespace { + +void ExpectVector4Eq( + const XCEngine::Math::Vector4& actual, + const XCEngine::Math::Vector4& expected) { + EXPECT_FLOAT_EQ(actual.x, expected.x); + EXPECT_FLOAT_EQ(actual.y, expected.y); + EXPECT_FLOAT_EQ(actual.z, expected.z); + EXPECT_FLOAT_EQ(actual.w, expected.w); +} + +} // namespace + +TEST(FullscreenPassFactory_Test, BuildsColorScaleDescriptorIntoBuiltinVectorFullscreenPass) { + const XCEngine::Math::Vector4 payload(1.1f, 0.95f, 0.9f, 1.0f); + const XCEngine::Rendering::FullscreenPassStack fullscreenPasses = { + XCEngine::Rendering::FullscreenPassDesc::MakeColorScale(payload) + }; + + std::unique_ptr sequence = + XCEngine::Rendering::BuildFullscreenPassSequence(fullscreenPasses); + ASSERT_NE(sequence, nullptr); + ASSERT_EQ(sequence->GetPassCount(), 1u); + + auto* pass = dynamic_cast( + sequence->GetPass(0u)); + ASSERT_NE(pass, nullptr); + ExpectVector4Eq(pass->GetVectorPayload(), payload); + EXPECT_EQ( + pass->GetShaderPath(), + XCEngine::Resources::GetBuiltinColorScalePostProcessShaderPath()); + EXPECT_EQ( + pass->GetPreferredPassName(), + XCEngine::Containers::String("ColorScale")); +} + +TEST(FullscreenPassFactory_Test, BuildsShaderVectorDescriptorIntoBuiltinVectorFullscreenPass) { + const XCEngine::Containers::String shaderPath( + "builtin://shaders/color-scale-post-process"); + const XCEngine::Containers::String passName("ColorScale"); + const XCEngine::Math::Vector4 payload(0.95f, 1.05f, 1.1f, 1.0f); + const XCEngine::Rendering::FullscreenPassStack fullscreenPasses = { + XCEngine::Rendering::FullscreenPassDesc::MakeShaderVector( + shaderPath, + payload, + passName) + }; + + std::unique_ptr sequence = + XCEngine::Rendering::BuildFullscreenPassSequence(fullscreenPasses); + ASSERT_NE(sequence, nullptr); + ASSERT_EQ(sequence->GetPassCount(), 1u); + + auto* pass = dynamic_cast( + sequence->GetPass(0u)); + ASSERT_NE(pass, nullptr); + ExpectVector4Eq(pass->GetVectorPayload(), payload); + EXPECT_EQ(pass->GetShaderPath(), shaderPath); + EXPECT_EQ(pass->GetPreferredPassName(), passName); +} + +TEST(FullscreenPassFactory_Test, IgnoresInvalidShaderVectorDescriptor) { + XCEngine::Rendering::FullscreenPassStack fullscreenPasses = { + XCEngine::Rendering::FullscreenPassDesc::MakeShaderVector( + XCEngine::Containers::String(), + XCEngine::Math::Vector4::One()) + }; + + std::unique_ptr sequence = + XCEngine::Rendering::BuildFullscreenPassSequence(fullscreenPasses); + EXPECT_EQ(sequence, nullptr); +} diff --git a/tests/scripting/test_mono_script_runtime.cpp b/tests/scripting/test_mono_script_runtime.cpp index 4e34d77c..7d1b8cce 100644 --- a/tests/scripting/test_mono_script_runtime.cpp +++ b/tests/scripting/test_mono_script_runtime.cpp @@ -679,7 +679,7 @@ TEST_F( TEST_F( MonoScriptRuntimeTest, - ManagedStageRecorderRecordsPostProcessThroughScriptableRenderContext) { + ManagedStageRecorderRecordsShaderVectorPostProcessThroughScriptableRenderContext) { const auto bridge = XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge(); ASSERT_NE(bridge, nullptr);