Qt OpenGL纹理映射内存泄漏

Qt OpenGL纹理映射内存泄漏,qt,opengl,opencv,memory-leaks,textures,Qt,Opengl,Opencv,Memory Leaks,Textures,我正在编写一个视频内容分析应用程序,用于分析录制的视频和实况视频 我使用opengl在qt界面上显示视频(使用qglwidgets)。如果图形卡支持,我将使用纹理映射和图片缓冲区对象来显示视频(从OpenCV的IPLImage加载) 问题是,应用程序的内存会随着时间的推移不断增加。大约每秒4-8KB。我正在使用任务管理器验证这一点。 我已经缩小了视频渲染的问题范围,因为我看到了很多关于纹理未被释放导致内存使用的帖子,但我还没有找到解决问题的方法 我只在initializeGL()中使用glGen

我正在编写一个视频内容分析应用程序,用于分析录制的视频和实况视频

我使用opengl在qt界面上显示视频(使用qglwidgets)。如果图形卡支持,我将使用纹理映射和图片缓冲区对象来显示视频(从OpenCV的IPLImage加载)

问题是,应用程序的内存会随着时间的推移不断增加。大约每秒4-8KB。我正在使用任务管理器验证这一点。
我已经缩小了视频渲染的问题范围,因为我看到了很多关于纹理未被释放导致内存使用的帖子,但我还没有找到解决问题的方法

我只在initializeGL()中使用glGenTextures,因此纹理只生成一次并重新使用

以下是问题所在的代码:

void paintGL(){  
static int index = 0;  
int nextIndex = 0;                  // pbo index used for next frame  
if(paintFlag){  
    if(pboMode > 0) {  
// "index" is used to copy pixels from a PBO to a texture object  "nextIndex" is used to update     pixels in a PBO    

if(pboMode == 1){  
// In single PBO mode, the index and nextIndex are set to 0  

        index = nextIndex = 0;
        }
        else if(pboMode == 2)
        {
            // In dual PBO mode, increment current index first then get the next index
            index = (index + 1) % 2;
            nextIndex = (index + 1) % 2;
        }

        // start to copy from PBO to texture object ///////

        // bind the texture and PBO
        glBindTexture(GL_TEXTURE_2D, texture);
        glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[index]);

        // copy pixels from PBO to texture object
        // Use offset instead of ponter.
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_BGR, GL_UNSIGNED_BYTE, 0);

        // measure the time copying data from PBO to texture object
        //t1.stop();
        //copyTime = t1.getElapsedTimeInMilliSec();
        ///////////////////////////////////////////////////


        // start to modify pixel values ///////////////////
        //       t1.start();

        // bind PBO to update pixel values
        glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[nextIndex]);

        // map the buffer object into client's memory
        // Note that glMapBufferARB() causes sync issue.
        // If GPU is working with this buffer, glMapBufferARB() will wait(stall)
        // for GPU to finish its job. To avoid waiting (stall), you can call
        // first glBufferDataARB() with NULL pointer before glMapBufferARB().
        // If you do that, the previous data in PBO will be discarded and
        // glMapBufferARB() returns a new allocated pointer immediately
        // even if GPU is still working with the previous data.
        glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_DRAW_ARB);
        GLubyte* ptr = (GLubyte*)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB);
        if(ptr)
        {
            // update data directly on the mapped buffer
            //updatePixels(ptr, DATA_SIZE);
            memcpy(ptr,original->imageData,DATA_SIZE);
            glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); // release pointer to mapping buffer
        }

        // measure the time modifying the mapped buffer
        //t1.stop();
        //updateTime = t1.getElapsedTimeInMilliSec();
        ///////////////////////////////////////////////////

        // it is good idea to release PBOs with ID 0 after use.
        // Once bound with 0, all pixel operations behave normal ways.
        glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
    }
    else
    {
        ///////////////////////////////////////////////////
        // start to copy pixels from system memory to textrure object
        //t1.start();

        glBindTexture(GL_TEXTURE_2D, texture);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_BGR, GL_UNSIGNED_BYTE, (GLvoid*)original->imageData);

        //t1.stop();
        //copyTime = t1.getElapsedTimeInMilliSec();


    }
    paintFlag=false;
}


