- Simple Introduction - 简介
- Motivation - 值得一提
- Mesh Shading Pipeline - Mesh 着色管线
- Meshlets and Mesh Shading - Meshlets 和 Mesh 着色
- Pre-Computed Meshlets - 与计算的Meshlets
- Data Structure - 数据结构
- Rendering Resources and Data Flow - 渲染资源与数据流
- Cluster Culling with Task Shader - Task Shader 的剔除
新版的 Turing (图灵)结构介绍 通过使用 Mesh Shader 来实现几何可编程着色器管线。Mesh Shader 带来了新的计算模型,在GPU中图形管线中将多线程合作生成精简的网格(meshlets),该网格是直接为 Rasterizer(光栅器)提供数据使用的。应用程序和游戏处理高精度的几何体将得益于灵活的两个阶段,允许高效的 culling(剔除),程序生成的LOD(level-of-detail) 技术。
Motivation - 值得一提真实的世界中视觉是非常的丰富的,各种几何体的形状,还有各种摆放的位置。特别是户外场景可以成百万或上千万的物件、元素的数量(岩石、树、小的植物、等等)。CAD 模型的呈现就类似的挑战了这两点,复杂的表面形状,就像是由许多的很小部分组成,例如太空飞船。图1展示了一些示例,关于当代的图形管线中,使用 vertex(顶点),tessellation(曲面细分),和 geometry(几何) shader,instancing(实例化绘制)和 multi draw indirect(非立即绘制的:延迟多绘制),也是非常的高效的,但仍然限制于全屏分辨率时的几何体到达了 上千万的三角形 和 上十万的对象。 图1。用巨量的复杂几何体来提升逼真度。
其他的使用案例不会展示像上面的包含大量几何体,而是合理的计算(粒子、文字、代理对象、点云)或生成形状(电子工程布局,vfx 粒子,带条、拖尾,路径渲染)。
后面我们将看看使用 mesh shader 来加速渲染大量三角形的网格。原始的网格被分解为更小的 meshlets ,如 图2 的展示。理想情况下每个 meshlet 用于优化顶点复用。使用新的硬件阶段和和分解调度机制,我们可以并行的渲染更多的几何体而无需 fethcing(获取)所有的数据。 图2。大量的网格被分解到 meshlets,用于 mesh shader 渲染使用。
例如 CAD 中可达上千万级技术细节,说明几何体可以不限制顶点数量、多边形的数量,可以做到非常密集的的数量,密集到一个像素还可以容纳到数个多边形。
例如 CAD 中可达上千万级或亿级别数量的三角形。即使在 occlusion culling(遮挡剔除)后,还是有大量的三角形存在。一些在管线中固定功能可能还一些浪费的工作、浪费的内存加载:
- 顶点批量创建,它在硬件每次都 primitive distributor scanning(图元分布扫描) indexbuffer(索引缓存),即使拓扑没有改变过
- 看不见(背面,视锥体外,或子像素剔除)的顶点和属性数据的也 fetch (获取)
mesh shader 给开发者提供了新的可能性来避免这些瓶颈。新的方法允许内存被一次读取,并保持在 on-chip (芯片)中,而不是之前的方法,例如,基于 compute shader 的图元剔除(查看 脚注3,4,5),可见的三角形的索引缓存被计算并延迟绘制。
mesh shader 阶段为 光栅器 生成三角形,内部使用的是协作线程模型来处理,而不是单线程程序模式,类似 compute shader。在新的 mesh shader 管线中在 mesh shader 阶段的前一个是 task shader。task shader 操作类似于 control stage of tessellation(tessellation control stage,曲面细分的控制阶段),为了能动态生成的工作。然而,使用一个协作线程模式而不是像tessellation的输出决定输出的方式,它是输入和输出都是用户定义的。
简单的比较一下 on-chip 的几何体创建 与之前的死板的方式,与带有限制的 tessellation 和 geometry shader 的线程只能用于特定的任务,如图3 的展示。
图3。Mesh Shader 代表着在处理复杂几何的逐步步骤
一个新的两个阶段的管线可替代传统的 attribute fetch, vertex, tessellation, geometry shader 管线。这个新的管线包含一个 task shader 和 mesh shader:
- Task shader:一个可编程单元,它是在 workgroups 工作组中操作生成每个需要(或不需要)的mesh shader 工作组。
- Mesh shader:也是一个可编程单元,它在 workgroups 工作组中操作,并允许生成图元。
mesh shader 阶段为 rasterizer 光闪器生成三角形,内部使用到的方式就是上面提及到的写作线程模式。 task shader 操作类似与 tessellation 阶段的 hull shader,为了可动态生成的工作。然而,类似 mesh shader一样,task shader 也使用写作线程模式。它们的输入和输出都是用户定义的,而不是像 tessellation 中拿一小块数据来决定输出的内容。
pixel/fragment shader 没有影响。传统的管线仍然能依赖用于使用提供很好的效果。图5 高亮了管线风格的差异。
Mesh Shader 的管线 mesh shading pipeline (网格着色器管线)替换了一般的 VTG pipeline 管线(VTG = Vertex / Tessellation / Geometry)。
新的 mesh shader 管线为开发者提供了一些好处:
- Higher scalability:更高的稳定性着色器单元,减少固定函数对图元处理的影响。通用性,现在 GPUs 将可以用于更多不同的应用程序中,添加更多的内核,和提升 着色器通用内存,和算术性能。
- Bandwidth-reduction:减少带宽消耗,更加直接的重复顶点(可复用的顶点),再许多帧中都可以复用。当前的 API 模型意味着 index buffer 在硬件中每次都扫描。巨量的 meshlets 意味着更高的顶点复用,也降低了对带宽的需求(bandwitdh requirements)。还有开发者可以引入他们自己的压缩或程序生成的调度。task shader 的 expansion/filtering 都是可选的,可以完全的跳过这些数据的获取。
- Flexibility:灵活性,它是定义 mesh topology(网格拓扑)和创建图形的工作。之前的 tessellation shader 限制于 固定的 tessellation 模式, geometry shader 忍受着低效的线程,不友好的编程模型方式来在每个线程创建三角带条。
Mesh shading 用的是 compute shader 的编程模式,给开发者自由的使用线程来处理不同的共享数据。当 rasterization(光栅化)禁用了,两个阶段可以用于通用计算的工作。 图5。Mesh shader 表现的类似与 compute shader,使用写作线程的模型。
但 mesh 和 task shader 都是 compute shader 编程模型,使用协作线程来计算他们的结果,no inputs other than a workgroup index(除了 workgroup 索引外都不需要输入的数据)。这些执行在图形管线;因此硬件直接管理内存在多个阶段间的传输并保持在芯片中(kept on-chip)。
我们将展示如何处理图元剔除的例子,线程可以在一个 workgroup 中访问所有的顶点。图6 代表 task shader 可以提早剔除的能力。 图6。task shader 是可选的,task shader 开启可提前剔除来提升 throughput(吞吐量)。
通过 task shader optional expansion(可选的展开)允许提早的剔除图元组,或是直接的标记 LOD。该机替代了 instancing 或是 multi draw indirect 的方式来绘制小网格。这些配置类似与 tessellation control shader 设置如和细分一小块表面(~task workgroup)和影响要创建多少个 tessellation evaluation 的调用(~mesh workgroup)。
在一个 task workdgroup 能发射(生成)多少个 mesh workdgroups 是有限制的。第一代硬件最大支持 每个 task 任务生成 64K 子空间。在 mesh 子对象的总数没有限制,通过所有 tasks 执行 draw call 绘制。同样的,如果没有使用 task shader,draw call 时 的大量的 mesh workgroups 生成是没有限制的。图7 表示了这个工作。 图7。Mesh shader 工作组流
第T个task的children子任务都会保证在第T-1个之后执行。然而,task 和 mesh workdgroups 工作组是完全管线化的,所以是不需要等待之前的 childrene task 任务完整。
task shader 一般用于动态的生成或是过滤工作。静态的设置受益于单独使用 mesh shader。
光栅器输出的网格和图元都是保留的。光栅器禁用的话,task 和 mesh shader 可用于实现基础的 compute-trees (计算树)。
Meshlets and Mesh Shading - Meshlets 和 Mesh 着色每一个 meshlet 代表着一个可变的顶点和图元的数量。连接的对应图元是没有限制的。然而,他们的 shader code 的数量必须在限制的范围内。
我们推荐使用 64 个顶点 和 126 个图元。126中的’6’没有打错。第一代的硬件分配图元的索引使用 128 字节并预留 4 字节作用图元的数量。因为 3 * (126 + 4)
就是 3 * 128 = 384
个字节块。超过 126 个三角形将分配到下一个 128 字节(说实话,我对这英文表达能力、和我自己的理解能力表示怀疑,我看过一些其他的教程英文表达能力的清晰度,绝对比 NVidia 这篇好很多,为何会酱紫。。。)84 和 40 都都是很好的数值。
在每个 GLSL mesh-shader 代码中,workdgroup 在图形管线分配 大量固定的网格内存。
最大值,与大小 与 图元的输出如下定义:
分配的每个 meshlet 的大小依赖于编译期间的决定的大小,就像 输出的attributes 是参考shader的。分配的越少,能在硬件并行运行的 workdgroup 就可以越多。workdgroup 共享的一块在 on-chip 上的共用内存都是可以访问的。因为我们推荐输出的或是共享的内存尽可能这块共享内存。这在现在的着色器是可行的。然而,内存的占用量将会更高,自从我们允许更大量的顶点和图元的数量在当前编程模式中。
// Set the number of threads per workgroup (always one-dimensional).
// 设置每个 workdgroup 的线程数量(总是一维的)
// The limitations may be different than in actual compute shaders.
// 限制可能与 compute shader 不同。
layout(local_size_x=32) in;
// the primitive type (points,lines or triangles)
// 图元类型(点,线或三角形)
layout(triangles) out;
// maximum allocation size for each meshlet
// 每个 meshlet 的最大分配大小
layout(max_vertices=64, max_primitives=126) out;
// the actual amount of primitives the workgroup outputs (
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?