量子计算9 发表于 2021-12-1 13:30

Unity-SRP_02

翻CoreRP文件夹的时候偶然间发现Unity也有RG系统了,so那当然是拿来high啦。文档如下。
所以这一期就把上一期的内容移植到RG下。首先需要一个RG对象然后在SRP的构造函数里面对它进行初始化。


然后吧我们的SRP改成partial class并新建一个DepthPass.cs。Pass_PerDepthData是当前Pass需要的Parameters,Pass_PerDepthOutput是当前Pass需要输出给下一个Pass可能用到的Parameters。RenderGraphResource是可读资源,RenderGraphMutableResource是可读可写资源,他们的生命周期都由RG管理。创建Texture和RenderObjectList都是由GraphBuilder来实现。同时使用GraphBuilder.UseDepthBuffer()来绑定DepthBuffer。使用GraphBuilder.UseRendererList()来绑定RenderObject并把它们注册到该Pass的Resource。然后通过AddRenderPass()来确定该Pass需要的参数类型,且使用GraphBuilder.SetRenderFunc()来获得参数和RenderGraphContext和PassData(这两个东西便是RGExecute提交的注册的ScriptaleRenderContex和CommandBuffer以及SetRenderFunc前注册的资源参数)。然后在此函数下通过RenderGraphContext来获取前面注册的Resource和ScriptableRenderContext以及CommandBuffer,然后进行一系列CommandBuffer.Set/Draw/Dispatch即可。
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering.RenderGraphModule;

namespace InfinityExtend.Runtime.Rendering
{
    public partial class InfinityRenderPipeline
    {
      class Pass_PerDepthData
      {
            public RenderGraphResource RendererList;
            public RenderGraphMutableResource DepthBuffer;
      }

      struct Pass_PerDepthOutput
      {
            public RenderGraphMutableResource DepthBuffer;
      }

      Pass_PerDepthOutput RenderPass_PerDepth(Camera RenderCamera, RenderGraph RenderGraph, CullingResults CulingData)
      {
            Pass_PerDepthOutput DepthPrePassOut;
            {
                using ( RenderGraphBuilder GraphBuilder = RenderGraph.AddRenderPass<Pass_PerDepthData>("InfinityPass_PreDepth", out Pass_PerDepthData DepthPassData, CustomSamplerId.DepthBuffer.GetSampler() ) )
                {
                  TextureDesc DepthTextureDesc = new TextureDesc(Vector2.one, false, false) {
                        enableMSAA = false,
                        clearBuffer = true,
                        bindTextureMS = false,
                        name = "DepthStencilBuffer",
                        depthBufferBits = DepthBits.Depth24,
                  };
                  DepthPassData.DepthBuffer = GraphBuilder.UseDepthBuffer(RenderGraph.CreateTexture(DepthTextureDesc, InfinityShaderIDs.RT_DepthBuffer), DepthAccess.ReadWrite);
                  DepthPassData.RendererList = GraphBuilder.UseRendererList( RenderGraph.CreateRendererList(CreateOpaqueRendererListDesc(CulingData, RenderCamera, InfinityPassIDs.PreDepthPass) ) );

                  GraphBuilder.SetRenderFunc( (Pass_PerDepthData PassData, RenderGraphContext RenderContent) =>
                  {
                        DrawRendererList(RenderContent.renderContext, RenderContent.resources.GetRendererList(PassData.RendererList), RenderContent.cmd);
                  });

                  DepthPrePassOut.DepthBuffer = DepthPassData.DepthBuffer;
                }
            }
            return DepthPrePassOut;
      }
    }
}
这里我就封成了一个DrawList因为后面Pass也可以用这个方法进行DrawRender。
public static void DrawRendererList(ScriptableRenderContext RenderContext, RendererList RendererList, CommandBuffer CommandList)
{
    if (!RendererList.isValid) {
      throw new ArgumentException("Invalid renderer list provided to DrawRendererList");
    }

    RendererList.drawSettings.enableInstancing = true;
    RendererList.drawSettings.enableDynamicBatching = true;
            
    RenderContext.ExecuteCommandBuffer(CommandList);
    CommandList.Clear();

    if (RendererList.stateBlock == null) {
      RenderContext.DrawRenderers(RendererList.cullingResult, ref RendererList.drawSettings, ref RendererList.filteringSettings);
    } else {
      var RenderStateBlock = RendererList.stateBlock.Value;
      RenderContext.DrawRenderers(RendererList.cullingResult, ref RendererList.drawSettings, ref RendererList.filteringSettings, ref RenderStateBlock);
    }
}同样的一路套,GBufferPass基本和PreZ一样只是多了一个GraphBuilder.UseColorBuffer来注册SV_Tatget。且输入是PreZ的Out这样就可以直接读前面PreZPass的深度数据。
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Experimental.Rendering.RenderGraphModule;