// clear buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBegin(GL_QUADS);
glTexCoord2i(0,1); glVertex2i(0,HEIGHT);
glTexCoord2i(0,0); glVertex2i(0,0);
glTexCoord2i(1,0); glVertex2i(WIDTH,0);
glTexCoord2i(1,1); glVertex2i(WIDTH,HEIGHT);
glEnd();
glFlush();
glBindTexture(GL_TEXTURE_2D, 0);
swapBuffers();
glDeleteBuffers(1,&texture);
updateGL(); 
}
代码与教程中的代码基本相同。但是,我的纹理数据来自IplImage结构,该结构由单独的线程不断更新。我还使用boost的lock_-guard进行同步

我在这里做错什么了吗

编辑:我正在添加剩余代码:

//Constructor, this is where all the allocation happens
const int    DATA_SIZE = WIDTH * HEIGHT * 3;
QGLCanvas::QGLCanvas(QWidget* parent,QString caption)
    : QGLWidget(parent)
{
    imageFormat=QImage::Format_RGB888;
this->name=caption;
original=cvCreateImage(cvSize(WIDTH,HEIGHT),IPL_DEPTH_8U,3);
if(this->name=="Background")
    bgFrameBackup=cvCreateImage(cvSize(WIDTH,HEIGHT),IPL_DEPTH_8U,3);
cvZero(original);
//cvShowImage("w",original);
//cvWaitKey(0);

switch(original->nChannels) {
case 1:
    format = GL_LUMINANCE;
    break;
case 2:
    format = GL_LUMINANCE_ALPHA;
    break;
case 3:
    format = GL_BGR;
    break;
default:
    return;
}


drawing=false;
setMouseTracking(true);
mouseX=0;mouseY=0;
startX=0; endX=0;
startY=0; endY=0;
dialog=new EntryExitRuleDialog();
makeCurrent();
GLenum result=glewInit();
if(result){
    qDebug()<<(const char*)(glewGetErrorString(result));
}
//qDebug()<<"Open GL Version: "<<(const char*)glGetString(GL_VERSION);
bgColor=QColor::fromRgb(100,100,100);
initializeGL();
qglClearColor(bgColor); 
glInfo glInfo;
glInfo.getInfo();

#ifdef _WIN32
// check PBO is supported by your video card

if(glInfo.isExtensionSupported("GL_ARB_pixel_buffer_object"))
{
    // get pointers to GL functions
    glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB");
    glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB");
    glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB");
    glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)wglGetProcAddress("glBufferSubDataARB");
    glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)wglGetProcAddress("glDeleteBuffersARB");
    glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)wglGetProcAddress("glGetBufferParameterivARB");
    glMapBufferARB = (PFNGLMAPBUFFERARBPROC)wglGetProcAddress("glMapBufferARB");
    glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)wglGetProcAddress("glUnmapBufferARB");

    // check once again PBO extension
    if(glGenBuffersARB && glBindBufferARB && glBufferDataARB && glBufferSubDataARB &&
        glMapBufferARB && glUnmapBufferARB && glDeleteBuffersARB && glGetBufferParameterivARB)
    {
        pboSupported = true;
        cout << "Video card supports GL_ARB_pixel_buffer_object." << endl;
    }
    else
    {
        pboSupported = false;
        cout << "Video card does NOT support GL_ARB_pixel_buffer_object." << endl;
    }
}

#else // for linux, do not need to get function pointers, it is up-to-date
if(glInfo.isExtensionSupported("GL_ARB_pixel_buffer_object"))
{
    pboSupported = pboUsed = true;
    cout << "Video card supports GL_ARB_pixel_buffer_object." << endl;
}
else
{
    pboSupported = pboUsed = false;
    cout << "Video card does NOT support GL_ARB_pixel_buffer_object." << endl;
}
#endif

