Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Kethers/c84cd4cb68a255a6fe30dfe8d7a92fa9 to your computer and use it in GitHub Desktop.

Select an option

Save Kethers/c84cd4cb68a255a6fe30dfe8d7a92fa9 to your computer and use it in GitHub Desktop.
Chinese subtile text from Game Engines & Shader Stuttering Unreal Engine’s Solution Inside Unreal
注:前面的是虚幻周报,可以从129行开始看。原始视频链接:https://www.youtube.com/watch?v=i35yf-wh3Bs&t=574s
大家好 欢迎收看本周的
新闻和社区热点
虚幻引擎5.5.2版本已正式发布
这个最新补丁
这个最新补丁解决了
100多个不同的错误和修复
包括更正了MegaLights
Alpha遮罩和光照通道问题
修复了毛发阴影和毛发漏光问题
以及已知的崩溃和停止响应等问题
完整的补丁说明已发表在
Epic开发者社区供你查看
如往常一样
如果你遇到任何问题
请务必在更新帖子中
报告并提交错误表单
以便我们今后继续改进虚幻引擎
在过去四十年里
《神偷卡门》中的环球冒险
以其寓教于乐的独特模式
吸引了无数粉丝
尽管这个制作精良的
动画剧集长期以来广受欢迎
但玩家此前从未得以亲自操控这位
国际恶联(VILE)的盗贼团伙头目
而现在机会终于来了
《神偷卡门》是一款全新的
单人解谜冒险游戏
讲述传奇神偷卡门·圣地亚哥的故事
在游戏中 玩家将使用
各种炫酷的高科技道具
在全世界展开一场惊险之旅
收集线索和破译密码
与行踪诡秘的犯罪分子们斗智斗勇
为了打造出生动鲜活的冒险故事
开发商Gameloft Brisbane
选择了虚幻引擎5
因为它用途广泛 功能全面
能在多个平台上打造引人入胜的动态体验
那为什么要选在现在
让玩家扮演神偷卡门?
虚幻引擎的哪些具体功能
帮助团队实现目标?
开发人员如何为多个平台优化体验?
最近我们采访了
Gameloft Brisbane
对他们进行一场深入“ 调查”
如果你有兴趣和我们一起
查出这起案件的真相
请在unrealengine.com/feed
了解完整内容
想认识一些开发人员同行吗?
以下是这个月的社区聚会
不要害羞
快去参加离你最近的聚会吧
介绍完这些激动人心的消息后
让我们继续关注本周的精彩社区热点
本周聚焦的热点略微有些不同
这一次我们并不会着重于单个项目
而是要全面介绍VFX美术师Nine的整个作品集
从巨斧劈出的强力冰锥爆炸
到从多个角度发动远距离突袭
并最终施放强力攻击的阴影分身
再到威力的强度不断增加
带给你终极绝招感觉的光束攻击
每一个招式都充满力量感和打击感
每一次我们感觉特效的规模强度
和炫酷程度已经触及天花板了
就有新的东西出现
证明我们错了
这位艺术家的作品以精湛的方式展
现了如何打造具有实感的视觉冲击
希望大家也和我们一样
能够欣赏这些项目
请前往ArtStation页面
了解更多信息
Louis Sullivan制作的
《The Green Chapel》
灵感源于David Lowery于2021年
改编执导的电影《绿衣骑士》
这个故事源自亚瑟王的传说
为了以自己的方式重新演绎
这个绿色礼拜堂场景
作者Louis以兴趣爱好的形式
启动了这个项目
这个项目让他有机会练习
在虚幻引擎5中
使用Lumen进行植被设计 资产制作
场景构建 以及光照设计
他还用到了Megascans
制作岩石资产 草 和生态群系纹理
这棵敦实的橡树则是结合了手工制作细节
可平铺(tileable) 纹理
以及树叶图集(atlas)
场景中还使用了虚幻引擎素材包中的
一些视觉特效和着色器
为场景带来了更多的动态效果
让项目更加鲜活逼真
我们可以很容易地想象到
绿衣骑士正坐在他的宝座上
等待与高文爵士进行最后的对决
你能想象到吗? 你可以在TA的
ArtStation页面上查看完整项目
由QianXin Lee设计的
Stylized Holy Spring
不仅拥有精美的制作和设计
沉浸式的呈现方式也引人入胜
动态的摄像机运动带领我们
领略了这幅别具一格的风景画
波光粼粼的河流周围环绕着
色彩缤纷 生机勃勃的植被
吸引人们将目光移向背景中
震撼人心 飞流直下的瀑布
如果你震惊于这个场景
在温暖的夕阳下看起来有多么动人
那么请允许我们现在向你展现
充满氛围感的夜景版本
在这柔和迷人的午夜微光下
水面泛着星光
我们无法用语言来描述这段演示视频
为我们的内心带来的感动和震撼
只希望大家也和我们一样喜欢这个场景
完整文章可在作者的ArtStation页面找到
作者还在介绍中列出了TA用到的资产和素材包
以上就是今日的新闻和社区热点
非常感谢您的收看 我们下期见
大家好 欢迎收看《Inside Unreal》
一档学习和探索虚幻引擎相关知识的周更节目
我是主持人Tina
今天我们请来了几位非常优秀的来宾
他们将讨论PSO预缓存
着色器卡顿等问题
那么我们直入主题
先请来宾进行自我介绍
好了 Ari 你先来吧
为观众们介绍一下自己
谢谢你 Tina 这已经是我第三次
参加Insight Unreal直播节目了
很高兴能和大家一起直播
我叫Ari
我是Epic Games的开发者关系专员
我工作的大部分时间
都花在做演示和分享知识上
简而言之 这就是我的工作
没错 Ari的演示都非常棒
大家有兴趣的话
可以到虚幻引擎的社交频道上查看
特别是Unreal Fest的频道
快去看看吧
下面有请Daniele
请简单介绍一下你自己吧
大家好 我是Daniele
是《Fortnite》桌面版团队的主程序员
我和团队致力于
为PC平台的《Fortnite》提供支持
包括稳定性 性能以及
在PC上玩《Fortnite》的整体体验
我为PSO预缓存系统开发了
多个组件
并针对《Fortnite》和我们支持的
多种硬件对其进行了优化
这就是我的工作
很高兴你能来到这里
接下来是Mihnea
请简单介绍一下你自己
嗯 大家好 嗯 我叫Mihnea
我是渲染团队的工程总监
我负责领导渲染架构团队
我也站在管理的角度参与解决PSO预缓存问题
另外 我的团队还负责处理
骨骼网格体渲染
流送 虚拟纹理等内容
我们的专业领域就是底层渲染器
很好 最后我们要介绍的是Kenzo
也向大家介绍一下你自己吧
嗨 我是Kenzo
我在Epic工作已经大概有5年多了
我也在Mihnea的团队中工作
在Epic 我们团队负责多个不同工作
我参与过那个
《黑客帝国》游戏演示的工作
帮助完成了《Fortnite》
光线追踪的初期工作
还参与了乐高游戏的开发工作
这个PSO预缓存问题就是我发起的
因为我们都想要把工作做得更好
想要帮大部分开发者
以及《Fortnite》解决这个问题
这也解释了我们今天在这里
很好
能邀请到大家真是太棒了
非常感谢大家抽出时间
Ari 我决定把话筒先交给你
你来帮我们开个头吧 太感谢了
谢谢 我来开头吧 因为PSO预缓存
和着色器的问题对我来说
是个非常重大的课题
我正在研究这方面的问题
准备通过我的下一次演示进行讲解
然后我发现这个问题
要深究起来其实非常棘手
我准备了很多材料
可以进行完整的演示
那么何不通过这次机会讲一讲呢
现在我要在屏幕上播放几张幻灯片
这次演示的标题是
《游戏引擎与着色器卡顿:虚幻引擎的解决方案》
和我们刚刚发布的博客文章同名
这个幻灯片中会介绍许多相同的信息
但呈现的具体方式有所不同
我会按照我对这个问题的理解来介绍
并且以我的方式
让大家也理解这个问题
那么我们开始吧
首先要说的是现代PC GPU的性能现状
事实是 GPU已经发展到性能非常强大
效率非常高 并且可编程性也很强
而如今的着色器也已经演变为完整的程序
需要针对GPU进行编译
就和CPU一样
但这给我们带来了难题
GPU和CPU不一样
没有共享指令集架构(ISA)
比如说CPU有x86 Arm等等架构
大家都会严格遵从这些传统的指令集架构
在未来也会一直持续下去
但GPU没有这种共享架构
每个独立的硬件供应商
都有自己的架构
这些架构有自己的着色器编译方式
甚至于每一代GPU
都会发生架构上的改变
就连不同的驱动版本也会
导致编译着色器的方式发生变化
这给我们带来了一个坏消息
为这些不同的GPU和驱动程序之类的东西
提前编译着色器是不可能的
基本上我们只能在用户的计算机上编译着色器
而用户的计算机差异实在太大了
要提前为每一款GPU和每一个
版本的驱动程序提前编译着色器
那将会花费太多的时间
占用太多硬盘空间
这么做根本不可行
万一未来发布全新的GPU怎么办
未来发布新的驱动程序怎么办
这根本就是不可能完成的任务
但也有一些好消息
正因如此 每家GPU供应商现在都会不断优化
他们自己的着色器编译流程
优化他们的指令集架构
接下来深入讲讲着色器编译
大家都会想
啊 过去的日子多么美好啊
过去我们有图像API
比如有DirectX 11 有OpenGL
而GPU驱动程序
仅会在调度的那一瞬间
得知绘制调用的最终状态
就像是当你要发送绘制调用时
你会说
嘿 这就是图像最终呈现的样子
而驱动程序只有很少的时间
可以编译着色器
基本上驱动程序通常就是在
调度绘制的瞬间完成这个工作
也就是说在绘制之前
GPU只会有很短的延迟时间
也就是说大家可能会
体验到一些小卡顿
因为当游戏遇到
新的着色器或图形状态时
就得等待驱动程序完成编译
所以会卡顿
但是这个问题并不严重
过去的驱动程序效率很高
驱动程序已经尽其所能
减小编译过程造成的影响
但调度时间毕竟有限
要想完全消除延迟是不可能的
所以没错
玩家可能会遇到微小的卡顿
尽管我们都认为 或至少一些人认为
DirectX 11是个没有任何卡顿的好时代
但其实它还是会卡顿
只是在第一次运行时卡顿
但现在我们会思考
我们要如何改进这一点?
很明显
在发出绘制调用前的一瞬进行调用就已经太晚了
那么有没有办法
让GPU驱动程序提前知道
比如说提前知道我们要绘制什么
提前知道绘制调用的各项参数
那不就更好吗?
那不就是我们一直
想要寻找的终极答案吗?
如果能让游戏提前知道绘制调用
需要用到哪个着色器和图形数据
它就可以提前告诉GPU驱动程序
驱动程序就可以在这次绘制调用发出前
就编译好专用于这次绘制调用的着色器版本
如果我们通过合适的
通知传输这些信息
即便只是比绘制调用提前几秒钟
着色器编译过程就能够有更多时间
做一些优化之类的工作
从前只有不到一毫秒 而现在可以有几毫秒
可以用来进行编译和优化 实现最佳的性能
假设我们有一个描述符
其中包含每个绘制调用的图形状态
以及已编译和优化的着色器
这样一来 驱动程序就不需要
为每一次绘制调用进行额外的工作
绘制调用就会像是放在传送带上一样
那么对于这么神奇的存在
我们要如何称呼它呢?
这就是我们的管线状态对象 简称PSO
这就是我们的终极答案
这种设计确实为我们的
实时渲染过程带来了范式上的转变
这个机制是在DirectX 12中引入的
要充分利用PSO
整个实时渲染行业都需要调整工作机制
用一种全新的方式来发出绘制指令
DirectX 12 Vulkan和Metal
将这个责任下发给了应用开发人员
他们必须知道在调度之前
绘制调用需要哪些信息
这样做很合理
因为驱动程序不可能知道这些信息
而游戏和引擎则可以提前知道
但问题在于 许多游戏 框架和引擎
在开发的时候并没有考虑到这一点
所以应用程序开发者们没办法
提前准备好这些信息
那么对于PSO的运用
最糟糕的情况会是什么呢?
如果在绘制调用进行的时候PSO还没有准备好
我们该怎么做?
许多游戏或引擎就只能在发出绘制调用之前占用线程
等到PSO完成编译
这就会造成大家避之不及的
卡顿或延迟问题
好的一面是 这种卡顿对于
每个不同的PSO只会发生一次
因为之后驱动程序会将其缓存
也就是说 比如你进入了一个关卡
所有东西都会发生卡顿
情况简直太糟糕了
你喊其他人来看 说这游戏简直太卡了
结果运行得无比顺畅
这是因为游戏会进行缓存
问题是 如果你安装或更新了GPU驱动程序
或者更新了游戏
又或者你运行了一个别的游戏
而这个游戏会另外进行PSO编译并占用缓存
从而删除了之前哪个游戏的缓存PSO
那么最佳的工作流程是什么样呢?
我们认为 如果游戏或引擎
在加载场景Object时
知道该Object的确切PSO状态是什么
那就最好了
因为如果需要编译该PSO
只会把加载的时间稍微延长一些
但问题是 许多游戏和引擎
在开发时并没有考虑到这一点
例如 就像我之前提到的
虚幻引擎是非常动态的
这是一个非常动态的引擎
如果我们要预设一个场景Object
在加载时的图形状态
这在某种程度上会牺牲了
这个Object的动态程度
就好像规定了它必须是这样
然后我们必须这样处理
万一我们想要实时更改怎么办?
在虚幻引擎中
我们可以通过蓝图或C++进行更改
就像我们每个人
都可以创建的各种不同材质一样
那么对于虚幻引擎以及这一类引擎和游戏来说
它们现在能怎么做呢
第一个解决方案是捆绑式PSO缓存
所以问题就是
我们是否能在游戏启动时提前编译所有的PSO
这样一来 当你需要在运行时用到某个PSO的时候
它已经准备好了
好消息是 这样就把问题解决了
运行时不会再出现卡顿 搞定
但这样做有一个缺点
也就是说 玩家需要在
第一次游戏开始时等待一段时间
让PSO全部完成编译
然后每次更新GPU驱动程序或游戏时
又需要重新等待
这种方式非常适合线性游戏
提前处理所有PSO并不难
对于开放世界游戏来说这可能会很困难
而对于用户生成的内容
这种方式几乎就完全不可行了
但我认为这个解决方案已经足够好了
事实上 我们不可能提前编译所有的PSO
比如对于虚幻这种非常动态的引擎来说
每一个着色器的变体都太多
无法一一编译
而且其中大部分变体
可能根本就用不到
着色器编译可能会花费数小时
对于运行游戏的用户来说
这样做会占满他们的硬盘空间
于是 业界提出了另一个解决方案
生成一份仅包含游戏实际使用的
PSO状态的列表
并在应用程序启动时
仅编译这些PSO状态
所以这是一种更有针对性的方法
但这时 我们就遇到了新问题
我们如何知道游戏实际使用了哪些PSO状态?
市面上有一些游戏和引擎
其一切内容都是高度定制的
它们清楚地知道自己用到了哪些PSO状态
但也有其他类别的游戏和引擎是非常动态的
比如虚幻引擎
对于这些游戏或引擎来说 解决方案就是
手动和/或自动通关游戏
然后记录遇到的PSO状态
将它们全部列出来
然后玩家的计算机
将根据此列表计算要编译的内容
我认为要找出所有用到的内容非常困难
尤其是对于非常动态的游戏世界
要把这些PSO状态
找出来简直难于登天
而且大家知道
还有那么多不同的图像质量设置呢
如果你安排一个人去跑遍整个游戏
费很大力气找出每一个材质和Object
走遍游戏世界的每一个角落
然后有人告诉你 这只是画质拉满的设置
现在你还得把中等和最低设置的整个流程再全跑一遍
这个过程的工作量非常大 非常繁琐
即便这样 玩家还是需要编译大量的着色器
其中有很多是游戏过程中根本用不到的
比如有人只是玩一两个小时就不玩了
但游戏依然会把整个游戏里的
每一个PSO都编译出来
而且也许一款游戏没有对每个图像设置的
PSO编译需求进行区分
那么它就会把所有图像画质
设置都编译出来
即便永远也不会用到
所以这不是一个完美的解决方案
另外还有一个常见问题就是
如果遇到PSO缺失的情况游戏依然会卡顿
也就是说 如果缺失了某个PSO
预先渲染的部分里不包括它
此时就会卡顿
因为游戏需要在绘制它之前进行编译
这就是当今游戏卡顿的主要原因之一
这不仅仅出现在
虚幻引擎开发的游戏里
这个问题普遍出现在使用
这些新图形API的所有游戏中
所以我们问自己 也问整个行业
我们是否可以做得更好?
在Epic Games虚幻引擎团队里
我们想出了一个称为
预缓存(Prechacing)的解决方案
借此 我们将改用一种
更加解放双手的方法
因此对于预缓存 我们的目标是
仅在加载场景Object时进行PSO编译
对于这些Object 我们会编译
该Object可能需要用到的PSO
目前我们不可能在渲染之前
准确知道会用到哪些PSO
有很多方面 例如使用点光源和定向光源
在投射阴影时就会有很多不同之处
所以我们只需要编译它们的超集
这个超集里的内容只会比
我们实际需要的内容多一点点
因为我们不知道我们需要什么
关键是这个超集里的内容会由驱动程序进行缓存
然后我们会立刻将这些PSO从内存中删除
所以我们不会保存太多的PSO
然后 当我们发出绘制调用时
驱动程序就会从缓存中指定获取所需的PSO
这就是为什么我们称其为预缓存
就好像我们在将其全部丢弃之前对其进行缓存
然后驱动程序从缓存中获取数据
因为这些术语可能会有些繁琐
让人眼花缭乱
比如预缓存(Precaching)
预编译(Precompiling)着色器
还有一般的缓存(Caching)和
捆绑(Bundling)很容易混淆
有人会说预编译不起作用
然后我们必须问清楚
你说的预编译是指PSO捆绑式缓存中的预编译环节
还是指预缓存期间的预编译环节
要聊这个问题简直太难了
因此我们将其拆分为两种解决方案
一种是预缓存
就是在载入期间进行编译
另一种是在游戏开始时
进行的捆绑式PSO缓存
此时会预编译所有的着色器
使用预缓存方法时的平均编译数量
可能是实际使用的PSO的五倍
其实这个效率已经非常不错了
和捆绑式PSO缓存
相比已经少很多了
所以总体而言 这种解决方案更好
显而易见 最大的好处是
这种方式没有大量的前期编译工作量
玩家们在玩游戏之前不必看着一个画面
等待一分钟两分钟 十分钟 甚至十五分钟
就看着游戏在编译着色器 等着游戏开始
另一个优点是
游戏不需要编译太多的PSO
只需要编译比需要的数量多出一点点
但这种方案依然不完美
我们终究需要找个时间来进行编译
所以在第一次载入资产时
花费的时间还是会变多
因为要编译PSO
如果你需要快速地流送大量内容
比如游戏的地图图块或子关卡时
或者你想做没有加载界面 无缝流送的游戏时
这种方式就会不太便利
另一个缺点是 这种方式
不能用于在运行时动态应用材质
因为我们不知道你会在运行时动态应用什么材质
但我们正在研究一种方法
让开发人员能够以某种方式告知引擎
他们将要应用哪些动态材质
也就是说 比如开发人员能够将
这个信息放入配置或蓝图或C++中
告诉引擎 我早晚会显示这个
动态材质 然后我们可以对其进行编译
然后就可以在不造成任何影响的情况下切换显示它
我们还有很长的路要走 仍在不断完善中
另一个缺点就是 如我刚才所说
这种方式会在运行时进行编译
这就会充分占用CPU核心
因此没有太多空间供其他系统使用
如果你是在流送内容
像动画系统这样的东西现在会占用多核心
如果你还要同时编译着色器
可能会挤占动画系统的资源
但你完全可以对其进行配置
你可以告诉引擎
我想要让PSO预缓存仅占用几个核心
我们甚至可以在运行时更改占用情况
另一个弊端是 如果在运行时更改画质设置
比如我现在用的是最高画质
我不想要这个帧率
我现在想改成中等画质
那么此时游戏就需要编译新的PSO
此外还有全局着色器这种东西
类似于全屏幕着色器 或者叫计算机着色器
这类着色器需要在其他东西之前预先编译
进行编译时 你还是会看到一个初始的载入界面
因为这类着色器并非某些特定资产专用
预缓存这个新系统
已经在虚幻引擎5.3版本中默认启用了
也包含在升级版本的项目中
也就是说 如果你用旧版引擎制作了一个项目
现在把引擎升级到了5.3
那么你的项目也会默认启用预缓存
除非你手动关闭它
游戏工作室也可以将两者结合使用
你可以使用捆绑式PSO缓存 同时启用预缓存
这样做也很好
如果采用这种方法
你就可以对你能找到的
所有PSO使用捆绑式PSO缓存
而对于你没能找到的PSO
预缓存就可以解决这部分问题
这样你就不会遇到卡顿问题了
我们一直在大力进行相关开发
最近我们确认了
它基本上已经涵盖了《Fortnite》
和我们的其他示例项目中
用到的所有渲染路径
虽然它对我们的使用案例来说已经非常好用了
但并不代表对所有人的项目都很好用
可能很多授权项目中还有
很多罕见的渲染路径我们尚未发现
如果遇到了一个我们漏掉的路径
尚未给它编写预缓存程序
那么此时可能就会卡顿
如果你遇到PSO卡顿的情况
请通过虚幻开发者网络
(现已更名为Epic专业支持)告诉我们
让我们来解决这些问题
不放过任何一个问题
让虚幻引擎打造的游戏在未来不会遇到这样的问题
因为有了这种解决方案
我们基本上已经把问题都解决掉了
在未来的虚幻引擎游戏中
我们已经基本不会再遇到PSO卡顿的问题了
这让我感到无比振奋
但也许你还会想
你们付出了这么大的努力开发这个功能
但是为什么主机上没有这个问题
为什么主机平台就没有卡顿问题
为什么主机游戏在运行时不需要初始编译阶段
这个问题对于主机平台来说并不复杂
因为每款主机都使用同一种GPU
游戏开发者也只需要对每款主机制作一个版本
比如这个版本专用于Xbox
这个版本专用于PS5
这个专用于PS4等等
这样你就可以在游戏打包阶段
直接编译成最终的可执行代码
而在PC上 决定要编译哪些内容的因素
不止是着色器
还包括图形数据 混合数据和Alpha通道等等
主机游戏不用考虑这么多
对于主机游戏
只要你准备好了着色器
那我们就用这些着色器
我们不关心混合状态 有什么用什么
而采用这种做法
可能还是会对性能有所限制和挤占
但重点是主机游戏开发商们
认为这种妥协是可以接受的
他们明白这个问题是无法完全解决的
这就是平台的特点决定的
总之言之 对于像我这样的开发人员来说
这种妥协是完全可以接受的
因为我不需要去处理PSO缓存之类的东西
我相信所有的玩家也都会认同的
我们认为这是一个非常可以接受的妥协
那么问题来了 移动端又是什么情况呢
与主机相同吗
不幸的是 移动端和主机情况不同
反而会遇到和PC平台一样的问题
不同的设备型号太多
你懂的 不可能为每一种设备专门进行编译
另外手机CPU的核心速度较慢
核心数量也较少
意味着编译PSO需要花费更多时间
我们已经采取了一些方法
尽量改善移动平台的情况
比如说我们列出了一些
不那么常见的PSO排列组合
直接把它们过滤掉了
这些PSO直接就不编译了
所以工作量少了很多
希望速度能快一些 但这样一来
如果你确实要用到其中某个PSO排列组合
那就会出现卡顿
但这种情况应该很少见
那么下一个问题很重要
如果我们展望未来
这个情况可以得到改善吗
比如说 我们可以做分布式的PSO缓存吗
经常有人会问到我这个问题
我自己也会思考这个问题
如果你在Steam上玩游戏
它会将SteamOS或SteamDeck的
PSO缓存分布在Vulkan上 这简直太棒了
虽然它本质上是一台PC
但其实这个问题并不存在
他们会用到一个
叫Fossilize的Vulkan插件
但这个插件对Dx12游戏是不可用的
而对于Linux发行版
也有一个叫Bazzite的插件
可以用于掌机设备
但这种方式只能对固定的硬件配置
如SteamDeck或其他掌机设备
使用而想要对所有
Dx12游戏使用这种方式
如果有人能做到当然非常好
但其实是不切实际的
首先 数据量非常大 多达许多许多TB
其次 这种东西不能放到云端
你不能让一个玩家在玩游戏的
时候遇到一个什么东西
然后把它上传到云端
然后再让另一名玩家下载运行
因为就像我一开始说过的那样
这些着色器都是编译好的程序
会在你的电脑上运行
如果任何人都能够将可执行代码上传
然后在别人的电脑上运行
这会导致非常严重的安全风险问题
除了安全问题以外
这些代码还会让你的电脑很容易崩溃死机
所以归根结底 还是得安排人去为
每一款游戏 每一个驱动程序版本
每一种GPU 手动收集所有会用到的PSO
这种方式真的太不切实际了 也很难做到
也许有人能解决这个问题
那可就太好了 我非常期待
但目前来说我还想不到要如何解决
如果有人能开发出这样的服务 然后收费的话
相信他能挣一大笔钱
那么问题又来了
非要做这样的优化是不是有点过于积极了
我们是不是太贪心
我们是不是飞得太靠近太阳了
将PSO状态参数进行捆绑式编译是不是个错误
因为要编译的排列组合太多
所以我想问 这么做是否值得
因就像我刚才说的那样
主机游戏开发商们就认为不值得
他们觉得 算了 就用这一个着色器 直接编译它
不去管什么状态参数
有一样东西可以稍微缓解这个问题
Vulkan图形库里有个图形管线库
它允许你将PSO拆分为四个阶段
也就是说我们会进行实时拆分
你只会编译这一个着色器
而其他的PSO状态参数会储存在
这些散列的片段中
然后你可以在绘制时再把它们拼接起来
就像是Dx11时代的做法
但这样做就可以提前编译着色器
进而实现非常优秀的性能
但这个解决方案还处于早期阶段
并非所有GPU供应商都支持它
但我对未来充满希望
希望能够有更多人支持它
比如GPU供应商和图形API等等
那么我来做个总结
你也许会想 天啊 听起来太可怕了
PSO真的是更好的解决方案吗
这么做值得吗
要我说当然 PSO确实拥有很大优势
但是如果没有事先做好周密的计划
我不建议所有游戏都迁移到Dx12
不然可能不尽人意
现在问题是 没人强调过这个问题
我们自己之前都没有太过注意
我已经在游戏行业里工作18年了
在它出现前我也不知道这是个问题
游戏开始卡顿 从消费者的角度来看
这才是我第一次意识到有这个问题
啊 我发现游戏卡顿了
这是为什么 然后我去查了资料
才发现原来是这样
而现在这已经变成了一个大问题
现在每个人都知道这是什么情况了
但在几年前 人们还没有意识到这一点
我们也没有足够重视和强调这一点
我们就只是说 现在虚幻引擎
已经默认全部启用DirectX 12了
但是根本没人提过游戏会卡顿
这个PSO问题居然这么严重
不得不承认 我们本来可以
将这个问题处理得更好
但是话又说回来 如果处理得当
DirectX 11和OpenGL确实很好用
大家都会带着一种怀旧的滤镜去看它们
觉得那个时代太美好了
它们确实很优秀 但并不是没有卡顿问题
首次编译着色器时仍可能出现卡顿
只是问题和程度较低
毕竟在那个只有DirectX 11的时代
着色器本身也要小得多
如果你现在用DirectX 12
或Vulkan来运行游戏
并且你可以提前提供PSO的话
不用怀疑 卡顿的情况会少很多
渲染的效率也会快得多
此外 现在你还可以进行多线程处理
不像DirectX 11
它不支持多线程运用PSO
我们可以做到很多很强大的功能
只是之前对PSO的运用不太得当
影响了这种解决方案的名声
但是假设你是一名开发人员
听了我半个小时的演讲
你肯定心里在想 别说了
快告诉我该怎么做吧
快给我划重点吧 那好 重点来了
我专门做了一张重点的幻灯片
如果你正在用虚幻引擎5.3之前的旧版本
制作或发布游戏
如果你用的是DirectX 12或Vulkan
那么你需要设置一个捆绑式PSO缓存
这项工作非常繁琐 工作量很大
你会感到很挫折 但那你必须完成这项工作
如果你用的是虚幻引擎5.3或更新版本
引擎会有预缓存功能 而且是默认启用的
这个功能非常好用
但也并非完全不需要你付出努力
这个功能并非完全即插即用
你还是需要做一点点努力
因为游戏需要等到初始预缓存完成
所以你需要做一个简单的载入界面
告诉玩家我们要预缓存
和预编译着色器类似 只是速度快得多
此外你还需要留意
第一次载入会比较慢
因为要流送大量内容进来
你还需要注意在流送内容时
什么时候会发生核心饱和
你需要相应地控制预算
此外你还需要决定
当出现PSO缺失时要怎么做
目前的默认行为是
当发生这种情况时 游戏会卡顿
但是我们也可以设置成
暂时隐藏这个Object
或者我们先显示一个默认材质
不过这种设置并不是默认启用的
因为我们并不知道你的游戏是什么样
如果我们擅自设置这些选项
可能会让你的游戏看上去一塌糊涂
所以你需要自己研究出最适合你的设置
而且这套解决方案并不适用于
我们尚未发现的渲染路径
遇到这些情况的话 也会发生卡顿
不过这种情况应该很少见
我们应该已经覆盖到了
绝大部分渲染路径
如果我们漏掉了什么
请告诉我们 再重复一遍
预缓存功能可以
和捆绑式PSO缓存搭配使用
充分发挥两种解决方案的优势
这就是划出来的重点啦
大家可以先截个屏
3 2 1 我会把它放到网上
大家放心
今天的内容就到这里了 谢谢大家
我在想象现在大家都在鼓掌
言归正传 我觉得接下来应该进入问答环节了
毕竟我只是个会讲幻灯片的帅哥
幸运的是 我们有一整支渲染团队
对于这个话题 他们比我懂得可多得多
那么我们去和他们聊聊吧
这番演讲太棒了 Ari
谢谢 好吧 那么接下来
我们先看看这个问题
大家经常问到这个问题
PSO是否是一种错误的选择
还是我们用的方法不对
答案是否定的
PSO帮助我们解决了编译问题
以及其他很多困难
在过去 Dx11没有这个功能
它可以让你随时设置状态
这导致绘制调用的成本很高
比如大家都会努力优化
提高游戏完成绘制调用的数量
因为驱动程序要完成的
工作量确实太大了
当你发出绘制调用时
你需要收集大量的状态
而现在 这些数据都打包
进了一个PSO里
所以采用PSO不止可以
让你提前编译着色器
还可以让你运用多线程指令录制
让绘制调用的成本大幅降低
所以PSO绝对是个很好的解决方案
但是
现在事后来看
把状态都放进PSO里
让驱动程序在编译着色器的时候查看这些状态
确实可以称之为一个错误
但事后诸葛亮没有意义
当我们在开发这个功能时
我相信几乎没人能够预料到
也许有极少数人预料到了
但这个问题在未来会滚雪球一样越滚越大
幸好现在情况已经有了改观
正如你刚才提到的
Vulkan图形管线扩展插件
这是向正确的方向迈出的一大步
因为它允许我们编译
至少是预先编译各个阶段
并且它可以避免对所有状态和渲染模板格式
以及所有影响PSO的数据进行捆绑式处理
所导致的信息量爆炸
所以我们不需要面对
顶点着色器数量乘着色器数量再乘以状态数量
这样的庞大运算量
你只需要单独编译每个着色器和每个PSO
然后把它们拼接起来
这个拼接过程应该不会消耗太大成本
创建API需要的时间并不长
所以只要编译好了着色器就轻松了
不久之前Microsoft推出了一款敏捷SDK
这个东西非常好用
因为可以提供一个向DirectX中
添加新功能的方式
这种方式更快 因为游戏可以直接将这个DLL打包
然后这个DLL可以和驱动交互
这样你就不必像Windows更新那样
用一整个缓慢的流程来发布Windows更新
再要求玩家完成Windows更新
然后他们才能获得新功能
这一步很重要
因为只要我们向这个方向做出了改进
我们就可以更快地将新功能推广给用户
然后游戏和引擎可以开始使用新功能
所以我们对这方面情况的改善很有信心
让我们可以对API做出调整
让API在这方面更高效
当然 要完成这些工作还需要一些时间
在这种新功能成熟之前
我们可以先用预缓存系统
现在这个系统已经比较成熟好用了
已经尽量利用当前的API做到最好了
只是需要你去手动关联
其中包含的所有状态
我还想简单介绍一下其他引擎采用的解决方案
因为人们经常将那些引擎与虚幻引擎进行比较
让一款定制引擎解决这个问题当然要容易得多
因为当你确切知道
自己在做什么样的游戏
以及要支持什么功能时
你的优化空间就更大
不过虚幻引擎是一款通用引擎
你可以用它制作任何类型的游戏
大家已经制作了很多不同类型的游戏
而且我们的目的就是
让艺术家 不止是程序员
也能发挥出强大的创造力
这就是为什么我们很久以前就
决定允许美术师创建材质
这意味着你不必去找
一名渲染程序员来为你编写着色器
而且那样的话
你就只能在游戏里用这一个着色器
你也只需要编译这一个着色器
而虚幻引擎采用的模式
可以带来极大的灵活性
允许大型团队同时工作
创建大量内容
但如果不加以限制的话
这种工作模式就会创建大量的着色器
这个问题一部分是我们的责任
我们没有在工具里充分地表现出这一点
没有向大家显示出你用
一个材质就会创建多少个着色器
以及 比如说一个静态开关
也会影响你创建的着色器数量
或者 一些材质实例也会有着色器 有的没有
总之我们没有充分地公开这些信息
因此大家在使用中很容易就会
在不知不觉中创建大量着色器
我们已经计划对这个问题进行改进
会在编辑器中明确显示此类信息
以便你可以减少着色器数量
从而优化你的游戏
因为退一万步说
你最想要的就是少用一些着色器
也许你用不到那么多材质
也许有的材质不那么重要
我认为这里需要强调一下
材质和材质实例之间的区别
是的 你可能
有个基础材质
你会用到它 还会用到材质实例
不过只要你不更改或添加任何
需要时间编译的内容
它就不会创建新的着色器排列组合
这是好事 因为这样的话
这些内容就可以打包起来
用来更高效地进行绘制调用之类的任务
但有的时候
有的变量在编译时是常量
用来降低 比如静态开关
没错 就像静态开关一样
可以降低这些着色器的复杂程度
并使其运行得更快
很多时候我们有充分的理由
将其做成静态开关
但也有很多时候不能这么做
曾经把这些东西做成静态开关非常重要
因为着色器代码占用都很大
但现在GPU已经强大很多了
而且我认为 如果它不影响寄存器
那么最好将它们
保留为运行时变量
这也可以说是我们的责任
因为目前材质编辑器
并没有很好地显示动态分支
委婉地说就是这样 这方面我们也在努力
目前我们正在对引擎做出很多改进
希望能够更好地支持这方面问题
不过确实 有的时候我们都会看到一些材质
包含毫无意义的静态开关
可能只是一条if指令
运行起来效率一样很高
当你在添加静态开关时务必要注意
这样做会创建一个全新的着色器贴图
其中包含所有排列组合
这些排列组合都会进行编译和预缓存
是的 我们有去重功能
比如说 如果你创建了多个相同的着色器
引擎会发现这个情况
不会创建多个相同的着色器
但有的时候你可能会创建多个着色器
它们只是文本上的不同
但功能和机制完全一样
比如说 也许你会对一个数据乘以一个常量
生成一个新的常量 所以差别很小
但也是一个新的着色器
又或者你可能会合并一些材质
比如说这两个材质是由两个不同的人制作的
他们彼此之间并不知道
对方已经做了一个一模一样的材质
这就会导致着色器数量
发生不必要的爆炸式增长
只要开发规范和管理得当
这种情况就可以避免
但此时你还是需要在制作成本
和最优化着色器集之间做一个取舍
因为如果你要打造一个巨大的世界
你不可能让所有参与的美术师
时时刻刻都保持充分沟通
如果你拉所有人开个会说
我们来制定一个计划
尽量减少使用到的着色器数量
这很好 这就是组织生产的正确方式
我们团队在内部就是这样做的
我们会安排技术美术师制作材质
然后由其他人来使用
将这些材质实例化
你只需更改它们的纹理或参数即可
但是采用这种工作方式
我想说的是 你可以做到尽善尽美
但在开发成本和提前做好一切规划
列出你要用到的着色器集或材质集之间
你总得有所取舍
- 你说
- 我可以聊一聊基于云的内容
我们另外可以聊的一个话题
如果你有很多人运行同一款游戏
使用同一个版本的驱动程序
使用相同的GPU
然后其中一个人完成了PSO编译
那么也许你会想 嘿 我能不能
把编译完的东西分享给其他人
这里的问题是着色器是一种可执行的代码
这种东西不能分享
不安全 是一种安全隐患
所以很遗憾 这种方式是不可行的
而且也很不理想 毕竟第一个遇到
问题的玩家肯定会经历卡顿
所以是个不太公平的解决方案
另一个问题是
我们的工作完成了吗
当然没有 我们的工作还没有完成
我们还有很多事情要做
我们仍然想改进这各方面 我们正在收集反馈
我们获得的反馈越多
推进的下一步工作就越多
还有几个问题 我还需要再回去研究一下
是的 如果我们漏掉了什么
我们非常想知道 因为我们想解决这些问题
理想情况下 就算你遇到了我们漏掉的东西
应该也不会发生卡顿 只是会突然刷新材质之类的
另外我们还在努力减轻PSO预缓存功能
在拥有6或4个核心且支持超线程的常规游戏电脑上
进行预缓存
正因如此 我们可能需要
更高效地区分哪些东西
需要先进行编译
这方面我们还有很多工作要做
我还看到一些问题
关于世界分区的问题
我还需要多查一些资料
直接聊一下世界分区的内容吧
因为大家可能会看到较低的地块
在替换之前持续较长时间
我也同意 有很多使用案例是我们没有遇见过的
因为我们只会跟授权厂商合作
我们自己有《Fortnite》 和很多示例项目
我们会利用这些项目的经验来指导这个优化过程
而且效果很不错
但是 恳请大家
如果谁做了个游戏 卡顿问题很严重
请通过UDN(现Epic专业支持)
或论坛联系我们
因为只要有问题 我们就要调查清楚
是的 正如Kenza和Ari所说
我们的工作尚未完成
我们正在努力解决这个问题
当然 我们给你的第一个建议
就是使用最新版本引擎
我们明白 并不是每个人都有条件随时用最新版本
但这是一套不断演变的系统
所以你可以相信 5.5版本
在这方面表现肯定会比5.4版好
而5.4版又比5.3版好
以此类推
而对于有能力搭建
自己的引擎的授权厂商
大家也可以精准挑选一些
与PSO预缓存相关的特定改动
即便你还是使用5.4版引擎
也可以把5.5版中的
相关功能迁移进去
但还是那句话 如果你已经用了
最新最好的版本
但依然遇到问题 请一定要联系我们
你们提供的信息非常重要
可以帮助我们进一步优化这方面的问题
没错 我们也对开发者有一些建议
比如Ari的最后一张幻灯片里的内容
此外我们已经聊了一些别的事情
比如我们已经说过
要尽量控制使用的材质的数量
这是可以做到的 只是有些繁琐
我们正在添加相关的工具
可能不会在5.6版中推出
不过我们会尽快推出的
我们正在尽最大努力
争取在5.7版里推出
另一个建议是
这些解决方案不是全自动的
也永远不可能是全自动的
你还是得让游戏做一些事情
比如说做一个加载界面
告诉玩家你正在编译着色器 对吧
引擎无法为你执行此操作
只能在游戏层面做
此外我们有API 相关的问题
都在文档中解释了
我们有API可以知道什么时候是游戏在提供功能
什么时候是引擎在提供功能
这样你就可以用文本显示正在编译着色器
因为从用户体验的角度来看
这种做法非常好 因为这样玩家就会知道
哦 游戏没有卡住 正在加载呢
我明白游戏必须要执行这一步
这可能需要30秒或
一分钟左右的时间
所以把这个信息放在加载界面上
就可以把这个情况告诉玩家们
好吧 我觉得差不多了
哦对了 拜托 请一定要去
读我们发布的博文
请一定要阅读文档
我们必须要反复强调
因为我们之前吃过这个亏
我们把这个PSO预缓存功能
加入到你的常规性能优化流程里
否则的话 你就会遇到卡顿的问题
我们现在有许多选项
可以用来分析这个功能
你会获得一份报告 告诉你是编译了PSO
还是漏掉了什么东西
以及为什么会发生这种情况
是哪个组件导致了缺漏
所有信息都会显示
但请一定要将这个功能
包含在你的试玩过程中
和你的性能收集过程中
然后再采取相应的改动 再重复一遍
如果你遇到了一个缺漏的情况
你无法修复
请与我们联系 我们将进行调查
好 这里我可以补充一点
Unreal Insights中
包含很多这样的调试功能
所以当你遇到卡顿时 很容易就能
在Unreal Insights中查看
如果你查看问题详情
就能看到问题可能是
由于PSO编译造成的
它会告诉你哪些没问题
哪些编译 哪些着色器缺失
是PSO预缓存没有涵盖到的
这样你就可以判断你遇到的问题
是否是我们上面说的引擎bug
之前我们以为不会用到的东西
那么我们就需要修复这个问题
呃 又或者问题可能是
某个不正确的PSO预缓存配置导致的
总之这些信息都会
在Unreal Insights里
这个功能也是许多开发者会频繁用到的
所以你就可以在你常规的分析工作流程之上
再采用和大家相同的工作流程
好 再收集一波问题吧
好的 没问题 现在可以开始了
那么 那么大家请注意了
现在我们开始问答环节
如果你对本次直播
到目前为止的内容有任何疑问
请在聊天中提出 我们会尽量解答
那么首先呢 在我们开始回答
观众提出的第一个问题之前
我们先来看看事先
在网上收集的几个常见问题
那么 我想请问大家的第一个问题是
为什么虚幻引擎不能编译所有的着色器组合
好的 我来回答吧
没错 我记得这方面问题
在刚才的幻灯片中提到过了
如果你要编译所有可能的排列组合
那么数量可能会高达上百万个
因为有很多种状态
不止是有着色器
就像是顶点着色器的深度
将你的网格体应用到你的渲染目标布局的顶点元素
而且取决于你是否使用HDR之类的东西
都会用到不同的PSO
那么问题是 我们是否能编译它的子集
但这样还是会卡顿 所以我们需要
处理非常非常多的着色器
但是我们又不会用到
着色器表里的所有着色器
我们只使用这些着色器的一部分
但当我们编译时
我们不知道会用到哪些
不会用到哪些
所以我们实际上会过量编译
大量的着色器
是的 此外
我认为还有一件事值得一提
比如当你使用一个捆绑包时
当你已经找出了
游戏中的所有PSO时
如果你的游戏是个
规模庞大的开放世界游戏
或者其中包含大量不同的元素
当你在PlayStation上运行时
你只会处理 海量PSO中的一小部分
因为如果你只玩几个小时的
游戏 根本不会看到游戏的全部内容
或者当你运行《Fortnite》中的大逃杀
情况就会变得非常极端
因为游戏中总共有数千款外观道具
如果你只玩几场游戏
你不可能看到所有这些外观道具
你很有可能永远也不会见到全部的外观道具
你只会见到这些道具中的一小部分
因此 提前编译所有内容是一种浪费
至于你具体会见到的内容的比例
每款游戏都有所不同
但尽管每款游戏你见到的部分都不同
但总体而言都只是
这些游戏的一小部分
因为大家不可能会同时应用所有的皮肤
或者你不可能同时玩到所有的模式
比如说你玩乐高或者音乐节体验的时候
或者你来到一个用户制作的小岛上
那么你就只会见到这些内容相对应的PSO
而不是所有PSO
所以这就是捆绑式缓存的问题所在
因为如果你把整个游戏的所有PSO都找出来
提前进行编译
那么你就会编译太多
根本用不上的东西
而预缓存可以解决这个问题
因为它只会处理你在一次游戏中载入的东西
没错 那么再多说一下
《Fortnite》与这方面问题相关的事情
在采用PSO预缓存方式之前
我们曾经使用过捆绑式缓存
我们也遇到过这个问题
我们可以努力找出很多的PSO
事实上我们已经为大逃杀
找出了很多需要渲染的东西
所以大多数时候都不会出现卡顿
问题在于
即便我们可以覆盖到所有的PSO
但困难还是存在
总数可能高达上百万个
此时我们就会撞到性能上限
这样在引擎和游戏之外
当你需要编译这么多东西
即便是可行的
你还是会遇到问题 比如你会看到
驱动程序缓存占用很大硬盘空间
而且缓存是有上限的
因为驱动程序不希望
一个游戏就把整个缓存占满了
这样当驱动程序在预编译时
需要处理太多PSO
就会在你持续预缓存的期间
开始清除缓存里的东西
这就会导致整个
预编译过程变得失去意义
因为驱动程序会开始删除
你之前编译好的东西
此外还有一些别的原因
可以证明这样做
并不是最好的解决方案
即便你非常高效地找出了所有PSO
如果你的游戏内容量太庞大
那就会把驱动程序缓存给撑爆
这就是问题所在
是的 回到我们之前提到的一点
一些引擎和游戏采用了另一种方法
比如设计一种所谓的超级着色器
把一个材质应用到所有地方
只是用参数来控制 对吧
这是一个很好的方法
如果你可以只更改着色器的纹理
或某些值或其他内容 那就很高效
但是如果材质有很多不同的行为方式
基本上你就会用到一个巨大的Switch语句
就会包含很多类似
if是这类材质 那就运行这段代码
if是那种材质 那就运行这段代码
这种方式的问题在于寄存器分配
要说这个可能会
涉及太多技术面的事情
但稍微聊一聊
我觉得也很有意义
简单来说 硬件的寄存器数量是有限的
在最糟糕的情况下
如果你的着色器
使用太多这样的代码 调用太多的函数
那么就会影响所谓的占用率
也就是说 当你在编译你的着色器时
GPU需要分配多少硬件资源
即便你用不到其中的一些PSO
或者即便没有任何任务会
占用GPU最宝贵的硬件资源
那么GPU还是会在判定占用率的时候
认为你的游戏占用了最宝贵的硬件资源
这一情况在达到某个阈值后
就会大幅影响你的性能
那样的话情况就太糟糕了 因此
这种超级着色器方式也不太合理
它在一定程度上是可行的
绝对能够为特定类型的游戏解决问题
但并不是所有游戏
还是说《Fortnite》
不对 我是说虚幻引擎
虚幻引擎的设计思路是为了
支持所有的使用场景
甚至包括一些非游戏的使用场景
我们不能轻易地决定说
我们不会做这个功能
所以你就不能
做你想要做的内容
好的 下面是大家提出的下一个问题
是关于预缓存的
是的 这篇帖子挺长
我来逐字逐句读一下
尽量不漏掉任何部分
这个问题是说 在我看来
在载入期间没有编译好的任何内容
都会在运行时
进行着色器编译
这个操作不会阻塞游戏
因为这些着色器不会用到
但如果你在游戏中每一次
见到一个新的粒子效果之类的东西时
不会在整体上严重影响CPU性能吗
我觉得确实会这样
如果遇到这样的情况
那么捆绑式预编译
可能依然是最佳的方式
所以我们尚未移除
捆绑式PSO缓存这个功能
捆绑式缓存依然可以使用
我觉得对于某些游戏
比如线性游戏之类的
如果你有能力收集所有的PSO版本
那么我觉得采用这种方式很合理
但游戏规模越大
玩家生成的内容和世界就会越多
缓存也可能变得非常大
这个方案可能就不太跟得上形势了
因为PSO版本会变得太多
没错 PSO预缓存功能
会占用核心时间
不过有一个选项可以加以限制
而且我好像看到下面
有一些建议 理想情况下
我们希望加入一个选项
在载入时处理的PSO预缓存
工作量当然应该
比运行时处理的数量要多
而且我们还需要研究一下
将内存占用的问题也纳入考虑范围内
系统应该控制
预缓存功能占用的资源比例
这样也许更好
此外 按理来说 这种占用率过高的
问题应该只会在第一次执行时发生
另外还有一个因素就是
如果你使用捆绑式缓存
会对QA部门或者相关人员造成巨大的工作压力
毕竟他们需要收集所有可能的PSO排列组合
以及覆盖所有不同的图像设置
对于那些线上服务游戏
特别是每隔几个月都会对世界进行更新的游戏
我不是说《Fortnite》
其他游戏也是这样
是的 这样的话你
每次都得重新收集一遍
而且每次你遇到着色器改动或者别的情况
就好比我们引擎也会
每隔两三个月更新一次
甚至频率更高 我们就遇到过着色器改动的情况
那么所需的一切资源都会失效
那么你就需要再次收集所有东西
像这样持续地重复收集工作
会耗费大量的人力和时间
而且如果你在这个过程中漏掉了
什么东西 那还是会出现卡顿
那么你就可以结合这两个解决方案
比如你知道一定数量的资源集一定会出现
你很容易就能把它们全找出来
这就能在启动时先解决掉一部分问题
为你的游戏制作一个简单快速的载入界面
然后再处理别的问题
所以对这类游戏来说
PSO预缓存方案就非常合适
好吧 就是这样
太棒了 好吧 我们继续
接下来是一些我们
从聊天记录里找出来的问题
我把这些问题
快速地给大家念一遍
所以先说声抱歉 重头戏要来了
那么第一个问题是 预缓存功能是
如何与世界分区配合工作的?
他们给出了一个示例
就是他们自己的项目
在流送进来新单元时
似乎编译了很多新PSO
但这样还不够 所以他们会遇到很多卡顿的情况
我来回答吧 呃 我之前也看到这个问题了
简单扫了一眼
我们自己的游戏也会使用世界分区
并没有遇到卡顿问题
但确实 这并不代表这个问题不存在
这个情况很有意思
那么 我记得我们应该已经更新了文档
提供了更详细的信息
应该是在5.5还是5.3版本中
当前的5.5版本中应该有
就是引擎会显示一些信息
你可以清楚地看到卡顿来自于哪里
你可以通过操作记录
查看是哪个着色器造成了卡顿
而且PSO预缓存验证功能
也会在日志里清晰地显示
是哪个组件或材质
引擎还会告诉你 嘿 你缓存了这个东西
但我们没见到这个东西
或许这些功能可以帮上忙
还有另一个造成卡顿的可能性
那就是全局材质
因为当你在世界中四处移动时
全局着色器会创建光源的变体
我们的游戏中就有数千个光源的排列组合
因为这些光照效果很重要
确实会对寄存器造成压力
所以需要快速处理
此外 我们还在最新版本中
对特定的全局图像PSO集
添加了PSO预缓存
这也有可能会在运行时造成卡顿
这些PSO主要用于烟雾
灯光 Slate等
我记得应该还有别的东西 对不起
但是记不太清了
总之这些因素确实可能造成卡顿
搞不好你们已经修复了
但世界分区的另一个问题是
如上所述 如果有必要的话
我们也可以帮忙
解决低面数地块刷新的问题
但是如果可以的话
我想要先看一下日志
深度分析一下具体情况
才能更好地查明问题所在
哇 那太好了
下一个问题是
PSO是否是基于每个核心着色器
排列组合进行编译
还是也会对实例化材质进行编译
这个问题也让我来回答吧
这个问题的答案取决于使用的是什么组件
如果这个组件用到的材质实例
因为静态编译的常量
而有排列组合上的不同
那就也会对其进行编译
否则的话就会编译
基础材质里的PSO
好的 接下来是遍历卡顿的问题
你们是否会解决或者尽量减轻这个问题
它又是什么原因造成的呢
我刚刚不小心静音了
不好意思 我来回答
是的 遍历卡顿
或者一般来说的卡顿
是一个更广泛的问题
而不只是PSO编译的问题
当然我们正在调查这些情况
发生这种情况的原因有很多很多
不同游戏之间的情况也不同
这个问题嘛 没有通用的答案
我们需要分析具体游戏
看看到底是什么原因造成的
是加载的问题吗 比如冗余载入
比如说 你有一个场景捕获条件
当你在四处移动时就会触发
这就会增加你的处理时间
也可能是垃圾回收
原因有很多很多
其中一些是在引擎端
比如PSO编译
举例来说 比如
流送Actor或组件
有时这些任务会花费较长时间
我们正在研究
如何对游戏物体的表现方式进行轻量化处理
因为如果一个东西没有互动性机制
那么就没有理由把它当作一个完整的组件
付出Actor和其中全部组件的完整运算开销
顺便说一下
CD Project Red和我们说
他们已经为接下来要推出的游戏
解决了这个问题
虚幻引擎是一个开放引擎
你可以拿到它的源代码
很多人都会对它进行改造
当然 我们还是希望为大家提供
开箱即用的优质解决方案
我们正在努力解决这个问题
但还是那句话
这是一个非常非常大的问题
如果我们要解释所有的情况
那么本次直播的时间完全不够用
所以今天直播的问题
还是限定在和PSO有关吧
没错 太对了 下一个问题
你们是否有计划加入某种程度的
预测性PSO预缓存机制
比如对附近的网格单元(grid cell)
和世界分区提前进行预缓存
从而减轻加载卡顿的问题
我来吧 我认为这是一个好主意
可以与其他世界分区相关功能协同工作
这种预测式的预缓存机制
之前还真没想到
这确实是个很有趣的思路
太好了 我要把它记下来
不错不错 我看看 下一个问题是
有用户说他们最近将自己的实时开放世界游戏
从捆绑式解决方案迁移到了预缓存解决方案
自那以后 他们就一直在
与显示默认材质或在低端设备上
CPU占用率较高等问题作斗争
或者因为预缓存缺失而导致卡顿
以及游戏会不停地释放和加载PSO
都是捆绑式解决方案没有遇到的问题
他们表示 现在他们的游戏会占用较高的CPU
并且导致玩家看到默认材质
偶尔还会导致卡顿
似乎这种解决方案对他们的特定案例来说并不理想
这种情况是正常的吗
还是说只是他们的项目遇到了这种问题
这种情况并不正常
我想看看你们的项目中用到了多少材质
这确实是个问题
如我们之前提到的 刚才应该分享过这个信息了
目前在一张《Fortnite》地图里
我们会加载大约20000个PSO
基本是就是这么个数量级
我猜你们的项目用到的
PSO数量应该比这个少
因为《Fortnite》包含
大量不同类型的内容
所以我想看看你们的项目用到了多少
默认情况下 不应该显示默认材质
不过可能会突然刷新
这可能是出于竞技游戏的原因
这是设置决定的
是的 设置 我的意思是
你可能会因为竞技游戏的原因
不想要出现这种刷新材质的情况
另外我们可以帮助的一点是
如之前所说
我们也希望降低CPU占用
如果我们能够更好地分拣出
需要编译的东西
比如区分出屏幕上需要
编译的内容 当然
这个功能我们还没做完
这样应该也能大幅减轻
突然刷新材质的问题
还有个选项 刚刚在别的问题里
也问到了这个情况
有个选项可以让你在预缓存PSO之后
把PSO保留在内存里
我们一直在开发这个功能
而且已经加入到5.5版里了
之前确实没有这个选项
所以不知道你们用得是哪个版本的引擎
如果你们试试5.5版的话
情况应该有大有改观
你的游戏应该运行得更顺畅
当然其中也有一点需要权衡
如果你不是在PSO缓存完成后
就将其删除
而是选择将其保存在内存里
这就会占用更多内存
如果是大逃杀
我猜大概会占用900MB的系统内存吧
这要视具体情况而定
关于《Fortnite》
我想要多聊一聊和这个策略有关的事情
没错 问题中提到
捆绑式缓存会把所有PSO保留在内存里
而预缓存系统会将它们删除
然后让驱动程序对它们进行缓存
并在需要的时候重建
就像我说过的那样 有个选项
可以把PSO留在内存里
因为对于某些硬件厂商和驱动程序来说
这样做可以在必要情况下
重建PSO时大幅提升性能
不过确实 这样会带来内存开销
正因如此 默认情况下
预缓存方案会在编译完PSO之后立刻将其释放
所以 以《Fortnite》为例
如果我们把所有预缓存的
PSO保留在内存里
应该会增加 上次我看的时候
大概是几GB的内存占用
就是说光预缓存就会占用几GB的内存
因此我们为《Fortnite》采用的方式
以及我推荐其他游戏也采用这个方式
虚幻引擎中提供了一个选项
可以基于内存桶(memory bucket)
来设置控制台变量(CVar)
那么如果电脑拥有XGB的内存
那么你可以为不同的内存桶设置CVar
那么通过这种设置
你就可以在内存较大的电脑上
保留更多的PSO
因为这些用户可能不会太担心内存发热的问题
然后在内存较小的电脑上
保留较少的PSO
而且现在我们正在和硬件厂商合作
对GPU和驱动程序进行改良
那样的话也许我们就根本不需要做这些设置了
只依靠驱动程序缓存就够了
这点确实很重要
而如果用捆绑式缓存
把所有PSO保留在内存里
那也需要确保这个捆绑式
缓存占用的空间不要太大
况且还有其他原因
总之把PSO保留在内存里本身不是一件坏事
但是对于PSO预缓存来说 这是个运行时系统
会在游戏运行的整个期间运行
那就会导致问题了
好 下一个问题
你们是否有计划优化PSO设置
使其更好用
我想这个问题指的是捆绑式缓存方案
众所周知难度确实很高
要收集和核对所有需要编译的东西
这个工序需要耗费大量资源
我们目前并没有展开这方面工作
短期内可能也不会去回顾这个问题
我们的计划是将PSO预缓存方案
优化到足够好的程度
这样大家就完全没必要用捆绑式缓存了
但在此期间
我们承认要对捆绑式缓存进行妥善的设置
需要花费一定成本
我们有文档 我们知道文档里写的
也不一定是最佳操作
但如果你按照文档操作 你只需要做一次
是的 而且还有个问题是
我不确定引擎是否能帮你
把所有的排列组合都收集到位
-没错 这就是问题所在
-这是工作量最大的部分
虽然设置起来很麻烦
但只要设置好了就没问题了
但最大的工作量
肯定是找到所有排列组合
所以你需要进行测试
你必须 也许你需要对
整个世界进行流程试玩
或者把摄像机路径跑一遍
也许这样就可以收集到你游戏里的
所有东西 但也可能做不到
是的 问题是 比如我们
经常遇见这样的情况
比如某个东西被另一个东西盖住了
你需要自定义它的深度
或者一个物体突然之间靠近一个光源
或者不处在光照之中
你必须收集到所有这些案例
是的 因为你可以把这个物体四处移动
一会儿放到这里
一会儿放到那里 突然之间在阳光下
突然之间又离开阳光
进入阴影中了 当然我们已经尽量进行优化
不去渲染这些物体
但毕竟它们可能会对光遮蔽系统和
其他各种效果起到作用
因此你必须确保找到
所有这些代码路径
对于捆绑式缓存方案
也许我还能提一个建议
我记得我之前和一个授权厂商聊过
那就是预缓存方案
也可以用来推动捆绑式缓存的收集过程
不过这样的话
最终你会得到一个巨大的超集
那么在加载你的捆绑包时
可能就会花费很长的时间
这个操作本身是很容易的
另外 如果你启用了预缓存
当然默认就会启用
那么引擎会在将PSO添加到
捆绑包的时候 将这些PSO排除在外
如果PSO已经进行预缓存了
那么就只会把漏掉的
那些PSO加入到捆绑包中
你可能想把这个功能关闭或者开启
都可以调整
我们已经有很多CVar了
所以除非有特殊的必要
我们应该尽量把所用东西放在CVar里调整
因此对于不同的游戏
肯定有一些不同的解决方法
还有一个问题是
你什么时候梳理这个捆绑包
因为你当然可以在开发过程中
全程启用收集机制
然后到最后再核对你找到的所有东西
但是 比如你原本用到了一个材质
然后又将其删除
或你将这个材质用到了别的地方
这就会导致你缓存的PSO
比游戏实际使用的多得多
例如《Fortnite》 过去就采用这种方式
我们会每个赛季收集一份新的缓存内容
我们没有直接沿用上一个赛季的缓存内容
因为这样的话它会变得越来越大
太棒了 那么下一个问题是
由于一些着色器
已经变得越来越复杂
那么有没有方法可以让游戏开发者
对默认着色器进行精简
用来在虚幻引擎中制作类似N64风格的游戏
这种方法存在吗
好的 我觉得要实现这个目的
最简单的方式就是在PC上使用移动渲染器
这个操作是绝对没问题的
我们对《Fortnite》的性能模式
就是采用的这种方法
不知道这种方法是否能满足
你的画质需求 呃 如果不能的话
那还是得想办法
削减材质的精细度以及材质的数量
不过当然啦 你能做到的事情有限
毕竟要支持这么多功能
固定的资源开销是不可避免的
对于《Fortnite》 呃抱歉 我是说
虚幻引擎有很多很多着色器代码
开销并不大
因为如果一份材质不使用任何功能
那么它就不会包含在最终的着色器代码中
但有一些固定开销我们确实没法消除
因为我不知道游戏会怎么做
所以我建议 要么使用移动渲染器
或者前向渲染器 这个功能还在引擎里
可以在前向模式下运行桌面渲染器
它的着色器稍微简单一些
也可以试试这个方法
在着色器编译期间
是否有蓝图节点可用于检查
完成的百分比 然后显示在UI上
有的 我们还没有明确公开这各功能
但大家最近已经开始讨论一个函数
你只需要调用这个静态全局函数
就可以检查未完成的正在编译中PSO数量
你可以用这个函数
也许显示的百分比不太准确
这个函数能从蓝图调用吗
它不能从蓝图调用
所以你必须把它公开
你必须将其放在C++里
没错 C++里 你可能还需要
获取一个初始计数 然后
才能做一个类似进度条之类的东西
应该不难
请与我们联系
我们会尽力帮助你实现这一目的
是的 我们正在计划对API做一些小的改进
能够返回未完成的PSO数量 此外
我们在博客文章里讨论到的所有内容
我觉得之前还没有提到过
用于告诉引擎你可能要更换网格体材质的这个API
因为它是我们的工作还没有覆盖到的案例之一
它对渲染器来说完全是个意外情况
如果你在运行时更换材质
因为我们没办法预测这个情况
我们也无法足够早地提前预缓存
所以它要么会突然刷新
要么会显示默认材质 或发生卡顿
我们将添加一个API
你可以在其中提前说明
我可能会将这组材质用于这个网格体
然后你可以切换使用
这个组中你已经提供的材质
这样它们就能像所有内容一样被缓存
但目前我们还没有实装这个功能
我们希望在5.6中添加这个功能
太棒了 我很期待这个功能
那么下一个问题 是否有办法
可以查看管线缓存
看看捆绑包中已经记录了哪些东西
存在哪些东西?
这个问题我来答吧
是的 我们有一个编辑器命令行
应该叫做着色器通道缓存工具
(Shader Pipeline Cache Tools)
你可以使用这个工具
提供一个新的管线缓存文件
它将转储你记录的所有PSO描述符
这样你就可以看看
这个文件有多大
其中有多少个PSO
我记得还有一些工具
可以用来合并缓存
这样你就可以将多次
收集工作得到的结果进行合并
是的 我建议你研究一下这个命令行工具
有相关文档介绍如何使用
太好了
下一个问题是
是否有可能在未来的引擎版本中对PSO进行简化
没有 不太可能
PSO只会变得越来越繁重
随着功能的增加
PSO的成本只会变得越来越大
因为虚幻引擎始终致力于将图像
保真度推上新的高度
所以我们会不断加码
着色器很复杂
话虽如此 还是有可能的
随着接下来的API改进
比如之前提过的动态
着色器连接之类的功能
目前已经暂时搁置了
不过这些功能最终会针对DirectX发布
这就可以让我们编译多个不同部分的着色器
然后把它们连接在一起
这样一来 编译材质的成本就会降低
因为你可以在开始时先编译
一大段常用代码 这一步只用做一次
然后每次编译材质时都只编译一小部分代码
然后将其连接到之前
已经编译好的大部分代码
让每一个材质的编译成本降低
所以目前阶段
我们无法去做简化PSO
或着色器代码这样的工作
但这方面问题在未来还是有希望的
随着API 和硬件变得越发先进
可以让我们更高效地进行编译
是的 当D3d12在十年前发布时
编译PSO大概
只需要10 20或30毫秒
现在就麻烦多了
比现在少多了 对吧
没错 现在呢
编译一个PSO可能就需要上百毫秒
没错 当编译的概念出现时
基本上指的就是
从XPC或甚至更早的字节码
1比1映射到机器码的过程
编译器非常简单
只需查看并翻译它们看到的内容
我想说的是 DirectX 9
只有64个指令槽
因为本来就没有太多工作需要处理
着色器很简单 这个翻译过程很简单
这根本就不是问题
但随着着色器变得越发复杂
问题就开始出现了
当然 现在的驱动程序
想要进行编译成本方面的优化
因为优化本身非常值得一做
可以让运行速度加倍
不过还是那句话 随着着色器变得越发庞大
编译器也变得越发复杂
还想让一个编译器
在10毫秒内完成工作是非常困难的
而10毫秒本身就已经很长了
因为整个帧也才16毫秒
你不可能为了等待着色完成而让游戏暂停
是的 200毫秒
简直是比永远还要久 相当于很多很多帧
好的 下一个问题是有人说
他们使用静态开关在常规材质和调试逻辑之间切换
你们推荐这样做吗
我觉得问题不大
只要你的最终关联组件中没有引用
这些调试逻辑 那就不会使用它们
但是如果它们最终仍处于关联状态
可以切换到它们
就好比它们存在于组件可以使用的材质列表中
那么引擎就会预缓存它们
这样就会对你造成影响了
我们有很多调试着色器 例如全局图形PSO
(Global Graphic PSO)
和全局计算PSO
(Global Compute PSO)
我们知道它们的存在
因为它们是全局的
所以在引擎中
我们可以将这些内容从预缓存中排除
但只从材质的角度来看的话
我们无法预知要排除哪些
因此请确保没有
将常规材质和调试逻辑连接
这样你就不会 不要对组件使用它们
是的 不要使用它们 那就没关系
没有任何组件会加载它们
所以我们就不会预缓存它们
在游戏的发行版本中 对吧
好的 可以给一个清晰明了的静态开关使用范例吗
举例来说 如果你想控制资源数量
比如材质中有两条路径
一条路径使用大量纹理
另一条路径使用一组不同的纹理
这时应用静态开关就很合理
另外在一些平台上
显示元素数量限制压得很低
你就可以这么做
否则的话资源太多 根本无法绑定
另一个是我们之前
讨论过的超级着色器
如果你的静态开关分支有很多代码
那么你的动态着色器就会
按照该开关里最低效的分支运行
有了静态开关
只使用两种材质就更合理了
你要么凭直觉判断这种方法是否合适
这两个选项都执行以下
看看哪一个更好
太棒了 我们还有很多问题
遗憾的是我们没有时间一一解答所有问题
不过这里有个问题非常好
我觉得很适合用来结束今天的直播问答
那么独立开发者可以怎么做
特别是技术力不太强的开发者
在为自己的项目设置PSO预缓存的时候
我们可以怎么做
这个问题太好了 这正是我们
开发这个功能的初衷
理想情况下 PSO预缓存功能
本身就应该是开箱即用的
我们希望这个功能就是能降低
对你的技术力要求
你不需要手动设置太多东西
但要确保自己不会使用太多的材质
因为会影响你游戏的性能
这整个系统没有太复杂的问题
直接就可以用
- 就是这样
- 一定要控制你所使用的PSO数量
你所使用的材质数量
- 是的
-并且在缓存清空的情况下运行游戏
在PSO缓存选项里 文档中有说明
使用这个系统运行时
它会在运行前清空缓存
就好像是有人刚刚安装好了你的游戏
你就可以看看运行状况如何
如果出现卡顿 请阅读文档
其中说明了如何对其进行分析 并找出卡顿的来源
希望你可以找到出问题的材质或组件
然后对其进行调整 或者
你也可以将类似的材质合并
如果你试过了很多方法
还是会卡顿的话 请咨询我们
当然 请尽量使用最新的引擎版本
因为5.5中包含许多5.4中没有的改进
很好
我们今天的提问时间就到此为止
如果大家还有任何问题
很遗憾我们没有时间在直播里即时回答
请随时在Epic开发者社区上
发布你的问题 我们将尽力跟进
我想请各位再最后一次发言
在我们结束之前 关于今天的主题
大家最后还有什么想要说的吗
或者有什么希望大家知道的吗
我相信大家还有很多想要说的话
不过也得考虑别占用其他人太多时间
我想要感谢大家的收看
还有 再次强调
如果有任何问题 请联系我们
是的 非常感谢大家
请记住 我们处在同一阵线
都在努力制作更好的游戏
因此 遇到问题时不要先怪别人
请记住 我们都会竭尽全力互相帮助
是的
没错 那是一定的 好的
非常感谢各位来宾
今天抽出宝贵的时间来到这里
和我们一起探讨这个话题
能够深入了解这个问题
真的非常非常有帮助
我还要向所有来看直播 提出问题和参与其中的人
表示衷心的感谢
非常感谢大家来到这里
成为这个社区的一员
并与我们一起学习有关
这个主题的更多知识
中途加入直播的朋友也别担心
如果你错过了一些内容
没有关系 因为我们所有的直播内容视频
都会在我们的虚幻引擎
Twitch和YouTube频道上发布
你也可以通过虚幻引擎的所有社交频道
与我们保持联系
如果你还没有加入Epic开发者社区
强烈建议你立即加入
你可以在这里查阅我们发布的所有文档
还可以在我们的论坛里进行讨论
我们还有大量由Epic员工和
社区优秀成员制作的学习内容和教程
几乎所有与引擎有关的内容
都可以在这里找到
如果你还没有查看的话
请务必先查看一下
这就是今天的全部内容
再次对各位来宾和所有观看直播的观众
表示衷心的感谢
和往常一样 我们下周还有
Inside Unreal直播节目
我们到时候见
非常感谢大家
祝各位度过愉快的一天
再见啦 谢谢
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment