blog

Vulkanマルチスレッドレンダリング

1.Vulkan ming パッケージの概要)。 専用パッケージは通常、ユーザーが内部実装を気にすることなく、目的のグラフィックを直接生成できるUIデザイン言語を提供します。PSやCADなどがその例で...

May 31, 2020 · 9 min. read
シェア

Overview of Vulkan

コンピュータグラフィックスソフトウェア

グラフィックス・ソフトウェアには、専門的なパッケージと汎用のプログラミング・パッケージという2つの大きなカテゴリーがあります。

専用のソフトウェアパッケージは通常、UIデザイン言語を提供しており、ユーザーは内部実装を気にすることなく、目的のグラフィックを直接生成することができます。そのようなソフトウェアの例としては、PS、CADなどがあります。

代わりに、汎用プログラミングパッケージは、C、C++、Javaなどの高水準言語でプログラミングできるグラフィックス関数のライブラリを提供します。グラフィックス関数ライブラリは、幾何学的要素、行列変換、およびハードウェアを間接的に操作するためのソフトウェアインタフェースを提供するその他の操作を提供するため、このグラフィックス関数のセットは、コンピュータグラフィックスアプリケーションのプログラミングインタフェースとしても知られています。

Vulkanのマルチスレッド設計思想

Vulkanは単なるグラフィックスAPIではなく、グラフィックスとコンピューティングのためのプログラミングインターフェースです。Vulkanをサポートするデバイスは、GPU、DSP、または固定機能ハードウェアです。

Vulkanの計算モデルは主に並列コンピューティングに基づいているため、マルチスレッドのサポートはVulkan設計のコアコンセプトの1つです。

相互排他同期やVulkan内の他の操作によって引き起こされるラグの問題を軽減するために、Vulkanはデフォルトで、任意のリソースへのアクセスのためのマルチスレッドの競合がないとみなし、リソースのすべての同期はアプリケーション開発者の責任であるとみなします。

このため、リソース管理とスレッド同期の取り組みは、Vulkanプログラムを書く際の最大の難関の1つになります。Vulkanのマルチスレッドを適切に動作させるには、多くの作業を行う必要があります。もちろん、トレードオフは、Vulkanが他のCG APIよりもはるかにクリーンなスレッドモデルとはるかに高いパフォーマンスを持っているということです。

Instances, Devices, and Queues

Vulkanのマルチスレッドを正式に調査する場合、インスタンス、デバイス、キューという3つの重要な基本概念を理解する必要があります。

インスタンスは、アプリケーションのサブシステムとして考えることができ、アプリケーションコンテキスト内の他のロジックからVulkanを論理的に分離します。インスタンスは、Vulkanのコンテキストとして考えることができ、すべての状態を追跡し、すべてのVulkan対応デバイスを論理的に統合します。

デバイスには、物理デバイスと論理デバイスという2つの概念があります。

物理デバイスは通常、一連のキューを提供する特定の機能を持つ1つ以上のVulkan対応ハードウェアデバイスを表します。グラフィックカード、アクセラレータ、DSPなどがVulkanの物理デバイスになります。

論理デバイスは物理デバイスをソフトウェアで抽象化したもので、ハードウェアリソースを確保するために使用されます。

キューは、Vulkanマルチスレッド実装の重要な要素の1つである「GPUスレッド」と考えることができ、アプリケーションからのリクエストに応答するために使用されます。

Vulkanの機能の階層図を以下に示します:

Queues and Command Buffer

Queues

QueueはGPUスレッドを表し、VulkanデバイスはQueueに提出された作業を実行します。物理デバイスには複数のQueueが存在する可能性があり、各QueueはQueueファミリーに含まれます。

キューファミリーは、同じ機能を持つキューの集まりで、同じレベルのパフォーマンスとシステムリソースへのアクセスを持ち、キュー間のデータ転送作業にはコストがかかりません。

物理デバイスには複数のキューファミリーが存在でき、キューファミリーが異なると特性も異なります。同じキューファミリーのキューは同じ機能を持ち、並列実行が可能です。

キューの能力によって、キューは次のように分けられます:

  • グラフィックス
    • このファミリーのキューは、点、線、三角形の描画などのグラフィック操作をサポートします。
  • コンピュート
    • このファミリーのキューは、コンピュータシェーダなどの計算処理をサポートします。
  • 転送
    • このファミリーのキューは、バッファやイメージコンテンツのコピーなどの転送操作をサポートします。
  • スパースバインディング
    • このファミリのキューは、スパースリソースを更新するためのメモリバインディング操作をサポートしています。

### 2.2 コマンド・バッファ

シングルスレッドにおける性能ボトルネック

従来のCG APIはシングルスレッドであり、パフォーマンスの向上はCPUの周波数を上げることでしか達成できません。利用可能な唯一の最適化オプションは、メインスレッドをレンダリングスレッドから分離するか、特定のリソースを非同期にロードするか、オフラインで処理することです。

しかし、従来のCG APIが引き起こすパフォーマンスボトルネックは、実用的なアプリケーションではまだしばしば遭遇します。

携帯電話端末は、例えば、CPUの主な周波数は、消費電力と温度制御の問題を考慮し、主要なチップメーカーは、マルチコアマルチスレッド開発を強化するために制限されており、CPUの周波数が高すぎることはできません、より多くのリアルタイムレンダリングの高いリフレッシュレートますます厳しい要件の速度。

