Skip to content

Latest commit

 

History

History
478 lines (404 loc) · 15.2 KB

FrameGraph.md

File metadata and controls

478 lines (404 loc) · 15.2 KB

FrameGraph设计

注:在书写该文档之前已经存在一部分设计,位于./docs/TurboDesign.drawio:FrameGraph中。由于Markdown文件书写起来比较方便,所以使用该文档继续书写设计

注:该文档与./docs/Design/FrameGraphAdvance.md文档配合使用,FrameGraphAdvance.md文档是书写Turbo引擎如何基于FrameGraph驱动起来的设计文档

更新日志

  • 2022/12/30

    • 创建FrameGraph文档
    • 创建PassNode与RenderPass章节
    • 创建FrameGraph::Subpass章节
    • 创建FrameGraph::RenderPass章节
  • 2022/12/31

    • 更新FrameGraph::Subpass章节
    • 更新FrameGraph::RenderPass章节
  • 2023/1/1

    • FrameGraph::Subpass章节重命名为FrameGraph::Builder::Subpass,并更新FrameGraph::Builder::Subpass章节
    • 更新FrameGraph::RenderPass章节
    • 创建FrameGraph::Subpass章节
  • 2023/1/3

    • 创建FrameGraph::Mermaid章节
  • 2023/1/4

    • 更新FrameGraph::Mermaid章节
  • 2023/1/11

    • 更新PassNode与RenderPass章节
  • 2023/1/12

    • 更新PassNode与RenderPass章节
    • 更新FrameGraph::Builder::Subpass章节
    • 更新FrameGraph::Subpass章节
    • 更新FrameGraph::Mermaid章节的示例图
  • 2023/1/18

    • 更新FrameGraph::RenderPass章节,其中,增加如何在Execute阶段获得FrameGraph::RenderPass章节

PassNode与RenderPass

PassNode::Setup阶段需要配置当前PassNode的各种Subpass,之后Turbo引擎会根据用户的配置创建RenderPassFrameBuffer

//FrameGraph::PassNode::Setup
[&](TFrameGraph::TBuilder &builder, CustomPassData &data)
{
    data.colorTex = builder.Create<Texture2D>("color",{512,512,Usage::Color})
    data.normalTex = builder.Create<Texture2D>("normal",{512,512,Usage::Normal})
    data.depthTex = builder.Create<DepthTexture2D>("depth",{512,512,Usage::Depth})

    Subpass subpass0 = builder.CreateSubpass();    
    subpass0.Write(data.colorTex);
    subpass0.Write(data.depthTex);

    Subpass subpass1 = builder.CreateSubpass();
    subpass1.Read(data.colorTex);
    subpass1.Read(data.depthTex);
    subpass1.Write(data.normalTex);
}

用户每调用一次TFrameGraph::TBuilder::CreateSubpass()就是声明一个Subpass,并且创建一个Subpass并添加进PassNode所代表的RenderPass中,而Subpass中有对应资源的读写配置

Subpass对应资源测操作有:

  1. 读,Subpass::Read(Resource)
  2. 写,Subpass::Write(Resource)

FrameGraph::RenderPassRender::RenderPass

  • Subpass::Read(Resource),对应于Vulkan底层的InputAttachment
  • Subpass::Write(Resource),对应于Vulkan底层的ColorAttachmentDepthStencilAttachment,具体需要看是什么资源

对于当Subpass::Write(Resource)资源为DepthStencil纹理时,会有个问题,按照Vulkan标准每个Subpass只能绑定一个DepthTexture,而Turbo并不会制止用户往多个DepthTexture中写入,这会与Vulkan标准冲突,一种解决方案是当写入多个DepthStencil纹理时,只有最后一个深度模板纹理有效,Turbo输出警告信息

此处有一点要注意一下,如下:

subpass.Write(DepthStencilTexture);

此时代表DepthStencilTextureVulkan底层作为DepthStencilAttachment进行使用

subpass.Read(DepthStencilTexture);

此时代表DepthStencilTextureVulkan底层作为InputAttachment进行使用

此时可能会有如下问题:

