views:

598

answers:

2

Hi

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.

Thanks for you time and help

A: 

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 ?

Bahbar
When I free the the texture, only about 1 to 10% of the aquired memory(at a per texture basis gets freed).My mehtod of watching this was the linux cmd tool top.I know this code looks completly ok, but it fills 4GiB of RAM (and if I repeatetly scroll up and down.I think its the textures, because there can't be anything else. Just replace the texture stuff with simple Vertex Data (and increase the Number by 1000x) and it will not happen (as far as I can say, but I will give this a shot).Maybe a nvidia Driver bug? This would explain it.I keep the context across the whole program.
penguinpower
Sorry, it is incompareable to Vertex rendering, mindflaw.I just don't see where it comes from. I mean the memory consumption is real ( it massivly slows down when the 4GiB barrier is broken, as Disk caching occures)I really need help there, the code itself shoulnd't leak anything at.
penguinpower
time to instrument you allocation routines, look at memory consumption from a debugger to look at the heap allocations, lint your program, request heap dumps... There are a number of ways to look at memory leaks or ever-growing lists.
Bahbar
The point is, the leak, or at least mem that stays occupied, is there, I actually replaced the OpenGL part with doNothing() and just used lists to test if it is still there (with QImages), but it is not.And I allready rechecked the demo with gdb for any signs, but nothing.
penguinpower
I misinterpreted your previous message. Well, I'm stumped. Any GL error ? otherwise, I'd try and reduce to a minimum program to submit a bug report.
Bahbar
I use this function to print all existing errors, but there are no errors, at least no gl errors.void printGLErrors(){ GLenum e(glGetError()); while(e!=GL_NO_ERROR){ qDebug() << "glerror [" << e << "] " << (unsigned*) gluErrorString(e); e=glGetError(); }}That's the point, I don't know what to do anymore, I doubt it's within my code, I *think* it's a gfx driver bug :SLucky me :S
penguinpower
A: 

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.

Andrew
It is not about getting it to work, it is all around memory leaking.And I don't get your point anways, this has nothing to do with the exacution order, the texture gets spawn _after_ gl init and after the first resize event.
penguinpower
If memory is leaking, I would consider the possibility that the calls to glDeleteTextures aren't working.
Andrew
So this was my point, but I don't get yours. The whole thing is running within one thread, the context is auto-auired by Qt at spawntime of QGLWidget.If it was a generic error, _no_ gl commands would work, but it all draws.So the question is why glDeleteTextures does not free the memory, while all other gl commands work flawless (even within wheelEvent).
penguinpower
Tested, and it does not change anything (as I expected).
penguinpower