2013-07-24 11 views
9

Ho riscontrato un problema di prestazioni piuttosto strano. Finora ho ridotto il problema a questo: sto rendendo i cubi 20x20x20 in una griglia, usando glDrawElementsInstanced, che funziona bene finché la mia macchina fotografica è lontana dall'origine, tuttavia quando si avvicina all'origine, inizia fermandosi.Curioso rallentamento in opengl quando si utilizza il rendering istanza

sto definendo il mio modello matrice di proiezione vista attraverso:

float distance=3.8; 
Projection = glm::perspective(65.0f, (float)(width)/height, 0.1f, 300.0f); 
View = glm::lookAt( glm::vec3(0,0,-distance), 
         glm::vec3(0,0,10), 
         glm::vec3(0,1,0)); 
Model = glm::rotate(glm::mat4(1.0f), 0.0f, glm::vec3(0.25f, 1.0f,0.75f)); 

Con la distanza a 40, non ci sono problemi, ma quando la distanza si riduce a circa 3,8 e inferiore, tutto si blocca.

La chiamata effettiva al rendering viene effettuato attraverso:

glBindVertexArray(cubeVAO); 
glDrawElementsInstanced(GL_TRIANGLES, indices.size(),GL_UNSIGNED_INT,(GLvoid*)(0),latticePoints.size()); 

Mentre mettendo tutti i vertici in un unico buffer e il rendering chiamando:

glBindVertexArray(nonInstancedVAO); 
glDrawArrays(GL_TRIANGLES, 0,vertices.size()); 

rimuove completamente il comportamento. Chiunque abbia avuto un comportamento simile che possa indicarmi la direzione di una soluzione? In caso contrario, chiunque abbia un'idea di come rintracciare qualcosa di simile? Speravo di poter determinare cosa stava causando il rallentamento con gDEBugger, ma questo semplicemente riconferma che non ci sono altre chiamate opengl, e in realtà non aiuta a capire cosa sta occupando tutto il tempo di elaborazione.

Un'altra nota è che glDrawArraysInstanced mostra anche lo stesso rallentamento e che suddividendo la chiamata in 4 chiamate separate con un quarto della geometria cessa anche il rallentamento.

Aggiornamento

Ecco un tentativo di illustrazione minima del problema.

//Minimal reproduction of problem 

#include <stdio.h> 
#include <string> 
#include <fstream> 
#include <stdlib.h> 
#include <string.h> 

#include <GL/glew.h> 
#include <GLFW/glfw3.h> 

// Include GLM 
#include <glm/glm.hpp> 
#include <glm/gtc/matrix_transform.hpp> 
#include <glm/gtc/type_ptr.hpp> 

#include <vector> 
#include <iostream> 
#include <stdio.h> 

//Set to true to use instanced rendering (glDrawElementsInstanced), false to render a generated grid instead (glDrawElements) 
    #define Instanced true 

//Translation from origin. Problme is pressent at 0 distance, but disapears at ex. 40. 
    const float distanceFromOrigin=0; 

// Function to load shaders 
GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path); 


    int main(){ 

    int  width, height; 
    bool running = true; 

    // Initialise GLFW 
    glfwInit(); 

    glfwWindowHint(GLFW_SAMPLES,1); 
    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT,GL_TRUE); 
    glfwWindowHint(GLFW_VERSION_MAJOR, 4); 

    GLFWwindow* windowRef = glfwCreateWindow(512, 512, "",0,0); 
    glfwMakeContextCurrent(windowRef); 

    glewInit(); 

    //Load Shader 
    GLuint programID = LoadShaders("Simple.vs.c", "Simple.fs.c"); 
    GLuint MatrixID = glGetUniformLocation(programID, "MVP"); 
    glUseProgram(programID); 

    glm::mat4 Model,Projection,MVP,View,checkMVP; 

    std::vector<GLuint> sqIndice = {3,2,1,1,0,3,4,5,6,6,7,4,0,4,7,7,3,0,0,1,5,5,4,0,2,3,7,7,6,2,6,5,1,1,2,6,0,4,7,7,3,0}; 
    std::vector<GLfloat> sqVertex = {-1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1}; 
    std::vector<GLfloat> sqColor = {0.2472,0.24,0.6,0.6,0.24,0.442893,0.6,0.547014,0.24,0.24,0.6,0.33692,0.24,0.353173,0.6,0.6,0.24,0.563266,0.6,0.426641,0.24,0.263452,0.6,0.24}; 

    const float lattice = 5; 
    const int mxn = 10; 
    std::vector<GLfloat> v1 = {lattice,-1,0}; 
    std::vector<GLfloat> v2 = {1,lattice,0}; 
    std::vector<GLfloat> v3 = {0,0,lattice}; 
    std::vector<GLfloat> offset = {0,0,-distanceFromOrigin}; 

    std::vector<GLfloat> latticePoints,sqVertexGrid,sqColorGrid;// = {0,0,0}; 
    std::vector<GLuint> sqIndiceGrid; 