graph LR;
   classDef Resource fill:#608ba3
   classDef Pass fill:#e8924a
   classDef Subpass fill:#8474a0
   classDef Start fill:#95ad5b,stroke:#95ad5b,stroke-width:4px
   classDef End fill:#a44141,stroke:#a44141,stroke-width:4px

   Start((" ")):::Start
   End((" ")):::End
   
   DepthBuffer0("Depth Buffer"):::Resource
   DepthBuffer1("Depth Buffer"):::Resource
  
   PassNode0:::Pass
       subgraph PassNode0["PassNode0"]
           direction TB
           PassNode0Subpass0("Subpass 0"):::Subpass
       end
   PassNode1:::Pass
       subgraph PassNode1["PassNode1"]
           direction TB
           PassNode1Subpass0("Subpass 0"):::Subpass
       end

   Start-.->PassNode0
   PassNode0Subpass0-->DepthBuffer0
   DepthBuffer0-.->PassNode1Subpass0
   PassNode1Subpass0-->DepthBuffer1
   DepthBuffer1-.->End

   linkStyle 1 stroke:#a44141,stroke-width:3px %% write link style
   linkStyle 2 stroke:#95ad5b,stroke-width:0.5px %% read link style
   linkStyle 3 stroke:#a44141,stroke-width:3px
   linkStyle 4 stroke:#a44141,stroke-width:3px
Loading

此时对应的代码为:

DepthTexture2D depth_texture;

PassNode pass_node0;
pass_node0.Subpass0.Write(depth_texture);

PassNode pass_node01;
pass_node01.Subpass0.Write(depth_texture);

如果此时调用FrameGraph::Compile(),当走到pass_node01之后写入Depth Buffer时,发现没有人使用该Depth Buffer,这会导致FrameGraph进行一系列剔除操作。此非良构。


如何解决该问题呢?

问题的根源在于对于像DepthStencilTexture这样的资源,目前FrameGraph::Read(...)可以解释成VulkanInputAttachment,但对于DepthStencilTexture这样的资源这不是必须的,DepthStencilTexture可以不作为InputAttachment而被其他PassNode使用。

目前能想到的解决方案就是:FrameGraph::Read(someResource)唯一的作用是告诉FrameGraph有人要使用该someResource,请在FrameGraph::Compile()阶段不要剔除相关节点,之后为FrameGraph::Read(someResource)增加一个标志位,用于表示此次读操作是否作为InputAttachment进行解析。可能的函数声明如下:

//in FrameGraph
FrameGraph::Read(Resource resource, bool isInput=false);

或许给Subpass中添加Subpass::Input(Resource resource)会更加方便

//in FrameGraph
FrameGraph::Input(Resource resource)
{
   FrameGraph::Read(resource, true);
}

详情请预览下面的FrameGraph::Builder::Subpass章节和FrameGraph::Subpass章节

FrameGraph::Builder::Subpass

位于:

namespace TFrameGraph
{
    class FrameGraph
    {
        class Builder
        {
            class Subpass;//位于此处
        };
    };
}

Builder::Subpass中的class Subpass是真正的PassNode::RenderPass::Subpass的代理(也可理解成前端),用户利用Builder::Subpass这个前端类来完善底层的RenderPass数据

由于原先是使用TFrameGraph::Builder::Write(...)TFrameGraph::Builder::Read(...)函数,现由于资源的读写由Subpass负责,则TFrameGraph::Builder对于资源的读写改成私有,通过友元Subpass调用TFrameGraph::Builder对于资源的读写即可,所以Subpass中需要存有TFrameGraph::Builder引用

而对于资源的读写,同样要注册到PassNode对应的RenderPass中,所以

  • Subpass::Write(Resource)的同时将向其中的RenderPass下对应的Subpass中注册资源
  • Subpass::Read(Resource)的同时将向其中的RenderPass下对应的Subpass中注册资源
  • Subpass::Input(Resource)的同时将向其中的RenderPass下对应的Subpass中注册资源

注:Subpass::Input(Resource)Subpass::Read(Resource)本质上没区别,唯一的区别就是Subpass::Input(Resource)会将对应得Resourceinput标志位设置成true

考虑:是否将Subpass::Write(...)Subpass::Read(...)设计成私有,并成为TBuilder的友元,这样只有在PassNode::Setup阶段可以调用Subpass::Write(...)Subpass::Read(...),如果设计成友元,其他私有成员也可以访问到了,也是个问题

//in FrameGraph::TBuilder
class Subpass
{
    private:
        TBuilder& builder;
        RenderPass& renderPass;

        uint32_t subpass;//当前subpass在RenderPass中的index

    public:
        TSubpass();
        TSubpass(TBuilder& builder);

