glDeleteTextures, leaking?

2019-06-14 06:18发布

问题:

I found a rather disguting behaviour of glDeleteTexture, deleteing only parts of the aqcuired memory (GPU side and as Textures get saved back for the sake of speed in RAM), which in my case, is a showstopper bug, my program eating up all memory.

I don't want/require you to read all of the code, it's just a demo, I'd rather know how to actually use glDeleteTextures so it does not leak any memory.

The example code requires Qt 4.5 or later to compile:

glleak.pro

QT += opengl

SOURCES += main.cpp \
    glleak.cpp
HEADERS += glleak.h

main.cpp

#include <QtOpenGL>
#include <QtGui>
#include "glleak.h"

int main(int argc, char** argv){
    QApplication app(argc, argv);
    glleak gll(0);
    gll.show();
    return app.exec();
}

glleak.h

#ifndef GLLEAK_H
#define GLLEAK_H

#include <QGLWidget>
#include <QMouseEvent>
#include <QDebug>
#include <QList>

class glleak : public QGLWidget
{
    Q_OBJECT
public:
    glleak(QWidget* parent = 0);
    virtual ~glleak();
protected:
    void initializeGL();
    void paintGL();
    void resizeGL(int w, int h);
    void drawScene(GLenum mode);

    void wheelEvent(QWheelEvent* event);

    void hardcoreTexturing();
private:
    QList<GLuint> texels;

};

#endif // GLLEAK_H

glleak.cpp

glleak::glleak(QWidget* parent) :
        QGLWidget(parent)
{
}

glleak::~glleak()
{
}


void glleak::initializeGL(){
    glClearColor(0.0f,0.0f,0.0f,0.0f);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_MULTISAMPLE);
    glLineWidth (1.5f);
    glPointSize(4.5f);
    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

void glleak::resizeGL(int w, int h){
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-w/2.0, w/2.0, h/2.0, -h/2.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glViewport(0, 0, w, h);
    glLoadIdentity();
}

void glleak::paintGL(){
    glPushMatrix();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glColor3f(1.0f,1.0f,1.0f);
    drawScene(GL_RENDER);
    glPopMatrix();
}




void glleak::drawScene(GLenum mode){
    qDebug() << "drawed #" << texels.count() << " Textures";
    hardcoreTexturing();
}


void glleak::hardcoreTexturing(){
    glEnable(GL_TEXTURE_2D);
    for ( int i(0); i<texels.count(); ++i){
        glPushMatrix();
        glTranslatef(1.1f*i, 2.2f*i, 0.0f);
        glBindTexture(GL_TEXTURE_2D, texels.at(i));
        glBegin(GL_QUADS);
        {
            glTexCoord2i(0,0);
            glVertex2i(-128,-128);

            glTexCoord2i(0,1);
            glVertex2i(-128,128);

            glTexCoord2i(1,1);
            glVertex2i(128,128);

            glTexCoord2i(1,0);
            glVertex2i(128,-128);

        }
        glEnd();
        glPopMatrix();
    }
    glDisable(GL_TEXTURE_2D);
}


void glleak::wheelEvent(QWheelEvent* event){
    glEnable(GL_TEXTURE_2D);
    int n(50);
    if (event->delta()>0){
        qDebug() << "gen textures";
        for (int i(0); i<n; ++i){
            QImage t("./ballmer_peak.png","png");
            GLuint tex(0);
            glGenTextures(1, &tex);
            glBindTexture(GL_TEXTURE_2D, tex);
            glTexImage2D( GL_TEXTURE_2D, 0, 3, t.width(), t.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t.bits() );
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            texels.append(tex);
        }
    }
    else{
        qDebug() << "del textures";
        for (QList<GLuint>::iterator i(texels.begin()); i!=texels.end();){
            glDeleteTextures(1, &(*i));
            i = texels.erase(i);
            if (--n <= 0)
                break;


        }
    }
    glDisable(GL_TEXTURE_2D);
        updateGL();
}

ballmer_peak.png A Image to load and render

Note: Compile demo: Just put it all in a folder, rename your image to ballmer_peak.png, call qmake, make, ./glleak Note: Demo usage: Use mousewheel to generate or delete 50 Textures at once

If I use glDeleteTextures completly wrong, please tell me how to use it. I am way out of ideas as my usage complies to the official OpenGL glDeleteTextures usage.

回答1:

This may or may not be the reason for your leak, but for starters you are using glGenTextures wrongly.

1) You should not put this inside the for loop which initializes the textures. You need to put it before the loop and call it ONCE, with the number of textures required as the first parameter. Say n == 50:

glGenTextures(50, &tex);

2) tex should be a static array of n GLuints and should be persisted (not an auto variable as you have it!) until glDeleteTextures has been called, again, ONCE - not in a loop:

glDeleteTextures(50, &tex);

Think of tex as a repository for storing texture ids. It is important you use it and not say a separate QList, as you have done, for binding textures, since (as specified in the OpenGL reference) there is no guarantee that the texture ids will be a contiguous set of integers. I should imagine your leak happens because internally OpenGL loses the original pointer to the local (auto) variable you used to generate each texture, so the texture memory becomes orphaned.

Hope this helps!



回答2:

I did not run your example code, but I get a similar thing on Windows7-64bits. Using per-texture glGenTextures() and glDeleteTextures(), it might leak memory, but I'm seeing my thread's handle count increase (in TaskManager for example, but I can also check it from the source). It seems glDeleteTextures() does not release a handle. Perhaps it'd do it later on, but 24-hour tests indicate it never releases the handle. Seems like a leak inside the driver (nVidia GTX285, driver 270.61). Eventually indeed the program runs out of memory. I'm beginning to think it's a driver issue...



回答3:

There's nothing that looks wrong in your code. So... What makes you think you have a memory leak ? What makes you think it's textures specifically that leak ?

It is possible, but highly unlikely, that the OpenGL implementation you use leaks. That would be implementation specific.

Whatever the mechanism you use to look at memory leaks, what happens once you free the OpenGL context ?



回答4:

You may need to call makeCurrent() at the top of wheelEvent.

For paintEvent, resizeEvent etc, Qt provides an implementation which handles this before calling paintGL/resizeGL/etc, but for other events like wheelEvent you have to do it yourself.



回答5:

I might be doing this wrong, but when I compiled and ran your code I didn't run into any problems? Going up to 650 textures (can't increase further: get a 'killed' message then) and back my ram usage goes up from 1% to 24% and back to 1%. Going up to about 200 and back down repeatedly also doesn't cause problems: ram usage is still 1% at the end. From what I understand this would've caused massive leaks on your system? Ubuntu 10.10 here (Qt 4.7.0).



回答6:

Your test on my system eats memory well, and not releases it immediatly when I delete all the textures, but if I wait for some time, memory is returned to system. It seems that OGL driver uses some lazy memory releasing algorithm.



回答7:

for (QList<GLuint>::iterator i(texels.begin()); i!=texels.end();)

switch to

for (QList<GLuint>::iterator i(texels.end()); i!=texels.begin();)