2013-02-21 2 views
2

나는 기록 된 비디오와 실시간 비디오를 분석하는 비디오 컨텐츠 ​​분석 응용 프로그램을 작성 중입니다.OpenGL 텍스처 매핑 메모리 누수

나는 (qglwidgets를 사용하여) qt 인터페이스에 비디오를 표시하기 위해 opengl을 사용합니다. 비디오 카드 (OpenCV의 IPLImage에서로드 됨)를 표시하기 위해 그래픽 카드가 지원하는 경우 그림 버퍼 객체와의 텍스처 매핑을 사용하고 있습니다 (참조 : http://www.songho.ca/opengl/gl_pbo.html).

문제는 응용 프로그램의 메모리가 시간이 지남에 따라 계속 증가한다는 것입니다. 대략 초당 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 구조에서 비롯됩니다. 나는 또한 동기화 목적으로 부스트의 lock_guard를 사용하고있다.

여기에 내가 잘못하고있는 것이 있습니까?

편집 : 나는 나머지 코드를 추가하고 : 당신은 텍스처 객체에 glDeleteBuffers()를 호출하고

//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 

} 
+0

iplimages 처리시 메모리가 누출되었을 수 있습니다. 너 어디서 들었 니? 당신이 풀어주지 않은 어떤 변환/사본을하고 있습니까? – berak

+1

게시 한 코드에 메모리 할당이 없습니다. 거기에 메모리 누출을하는 것은 불가능합니다. –

+0

메모리 누수는 qglwidget에 iplimages를 "표시"할 때만 발생합니다. 나는 페인트 GL 코드만을 주석으로 검사하여 메모리 누수가 사라졌습니다. 이것이 내가이 코드에 문제를 국한 시켰다고 말한 이유이다. 그러나 나는 여전히 나머지 코드를 추가했다. –

답변

2

(버퍼 오브젝트해야한다), 또는 오히려, 내 생각은 전혀 여기 안된다. 다른 GL 개체와 마찬가지로 모든 glGen() 호출마다 한 번 glDelete() 만 수행됩니다.

glFlush() 및 swapBuffers()를 호출하면 Qt가이를 처리합니다.

OpenGL 드라이버에 메모리 누수가있을 수 있습니다. PBO없이 시험해보십시오.

GL 호출 후 glGetError()를 사용하여 다른 곳에서 실수를했는지 확인하십시오.

+0

Qt의 autoswap 버퍼 플래그를 비활성화했습니다. 자동 버퍼 교체가 작동하지 않기 때문에 swapBuffers를 사용하고 있습니다. 나는 또한 PBO를 사용하지 않고 확인했다. 문제가 지속됩니다 (Windows 7에서만 드라이버 문제 일 수 있음). –