// Looping stuff to generate the full grid of "instances" to render in a single call. 
    int instanceCount=0; 
//Generate Lattice vectors, aswell as a vector containing the full grids of indices,vertexes and colors 
    for(int x=-mxn;x<mxn;++x){ 
     for(int y=-mxn;y<mxn;++y){ 
      for(int z=-mxn;z<mxn;++z){ 
       for(int n=0;n<3;++n){ 
        latticePoints.push_back(x*v1[n]+y*v2[n]+z*v3[n]+offset[n]); 
       }; 
       for(int elm=0;elm<sqVertex.size();elm+=3){ 
        for(int n=0;n<3;++n){ 
         sqVertexGrid.push_back(sqVertex[elm+n]+x*v1[n]+y*v2[n]+z*v3[n]+offset[n]); 
         sqColorGrid.push_back(sqColor[elm+n]); 
        }; 
       }; 
       for(int elm=0;elm<sqIndice.size();++elm){ 
        sqIndiceGrid.push_back(sqIndice[elm]+instanceCount*sqVertex.size()/3); 
       }; 
       ++instanceCount;glewInit 

      }; 
     }; 
    }; 

#if Instanced==true 
//Initialize and fill vertex,color and indice buffers with the relevant data. 
GLuint cubeVAO; 
    glGenVertexArrays(1, &cubeVAO); 
    glBindVertexArray(cubeVAO); 
    glEnable(GL_DEPTH_TEST); 

//Vertex buffer 
    GLuint vertexBuffer; 
    glGenBuffers(1, &vertexBuffer); 
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); 
    glBufferData(GL_ARRAY_BUFFER, sqVertex.size()*sizeof(GLfloat), &sqVertex[0], GL_STATIC_DRAW); 
    glEnableVertexAttribArray(0); 
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,(void*)0); 

//Color buffer 
    GLuint colorBuffer; 
    glGenBuffers(1, &colorBuffer); 
    glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); 
    glBufferData(GL_ARRAY_BUFFER, sqColor.size()*sizeof(GLfloat), &sqColor[0], GL_STATIC_DRAW); 
    glEnableVertexAttribArray(1); 
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(void*)0); 

// Indice buffer 
    GLuint indicesBuffer; 
    glGenBuffers(1, &indicesBuffer); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer); 
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sqIndice.size()*sizeof(GLuint), &sqIndice[0], GL_STATIC_DRAW); 

//Lattice point buffer 
    GLuint latticePointBuffer; 
    glGenBuffers(1, &latticePointBuffer); 
    glBindBuffer(GL_ARRAY_BUFFER, latticePointBuffer); 
    glBufferData(GL_ARRAY_BUFFER, latticePoints.size()*sizeof(GLfloat), &latticePoints[0], GL_STATIC_DRAW); 
    glEnableVertexAttribArray(2); 
    glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,0,(void*)0); 
    glVertexAttribDivisor(2,1); 

glBindVertexArray(0); 
#elif Instanced==false 
GLuint cubeGridVAO; 
    glGenVertexArrays(1, &cubeGridVAO); 
    glBindVertexArray(cubeGridVAO); 
    glEnable(GL_DEPTH_TEST); 

