Vulkan中的RenderPass

Intro

在很多地方你都听说过pass这个概念,比如说Unity shaderlab中一个pass可能就是一段vs和ps再加上一系列的状态,用来进行以此mesh的渲染。
在vulkan中有个名字相似的对象RenderPass,但深入观察就会发现它只承担了我们广义的pass中一部分“框架”功能--描述了pass之间的流程,比如这个pass用到了哪些格式的attachment,subpass会有几个inputattachment,subpass之间是否存在依赖等等。
而更多承载了所谓pass功能的对象应该是GraphicPipeline,在其中设置shader的spirvCode,固定功能---而且还需要一个RenderPass(准确来说是一个Compatible renderPass),并且指定这个Pipeline会在RenderPass的第几个subpass中被用到。
进入渲染的时候,我们需要绑定FrameBuffer,beginRenderPass,绑定pipeline,进入下一个subpass,再绑定pipeline....对了,创建FrameBuffer也需要RenderPass。
可以看得出来RenderPass是个很不得了的对象,到处都得用得到,甚至有些...过度耦合了?
所以你会看到知乎上,很多博客上会有人推荐你启用VK1.3的某个拓展,DynamicRendering,这东西几乎是回归了传统的pass概念,你只需要去维护Pipeline,Attachment,绑定,渲染....没有FrameBuffer,RenderPass,Subpass简直太美好力
但是..哼哼..马上就会发现这东西在移动端竟然不支持,为甚么,初生Khronos!

当然我得承认有部分人只会在pc上玩vk....但是都用vk了不考虑移动端,那为甚么不去学dx12,那个可是真的简单奥
但是随着pc端显卡也有tbdr化的趋势,dx12也加入了renderPass,sad

这背后其实很奇妙,涉及到很多Vendor背后的工作,甚至硬件工作原理,我也是在一个个疑惑中拼出来些许所谓的非官方但正确的解答,所以有问题别打我就好(>︿<)

速速看spec

Renderpass compatibility

我打算先从所谓的Compatible RenderPass讲起...我的发现!
首先我恳请任何读到此处的人去阅读vulkan spec 的这一部分,并且尝试理解,写出一个检验两个renderPass是否compatible的函数
别告诉窝泥找不到哇,窝贴在这里辣Render Pass Compatibility

不知道你能否写的出来,但我是被这模糊不清的描述震惊到了

Two attachment references are compatible if they have matching format and sample count, or are both VK_ATTACHMENT_UNUSED or the pointer that would contain the reference is NULL.

attachment reference哪来的format和sample count啊?我姑且猜测为是其指向的attachmentDesc。

Two arrays of attachment references are compatible if all corresponding pairs of attachments are compatible. If the arrays are of different lengths, attachment references not present in the smaller array are treated as VK_ATTACHMENT_UNUSED.

corresponding pairs?什么样的pair是corresponding的?pairs of attachments?所以是在说指向有相同index的attachment的reference,还是仅仅只是在数组中拥有相同下标的reference呢?

Two render passes are compatible if their corresponding color, input, resolve, and depth/stencil attachment references are compatible and if they are otherwise identical except for:

Initial and final image layout in attachment descriptions

Load and store operations in attachment descriptions

Image layout in attachment references

这里就好理解多啦,“除了以下的东西可以不一样,其他全部必须一致,当然前面的要求也得满足就是!”

好吧好吧到这里就得求助一下网络了,但是中文互联网上我搜不到关于这个话题的讨论...哭

最后也是花了好一阵子找到了这个讨论
我靠提问老哥怎么和我钻一个牛角尖里去了,肯定不是我的问题,就是spec写的不好!

最后得到了一个简单易懂的伪代码

2 RenderPasses are compatible if each corresponding pair of subpasses are compatible
2 subpasses are compatible if each corresponding pair of AttachmentReferences are compatible
2 AttachmentReferences are compatible if
    both is either nullptr or UNUSED, or neither are and:
    both have the same format and sample count

当然最好理解的还是Validation layer的校验代码,真的是薄纱Spec

Compatible RenderPass的作用

因为有Compatible Renderpass的存在,就说明...我们可以用一个RenderPass去创建给另一个RenderPass用的Pipeline,FrameBuffer
也就是说...RenderPass虽然会传进那些对象的CreateInfo,但并不会真的在对象其内部存有一个句柄,相反只是作为一个临时对象进行一些设置...

所以RenderPass还是用来解耦合用的咯?听着有些奇怪啊....

不对!在BeginRenderPass中需要显示指定RenderPass,为甚么这些工作不能延后做呢?还是太耦合了!狗比Khronos设计就是辣~鸡~

这些质疑完全可以用所谓的Vulkan设计理念来搪塞过去:把一切状态的设置提前。但仍然让人十分不满意

这里我要抛出一个爆论,在Framebuffer创建这步,你完全可以用VK_NULL_HANDLE赋值给renderPass那一项,虽然validationlayer会报错,但你依旧可以正常运行!
不信,去试试啊


反正你们不会试移动端,也就发现不了移动端会炸了嘿嘿(‾◡◝)

好吧,这东西确实涉及到了Vendor内部的小 秘 密
对于tbdr架构的gpu,创建FrameBuffer时驱动第一次收集齐了所有信息,以便GPU能编译合适的tiling strategy。这步在移动端上格外耗时,也就是FrameBuffer必须提前创建的理由

甚至还有额外的佐证:VK_KHR_imageless_framebuffer代表着你可以在创建FB时不用(延后)提供Attachment/ImageView,但必须给到RenderPass

在BeginRenderPass的时候,被编译好的tiling strategy会随着RenderPass的指定同时被应用到此次RenderPass的渲染中。



于此同时pc端的显卡创建FrameBuffer恐怕只是把几个Attachment揉吧揉吧丢给你玩,然后就有了大家喜欢的Dynamic Rendering(


The END!

尽管我很想再讨论下我目前理解的RenderPass/Subpass对tbdr的优化还有subpassDependency带来的隐式同步,甚至external subpass的同步,但是我要睡觉辣!

END