2019 Rust 图形库之路
介绍
因为,在Unofficial Rust Discord 的游戏开发频道上,大家在谈论图形API 的问题,哪个到了什么地步以及做什么的,人们相互争执和相互纠正, “雨水来得过于猛烈”,一切的一切都变得有些模糊,混乱。所以在这里,我准备 尝试 捋一捋记录。本文旨在为希望使用Rust ,编写图形东西(视频游戏,动画,炫酷的可视化等)的人,但又不知道他们应从哪里开始,提供些背景知识。
你为什么要相信我?因为我需要知道这些东西才能选择ggez
的伙伴,所以最近几年我一直在关注事物的状态。而且因为我对这些东西真的很感兴趣; 而且因为我喜欢写作。就说,虽然我远不是专家,但更像是一个兴致勃勃的观察家。
但是,在深入研究Rust 细节之前,让我们先了解一下图形API 的奇妙世界……
到底事情是怎么的
没有人直接为GPU 编写代码。硬件接口,指令集以及它们实际工作方式的细节受到制造商的严密保护,好吧,除了三个主要制造商中的两位 —— 现在开源了(大部分)驱动程序。因此,实际上只剩下NVidia 是锁源的。GPU 硬件可以快速发展,而不必担心向后兼容性,但最近十年左右,看起来GPU 和CPU 相互通信的基本设计和折衷方法已经达到了更稳定的状态。Anyway,不管实际原因,操作系统的GPU 驱动程序会与硬件进行所有对话,并为(可以是你写的)程序提供与之对话的API 。实际上,有很多这样的API ,您以前可能已经听说过其中一些,所以让我们来看看明星选手:
OpenGL
基本上,称得上是图形API 界的Javascript 。OpenGL 最初是SGI 在1992 年提出的一个合理的好主意,从那时起,它便开始生长,融合,变异,发芽怪异的副枝等。历史累赘比JS 还要糟糕,原因可以一直追溯到90 年代初,那时专用GPU 是唯一的至高科技,而GPU 硬件从那之后却快速发展。那时,您要为GPU 的每个函数调用都提供一个vertex ,且像per-face shading 这样的的事情是非常重要。当前版本为4.6 ,低于3.2 的版本均不值得使用。
OpenGL 是由工业组织Khronos 开发的开放标准,并且在Windows ,Linux 和Mac 上均受支持,此外,它的变体还可以在移动(OpenGL ES)和Web 浏览器(WebGL)上运行。它有一百万个不同的版本,亿个扩展,并且在3.2 版之前,没有办法选择 要使用的哪个版本。大多数时候,您实际使用的版本是“GPU 驱动程序想给您的任何东西”,而GPU 驱动程序的实现是 多为awful (该文章已有几年历史了,现在情况已经好了一些) 。它的API 设计具有完整向后的兼容性,而这可是将近三十年的负重,其中充满了烦人的边缘情况和漏洞抽象,这些抽象在1997 年当年完全有效,但命不长。这里有programming techniques that get around them (PDF 链接) ,但这仍然很复杂。总的来说,就像Javascript 一样,它是所有 主要平台支持的唯一API ,经过精心设计才能 运作良好,并且短时间内不会有变化。
DirectX
与OpenGL 类似,但由Microsoft 拥有。他们设计了API ,仅在Windows 和XBox 上运行,并且我认为 GPU 供应商与Microsoft 合作编写了驱动程序。坦白地说,我对此并不了解很多,但是据我所知,它与OpenGL 很像,除了不向后兼容的能力。这意味着,旧功能可能实际上已被弃用,新版本可能进行了重大的重新设计,从而改变了工作方式的基本要素,总的来说,它与时俱进。另外,由于它得到了Microsoft 的支持,因此在制造游戏在其系统上运行良好这方面下了很大功夫,因此驱动程序通常不会出现故障,而工具则更好。如果游戏仅Windows 玩耍,那么它将在DirectX 上运行。9 之前的DirectX 版本可能不再值得使用,而9~11 版本在功能上与OpenGL 4 大致相当。但是,最新版本的DirectX 12 是新一代,更底层API 的一部分 ,该API 使程序员可以更好地控制GPU 执行和排列事物的细节。而我们也迎来了一个…
Vulkan
新的热点。Vulkan 由OpenGL 所在的行业组织开发,于2016 年首次发布,当前版本为1.1 。如果OpenGL 是GPU Javascript ,那Vulkan 就是GPU C 。更 底层级,更 更广泛的用途,和(潜在的)比OpenGL 更 容易编写快速代码。当然,多数时候这也可能不是你想直接地 使用的东西,因为它真的非常具体且冗长。它不是一个图形API ,而是一个与GPU 对话的接口;而实际的图形API 是您使用Vulkan ,来创建的。
我们为什么需要这个东东?我的理解is not the best ,但我将尝试总结一下:OpenGL 中的许多东西(可能还有DirectX pre-DX12 )涉及到GPU 驱动程序中的state changes (状态改变) ,比如告诉它“切换到这个混合模式”或“加载这个shader ”。这些事情只会影响到一个又大又长的多步骤渲染过程的一部分 ,但是如果你不小心就会发生这种问题:GPU 同时忙于处理依赖该部分状态 的一些事情( 而你,改变了这个状态) 。每当发生重大变化时,GPU 必须等待1000 多个线程完成它们的工作、同步、等待输入,以及然后 告诉它们新的操作模式是什么,并重新使它们走上快乐流程。而这(问题)会导致大量的死亡时间,其中大多数GPU 都无所事事。OpenGL 意外改变一个本不需要影响其他东西,但却实际改变了的设置,结果就是,使得创建这些死亡时间变得非常容易。另外,由于它是如此stateful (富有状态) ,所以GPU 驱动程序很难提供帮助。驱动程序的工作会试图将state changes 合并成块,有效地组织它们,并创建大量命令供GPU 一次完成所有操作,但此时,驱动程序不知道接下来你要告诉它做什么,所以很多都是灵光一闪、猜测和权衡。与JIT- 编译 语言一样,没有明显的原因,它就能简单用一个相对无害的更改,将你扔出“fast path”。
而Vulkan 根本就不会这么做。使用Vulkan ,你可以创建一个巨大的树结构,它的各个部分之间有很多非常明确的设置和关联,准确点 来说:如何运行一系列的处理步骤以及如何处理结果,加载上vertex 数据等的巨大数组,把它放到一个大箱子里,然后把它送到GPU 。然后第二天(就计算机世界的时间) ,GPU 会给你的前门送上一个漂亮的、闪亮的渲染图形帧,按响你的门铃,然后继续下一个帧的工作,因为它已经拿到它的工单。由于GPU 一次基本上获得了绘制一个帧所需的所有信息,所以,所有内容都被显式地指定了,如果程序员若是说“这一帧快,而那一帧慢,两者之间改变了什么? ” —— 而“事实上,他们就可以知道发生了什么变化。此外,GPU 驱动程序有一个更简单的流程,因为它需要猜测您打算做什么的工作更少了;它有了更多的信息,可以尝试调度命令,用于GPU 的实际执行。除了让你的程序运行得更快之外,这还将引出更快、更高效(希望更少的bug )的GPU 驱动程序。
基本上可以说是DirectX 12 ,但由苹果公司制造。它(连同AMD 现在已经被弃用的Mantle 原型)实际上早于Vulkan 和DirectX 12 ,可以说是开启了新一代的底层图形系统。像DirectX 12 我对它了解不多;它是一个类似于Vulkan 的底层API ,那些尝试过它的人说它非常适合使用。不过,苹果过去所有的图形都是通过OpenGL 完成的。他们的工作鼓舞了Vulkan ,这样的话,他们当然可以在它的发展指导上,大有作为。但是,在其他人都已经加盟Vulkan 之后,苹果宣布他们将不再使用Vulkan ,他们制造了一种大家都应该使用的,很酷的新东西叫做Metal ,噢顺便说一下,无论怎样,他们会在某个时候停止支持OpenGL ,如此除了他们之外,没有人可以为他们的硬件编写图形驱动程序。所以,我不在乎Metal 有多好,它的唯一目的就是加强苹果的锁。
说真的。我愿意原谅DirectX 的 亿点点 恶作剧,但我们真的不需要更多的proprietary crap 活在宇宙中。我想写一个游戏,它能在任何人的系统上运行,所以如果我可以避免的话,我永远不会使用Metal 。And I can。这让我们走向了…
真真真Rust!
好吧,尽管上面列出了所有的不同点,但上面所有的东东都是痛苦的 低层,按照一次一行“获取一个vertices 数组和一堆配置数据,并生成一个渲染帧”的执行操作。如果你真的只想把模型推进去,选择一些材质设置,放置一些灯光,得到漂亮的渲染图形,你就需要使用一些更高层次的东西。 “它“可能是Amethyst 游戏引擎。可能kiss3d 或three 。也许你自己写。或者像一个理智的人一样使用Godot 或Unity 。 “但这不是我摇滚的方式,所以让我们继续滚吧”。
所有这些基本图形API 都是in, by and for( 在,通过,和 为) C 编写的,在某些地方可能有一些C++ ,但它们通常作为C 库被公开。而使用Rust 中的raw C API 是相当痛苦的,所以人们为它们编写了Rust 包装器,来让生活变得更美好、更安全。但是还是那句,这些仍是相当底层的系统,并且要做到C API 与Rust 结合时的绝对 安全性,既昂贵又困难,而且it is not always worth the trouble 。也就是说,即使是Unsafe Rust 仍然比C 要酷得多,所以我们还是希望使用更好的Rust 绑定库来编写我们的图形代码。
有许多用于图形的Rust 库,具有安全性和合理性的不同级别。包装OpenGL 的是glium
和luminance
,包装Vulkan 的是vulkano
。还有一些底层包装,只是呈现了一个不错的Rust-y ,但为相同系统的不安全版本:winapi
和d3d12-rs
for DirectX ,metal-rs
对于Metal ,gl-rs
for OpenGL,以及ash
for Vulkan 。这样的话,如您所见,似乎有更多的人在底层库之上,制作更高级别的跨平台API 箱子,以及一个更酷的名称。这很可能是为了让我们这些Rustaceans 可以用整洁的功能play around ,和看看我们可以制作一个到底有多安全 的API ,以至于你不得不说“这些完全独立的程序恰好共享一大块内存,如果程序A 在其中写入一个float ,那么程序B 将仅会尝试从同一个位置读取一个float ,绝对可靠”。假设,有那么一群人,只编写在单一平台上运行的程序,而他们实际上都忙于完成有用的工作,而没功夫担心那些高大上的高级抽象,这样的话,下面让我们揭开那些不仅仅是简单包装的库的面纱。
但是,如果仅仅以一个底层C API 编写成的Rust 程序,对您来说还不够好呢?那这时,gfx
来到你身边,它打算成为一个全能图形库,可以使用任何 图形API 作为图形后端。你可以拿上一个程序,在Windows 上编译它,那它将使用DirectX ,然后在Mac 上编译相同的程序,那它将使用Metal 。且它还很底层,所以人们很容易在上面编写更高级别的工具,并且工作迅速 。 嗯,如是说,运行时性能会很快;编译它可能需要一段时间…
不管怎样!gfx
有点复杂的历史。它是开源的,但它背后的许多推动力来自Mozilla 。它最初是一个研究项目,在大部分的目标后端上都做得非常得当,这本身就不是一个小的壮举,它的目的是作为Mozilla 实验性Servo 浏览器引擎的后端。但是这个系统一直在发展,最终开发人员清楚地意识到必须做出选择:它必须变得不安全,或者必须变得更慢,强制运行时安全。开发人员认为,在现有的不安全API 上创建一个安全但速度较慢的包装器,要比反过来做容易得多,因此决定放弃纯粹的内存安全性。事实上,他们基本上重组了所有的东西并重新命名gfx-hal
,全称“图形硬件抽象层(Graphics Hardware Abstraction Layer)”,采用了与Vulkan 97% 相同的API 。剩下的3% 左右是一些额外的摆动空间,来处理任何需要的边缘情况,如使它与DirectX12 和Metal 配合更灵活一些,和那些不合适的部分。但总的来说,gfx-hal
基本上和使用Vulkan 是一样的。
这一过程来之不易,但就在2018 年圣诞节之后gfx-hal
0.1 已发布。而且…很管用!令人惊讶。当然还没有完成 ,但它瞄准了一个已知的目标,而且基本上来说,是击中了。Vulkan 后端工作得很好(应该如此) ,它在DirectX12 和Metal 上也能正常工作。不过,让它在OpenGL 和DirectX pre-12 运行良好,仍然是一项正在进行的工作。搞定那些个后端真的不容易 ,因为gfx 可以说是,要在更高级别的API 之上,实现一个底层API ,比如将C 编译成Javascript 。我敢打赌,在更高级别的API 中,使用后端可能总会有一些性能问题,但我也希望它们最终会变得非常好,而且尖端人才 现在已加入gfx 豪华大家桶。
所以,我们现在基本上有一个Rust Vulkan 实现,可以在任何平台上运行,并且可以在将来添加更多。事实上,还有gfx-portability
,它将纯净的 Vulkan 作为提个C API ,并成为在其之上的一个" 精瘦" 层,类似于MoltenVK 。同样,这还没有完全完成,但实际上在一些挺重要的用例 中,工作的不错。当它更接近完成体,你就可以把使用了Vulkan 的任何程序,接入gfx-portability
,ok,运作在任何平台。
好了,你是 gfx
粉丝仔,那其他的呢
哦,对了,还有其他的箱子!实际上,它们都非常好。这不是一个详尽的清单,我只想列出最重要的事情。我也没有投入足够的时间和精力,像gfx
一样使用他们,所以我的知识并没有那么深,但这里有:
glium
原安全OpenGL 库,由尊敬的Tomaka 编写。关于它死亡的传言被大大地夸大了,因为它现在是由它的用户社区维护的,尽管我承认它的维护很" 温柔”。它的安全性在于它比纯OpenGL 具有更高的级别,引入一些更高级别的抽象,并自动地将它们组合在一起,这样就不必手动,瞎处理状态变量了。尽管如此,它还是有一些众所周知的安全漏洞,而主要的维护者把所有的时间都花在了愚蠢的赚钱上(他怎么敢! ) ,没有人做过研究,重新设计,以找出如何关闭漏洞。据我所知,98% 的人可能不会注意到这些洞洞。而作为比较其他事物的基准,glium
仍挺着。
luminance
另一个安全的OpenGL 库,由Phaazon 编写,基本上是一个人的项目。我承认我除了那里看看这里瞧瞧之外,实际上还没用过它,but that playing around was quite nice。对我来说,主要的卖点是作者:我使用了Phaazon 编写的其他一些库,文档简洁但完整,功能集非常实用,他总是愿意帮忙解释工作原理,并听取更改或添加内容的建议。还有就是维护更频繁 ,比起glium
某个做自己的东西的人来说。在抽象和安全级别上,它大约相当于glium
。一个区别是glium
打算支持全部的 OpenGL 的版本,而luminance
只打算支持OpenGL 3.3+ ,这是一种更明智的方法。
ash
你是否通关了https://vulkan-tutorial.com/ ,并说“这很好,但我希望它是Rust 的,加上好结构和Copy
trait 在正确的地方,还有还有enums 等等等?噢,这就是你想要的。这是Vulkan API 的一个非常简单的包装起,Rust 化。这就是它的目的,这就是它所做的一切,最后它做得很好。虽然它非常不安全,但也不会妨碍您的工作;如果您可以使用纯C 在Vulkan 中,编写一些东西,那么在ash
同样的事情可以直截了当。我已经通关了上面的C++ 教程,并把所有的东西都用Rust ash
写出来了。库是由一个MaikKlein 维护的,一个我没交流乐趣的人,但是Rust graphics/gamedev 社区中,各种各样的知名人士似乎一有机会,就时不时地插手,所以它基本上是go-to Vulkan 绑定库。
同样,如果您想使用底层Metal 、OpenGL 或DirectX11/DirectX12 ,并且只要那最基本的方便,那么metal-rs
, gl-rs
和winapi
/ d3d12-rs
会是你的去哪里。我所说的关于ash
的一切,或多或少也适用。
vulkano
另一个特别的Tomaka ,在精神上类似于glium
, vulkano
是一种完全安全的Vulkan 包装器。唉,它也有点被它最初的创造者抛弃了,但也许当他中彩票时他会回来,现在由Rukai 来维护。我自己还没有使用过它,所以我只能说文档中的内容: “Vulkano 仍在大量开发中,还没有达到非常健壮的目标。不过,库的总体结构很可能是确定的,未来所有突破性的更改都可能直接在用户代码中修复。 ”
rendy
我在作弊,我们又回到了gfx-hal
。编写原始的Vulkan 代码是相当乏味和烦琐的,并且有相当数量的东西你必须为亲手写上。比如内存管理。所以,为什么不有一个好的助手库,可以处理一堆常见的和死记硬背的bits 呢?完全控制GPU 的功能是很好的,但是300 行代码, 仅仅是为了完成95% 的用户基本相同的基本设置时,很明显,此时应该大开方便bit 门。方便即是rendy
打算提供的:一套模块化的、相当基本的工具,使与gfx-hal
工作生活更美好。您可以在图形中,创建nodes 并告诉它们依赖是什么,而不是一次将渲染过程中的各个阶段细心地连接到一个数组索引。而从一堆渲染目标, “交通手势和胶水”,拼凑出一个交换链,只是,拜托,只是为了在屏幕上显示已经搞定的东西??,而rendy
的方便帮你搞定那一堆。但它是非常模块化和无定向,当然你提起胆气向gfx-hal
进发。而 rendy
更像是一个工具包而不是包装器。
它是由Amethyst 游戏引擎( Viral , Frizi ,和Fusha ) 后面的一些开发人员编写的,我正在认真考虑是否将其用于a future version of ggez
。我真的很怀疑rendy
一开始,部分原因是我觉得Amethyst 有点像( 雾件 —— 宣布要上市但尚未设计生产的电脑软硬件产品) —— 它是如此超级雄心勃勃和创新,而这使它很难有循序渐进的过程,从中你能在设计错误中学习。但后来,我真的试过rendy
,我不得不说,这正是我想要的。
未来在继续书写(in Rust)
但是等等,还有…
尤其,是在Vulkan 发布后,anal 图形编程人员(即我)非常放心,他们提供了一种不像OpenGL 那样糟糕,编写图形代码的方法。事实上,在Webassembly 发布之后,这种只有那些专门anal 编程语言的呆子(还是我)感受到的放心受到了" 敌袭" ,因为他们提供了一种不需要Javascript ,就能为web 编写前端代码的方法。显然,我不是单着的,在Vulkan 1.0 发布后不久,就有人开始问我这样的问题: “我们能不能有比OpenGL 更好的东西,能在互联网上做3D 图形? ”?而当苹果、谷歌、微软、Mozilla 和英特尔的人开始问这样的问题时,不管是好是坏他们聚集在一起,并且造出了它 ,and so we get WebGPU 。
所以,第一件事是,截至2019 年5 月,WebGPU 是未完成 品。这是多个供应商之间的巨大努力之一,因此在每个人都试图找到最佳解决方案时,有很多技术问题需要解决。再加上很多政治问题需要解决,因为每个人都试图压垮其他人,所以他们拥有技术优势和更多的供应商锁源(惊讶,这似乎主要是苹果做的) 。第二,WebGPU 必须 完全安全。浏览器执行不受信任的代码,因此即使是一个相当底层的API 的不好的事情发生,也要变为不可能。第三,它非常让人联想到Vulkan ,但实际上不是Vulkan ,更像是Vulkan 的一个合理的子集,有着一堆剪下来的’毛发’功能。最后,有一个由gfx-rs
社区制作与使用gfx-hal
制图的Rust 功能型原型,叫做wgpu
。
注意标准、WebGPU 和wgpu
实现之间的区别。还有其他WebGPU 的原型实现:Dawn, by Google 和苹果的另一个原型可能不是开源的。
作为一个简单的,安全的,像Vulkan-ish 一样的东西,wgpu
实际上是一个相当有吸引力的库,还能用于在桌面上编写图形代码。事实上,为便于跨不同桌面平台的可移植性而设计,它正被一些相关团体,作为’标准’所推动。着已经有了,使用它的实验性的游戏框架 —— 事实上,一个真漂亮 粒子演示,你应该百分百下载试试,即使框架本身是相当不成熟的。我个人抱有小小 疑虑:现在确实是实验性的原型,制作实际东西,尽管kvark (一个主要的wgpu
(devs)向我保证,这不是真的,这是一件不会死的实事,而事情会变得awesome ,必然在某天完成。他在gfx-hal
2017 年末也是这么说的,老实说,他是对的,除了“某天完成”已过了大约一年半。:-P 我们拭目以待。
另一个有趣的可能性是相反的方向:会有什么让gfx-hal
有一天不在WebGPU 之上运行?Nothing,这就是什么。可能不是特别容易,但是gfx-hal
已经做了很多不容易的事情,而且效果很好。事实上,Amethyst 计划获得了一个准许 ,让rendy
在网络上工作得更好。而rendy
作为一个gfx-hal
的工具箱,至此他们的努力可能包括gfx-hal
也在网上工作。所以gfx-hal
在任何最终的WebGPU API 上运行良好的机会真的非常大大。
所以,尽管所有的网络标准都很糟糕,但在WebAssembly 和WebGPU 之间,网络游戏的近期前景看起来非常非常有趣。
所以,我要用什么,让一个三角形图出现在屏幕上呢?
就像我说的,整篇文章都是为crazy people 写的。如果你只想在Blender 中建模,然后编写一个程序加载它,并显示在屏幕上,那你应该使用一个现有的游戏引擎,如Godot 或Unity 。我们还没有Rust 对标产物(尽管Amethyst 已经到了那里) ,只有一大堆零件。把零件组装成漂亮的东西不是太 很难,但这仍然是一个需要一些肘部润滑脂和电动工具的项目,而不是宜家家具,你可以简单地拼凑在一起。目前,生态系统大致如下:
如果你想做你自己的triangle-slinging ,并希望它马上 能在所有平台上工作,但不想编写1200 行安装代码,请使用glium
, luminance
,或其他OpenGL 绑定之一。
如果你不害怕让你的手深 入到渲染管道的五脏六腑,并希望它马上 在所有平台上工作,使用gfx-hal
。除非你真的决定亲力亲为,不然的话,推荐你使用rendy
,将1200 行设置代码减少到400 行。
如果上面所说的适用,但你不想让所有的,这些gfx-hal
奇特方式,和如果你只想让它在Linux 和Windows 上运行,那么使用ash
或vulkano
。最坏的情况是,无论如何它都能在gfx-portability
运行。不过,rendy
不能用就是呐;是否有其他类似rendy
的工具,让Vulkan 变得更好,我( 还) 不知道。如果你找到了就告诉我!
如果你想要一个比OpenGL 更酷、更具未来感,但又不像Vulkan 那么惊悚的东西, (愿意下注)并且你想让它运行在任何东西上,包括某天来临的web 原生的话,使用wgpu
。
附录: 游戏框架
我喜欢制作流程图,所以我会给你另一个我知道的各种Rust 游戏引擎/ 框架箱子,以及它们在图形上的使用/ 能用于:
快速、有偏见的精简列表:
ggez:我选择的二维游戏框架,因为我是主要的维护者。我做的是因为试图使用Piston 太烦人了。基本设计对标Love2D 。
quicksilver、tetra —— 等二维游戏框架。 API 上与ggez 非常相似,主要不同于他们所做的技术选择。从不同的角度处理与ggez 相同的问题。
coffee:新的和实验性的2D 游戏框架,比ggez/quicksilver/tetra 更高层次和更个性。
Amethyst:大猩猩3D 游戏框架。显然它确实有效。
piston:原Rust 二维游戏引擎。范围太广,设计变化无常,没有足够的文档,尽管我可能对此有点偏见。但人们似乎仍在使用它?