if(pboSupported){
    glGenBuffersARB(2, pboIds);
    glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[0]);
    glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_DRAW_ARB);
    glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[1]);
    glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_DRAW_ARB);
    glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
    //Note: pboMode=2 somehow does not work while calibration. Fix this later.
    pboMode=1;
}
else{
    pboMode=0;
}

paintFlag=false;
}

void QGLCanvas::setImage(IplImage image){

if(QString(this->name)=="Background"){
    cvCopyImage(&image,bgFrameBackup);
}
//cvShowImage(name,&image);
//  Display a rectangle between startX ,startY and endX,endY if we are in calibration mode
//and drawing flag is set.(typically, by a mouse click)
if(QString(this->name)=="Calibrate" && calibrating ){
    if(drawing) 
        cvRectangle(&image,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee)); 
    if(select_object) //During calibration
        cvRectangle(&image,cvPoint(selection.x,selection.y),cvPoint(selection.x+selection.width,selection.y+selection.height),cvScalarAll(0xee)); 
    //Draw existing calibration rectangles

    for (list<CvRect>::iterator it=calibration_rect_list->begin(); it!=calibration_rect_list->end(); ++it)
    {
        cvRectangle(&image, cvPoint((*it).x, (*it).y), cvPoint((*it).x + (*it).width, (*it).y + (*it).height), CV_RGB(100,255,0), 2, 8, 0);
    }


}
//Only draw on the video widget with the name "Final"
if(QString(this->name)=="Final")
{

    if(calibrating && drawing) 
        cvRectangle(&image,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee)); 
    //If we are adding a rule, the corresponding rule shape must be drawn on the widget.
    if(addingRule && drawing){
        if(currentShape==RULE_SHAPE_RECT){
            cvRectangle(&image,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee)); 
        }
        else if(currentShape==RULE_SHAPE_POLY){
            int linecolor=0xee;
            if(points.count()>0){
                //Draw polygon...
                for(int i=1;i<points.count();i++){
                    cvLine(&image,cvPoint(points[i-1]->x(),points[i-1]->y()),cvPoint(points[i]->x(),points[i]->y()),cvScalarAll(linecolor));
                }

                cvLine(&image,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee));
                cvLine(&image,cvPoint(endX,endY),cvPoint(points[0]->x(),points[0]->y()),cvScalarAll(linecolor));
            }
        }
        else if(currentShape==RULE_SHAPE_TRIPLINE){
            for(int i=1;i<points.count();i++){
                cvLine(&image,cvPoint(points[i-1]->x(),points[i-1]->y()),cvPoint(points[i]->x(),points[i]->y()),cvScalarAll(0xee));
            }
            cvLine(&image,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee));
        }

    }
    if(entryExitRuleCreated && currentZoneType==RULE_ZONE_TYPE_ENTRY_EXIT ){
        //Highlight appropriate sides of the currentRule to mark them as Entry/Exit Zone
        for(int i=0;i<currentRule->points.count();i++){
            QPoint* P1=currentRule->points[i];
            QPoint* P2;
            //Implement cyclic nature of polygon
            if(i<currentRule->points.count()-1)
                P2=currentRule->points[i+1];
            else P2=currentRule->points[0];
            int deltax=mouseX-P1->x();
            int deltax1=P2->x()-P1->x();
            float m,m1;
            if(deltax!=0)
                m= (float)(mouseY-P1->y())/deltax;
            if(deltax1!=0 && deltax!=0){
                m1=(float)(P2->y()-P1->y())/deltax1;
                if(round(m,1)==round(m1,1))//Mouse pointer lies on the line whose slope is same as the polygon edge
                {
                    //Mouse pointer is on the edge of a polygon, highlight the edge
                    if(abs(P1->y()-P2->y()) >= abs(mouseY-P2->y()) && abs(P1->y()-P2->y()) >= abs(mouseY-P1->y()) 
                        &&  abs(P1->x()-P2->x()) >= abs(mouseX-P2->x()) && abs(P1->x()-P2->x()) >= abs(mouseX-P1->x())
                        ){
                            edgeHighlighted=true;
                            highlightedEdge[0]=P1;
                            highlightedEdge[1]=P2;
                            currentEdgeNumber=i;
                            break;
                    }
                }
                else{
                    edgeHighlighted=false;
                }
            }
            else{
                //Vertical edge of a polygon.
                if(abs(mouseX-P1->x())<4) { //Same vertical line
                    if(abs(P1->y()-P2->y()) > abs(mouseY-P2->y()) && abs(P1->y()-P2->y()) > abs(mouseY-P1->y())){
                        //Current y lies between the two vertices of an edge
                        //Mouse pointer is on the edge of polygon,highlight the edge
                        //qDebug()<<"P1="<<P1->x()<<","<<P1->y()<<", P2="<<P2->x()<<","<<P2->y();
                        edgeHighlighted=true;
                        highlightedEdge[0]=P1;
                        highlightedEdge[1]=P2;
                        currentEdgeNumber=i;
                        break;
                    }
                    else
                        edgeHighlighted=false;
                }
            }

        }
        if(edgeHighlighted || edgeHighlightedFromButton){
            cvLine(&image,cvPoint(highlightedEdge[0]->x(),highlightedEdge[0]->y()),cvPoint(highlightedEdge[1]->x(),highlightedEdge[1]->y()),cvScalar(0xff,0x00,0x00),3);
        }
    }
}