namespace InfinityExtend.Runtime.Rendering
{
    public partial class InfinityRenderPipeline
    {
      class Pass_GBufferData
      {
            public RenderGraphResource RendererList;
            public RenderGraphMutableResource GBufferBaseColor;
            public RenderGraphMutableResource GBufferMicroface;
            public RenderGraphMutableResource GBufferWorldNormal;
            public RenderGraphMutableResource GBufferEmissive;
      }

      struct Pass_GBufferOutput
      {
            public RenderGraphMutableResource GBufferBaseColor;
            public RenderGraphMutableResource GBufferMicroface;
            public RenderGraphMutableResource GBufferWorldNormal;
            public RenderGraphMutableResource GBufferEmissive;
      }

      Pass_GBufferOutput RenderPass_ThinGBuffer(Camera RenderCamera, RenderGraph RenderGraph, CullingResults CullingData, RenderGraphMutableResource DepthBuffer)
      {
            Pass_GBufferOutput GBufferPassOut;
            {
                using (RenderGraphBuilder GraphBuilder = RenderGraph.AddRenderPass<Pass_GBufferData>("InfinityPass_GBuffer", out Pass_GBufferData GBufferData, CustomSamplerId.GBuffer.GetSampler()) )
                {
                  ///Init RenderDesc
                  TextureDesc GBufferBaseColorDesc = new TextureDesc(Vector2.one, true, true) {
                        clearBuffer = true,
                        clearColor = Color.clear,
                        name = "GBufferBaseColor",
                        colorFormat = GraphicsFormat.R8G8B8A8_UNorm,
                  };
                  TextureDesc GBufferMicrofaceDesc = new TextureDesc(Vector2.one, true, true) {
                        clearBuffer = true,
                        clearColor = Color.clear,
                        name = "GBufferMicroface",
                        colorFormat = GraphicsFormat.R8G8B8A8_UNorm,
                  };
                  TextureDesc GBufferNormalDesc = new TextureDesc(Vector2.one, true, true) {
                        clearBuffer = true,
                        clearColor = Color.clear,
                        name = "GBufferNormal",
                        colorFormat = GraphicsFormat.A2B10G10R10_UNormPack32,
                  };
                  TextureDesc GBufferEmissiveDesc = new TextureDesc(Vector2.one, true, true) {
                        clearBuffer = true,
                        clearColor = Color.clear,
                        name = "GBufferEmissive",
                        colorFormat = GraphicsFormat.R16G16B16A16_SFloat,
                  };

                  ///Set RenderTarget
                  GraphBuilder.UseDepthBuffer(DepthBuffer, DepthAccess.Read);
                  GBufferData.GBufferBaseColor = GraphBuilder.UseColorBuffer(RenderGraph.CreateTexture(GBufferBaseColorDesc, InfinityShaderIDs.RT_GBufferBaseColor), 0);
                  GBufferData.GBufferMicroface = GraphBuilder.UseColorBuffer(RenderGraph.CreateTexture(GBufferMicrofaceDesc, InfinityShaderIDs.RT_GBufferMicroface), 1);
                  GBufferData.GBufferWorldNormal = GraphBuilder.UseColorBuffer(RenderGraph.CreateTexture(GBufferNormalDesc, InfinityShaderIDs.RT_GBufferNormal), 2);
                  GBufferData.GBufferEmissive = GraphBuilder.UseColorBuffer(RenderGraph.CreateTexture(GBufferEmissiveDesc, InfinityShaderIDs.RT_GBufferEmissive), 3);
                  GBufferData.RendererList = GraphBuilder.UseRendererList( RenderGraph.CreateRendererList(CreateOpaqueRendererListDesc(CullingData, RenderCamera, InfinityPassIDs.GBufferPass)) );

                  ///Set DrawPass
                  GraphBuilder.SetRenderFunc( (Pass_GBufferData PassData, RenderGraphContext RenderContent) =>
                  {
                        DrawRendererList(RenderContent.renderContext, RenderContent.resources.GetRendererList(PassData.RendererList), RenderContent.cmd);
                  });

                  ///Get Output
                  GBufferPassOut.GBufferBaseColor = GBufferData.GBufferBaseColor;
                  GBufferPassOut.GBufferMicroface = GBufferData.GBufferMicroface;
                  GBufferPassOut.GBufferWorldNormal = GBufferData.GBufferWorldNormal;
                  GBufferPassOut.GBufferEmissive = GBufferData.GBufferEmissive;
                }
            }
            return GBufferPassOut;
      }
    }
}最后在SRP.cs里面吧原来的RenderContext()修改一下,如下。首先就是把五个Pass的函数按顺序设置好Dependecy。然后从CommandBufferPool申请一个CommandBuffer,并使用RenderGraph.Execute()来真正执行内容,并吧CommandBuffer和ScriptableRenderContext设置给RenderGraph即可。
      private void RenderContent_RenderGraph(bool isSceneViewCam, Camera RenderCamera, CullingResults CullingData, ScriptableRenderContext RenderContext)
      {
            //////Draw RenderFeaturePass
            Pass_PerDepthOutput PreDepthPass = RenderPass_PerDepth(RenderCamera, RenderGraph, CullingData);
            Pass_GBufferOutput GBufferPass = RenderPass_ThinGBuffer(RenderCamera, RenderGraph, CullingData, PreDepthPass.DepthBuffer);

            //////Draw SkyBox
            RenderSkyBoxPass(RenderCamera, RenderGraph);

            //////Draw Gizmos on SceneView
      #if UNITY_EDITOR
            if (isSceneViewCam) {
                RenderGizmoPass(RenderCamera, GizmoSubset.PostImageEffects, RenderGraph);
            }
      #endif

            //////Bilt To Screen
            BlitRenderGraphTextureToCameraTarget(GBufferPass.GBufferBaseColor, RenderCamera.targetTexture, RenderGraph, RenderCamera);

            //////Execute RenderGraph
            RenderGraphExecuteParams RenderGraphParams = new RenderGraphExecuteParams() {
                msaaSamples = MSAASamples.None,
                renderingWidth = RenderCamera.pixelWidth,
                renderingHeight = RenderCamera.pixelHeight
            };
            CommandBuffer RenderCommandList = CommandBufferPool.Get("InfinityCommandList");
            RenderGraph.Execute(RenderContext, RenderCommandList, RenderGraphParams);
            RenderContext.ExecuteCommandBuffer(RenderCommandList);
            CommandBufferPool.Release(RenderCommandList);
      }最后 :

