diff --git a/docs/plan/SRP_RenderStateBlock计划_2026-04-20.md b/docs/plan/SRP_RenderStateBlock计划_2026-04-20.md new file mode 100644 index 00000000..5115c6d0 --- /dev/null +++ b/docs/plan/SRP_RenderStateBlock计划_2026-04-20.md @@ -0,0 +1,144 @@ +# SRP RenderStateBlock 计划 2026-04-20 + +## 1. 阶段目标 + +把当前 SRP scene draw authoring 的最后一块关键能力补齐: + +1. managed 侧可以像 Unity `RenderObjects` 一样为 scene draw 指定 `RenderStateBlock` +2. native scene draw 真正消费这些覆盖状态,而不是继续只能靠材质自身状态 +3. `RenderObjectsRendererFeature` 形成最小闭环: + - filtering / sorting + - override material + - shader pass + - render state override + +这一阶段收口后,当前这条 “scene draw primitive” 主线就基本完整,可以转去做更高层的 SRP/URP 组织。 + +## 2. 现状问题 + +当前已经有: + +1. `RendererListDesc` +2. `DrawingSettings` +3. `overrideMaterial` +4. `shaderPassName` +5. `DrawSkybox / DrawOpaqueRenderers / DrawTransparentRenderers` + +但还缺: + +1. `RenderStateBlock` +2. depth/stencil override 的 managed authoring 面 +3. native scene draw 对 override render state 的正式消费 + +结果就是: + +1. 现在能决定 “画谁” +2. 现在能决定 “用什么材质 / 什么 pass 画” +3. 但还不能决定 “以什么覆盖状态画” + +这会让很多 URP 本来应该在包层做的行为,继续被逼回材质或 C++ 特判。 + +## 3. 本阶段范围 + +本阶段只做最小可用的 Unity 风格 `RenderStateBlock`: + +1. `RenderStateMask` +2. `CompareFunction` +3. `StencilOp` +4. `DepthState` +5. `StencilFaceState` +6. `StencilState` +7. `RenderStateBlock` + +优先支持: + +1. depth write +2. depth compare function +3. stencil enable / read-write mask / reference +4. stencil front/back compare + op + +本阶段暂不做: + +1. blend state override +2. raster state override +3. color write mask override +4. 更高层 renderer preset / inspector authoring + +## 4. 实施步骤 + +### Step 1:补齐 managed API 面 + +新增 core rendering 类型: + +1. `CompareFunction` +2. `StencilOp` +3. `RenderStateMask` +4. `DepthState` +5. `StencilFaceState` +6. `StencilState` +7. `RenderStateBlock` + +并把它们挂进 `DrawingSettings`。 + +### Step 2:打通 Mono bridge + +让 `ScriptableRenderContext.DrawRenderers(...)` 可以把 `RenderStateBlock` 从 managed 传到 native。 + +要求: + +1. 结构体布局稳定 +2. 枚举值与 native 对齐 +3. 默认值不改现有行为 + +### Step 3:native scene draw 消费 override render state + +在 `BuiltinForwardPipeline` scene draw 路径里: + +1. 先解析材质 / shader pass 自身 render state +2. 再叠加 `RenderStateBlock` +3. pipeline key 与动态 stencil ref 都基于最终 resolved state + +这一步要保证: + +1. 不影响未设置 override 的现有行为 +2. stencil reference 仍然走动态状态,不污染 pipeline key + +### Step 4:接入 URP `RenderObjectsRendererFeature` + +让 URP 包层真的能 author 这套状态,而不是只有 API 没有 feature 消费方。 + +### Step 5:编译 / 冒烟 / 阶段提交 + +每个阶段完成后都执行: + +1. 编译 `XCEditor` +2. 运行旧版 `editor/bin/Debug/XCEngine.exe` 至少 10 秒 +3. 检查 `editor.log` +4. 规范提交并推送 + +## 5. 验收标准 + +本阶段收口后应满足: + +1. `DrawingSettings` 可以表达最小可用 `RenderStateBlock` +2. `RenderObjectsRendererFeature` 可以 author render state override +3. native scene draw 真正按 override state 绘制 +4. 默认场景绘制行为不回退 +5. `XCEditor` 编译通过 +6. 旧 editor 冒烟通过 + +## 6. 阶段完成后的意义 + +这一步完成后,当前 SRP 主线就从: + +`只能控制 draw list + material/pass` + +推进到: + +`已经具备最小完整的 scene draw authoring contract` + +这样下一阶段就可以更自然地进入: + +1. 更高层的 URP renderer data 组织 +2. shadow / post process / special effects 逐步包层化 +3. scene draw 与 render graph 的更正式结合 diff --git a/engine/include/XCEngine/Rendering/Execution/DrawSettings.h b/engine/include/XCEngine/Rendering/Execution/DrawSettings.h index 974afdf4..57b8d811 100644 --- a/engine/include/XCEngine/Rendering/Execution/DrawSettings.h +++ b/engine/include/XCEngine/Rendering/Execution/DrawSettings.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -14,6 +15,7 @@ struct DrawSettings { RendererListDesc rendererListDesc = {}; Resources::ResourceHandle overrideMaterial = {}; Containers::String shaderPassName = {}; + RenderStateBlock renderStateBlock = {}; bool HasOverrideMaterial() const { return overrideMaterial.IsValid(); @@ -22,6 +24,10 @@ struct DrawSettings { bool HasShaderPassName() const { return !shaderPassName.Empty(); } + + bool HasRenderStateOverrides() const { + return renderStateBlock.HasOverrides(); + } }; } // namespace Rendering diff --git a/engine/include/XCEngine/Rendering/Execution/RenderStateBlock.h b/engine/include/XCEngine/Rendering/Execution/RenderStateBlock.h new file mode 100644 index 00000000..3663b81d --- /dev/null +++ b/engine/include/XCEngine/Rendering/Execution/RenderStateBlock.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include + +namespace XCEngine { +namespace Rendering { + +enum class RenderStateMask : Core::uint32 { + None = 0u, + Depth = 1u << 0u, + Stencil = 1u << 1u +}; + +inline Core::uint32 ToRenderStateMaskBits(RenderStateMask mask) { + return static_cast(mask); +} + +inline bool HasRenderStateMask( + RenderStateMask value, + RenderStateMask flag) { + return (ToRenderStateMaskBits(value) & + ToRenderStateMaskBits(flag)) != 0u; +} + +struct DepthState { + bool writeEnabled = true; + Resources::MaterialComparisonFunc compareFunction = + Resources::MaterialComparisonFunc::Less; + + bool operator==(const DepthState& other) const { + return writeEnabled == other.writeEnabled && + compareFunction == other.compareFunction; + } +}; + +struct StencilFaceState { + Resources::MaterialStencilOp failOp = + Resources::MaterialStencilOp::Keep; + Resources::MaterialStencilOp passOp = + Resources::MaterialStencilOp::Keep; + Resources::MaterialStencilOp depthFailOp = + Resources::MaterialStencilOp::Keep; + Resources::MaterialComparisonFunc compareFunction = + Resources::MaterialComparisonFunc::Always; + + bool operator==(const StencilFaceState& other) const { + return failOp == other.failOp && + passOp == other.passOp && + depthFailOp == other.depthFailOp && + compareFunction == other.compareFunction; + } +}; + +struct StencilState { + bool enabled = false; + Core::uint8 readMask = 0xFFu; + Core::uint8 writeMask = 0xFFu; + StencilFaceState frontFace = {}; + StencilFaceState backFace = {}; + + bool operator==(const StencilState& other) const { + return enabled == other.enabled && + readMask == other.readMask && + writeMask == other.writeMask && + frontFace == other.frontFace && + backFace == other.backFace; + } +}; + +struct RenderStateBlock { + RenderStateMask mask = RenderStateMask::None; + DepthState depthState = {}; + StencilState stencilState = {}; + Core::uint8 stencilReference = 0u; + + bool HasDepthOverride() const { + return HasRenderStateMask(mask, RenderStateMask::Depth); + } + + bool HasStencilOverride() const { + return HasRenderStateMask(mask, RenderStateMask::Stencil); + } + + bool HasOverrides() const { + return mask != RenderStateMask::None; + } +}; + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/Materials/RenderMaterialStateUtils.h b/engine/include/XCEngine/Rendering/Materials/RenderMaterialStateUtils.h index c05e7133..c838b497 100644 --- a/engine/include/XCEngine/Rendering/Materials/RenderMaterialStateUtils.h +++ b/engine/include/XCEngine/Rendering/Materials/RenderMaterialStateUtils.h @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -140,6 +141,51 @@ inline Resources::MaterialRenderState ResolveEffectiveRenderState( return renderState; } +inline Resources::MaterialRenderState ApplyRenderStateBlock( + Resources::MaterialRenderState renderState, + const RenderStateBlock* renderStateBlock) { + if (renderStateBlock == nullptr || + !renderStateBlock->HasOverrides()) { + return renderState; + } + + if (renderStateBlock->HasDepthOverride()) { + renderState.depthWriteEnable = + renderStateBlock->depthState.writeEnabled; + renderState.depthFunc = + renderStateBlock->depthState.compareFunction; + } + + if (renderStateBlock->HasStencilOverride()) { + renderState.stencil.enabled = + renderStateBlock->stencilState.enabled; + renderState.stencil.readMask = + renderStateBlock->stencilState.readMask; + renderState.stencil.writeMask = + renderStateBlock->stencilState.writeMask; + renderState.stencil.reference = + renderStateBlock->stencilReference; + renderState.stencil.front.failOp = + renderStateBlock->stencilState.frontFace.failOp; + renderState.stencil.front.passOp = + renderStateBlock->stencilState.frontFace.passOp; + renderState.stencil.front.depthFailOp = + renderStateBlock->stencilState.frontFace.depthFailOp; + renderState.stencil.front.func = + renderStateBlock->stencilState.frontFace.compareFunction; + renderState.stencil.back.failOp = + renderStateBlock->stencilState.backFace.failOp; + renderState.stencil.back.passOp = + renderStateBlock->stencilState.backFace.passOp; + renderState.stencil.back.depthFailOp = + renderStateBlock->stencilState.backFace.depthFailOp; + renderState.stencil.back.func = + renderStateBlock->stencilState.backFace.compareFunction; + } + + return renderState; +} + inline RHI::RasterizerDesc BuildRasterizerState(const Resources::MaterialRenderState& renderState) { RHI::RasterizerDesc desc = {}; desc.fillMode = static_cast(RHI::FillMode::Solid); diff --git a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h index c0148c65..7fc9f249 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h +++ b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h @@ -311,7 +311,8 @@ private: const RenderSurface& surface, const RenderSceneData& sceneData, const Resources::Material* material, - const BuiltinMaterialPass* preferredPass); + const BuiltinMaterialPass* preferredPass, + const RenderStateBlock* renderStateBlock); RHI::RHIPipelineState* GetOrCreateSkyboxPipelineState( const RenderContext& context, const RenderSurface& surface); @@ -368,7 +369,8 @@ private: const FrameExecutionContext& executionContext, const VisibleRenderItem& visibleItem, const Resources::Material* material, - const BuiltinMaterialPass* preferredPass); + const BuiltinMaterialPass* preferredPass, + const RenderStateBlock* renderStateBlock); bool EnsureSkyboxResources(const RenderContext& context); bool CreateSkyboxResources(const RenderContext& context); void DestroySkyboxResources(); diff --git a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSurface.cpp b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSurface.cpp index 79e9288a..ad318ba3 100644 --- a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSurface.cpp +++ b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSurface.cpp @@ -62,13 +62,13 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( const Resources::ShaderPass& shaderPass, const Containers::String& passName, const Resources::ShaderKeywordSet& keywordSet, - const Resources::Material* material, + const Resources::MaterialRenderState& renderState, const RenderSurface& surface) { RHI::GraphicsPipelineDesc pipelineDesc = {}; pipelineDesc.pipelineLayout = pipelineLayout; pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); ::XCEngine::Rendering::Internal::ApplySurfacePropertiesToGraphicsPipelineDesc(surface, pipelineDesc); - ApplyResolvedRenderState(&shaderPass, material, pipelineDesc); + ApplyRenderState(renderState, pipelineDesc); pipelineDesc.inputLayout = BuiltinForwardPipeline::BuildInputLayout(); @@ -268,7 +268,8 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState( const RenderSurface& surface, const RenderSceneData& sceneData, const Resources::Material* material, - const BuiltinMaterialPass* preferredPass) { + const BuiltinMaterialPass* preferredPass, + const RenderStateBlock* renderStateBlock) { const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material); const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass( @@ -288,8 +289,15 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState( } PipelineStateKey pipelineKey = {}; + const Resources::MaterialRenderState effectiveRenderState = + ApplyRenderStateBlock( + ResolveEffectiveRenderState( + resolvedShaderPass.pass, + material), + renderStateBlock); pipelineKey.renderState = - BuildStaticPipelineRenderStateKey(ResolveEffectiveRenderState(resolvedShaderPass.pass, material)); + BuildStaticPipelineRenderStateKey( + effectiveRenderState); pipelineKey.shader = resolvedShaderPass.shader; pipelineKey.passName = resolvedShaderPass.passName; pipelineKey.keywordSignature = ::XCEngine::Rendering::Internal::BuildShaderKeywordSignature(keywordSet); @@ -315,7 +323,7 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState( *resolvedShaderPass.pass, resolvedShaderPass.passName, keywordSet, - material, + effectiveRenderState, surface); RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc); if (pipelineState == nullptr || !pipelineState->IsValid()) { @@ -448,7 +456,8 @@ bool BuiltinForwardPipeline::DrawVisibleItem( const FrameExecutionContext& executionContext, const VisibleRenderItem& visibleItem, const Resources::Material* material, - const BuiltinMaterialPass* preferredPass) { + const BuiltinMaterialPass* preferredPass, + const RenderStateBlock* renderStateBlock) { const RenderContext& context = executionContext.renderContext; const RenderSceneData& sceneData = executionContext.sceneData; @@ -490,7 +499,11 @@ bool BuiltinForwardPipeline::DrawVisibleItem( return false; } const Resources::MaterialRenderState effectiveRenderState = - ResolveEffectiveRenderState(resolvedShaderPass.pass, material); + ApplyRenderStateBlock( + ResolveEffectiveRenderState( + resolvedShaderPass.pass, + material), + renderStateBlock); PassLayoutKey passLayoutKey = {}; passLayoutKey.shader = resolvedShaderPass.shader; @@ -628,6 +641,10 @@ bool BuiltinForwardPipeline::DrawVisibleItems( RHI::RHICommandList* commandList = context.commandList; RHI::RHIPipelineState* currentPipelineState = nullptr; bool drawFailed = false; + const RenderStateBlock* renderStateBlock = + drawSettings.HasRenderStateOverrides() + ? &drawSettings.renderStateBlock + : nullptr; BuiltinMaterialPass requestedPass = BuiltinMaterialPass::ForwardLit; bool hasRequestedPass = false; if (!TryResolveRequestedSurfacePassType( @@ -674,7 +691,8 @@ bool BuiltinForwardPipeline::DrawVisibleItems( surface, sceneData, material, - preferredPass); + preferredPass, + renderStateBlock); if (pipelineState == nullptr) { drawFailed = true; return; @@ -688,7 +706,8 @@ bool BuiltinForwardPipeline::DrawVisibleItems( executionContext, visibleItem, material, - preferredPass)) { + preferredPass, + renderStateBlock)) { drawFailed = true; } }; diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index 27efd445..3ff6b8b3 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -963,6 +963,58 @@ static_assert( sizeof(Rendering::RendererListDesc), "Managed renderer list desc bridge layout must match native RendererListDesc."); +struct ManagedDepthStateData { + uint8_t writeEnabled = 1u; + uint8_t compareFunction = + static_cast(Resources::MaterialComparisonFunc::Less); +}; + +static_assert( + sizeof(ManagedDepthStateData) == + sizeof(Rendering::DepthState), + "Managed depth state bridge layout must match native DepthState."); + +struct ManagedStencilFaceStateData { + uint8_t failOperation = + static_cast(Resources::MaterialStencilOp::Keep); + uint8_t passOperation = + static_cast(Resources::MaterialStencilOp::Keep); + uint8_t depthFailOperation = + static_cast(Resources::MaterialStencilOp::Keep); + uint8_t compareFunction = + static_cast(Resources::MaterialComparisonFunc::Always); +}; + +static_assert( + sizeof(ManagedStencilFaceStateData) == + sizeof(Rendering::StencilFaceState), + "Managed stencil face state bridge layout must match native StencilFaceState."); + +struct ManagedStencilStateData { + uint8_t enabled = 0u; + uint8_t readMask = 0xFFu; + uint8_t writeMask = 0xFFu; + ManagedStencilFaceStateData frontFace = {}; + ManagedStencilFaceStateData backFace = {}; +}; + +static_assert( + sizeof(ManagedStencilStateData) == + sizeof(Rendering::StencilState), + "Managed stencil state bridge layout must match native StencilState."); + +struct ManagedRenderStateBlockData { + uint32_t mask = 0u; + ManagedDepthStateData depthState = {}; + ManagedStencilStateData stencilState = {}; + uint8_t stencilReference = 0u; +}; + +static_assert( + sizeof(ManagedRenderStateBlockData) == + sizeof(Rendering::RenderStateBlock), + "Managed render state block bridge layout must match native RenderStateBlock."); + Rendering::FinalColorOutputTransferMode ResolveManagedFinalColorOutputTransferMode( uint8_t value) { switch (value) { @@ -995,6 +1047,121 @@ Rendering::FinalColorToneMappingMode ResolveManagedFinalColorToneMappingMode( } } +Resources::MaterialComparisonFunc ResolveManagedMaterialComparisonFunc( + uint8_t value) { + switch (value) { + case static_cast(Resources::MaterialComparisonFunc::Never): + return Resources::MaterialComparisonFunc::Never; + case static_cast(Resources::MaterialComparisonFunc::Equal): + return Resources::MaterialComparisonFunc::Equal; + case static_cast(Resources::MaterialComparisonFunc::LessEqual): + return Resources::MaterialComparisonFunc::LessEqual; + case static_cast(Resources::MaterialComparisonFunc::Greater): + return Resources::MaterialComparisonFunc::Greater; + case static_cast(Resources::MaterialComparisonFunc::NotEqual): + return Resources::MaterialComparisonFunc::NotEqual; + case static_cast(Resources::MaterialComparisonFunc::GreaterEqual): + return Resources::MaterialComparisonFunc::GreaterEqual; + case static_cast(Resources::MaterialComparisonFunc::Always): + return Resources::MaterialComparisonFunc::Always; + case static_cast(Resources::MaterialComparisonFunc::Less): + default: + return Resources::MaterialComparisonFunc::Less; + } +} + +Resources::MaterialStencilOp ResolveManagedMaterialStencilOp( + uint8_t value) { + switch (value) { + case static_cast(Resources::MaterialStencilOp::Zero): + return Resources::MaterialStencilOp::Zero; + case static_cast(Resources::MaterialStencilOp::Replace): + return Resources::MaterialStencilOp::Replace; + case static_cast(Resources::MaterialStencilOp::IncrSat): + return Resources::MaterialStencilOp::IncrSat; + case static_cast(Resources::MaterialStencilOp::DecrSat): + return Resources::MaterialStencilOp::DecrSat; + case static_cast(Resources::MaterialStencilOp::Invert): + return Resources::MaterialStencilOp::Invert; + case static_cast(Resources::MaterialStencilOp::IncrWrap): + return Resources::MaterialStencilOp::IncrWrap; + case static_cast(Resources::MaterialStencilOp::DecrWrap): + return Resources::MaterialStencilOp::DecrWrap; + case static_cast(Resources::MaterialStencilOp::Keep): + default: + return Resources::MaterialStencilOp::Keep; + } +} + +Rendering::RenderStateMask ResolveManagedRenderStateMask( + uint32_t value) { + const uint32_t knownMask = + Rendering::ToRenderStateMaskBits(Rendering::RenderStateMask::Depth) | + Rendering::ToRenderStateMaskBits(Rendering::RenderStateMask::Stencil); + return static_cast( + value & knownMask); +} + +Rendering::DepthState BuildManagedDepthState( + const ManagedDepthStateData& depthStateData) { + Rendering::DepthState depthState = {}; + depthState.writeEnabled = depthStateData.writeEnabled != 0u; + depthState.compareFunction = + ResolveManagedMaterialComparisonFunc( + depthStateData.compareFunction); + return depthState; +} + +Rendering::StencilFaceState BuildManagedStencilFaceState( + const ManagedStencilFaceStateData& stencilFaceData) { + Rendering::StencilFaceState stencilFaceState = {}; + stencilFaceState.failOp = + ResolveManagedMaterialStencilOp( + stencilFaceData.failOperation); + stencilFaceState.passOp = + ResolveManagedMaterialStencilOp( + stencilFaceData.passOperation); + stencilFaceState.depthFailOp = + ResolveManagedMaterialStencilOp( + stencilFaceData.depthFailOperation); + stencilFaceState.compareFunction = + ResolveManagedMaterialComparisonFunc( + stencilFaceData.compareFunction); + return stencilFaceState; +} + +Rendering::StencilState BuildManagedStencilState( + const ManagedStencilStateData& stencilStateData) { + Rendering::StencilState stencilState = {}; + stencilState.enabled = stencilStateData.enabled != 0u; + stencilState.readMask = stencilStateData.readMask; + stencilState.writeMask = stencilStateData.writeMask; + stencilState.frontFace = + BuildManagedStencilFaceState( + stencilStateData.frontFace); + stencilState.backFace = + BuildManagedStencilFaceState( + stencilStateData.backFace); + return stencilState; +} + +Rendering::RenderStateBlock BuildManagedRenderStateBlock( + const ManagedRenderStateBlockData& renderStateBlockData) { + Rendering::RenderStateBlock renderStateBlock = {}; + renderStateBlock.mask = + ResolveManagedRenderStateMask( + renderStateBlockData.mask); + renderStateBlock.depthState = + BuildManagedDepthState( + renderStateBlockData.depthState); + renderStateBlock.stencilState = + BuildManagedStencilState( + renderStateBlockData.stencilState); + renderStateBlock.stencilReference = + renderStateBlockData.stencilReference; + return renderStateBlock; +} + bool TryUnboxManagedFinalColorSettings( MonoObject* boxedValue, Rendering::FinalColorSettings& outSettings) { @@ -4593,7 +4760,8 @@ InternalCall_Rendering_ScriptableRenderContext_DrawRenderersByDesc( int32_t scenePhase, ManagedRendererListDescData* rendererListDescData, MonoString* overrideMaterialPath, - MonoString* shaderPassName) { + MonoString* shaderPassName, + ManagedRenderStateBlockData* renderStateBlockData) { ManagedScriptableRenderContextState* const state = FindManagedScriptableRenderContextState(nativeHandle); if (state == nullptr || @@ -4633,6 +4801,11 @@ InternalCall_Rendering_ScriptableRenderContext_DrawRenderersByDesc( drawSettings.shaderPassName = Containers::String(shaderPassNameUtf8.c_str()); } + if (renderStateBlockData != nullptr) { + drawSettings.renderStateBlock = + BuildManagedRenderStateBlock( + *renderStateBlockData); + } return state->sceneRecorder->RecordSceneDrawSettings( drawSettings) diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index c7ccddf4..4a023522 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -173,6 +173,8 @@ set(XCENGINE_SCRIPT_CORE_SOURCES # Rendering core ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/CameraFrameColorSource.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/CameraFrameStage.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/CompareFunction.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/DepthState.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/DrawingSettings.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/FinalColorExposureMode.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/FinalColorOutputTransferMode.cs @@ -182,6 +184,8 @@ set(XCENGINE_SCRIPT_CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/GraphicsSettings.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/RenderPipelineAsset.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/RenderQueueRange.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/RenderStateBlock.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/RenderStateMask.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/RendererListDesc.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/RendererListType.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/RendererSortMode.cs @@ -192,6 +196,9 @@ set(XCENGINE_SCRIPT_CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelineAsset.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelinePlanningContext.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/SortingSettings.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/StencilFaceState.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/StencilOp.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/StencilState.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Core/CameraRenderRequestContext.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Graph/RenderGraphRasterPassBuilder.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Graph/RenderGraphTextureDesc.cs diff --git a/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs b/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs index 0b665303..d5953256 100644 --- a/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs +++ b/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs @@ -69,6 +69,13 @@ namespace Gameplay public bool HasSceneRenderInjectionPointType; public bool HasRendererListType; public bool HasDrawingSettingsType; + public bool HasCompareFunctionType; + public bool HasStencilOpType; + public bool HasDepthStateType; + public bool HasStencilFaceStateType; + public bool HasStencilStateType; + public bool HasRenderStateMaskType; + public bool HasRenderStateBlockType; public bool HasRenderQueueRangeType; public bool HasRendererSortModeType; public bool HasFilteringSettingsType; @@ -375,6 +382,27 @@ namespace Gameplay HasDrawingSettingsType = contextType.Assembly.GetType( "XCEngine.Rendering.DrawingSettings") != null; + HasCompareFunctionType = + contextType.Assembly.GetType( + "XCEngine.Rendering.CompareFunction") != null; + HasStencilOpType = + contextType.Assembly.GetType( + "XCEngine.Rendering.StencilOp") != null; + HasDepthStateType = + contextType.Assembly.GetType( + "XCEngine.Rendering.DepthState") != null; + HasStencilFaceStateType = + contextType.Assembly.GetType( + "XCEngine.Rendering.StencilFaceState") != null; + HasStencilStateType = + contextType.Assembly.GetType( + "XCEngine.Rendering.StencilState") != null; + HasRenderStateMaskType = + contextType.Assembly.GetType( + "XCEngine.Rendering.RenderStateMask") != null; + HasRenderStateBlockType = + contextType.Assembly.GetType( + "XCEngine.Rendering.RenderStateBlock") != null; HasRenderQueueRangeType = contextType.Assembly.GetType( "XCEngine.Rendering.RenderQueueRange") != null; diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderObjectsRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderObjectsRendererFeature.cs index 613b3eba..2cb7ca62 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderObjectsRendererFeature.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderObjectsRendererFeature.cs @@ -21,6 +21,8 @@ namespace XCEngine.Rendering.Universal public uint renderLayerMask = uint.MaxValue; public string overrideMaterialPath = string.Empty; public string shaderPassName = string.Empty; + public bool overrideRenderStateBlock; + public RenderStateBlock renderStateBlock; public bool overrideFilteringSettings; public FilteringSettings filteringSettings; public bool overrideSortingSettings; @@ -114,6 +116,11 @@ namespace XCEngine.Rendering.Universal overrideMaterialPath ?? string.Empty; drawingSettings.shaderPassName = shaderPassName ?? string.Empty; + if (overrideRenderStateBlock) + { + drawingSettings.renderStateBlock = + renderStateBlock; + } return drawingSettings; } } diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index 8abe5596..3abdb1bf 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -783,7 +783,8 @@ namespace XCEngine int scenePhase, ref Rendering.RendererListDesc rendererListDesc, string overrideMaterialPath, - string shaderPassName); + string shaderPassName, + ref Rendering.RenderStateBlock renderStateBlock); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/CompareFunction.cs b/managed/XCEngine.ScriptCore/Rendering/Core/CompareFunction.cs new file mode 100644 index 00000000..cc8538ec --- /dev/null +++ b/managed/XCEngine.ScriptCore/Rendering/Core/CompareFunction.cs @@ -0,0 +1,14 @@ +namespace XCEngine.Rendering +{ + public enum CompareFunction : byte + { + Never = 0, + Less = 1, + Equal = 2, + LessEqual = 3, + Greater = 4, + NotEqual = 5, + GreaterEqual = 6, + Always = 7 + } +} diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/DepthState.cs b/managed/XCEngine.ScriptCore/Rendering/Core/DepthState.cs new file mode 100644 index 00000000..73bc8f4d --- /dev/null +++ b/managed/XCEngine.ScriptCore/Rendering/Core/DepthState.cs @@ -0,0 +1,22 @@ +using System.Runtime.InteropServices; + +namespace XCEngine.Rendering +{ + [StructLayout(LayoutKind.Sequential)] + public struct DepthState + { + [MarshalAs(UnmanagedType.I1)] + public bool writeEnabled; + + public CompareFunction compareFunction; + + public static DepthState CreateDefault() + { + return new DepthState + { + writeEnabled = true, + compareFunction = CompareFunction.Less + }; + } + } +} diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/DrawingSettings.cs b/managed/XCEngine.ScriptCore/Rendering/Core/DrawingSettings.cs index e59a86e1..1e2c33a3 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/DrawingSettings.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/DrawingSettings.cs @@ -4,6 +4,7 @@ namespace XCEngine.Rendering { public string overrideMaterialPath; public string shaderPassName; + public RenderStateBlock renderStateBlock; public bool hasOverrideMaterial => !string.IsNullOrEmpty( @@ -13,12 +14,17 @@ namespace XCEngine.Rendering !string.IsNullOrEmpty( shaderPassName); + public bool hasRenderStateOverrides => + renderStateBlock.hasOverrides; + public static DrawingSettings CreateDefault() { return new DrawingSettings { overrideMaterialPath = string.Empty, - shaderPassName = string.Empty + shaderPassName = string.Empty, + renderStateBlock = + RenderStateBlock.CreateDefault() }; } } diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/RenderStateBlock.cs b/managed/XCEngine.ScriptCore/Rendering/Core/RenderStateBlock.cs new file mode 100644 index 00000000..0c23770f --- /dev/null +++ b/managed/XCEngine.ScriptCore/Rendering/Core/RenderStateBlock.cs @@ -0,0 +1,27 @@ +using System.Runtime.InteropServices; + +namespace XCEngine.Rendering +{ + [StructLayout(LayoutKind.Sequential)] + public struct RenderStateBlock + { + public RenderStateMask mask; + public DepthState depthState; + public StencilState stencilState; + public byte stencilReference; + + public bool hasOverrides => + mask != RenderStateMask.None; + + public static RenderStateBlock CreateDefault() + { + return new RenderStateBlock + { + mask = RenderStateMask.None, + depthState = DepthState.CreateDefault(), + stencilState = StencilState.CreateDefault(), + stencilReference = 0 + }; + } + } +} diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/RenderStateMask.cs b/managed/XCEngine.ScriptCore/Rendering/Core/RenderStateMask.cs new file mode 100644 index 00000000..5ba7658a --- /dev/null +++ b/managed/XCEngine.ScriptCore/Rendering/Core/RenderStateMask.cs @@ -0,0 +1,12 @@ +using System; + +namespace XCEngine.Rendering +{ + [Flags] + public enum RenderStateMask : uint + { + None = 0, + Depth = 1u << 0, + Stencil = 1u << 1 + } +} diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs index 9d7a9df5..3eac56e4 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs @@ -101,6 +101,8 @@ namespace XCEngine.Rendering RendererListDesc rendererListDesc, DrawingSettings drawingSettings) { + RenderStateBlock renderStateBlock = + drawingSettings.renderStateBlock; return InternalCalls .Rendering_ScriptableRenderContext_DrawRenderersByDesc( m_nativeHandle, @@ -109,7 +111,8 @@ namespace XCEngine.Rendering drawingSettings.overrideMaterialPath ?? string.Empty, drawingSettings.shaderPassName ?? - string.Empty); + string.Empty, + ref renderStateBlock); } public bool RecordOpaqueScenePhase() diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/StencilFaceState.cs b/managed/XCEngine.ScriptCore/Rendering/Core/StencilFaceState.cs new file mode 100644 index 00000000..b15aec3a --- /dev/null +++ b/managed/XCEngine.ScriptCore/Rendering/Core/StencilFaceState.cs @@ -0,0 +1,24 @@ +using System.Runtime.InteropServices; + +namespace XCEngine.Rendering +{ + [StructLayout(LayoutKind.Sequential)] + public struct StencilFaceState + { + public StencilOp failOperation; + public StencilOp passOperation; + public StencilOp depthFailOperation; + public CompareFunction compareFunction; + + public static StencilFaceState CreateDefault() + { + return new StencilFaceState + { + failOperation = StencilOp.Keep, + passOperation = StencilOp.Keep, + depthFailOperation = StencilOp.Keep, + compareFunction = CompareFunction.Always + }; + } + } +} diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/StencilOp.cs b/managed/XCEngine.ScriptCore/Rendering/Core/StencilOp.cs new file mode 100644 index 00000000..0dd0069d --- /dev/null +++ b/managed/XCEngine.ScriptCore/Rendering/Core/StencilOp.cs @@ -0,0 +1,14 @@ +namespace XCEngine.Rendering +{ + public enum StencilOp : byte + { + Keep = 0, + Zero = 1, + Replace = 2, + IncrementSaturate = 3, + DecrementSaturate = 4, + Invert = 5, + IncrementWrap = 6, + DecrementWrap = 7 + } +} diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/StencilState.cs b/managed/XCEngine.ScriptCore/Rendering/Core/StencilState.cs new file mode 100644 index 00000000..8dcc55b6 --- /dev/null +++ b/managed/XCEngine.ScriptCore/Rendering/Core/StencilState.cs @@ -0,0 +1,28 @@ +using System.Runtime.InteropServices; + +namespace XCEngine.Rendering +{ + [StructLayout(LayoutKind.Sequential)] + public struct StencilState + { + [MarshalAs(UnmanagedType.I1)] + public bool enabled; + + public byte readMask; + public byte writeMask; + public StencilFaceState frontFace; + public StencilFaceState backFace; + + public static StencilState CreateDefault() + { + return new StencilState + { + enabled = false, + readMask = 0xFF, + writeMask = 0xFF, + frontFace = StencilFaceState.CreateDefault(), + backFace = StencilFaceState.CreateDefault() + }; + } + } +} diff --git a/project/Assets/Scripts/ProjectRenderPipelineProbe.cs b/project/Assets/Scripts/ProjectRenderPipelineProbe.cs index 1de12923..571ad704 100644 --- a/project/Assets/Scripts/ProjectRenderPipelineProbe.cs +++ b/project/Assets/Scripts/ProjectRenderPipelineProbe.cs @@ -143,6 +143,24 @@ namespace ProjectScripts "Unlit", overrideMaterialPath = "Assets/New Material.mat", + overrideRenderStateBlock = + true, + renderStateBlock = + new RenderStateBlock + { + mask = + RenderStateMask.Depth, + depthState = + new DepthState + { + writeEnabled = true, + compareFunction = + CompareFunction.LessEqual + }, + stencilState = + StencilState.CreateDefault(), + stencilReference = 0 + }, overrideRenderQueueRange = true, renderQueueRange = RenderQueueRange.Opaque,