Search code examples
c++optimizationopengl-estextures

OpenGL ES glTexImage2D optimization


My task is to show several pictures. I implemented it as a class to make several instances. Each instance represents a picture. It compiles shaders, set two triangles and loads picture data in constructor. The main program creates instances and then goes to loop to switch prigramid and call render() method for each instance.

while(true)
    for (uint g = 0; g < pictures.size(); g++){
        glUseProgram(pictures[g]->ProgramId);
        pictures[g]->render();
    }

It works well and shows the pictures but I do not like it. It could be done much better.

Here is partial code of the class

Picture::Picture(picPosition* pPosition, const char * fileName)
:BasePicture(pPosition)
{
    pos = glGetAttribLocation(ProgramId, "position");
    uv = glGetAttribLocation(ProgramId, "texture_vert");

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glGenBuffers(1, &uvbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);

    int n;
    textureData = stbi_load(fileName, &picWidth, &picHeight, &n, STBI_rgb_alpha);
    TextureID  = glGetUniformLocation(ProgramId, "myTextureSampler");
    glBindTexture(GL_TEXTURE_2D, TextureID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glActiveTexture(GL_TEXTURE0);

    glDepthMask(GL_FALSE);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    //calculating the vertex matrix using MVP calculated in parent class
    for (int i = 0; i < 6; i++)
        ModeledVerts.push_back(MVP * verts[i]);
    v = glm::value_ptr(ModeledVerts[0]);
}

Picture::~Picture()
{
    stbi_image_free(textureData);
    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &uvbuffer);
}

void Picture::render()
{
    glBufferData(GL_ARRAY_BUFFER, 96, v, GL_STATIC_DRAW);
    glVertexAttribPointer(pos, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*) 0);
    glEnableVertexAttribArray(pos);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glBufferData(GL_ARRAY_BUFFER, sizeof(verticesUV), verticesUV, GL_STATIC_DRAW);
    glVertexAttribPointer(uv, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*) 0);
    glEnableVertexAttribArray(uv);
    glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);

    glDrawArrays(GL_TRIANGLES, 0, 6);
}

I played a lot with the code to make render() function as light as possible but I cannot make it lighter then it is now.

The biggest problem is sending textureData every time (glTexImage2D). The data never gets changed. I tried to move it to constructor but in this case all picture objects show the same picture that was loaded latest. It looks like one instance overrides texture data uploaded before. I am looking for a way to load the picture data once in the constructor and not every time it renders. It looks like there is something in OpenGL API for that but I do not know it yet.

Another improvement could be for taking vertex data set up from render(). That data never changes. But it is not that significant as glTexImage2D call in render().

Could you point me to the OpenGL API to separate shader's data? Or show me what I am doing wrong...


Solution

  • Answering my own question. The solution is to use atlas map. Software generates atlas that contains all pictures, upload it once (glTexImage2D) and use coordinates for each picture. That improves performance very significantly as glTexImage2D was called just once.