Help us understand the problem. What is going on with this article?

Compute Shader Compact解釈

More than 1 year has passed since last update.

皆さんこんにちは!先回は簡単なGPU Driven Cullingの原理を説明しましたが、ComputeShaderの部分は難しいですから、説明しない。今回は先回解釈しないCompute Shader部分を説明します。

Compute Shader

先ずComputeShaderというのもを説明します。ComputeShaderは通用計算のシェーダーです。テクスチャだけではなく、普通なバッファの訪問もできます。特別な関数もあります。

GPUには、たくさんコロールがあります。それから、パラレルでComputeShaderを実行することができます。ComputeShaderは対応のスレッドに実行するが、全て1回実行するわけではないです。GPUはスレッドをグループに入れって、たくさんグループをパラレル実行します。しかし、全てグループを一回だけに実行するもわけではない。それは注意点です。スレッドの詳しい実行方法はこちらです。

Compact

先回の問題また説明します。

image.png

今回の問題点はどのようなComputeShaderを使用してコンパクトします。説明は簡単になるために、会社の役割分担プランを使用して説明します。

image.png

まず皆さんは自分の対応の仕事をしました。その後はコンパクトします。つまり、皆さんが確認した書類をまとめます。

リーダー

簡単な方法は、リーダーさんはその後、一々でまとめます。他の社員さんは何もしない、休みます。
image.png

リーダーさんは「重い仕事ですね!」と思います。優しい先輩ですが、遅いです。そして、書類数量は多い場合は、あまり遅いです。

少し考えました、問題点は書類の転送です。皆さん、助けてください!!!

ボス

最近、先輩はボスになります。それから、働く方法も変えます。書類の番号を付けていれば、社員さんは自らによる書類を転送するもできます!
image.png

今回ボスの仕事は「計数」だけです。それは「ビッグボス」です。

 平等主義

最近は「社内平等」と考える社員も多いです。GPUのスレッドも平等です。ボスような人物はない。それから、ボススレッドは計数の仕事をしているでも、普通な社員の仕事も担当します。

じゃ、少し整理しましょう!

image.png

1. 皆さんは書類をチェックして、結果は自分に持っています。
2. スレッド0は、ボスになります。他のスレッドは待ちます。
3. ボスさんは、書類を統計して、番号を決まります。
4. 皆さんは、自分の番号をもらって、書類を転送します。

 ??主義

でも、今のGPUは、「Interlock」オペレーションをサポートしました。ボスじゃない場合も、役割はできます。

以前の場合は、たくさんスレッドは同じ場所のデータを変わりますと、間違ったデータを見ました。それはポピュリズムです。

でも「Interlock」では、実行のスレッドはいつだけです。データを変わって、そのデータの改変前の内容ももらいました。

それでは、皆さん同じ場所に「InterlockedAdd」して、番号も決まります。
image.png

 グループ

でも、実はGPUの全てスレッドは同じ場所のデータを改変することが少し無理です。
同じグループのスレッドはできます。少し残念ですね。この場合は、本社と分部の関係ようなものです。

それじゃ、グループのリーダーを選びます。リーダーさんは本社に会議を参加します。グループの中には、「Interlock」を使用して、自らによる番号を決めます。その後、リーダーさんは自分のグループの最大番号を持って本社の会議を参加します。本社は、Globalのオフセットを計算します。リーダーさんは本社の結果を持って、自分のグループを説明します。その後、スレッドはグローボとグループの番号に基づいて最後の番号を計算します。

image.png

 メモリバリア

でも、スレッドはリーダーを会社へ帰ることを待っていない、勝手に自分の番号を計算してデータを転送すれば、結果は間違いです。それからメモリバリアを使用します。

メモリバリアは、全てスレッドがここまで実行完成じゃないでは、この場所に待っています。メモリバリアを付けていれば、この感じです

image.png

それは最後のバージョンです。対応のコードはこれです

    GroupMemoryBarrierWithGroupSync();



    if (Invalid == false && NeedCulling)
    {
        if (cullingData.Radius < 0.0f)
        {
            Inside = true;
        }
        else
        {
            Inside = FrustumCullingSphere(InputInstance, cullingData.Radius);
        }
    }
    if (Invalid == false)
    {
        if (Inside || NeedCulling == false)
        {
            InterlockedAdd(localValidInstances, 1, localSlot);
        }

    }
    GroupMemoryBarrierWithGroupSync();

    if (threadId == 0 && Invalid == false)
    {
        InterlockedAdd(
                 outCommand[DrawArgsCount * 5 + 1],
                 localValidInstances,
                 globalSlot
        );
    }

    GroupMemoryBarrierWithGroupSync();
    //if (index == 0)
    //{
    //    outCommand[DrawArgsCount * 5 + 1] = g_CommandCount;
    //}
    if (Invalid)
    {
        return;
    }
    if (NeedCulling == false || Inside)
    {
        uint target_pos = args.StartInstanceLocation + globalSlot + localSlot;
        outInstances[target_pos * 4] = srcInstances[RealInstanceIndex * 4];
        outInstances[target_pos * 4 + 1] = srcInstances[RealInstanceIndex * 4 + 1];
        outInstances[target_pos * 4 + 2] = srcInstances[RealInstanceIndex * 4 + 2];
        outInstances[target_pos * 4 + 3] = srcInstances[RealInstanceIndex * 4 + 3];
    }

コードは少ないでも、原理は難しいそうです。じゃ、今回はそれそれです。

みんなさん、本当にありがとうございます!!!

参考資料

Life of a triangle - NVIDIA's logical pipeline
https://www.irasutoya.com/

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away