Roblox八年

2020-11-21 12:24:17

我于2012年底加入Roblox,担任渲染工程师。经过多年的主机游戏开发,我刚刚花了一年多的时间来研究FIFA系列的各种游戏,对“大型游戏开发”感到有些厌倦。我在FIFA上的工作是作为承包商的,我得到了一份全职职位的报价,但是我还有一个在Roblox工作的朋友伸出援手,并提议我搬到加利福尼亚并在Roblox上工作。我对Roblox完全一无所知,但是加利福尼亚很好,我的朋友告诉我那太棒了。这个平台是如此的不同(太奇怪了!),我决定抓住一个机会-8年后的今天,我仍然在Roblox工作并享受它。我于2007年4月开始了我的第一份全职工作,因此我在游戏开发领域已经工作了13年,其中有8年在Roblox。

我的记忆以有趣的方式工作。我记得我的采访很好,我记得在Roblox总部附近的San Mateo市区的某个地方吃过午餐-包括Roblox首席执行官David Baszucki在内的一些人共进午餐,我记得他问了很多关于我对引擎和渲染的想法的问题,并且清楚地记得我大部分时间没有吃完午餐,因为我大部分时间都在讲话。但是,我真的不记得我对Roblox的看法正在经历什么?除了为什么我想做一些其他改变之外,我为什么还加入?谁知道,但我很高兴自己做到了。

我真的不明白为什么Roblox如此成功-您可以回顾一下各种原因,但是很难验证它们,如果您在2012年遇到任何人并要求投资以构建一个平台,让所有游戏用户生成并在具有自定义工具集的自定义引擎上运行,并且所有用户都参与了巨大的虚拟经济,而且…,我想您会茫然无措。

但是我确实知道我为自己找到了理想的地方,尤其是在我职业生涯的那一点上-我喜欢从事游戏技术的工作,但我从不喜欢从事实际游戏的工作,并且Roblox最大限度地增加了可以使用您所运用的技术的开发人员同时保持良好的自主权和您需要解决的广泛问题。这里很难无聊。

我想我可以谈论Roblox几个小时-它在某种程度上成为了我一生的重要组成部分。我很幸运能在那时加入,并见证了我们技术和业务的增长。我真的不确定未来会怎样,但很难想象Roblox之后会发生什么,如果有的话-我当然不打算很快离开。

因此,我认为现在进行一年或一年以上计划的工作,并处理我在Roblox从事过的所有大型项目,可能会很有趣。这是基于对源代码控制历史的总结和回顾,它告诉我我对主分支进行了2752项更改,合并提交为一,所以,这个博客可能面目全非。希望这会很有趣!

在开始之前,我只想总结一下,我非常感谢Roblox领导层对我的良好对待,我一路交往的所有朋友和同事以及美好的Roblox社区。我之所以仍然喜欢我的工作,是因为每当我写我正在处理的新事物,小功能甚至是错误修复时,通常都会感到兴奋,这使我不断前进。衷心感谢大家。我想如果没有您的帮助我就做不到,尽管目前困难重重,但我希望这种情况能持续尽可能长的时间。

值得注意的是,它包括Direct3D9的半像素偏移修复程序,我认为这对渲染工程师来说是很容易的事情。当时的渲染代码基于OGRE渲染引擎,所以我必须学习这一点,这也是我第一次专业使用OpenGL-在此之前,我已经使用Direct3D 9和专有控制台API,以及Direct3D 10/11作为一个爱好。

最初是为“ 100播放器”项目添加的,10月份演变为渲染所有零件,并一直用作零件渲染器,直到2018年引入实例化。否则称为“轻量级零件”。该代码在2012年11月前后得到了进一步的优化和部署。大多数代码都可以保留到今天,但是随着时间的推移而不断发展,当不适用实例化时仍然可以使用。

该系统的核心思想是将网格动态地批处理在一起,对于字符而言,这将基于字符模型层次结构,而对于其他所有方面,分组都是空间的。由于驱动程序开销和OGRE效率低下,这使我们减少了绘图调用的数量,这是一个很大的问题。

这将为最终成为渲染堆栈的完整但逐步的重写铺平道路。其主要动机始终是性能-最终我们让我们移植到了移动设备上(即使对于相对简单的场景,旧的渲染代码也远远不够快),并在可在帧中渲染的对象数量上取得了新的突破。 。

