我已经写了好几篇有关piet-gpu的博客文章,更广泛地说是关于GPU计算的文章,但是本文的范围有所不同。它不会展示原型并提供研究结果,而是会针对其发展方向制定一个大胆而雄心勃勃的计划。我发现这种愿景令人信服,这激发了我投入大量精力来掌握一些困难的材料。宏伟的愿景远不止一个人可以做,所以我自己做一些,也许激发其他人的协作。
piet-gpu的完整愿景是一个2D渲染引擎,它比当前最新技术快得多,质量更高且更灵活,并且可以在各种硬件上运行。我将详细介绍为什么我认为可以实现这个目标,以及实现该目标需要进行哪些工作。
piet-gpu代码库的当前状态是一个早期原型,主要用于测试这些想法是否可行,并在渲染问题的一些较密集的部分上收集经验性能数据,到目前为止主要是抗锯齿矢量填充和描边。
piet-gpu的中心主题是执行计算着色器中的大多数或所有渲染步骤。这与传统的基于栅格化的2D渲染方法截然不同,后者将场景(在CPU端)分成一系列绘制调用,然后将其发送到GPU。当绘制调用的映射很简单时(这是由文本和简单图形元素组成的imgui风格的UI的情况),这非常好用,但是要少得多。在广泛使用GPU计算时,piet-gpu从Spinel中获得了很多启发。
在管道的两个特定阶段,使用计算着色器会产生深远的影响。首先,在早期阶段,它可以让GPU提取场景描述,即尽可能多地对场景进行简单的二进制编码。相应地,这使得工作的CPU端部分变得简单而高效,从而在复杂的场景中实现了更高的帧速率而不会产生混乱。
其次,在最后一个阶段(“精细光栅化”),使用向量寄存器而不是全局内存中用于中间RGBA值的纹理缓冲区,在计算着色器中进行合成。
注意,收益取决于场景。对于静态(或大多数情况下为静态)场景,CPU端编码成本可能没有多大关系,因为它可以提前完成。同样,如果场景不需要复杂的合成,而只是一系列alpha混合绘制,则现有的栅格化管道可以非常有效地处理这些场景。但是piet-gpu应该在动态场景中进行很多遮罩和混合,而现有的2D引擎可能会遇到困难。
中间阶段也受益。粗略栅格化步骤可以采用复杂的逻辑,以实现对小块粒度的优化,否则将依赖于蛮力。
应用程序的动态程度各不相同。在一个极端情况下,场景大部分是静态的,可能包含一些可变元素,并且可能在合成时完成了一些动画(我认为这是iPhone的UI风格,因为它非常适合诸如Core Animation之类的机制)。另一方面,每个渲染帧都与之前完全不同,因此每次都需要完全从头开始进行编码。这些应用程序非常适合“立即模式”方法。
我对中间的案件最感兴趣。我相信最好的方法是拆分编码过程,以便可以将场景图的静态部分编码一次为保留的场景图片段,然后可以将这些片段与场景的动态编码部分一起缝合在一起,最少的CPU工作量。
piet-gpu的许多体系结构都旨在支持该目标。值得注意的是,全局仿射变换并未纳入向量路径的编码中,因此可以使用不同的变换(在场景中多次)实例化向量路径的相同二进制编码。应用的转换是在管道的早期在GPU端完成的。因此,对变换进行动画处理应该非常有效,并且将以具有矢量清晰度的全分辨率重新渲染矢量路径。
即使这样,完全实现保留的场景图片段将是视觉中比较困难的部分之一。它需要一个良好的API来表示保留的片段,以及递增更新参数(例如转换和不透明度)。它还需要一种复杂的资源管理方法,以便可以在GPU端高效地缓存支持保留的片段的资源,而不会占用相对稀缺的GPU内存。因此,我将首先关注即时模式,因为这也是一个重要案例。但是请不要误会,保留场景片段的目的是激发许多设计决策,尤其是使我摆脱诸如在编码期间应用仿射变换等CPU端之类的捷径。
piet-gpu面临的挑战之一是缺乏用于便携式GPU计算的适当基础架构。大多数研究都是在CUDA上完成的,因为它是当今唯一真正可行的GPU计算平台,但是从根本上讲,不可能在Nvidia之外的任何硬件上部署工作。
我坚信Vulkan会成为使用GPU计算资源的可行的低级平台。我也不是唯一遵循这些思路的人。 VkFFT项目是一个令人印象深刻的演示,它证明了Vulkan部署的一种数学密集型算法的性能与CUDA版本一样。此外,在Vulkan(尤其是TensorFlow Lite)上运行机器学习工作负载还需要采取早期步骤。
当然,虽然可以在相当广泛的硬件上运行Vulkan,但这并不能解决所有可移植性问题。 “ Runs Vulkan”不是二进制文件,而是通往大量可选功能和限制的门户,这些功能和限制来自硬件,驱动程序和兼容性垫片的各种组合(vulkan.gpuinfo.org是绝佳的资源)。苹果公司特别要求使用金属。从理论上讲,MoltenVk(或更广泛地讲是Vulkan可移植性扩展)可让您在Apple硬件上运行Vulkan代码,但实际上并不能完全起作用(请参阅#42),并且DX12与Vulkan相比具有兼容性和集成优势在Windows上; Haswell和Broadwell等较老的CPU根本不支持Vulkan。为此,我已经启动了一个可移植层(piet-gpu-hal),该层应该可以在其他API上本地运行。
兼容性层具有重叠的目标,例如wgpu和更广泛的WebGPU。为什么不像Rust生态系统所做的那样仅仅使用它呢?
这很诱人,但目标也有所不同。主要是为了保持piet-gpu的运行时光和启动时间更快,我真的很想提前进行着色器编译,以便二进制嵌入目标平台的中间表示形式(适用于Windows 10的DXIL等) )。此外,通过直接使用Vulkan,我们可以尝试使用高级功能,例如子组,内存模型等,而wgpu尚未很好地支持这些功能,尽管肯定可以添加这些功能。我不知道这些高级功能有多大作用,但这是需要解决的研究问题之一。如果增益适中,则实施它们的优先级较低。如果收益显着,则应该增加诸如wgpu之类的运行时包含它们的动力。
另请参见下面有关增量存在的部分,这是wgpu中尚未很好支持的另一个功能,因此使用较低级别的API可以减少摩擦。
同时,wgpu继续改进,包括致力于使运行时更精简(使用新的naga着色器编译引擎而不是spirv-cross是这样的进步)。我的感觉是:piet-gpu拥有自己的兼容性层的主要原因是,我们可以真正阐明和提高对更通用的GPU计算运行时的要求。
以计算为中心的方法的一个挑战是(尚)没有铁定的保证,GPU和驱动程序实际上将能够处理计算着色器和资源管理模式(实际上,后者可能更具挑战性, -gpu依靠描述符索引在精细光栅化过程中处理多个图像。
有许多方法可以解决此问题,包括构建混合管道以及进行大量的兼容性工程,以很好地将平台定位为过时的目标。但是我非常担心复杂性负担,以及如果问题带来兼容性挑战,就无法从绝对最佳的解决方案中解脱出来。
我更倾向于使用CPU渲染。 Blend2D等项目表明,CPU渲染可以实现高性能,尽管远不及GPU。当然,这意味着要提出算法的CPU实现。
一种有趣的可能性是将Vulkan计算着色器自动转换为CPU可运行的代码。这种方法的优点是为流水线维护一个代码库,减少添加新功能的摩擦,并保证像素完美的一致性。最大的问题是这种方法是否足够有效。获得初步答案的一种很好的方法是使用SwiftShader或Mesa的Lavapipe,它们可以通过JIT生成CPU边代码。显然,由于启动时间和二进制大小的原因,最好提前交付翻译的着色器,但这是一个实际问题,而不是概念问题。
有将着色器编译时转换为CPU代码的示例。 spirv转换为ispc转换器是一种有趣的方法,它似乎并未得到积极开发,但似乎是从着色器获得相当好的CPU性能的途径。在WebRender生产中实际使用的另一个是glsl-to-cxx。
具有统一着色器源的真正通用计算基础架构的意义远不止2D渲染。在此领域最有可能投资的领域是AI(部署到消费类硬件;对于服务器端和内部部署,他们显然只会使用CUDA和神经加速器)。我还要指出,这个问题表面上在OpenCL的范围内,但是到目前为止,它们仍未能交付,主要是因为它们一直完全依赖GPU制造商的驱动程序支持。我希望会发生一些事情。
这可以采取另一种完全可行的途径,较少依赖着色器编译基础结构:与GPU并行开发的软件渲染器。现有的可能使用的Rust代码库包括raqote和tiny-skia。这些作为社区子项目更有意义(见下文)。
任何2D库的重要组成部分是文本渲染。这实际上可分为文本布局和字形绘制。两者都很重要。
当今的Piet主要是平台2D图形库上的抽象层,文本也同样如此。最近,在常见的富文本API和DirectWrite和Core Text的实现方面,我们取得了一些非常好的进展。但是,当前缺少Linux后端。 (作为占位符,我们使用开罗“玩具文本API”,但是由于多种原因,它并不令人满意。)
我认为,出于几个原因,我们希望摆脱对平台功能的抽象。一是难以确保结果一致。另一个是很难添加新功能,例如hz风格的对正(见下文)。因此,我们遵循与Web浏览器类似的轨迹。
作为与piet-gpu相关的项目,我很想在Rust中构建(或指导某人构建)文本布局引擎,该引擎适合大多数UI工作。这不是我第一次我写了Minikin的原始版本,它是Android Lollipop中首先提供的文本布局引擎。
最终,我希望piet-gpu支持3个字形数据源进行绘画。
第一个是平台生成的位图。这些具有匹配本机UI的优势,并且还充分利用了提示和子像素RGB渲染的优势,从而提高了对比度和清晰度。这些位图将主要在CPU端渲染,并上传到纹理图集。实际的栅格化只是纹理查找,应该超级有效。
第二个是字形轮廓的动态矢量渲染。该资源针对大型文本,动画(包括支持捏到缩放样式的手势)以及可能扩展到3D(包括VR和AR)进行了最佳优化。在高dpi的屏幕上,缺少提示和RGB子像素渲染并不是一个严重的问题,也不是移动设备上的期望。 piet-gpu的早期测量表明,在大多数GPU上应该可以保持60fps的文本密集场景,但是功耗可能不是理想的。
因此,第三个源是通过字形缓存进行的矢量渲染,这是前两个源的混合体。最初,缓存的管理将在CPU端进行,并在编码过程中进行管理(可能使用Guillotière,Étagère或类似方法),但是在将来,我们可能会探索GPU端算法来并行管理缓存,从而进一步降低CPU需求。
一个非常有趣的可能性是将渲染可变字体的大部分工作转移到GPU。有理由相信这会很好地起作用:可变字体技术基本上是基于将“增量”矢量与基本函数相乘并将其相加,这是最适合GPU的任务。
挑战在于以GPU友好的格式表示坐标数据和增量。 glyf和gvar表格式设计用于紧凑的数据表示和标量CPU的(合理地)简单解码,但是对于大规模并行算法却具有挑战性。解码为固定大小的数字很简单,但是可能会使用大量的GPU内存和带宽来表示字体数据(尤其是CJK字体存在问题)。一种有趣的方法是使用自同步可变整数编码对基础数据进行重新编码,这将减少内存需求,但保留并行处理能力。
GPU端可变字体渲染的主要优点是可以对可变字体轴进行有效的动画处理,并且还可以通过调整轴来改善文本布局,例如提高hz率先提出的段落对齐的质量原型,并且最近使用amstelvar进行了演示,或者更好地支持书法样式和复杂的脚本,例如使阿拉伯语的kashida更加美观,而所有这些都没有显着降低性能。
长期以来,GPU 2D渲染的质量问题一直很复杂。许多基于栅格化的方法都依赖于GPU固定功能管线中的MSAA,MSAA可能并不总是可用,或者可能仅在较低设置下才适用(尤其是在移动设备上)。因此,GPU加速的2D渲染质量已不受欢迎。
以计算为中心的方法改变了故事。所有实际像素都是通过代码生成的;渲染的质量完全取决于该代码的作者。当前的piet-gpu代码库使用精确区域方法进行抗锯齿(按照libart的传统),因此在低或中等设置下不表现出MSAA的步进或粒度特性。质量应该与优质的软件渲染器相同,因为它是一种软件渲染器,只有一种恰好在硬件上运行,并且并行性比任何合理的CPU高几个数量级。
即使如此,我相信有可能做得更好。与CPU绑定的渲染器几乎没有足够的性能来将像素显示在屏幕上,因此需要采取任何快捷方式来完成该性能预算中的工作。 GPU通常具有更多的原始计算带宽一个数量级,因此可以使用余量来提高质量。
我所想的细节本身可能是一篇博客文章,但我将勾勒出重点内容。
也许最重要的质量问题是所谓的“合并伪像”问题,即合并抗锯齿元素时发生的接缝(请参阅#49)。关于GPU上2D渲染的大多数学术文献都解决了这个问题。我认为在piet-gpu架构中可行,基本上是通过将精细光栅化器中的软alpha合成与基于超级采样的合成交换掉。在此阶段的某些学术文献中也抓住了机会,使用了比盒式过滤器更复杂的重建过滤器,但我仍不相信这种改进是值得的,尤其是随着物理显示分辨率的提高。
潜在质量改进的下一个主要领域是正确地使用伽玛。这是一个令人难以置信的棘手区域,因为从理论上讲,“正确”的伽玛方法通常会产生文本和发际线笔画,看起来很脆弱。另一个问题是文档兼容性。只需更改发生Alpha混合的颜色空间的伽玛值,就会更改结果的颜色。一个完美的解决方案可能需要与驱动渲染器的应用程序合作。如果设计时考虑到了伽玛完美的渲染效果,那么就没有真正的问题,但是,否则可能需要应用各种启发式方法才能获得良好的效果。 (请注意,词干变暗是一种专门用于文本呈现的方法,其中之一是平台之间存在很大差异。)
在驱动低dpi显示器(仍然存在)时,一种改善质量的机会是更复杂的RGB子像素渲染。目前,它基本上仅是文本,但是也可以应用于矢量渲染,并且通常无法经受复杂的合成,因为具有透明背景的RGBA纹理无法表示RGB亚像素文本。一种解决方案是使用每通道Alpha进行合成,当在计算着色器中进行合成时,可以非常高效地完成合成,但是如果需要将中间纹理缓冲区写出到全局内存中,则会带来严重的性能问题。
这些潜在的质量改进很可能为以下问题提供答案:“为什么要迁移到新的渲染体系结构而不是逐步改进我们现在所拥有的?”
人们对“现代2D成像模型”达成了共识,该模型大致涵盖了PDF,SVG,HTML Canvas和Direct2D,但它并不是一成不变的,并且这些系统中的高级功能也有很大差异(例如,梯度网格更多或少了PDF所特有的功能-该功能是为SVG 2提出的,但后来删除了)。
我喜欢这种一致的2D成像模型,因为我觉得它非常适合UI和文档,这些文档非常丰富和复杂,并且对设计人员非常友好。我认为,也有一些紧张局势正在拉开,这有两个原因。一是它并非总是在GPU上有效地实现,尤其是在嵌套深层的软裁剪和其他非平凡的合成要求的情况下。另一个是可以在GPU上执行某些操作(尤其是使用自定义着色器),而这是标准2D API难以做到的。 Shadertoy显示了着色器中可能发生的许多事情。我想探索的一个想法是水彩笔触(请参阅计算机生成的水彩画以获取灵感)。我认为,距离场和程序噪声可能会走得更远,并且有一个简单的函数可以将距离场和过程噪声转化为用于绘画般合成的绘画值。
成像模型应该走的另一个方向是对HDR的支持(与上面的伽马问题强烈重叠)。这将需要颜色转换,以便在合成管道中进行色调映射,并且可以再次将其编写为着色器。
现有的一种有趣的带有扩展点的2D引擎是Direct2D,它可以让用户提供Cus
......