グラフィカルリソースを非同期に読み込むにはどうすればよいですか?


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

フレームごとに1つのリソースのみを効果的にロードします。ただし、ロードする小さなリソースが多数ある場合、それらすべてのロードには多くのフレームがかかり、多くの無駄な時間がかかります。

最適には、次の方法でロードの時間を計りたいと思います。

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

このようにして、そのフレームの時間内にリソースをロードできる場合にのみ、リソースをロードします。残念ながら、これには特定のリソースをロードするのにかかる時間を見積もる方法が必要であり、私の知る限り、これを行う方法は通常ありません。

ここで何が欠けていますか?多くのゲームは、すべてのコンテンツを完全に非同期で、フレームのドロップや非常に長いロード時間なしにロードできますか?

回答:


7

完璧な世界を想定することから始めましょう。リソースをロードするには、2つのステップがあります。1つ目は、ストレージメディアから正しいフォーマットでメモリに取り出し、次に2つ目は、メモリバスを介してビデオメモリに転送します。これら2つのステップのどちらも、メインスレッドで実際に時間を使用する必要はありません。I/ Oコマンドを発行するために関与する必要があるだけです。CPUとGPUはどちらも、リソースがコピーされている間、他の処理を続行できます。消費されている唯一の実際のリソースはメモリ帯域幅です。

あなたとハードウェアの間に抽象化レイヤーがあまりないプラットフォームを使用している場合、APIはおそらくこれらの概念を直接公開します。しかし、PCを使用している場合は、おそらくあなたとGPUの間にドライバーが座っており、それがその方法で実行したいと考えています。APIによっては、所有しているメモリに裏打ちされたテクスチャを作成できる場合あります、「テクスチャ作成」APIを呼び出すと、ドライバが所有するメモリにテクスチャがコピーされる可能性が高くなります。その場合、テクスチャを作成すると、オーバーヘッドが固定され、テクスチャのサイズに比例して時間がかかります。その後、ドライバーは何かを行う可能性があります。テクスチャーをプロアクティブにVRAMに転送したり、初めて使用するまでテクスチャーをアップロードしなくてもよい場合があります。

これに関して何かできることもできないこともありますが、「テクスチャの作成」を呼び出すのにかかる時間を推測することができます。もちろん、すべての数値はハードウェアとソフトウェアによって異なりますので、リバースエンジニアリングに多くの時間を費やす価値はないでしょう。ぜひ試してみてください。メトリックを選択します。「フレームごとのテクスチャの数」または「フレームごとのテクスチャの合計サイズ」のいずれかを選択し、割り当て(たとえば、フレームごとに4つのテクスチャ)を選択して、ストレステストを開始します。

病理学的なケースでは、両方の割り当てを同時に追跡する必要がある場合もあります(たとえば、フレームあたり4つのテクスチャまたはフレームあたり2 MBのテクスチャのいずれか低い方に制限)。しかし、ほとんどのテクスチャストリーミングへの本当のトリックを考え出すさますが、それは周りにそれらをコピーするのにかかるませんどのくらいの時間、あなたの限られたメモリに収まるようにしたいテクスチャ。

また、テクスチャを作成するための病理学的なケース(一度にたくさんの小さなテクスチャが必要な場合など)は、他の領域でも病理学的なケースになる傾向があります。テクスチャがコピーするのに正確に何マイクロ秒かかるかを心配する前に、簡単に機能する実装を取得する価値があります。(さらに、実際のパフォーマンスヒットは、「テクスチャの作成」呼び出しのCPU時間としてではなく、テクスチャを使用する最初のフレームのGPU時間として発生する場合があります。


それはかなり良い説明です。知らなかったことがたくさんありますが、それはとても理にかなっています。ストレステストの代わりに、実行時にテクスチャ作成のオーバーヘッドを測定し、穏やかに開始して、外れ値の余地を残すために利用可能な実行時間の80%まで絞り込みます。
パンダパジャマ2013

@PandaPajama私は少し懐疑的です。定常状態は「テクスチャがコピーされていない」ことと、非常に大きな変動があることを期待しています。そして、私が言ったように、ヒットの一部はテクスチャを使用する最初のレンダーフレームであると思われます。これは、パフォーマンスに影響を与えずに動的に測定することがはるかに困難です。
John Calsbeek、2013

また、非同期テクスチャ転送に関するNVIDIAのプレゼンテーションもあります。それが私が読んでいる限り、それが家に帰っているという重要なことは、アップロードした直後にテクスチャを使用すると失速することです。 developer.download.nvidia.com/GTC/PDF/GTC2012/PresentationPDF/...
ジョンCalsbeek

私はドライバー開発ジョッキーではありませんが、それは一般的ですか?このようにドライバーを実装することはあまり意味がありません。テクスチャの最初の使用は、タイムラインに沿って間隔を置くのではなく、(各レベルの最初のように)スパイクになる可能性が非常に高いためです。
パンダパジャマ2013

@PandaPajamaまた、アプリケーションが使用可能なVRAMよりも多くのテクスチャを作成し、テクスチャを作成してからそれらを使用しないこともよくあります。一般的なケースは、「テクスチャの束を作成し、すぐにそれらを使用するシーンを描画する」ことです。この場合、レイジーになると、実際に使用されているテクスチャがわかり、最初のフレームがとにかくヒッチするため、ドライバーに役立ちます。。しかし、私もドライバー開発者ではありません。それを一粒の塩で味わってください(そしてテストしてください!)。
John Calsbeek、2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.