{
    //qDebug()<<name<<":Saving original image";
    ExclusiveLock xlock(globalXMutex);
    this->original=&image;
    paintFlag=true;
}

updateGL(); 


/*if(this->name=="Final"){
cvShowImage("Final",original);
cvWaitKey(1);
}*/
}

//Texture is generated here
void QGLCanvas::initializeGL(){

glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glClearColor(0, 0, 0, 0);                   // background color
glClearStencil(0);                          // clear stencil buffer
glClearDepth(1.0f);                         // 0 is near, 1 is far
glDepthFunc(GL_LEQUAL);

glEnable(GL_TEXTURE_2D);
glGenTextures(1,&texture);
glBindTexture(GL_TEXTURE_2D,texture);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glBindTexture(GL_TEXTURE_2D,texture);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,WIDTH,HEIGHT,0,GL_BGR,GL_UNSIGNED_BYTE,NULL);
glBindTexture(GL_TEXTURE_2D, 0);

glClearStencil(0);                          // clear stencil buffer
glClearDepth(1.0f);                         // 0 is near, 1 is far
glDepthFunc(GL_LEQUAL);
setAutoBufferSwap(false);
}


void QGLCanvas::resizeGL(int width,int height){

if (height==0)                                      // Prevent A Divide By Zero By
{
    height=1;                                       // Making Height Equal One
}

glViewport(0,0,WIDTH,HEIGHT);                       // Reset The Current Viewport
glMatrixMode(GL_PROJECTION);                        // Select The Projection Matrix
glLoadIdentity();                                   // Reset The Projection Matrix
glOrtho(0.0f,WIDTH,HEIGHT,0.0f,0.0f,1.0f);
glEnable(GL_TEXTURE_2D);


glMatrixMode(GL_MODELVIEW);                         // Select The Modelview Matrix
glLoadIdentity();                                   // Reset The Modelview Matrix

}
//构造函数,这是所有分配发生的地方
const int DATA_SIZE=宽度*高度*3;
QGLCanvas::QGLCanvas(QWidget*父项,QString标题)
:QGLWidget(父级)
{
imageFormat=QImage::Format_RGB888;
此->名称=标题;
原始=cvCreateImage(cvSize(宽度、高度)、IPL_深度_8U,3);
如果(此->名称==“背景”)
bgFrameBackup=cvCreateImage(cvSize(宽度、高度)、IPL_深度_8U,3);
cvZero(原件);
//cvShowImage(“w”,原件);
//cvWaitKey(0);
开关(原始->通道){
案例1:
格式=GL_亮度;
打破
案例2:
格式=GL_亮度_α;
打破
案例3:
格式=GL_BGR;
打破
违约:
返回;
}
图纸=假;
setMouseTracking(真);
mouseX=0;mouseY=0;
startX=0;endX=0;
startY=0;endY=0;
dialog=newentryexitruledialog();
makeCurrent();
盂结果=glewInit();
如果(结果){
qDebug()y()),cvScalarAll(0xee));
}
cvLine(&图像,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee));
}
}
if(entryExitRuleCreated&¤tZoneType==RULE\u ZONE\u TYPE\u ENTRY\u EXIT){
//突出显示currentRule的相应侧面,将其标记为进入/退出区域
对于(int i=0;ipoints.count();i++){
QPoint*P1=当前规则->点[i];
QPoint*P2;
//实现多边形的循环性质
if(ipoints.count()-1)
P2=当前规则->点数[i+1];
else P2=currentRule->points[0];
int deltax=mouseX-P1->x();
int deltax1=P2->x()-P1->x();
浮点数m,m1;
如果(deltax!=0)
m=(浮动)(mouseY-P1->y())/deltax;
如果(deltax1!=0&&deltax!=0){
m1=(浮动)(P2->y()-P1->y())/deltax1;
if(round(m,1)=round(m1,1))//鼠标指针位于坡度与多边形边相同的线上
{
//鼠标指针位于多边形的边上,高亮显示该边
如果(abs(P1->y()-P2->y())>=abs(mouseY-P2->y())和&abs(P1->y()-P2->y())>=abs(mouseY-P1->y())
&&abs(P1->x()-P2->x())>=abs(mouseX-P2->x())&&abs(P1->x()-P2->x())>=abs(mouseX-P1->x())
){
edgeHighlighted=真;
highlightedEdge[0]=P1;
highlightedEdge[1]=P2;
currentEdgeNumber=i;
打破
}
}
否则{
边缘高亮=假;
}
}
否则{
//多边形的垂直边。
如果(abs(mouseX-P1->x())y()-P2->y())>abs(mouseY-P2->y())和&abs(P1->y()-P2->y())>abs(mouseY-P1->y()){
//当前y位于边的两个顶点之间
//鼠标指针位于多边形的边上,高亮显示该边

//qDebug()您正在对纹理对象(应该是缓冲区对象)调用glDeleteBuffers(),或者更确切地说,我认为根本不应该在这里。与其他GL对象一样,每次glGen()调用只调用一次glDelete()

您正在调用glFlush()和swapBuffers(),我相信Qt会为您解决这个问题

OpenGL驱动程序可能存在内存泄漏。请在没有PBO的情况下尝试


尝试glGetError()每次GL调用后,检查您是否在其他地方出错。

可能您在处理iplimages时正在泄漏内存?这是从哪里得到的?您是否正在进行未发布的转换/复制?您发布的代码上没有内存分配。不可能存在内存泄漏。内存泄漏会发生只有当我“显示”的时候qglwidget上的iplimages。我仅通过注释paintGL代码进行检查,内存泄漏消失。这就是为什么我说我已将问题定位到此代码上。但我仍然添加了剩余的代码,以防万一。我仍在寻找替代解决方案,但我遇到了这个问题。事实证明,这只是windows 7(使用nvidia 7950GT)就有这个问题!我在另外两台装有WinXp、ATI和Matrox卡的计算机上进行了尝试,但内存泄漏没有发生!也许这是微软的说法“使用directdraw,而不是opengl”?我已禁用Qt的autoswap buffers标志,这就是我使用swapBuffers的原因,因为自动缓冲区交换对我不起作用。我也在不使用PBO的情况下进行了检查。问题仍然存在(仅在Windows 7上,这可能是驱动程序问题)。