        Resource Write(Resource);
        Resource Read(Resource);
        Resource Input(Resource);
}

Resource Subpass::Write(Resource resource)
{
   Resource write_resource = builder.Write(resource);
   renderPass.Subpasses[subpass].Write(write_resource);

   return write_resource;
}

Resource Subpass::Read(Resource resource)
{
   Resource read_resource = builder.Read(resource, false/*Input标志位*/);
   //对于FrameGraph::Subpass来说read信息对其不重要,该信息只用于FrameGraph计算是否进行剔除,FrameGraph::Subpass保留read信息也不是一件坏事,在转成图表化信息时可以有更丰富的信息
   renderPass.Subpasses[subpass].Read(read_resource);

   return read_resource;
}

Resource Subpass::Input(Resource resource)
{
   Resource read_resource = builder.Read(resource,true/*Input标志位*/);
   renderPass.Subpasses[subpass].Input(read_resource);

   return read_resource;
}

FrameGraph::Subpass

FrameGraph::Builder::Subpass大致差不多,为其后端,本质上用于存储资源的读写情况

//in FrameGraph
class TSubpass
{
  private:
    std::vector<TResource> writes;
    std::vector<TResource> inputs;

  public:
    TSubpass() = default;
    ~TSubpass() = default;

    void Write(TResource resource);
    //void Read(TResource resource);
    void Input(TResource resource);
};

void Write(TResource resource)
{
    this->writes.push_back(resource);
}

// void Read(TResource resource)
// {
//     this->reads.push_back(resource);
// }

void Input(TResource resource)
{
    this->inputs.push_back(resource);
}
  • 其中TSubpass::writes如果是ColorImage,应该对应VulkanColorAttachment
  • 其中TSubpass::writes如果是DepthStencil,应该对应VulkanDepthStencilAttachment
  • 其中TSubpass::inputs应该对应VulkanInputAttachment

FrameGraph::RenderPass

Subpass对于资源的读写,其实就是将对应的读写注册到RenderPass中,而一个PassNode代表一个RenderPass,所以一个PassNode中就应该存有一个RenderPass信息。

//in FrameGraph
class PassNode
{
    private:
        RenderPass renderPass;
};

而一个RenderPass下有多个Subpass

class RenderPass
{
    private:
        std::vector<Subpass> subpasses;

    public:
        RenderPass();
       
        void AddSubpass(Subpass& subpass);
};

如何在Execute阶段获得FrameGraph::RenderPass

Execute阶段,回调函数参数有一个Turbo::FrameGraph::TResources,可以通过该参数获取FrameGraph::RenderPass

//PassNode::Execute阶段回调
[=](const ColorPassData &data, const Turbo::FrameGraph::TResources &resources, void *context)
{
    FrameGraph::RenderPass render_pass = resources.GetRenderPass();
}

FrameGraph::Mermaid

FrameGraph中应该提供一种接口,用于输出通用图形化图表结构,目前常见的通用图形化图表标准有:

  1. Graphviz
  2. Mermaid

Turbo选择Mermaid标准作为通用图形化图表接口。

std::string FrameGraph::ToMermaid();

该接口将会输出Mermaid标准字符串,之后最常见的用法有两种:

  1. 推送到http服务器,展示在浏览器页面上
  2. 保存到本地,进而在本地打开,浏览查看

示例:

