Unreal-Tick_RenderGraph
最近拿UE玩一些渲染发现新加的RDG(RenderDependecyGraph)非常好用,所以来分享一下。至于什么是RDG简单介绍一下吧,在当今游戏渲染流程里面可能会有很多的特性比如SSR,SSAO,Volumetric等等。它们之间的引用关系比如SSAO需要GBuffer的Normal而SSR需要Normal和Microface,而这两个Pass之间又隔了不知道多少个Pass甚至隔了前后帧,这个时候爪动的维护上下层Pass的引用关系和资源的释放就显得无比麻烦,因此RG孕育而生。这个架构思想最初是又寒霜引擎分享的,基本可以理解为一个系统自动管理你每个资源的创建和销毁以及Pass之间的资源引用和复用,以及剔除掉没有连接到FinalColor的Pass,做到不浪费任何一个系统资源从而达到极致的解耦和系统资源占用。详细咨询可见下方连接。首先RDG里面有两个最基本也是最重要的组件,RDGBuilder和RDGResource,前者一般用于创建渲染资源比如RT和Buffer等以及添加一个RenderPass到这个Graph内,后者为可被RDG管理的资源所有的RDGBuffer和RDGTexture等都继承自它。
执行一个Graph的基本步骤 :
1 : 使用一个RHICmdList来初始化一个Graph(这个CmdList既为整个Graph的CommandList),用法为"FRDGBuilder GraphBuilder(RHICmdList)"。
2 : 使用GraphBuilder.AllocaParameter()来分配一个当前Pass的参数结构体。
3 : 使用GraphBuilder.AddPass()结合当前Pass参数和一个Lambda表达式来创建一个Pass,Lambda表达式主要传递一些Pass的参数比如用到的Shader指针。
4 : 在AddPass内执行当前Pass的计算,如果是CS就直接Dispatch,如果是VS/PS就设置一些PSO等内容然后Draw即可。
5 : 使用GraphBuilder.QueueTex/Buffer()来吧RGPass内的结果提取到外部的一个PoolRT或者RHIBuffer内。并使用GraphBuilder.Execute()去真正执行整个Graph的所有Pass,是的前面一大堆只是DeferredContext。
Pass Resource :
渲染资源这里也不同于以往的PoolRT来做了,而是统一使用GraphBuilder.CreateTexture()或者CreateBuffer()来创建需要的资源。它只记录资源描述符。当需要资源时通过这个GraphBuilder来完成分配它将会跟踪这个资源的生命周期,并且在后面的Pass不再引用它时自动释放和复用。并且需要注意的是一个GraphPass内的资源有可能不是由Graph创建的,这个时候就需要使用GraphBuilder.RegisterExternalBuffer/Texture来吧某个PoolRT或者RHIBuffer转成RDGResource才能使用。同样的吧一个RDGResource转成oolRT或者RHIBuffer的方法则是GraphBuilder.QueueExternalBuffer/Texture,感觉这两对更适合叫ImportResource和ExportResource。如下图。
Pass Parameters :
当增加一个RGPass它必须带有Shader参数,可以是任何Shader参数比如UnifromBuffer,Texture等。且参数使用" GraphBuilder.AllocaParameter "来分配保留所有参数的结构体,因为Lambda执行被延迟确保了正确的生命周期。参数采用宏的形式来声明。且参数结构体的声明最好的方法是内联,直接在每个Pass的ShaderClass内声明好结构。
首先得在Shader里使用宏SHADER_USE_PARAMETERSTRUCT(FYouShader, ShaderType)设置Shader需要使用Prameter。然后需要实现一个FParameter的宏包裹的结构体里面声明该Pass需要用到的所有参数,参数基本上都是靠新的RDG系列宏来声明。需要注意一点的是对于UnifromBuffer需要使用StructRef来引用一层,可以理解为Parameter结构体里面还有一个结构体。如下所示。
因为我这儿是CS所以没有什么RenderTarget的绑定概念,如果是VS/PS的话需要用到RT Bind Slots只需要在FParameter结构里面声明一行RENDER_TARGET_BINDING_SLOTS()的宏,然后在Graph里面就可以直接Bind MultiRenderTarget和DepthStencilTarget了。
且在一个RGPass中使用的所有资源必须在GraphBuilder.AddPass()的Pass的参数中提供。它允许Render Graph了解在一个Pass内所使用的资源。这些资源保证在执行Pass的时候被分配。因此,它们只能在GraphBuilder.AddPass()创建的RGPass的Lambda作用域中可以访问。
AddPass :
GraphBuilder.AddPass()主要用来配置管线状态用于延迟执行。比如使用 FGraphicsPipelineStateInitializer对象来配置PSO,并调用RHI的API来进行绘制。或者使用SetComputeSahder()来DispatchCompute。注意此时还不会实际执行绘制而是在所有AddPass完成后调用GraphBuilder.Execute()才实际执行。而且更主要的是SetShaderParameters()也是在这儿做,这个函数是UE封装的,因为我们的一个Pass只能有一个AllocParameter所以这个东西里面是塞了UnifromBuffer SRV UAV等等各种东西。在以前的流程里面是自己再Shader里封装Shader.SetSRV/UAV/Unifrom等等的函数,而现在则只需要吧所有参数塞一起并在GraphPass内设置即可。RDG会自动检测参数的每一个成员和类型自动SetUAV/SRV/Unifrom。
最后 :
展示一下效果吧毕竟前面的东西都是Code层太硬盒了GG,光照是Unlit自己伪造的Ligthing。等后面吧OceanMesh整完了再继续深入分享RDG使用案例吧。
https://www.zhihu.com/video/1197294034694373376
页:
[1]