Vulkan进阶-输入附件和Subpass以及One Pass Defer技术

news/2024/7/8 12:06:06 标签: vulkan

原文链接:https://zhuanlan.zhihu.com/p/648162775

        inputattachment和subpass是vulkan中比较有特色的模块,inputattachment可以用于管线的描述符中用来表示缓冲区的图像视图,而subpass讲的是renderpass中的子渲染流程,切换subpass会改变渲染管线但是不会切换渲染缓冲区(framebuffer)。

        inputattachment可以被用来按像素点读取framebuffer中的内容,也就在一个renderpass内当执行到某个subpass的Fragment阶段,可以读取前一个subpass在与当前着色像素相同位置的前一个subpass的颜色值,如下图所示,第二个subpass渲染了UI,并叠加到了前一个subpass的颜色缓冲上。

编程实现

设置描述符

vulkan中提供了相应的描述符来描述输入附件,对应的在描述符集合中也要设置好描述符的类型和对应的imageInfo。

std::array<VkDescriptorImageInfo, 2> descriptors{};
descriptors[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
descriptors[0].imageView = attachments[i].color.view;
descriptors[0].sampler = VK_NULL_HANDLE;

std::array<VkWriteDescriptorSet, 3> writeDescriptorSets{};
writeDescriptorSets[0].dstSet = descriptorSets.attachmentRead[i];
writeDescriptorSets[0].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
writeDescriptorSets[0].descriptorCount = 1;
writeDescriptorSets[0].dstBinding = 0;
writeDescriptorSets[0].pImageInfo = &descriptors[0];

同时要注意的是创建图片的时候需要设置使用为输入附件,如下:

imageCI.usage = usage | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;

shader读取

layout (input_attachment_index = 0, set = 0, binding = 0) uniform highp subpassInput scene_input_color;
layout (input_attachment_index = 1, set = 0, binding = 1) uniform highp subpassInput ui_output_color;

layout (location = 0) out highp vec4 color;

void main()
{
    highp vec4 scene_color = subpassLoad(scene_input_color);
    highp vec4 ui_color = subpassLoad(ui_output_color);

    highp float blend_weight = ui_color.a;
    color = mix(scene_color, ui_color, blend_weight);
}

在shader中按照描述符集合中绑定的下标声明subpassInput,然后使用subpassLoad读取,这里并不需要像sampler那样输入坐标采样颜色,因为input attachment和当前操作的缓冲区在同一个renderpass的framebuffer里边,可以按对应位置的像素读取。

如上述片段着色器代码所示,后处理之后的颜色缓冲是scene_input_color,UI渲染结果保存在ui_output_color,使用subpassLoad逐像素点读取颜色,并根据ui_color的透明度混合。

Subpass作用机制深入研究

        如果只需要按图像像素对其读取input attachment,并不一定要设计subpass这一机制,比如OpenGL ES中有Framebuffer Fetch就可以按像素对其读取framebuffer,这样同样可以避免采样的开销。但是为什么要使用subpass而不是使用多个renderpass,然后通过Framebuffer Fetch读取呢?

        原因在于使用subpass设计渲染管线可以利用上Tile-Based GPU架构来节约内存带宽,同一个renderpass中的subpass之间,framebuffer是完全一样的,区别在于graphic pipeline和render command不一样。站在GPU的视角来说,对于每个subpasse而言,只需要分别执行一次vertex shader后,将生成的triangle list存入主存,然后再执行fragment shader时,先对framebuffer切Tile,然后逐个Tile执行所有的subpass,用伪代码描述如下:

for(auto &tile in tiles)
{
	for(auto &subpass in subpasses)
	{
		FragmentShaders[subpass](tile);
	}
}

        这样的好处在于,执行FragmentShader的时候,可以将Tile的Framebuffer数据存储在In-Chip Cache上面,只有当这个Tile所有的subpass都执行完了之后,才会把渲染结果写回dram,这样subpass对于framebuffer的读写带宽开销可以省略很多,有效的利用了tile-based架构节约内存带宽。特别是对于延迟渲染管线而言,将gbufferpass和lightpass以subpass的形式放在同一个renderpass中执行,可以实现one pass defer,能够让Gbuffer完全只存在于片上缓存中,极大地节省了系统带宽,如下图:

传统的延迟渲染管线

但是使用inputattachment也会导致一些问题,因为要提高subpass执行的效率,输入附件被设计为了只能按照对应像素点读取,而不能读取其他位置的,那么在一些后处理操作中,比如bloom特效就无法使用这一技术,因为要对像素点每个邻域内亮度做一次高斯滤波,只能用多个renderpass进行迭代,或者干脆用Compute Shader搞定。

One Pass Defer

        但是使用inputattachment也会导致一些问题,因为要提高subpass执行的效率,输入附件被设计为了只能按照对应像素点读取,而不能读取其他位置的,那么在一些后处理操作中,比如bloom特效就无法使用这一技术,因为要对像素点每个邻域内亮度做一次高斯滤波,只能用多个renderpass进行迭代,或者干脆用Compute Shader搞定。

 

参考资料:

Vulkan input attachments and sub passes

https://stackoverflow.com/questions/43632903/why-do-input-attachments-need-a-descriptor-set-to-be-bound

https://community.khronos.org/t/specialization-constant-for-input-attachment-index/108503

本文使用  Zhihu On VSCode 创作并发布

http://www.niftyadmin.cn/n/5537215.html

相关文章

【Vue】使用html、css实现鱼骨组件

文章目录 预览图组件测试案例预览图 组件 <template><div class="context"><div class="top"><div class="label-context"><div class="label" v-for="(item, index) in value" :key="ind…

机器学习——强化学习状态值函数V和动作值函数Q的个人思考

最近在回顾《西瓜书》的理论知识&#xff0c;回顾到最后一章——“强化学习”时对于值函数部分有些懵了&#xff0c;所以重新在网上查了一下&#xff0c;发现之前理解的&#xff0c;包括网上的大多数对于值函数的描述都过于学术化、公式化&#xff0c;不太能直观的理解值函数以…

uni-app使用ucharts地图,自定义Tooltip鼠标悬浮显示内容并且根据@getIndex点击事件获取点击的地区下标和地区名

项目场景&#xff1a; uni-app使用ucharts地图,自定义Tooltip鼠标悬浮显示内容并且根据getIndex点击事件获取点击的地区下标和地区名 例如&#xff1a; 问题描述 官方给的文档有限&#xff0c;需要自己下载地图json数据然后自己渲染和编写鼠标悬浮显示内容以及获取点击地址…

深入 Laravel 的错误处理与异常处理机制

引言 Laravel 是一个优雅而强大的 PHP Web 应用框架&#xff0c;它提供了一系列工具来帮助开发者处理应用中的错误和异常。了解 Laravel 的错误处理和异常处理机制对于构建健壮和用户友好的应用程序至关重要。本文将深入探讨 Laravel 如何实现错误和异常处理&#xff0c;并提供…

【3维BFS】个人练习-Leetcode-LCP 79. 提取咒文

题目链接&#xff1a;https://leetcode.cn/problems/kjpLFZ/ 题目大意&#xff1a;给一个矩阵matrix[][]&#xff0c;元素为小写英文字母。给一个字符串mantra&#xff0c;求从矩阵的(0,0)位置开始&#xff0c;可以移动&#xff08;上下左右&#xff09;或者提取字母&#xff…

使用deep修改前端框架中的样式

目录 1.deep的作用 2.使用方式 3.特别说明 scoped 的实现原理&#xff1a; !important 1.deep的作用 /deep/、::v-deep 和 :deep 都是用于穿透组件作用域的选择器。它们的主要目的是允许开发者在父组件中直接选择并样式化子组件内部的元素&#xff0c;即使这些元素被封装在…

数据分析入门指南:从基础概念到实际应用(一)

随着数字化时代的来临&#xff0c;数据分析在企业的日常运营中扮演着越来越重要的角色。从感知型企业到数据应用系统的演进&#xff0c;数据驱动的业务、智能优化的业务以及数智化转型成为了企业追求的目标。在这一过程中&#xff0c;数据分析不仅是技术的运用&#xff0c;更是…

深入浅出3D感知中的优化与基于学习的技术1(原创系列)

近期几乎看了所有有关NERF技术论文&#xff0c;本身我研究的领域不在深度学习技术方向&#xff0c;是传统的机器人控制和感知。所以总结了下这部分基于学习的感知技术&#xff0c;会写一个新的系列教程讲解这部分三维感知技术的发展到最新的技术细节&#xff0c;并支持自己最近…