/ / glslプログラミングアーキテクチャどの部分が「本当に」並列実行ですか? -android、画像処理、opengl-es、並列処理、glsl

glslプログラミングアーキテクチャのどの部分が「本当に」並列実行ですか? -アンドロイド、画像処理、opengl-es、並列処理、glsl

glslを使用してGPUでガウスフィルタリングやバイラテラルフィルタリングなどの画像処理アルゴリズムを実装しようとしています。

そして、私はどの部分が「本当に」並列実行。たとえば、テクスチャとして1280 * 720のプレビューがあります。どの部分が実際に1280 * 720回実行されているのか、どの部分が実行されていないのかはよくわかりません。

glslコードのディスパッチメカニズムとは何ですか?

私のガウスフィルタリングコードは次のようなものです。

#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vTextureCoord;
uniform samplerExternalOES sTexture;
uniform sampler2D sTextureMask;

void main() {

float r=texture2D(sTexture, vTextureCoord).r;
float g=texture2D(sTexture, vTextureCoord).g;
float b=texture2D(sTexture, vTextureCoord).b;

// a test sample
float test=1.0*0.5;

float width=1280.0;
float height=720.0;

vec4 sum;

//offsets of a 3*3 kernel
vec2 offset0=vec2(-1.0,-1.0); vec2 offset1=vec2(0.0,-1.0); vec2 offset2=vec2(1.0,-1.0);
vec2 offset3=vec2(-1.0,0.0); vec2 offset4=vec2(0.0,0.0); vec2 offset5=vec2(1.0,0.0);
vec2 offset6=vec2(-1.0,1.0); vec2 offset7=vec2(0.0,1.0); vec2 offset8=vec2(1.0,1.0);

//gaussina kernel with sigma==100.0;
float kernelValue0 = 0.999900; float kernelValue1 = 0.999950; float kernelValue2 = 0.999900;
float kernelValue3 = 0.999950; float kernelValue4 =1.000000; float kernelValue5 = 0.999950;
float kernelValue6 = 0.999900; float kernelValue7 = 0.999950; float kernelValue8 = 0.999900;

vec4 cTemp0;vec4 cTemp1;vec4 cTemp2;vec4 cTemp3;vec4 cTemp4;vec4 cTemp5;vec4 cTemp6;vec4 cTemp7;vec4 cTemp8;



//getting 3*3 pixel values around current pixel
vec2 src_coor_2;
src_coor_2=vec2(vTextureCoord[0]+offset0.x/width,vTextureCoord[1]+offset0.y/height);
cTemp0=texture2D(sTexture, src_coor_2);
src_coor_2=vec2(vTextureCoord[0]+offset1.x/width,vTextureCoord[1]+offset1.y/height);
cTemp1=texture2D(sTexture, src_coor_2);
src_coor_2=vec2(vTextureCoord[0]+offset2.x/width,vTextureCoord[1]+offset2.y/height);
cTemp2=texture2D(sTexture, src_coor_2);
src_coor_2=vec2(vTextureCoord[0]+offset3.x/width,vTextureCoord[1]+offset3.y/height);
cTemp3=texture2D(sTexture, src_coor_2);
src_coor_2=vec2(vTextureCoord[0]+offset4.x/width,vTextureCoord[1]+offset4.y/height);
cTemp4=texture2D(sTexture, src_coor_2);
src_coor_2=vec2(vTextureCoord[0]+offset5.x/width,vTextureCoord[1]+offset5.y/height);
cTemp5=texture2D(sTexture, src_coor_2);
src_coor_2=vec2(vTextureCoord[0]+offset6.x/width,vTextureCoord[1]+offset6.y/height);
cTemp6=texture2D(sTexture, src_coor_2);
src_coor_2=vec2(vTextureCoord[0]+offset7.x/width,vTextureCoord[1]+offset7.y/height);
cTemp7=texture2D(sTexture, src_coor_2);
src_coor_2=vec2(vTextureCoord[0]+offset8.x/width,vTextureCoord[1]+offset8.y/height);
cTemp8=texture2D(sTexture, src_coor_2);

//convolution
sum =kernelValue0*cTemp0+kernelValue1*cTemp1+kernelValue2*cTemp2+
kernelValue3*cTemp3+kernelValue4*cTemp4+kernelValue5*cTemp5+
kernelValue6*cTemp6+kernelValue7*cTemp7+kernelValue8*cTemp8;

float factor=kernelValue0+kernelValue1+kernelValue2+kernelValue3+kernelValue4+kernelValue5+kernelValue6+kernelValue7+kernelValue8;

gl_FragColor = sum/factor;
//gl_FragColor=texture2D(sTexture, vTextureCoord);

}

このコードは、私の電話(ギャラクシーネクサス)での純粋なプレビューに対して低いfpsで実行されています。

しかし、コードの最後の部分を元のピクセル値で直接出力するように変更すると、

    //gl_FragColor = sum/factor;
gl_FragColor=texture2D(sTexture, vTextureCoord);

純粋なプレビューと同じfpsで高速に実行されます。

質問は次のとおりです。私がテスト用に書いたもので、最初は役に立たないものは次のようになります。

float test=1.0*0.5;

何回実行されますか?

のような他の部分:

sum =kernelValue0*cTemp0+kernelValue1*cTemp1+kernelValue2*cTemp2+
kernelValue3*cTemp3+kernelValue4*cTemp4+kernelValue5*cTemp5+
kernelValue6*cTemp6+kernelValue7*cTemp7+kernelValue8*cTemp8;