graph LR;
    classDef Resource fill:#608ba3
    classDef Pass fill:#e8924a
    classDef Subpass fill:#8474a0
    classDef Start fill:#95ad5b,stroke:#95ad5b,stroke-width:4px
    classDef End fill:#a44141,stroke:#a44141,stroke-width:4px

    Start((" ")):::Start
    End((" ")):::End
    
    DepthBuffer0("Depth Buffer"):::Resource
    DepthBuffer1("Depth Buffer"):::Resource
    GBuffer1("GBuffer 1"):::Resource
    GBuffer2("GBuffer 2"):::Resource
    GBuffer3("GBuffer 3"):::Resource
    LightBuffer("Light Buffer"):::Resource
    BackBuffer("Back Buffer"):::Resource

    DepthPass:::Pass
        subgraph DepthPass["Depth Pass"]
            direction TB
            DepthPassSubpass0("Subpass 0"):::Subpass
        end
    GBufferPass:::Pass
        subgraph GBufferPass["GBuffer Pass"]
            direction TB
            GBufferPassSubpass0("Subpass 0"):::Subpass
        end
    LightingPass:::Pass
        subgraph LightingPass["Lighting"]
            direction TB
            LightingPassSubpass0("Subpass 0"):::Subpass
        end
    PostPass:::Pass
        subgraph PostPass["Post"]
            direction TB
            PostPassSubpass0("Subpass 0"):::Subpass
        end
    PresentPass:::Pass
        subgraph PresentPass["Present"]
            direction TB
            PresentPassSubpass0("Subpass 0"):::Subpass
        end

    Start-.->DepthPass
    DepthPassSubpass0-->DepthBuffer0
    DepthBuffer0-->GBufferPassSubpass0
    GBufferPassSubpass0-->DepthBuffer1
    GBufferPassSubpass0-->GBuffer1
    GBufferPassSubpass0-->GBuffer2
    GBufferPassSubpass0-->GBuffer3
    DepthBuffer1-.->LightingPassSubpass0 %% read link is dashed
    GBuffer1-->LightingPassSubpass0
    GBuffer2-->LightingPassSubpass0
    GBuffer3-->LightingPassSubpass0
    LightingPassSubpass0-->LightBuffer
    LightBuffer-->PostPassSubpass0
    PostPassSubpass0-->BackBuffer
    BackBuffer-->PresentPassSubpass0
    PresentPass-.->End

    linkStyle 1 stroke:#a44141,stroke-width:3px %% write link style
    linkStyle 2 stroke:#95ad5b,stroke-width:3px %% input link style
    linkStyle 3 stroke:#a44141,stroke-width:3px
    linkStyle 4 stroke:#a44141,stroke-width:3px
    linkStyle 5 stroke:#a44141,stroke-width:3px
    linkStyle 6 stroke:#a44141,stroke-width:3px
    linkStyle 7 stroke:#ffd305,stroke-width:3px %% read link style
    linkStyle 8 stroke:#95ad5b,stroke-width:3px
    linkStyle 9 stroke:#95ad5b,stroke-width:3px
    linkStyle 10 stroke:#95ad5b,stroke-width:3px
    linkStyle 11 stroke:#a44141,stroke-width:3px
    linkStyle 12 stroke:#95ad5b,stroke-width:3px
    linkStyle 13 stroke:#a44141,stroke-width:3px
    linkStyle 14 stroke:#95ad5b,stroke-width:3px
Loading
graph LR;
classDef Resource fill:#608ba3
classDef Pass fill:#e8924a
classDef Subpass fill:#8474a0
classDef Start fill:#95ad5b,stroke:#95ad5b,stroke-width:4px
classDef End fill:#a44141,stroke:#a44141,stroke-width:4px
Start((" ")):::Start
End((" ")):::End
PassNode0:::Pass
subgraph PassNode0["Color Pass"]
direction TB
PassNode0Subpass0("Subpass 0"):::Subpass
end
Color_Texture2D0("Color Texture2D"):::Resource
PassNode0Subpass0-->Color_Texture2D0
Depth_Texture2D0("Depth Texture2D"):::Resource
PassNode0Subpass0-->Depth_Texture2D0
PassNode1:::Pass
subgraph PassNode1["Post Pass"]
direction TB
PassNode1Subpass0("Subpass 0"):::Subpass
end
Color_Texture2D0("Color Texture2D"):::Resource
Color_Texture2D0-->PassNode1Subpass0
Depth_Texture2D0("Depth Texture2D"):::Resource
Depth_Texture2D0-->PassNode1Subpass0
RenderTarget_Texture2D0("RenderTarget Texture2D"):::Resource
PassNode1Subpass0-->RenderTarget_Texture2D0
PassNode2:::Pass
subgraph PassNode2["Present Pass"]
direction TB
PassNode2Subpass0("Subpass 0"):::Subpass
end
RenderTarget_Texture2D0("RenderTarget Texture2D"):::Resource
RenderTarget_Texture2D0-->PassNode2Subpass0
Start-.->PassNode0
PassNode2-.->End
linkStyle 0 stroke:#a44141,stroke-width:3px
linkStyle 1 stroke:#a44141,stroke-width:3px
linkStyle 2 stroke:#95ad5b,stroke-width:3px
linkStyle 3 stroke:#95ad5b,stroke-width:3px
linkStyle 4 stroke:#a44141,stroke-width:3px
linkStyle 5 stroke:#95ad5b,stroke-width:3px
Loading