Vulkanは、CPUのマルチコア・マルチスレッディングを最大限に活用するために、コマンドバッファの概念を導入しています。複数のスレッドが同時に共同作業でき、各CPUスレッドはレンダリングコマンドを自身のコマンドバッファに送信し、対応するQueueに一律に送信できるため、CPUの利用率が大幅に向上します。

Command Buffer

アプリケーションは描画するときに一連の描画コマンドを GPU ドライバに送信しますが、これらの描画コマンドはすぐには実行されず、単にコマンドバッファの最後に追加されます。

他のCG APIでは、ドライバがAPIコールをGPUコマンドに変換し、アプリケーションが意識することなくコマンドバッファに格納し、最終的にGPUに渡して処理させます。コマンドバッファの作成と破棄はドライバの責任です。

コマンドバッファプール:

Recording command

コマンド・バッファは、状態設定、描画操作、データ・コピーなど、多くのコマンドを記録することができます。

理論的には、1つのスレッドが複数のCommand BufferにCommandを記録したり、複数のスレッドが同じCommand Bufferを共有したりすることができますが、一般的に複数のスレッドがCommand Bufferを共有することは推奨されません。

Vulkanの重要な設計原則の1つは、効率的にマルチスレッド化することです。これを達成するために、アプリケーションはリソースの競合によって複数のスレッドが互いにブロックすることを意識する必要があります。したがって、1つのスレッドを共有するのではなく、スレッドごとに1つまたは1組の Command Buffer を持つことが最善です。アプリケーションは各スレッドに Command Buffer Pool を作成し、個々のワーカースレッドが競合することなく Command Buffer Pool から Command Buffer を割り当てることができます。

Submitting Command Buffers

模式図を使えば、提出プロセスがもう少しわかりやすくなります。

シングルスレッドコマンドバッファコミット処理

マルチスレッドコマンドバッファコミット処理

同期

ディスプレイ同期操作

Vulkanは同期をアプリケーションの手に委ねます。Vulkanコマンドの大部分は同期を全く提供せず、アプリケーション自身で処理する必要があります。Vulkanは、アプリケーションが同期操作を実行するのを助ける同期プリミティブをアプリケーションに提供します。

Vulkanには主に4つの同期プリミティブがあります:

  • フェンス
    • 最大粒度の同期プリミティブで、GPUまたは他のVulkanデバイスが提出したすべての作業をいつ完了したかをCPU側が知る方法を提供することを目的としています。
  • セマフォ
    • はフェンスよりも少し粒度が小さく、通常は異なるキュー間のデータ同期に使用されます。
  • イベント
    • コマンドバッファ間の同期に使用できます。
  • バリア
    • Vulkanパイプラインステージ内のメモリアクセス管理とリソース状態移動のための同期メカニズム

下のイメージは、NVIDIA Vulkan Multi-Threading プレゼンテーションPPTからの抜粋です:

隠された実行順序

Vulkanは明示的なAPIであり、「秘密のないAPI」と謳われています。しかし、マルチスレッドの同期に関しては、いくつかの暗黙のルールがあります。

例えば以下の図では、同じQueueの中で、Command Buffer1とCommand Buffer2のどちらが先に実行され、Command Bufferに記録されたコマンドの束はどのように実行されるのでしょうか?

Vulkanの実行順序には、同期プリミティブがないため、実際にはいくつかの裏技があります:

  • コマンド・バッファ内のコマンドが最初に実行されます。
  • 先に投入されたコマンドバッファが先に実行されます。

Barriers

バリアは、バッファやイメージへのアクセス範囲を明示的に制御し、ハザードを回避し、データの一貫性を確保するために使用されます。

障壁は、開発者がレンダリングパイプラインのさまざまな段階を理解し、パイプラインの各段階でリソースを読み書きする順序を明確に把握していることを必要とします。

パイプラインのステージは、Vulkanでは次のように定義されています:

  • 先に投入されたコマンド・バッファが先に実行されます!
  • TOP_OF_PIPE_BIT
  • DRAW_INDIRECT_BIT
  • VERTEX_INPUT_BIT
  • VERTEX_SHADER_BIT
  • TESSELLATION_CONTROL_SHADER_BIT
  • TESSELLATION_EVALUATION_SHADER_BIT
  • GEOMETRY_SHADER_BIT
  • FRAGMENT_SHADER_BIT
  • EARLY_FRAGMENT_TESTS_BIT
  • LATE_FRAGMENT_TESTS_BIT
  • 転送ビット
  • TRANSFER_BIT
  • COMPUTE_SHADER_BIT

通信:

P1とP2の2つのレンダリングパイプラインがあり、P1は頂点シェーダを通して頂点データをバッファに書き込み、P2はこのデータをコンピュートシェーダで使用する必要があるとします。

P1のCommandがコミットされた後、P2はP1のオペレーションがフェンスを介して完全に実行されたことを確認してから作業を開始します。

P2はさらに長い時間待ち、その間にP2の他のフェーズはP1のデータを使用せず、Ahを実行することができます。

Barriersの導入により、P1のVertex Shader内のデータをP2のCompute Shaderステージでのみ待機するようVulkanに指示するだけで、他のステージは気にせず同期できるため、この問題は完全に解決されます。

使い方:

Read next

セルフテスト問題のまとめ

1. ``` window.n = 'ウィンドウ名' let obj = { n: 'オブジェクト名', sayN } } let fn =

May 31, 2020 · 2 min read