diff --git a/docs/used/SRP_URP_ManagedFullscreenAuxTextureBindingPlan_2026-04-22_完成归档.md b/docs/used/SRP_URP_ManagedFullscreenAuxTextureBindingPlan_2026-04-22_完成归档.md new file mode 100644 index 00000000..75f3ae08 --- /dev/null +++ b/docs/used/SRP_URP_ManagedFullscreenAuxTextureBindingPlan_2026-04-22_完成归档.md @@ -0,0 +1,107 @@ +# SRP / URP Managed Fullscreen Auxiliary Texture Binding Plan + +时间:2026-04-22 + +## 背景 + +上一阶段已经补上了 managed raster pass 对 depth 读依赖和 depth attachment 的声明能力,但这条链只走到了 render graph 依赖记录层,并没有真正把额外输入纹理绑定到 fullscreen/custom pass 的 shader 资源槽位。 + +当前实际问题: + +1. `RenderGraphRasterPassBuilder.UseTexture(...)` / `UseDepthTexture(...)` 只声明读依赖,不会形成 shader 资源绑定。 +2. `BuiltinVectorFullscreenPass` 仍然是手写单 `SourceColorTexture` + 单 sampler 的 descriptor layout。 +3. render graph callback 在执行 `RenderPassContext` 时只携带 `sourceColorView`,没有携带额外输入纹理的 SRV。 +4. render graph 对 transient depth 只建了 DSV,没有建可采样的 SRV,导致 graph 内部深度纹理即使声明为 read depth,也不能被 fullscreen shader 真正采样。 + +这会导致 managed SRP / URP 侧写出来的 fullscreen/custom pass 只能做“单输入 source color”效果,无法形成真正可用的自定义后处理输入面。 + +## 本阶段目标 + +把 managed fullscreen/custom pass 的“声明输入纹理”与“运行时真实绑定纹理”收成闭环,并保持架构方向继续对齐 Unity 风格: + +- C++ core 继续拥有 render graph、资源生命周期和执行调度。 +- managed SRP / URP 继续拥有 pass authoring 和 shader 输入声明。 +- fullscreen pass 不再手写固定单纹理布局,而是接到现有 shader 资源绑定计划能力上。 + +## 范围 + +本阶段只做 fullscreen/custom raster pass 输入绑定闭环,不做: + +- deferred pipeline +- shadow 系统迁移 +- surface/imported texture 多视图系统大改 +- renderer 多实现切换 + +## 实施步骤 + +### 1. Managed API 补齐“按 shader 资源名绑定输入纹理” + +在 `RenderGraphRasterPassBuilder` 增加显式输入绑定 API: + +- `BindTexture(string shaderResourceName, RenderGraphTextureHandle texture)` +- `BindDepthTexture(string shaderResourceName, RenderGraphTextureHandle texture)` + +这两个 API 需要同时完成两件事: + +1. 自动登记 graph read dependency +2. 自动登记 runtime shader resource binding request + +这样 managed 侧不会再出现“声明读了,但执行没绑上”的双轨状态。 + +### 2. Mono bridge 保留纹理绑定描述 + +在 `ManagedScriptableRenderContextState::RasterPassRecordRequest` 中新增 fullscreen 输入绑定记录,至少包含: + +- shader resource name +- render graph texture handle +- texture aspect(color / depth) + +并增加 internal call,把 managed builder 的输入绑定请求带到 native。 + +### 3. RenderGraph callback 执行上下文补额外 SRV + +扩展 `RenderPassContext`,让 callback/fullscreen pass 在执行时能拿到: + +- `sourceColorView` +- 额外输入纹理对应的已解析 SRV 列表 + +执行阶段需要按 handle 解析实际 view: + +- `sourceColorTexture` 特殊走现有 `sourceColorView` +- 其他 graph-managed 纹理走 `ResolveTextureView(..., ShaderResource)` +- 如果拿不到 shader-readable view,则 pass 执行失败 + +### 4. 补 transient depth SRV + +render graph runtime 目前对 transient depth 只创建 DSV,不创建 SRV。 + +需要修改 runtime texture allocation 逻辑,让 transient depth 也能生成 `shaderResourceView`,从而支持 fullscreen shader 读取 graph 内部 depth texture。 + +### 5. BuiltinVectorFullscreenPass 接入 shader 资源绑定计划 + +把 `BuiltinVectorFullscreenPass` 从“手写 3 个 descriptor set”改成: + +- 解析 shader pass 的资源绑定计划 +- 基于 binding plan 自动创建 set layout / pipeline layout +- 自动填充 `PassConstants` +- 自动填充 `SourceColorTexture` +- 自动填充 `LinearClampSampler` +- 对其余 `Texture2D` 资源按 resource name 从 runtime 输入绑定表里解析 + +也就是说,fullscreen vector pass 要变成一个真正的“受控小型 material/fullscreen pass”,而不是只支持 `SourceColorTexture` 的专用壳。 + +## 阶段完成标准 + +满足以下条件才算本阶段收口: + +1. managed fullscreen/custom pass 可以声明并真正绑定额外 color/depth 输入纹理。 +2. `BuiltinVectorFullscreenPass` 不再硬编码单纹理 descriptor layout。 +3. graph 内部 transient depth 能生成并提供可采样 SRV。 +4. `XCEditor` Debug 构建通过。 +5. old editor 冒烟至少 10-15 秒通过,日志出现 `SceneReady`。 + +## 已知边界 + +本阶段不主动扩 surface/imported texture 的多视图系统。 + +也就是说,若某些 imported texture 当前只有 RTV / DSV、没有独立 SRV 视图,这部分仍然不是完整方案;后续如果要让任意 imported color/depth 都可被 graph 中任意 pass 采样,需要单独做 imported resource multi-view / surface-view ownership 收口计划。 diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinVectorFullscreenPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinVectorFullscreenPass.h index 0e7bce8e..978ba453 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinVectorFullscreenPass.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinVectorFullscreenPass.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,23 @@ public: RHI::RHIDescriptorSet* set = nullptr; }; + struct BoundSetState { + RHI::RHIResourceView* sourceColorTextureView = nullptr; + std::vector materialTextureViews = {}; + }; + + struct PassResourceLayout { + std::vector setLayouts = {}; + std::vector descriptorSets = {}; + std::vector boundSetStates = {}; + RHI::RHIPipelineLayout* pipelineLayout = nullptr; + PassResourceBindingLocation passConstants = {}; + PassResourceBindingLocation sourceColorTexture = {}; + PassResourceBindingLocation linearClampSampler = {}; + uint32_t firstDescriptorSet = 0u; + uint32_t descriptorSetCount = 0u; + }; + explicit BuiltinVectorFullscreenPass( const Math::Vector4& vectorPayload = Math::Vector4::One(), Containers::String shaderPath = {}, @@ -51,7 +69,15 @@ public: private: bool EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface); bool CreateResources(const RenderContext& renderContext, const RenderSurface& surface); + bool CreateOwnedDescriptorSet( + const BuiltinPassSetLayoutMetadata& setLayout, + OwnedDescriptorSet& descriptorSet); + bool UpdateDescriptorSets(const RenderPassContext& context); + RHI::RHIResourceView* ResolveExtraTextureView( + const RenderPassContext& context, + const BuiltinPassResourceBindingDesc& bindingDesc) const; void DestroyResources(); + void DestroyPassResourceLayout(); void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet); Containers::String m_shaderPath; @@ -64,12 +90,8 @@ private: uint32_t m_renderTargetSampleQuality = 0u; Resources::ResourceHandle m_shader; RHI::RHISampler* m_sampler = nullptr; - RHI::RHIPipelineLayout* m_pipelineLayout = nullptr; RHI::RHIPipelineState* m_pipelineState = nullptr; - OwnedDescriptorSet m_constantsSet = {}; - OwnedDescriptorSet m_textureSet = {}; - OwnedDescriptorSet m_samplerSet = {}; - RHI::RHIResourceView* m_boundSourceColorView = nullptr; + PassResourceLayout m_passLayout = {}; }; } // namespace Passes diff --git a/engine/include/XCEngine/Rendering/RenderPass.h b/engine/include/XCEngine/Rendering/RenderPass.h index 8b86e81f..a6426b64 100644 --- a/engine/include/XCEngine/Rendering/RenderPass.h +++ b/engine/include/XCEngine/Rendering/RenderPass.h @@ -23,12 +23,18 @@ class RenderGraphBuilder; class RenderGraphBlackboard; struct RenderPassContext { + struct TextureBindingView { + Containers::String resourceName = {}; + RHI::RHIResourceView* resourceView = nullptr; + }; + const RenderContext& renderContext; const RenderSurface& surface; const RenderSceneData& sceneData; const RenderSurface* sourceSurface = nullptr; RHI::RHIResourceView* sourceColorView = nullptr; RHI::ResourceStates sourceColorState = RHI::ResourceStates::Common; + std::vector textureBindings = {}; }; inline RenderPassContext BuildRenderPassContext( @@ -39,7 +45,8 @@ inline RenderPassContext BuildRenderPassContext( executionContext.sceneData, executionContext.sourceSurface, executionContext.sourceColorView, - executionContext.sourceColorState + executionContext.sourceColorState, + {} }; } diff --git a/engine/include/XCEngine/Rendering/RenderPassGraphContract.h b/engine/include/XCEngine/Rendering/RenderPassGraphContract.h index 74e775c7..1f684516 100644 --- a/engine/include/XCEngine/Rendering/RenderPassGraphContract.h +++ b/engine/include/XCEngine/Rendering/RenderPassGraphContract.h @@ -13,6 +13,12 @@ struct RenderPassGraphIO { bool writeDepth = false; }; +struct RenderPassGraphTextureBindingRequest { + Containers::String resourceName = {}; + RenderGraphTextureHandle texture = {}; + RenderGraphTextureAspect aspect = RenderGraphTextureAspect::Color; +}; + inline RenderPassGraphIO BuildSourceColorFullscreenRasterPassGraphIO() { return { true, @@ -53,7 +59,8 @@ bool RecordCallbackRasterRenderPass( const RenderPassGraphIO& io, RenderPassGraphExecutePassCallback executePassCallback, std::vector additionalReadTextures = {}, - std::vector additionalReadDepthTextures = {}); + std::vector additionalReadDepthTextures = {}, + std::vector textureBindingRequests = {}); bool RecordRasterRenderPass( RenderPass& pass, diff --git a/engine/src/Rendering/Graph/RenderGraphExecutor.cpp b/engine/src/Rendering/Graph/RenderGraphExecutor.cpp index dbe6145c..fb835728 100644 --- a/engine/src/Rendering/Graph/RenderGraphExecutor.cpp +++ b/engine/src/Rendering/Graph/RenderGraphExecutor.cpp @@ -353,22 +353,25 @@ private: } } - if (!isDepthTexture) { - allocation.shaderResourceView = - renderContext.device->CreateShaderResourceView(allocation.texture, viewDesc); - if (allocation.shaderResourceView == nullptr) { + allocation.shaderResourceView = + renderContext.device->CreateShaderResourceView( + allocation.texture, + viewDesc); + if (allocation.shaderResourceView == nullptr) { + DestroyTextureAllocation(allocation); + return false; + } + + if (!isDepthTexture && + requiresUnorderedAccess) { + allocation.unorderedAccessView = + renderContext.device->CreateUnorderedAccessView( + allocation.texture, + viewDesc); + if (allocation.unorderedAccessView == nullptr) { DestroyTextureAllocation(allocation); return false; } - - if (requiresUnorderedAccess) { - allocation.unorderedAccessView = - renderContext.device->CreateUnorderedAccessView(allocation.texture, viewDesc); - if (allocation.unorderedAccessView == nullptr) { - DestroyTextureAllocation(allocation); - return false; - } - } } return true; diff --git a/engine/src/Rendering/Passes/BuiltinVectorFullscreenPass.cpp b/engine/src/Rendering/Passes/BuiltinVectorFullscreenPass.cpp index 0e2bae1d..93c4c0ba 100644 --- a/engine/src/Rendering/Passes/BuiltinVectorFullscreenPass.cpp +++ b/engine/src/Rendering/Passes/BuiltinVectorFullscreenPass.cpp @@ -2,10 +2,11 @@ #include "Core/Asset/ResourceManager.h" #include "Debug/Logger.h" -#include +#include "Rendering/Builtin/BuiltinPassLayoutUtils.h" #include "Rendering/Internal/RenderSurfacePipelineUtils.h" #include "Rendering/Internal/ShaderVariantUtils.h" #include "Rendering/RenderSurface.h" +#include #include "Resources/BuiltinResources.h" #include "RHI/RHICommandList.h" #include "RHI/RHIDescriptorPool.h" @@ -16,7 +17,9 @@ #include "RHI/RHIResourceView.h" #include "RHI/RHISampler.h" +#include #include +#include namespace XCEngine { namespace Rendering { @@ -45,7 +48,8 @@ const Resources::ShaderPass* FindCompatiblePass( return nullptr; } - const Resources::ShaderPass* colorScalePass = shader.FindPass("ColorScale"); + const Resources::ShaderPass* colorScalePass = + shader.FindPass("ColorScale"); if (colorScalePass != nullptr && ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants( shader, @@ -55,13 +59,87 @@ const Resources::ShaderPass* FindCompatiblePass( } if (shader.GetPassCount() > 0 && - ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(shader, shader.GetPasses()[0].name, backend)) { + ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants( + shader, + shader.GetPasses()[0].name, + backend)) { return &shader.GetPasses()[0]; } return nullptr; } +bool UsesContiguousDescriptorSets( + const BuiltinPassResourceBindingPlan& bindingPlan) { + if (bindingPlan.bindings.Empty()) { + return true; + } + + std::vector usedSets( + static_cast(bindingPlan.maxSetIndex) + 1u, + false); + Core::uint32 minSet = UINT32_MAX; + Core::uint32 maxSet = 0u; + for (const BuiltinPassResourceBindingDesc& binding : + bindingPlan.bindings) { + usedSets[binding.location.set] = true; + minSet = std::min(minSet, binding.location.set); + maxSet = std::max(maxSet, binding.location.set); + } + + for (Core::uint32 setIndex = minSet; + setIndex <= maxSet; + ++setIndex) { + if (!usedSets[setIndex]) { + return false; + } + } + + return true; +} + +bool IsSupportedFullscreenBindingPlan( + const BuiltinPassResourceBindingPlan& bindingPlan) { + if (!UsesContiguousDescriptorSets(bindingPlan)) { + return false; + } + + for (const BuiltinPassResourceBindingDesc& binding : + bindingPlan.bindings) { + switch (binding.semantic) { + case BuiltinPassResourceSemantic::PassConstants: + case BuiltinPassResourceSemantic::SourceColorTexture: + case BuiltinPassResourceSemantic::MaterialTexture: + case BuiltinPassResourceSemantic::LinearClampSampler: + break; + default: + return false; + } + } + + return true; +} + +const RenderPassContext::TextureBindingView* FindTextureBindingView( + const RenderPassContext& context, + const BuiltinPassResourceBindingDesc& bindingDesc) { + auto matchesName = + [&bindingDesc]( + const RenderPassContext::TextureBindingView& bindingView) { + return bindingView.resourceName == + bindingDesc.name; + }; + + const auto it = + std::find_if( + context.textureBindings.begin(), + context.textureBindings.end(), + matchesName); + return it != context.textureBindings.end() + ? &(*it) + : nullptr; +} + RHI::GraphicsPipelineDesc CreatePipelineDesc( RHI::RHIType backendType, RHI::RHIPipelineLayout* pipelineLayout, @@ -70,34 +148,54 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( const RenderSurface& surface) { RHI::GraphicsPipelineDesc pipelineDesc = {}; pipelineDesc.pipelineLayout = pipelineLayout; - pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); - ::XCEngine::Rendering::Internal::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc( - surface, - pipelineDesc); - pipelineDesc.depthStencilFormat = static_cast(RHI::Format::Unknown); + pipelineDesc.topologyType = static_cast( + RHI::PrimitiveTopologyType::Triangle); + ::XCEngine::Rendering::Internal:: + ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc( + surface, + pipelineDesc); + pipelineDesc.depthStencilFormat = static_cast( + RHI::Format::Unknown); - pipelineDesc.rasterizerState.fillMode = static_cast(RHI::FillMode::Solid); - pipelineDesc.rasterizerState.cullMode = static_cast(RHI::CullMode::None); - pipelineDesc.rasterizerState.frontFace = static_cast(RHI::FrontFace::CounterClockwise); + pipelineDesc.rasterizerState.fillMode = static_cast( + RHI::FillMode::Solid); + pipelineDesc.rasterizerState.cullMode = static_cast( + RHI::CullMode::None); + pipelineDesc.rasterizerState.frontFace = + static_cast(RHI::FrontFace::CounterClockwise); pipelineDesc.rasterizerState.depthClipEnable = true; pipelineDesc.blendState.blendEnable = false; - pipelineDesc.blendState.srcBlend = static_cast(RHI::BlendFactor::One); - pipelineDesc.blendState.dstBlend = static_cast(RHI::BlendFactor::Zero); - pipelineDesc.blendState.srcBlendAlpha = static_cast(RHI::BlendFactor::One); - pipelineDesc.blendState.dstBlendAlpha = static_cast(RHI::BlendFactor::Zero); - pipelineDesc.blendState.blendOp = static_cast(RHI::BlendOp::Add); - pipelineDesc.blendState.blendOpAlpha = static_cast(RHI::BlendOp::Add); - pipelineDesc.blendState.colorWriteMask = static_cast(RHI::ColorWriteMask::All); + pipelineDesc.blendState.srcBlend = static_cast( + RHI::BlendFactor::One); + pipelineDesc.blendState.dstBlend = static_cast( + RHI::BlendFactor::Zero); + pipelineDesc.blendState.srcBlendAlpha = + static_cast(RHI::BlendFactor::One); + pipelineDesc.blendState.dstBlendAlpha = + static_cast(RHI::BlendFactor::Zero); + pipelineDesc.blendState.blendOp = static_cast( + RHI::BlendOp::Add); + pipelineDesc.blendState.blendOpAlpha = static_cast( + RHI::BlendOp::Add); + pipelineDesc.blendState.colorWriteMask = + static_cast(RHI::ColorWriteMask::All); pipelineDesc.depthStencilState.depthTestEnable = false; pipelineDesc.depthStencilState.depthWriteEnable = false; - pipelineDesc.depthStencilState.depthFunc = static_cast(RHI::ComparisonFunc::Always); + pipelineDesc.depthStencilState.depthFunc = static_cast( + RHI::ComparisonFunc::Always); - const Resources::ShaderPass* shaderPass = shader.FindPass(passName); - const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType); + const Resources::ShaderPass* shaderPass = + shader.FindPass(passName); + const Resources::ShaderBackend backend = + ::XCEngine::Rendering::Internal::ToShaderBackend( + backendType); if (const Resources::ShaderStageVariant* vertexVariant = - shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) { + shader.FindVariant( + passName, + Resources::ShaderType::Vertex, + backend)) { if (shaderPass != nullptr) { ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( shader.GetPath(), @@ -108,7 +206,10 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( } } if (const Resources::ShaderStageVariant* fragmentVariant = - shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) { + shader.FindVariant( + passName, + Resources::ShaderType::Fragment, + backend)) { if (shaderPass != nullptr) { ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( shader.GetPath(), @@ -132,7 +233,8 @@ BuiltinVectorFullscreenPass::BuiltinVectorFullscreenPass( , m_preferredPassName(std::move(preferredPassName)) , m_vectorPayload(vectorPayload) { if (m_shaderPath.Empty()) { - m_shaderPath = Resources::GetBuiltinColorScalePostProcessShaderPath(); + m_shaderPath = + Resources::GetBuiltinColorScalePostProcessShaderPath(); } } @@ -155,44 +257,56 @@ bool BuiltinVectorFullscreenPass::RecordRenderGraph( context); } -bool BuiltinVectorFullscreenPass::Execute(const RenderPassContext& context) { +bool BuiltinVectorFullscreenPass::Execute( + const RenderPassContext& context) { if (!context.renderContext.IsValid() || - context.sourceSurface == nullptr || - context.sourceColorView == nullptr) { - return false; - } - if (!::XCEngine::Rendering::Internal::HasSingleColorAttachment(*context.sourceSurface)) { + !::XCEngine::Rendering::Internal::HasSingleColorAttachment( + context.surface)) { return false; } - const std::vector& colorAttachments = context.surface.GetColorAttachments(); - if (!::XCEngine::Rendering::Internal::HasSingleColorAttachment(context.surface) || - colorAttachments.empty() || + const std::vector& colorAttachments = + context.surface.GetColorAttachments(); + if (colorAttachments.empty() || colorAttachments[0] == nullptr) { return false; } - const Math::RectInt renderArea = context.surface.GetRenderArea(); - if (renderArea.width <= 0 || renderArea.height <= 0) { + const Math::RectInt renderArea = + context.surface.GetRenderArea(); + if (renderArea.width <= 0 || + renderArea.height <= 0) { return false; } - if (!EnsureInitialized(context.renderContext, context.surface)) { + if (!EnsureInitialized( + context.renderContext, + context.surface)) { return false; } - PostProcessConstants constants = {}; - constants.vectorPayload = m_vectorPayload; - m_constantsSet.set->WriteConstant(0, &constants, sizeof(constants)); - - if (m_boundSourceColorView != context.sourceColorView) { - m_textureSet.set->Update(0, context.sourceColorView); - m_boundSourceColorView = context.sourceColorView; + if (m_passLayout.sourceColorTexture.IsValid() && + (context.sourceSurface == nullptr || + context.sourceColorView == nullptr)) { + return false; } - RHI::RHICommandList* commandList = context.renderContext.commandList; - RHI::RHIResourceView* renderTarget = colorAttachments[0]; - const bool autoTransitionSource = context.sourceSurface->IsAutoTransitionEnabled(); + if (!UpdateDescriptorSets(context)) { + return false; + } + + RHI::RHICommandList* commandList = + context.renderContext.commandList; + if (commandList == nullptr) { + return false; + } + + RHI::RHIResourceView* renderTarget = + colorAttachments[0]; + const bool autoTransitionSource = + m_passLayout.sourceColorTexture.IsValid() && + context.sourceSurface != nullptr && + context.sourceSurface->IsAutoTransitionEnabled(); if (context.surface.IsAutoTransitionEnabled()) { commandList->TransitionBarrier( @@ -200,13 +314,16 @@ bool BuiltinVectorFullscreenPass::Execute(const RenderPassContext& context) { context.surface.GetColorStateBefore(), RHI::ResourceStates::RenderTarget); } - if (autoTransitionSource) { - commandList->TransitionBarrier( - context.sourceColorView, - context.sourceColorState, - RHI::ResourceStates::PixelShaderResource); - } else if (context.sourceColorState != RHI::ResourceStates::PixelShaderResource) { - return false; + if (m_passLayout.sourceColorTexture.IsValid()) { + if (autoTransitionSource) { + commandList->TransitionBarrier( + context.sourceColorView, + context.sourceColorState, + RHI::ResourceStates::PixelShaderResource); + } else if (context.sourceColorState != + RHI::ResourceStates::PixelShaderResource) { + return false; + } } commandList->SetRenderTargets(1, &renderTarget, nullptr); @@ -228,15 +345,38 @@ bool BuiltinVectorFullscreenPass::Execute(const RenderPassContext& context) { commandList->SetViewport(viewport); commandList->SetScissorRect(scissorRect); - commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); + commandList->SetPrimitiveTopology( + RHI::PrimitiveTopology::TriangleList); commandList->SetPipelineState(m_pipelineState); - RHI::RHIDescriptorSet* descriptorSets[] = { - m_constantsSet.set, - m_textureSet.set, - m_samplerSet.set - }; - commandList->SetGraphicsDescriptorSets(0, 3, descriptorSets, m_pipelineLayout); + if (m_passLayout.descriptorSetCount > 0u) { + std::vector descriptorSets( + m_passLayout.descriptorSetCount, + nullptr); + for (Core::uint32 descriptorOffset = 0u; + descriptorOffset < m_passLayout.descriptorSetCount; + ++descriptorOffset) { + const Core::uint32 setIndex = + m_passLayout.firstDescriptorSet + + descriptorOffset; + if (setIndex >= + m_passLayout.descriptorSets.size() || + m_passLayout.descriptorSets[setIndex].set == + nullptr) { + return false; + } + + descriptorSets[descriptorOffset] = + m_passLayout.descriptorSets[setIndex].set; + } + + commandList->SetGraphicsDescriptorSets( + m_passLayout.firstDescriptorSet, + m_passLayout.descriptorSetCount, + descriptorSets.data(), + m_passLayout.pipelineLayout); + } + commandList->Draw(3, 1, 0, 0); commandList->EndRenderPass(); @@ -246,7 +386,8 @@ bool BuiltinVectorFullscreenPass::Execute(const RenderPassContext& context) { RHI::ResourceStates::RenderTarget, context.surface.GetColorStateAfter()); } - if (autoTransitionSource) { + if (m_passLayout.sourceColorTexture.IsValid() && + autoTransitionSource) { commandList->TransitionBarrier( context.sourceColorView, RHI::ResourceStates::PixelShaderResource, @@ -260,7 +401,8 @@ void BuiltinVectorFullscreenPass::Shutdown() { DestroyResources(); } -void BuiltinVectorFullscreenPass::SetVectorPayload(const Math::Vector4& vectorPayload) { +void BuiltinVectorFullscreenPass::SetVectorPayload( + const Math::Vector4& vectorPayload) { m_vectorPayload = vectorPayload; } @@ -268,7 +410,8 @@ const Math::Vector4& BuiltinVectorFullscreenPass::GetVectorPayload() const { return m_vectorPayload; } -void BuiltinVectorFullscreenPass::SetShaderPath(const Containers::String& shaderPath) { +void BuiltinVectorFullscreenPass::SetShaderPath( + const Containers::String& shaderPath) { if (m_shaderPath == shaderPath) { return; } @@ -291,7 +434,8 @@ void BuiltinVectorFullscreenPass::SetPreferredPassName( m_preferredPassName = preferredPassName; } -const Containers::String& BuiltinVectorFullscreenPass::GetPreferredPassName() const { +const Containers::String& +BuiltinVectorFullscreenPass::GetPreferredPassName() const { return m_preferredPassName; } @@ -299,22 +443,25 @@ bool BuiltinVectorFullscreenPass::EnsureInitialized( const RenderContext& renderContext, const RenderSurface& surface) { const RHI::Format renderTargetFormat = - ::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u); + ::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat( + surface, + 0u); const uint32_t renderTargetSampleCount = - ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface); + ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount( + surface); const uint32_t renderTargetSampleQuality = - ::XCEngine::Rendering::Internal::ResolveSurfaceSampleQuality(surface); + ::XCEngine::Rendering::Internal::ResolveSurfaceSampleQuality( + surface); if (m_device == renderContext.device && m_backendType == renderContext.backendType && m_renderTargetFormat == renderTargetFormat && - m_renderTargetSampleCount == renderTargetSampleCount && - m_renderTargetSampleQuality == renderTargetSampleQuality && - m_pipelineLayout != nullptr && + m_renderTargetSampleCount == + renderTargetSampleCount && + m_renderTargetSampleQuality == + renderTargetSampleQuality && + m_passLayout.pipelineLayout != nullptr && m_pipelineState != nullptr && - m_sampler != nullptr && - m_constantsSet.set != nullptr && - m_textureSet.set != nullptr && - m_samplerSet.set != nullptr) { + m_sampler != nullptr) { return true; } @@ -330,38 +477,98 @@ bool BuiltinVectorFullscreenPass::CreateResources( } RHI::Format renderTargetFormat = RHI::Format::Unknown; - if (!::XCEngine::Rendering::Internal::TryResolveSingleColorAttachmentFormat(surface, renderTargetFormat)) { + if (!::XCEngine::Rendering::Internal:: + TryResolveSingleColorAttachmentFormat( + surface, + renderTargetFormat)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, - "BuiltinVectorFullscreenPass 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, - "BuiltinVectorFullscreenPass requires a shader path before resource creation"); + "BuiltinVectorFullscreenPass requires a shader path " + "before resource creation"); return false; } - m_shader = Resources::ResourceManager::Get().Load(m_shaderPath); + m_shader = + Resources::ResourceManager::Get().Load( + m_shaderPath); if (!m_shader.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, - "BuiltinVectorFullscreenPass failed to load configured fullscreen 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::ShaderBackend backend = + ::XCEngine::Rendering::Internal::ToShaderBackend( + renderContext.backendType); const Resources::ShaderPass* selectedPass = - FindCompatiblePass(*m_shader.Get(), backend, m_preferredPassName); + FindCompatiblePass( + *m_shader.Get(), + backend, + m_preferredPassName); if (selectedPass == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, m_preferredPassName.Empty() - ? "BuiltinVectorFullscreenPass could not resolve a compatible fullscreen shader pass" - : "BuiltinVectorFullscreenPass could not resolve the configured fullscreen shader pass"); + ? "BuiltinVectorFullscreenPass could not resolve " + "a compatible fullscreen shader pass" + : "BuiltinVectorFullscreenPass could not resolve " + "the configured fullscreen shader pass"); + DestroyResources(); + return false; + } + + BuiltinPassResourceBindingPlan bindingPlan = {}; + Containers::String bindingPlanError; + if (!TryBuildBuiltinPassResourceBindingPlan( + *selectedPass, + bindingPlan, + &bindingPlanError)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String( + "BuiltinVectorFullscreenPass failed to " + "resolve shader resource bindings: ") + + bindingPlanError) + .CStr()); + DestroyResources(); + return false; + } + + if (!IsSupportedFullscreenBindingPlan(bindingPlan)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "BuiltinVectorFullscreenPass only supports " + "PassConstants, SourceColorTexture, " + "MaterialTexture, and LinearClampSampler " + "resource semantics"); + DestroyResources(); + return false; + } + + std::vector setLayouts; + Containers::String setLayoutError; + if (!TryBuildBuiltinPassSetLayouts( + bindingPlan, + setLayouts, + &setLayoutError)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String( + "BuiltinVectorFullscreenPass failed to build " + "descriptor set layouts: ") + + setLayoutError) + .CStr()); DestroyResources(); return false; } @@ -369,62 +576,64 @@ bool BuiltinVectorFullscreenPass::CreateResources( m_device = renderContext.device; m_backendType = renderContext.backendType; m_renderTargetFormat = renderTargetFormat; - m_renderTargetSampleCount = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface); - m_renderTargetSampleQuality = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleQuality(surface); + m_renderTargetSampleCount = + ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount( + surface); + m_renderTargetSampleQuality = + ::XCEngine::Rendering::Internal:: + ResolveSurfaceSampleQuality(surface); - RHI::DescriptorSetLayoutBinding constantBinding = {}; - constantBinding.binding = 0; - constantBinding.type = static_cast(RHI::DescriptorType::CBV); - constantBinding.count = 1; - constantBinding.visibility = static_cast(RHI::ShaderVisibility::All); + m_passLayout.setLayouts = std::move(setLayouts); + m_passLayout.descriptorSets.resize( + m_passLayout.setLayouts.size()); + m_passLayout.boundSetStates.resize( + m_passLayout.setLayouts.size()); + m_passLayout.passConstants = bindingPlan.passConstants; + m_passLayout.sourceColorTexture = + bindingPlan.sourceColorTexture; + m_passLayout.linearClampSampler = + bindingPlan.linearClampSampler; + m_passLayout.firstDescriptorSet = + bindingPlan.firstDescriptorSet; + m_passLayout.descriptorSetCount = + bindingPlan.descriptorSetCount; - RHI::DescriptorSetLayoutBinding textureBinding = {}; - textureBinding.binding = 0; - textureBinding.type = static_cast(RHI::DescriptorType::SRV); - textureBinding.count = 1; - textureBinding.visibility = static_cast(RHI::ShaderVisibility::All); - - RHI::DescriptorSetLayoutBinding samplerBinding = {}; - samplerBinding.binding = 0; - samplerBinding.type = static_cast(RHI::DescriptorType::Sampler); - samplerBinding.count = 1; - samplerBinding.visibility = static_cast(RHI::ShaderVisibility::All); - - RHI::DescriptorSetLayoutDesc constantLayout = {}; - constantLayout.bindings = &constantBinding; - constantLayout.bindingCount = 1; - - RHI::DescriptorSetLayoutDesc textureLayout = {}; - textureLayout.bindings = &textureBinding; - textureLayout.bindingCount = 1; - - RHI::DescriptorSetLayoutDesc samplerLayout = {}; - samplerLayout.bindings = &samplerBinding; - samplerLayout.bindingCount = 1; - - RHI::DescriptorSetLayoutDesc setLayouts[] = { - constantLayout, - textureLayout, - samplerLayout - }; + std::vector nativeSetLayouts( + m_passLayout.setLayouts.size()); + for (size_t setIndex = 0u; + setIndex < m_passLayout.setLayouts.size(); + ++setIndex) { + nativeSetLayouts[setIndex] = + m_passLayout.setLayouts[setIndex].layout; + } RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; - pipelineLayoutDesc.setLayouts = setLayouts; - pipelineLayoutDesc.setLayoutCount = 3; - m_pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); - if (m_pipelineLayout == nullptr) { + pipelineLayoutDesc.setLayouts = + nativeSetLayouts.empty() + ? nullptr + : nativeSetLayouts.data(); + pipelineLayoutDesc.setLayoutCount = + static_cast(nativeSetLayouts.size()); + m_passLayout.pipelineLayout = + m_device->CreatePipelineLayout(pipelineLayoutDesc); + if (m_passLayout.pipelineLayout == nullptr) { DestroyResources(); return false; } RHI::SamplerDesc samplerDesc = {}; - samplerDesc.filter = static_cast(RHI::FilterMode::Linear); - samplerDesc.addressU = static_cast(RHI::TextureAddressMode::Clamp); - samplerDesc.addressV = static_cast(RHI::TextureAddressMode::Clamp); - samplerDesc.addressW = static_cast(RHI::TextureAddressMode::Clamp); + samplerDesc.filter = + static_cast(RHI::FilterMode::Linear); + samplerDesc.addressU = static_cast( + RHI::TextureAddressMode::Clamp); + samplerDesc.addressV = static_cast( + RHI::TextureAddressMode::Clamp); + samplerDesc.addressW = static_cast( + RHI::TextureAddressMode::Clamp); samplerDesc.mipLodBias = 0.0f; samplerDesc.maxAnisotropy = 1; - samplerDesc.comparisonFunc = static_cast(RHI::ComparisonFunc::Always); + samplerDesc.comparisonFunc = static_cast( + RHI::ComparisonFunc::Always); samplerDesc.minLod = 0.0f; samplerDesc.maxLod = 1000.0f; m_sampler = m_device->CreateSampler(samplerDesc); @@ -433,58 +642,47 @@ bool BuiltinVectorFullscreenPass::CreateResources( return false; } - auto createOwnedDescriptorSet = - [this](const RHI::DescriptorSetLayoutDesc& layout, - RHI::DescriptorHeapType heapType, - bool shaderVisible, - OwnedDescriptorSet& ownedSet) -> bool { - RHI::DescriptorPoolDesc poolDesc = {}; - poolDesc.type = heapType; - poolDesc.descriptorCount = 1; - poolDesc.shaderVisible = shaderVisible; - ownedSet.pool = m_device->CreateDescriptorPool(poolDesc); - if (ownedSet.pool == nullptr) { + for (size_t setIndex = 0u; + setIndex < m_passLayout.setLayouts.size(); + ++setIndex) { + const BuiltinPassSetLayoutMetadata& setLayout = + m_passLayout.setLayouts[setIndex]; + if (setLayout.bindings.empty()) { + continue; + } + + if (!CreateOwnedDescriptorSet( + setLayout, + m_passLayout.descriptorSets[setIndex])) { + DestroyResources(); + return false; + } + + if (setLayout.usesSampler) { + if (!setLayout.usesLinearClampSampler || + !m_passLayout.linearClampSampler.IsValid() || + m_passLayout.linearClampSampler.set != + setIndex) { + DestroyResources(); return false; } - ownedSet.set = ownedSet.pool->AllocateSet(layout); - if (ownedSet.set == nullptr) { - DestroyOwnedDescriptorSet(ownedSet); - return false; - } - - return true; - }; - - if (!createOwnedDescriptorSet( - constantLayout, - RHI::DescriptorHeapType::CBV_SRV_UAV, - false, - m_constantsSet) || - !createOwnedDescriptorSet( - textureLayout, - RHI::DescriptorHeapType::CBV_SRV_UAV, - true, - m_textureSet) || - !createOwnedDescriptorSet( - samplerLayout, - RHI::DescriptorHeapType::Sampler, - true, - m_samplerSet)) { - DestroyResources(); - return false; + m_passLayout.descriptorSets[setIndex] + .set->UpdateSampler( + m_passLayout.linearClampSampler.binding, + m_sampler); + } } - m_samplerSet.set->UpdateSampler(0, m_sampler); - m_pipelineState = m_device->CreatePipelineState( CreatePipelineDesc( m_backendType, - m_pipelineLayout, + m_passLayout.pipelineLayout, *m_shader.Get(), selectedPass->name, surface)); - if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) { + if (m_pipelineState == nullptr || + !m_pipelineState->IsValid()) { DestroyResources(); return false; } @@ -492,24 +690,149 @@ bool BuiltinVectorFullscreenPass::CreateResources( return true; } -void BuiltinVectorFullscreenPass::DestroyResources() { - m_boundSourceColorView = nullptr; +bool BuiltinVectorFullscreenPass::CreateOwnedDescriptorSet( + const BuiltinPassSetLayoutMetadata& setLayout, + OwnedDescriptorSet& descriptorSet) { + RHI::DescriptorPoolDesc poolDesc = {}; + poolDesc.type = setLayout.heapType; + poolDesc.descriptorCount = + CountBuiltinPassHeapDescriptors( + setLayout.heapType, + setLayout.bindings); + poolDesc.shaderVisible = setLayout.shaderVisible; + descriptorSet.pool = + m_device->CreateDescriptorPool(poolDesc); + if (descriptorSet.pool == nullptr) { + return false; + } + + descriptorSet.set = + descriptorSet.pool->AllocateSet(setLayout.layout); + if (descriptorSet.set == nullptr) { + DestroyOwnedDescriptorSet(descriptorSet); + return false; + } + + return true; +} + +bool BuiltinVectorFullscreenPass::UpdateDescriptorSets( + const RenderPassContext& context) { + for (size_t setIndex = 0u; + setIndex < m_passLayout.setLayouts.size(); + ++setIndex) { + const BuiltinPassSetLayoutMetadata& setLayout = + m_passLayout.setLayouts[setIndex]; + if (setLayout.bindings.empty()) { + continue; + } + + OwnedDescriptorSet& descriptorSet = + m_passLayout.descriptorSets[setIndex]; + BoundSetState& boundSetState = + m_passLayout.boundSetStates[setIndex]; + if (descriptorSet.set == nullptr) { + return false; + } + + if (setLayout.usesPassConstants) { + if (!m_passLayout.passConstants.IsValid() || + m_passLayout.passConstants.set != setIndex) { + return false; + } + + const PostProcessConstants constants = { + m_vectorPayload + }; + descriptorSet.set->WriteConstant( + m_passLayout.passConstants.binding, + &constants, + sizeof(constants)); + } + + if (setLayout.usesSourceColorTexture) { + if (context.sourceColorView == nullptr || + !m_passLayout.sourceColorTexture.IsValid() || + m_passLayout.sourceColorTexture.set != + setIndex) { + return false; + } + + if (boundSetState.sourceColorTextureView != + context.sourceColorView) { + descriptorSet.set->Update( + m_passLayout.sourceColorTexture.binding, + context.sourceColorView); + boundSetState.sourceColorTextureView = + context.sourceColorView; + } + } + + if (setLayout.usesMaterialTextures) { + if (boundSetState.materialTextureViews.size() != + setLayout.materialTextureBindings.size()) { + boundSetState.materialTextureViews.assign( + setLayout.materialTextureBindings.size(), + nullptr); + } + + for (size_t bindingIndex = 0u; + bindingIndex < + setLayout.materialTextureBindings.size(); + ++bindingIndex) { + const BuiltinPassResourceBindingDesc& + textureBinding = + setLayout.materialTextureBindings + [bindingIndex]; + RHI::RHIResourceView* resolvedTextureView = + ResolveExtraTextureView( + context, + textureBinding); + if (resolvedTextureView == nullptr) { + return false; + } + + if (boundSetState.materialTextureViews + [bindingIndex] != + resolvedTextureView) { + descriptorSet.set->Update( + textureBinding.location.binding, + resolvedTextureView); + boundSetState.materialTextureViews + [bindingIndex] = resolvedTextureView; + } + } + } else if (!boundSetState.materialTextureViews + .empty()) { + boundSetState.materialTextureViews.clear(); + } + } + + return true; +} + +RHI::RHIResourceView* +BuiltinVectorFullscreenPass::ResolveExtraTextureView( + const RenderPassContext& context, + const BuiltinPassResourceBindingDesc& bindingDesc) const { + const RenderPassContext::TextureBindingView* bindingView = + FindTextureBindingView( + context, + bindingDesc); + return bindingView != nullptr + ? bindingView->resourceView + : nullptr; +} + +void BuiltinVectorFullscreenPass::DestroyResources() { if (m_pipelineState != nullptr) { m_pipelineState->Shutdown(); delete m_pipelineState; m_pipelineState = nullptr; } - DestroyOwnedDescriptorSet(m_samplerSet); - DestroyOwnedDescriptorSet(m_textureSet); - DestroyOwnedDescriptorSet(m_constantsSet); - - if (m_pipelineLayout != nullptr) { - m_pipelineLayout->Shutdown(); - delete m_pipelineLayout; - m_pipelineLayout = nullptr; - } + DestroyPassResourceLayout(); if (m_sampler != nullptr) { m_sampler->Shutdown(); @@ -525,7 +848,30 @@ void BuiltinVectorFullscreenPass::DestroyResources() { m_renderTargetSampleQuality = 0u; } -void BuiltinVectorFullscreenPass::DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet) { +void BuiltinVectorFullscreenPass::DestroyPassResourceLayout() { + for (OwnedDescriptorSet& descriptorSet : + m_passLayout.descriptorSets) { + DestroyOwnedDescriptorSet(descriptorSet); + } + m_passLayout.descriptorSets.clear(); + m_passLayout.boundSetStates.clear(); + m_passLayout.setLayouts.clear(); + + if (m_passLayout.pipelineLayout != nullptr) { + m_passLayout.pipelineLayout->Shutdown(); + delete m_passLayout.pipelineLayout; + m_passLayout.pipelineLayout = nullptr; + } + + m_passLayout.passConstants = {}; + m_passLayout.sourceColorTexture = {}; + m_passLayout.linearClampSampler = {}; + m_passLayout.firstDescriptorSet = 0u; + m_passLayout.descriptorSetCount = 0u; +} + +void BuiltinVectorFullscreenPass::DestroyOwnedDescriptorSet( + OwnedDescriptorSet& descriptorSet) { if (descriptorSet.set != nullptr) { descriptorSet.set->Shutdown(); delete descriptorSet.set; diff --git a/engine/src/Rendering/RenderPassGraphContract.cpp b/engine/src/Rendering/RenderPassGraphContract.cpp index 38cd77fe..57db0c97 100644 --- a/engine/src/Rendering/RenderPassGraphContract.cpp +++ b/engine/src/Rendering/RenderPassGraphContract.cpp @@ -3,6 +3,7 @@ #include "Rendering/Execution/Internal/CameraFrameGraph/SurfaceUtils.h" #include "Rendering/FrameData/RenderSceneData.h" #include "Rendering/Graph/RenderGraph.h" +#include "RHI/RHIResourceView.h" #include #include @@ -146,6 +147,55 @@ bool ResolveGraphManagedOutputSurface( return true; } +bool ResolveGraphManagedTextureBindingViews( + const std::vector& + textureBindingRequests, + RenderGraphTextureHandle sourceColorTexture, + RHI::RHIResourceView* sourceColorView, + const RenderGraphExecutionContext& graphContext, + std::vector& + outTextureBindings) { + outTextureBindings.clear(); + outTextureBindings.reserve(textureBindingRequests.size()); + + for (const RenderPassGraphTextureBindingRequest& bindingRequest : + textureBindingRequests) { + if (bindingRequest.resourceName.Empty() || + !bindingRequest.texture.IsValid()) { + return false; + } + + RHI::RHIResourceView* resolvedView = nullptr; + if (sourceColorTexture.IsValid() && + bindingRequest.texture.index == + sourceColorTexture.index) { + resolvedView = sourceColorView; + } else { + resolvedView = + graphContext.ResolveTextureView( + bindingRequest.texture, + RenderGraphTextureViewType:: + ShaderResource); + } + + if (resolvedView == nullptr || + resolvedView->GetViewType() != + RHI::ResourceViewType::ShaderResource) { + return false; + } + + RenderPassContext::TextureBindingView + resolvedBinding = {}; + resolvedBinding.resourceName = + bindingRequest.resourceName; + resolvedBinding.resourceView = resolvedView; + outTextureBindings.push_back( + std::move(resolvedBinding)); + } + + return true; +} + } // namespace bool RecordCallbackRasterRenderPass( @@ -153,7 +203,9 @@ bool RecordCallbackRasterRenderPass( const RenderPassGraphIO& io, RenderPassGraphExecutePassCallback executePassCallback, std::vector additionalReadTextures, - std::vector additionalReadDepthTextures) { + std::vector additionalReadDepthTextures, + std::vector + textureBindingRequests) { if (!executePassCallback) { return false; } @@ -193,6 +245,7 @@ bool RecordCallbackRasterRenderPass( executePassCallback, additionalReadTextures, additionalReadDepthTextures, + textureBindingRequests, io]( RenderGraphPassBuilder& passBuilder) { if (io.readSourceColor && sourceColorTexture.IsValid()) { @@ -239,6 +292,7 @@ bool RecordCallbackRasterRenderPass( beginPassCallback, endPassCallback, executePassCallback, + textureBindingRequests, io]( const RenderGraphExecutionContext& executionContext) { if (executionSucceeded != nullptr && !(*executionSucceeded)) { @@ -283,13 +337,28 @@ bool RecordCallbackRasterRenderPass( return; } + std::vector + resolvedTextureBindings = {}; + if (!ResolveGraphManagedTextureBindingViews( + textureBindingRequests, + sourceColorTexture, + resolvedSourceColorView, + executionContext, + resolvedTextureBindings)) { + if (executionSucceeded != nullptr) { + *executionSucceeded = false; + } + return; + } + const RenderPassContext passContext = { renderContext, *resolvedSurface, *sceneData, resolvedSourceSurface, resolvedSourceColorView, - resolvedSourceColorState + resolvedSourceColorState, + std::move(resolvedTextureBindings) }; if (beginPassCallback && !beginPassCallback(passContext)) { diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index 1c1e4ad2..e5736663 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -119,6 +119,8 @@ struct ManagedScriptableRenderContextState { Rendering::RenderGraphTextureHandle sourceColorTexture = {}; std::vector readTextures = {}; std::vector readDepthTextures = {}; + std::vector + textureBindings = {}; std::vector colorTargets = {}; Rendering::RenderGraphTextureHandle depthTarget = {}; }; @@ -423,6 +425,8 @@ bool RecordManagedFullscreenRasterPass( std::vector colorTargets, std::vector additionalReadTextures, std::vector additionalReadDepthTextures, + std::vector + textureBindings, Rendering::RenderGraphTextureHandle depthTarget, const Containers::String& passName) { if (!Rendering::IsCameraFrameFullscreenSequenceStage(stageContext.stage) || @@ -466,7 +470,8 @@ bool RecordManagedFullscreenRasterPass( passPtr->Execute(passContext); }, std::move(additionalReadTextures), - std::move(additionalReadDepthTextures)); + std::move(additionalReadDepthTextures), + std::move(textureBindings)); } bool RecordManagedFullscreenPassToTexture( @@ -483,6 +488,7 @@ bool RecordManagedFullscreenPassToTexture( {}, {}, {}, + {}, passName); } @@ -1945,6 +1951,7 @@ private: resolvedColorTargets, request.readTextures, request.readDepthTextures, + request.textureBindings, request.depthTarget, resolvedPassName)) { return false; @@ -4730,6 +4737,67 @@ InternalCall_Rendering_ScriptableRenderContext_AddRasterPassReadDepthTexture( return 1; } +mono_bool +InternalCall_Rendering_ScriptableRenderContext_AddRasterPassTextureBinding( + uint64_t nativeHandle, + uint64_t rasterPassHandle, + MonoString* shaderResourceName, + int32_t textureHandle, + mono_bool isDepth) { + ManagedScriptableRenderContextState* const state = + FindManagedScriptableRenderContextState(nativeHandle); + ManagedScriptableRenderContextState::RasterPassRecordRequest* + const request = + FindPendingManagedRasterPassRecordRequest( + state, + rasterPassHandle); + if (request == nullptr) { + return 0; + } + + const Containers::String shaderResourceNameString( + MonoStringToUtf8(shaderResourceName).c_str()); + if (shaderResourceNameString.Empty()) { + return 0; + } + + const Rendering::RenderGraphTextureHandle texture = + DecodeManagedRenderGraphTextureHandle( + textureHandle); + if (!texture.IsValid()) { + return 0; + } + + Rendering::RenderPassGraphTextureBindingRequest + bindingRequest = {}; + bindingRequest.resourceName = + shaderResourceNameString; + bindingRequest.texture = texture; + bindingRequest.aspect = + isDepth != 0 + ? Rendering::RenderGraphTextureAspect::Depth + : Rendering::RenderGraphTextureAspect::Color; + + auto existingBinding = + std::find_if( + request->textureBindings.begin(), + request->textureBindings.end(), + [&shaderResourceNameString]( + const Rendering::RenderPassGraphTextureBindingRequest& + existingRequest) { + return existingRequest.resourceName == + shaderResourceNameString; + }); + if (existingBinding != request->textureBindings.end()) { + *existingBinding = std::move(bindingRequest); + } else { + request->textureBindings.push_back( + std::move(bindingRequest)); + } + + return 1; +} + mono_bool InternalCall_Rendering_ScriptableRenderContext_SetRasterPassColorAttachment( uint64_t nativeHandle, @@ -6380,6 +6448,7 @@ void RegisterInternalCalls() { mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_SetRasterPassSourceColorTexture", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_SetRasterPassSourceColorTexture)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_AddRasterPassReadTexture", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_AddRasterPassReadTexture)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_AddRasterPassReadDepthTexture", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_AddRasterPassReadDepthTexture)); + mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_AddRasterPassTextureBinding", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_AddRasterPassTextureBinding)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_SetRasterPassColorAttachment", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_SetRasterPassColorAttachment)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_SetRasterPassDepthAttachment", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_SetRasterPassDepthAttachment)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_SetRasterPassColorScaleFullscreenExecution", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_SetRasterPassColorScaleFullscreenExecution)); diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index 4685b8d2..c80752b7 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -493,6 +493,15 @@ namespace XCEngine ulong rasterPassHandle, int textureHandle); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool + Rendering_ScriptableRenderContext_AddRasterPassTextureBinding( + ulong nativeHandle, + ulong rasterPassHandle, + string shaderResourceName, + int textureHandle, + bool isDepth); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool Rendering_ScriptableRenderContext_SetRasterPassColorAttachment( diff --git a/managed/XCEngine.ScriptCore/Rendering/Graph/RenderGraphRasterPassBuilder.cs b/managed/XCEngine.ScriptCore/Rendering/Graph/RenderGraphRasterPassBuilder.cs index 662097fa..9f395a9b 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Graph/RenderGraphRasterPassBuilder.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Graph/RenderGraphRasterPassBuilder.cs @@ -14,12 +14,21 @@ namespace XCEngine.Rendering public sealed class RenderGraphRasterPassBuilder { + private struct TextureBindingRequest + { + public string shaderResourceName; + public RenderGraphTextureHandle texture; + public bool isDepth; + } + private readonly ScriptableRenderContext m_context; private readonly string m_passName; private readonly List m_readTextures = new List(); private readonly List m_readDepthTextures = new List(); + private readonly List m_textureBindings = + new List(); private readonly List m_colorAttachments = new List(); private RenderGraphTextureHandle m_sourceColorTexture; @@ -74,22 +83,40 @@ namespace XCEngine.Rendering public RenderGraphRasterPassBuilder UseTexture( RenderGraphTextureHandle texture) { - if (texture.isValid) - { - m_readTextures.Add(texture); - } - + AddReadTexture( + texture, + isDepth: false); return this; } public RenderGraphRasterPassBuilder UseDepthTexture( RenderGraphTextureHandle texture) { - if (texture.isValid) - { - m_readDepthTextures.Add(texture); - } + AddReadTexture( + texture, + isDepth: true); + return this; + } + public RenderGraphRasterPassBuilder BindTexture( + string shaderResourceName, + RenderGraphTextureHandle texture) + { + AddTextureBinding( + shaderResourceName, + texture, + isDepth: false); + return this; + } + + public RenderGraphRasterPassBuilder BindDepthTexture( + string shaderResourceName, + RenderGraphTextureHandle texture) + { + AddTextureBinding( + shaderResourceName, + texture, + isDepth: true); return this; } @@ -224,6 +251,29 @@ namespace XCEngine.Rendering } } + for (int i = 0; i < m_textureBindings.Count; ++i) + { + TextureBindingRequest binding = + m_textureBindings[i]; + if (!binding.texture.isValid || + string.IsNullOrEmpty( + binding.shaderResourceName)) + { + continue; + } + + if (!InternalCalls + .Rendering_ScriptableRenderContext_AddRasterPassTextureBinding( + m_context.nativeHandle, + nativePassHandle, + binding.shaderResourceName, + binding.texture.nativeIndex, + binding.isDepth)) + { + return false; + } + } + for (int i = 0; i < m_colorAttachments.Count; ++i) { RenderGraphTextureHandle colorAttachment = @@ -304,6 +354,80 @@ namespace XCEngine.Rendering RenderGraphRasterPassExecutionKind.None; } + private void AddReadTexture( + RenderGraphTextureHandle texture, + bool isDepth) + { + if (!texture.isValid) + { + return; + } + + List readList = + isDepth + ? m_readDepthTextures + : m_readTextures; + for (int i = 0; i < readList.Count; ++i) + { + if (readList[i].nativeIndex == + texture.nativeIndex) + { + return; + } + } + + readList.Add(texture); + } + + private void AddTextureBinding( + string shaderResourceName, + RenderGraphTextureHandle texture, + bool isDepth) + { + if (string.IsNullOrEmpty(shaderResourceName)) + { + throw new ArgumentException( + "Shader resource name cannot be null or empty.", + nameof(shaderResourceName)); + } + + if (!texture.isValid) + { + return; + } + + AddReadTexture( + texture, + isDepth); + + for (int i = 0; i < m_textureBindings.Count; ++i) + { + TextureBindingRequest existingBinding = + m_textureBindings[i]; + if (!string.Equals( + existingBinding.shaderResourceName, + shaderResourceName, + StringComparison.Ordinal)) + { + continue; + } + + existingBinding.texture = texture; + existingBinding.isDepth = isDepth; + m_textureBindings[i] = existingBinding; + return; + } + + m_textureBindings.Add( + new TextureBindingRequest + { + shaderResourceName = + shaderResourceName, + texture = texture, + isDepth = isDepth + }); + } + private bool HasAnyColorAttachment() { for (int i = 0; i < m_colorAttachments.Count; ++i)