我们需要做的几项OGRE升级之一是获得更好的GLES支持。就像其他大型中间件更新一样,执行这些操作非常痛苦。进一步阅读以了解OGRE最终发生了什么……

我记得做过的一件事是,源代码中的文档使升级过程变得更加痛苦。我有一些脚本将标头中的版权年更改为我们树中的内容,以减轻合并的痛苦,但是OGRE进行了一些升级,其中70%的更改是文档,这很难实现。

这些通常具有挑战性的原因是,每当我们进行升级时,我们都必须a)将大量更改与新代码合并,b)用标志标记升级的危险部分。自从我加入Roblox以来,我们就使用了相同的功能标志系统(我们称它们为快速标志),它允许我们根据指标动态禁用发行版的各个部分,但这实际上需要有选择地隔离if语句后面的更改-对于OGRE,有时是必需的,因为我们不知道OpenGL代码中的一些低级更改会带来什么影响。

在此之前,我们需要手工翻译的着色器,维护起来很麻烦。管道的第一个版本使用hlsl2glsl和glsl-optimizer(与当天的Unity相同)。我们今天使用的是版本3,请参见下文!

由于此操作是在使用OGRE的那一刻完成的,因此编译器将获取HLSL文件,对其进行预处理并将其转换为优化的GLSL,然后将生成的GLSL保存回磁盘-然后OGRE将通过材料定义文件直接将其加载。最终,我们用一个二进制着色器包替换了它,它可以存储OpenGL的GLSL代码和其他API的着色器字节码,但是那时候我们才发行了HLSL和GLSL源代码并在设备上编译了HLSL代码!

我们相当于通过SQL数据库进行“蒸汽硬件调查”,并合并了各种系统信息位,以帮助我们当时了解硬件。这是在我迷恋F#的时代,所以它是用F#编写的,而不是像Python这样的东西。我们不再使用它,甚至没有问题的SQL数据库!

我们从未发布过最终的数据,我不确定我们使用它进行决策的频率,但是查看来自不同供应商的图形卡的数量或典型Roblox用户拥有的RAM或分辨率的数量很有趣。

尽管我被聘为渲染工程师,但我在底层系统方面拥有非常丰富的经验,因此从一开始就从事优化工作和安全相关工作。这些天我不再这样做了,但在最初的3或4年中,我经常参与安全工作。现在我们幸运的是,有一些人可以全职做,并且比我做的更好:)

“ 100个玩家项目”的第二部分,必须在一次抽奖中呈现每个角色(对于我们来说,这真是太昂贵了!)。副作用包括在衬衫创作者不喜欢的角色物品上牺牲了一些分辨率。新系统管理了Atlas纹理记忆,将类人动物重新烘烤到较小的纹理以节省纹理记忆。尽管我们现在正在开发一个新的合成器,但直到今天,该合成器仍然进行了微小的更改。

该合成器以非常可配置的方式构建,允许高级代码指定要烘焙的布局,并自行管理所有复杂的异步处理和预算。这使我们可以在数年后为R15完全切换合成布局。

在2012年底,我们正在积极致力于移动端口。从那时起,我们不得不在引擎的许多不同部分中进行大量工作,以使数据结构更小,算法更快。当然,您永远不会完成优化工作,因此我们至今仍在这样做。奇怪的是,自2012年首次发布以来,我们在iOS上的最低规范一直保持不变!

一个有趣的事实是,即使我们最初只是将iPad 2作为开始。规格,我们讨论了在发布后为iPad 1添加支持。当时,很多人无法在较旧的硬件上的iOS上玩Roblox。但是这些设备的性能特征仅仅是……还不够好。您可以用手指触摸屏幕并摇摄相机,并且在摇摄过程中,您失去了30%的单个可用内核给处理触摸的OS。我们决定不为此提供支持,并且8年后肯定可以肯定的决定是:D

使用Xcode Instruments剖析iPad上的帧尖峰非常困难。为了设法弄清楚如何在移动设备上获得更好的性能,我编写了一些临时代码将所有内部日志事件转储到二进制流中,并编写了F#和WPF中的桌面UI工具以对其进行可视化。其中还包括一个Lua分析器,该分析器可以基于事件数据在传统的分层聚合视图中显示Lua代码的概要文件。这无法幸免,但奇怪的是,几年后,我们最终还是使用了类似的方法来进行微轮廓分析。