//Vertex buffer 
    GLuint vertexBuffer; 
    glGenBuffers(1, &vertexBuffer); 
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); 
    glBufferData(GL_ARRAY_BUFFER, sqVertexGrid.size()*sizeof(GLfloat), &sqVertexGrid[0], GL_STATIC_DRAW); 
    glEnableVertexAttribArray(0); 
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,(void*)0); 

//Color buffer 
    GLuint colorBuffer; 
    glGenBuffers(1, &colorBuffer); 
    glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); 
    glBufferData(GL_ARRAY_BUFFER, sqColorGrid.size()*sizeof(GLfloat), &sqColorGrid[0], GL_STATIC_DRAW); 
    glEnableVertexAttribArray(1); 
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(void*)0); 

// Indice buffer 
    GLuint indicesBuffer; 
    glGenBuffers(1, &indicesBuffer); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer); 
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sqIndiceGrid.size()*sizeof(GLuint), &sqIndiceGrid[0], GL_STATIC_DRAW); 

glBindVertexArray(0); 
#endif 


while(running) 
{ 
     glfwGetFramebufferSize(windowRef, &width, &height); 
     height = height > 0 ? height : 1; 

     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

     Projection = glm::perspective(65.0f, (float)(width)/height, 0.1f, 300.0f); 
     View = glm::lookAt( glm::vec3(0.0f,0.0f,-(distanceFromOrigin+3.8f)), 
           glm::vec3(0.0f,0.0f,100.0f), 
           glm::vec3(0.0f,1.0f,0.0f)); 
     Model = glm::rotate(glm::mat4(1.0f), 0.0f, glm::vec3(0.25f, 1.0f,0.75f)); 

     MVP = Projection*View*Model; 
     glUniformMatrix4fv(MatrixID, 1, GL_FALSE, glm::value_ptr(MVP)); 

     #if Instanced==true 
      glBindVertexArray(cubeVAO); 
      glDrawElementsInstanced(GL_TRIANGLES, sqIndice.size(),GL_UNSIGNED_INT,(GLvoid*)(0),latticePoints.size()); 
     #elif Instanced==false 
      glBindVertexArray(cubeGridVAO); 
      glDrawElements(GL_TRIANGLES, sqIndiceGrid.size(),GL_UNSIGNED_INT,(GLvoid*)(0)); 
     #endif 

     glfwPollEvents(); 
     glfwSwapBuffers(windowRef); 

     std::cout<<".\n"; 

    running = !glfwGetKey(windowRef,GLFW_KEY_ESCAPE) && !glfwWindowShouldClose(windowRef); 
    } 

    glfwDestroyWindow(windowRef); 
    glfwTerminate(); 

    return 0; 
}; 

GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){ 

     // Create the shaders 
     GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); 
     GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); 

     // Read the Vertex Shader code from the file 
     std::string VertexShaderCode; 
     std::ifstream VertexShaderStream(vertex_file_path, std::ios::in); 
     if(VertexShaderStream.is_open()){ 
       std::string Line = ""; 
       while(getline(VertexShaderStream, Line)) 
         VertexShaderCode += "\n" + Line; 
       VertexShaderStream.close(); 
     }else{ 
       printf("Impossible to open %s. Are you in the right directory?\n", vertex_file_path); 
       return 0; 
     } 

     // Read the Fragment Shader code from the file 
     std::string FragmentShaderCode; 
     std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in); 
     if(FragmentShaderStream.is_open()){ 
       std::string Line = ""; 
       while(getline(FragmentShaderStream, Line)) 
         FragmentShaderCode += "\n" + Line; 
       FragmentShaderStream.close(); 
     } 

     GLint Result = GL_FALSE; 
     int InfoLogLength; 

     // Compile Vertex Shader 
     printf("Compiling shader : %s\n", vertex_file_path); 
     char const * VertexSourcePointer = VertexShaderCode.c_str(); 
     glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL); 
     glCompileShader(VertexShaderID); 

     // Check Vertex Shader 
     glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); 
     glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 
     if (InfoLogLength > 0){ 
       std::vector<char> VertexShaderErrorMessage(InfoLogLength+1); 
       glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); 
       printf("%s\n", &VertexShaderErrorMessage[0]); 
     } 

     // Compile Fragment Shader 
     printf("Compiling shader : %s\n", fragment_file_path); 
     char const * FragmentSourcePointer = FragmentShaderCode.c_str(); 
     glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL); 
     glCompileShader(FragmentShaderID); 

     // Check Fragment Shader 
     glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); 
     glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 
     if (InfoLogLength > 0){ 
       std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1); 
       glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); 
       printf("%s\n", &FragmentShaderErrorMessage[0]); 
     } 


     // Link the program 
     printf("Linking program\n"); 
     GLuint ProgramID = glCreateProgram(); 
     glAttachShader(ProgramID, VertexShaderID); 
     glAttachShader(ProgramID, FragmentShaderID); 
     glLinkProgram(ProgramID); 

     // Check the program 
     glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); 
     glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); 
     if (InfoLogLength > 0){ 
       std::vector<char> ProgramErrorMessage(InfoLogLength+1); 
       glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); 
       printf("%s\n", &ProgramErrorMessage[0]); 
     } 

     glDeleteShader(VertexShaderID); 
     glDeleteShader(FragmentShaderID); 

     return ProgramID; 
} 
+0

