我发现了glDeleteTexture的一个相当令人反感的行为,只删除了部分内存(GPU侧,并且为了RAM中的速度而将Textures保存回来),在我的情况下,这是一个showstopper bug,我的程序吃所有记忆。
我不想/要求你阅读所有代码,它只是一个演示,我宁愿知道如何实际使用glDeleteTextures,所以它不泄漏任何记忆。
示例代码需要Qt 4.5或更高版本来编译:
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 要加载和渲染的图像
注意:编译演示:只需将其全部放在一个文件夹中,将图像重命名为 ballmer_peak.png ,调用qmake,make,。/ glak 注意: 演示用法:使用鼠标滚轮一次生成或删除50个纹理
如果我使用glDeleteTextures完全错误,请告诉我如何使用它。 由于我的用法符合官方OpenGL glDeleteTextures用法,我的想法已经过时了。
答案 0 :(得分:1)
这可能是也可能不是你泄密的原因,但对于初学者来说,你错误地使用了glGenTextures。
1)你不应该把它放在初始化纹理的for循环中。您需要将它放在循环之前并将其称为ONCE,并将所需的纹理数量作为第一个参数。说n == 50:
glGenTextures(50,&amp; tex);
2)tex应该是n个GLuints的静态数组,并且应该被持久化(不是你拥有的自动变量!),直到再次调用glDeleteTextures,ONCE - 不在循环中:
glDeleteTextures(50,&amp; tex);
将tex视为存储纹理ID的存储库。重要的是你使用它而不是单独的QList,就像你所做的那样,用于绑定纹理,因为(如OpenGL参考中所指定的),不能保证纹理id将是一组连续的整数。我应该想象你的泄漏发生是因为内部OpenGL丢失了用于生成每个纹理的本地(自动)变量的原始指针,因此纹理内存变为孤立。
希望这有帮助!
答案 1 :(得分:1)
我没有运行你的示例代码,但我在Windows7-64bits上得到了类似的东西。使用每纹理glGenTextures()和glDeleteTextures(),它可能会泄漏内存,但我看到我的线程的句柄数增加(例如在TaskManager中,但我也可以从源代码检查它)。 看来glDeleteTextures()似乎没有释放句柄。也许它会在以后做,但24小时测试表明它永远不会释放手柄。看起来像是驱动程序内部的泄漏(nVidia GTX285,驱动程序270.61)。 最终确实程序内存不足。我开始认为这是一个司机问题......
答案 2 :(得分:0)
您的代码中没有任何错误。那么......是什么让你认为你有内存泄漏?是什么让你认为它的纹理特别泄漏?
您使用的OpenGL实现可能(但不太可能)泄漏。这将是特定于实现的。
无论您使用什么机制来查看内存泄漏,一旦释放OpenGL上下文会发生什么?
答案 3 :(得分:0)
您可能需要在wheelEvent的顶部调用makeCurrent()。
对于paintEvent,resizeEvent等,Qt提供了一个在调用paintGL / resizeGL / etc之前处理它的实现,但对于像wheelEvent这样的其他事件,你必须自己动手。
答案 4 :(得分:0)
我可能做错了,但是当我编译并运行你的代码时,我没有遇到任何问题?最多650个纹理(无法进一步增加:然后获得“被杀”信息)并且我的ram使用率从1%上升到24%并回到1%。上升到大约200并反复倒退也不会造成问题:最终使用率仍为1%。根据我的理解,这会导致系统出现大量泄漏? Ubuntu 10.10(Qt 4.7.0)。
答案 5 :(得分:-1)
我的系统上的测试很好地占用了内存,当我删除所有纹理时立即释放它,但如果我等待一段时间,内存将返回系统。 似乎OGL驱动程序使用了一些惰性内存释放算法。
答案 6 :(得分:-2)
for (QList<GLuint>::iterator i(texels.begin()); i!=texels.end();)
切换到
for (QList<GLuint>::iterator i(texels.end()); i!=texels.begin();)