2017-06-05 18 views
0

私はOpenGL ESプログラムでパフォーマンスのボトルネックが現れています。私はそれがうまくいくと思いました - VBO、textureatlas、すべてのdraw-callのためのバインディングをほとんど使わないなど。しかし、多くのスプライトを同時に使用すると、パフォーマンスが大きく低下します。私はボトルネックがCPUに縛られていることに気づいた(ちょっと驚いた)。より正確には、ボトルネックは、各矩形の4つの頂点(x1、y1、x2、y2、x3、y3、x4、y4)のスクリーンポジションを計算するメソッドに浪費される可能性があります。これは、コリジョン検出に使用されます。私がこのメソッドで行うことは、シェーダで何が行われたかを繰り返すことであり、MV乗算によって多くのCPUサイクルが発生すると私は考えています。OpenGL ES:シェーダから変換された頂点を取得

rhsVecは、上記のように頂点を格納する浮動小数点配列です。

これはボトルネックと思われるので、たとえばクリップ座標が計算されるときにシェーダの同じベクトルにどのようにアクセスできるのでしょうか?パイプラインの下のシェーダーによって生成された座標をクリップ座標またはそれ以上に優れたものにします。

onSurfaceCreated

 final int vertexShaderHandle = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader); 
    final int fragmentShaderHandle = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader); 

    mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, 
      new String[] {"a_Position", "a_Color", "a_Normal", "a_TexCoordinate"}); 

    textureHandle = TextureHelper.loadTexture(context); 

    GLES20.glUseProgram(mProgramHandle); 

    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix"); 
    mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix"); 
    //mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color"); 
    mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate"); 

    mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); 

頂点変換(ボトルネック)を行う方法の頂点シェーダ

uniform mat4 u_MVPMatrix;       
uniform mat4 u_MVMatrix; 
varying vec2 v_TexCoordinate;   
attribute vec4 position; 

void main()              
{ 

    v_TexCoordinate = a_TexCoordinate  

    gl_Position = u_MVPMatrix * a_Position; 

}           

スニペット

