2017-06-05 106 views
0

到目前爲止,我的OpenGL ES程序中存在性能瓶頸。我認爲它會運作良好 - 使用VBO,textureatlas,每次平局等少數綁定。但是,當同時使用許多精靈時,性能會下降很多。我發現瓶頸是CPU限制的(有點驚訝)。更確切地說 - 瓶頸可能是一種計算每個矩形四個頂點(x1,y1,x2,y2,x3,y3,x4,y4)的屏幕位置的方法。這用於碰撞檢測。我在這個方法中做的是重複在着色器中做的事情,我認爲許多CPU週期是由MV乘法引起的。OpenGL ES:從着色器獲取轉換的頂點

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

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 - 感謝您的評論。其實我有點解決它 - 而不是使用庫函數,我做了手動矩陣乘法的頂點和volia - 幀率增加,問題似乎解決 - 至少部分。如果您注意較慢的設備,我仍然會使用不超過55-60個精靈。我的Galaxy s7可以管理超過100個精靈,但不是星系標籤 – java

+0

除了已經提供的答案,[*總是*以毫秒爲單位衡量性能而不是fps](https://www.mvps.org/directx/articles/fps_versus_frame_time熱媒)。 –

回答

1

要回答主要問題,我不認爲有可能從GPU中獲取轉換的vert。


第一遍優化循環。首先,當他們總是產生相同的結果時,不要在循環內反覆地做這些事情。在循環之外進行。特別是功能或財產的呼叫。

接下來,您可以將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