0

我发现 glDeleteTexture 的一个相当令人作呕的行为,只删除了部分 aqcuired 内存(GPU 端和纹理被保存回来以提高 RAM 中的速度),在我的情况下,这是一个令人讨厌的错误,我的程序耗尽了所有内存.

我不想/要求您阅读所有代码,这只是一个演示,我宁愿知道如何实际使用 glDeleteTextures,这样它就不会泄漏任何内存

示例代码需要 Qt 4.5 或更高版本才能编译:

glleak.pro

QT += opengl

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

主文件

#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 要加载和渲染的图像

注意:编译演示:只需将其全部放在一个文件夹中,将图像重命名为ballmer_peak.png,调用 qmake、make、./glleak 注意:演示用法:使用鼠标滚轮一次生成或删除 50 个纹理

如果我使用 glDeleteTextures 完全错误,请告诉我如何使用它。由于我的用法符合官方的 OpenGL glDeleteTextures用法,因此我没有想法。

4

7 回答 7

1

这可能是也可能不是您泄漏的原因,但对于初学者来说,您错误地使用了 glGenTextures。

1)你不应该把它放在初始化纹理的 for 循环中。您需要将它放在循环之前并调用它一次,并将所需的纹理数量作为第一个参数。说 n == 50:

glGenTextures(50, &tex);

2) tex 应该是一个 n GLuints 的静态数组,并且应该被持久化(而不是你拥有的自动变量!)直到 glDeleteTextures 再次被调用一次 - 不是在循环中:

glDeleteTextures(50, &tex);

将 tex 视为存储纹理 ID 的存储库。重要的是你使用它而不是像你所做的那样说一个单独的 QList 来绑定纹理,因为(如 OpenGL 参考中所指定)不能保证纹理 ID 将是一组连续的整数。我应该想象您的泄漏发生是因为 OpenGL 在内部丢失了指向您用于生成每个纹理的本地(自动)变量的原始指针,因此纹理内存变得孤立。

希望这可以帮助!

于 2011-04-14T03:06:59.777 回答
1

我没有运行你的示例代码,但我在 Windows7-64 位上得到了类似的东西。使用每个纹理 glGenTextures() 和 glDeleteTextures(),它可能会泄漏内存,但我看到我的线程的句柄计数增加(例如在 TaskManager 中,但我也可以从源代码检查它)。似乎 glDeleteTextures() 没有释放句柄。也许它稍后会这样做,但 24 小时测试表明它永远不会释放手柄。似乎驱动程序内部存在泄漏(nVidia GTX285,驱动程序 270.61)。最终,程序确实内存不足。开始怀疑是驱动问题。。。

于 2011-05-25T12:44:47.510 回答
0

您可能需要在wheelEvent的顶部调用makeCurrent() 。

对于paintEvent、resizeEvent 等,Qt 提供了一个在调用paintGL/resizeGL/etc 之前处理这个的实现,但是对于像wheelEvent 这样的其他事件,你必须自己做。

于 2010-02-20T15:27:11.837 回答
0

您的代码中没有任何问题。所以......是什么让你认为你有内存泄漏?是什么让您认为是纹理特别泄漏?

您使用的 OpenGL 实现可能会泄漏,但可能性极小。那将是特定于实现的。

无论您使用何种机制来查看内存泄漏,一旦释放 OpenGL 上下文会发生什么?

于 2010-02-20T14:55:43.097 回答
0

我可能做错了,但是当我编译并运行您的代码时,我没有遇到任何问题?上升到 650 个纹理(不能进一步增加:然后得到一个“被杀死”的消息)然后我的 ram 使用率从 1% 上升到 24% 再回到 1%。上升到大约 200 并反复下降也不会导致问题:最终内存使用率仍然是 1%。据我了解,这会导致您的系统出现大量泄漏?这里是 Ubuntu 10.10 (Qt 4.7.0)。

于 2011-03-26T13:16:31.767 回答
-1

您在我的系统上的测试很好地消耗了内存,当我删除所有纹理时不会立即释放它,但是如果我等待一段时间,内存会返回给系统。似乎 OGL 驱动程序使用了一些惰性内存释放算法。

于 2012-01-10T18:52:23.973 回答
-2
for (QList<GLuint>::iterator i(texels.begin()); i!=texels.end();)

切换到

for (QList<GLuint>::iterator i(texels.end()); i!=texels.begin();)
于 2012-01-03T16:31:25.697 回答