変更しただけでは1280 * 720回実行されません

gl_FragColor = sum/factor;

gl_FragColor=texture2D(sTexture, vTextureCoord);

どちらを1280 * 720回実行するかを決定するメカニズムはどのようになっていますか?これは、ピクセル全体で並列に実行すると役に立たないだけです。それは自動的に行われますか?

glslプログラムのアーキテクチャ、ディスパッチ、GPUへのデータの編成方法などは何ですか?

この3 * 3ガウスカーネルよりも9 * 9のようなカーネルサイズとピクセルあたり9回のバイラテラルフィルタリングのようなより複雑な操作に対して何をすべきか疑問に思っています。

回答:

回答№1は2

フラグメントシェーダーコード全体は、すべてのフラグメントの全体。フラグメントは、出力ピクセルのアンチエイリアシングが行われていない場合、またはフレームバッファーのサンプルをマルチサンプルアンチエイリアシングする場合に近似します。フラグメントが正確に何であるかは、OpenGL仕様では詳細に指定されていません。ただし、フラグメントステージの出力は、フレームバッファビットプレーンで値に変換されます。

ラスタライザは一連のフレームバッファを生成しますアドレスと値 ポイント、ラインセグメント、またはポリゴンの2次元記述を使用します。各 そのように生成されたフラグメントは、操作を実行する次のステージに送られます。 最終的にフレームバッファを変更する前の個々のフラグメント。これらの操作には次のものが含まれます

[OpenGL-3.3コア仕様、セクション2.4]


変更しただけでは1280 * 720回実行されません

gl_FragColor = sum/factor;

gl_FragColor=texture2D(sTexture, vTextureCoord);?

除算はコストがかかり複雑な操作です。カーネルの合計は定数であり、フラグメントごとに変化しないため、シェーダーでそれを評価するべきではありません。 CPUで評価し、供給します 1./factor ユニフォーム(すべてのフラグメントに等しい定数)として、それを乗算します sum これは分割よりもはるかに高速です。

ガウスカーネルは実際には3×3行列であり、GLSLには専用の型があります。実行する計算は、次の点で書き直すことができます。 ドット積 (数学的に正しい用語はスカラーまたは内積になります)、GPUには専用の高速化された命令があります。

また、テクスチャのコンポーネントを個々のフロートに分割しないでください。

全体として、コードにかなりの数のスピードバンプを組み込みました。


回答№2の場合は1

現代(シェーダーモデル3)。0+)GPU、フラグメントシェーダーは、一度に2x2ブロックのピクセル(ピクセルクワッド)で動作するようにスケジュールされています。面白いことに、これはShader Model 3.0で微分命令を実装するために必要であり、それ以来、GPUアーキテクチャ設計の一部として残っています。ピクセルクワッドは、フラグメントシェーダーのスケジューリングで得られる最低レベルの粒度です。実際、もしあなたが discard フラグメントシェーダーでは、ピクセルクワッド内のすべてのフラグメントも discard、その後、ブロック内のフラグメントシェーダーのすべてのインスタンスが実行を継続し、要求した個々のフラグメントの結果が最後にスローされます。 discard.

これに加えて、ほとんどのGPUには複数のGPUがあります処理ユニットをストリームし、ピクセルクワッドをより大きなワークグループにスケジュールします(NVはそれらをワープと呼び、AMDはそれらを波面と呼びます)。一言で言えば、すべてが並行して行われています。つまり、GPUの前提全体です。つまり、GPUは、同じデータを並行して操作する複数のスレッドに単一のタスクを適用します。これが、CPUとは対照的にコアが増加したときにそれらが非常にうまくスケーリングする理由です。

個人を派遣するのではなく、簡単に言えばGLSLシェーダーで個別の機能ユニットで実行するように指示すると、実際に発生するのはこれです。 GLSLシェーダーは、複数の処理ユニットで同時に実行され(概念的には、フラグメントごとに1つのスレッド)、これらのスレッドはすべて、次のパラダイムで同じ一連の命令を実行します。 SIMT (単一命令複数スレッド)。

基本的なスケジューリングユニットに戻る(ワープ/ウェーブフロント)、シェーダーの1つのインスタンスがメモリのフェッチを停止した場合、それらはすべて同じ命令を同時に実行するため、そのスケジューリングユニット内の残りのインスタンスも停止します。これが、依存するテクスチャ読み取りと大きなフィルタカーネルが悪いモジョである理由です。フラグメントの特定のグループに必要なテクスチャメモリは、実行時まで不確定であるか、広がりすぎる可能性があるため、スケジューリングユニット内でテクスチャデータを効率的にプリフェッチおよびキャッシュすることは、不可能ではないにしても困難になる可能性があります。

正確に記述することの最大の問題並列処理のレベルは、GPUアーキテクチャが変化し続けることです(上記の説明のほとんどは、Shader Model 3.0以降のGPUに関連しています)。少し前まで、GPUはISAをベクトル化していましたが、実際に命令スケジューリングの効率が向上するため、AMDとNVの両方がスーパースカラーに切り替わりました。特殊な組み込みGPUを組み合わせて使用​​すると、実際に悪夢が発生します。どのシェーダーモデルを実行するかを実際に判断するのは困難です(OpenGL ES 2.0では派生物はオプションであるため)。


これを見てください その他の質問 私が今書いたことのより簡潔な声明については、StackOverflowで。

いくつかのきれいな図については、これはやや時代遅れですが、それでも便利です nVIDIAからのプレゼンテーション.