感觉还是蛮好使的,得益于Unity的诡异图形封装使得RG里DrawMesh和Dispatch都很直接。和UE的RDG不大一样的就是UE是在new RDG的时候就设置了CommandList,Unity却是最后才做,为什么一样的东西用法思想能差这么多。233

xiaozongpeng 发表于 2021-12-1 13:35

DrawMesh和Dispatch难道本来不就是很直接的吗?比如在DX里Compute Shader和Shader都是提前编译好的二进制和一个Root Signature,CS要DispatchIndirect还有一个Command Signature,剩下绘制部分就是纯逻辑了,所以我觉得Unity并没有花多少精力维护封装。

JoshWindsor 发表于 2021-12-1 13:36

不啊,ue里面很不直接。。

kyuskoj 发表于 2021-12-1 13:37

看样子一个list把整个rg跑完了,甚至不是并行的…

RedZero9 发表于 2021-12-1 13:47

其实是并行的,srp的drawRenderer之类的是并行逻辑,只是上层指令抽象层是主线程,其实上层没多少消耗

APSchmidt 发表于 2021-12-1 13:52

那是UE自己的问题。。

RedZero9 发表于 2021-12-1 13:54

可是我看drawpass function的输入直接从rendercontext拿的command buffer,应该就是execute的时候绑的那个buffer吧

super1 发表于 2021-12-1 14:01

Execute并不是立即执行,是推到一个更大的队列里,最后submit才给渲染线程开始跑

KaaPexei 发表于 2021-12-1 14:04

并行?你说ASyncCompute?那个RG支持但是俺PC也无福消受了,233。

FeastSC 发表于 2021-12-1 14:13

不啊,我是说cmdbuffer,每个pass节点好像都用的是exe rg的时候传进去的cmd buffer,没看到多buffer execute,单buffer肯定没法多线程记录的吧
页: [1] 2
查看完整版本: Unity-SRP_02