它始于八月,最初是支持网格的仅角色渲染器,后来演变成可以像以前的渲染代码一样渲染Roblox中任何部分的东西。这并不容易,这是因为性能在代码的每个部分中都非常重要,并且因为很多极端情况下的功能必须像以前一样发挥作用。除了可能的旧圆柱体渲染:

该代码还支持FFP,使用矩阵调色板混合有效地渲染带有刚性接头的字符,并且在桌面上还附带了经过精心优化的顶点着色器,可在没有硬件顶点着色的情况下(通过软件顶点着色路径)在Intel GPU上更快地运行。同样,此方法使用基于GPU的拉伸和基于CPU的动态索引缓冲区检测轮廓来实现模具阴影。娱乐时间!

我对此记忆有些模糊,但我认为我们正在集思广益,以一种可以在移动设备上使用的方式来实现全场景阴影的方法,最近我观看了《小小大星球》上的演示,介绍了他们如何在PS3上进行照明基于体素的计算;我们的首席执行官是讨论的一部分,并提到“如果所有照明均基于体素该怎么办”,其余就是历史。据我所知,我们最终采用的方法非常独特,不同于许多其他基于体素的实现。

一月份,体素照明引擎获得了对太阳阴影和点/点光源的支持,但是感觉就像在良好的GPU硬件上,我们可以使用其他技术来获得更好的效果,因此我们正在寻找可以用于体素的其他东西。我不记得是谁提出了这个主意,但这是在实施天窗的时候进行的,这是一种以天空为光源的环境光遮挡形式,如果没有体素则很难正确地进行。

为了使体素照明实用,我还使用了手动编码的SIMD(SSE2)重写了所有功能,包括体素生成器-如果没有此功能,则无法在CPU上进行照明(此代码后来翻译为iOS端口的NEON)。

产生的照明代码一直保存到FIB第一阶段,该阶段增加了对HDR的支持并更改了体素器以使用各向异性的占用率,但直到今天仍在使用。

为了重新定义Roblox游戏的外观(当时我们认为Roblox作为平台需要一种艺术风格),开始了新材料的研究。我们曾经使用随机的着色器集,包括一些程序化的着色器。这项工作用“表面着色器”代替了着色器框架(这是由Aras P.在大约同一时间在Unity上的工作启发而来的;直到目前,我们仍使用最终的着色器接口,尽管尚不清楚它们实际上是在增加重量,并且如果我今天再做一次,我就不会走那条路),并在顶部使用更传统的基于纹理的材料,永远毁了木纹。

厌倦了我们的自定义XML解析器/序列化器在大型场所上使用的时间,我设计并实现了自定义二进制文件格式。它是基于块的,具有每块LZ4压缩和自定义二进制过滤器,以预处理数据以帮助LZ4;该格式的结构可以使反射互操作更便宜,并最大化加载性能。到目前为止,我们将其用作主要文件格式,尽管该格式进行了一些细微调整(主要是用于处理密码签名的扩展名和更有效的共享二进制blob)。我仍然对设计感到满意,但我会在几个地方稍作更改,以使非常大的地方的加载更加具有缓存一致性,这在当时并不重要。今天仍然可以完成此操作,但需要对一些块表示数据的方式进行小幅修改。

这项更改最初是针对Play Solo推出的,它将整个世界保存到一个文件中,并将结果加载回新的数据模型中。这意味着可以安全释放,因为不会发生永久性数据丢失。此后,我们逐渐切换到使用此格式发布位置,最终也开始将其用于模型(包)。如今,Roblox上几乎所有语义丰富的内容都使用这种格式。

具有讽刺意味的是,尽管二进制存储仍然具有更高的性能和空间效率,但我们最终还是在2019年用了我的pugixml库替换了XML解析器。

当我们交付iOS端口时,它是通过ES1(FFP)完成的;这意味着很多功能都无法使用,包括照明变得越来越重要。 OGRE对ES2的支持当时还不成熟,因此其中包括OGRE代码中的许多修复,大量的着色器调整以及上述针对体素照明代码的NEON优化,使其可以在移动设备上运行。

这项更改对将来的工作有所帮助-登陆之后,我们再也没有在移动设备上使用FFP,而是始终使用着色器渲染内容,这意味着我们不需要为任何技术升级都支持ES1,因为事实证明,ES1一直在等待角。

