在PC游戏开发中,在将模型,纹理,着色器等加载到加载屏幕后,某些游戏会将模型一次渲染到屏幕外目标,以确保驱动程序和gpu已经完成了保持该状态所需的所有工作。模型首次渲染时会出现故障。

有人知道这实际上有多大帮助吗?它只会影响某些卡和驱动程序吗?

评论

这是每帧吗?像在预填充中填充深度缓冲区一样?

不,在加载屏幕中完成。不是在谈论深度渲染!

#1 楼

据我所知,这类事情主要与着色器编译有关。游戏第一次出现渲染时可能会遇到障碍的主要原因之一是渲染所需的着色器尚未完成编译,并且驱动程序必须先完成该工作,然后框架才能继续。

有一些背景知识。在HLSL中编写着色器时,可以使用Microsoft的HLSL编译器(fxc.exe)对其进行编译,该编译器将输出独立于硬件的字节码。当您在D3D中调用CreateVertexShader等时,该字节码将传递到底层驱动程序,该驱动程序最终将其编译为您正在运行的特定GPU的机器代码。

此内部编译步骤是“琐碎-这是一个成熟的优化编译器。因此,在复杂的着色器上可能要花一些时间。因此,当您调用D3D的CreateVertexShader时,驱动程序并不一定就此完成着色器的编译。它可以将其添加到队列中,然后将控制权返回给应用程序。同时,后台线程(可能不止一个)消耗队列并编译着色器。 (驱动程序可能还具有以前编译的着色器的磁盘缓存,因此不必在每次游戏启动时都重做所有这些工作。)

此后台线程编译意味着游戏主线程可以继续进行其他工作,例如加载其他数据或构建场景图或您拥有什么,因此通常可以赢得总体加载时间。但是这样做的结果是,当游戏开始渲染时,着色器可能不会全部完成编译。不幸的是,该应用程序无法使用DX11和更早的API来告知着色器何时完成。您可以做的最好的事情就是使用着色器渲染某些东西,这实际上使应用程序等待编译器完成。这就是游戏有时会在屏幕外进行渲染的原因。
我没有任何硬性数字,但是从质量上讲,可以通过在加载过程中预先加载着色器来减少其影响。首先创建所有着色器,然后在加载其他所有内容时,编译器将遍历所有着色器。但是,对于实时流式传输(例如开放世界的游戏),要确保没有故障仍然很困难。

还值得注意的是,在某些情况下,状态更改会导致着色器重新编译。诸如顶点缓冲格式,渲染目标格式和着色器链接详细信息之类的事情(例如,将同一个像素着色器与不同的顶点着色器或镶嵌细分着色器一起重新使用)可能会导致驱动程序不得不在引擎盖下生成不同版本的着色器。详细信息因供应商和硬件而异。这不一定是完全从头开始的重新编译-通常只是停留在一些序言或结尾代码上,但这是需要注意的。

这是新API(DX12,金属和Vulkan)处于解决该问题的良好位置。这些API具有“流水线状态”对象,其中所有着色器阶段和所有可能的相关状态都汇总到一个对象中,当您创建其中一个对象时,驱动程序将具有实际完成所有着色器所需的全部信息。 DX12还使着色器缓存显式。另外,至少对于NVIDIA DX12驱动程序(我不了解其他驱动程序),着色器编译完全在管道状态创建方法内完成,并且该应用程序可以使用多个线程本身以并行化着色器编译和其他工作。因此,在DX12中不再需要进行屏幕外预渲染以确保最终确定着色器的做法。

评论


$ \ begingroup $
从这个(出色的)答案开始已经过去了几年,人们可以发现,从第一次抽奖到管道状态创建的这种障碍也不是灵丹妙药,因为游戏可能并不知道其中的所有状态。提前(尤其是高度多样化/可修改的游戏)来尽早创建它...
$ \ endgroup $
– RCL
20-04-22在3:48