如何异步加载图形资源?


9

让我们考虑与平台无关的问题:我想在游戏其余部分运行时加载一些图形资源。

原则上,我可以将实际文件加载到单独的线程上,也可以使用异步I / O。但是对于图形对象,我将不得不将它们上传到GPU,并且(通常)只能在主线程上完成。

我可以将游戏循环更改为如下所示:

while true do
    update()
    for each pending resource do
        load resource to gpu
    end
    draw()
end

同时有单独的线程将资源从磁盘加载到RAM。

但是,如果有很多大资源要加载,这可能会导致我错过帧截止日期并最终丢失帧。所以我可以将循环更改为此:

while true do
    update()
    if there are pending resources then
        load one resource to gpu
        remove that resource from the pending list
    end
    draw()
end

每帧仅有效加载一个资源。但是,如果要加载许多小资源,则加载所有小资源将花费许多帧,并且将浪费大量时间。

理想情况下,我想按以下方式安排加载时间:

while true do
    time_start = get_time()
    update()
    while there are pending resources then
        current_time = get_time()
        if (current_time - time_start) + time_to_load(resource) >= 1/60 then
            break
        load one resource to gpu
        remove that resource from the pending list
    end
    draw()
end

这样,我将仅在能够在该帧时间内完成的情况下加载资源。不幸的是,这需要一种方法来估计加载给定资源所花费的时间,据我所知,通常没有办法做到这一点。

我在这里想念什么?许多游戏如何完全异步地加载所有内容,而又不会丢帧或加载时间过长?

Answers:


7

让我们从一个完美的世界开始。加载资源有两个步骤:首先,以正确的格式将其从存储介质中取出并放入内存,然后,将其通过内存总线传输到视频内存。这两个步骤实际上都不需要在主线程上花费时间,只需要介入即可发出I / O命令。复制资源时,CPU和GPU都可以继续执行其他操作。唯一消耗的实际资源是内存带宽。

如果您使用的平台与您的硬件之间没有太多的抽象层,那么该API可能会直接公开这些概念。但是,如果您使用的是PC,则可能是您和GPU之间有一个驱动程序,它想按照自己的方式进行操作。根据API的不同,您可能能够创建由您拥有的内存支持的纹理,但是更有可能调用“创建纹理” API会将纹理复制到驱动程序拥有的某些内存中。在那种情况下,创建纹理将有一些固定的开销,并且有一些时间与纹理的大小成比例。此后,驱动程序可能会做任何事情-它可能会主动将纹理传输到VRAM,或者可能不会麻烦您上传纹理,直到您尝试首次使用它进行渲染。

您可能对此做某事,也可能无法做某事,但是您可以估算进行“创建纹理”调用所花费的时间。当然,所有数字都会根据硬件和软件而改变,因此花大量时间对它们进行反向工程可能不值得。因此,请尝试一下看看!选择一个指标:“每帧纹理数量”或“每帧纹理总大小”,选择一个配额(例如,每帧4个纹理),并开始对其进行压力测试。

在病理情况下,您甚至可能需要同时跟踪两个配额(例如,每帧限制为4个纹理或每帧限制为2 MB纹理,以较低者为准)。但是,大多数纹理流传输的真正窍门是弄清楚您想将哪些纹理放入有限的内存中,而不是花多少时间来复制它们。

此外,用于纹理创建的病理情况(例如一次需要大量微小的纹理)也倾向于是其他区域的病理情况。在担心纹理要复制多少微秒之前,有一个简单的工作实现值得。(此外,真正的性能下降可能不是“创建纹理”调用时的CPU时间引起的,而是您使用该纹理的第一帧时的GPU时间引起的。)


这是一个很好的解释。我不知道的很多东西,但是很有道理。与其进行压力测试,不如测量运行时纹理创建的开销,轻轻地启动并调高到80%的可用执行时间,以为异常值留出空间。
2013年

@PandaPajama我对此表示怀疑。我希望稳态是“没有纹理被复制”,并且变化很大。就像我说的那样,我怀疑命中率的一部分是使用纹理的第一个渲染帧,这在不影响性能的情况下很难动态测量。
John Calsbeek

另外,这是有关异步纹理传输的NVIDIA演示。据我所读,它开车回家的关键是在上载后过早使用纹理将使其停滞。 developer.download.nvidia.com/GTC/PDF/GTC2012/PresentationPDF/...
约翰Calsbeek

我不是驾驶员开发骑师,但这很常见吗?以这种方式实现驱动程序没有太大意义,因为纹理首次使用很可能会出现尖峰(例如在每个级别的开头),而不是沿着时间轴间隔开。
2013年

@PandaPajama应用程序通常会创建比可用的VRAM更多的纹理,并且创建纹理然后再不使用它们。一个常见的情况是“创建一堆纹理,然后立即绘制一个使用它们的场景”,在这种情况下,懒惰可以帮助驾驶员,因为它可以弄清楚实际使用了哪些纹理,并且第一帧无论如何都会顺利进行。但是我也不是驱动程序开发人员,只需加一点盐就可以(并进行测试!)。
约翰·卡尔斯贝克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.