Roblox引擎的开发通常遵循tic-toc-tac模式(好吧,我们实际上没有这个名称,但无论如何)。首先,我们为子系统提供了一个很棒的新实现,需要对其进行替换。然后我们致力于使新的实施变得更好。然后,我们删除旧系统以简化维护。至此,我们已将所有部分切换为在新的群集渲染路径中渲染,并且旧代码已准备好删除。提交说:“删除了500 kb的C ++代码,400 kb的着色器/材质代码以及3 Mb的内容/纹理。还删除了17个渲染快速标记和5个渲染日志组。”当时感觉还不错!

渲染缩略图的方式与我们通常在客户端上使用的渲染代码相同,但是它使用软件渲染器在我们的服务器上运行。这是低效的,因为我们使用了速度较慢的软件渲染器(没有JIT的单线程),此外,对于每个新图像,我们都要对整个引擎进行设置/拆卸。对其进行了重新设计,以使用更快的软件渲染器(这很困难,因为在Windows上构建开源软件很痛苦),并且将相同的引擎上下文重用于许多缩略图,这使我们可以大大减少使用的服务器数量。相对而言,我所做的工作可以用为公司花费的金钱来衡量,因此这种感觉很好,这种情况比较少见。

…我想三月份我只是做了一些准备工作,而实际上是在六月,我们开始研究新的着色器和新的纹理。我们的美术师反复进行很多次研究,以确保美术在游戏中看起来不错,但也不会为使用完全违背其预期目的的内置材料(例如使用沙色的蓝色如水)。

不过,这里的大部分工作是为地形材料设置新的UV布局-我们使用了基于体素的地形,该地形具有几种不同的块类型(块,楔形,角形楔形),并且与艺术家一起我们提出了新的UV可以在纹理中更均匀地分布像素的布局,以便在各处获得良好的密度。

我记得更多地考虑了体素照明,并且在某个时候意识到,我们可以做来自太阳的定向阴影,我们可以做点光源-为什么我们不能同时做这两个事情,以便每个光源都可以投射阴影?事实证明,我们用于定向阴影的方法可以适用于点光源,并且通过一些优化和调整,体素照明引擎的第一个版本终于完成。这将一直持续到将于2018年底发布的Future Is Bright第一阶段。该阶段于2013年9月完成,并使用SIMD进行了优化:

更改37700:SIMD阴影更新现在可以正常运行,但我不知道为什么。更改37701:SIMD阴影更新可以运行,现在我知道为什么:)仍然需要更多优化

我喜欢编写跨越“专业”和“有趣”之间界限的提交消息。

由于解决了所有容易的问题,例如零件渲染和照明,因此现在是时候面对最终的渲染挑战:文本。

当时,我们使用了两种大小的预烘焙位图(实际上是两个),并且编写的布局代码写得很差,不支持字距调整,也无法很好地处理间距。取而代之的是,我写了一个F#脚本(当然!),它将许多不同大小的单个字体烘焙到一个大图集中。为了节省纹理空间,我使用了矩形包装机。在运行时,布局算法使用字距调整数据将字形放置在正确的位置。这可以在最常用的尺寸上显着提高文本质量,并且可以持续数年,直到国际化成为优先事项为止,我们不得不开始从TTF来源动态渲染字体图集。在我们集成Harfbuzz进行复杂的Unicode感知成形之前,布局算法将继续存在数年,而这两者都是在几年后由其他人完成的。

继续扩大工作范围(不仅仅是渲染)的趋势,我一直在设计和实现新的远程事件API,包括即发即忘事件和远程函数调用(在Lua中非常漂亮,因为协程)支持,您可以仅在Lua中调用一个函数,该调用将被路由到服务器,服务器将运行代码并返回结果,并且协程将继续运行,几乎不花时间!)。很难找到所涉及的API的好名字。从那以后,我们没有做任何更改,而我仍然为有时使用正确的函数/事件名称而苦恼。

在我们的游戏开发人员中,Voxel地形并不是很受欢迎。对于需要花费大量精力来开发和维护的功能,这是不令人满意的,我正试图找出“为什么”。一种假设是有限的尺寸(我想说512x64x512体素?)太过局限了。以弥补我在研究新的稀疏体素storag

......