Filament源码分析

放一下类图。

简单介绍一下相关类型:

  • FrameGraphHandle:只有一个uint16_t的标识,初始化为一个无效值。
  • FrameGraphId:一个模板类,继承自FrameGraphHandle,没有自定义的属性或者方法。
  • FrameGraphRenderTarget: 一个命名空间,里面定义了Attachments和Descriptor两个结构体。
  1. Attachments,主要定义了AttachmentInfo这个内部结构,AttachmentInfo有两个属性:FrameGraphId<FrameGraphTexture>类型的handle(仅仅是个handle,对应的是ResourceNode,并不是真正的纹理资源)和一个mLevel(纹理的mipmap level)。一个Attachments包含了两个AttachmentInfo,这里写死了。

    

          2. Descriptor:包含三个属性:Attachments, Viewport 和 samples

  • FrameGraphTexture: 定义了一个内部结构Descriptor,实现了方法create/destroy,持有真正的gpu资源。其创建和销毁均通过调用FrameGraph类的ResourceAllocator相应方法实现。
  • VirtualResource:资源类的祖先。定义了虚方法create和destroy,有两个PassNode类型的指针,指向第一个和最后一个ref这个资源的pass,在FrameGraph的方法compile内计算赋值的。
  • ResourceEntryBase:继承自VirtualResource,id,name变量没啥用,imported标记资源是否FrameGraph外部导入,uint8_t型version变量,还有一个引用计数,fg.compile内更新为:所有相关ResourceNode的readerCount之和。
  • ResourceEntry<T>,一个模板类,继承自ResourceEntryBase。包含T类型的resource属性和T::Descriptor类型的descriptor属性。实现了VirtualResource定义的两个虚接口,当!imported时会调用resource.相应方法。一个常用的实例化为T=FrameGraphTexture。ResourceEntry有两个构造,其中一个包含一个T类型的引用,这时会将import置为true,意思是这个资源是外部引入的。这时候我们注意到:ResourceEntryBase/ResourceEntry都是不能通过handle来查找访问的。这时候需要一个类ResourceNode。
  • ResourceNode:资源节点类。构造时需要传入一个ResourceEntryBase指针和一个uint8_t的version,ResourceEntryBase指向其对应的资源(比如一张纹理),version初始化为ResourceEntryBase的version。PassNode* writer指针指向写入这个资源的pass,只有一个?引用计数readerCount,还有一个renderTargetIndex,是fg中rt数组的索引,用来获取rt的信息?
  • RenderTargetResource:继承自VirtualResource,构造时需要传入FrameGraphRenderTarget::Descriptor的描述,是否imported,backend::TargetBufferFlags,宽高,纹理格式。主要包含一个FrameGraphPassResources::RenderTargetInfo类型的属性,其只有真正的硬件资源和backend::RenderPassParams params(包含viewport,discardstart,end等信息)。也是资源类,所以需要实现create/destroy两个接口,具体也是根据Descriptor调用FrameGraph类的ResourceAllocator相应方法实现。
  • RenderTarget:构造时需要传入FrameGraphRenderTarget::Descriptor,fg中数组索引,及name。包含属性:backend::TargetBufferFlags userClearFlags,backend::RenderPassFlags targetFlags及RenderTargetResource* cache等。其包含一个方法:void resolve(FrameGraph& fg) noexcept,负责初始化RenderTargetResource* cache指针,具体实现为:首先根据Descriptor去fg的缓存中找(FrameGraph会缓存一个RenderTargetResource的列表),找到直接赋值然后执行一个cache->targetInfo.params.flags.clear |= userClearFlags;操作。否则重新创建一个新的。
  • FrameGraphPassResources:虽然也叫Resource但非继承自VirtualResource。构造时需要传入一个FrameGraph的引用和一个PassNode的引用,将其缓存下来。提供了几个get方法来从fg里面获取资源或者描述等信息,里面定义了结构RenderTargetInfo:

    

  • FrameGraphPassExecutor:只定义了一个接口virtual void execute(FrameGraphPassResources const& resources, backend::DriverApi& driver) noexcept = 0;
  • FrameGraphPass<typename Data, typename Execute>:一个模板类,继承自FrameGraphPassExecutor,包含两个变量:Execute mExecute和Data mData。构造时需要传入一个Execute&& execute。实现了父类的execute方法,只有一行代码:mExecute(resources, mData, driver); 所有pass的具体操作都是通过这个Execute传入执行的。
  • PassNode:构造时需要传入FrameGraph的引用,name,id, 及一个FrameGraphPassExecutor的指针。代表一个pass。

  主要属性:

  1. FrameGraph::UniquePtr<FrameGraphPassExecutor> base; // type eraser for calling execute()。
  2. 该pass reads/writes的资源handle列表。
  3. renderTargets列表。
  4. 该pass需要devirtualize/destroy的VirtualResource*列表。
  5. refCount // count resources that have a reference to us
  6. hasSideEffect // whether this pass has side effects

  主要方法:

  1. FrameGraphHandle read(FrameGraph& fg, FrameGraphHandle const& handle, bool isRenderTarget = false);将资源handle添加到reads列表中,保证不重复,且会确保相应资源描述为backend::TextureUsage::SAMPLEABLE
  2. FrameGraphHandle write(FrameGraph& fg, const FrameGraphHandle& handle);主要操作为:将资源的version加1,之前/传入的handle就无效了。如果资源是imported的,则将hasSideEffect置true。创建一个新的handle返回。
  3. void declareRenderTarget(fg::RenderTarget& renderTarrget);添加一个renderTarrget的id到renderTargets列表中。
  • ResourceAllocator: 提供了RT、Texture的创建和销毁实现。关于Texture的创建和销毁,维护了两个列表:在用的Texture和cache的texture。销毁时可以不执行真正的纹理销毁而是将其加入到cache列表(从在用列表中移除)。这样下次如果需要创建新的纹理时先去cache列表中找,如果找到就不用创建新的纹理了,将其添加到在用列表中。并提供了一个gc实现来释放cache的纹理资源。
  • FrameGraph::Builder:构造时需要传入FrameGraph和PassNode的引用,提供一些资源的创建和销毁操作转发到缓存的FrameGraph/PassNode引用对象上。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以上是FrameGraph相关的基础类,关于FrameGraph类:

  • 构造时需要传入一个ResourceAllocator
  • 主要属性:PassNode列表,ResourceNode列表,RenderTarget列表。ResourceEntryBase列表、RenderTargetResource列表(这两个列表存放真正的资源)。Alias资源重命名列表。
  • 模板方法

    template <typename Data, typename Setup, typename Execute>
    FrameGraphPass<Data, Execute>& addPass(const char* name, Setup setup, Execute&& execute)

   添加一个pass到pass列表中,并执行指定的Setup操作

  • void present(FrameGraphHandle input)方法为‘input’添加一个引用,防止被cull掉。
  • bool isValid(FrameGraphHandle r)。判断一个handle是否有效。主要判断其node.version和指向资源的version是否相同。A resource handle becomes invalid after it's used to declare a resource write
  • importResource方法:引入一个外部RT。主要操作:创建一个纹理资源handle/node(并未指向真正的硬件资源)。创建一个RenderTargetResource,填充相应信息,添加到cache列表中,等待resolve。
  • FrameGraphId<T> import方法:引入一个现有resource。主要操作:创建一个imported标记的ResourceEntryBase指针(缓存到ResourceEntryBase中),然后创建一个ResourceNode。返回handle。
  • moveResource(FrameGraphHandle from, FrameGraphHandle to)方法:all handles referring to the resource 'to' are redirected to the resource 'from'。 handle 'from' 失效了,返回一个新的handle。
  • compile方法:
  1. 处理mAliases:将所有指向to.resource的node的资源指向from.resource。如果某个rt包含指向from节点的resource的color attachment,且这个resource是imported,那么将这个rt的所有非color attachments置无效。处理所有pass的reads:如果是from,那么改成to。处理所有pass的writes:erase掉所有from。
  2. 更新pass和资源的refCount,并更新每个资源的writer为相应pass节点:pass的置为writes和hasSideEffect之和。resource为read该资源的pass数目。
  3. cull passes and resources:如果资源的readerCount为0,入栈。遍历栈中元素:如果writer不为空,那么writer的引用减一,如果writer的引用变0,将其所有的reads计数减1。最后根据node的计数更新resource的计数(因为多个node可能指向相同的resource)。
  4. resolve所有pass的所有rt。
  5. 对于所有资源(rt和纹理等),计算第一次及最后一次访问其的pass节点。
  6. 遍历所有资源,如果引用计数不为0的话,将其添加到第一次及最后一次访问其的pass节点中的devirtualize/destroy列表中。
  • execute方法:依序执行所有的pass:创建资源,执行,释放资源。
原文地址:https://www.cnblogs.com/redips-l/p/11457541.html