private void calcPos(int index) { 

    int k = 0; 
    for (int i = 0; i < 18; i += 3) { 

     rhsVec[0] = vertices[0 + i]; 
     rhsVec[1] = vertices[1 + i]; 
     rhsVec[2] = vertices[2 + i]; 
     rhsVec[3] = 1; 

     // *** Step 1 : Getting to eye coordinates *** 

     Matrix.multiplyMV(resultVec, 0, mModelMatrix, 0, rhsVec, 0); 

     // *** Step 2 : Getting to clip coordinates *** 

     float[] rhsVec2 = resultVec; 

     Matrix.multiplyMV(resultVec2, 0, mProjectionMatrix, 0, rhsVec2, 0); 


     // *** Step 3 : Getting to normalized device coordinates *** 

     float inv_w = 1/resultVec2[3]; 

     for (int j = 0; j < resultVec2.length - 1; j++) { 

      resultVec2[j] = inv_w * resultVec2[j]; 
     } 

     float xPos = (resultVec2[0] * 0.5f + 0.5f) * game_width; 

     float yPos = (resultVec2[1] * 0.5f + 0.5f) * game_height; 

     float zPos = (1 + resultVec2[2]) * 0.5f; 

     SpriteData sD = spriteDataArrayList.get(index); 

     switch (k) { 

      case 0: 
       sD.xPos[0] = xPos; 
       sD.yPos[0] = yPos; 
       break; 

      case 1: 
       sD.xPos[2] = xPos; 
       sD.yPos[2] = yPos; 
       break; 

      case 2: 
       sD.xPos[3] = xPos; 
       sD.yPos[3] = yPos; 
       break; 

      case 3: 
       sD.xPos[1] = xPos; 
       sD.yPos[1] = yPos; 
       break; 
     } 
     k++; 

     if (i == 3) { 
      i += 9; 
     } 

    } 

この方法は、各スプライトのために呼び出され100回のスプライトで100回繰り返されます。おそらく、MV乗算は性能に匹敵するでしょうか?

+1

この操作は、あなたのケースではあまりにも遅くはありませんでした。これは400の行列とベクトルの乗算ですか?可能であれば、スプライトの衝突にGPUを使うべきではありませんが、GPUからCPUにデータを転送する必要があります。この乗算が本当に遅すぎる場合、それは盗聴される必要がありますので、独自のものを作成する必要があります。 –

+0

@MaticOblak - ご意見ありがとうございます。実際には私はそれを解決しました。ライブラリ関数を使う代わりに、頂点とボリュームを手動で行列乗算しました。フレームレートが上がり、問題は少なくとも部分的に解決されたようです。私はまだ遅いデバイスに留意して、55-60スプライト以下のスプライトを使用しています。私の銀河s7は100以上のスプライトを管理できますが、銀河のタブは扱えません – java

+0

既に提供されている回答に加えて、[* always * ms単位の測定性能はfpsではありません](https://www.mvps.org/directx/articles/fps_versus_frame_time .htm)。 –

答えて

1

主な質問に答えるために、私はGPUから変換されたバーツをつかむことはできないと思います。


ループを最適化する最初のパス。最初に、ループ内で何度も何度も何度も同じ結果を出さないでください。それをループの外側で行います。特に、関数呼び出しまたはプロパティ呼び出し。

次に、変換が1つの行列乗算で順に適用されるように2つの行列を掛け合わせることができます。最終的な結果をスクリーンスペースに戻して変換していないようですが。

データをコピーしてから、そのデータを変更せずに使用しています。私は行列の乗算がおそらく4つの浮動小数点またはVec4を期待していることを知っていますが、コピーを避けてwパラメータを埋め込む行列乗算を書くことができます。

最終的には使用しない計算は避けてください。

結果をキャッシュし、変更されない限り再計算しません。

private void calcPos(int index) { 

// get only once, not every loop 
SpriteData sD = spriteDataArrayList.get(index); 

int[] vIndices = {0, 1, 2, 5}; // the 4 verts you want 

// multiply once outside the loop, use result inside loop 
Matrix mvpMatrix = mModelMatrix * mProjectionMatrix; // check order 

for (int i = 0; i < 4; ++i) { // only grab verts you want, no need for fancy skips 

    int nVert = 3 * vIndices[i]; // 3 floats per vert 

    // should avoid copying data when you aren't going to change the copy 
    rhsVec[0] = vertices[0 + nVert]; 
    rhsVec[1] = vertices[1 + nVert]; 
    rhsVec[2] = vertices[2 + nVert]; 

    rhsVec[3] = 1; // need to write multiplyMV3 that takes pointer to 3 floats 
        // and fills in the w param, then no need to copy 

    // E.g. : 
    // Matrix.multiplyMV3(resultVec2, 0, mvpMatrix, 0, &vertices[nVert], 0); 

    // do both matrix multiplcations at same time 
    Matrix.multiplyMV(resultVec2, 0, mvpMatrix, 0, rhsVec, 0); 

    // *** Step 3 : Getting to normalized device coordinates *** 
    float inv_w = 1/resultVec2[3]; 

    for (int j = 0; j < 2; ++j) // just what we need 
     resultVec2[j] *= inv_w; 

    // Curious... Transform into projection space, just to transform 
    // back into screen space. Perhaps you are transforming too far? 
    float xPos = (resultVec2[0] * 0.5f + 0.5f) * game_width; 
    float yPos = (resultVec2[1] * 0.5f + 0.5f) * game_height; 
    // float zPos = (1 + resultVec2[2]) * 0.5f; // not used 

    switch (i) { 

     case 0: 
      sD.xPos[0] = xPos; 
      sD.yPos[0] = yPos; 
      break; 

     case 1: 
      sD.xPos[2] = xPos; 
      sD.yPos[2] = yPos; 
      break; 

     case 2: 
      sD.xPos[3] = xPos; 
      sD.yPos[3] = yPos; 
      break; 

     case 3: 
      sD.xPos[1] = xPos; 
      sD.yPos[1] = yPos; 
      break; 
    } 
} 
+0

あなたの答えをありがとう - これ以上見てみましょう... – java

関連する問題