75053


私はグラフィックプログラミングをしたことがないのですが、GPUに関する記事を読んでいると、ピクセルの描画をするのに複数の三角形の前後を判定するために深度バッファ法を用いると書いてありました。

それについて調べてみると、深度バッファ法というのは深度バッファを見て自らの深度値と比較し更新することで、上書きするかどうか決めるアルゴリズムのようでしたが、これは複数のピクセル処理が同時に同じ深度バッファに読み書きする可能性があるということですから、アトミック操作で更新する必要があるように見えます。しかしこれでは、三角形がたくさん重なってる場面では並列性が損なわれるように思います。更に、同じ三角形について近くのZバッファを読み書きするわけですから、何度も同じキャッシュラインが無効化されそうにも見えます。（GPUのアーキテクチャをよくわかっていないので、これに関してはかなり意味不明なことを言っているかもしれません）

  1. そもそも、GPUはピクセルごとに並列ななんらかの計算していますか？
  2. これは現代のGPUでも用いられている手法ですか？
  3. 三角形がたくさん重なっているときに並列性が損なわれてしまうことに対するなんらかの解決策がありますか？
  4. あるいは、そのCAS程度ではほとんど並列性が損なわれないという見方がありますか？

私はグラフィックについて全く知識がないため、変なことに疑問を持っていたらすみません。


CC BY-SA 4.0
2021-02-16T21:04:24.257
74061
2021-04-02T21:34:35.837
2021-02-16T21:21:16.617
37013
37013
question
gpu directx
深度バッファ法は並列性が損なわれませんか？


レイトレーシングでない従来のラスタライズ法による3Dグラフィックスエンジンでは、ほぼ間違いなく何らかの形で深度バッファを使っています。

ラスタライズベースのDirect3Dレンダリングパイプラインでは、ポリゴン（三角形リストやラインリストなど）の描画は、簡略化すると

  1. インプットアセンブラーによるメモリからの頂点情報の読み出し
  2. 頂点シェーダーによる頂点単位処理（座標の同次変換など）
  3. ラスタライザーによるラスタライズ（ピクセル化）
  4. ピクセルシェーダーによるピクセル単位処理（陰影計算など）
  5. アウトプットマージャーによる統合処理