Non ho una risposta, ma ho letto che l'istanza non è sempre una buona cosa da fare: http://stackoverflow.com/a/11973466/804614. "L'istanza non dovrebbe essere usata con mesh che hanno troppi vertici o troppe poche, 100-1000 circa." Non riesco a spiegarne il motivo, ma se stai rendendo i cubi che sarebbero solo 8 vertici. – Gigo

+0

Grazie per il collegamento, ma la stessa cosa accade con maglie più grandi con 540 vertici. E se il problema era dovuto a questo, non dovrebbe dipendere dalla posizione della telecamera. – jVincent

+1

Ho testato l'istanziamento usando cubi 100^3 (1M) su una 580 GTX a 30-50 fps a seconda della posizione della telecamera. Testato con posizioni casuali e come cubo compatto. Non ho notato alcun rallentamento come descritto qui. Forse il rallentamento è solo causato dalla quantità di superamento, quindi i cubi coprono l'intero schermo e vengono disegnati da dietro in avanti? – Grimmy

risposta

6

Ok, fai un respiro profondo e prendi posto: il tuo problema è la velocità della memoria della scheda grafica.

Ma si potrebbe rendere più facile per la GPU mediante la fissazione di questo bug:

glDrawElementsInstanced(GL_TRIANGLES, sqIndice.size(),GL_UNSIGNED_INT,(GLvoid*)(0),latticePoints.size()); 

glDrawElementsInstanced prevede che il numero di istanze per disegnare come ultimo parametro. Ma si passa il numero di elementi in latticePoints. Questo è 3 volte il numero di istanze. Ciò si traduce in un punto di lettice zero all'interno dello shader (a causa dell'accesso impedito ai limiti). Quindi 16000 cubetti non sono tradotti e sono dipinti nella stessa posizione. Ciò si traduce in dipingere la faccia anteriore dei cubi 16000 volte. Il buffer di profondità non impedisce questo perché i volti non si nascondono a vicenda, si trovano sullo stesso punto.

Così quando il vostro distanceFromOrigin diminuisce i 16000 cubi centrali diventano sempre più grandi. OpenGL deve disegnare sempre più pixel. Molto di più, per essere precisi. Deve trarre così tanto da raggiungere il limite di velocità della memoria della scheda grafica.

Leggi Diagnose OpenGl Performance Problems per l'intera storia.

+1

Ehi, non ho avuto molto tempo per esaminare questo vecchio codice, mi dispiace per la lentezza con cui ho accettato e risposto.Quando incontrai il problema e non ottenni alcun aiuto immediato, feci come supponevo molti altri e continuai semplicemente a lavorare attorno al problema. Ma la domanda più importante qui non è solo come ottenere questo codice specifico corretto, ma come si fa a debug di problemi sconosciuti, e il tuo post sul blog è stato un ottimo esempio. Quindi grazie per l'ottima risposta. – jVincent

Problemi correlati