2017-06-05 2 views
0

J'ai actuellement un problème de performance dans mon programme OpenGL ES. Je pensais que cela fonctionnerait bien - en utilisant VBO, textureatlas, peu de liaisons pour chaque tirage au sort et ainsi de suite. Mais lorsque vous utilisez plusieurs sprites en même temps, la performance chute beaucoup. J'ai trouvé que le goulot d'étranglement est lié au processeur (un peu surpris). Plus précisément - le goulot d'étranglement pourrait être dervied à une méthode qui calcule l'écran pour chaque rectangles quatre vertitudes - x1, y1, x2, y2, x3, y3, x4, y4. Ceci est utilisé pour la détection de collision. Ce que je fais dans cette méthode est de répéter ce qui est fait dans les shaders et je pense que de nombreux cpu-cycles sont causés par les multiplications MV.OpenGL ES: Récupère les sommets transformés à partir du shader

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

le rhsVec est un tableau flottant qui stocke les sommets comme décrit ci-dessus.

Puisque cela semble être le goulot d'étranglement, je me demande comment j'ai pu accéder au même vecteur dans le shader lorsque, par exemple, les coordonnées de clip sont calculées? Clip-coordonne ou même mieux les coordonnées produites par il shaders plus bas dans la ligne de pipe.

le sommet shader

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; 

}           

extrait de 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"); 

la méthode qui permet la transformation de sommet (goulot d'étranglement)

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; 
     } 

    } 

Cette méthode est appelée pour chaque image-objet - donc pour 100 sprites, il a répété 100 fois. Probablement les multiplications MV atteint la performance?

+1

Cette opération ne peut probablement pas être trop lente dans votre cas. C'est 400 multiplications matricielles et vectorielles? Bien que possible, vous ne devez pas utiliser de GPU pour les collisions de sprites, vous devrez transférer les données de GPU à CPU, ce qui devrait dans votre cas être beaucoup plus lent. Si cette multiplication est vraiment trop lente, elle doit être buggée alors créez plutôt la vôtre. –

+0

@MaticOblak - merci pour votre commentaire. En fait, je l'ai en quelque sorte résolu - au lieu d'utiliser une fonction de bibliothèque j'ai fait une multiplication manuelle de la matrice avec les vertices et volia - le framerate a augmenté et le problème semble être résolu - au moins partiellement.Thouh j'ai encore utilisé pas plus de 55 - 60 sprites dans l'attention des appareils plus lents. Mon Galaxy S7 peut gérer plus de 100 sprites mais pas l'onglet galaxie – java

+0

En plus de la réponse déjà fournie, [* toujours * mesurer la performance en ms pas fps] (https://www.mvps.org/directx/articles/fps_versus_frame_time .htm). –

Répondre

1

Pour répondre à la question principale, je ne pense pas qu'il soit possible d'attraper les verts transformés à partir du GPU.


Premier passage lors de l'optimisation de la boucle. Tout d'abord, ne faites pas les choses encore et encore dans la boucle quand elles produisent toujours le même résultat. Faites-le en dehors de la boucle. En particulier les appels de fonction ou de propriété.

Ensuite, vous pouvez multiplier 2 matrices de telle sorte que leurs transformations soient appliquées dans l'ordre avec une seule multiplication matricielle. Bien qu'il semble que vous ne retransformiez pas le résultat final dans l'espace écran.

Vous copiez des données, puis vous utilisez ces données sans les modifier. Je sais que la multiplication matricielle attend probablement 4 flotteurs ou un Vec4, mais vous pouvez écrire une multiplication matricielle qui évite la copie et remplit le paramètre w.

Évitez les calculs que vous n'utilisez pas en fin de compte.

Les résultats du cache ne sont recalculés que s'ils sont modifiés.

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

merci pour votre réponse - se penchera sur ce plus ... – java