という流れになるのですが、アウトプットマージャーのステージで深度バッファを使ったZテストを実行します。  
詳しくは以下（Direct3D
10のレンダリングパイプライン）を参照してください。ジオメトリシェーダーはオプションなので上記では省略しています。Direct3D
11以降ではテッセレーション用のシェーダーステージが追加されていますが、これもオプションなので基本は同じです。OpenGLやVulkanも用語が違うだけで、やっていることはほぼ同じです。

  * [Rendering Pipeline - Windows drivers | Microsoft Docs](https://docs.microsoft.com/en-us/windows-hardware/drivers/display/rendering-pipeline)

頂点シェーダーによる頂点単位の処理に関しては、複数の三角形やラインのプリミティブ情報が入力されても個々の頂点は完全に独立しているので並列処理されますが、ピクセル単位の処理に関しては、複数の異なるプリミティブを個々に並列処理しているわけではないです。とはいえ、もちろん単純に1つ1つのプリミティブを順次処理しているわけではなく、出力先の画面（レンダーターゲット）を複数の小さなタイル（例えば8ピクセル×8ピクセルなど）に分割して、各タイルに1つずつストリームプロセッサーの集合（クラスター）を割り当て、1つのプリミティブに対する処理を分割して並列実行します。各クラスター内でキャッシュは共有されており、またクラスター内のプロセッサー群により駆動されるハードウェアスレッド群はすべて連動して同じ命令（同じシェーダープログラム内の同じステップ行）を実行するため、1つのタイル内の処理は並列かつ同時に実行されます
(Single Instruction, Multiple Threads;
SIMT)。個々のクラスターは独立して動作し、タイルごとの処理も失速しないように並列にパイプライン実行されます。

なお、GPUの中には、視点から見てオーバーラップすることがない三角形が同じタイル内に複数ある場合、同時に描画するものもあるそうです。

  * [opengl - Parallelism in GPU's rasterization process - Game Development Stack Exchange](https://gamedev.stackexchange.com/questions/141085/parallelism-in-gpus-rasterization-process)

Zテストはパイプラインの最後段で実行されますが、最終的に描画されない三角形に対するピクセルシェーダーの実行は無駄なので、早期Zカリングという機能を使って描画を早めに打ち切ってしまうこともあります。

  * <https://pc.watch.impress.co.jp/docs/2003/0408/kaigai02.htm>
  * <https://news.mynavi.jp/article/20100525-s_gpu/6>

PowerVRのようなモバイルデバイス用GPUだと、タイルベースの遅延レンダリング (Tile-Based Deferred Rendering;
TBDR) という、Zテストを早期に実行することで描画の無駄をより小さくするアーキテクチャ設計になっていて、深度バッファの実装形態も異なります。  
最近ではモバイルだけでなく、NVIDIA/AMD/IntelのようなPC用GPUでもTBDRの設計を取り入れているそうです。

  * <https://game.watch.impress.co.jp/docs/series/3dcg/586728.html>
  * <https://www.4gamer.net/games/251/G025177/20170309081/>
  * <https://pc.watch.impress.co.jp/docs/column/kaigai/1047092.html>

GPUの詳しい設計や並列処理の実装形態に関しては英語の情報を探したほうが良いと思います。全般的に日本語の情報は少なく、嘘が混じっていることも多々あります。  
（上記の説明にも誤りが含まれているかもしれないので、話半分にとらえてください）


CC BY-SA 4.0
2021-02-22T05:38:56.947
74195
2021-02-22T05:38:56.947
15413
74061
answer


# Vulkanの仕様から

Vulkan の仕様書におけるラスタライゼーションに関する規定を参照すると:  
(以下、強調は私)

> ## [25.2. Rasterization
> Order](https://www.khronos.org/registry/vulkan/specs/1.2/html/chap25.html#primrast-
> order)
>
> Within a subpass of a render pass instance, **for a given (x,y,layer,sample)
> sample location** , the following operations are guaranteed to execute in
> _rasterization order_ , **for each separate primitive that includes that
> sample location** :
>
>   1. Fragment operations, in the order defined
>
>   2. Blending, logic operations, and color writes
>
>

>
> Each operation is atomically executed for each primitive and sample
> location.  
>  Execution of these operations **for each primitive** in a subpass occurs in
> **primitive order**.

つまり、レンダーターゲットのフラグメント(i.e ピクセル)中のサンプル位置(マルチサンプリングも考慮した文言)について、  
そのサンプル位置を含むプリミティブ(ここでは三角形としていいでしょう)毎に、以下で定義される`fragment operations`が実行されます:

> ## [26\. Fragment
> Operations](https://www.khronos.org/registry/vulkan/specs/1.2/html/chap26.html)
>
> (前略)  
>  7\. Depth test  
>  (後略)

`depth test`とは[26.7. Depth
Test](https://www.khronos.org/registry/vulkan/specs/1.2/html/chap26.html#fragops-
depth)で定義されているように、デプスへの書き込みも含みます.

そしてあるサンプル位置の`fragment operations`は上記引用の[25.2. Rasterization
Order](https://www.khronos.org/registry/vulkan/specs/1.2/html/chap25.html#primrast-
order)の規定から`primitive order`で実行されます:

> ## [20.2. Primitive
> Order](https://www.khronos.org/registry/vulkan/specs/1.2/html/chap20.html#drawing-
> primitive-order)
>
>   1. **Submission order** determines the initial ordering
>   2. (省略)
>   3. (省略)
>   4. The order in which primitives are specified by a draw command:
>

>   * For non-indexed draws, **from vertices with a lower numbered vertexIndex
> to a higher numbered vertexIndex**.
>   * For indexed draws, **vertices sourced from a lower index buffer
> addresses to higher addresses**.
>

>
> (中略)  
>  Primitive order is later used to define rasterization order, which
> **determines the order in which fragments output results** to a framebuffer.

要するに描画コマンド順、そしてその中ではプリミティブの定義順になります.

# 結論

つまり、深度バッファの同じ位置について、そこに同時に値が書き込まれることはなく、  
その位置に相当するプリミティブ達を描画コマンドで定義された順番で処理していく、  
と言うことがVulkanの仕様書の表記から言えるかと思います.

ラスタライゼーションを使ったグラフィクスパイプラインにおいて大量の実行ユニットにより「並列で処理される」のは個々のフラグメントであり、  
1つのフラグメントを複数の実行ユニットが処理する、というわけではない、ということかと思います.


CC BY-SA 4.0
2021-04-02T21:34:35.837
75053
2021-04-02T21:34:35.837
7981
74061
answer