PDA

View Full Version : Memory leak using glTexSubImage2D



Aumnayan
05-03-2013, 11:26 AM
In the course of my application I create a 2048x2048 texture which then gets updated periodically. Every few times this texture gets updated via a glTexSubImage2D call there is a significant hit on the available memory of the system which never appears to be released.

I've attached a full application that demonstrates how I'm using this texture in my real application and having tested it find that this has the same memory issue as my real application. Can anyone see something I'm not doing properly that might account for this memory growth?



#define GL_GLEXT_PROTOTYPES

#include <GL/freeglut.h>
#include <GL/glext.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <iostream>
#include <fstream>
using namespace std;

GLuint texid(0);
unsigned char rawTex[2048*2048*3];
int totalMem(0), freeMem(0);

void GetMemUsage (void)
{
unsigned char rawData[512];

memset(rawData, 0, sizeof(rawData));
fstream f;
f.open("/proc/meminfo", ios::in|ios::binary);

if (f.is_open()) {
f.read((char*) rawData, 512);
f.close();

char *ptr(0);

ptr = strstr((char*)rawData, (const char*)"MemTotal:");
if (ptr != 0) {
totalMem = atoi(ptr+strlen("MemTotal:"));
}
ptr = 0;
ptr = strstr((char*) rawData, (const char*)"MemFree:");
if (ptr != 0) {
freeMem = atoi(ptr+strlen("MemFree:"));
}
}
}

void SetBitmap(void)
{
static int init(0);

if (!init) {
memset(rawTex, 0xA0, sizeof(rawTex));
} else {
memset(rawTex, 0xFF, sizeof(rawTex));
}
init ^= 1;
}

void Display (void)
{
char txtString[120];

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texid);
glBegin(GL_TRIANGLES);

glTexCoord2f(0.0, 0.0);
glVertex2f(-1, 0);
glTexCoord2f(0.0, 1.0);
glVertex2f(-1, 1);
glTexCoord2f(1.0, 0.0);
glVertex2f(0, 0);

glTexCoord2f(0.0, 1.0);
glVertex2f(-1, 1);
glTexCoord2f(1.0, 0.0);
glVertex2f(0, 0);
glTexCoord2f(1.0, 1.0);
glVertex2f(0, 1);

glEnd();
glDisable(GL_TEXTURE_2D);

GetMemUsage();
snprintf(txtString, 120, "Total(%d) Free(%d) Per(%f)", totalMem, freeMem, freeMem / ((float) totalMem));
glRasterPos2i(0, 0);
glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
glutBitmapString(GLUT_BITMAP_HELVETICA_18, (const unsigned char*) txtString);

glutSwapBuffers();
glutPostRedisplay();
return;
}

void Keyboard(unsigned char key, int x, int y)
{
switch(key) {
case 'n':
SetBitmap();
glBindTexture(GL_TEXTURE_2D, texid); // Bind the texture
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2048, 2048, GL_RGB, GL_UNSIGNED_BYTE, rawTex);
break;
default:
break;
}
return;
}

void InitTexture (void)
{
// Initially Load the texture
glGenTextures(1, &texid); // Creates the texture
glBindTexture(GL_TEXTURE_2D, texid); // Bind the texture
/*
* Set edge handling
*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
/*
* Set filtering
*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

/*
* Upload the texture to GL
*/
glTexImage2D(GL_TEXTURE_2D, // 2D Texture
0, // level
GL_RGB, // internal formal
2048, // width
2048, // height
0, // border
GL_RGB, // format
GL_UNSIGNED_BYTE, // type
rawTex); // data
}

int main (int argc, char *argv[])
{
GLint mainWindow;

glutInit (&argc, argv);
SetBitmap();

glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

glutInitWindowSize (1024, 768);
mainWindow = glutCreateWindow("Texture Test");

glutDisplayFunc(Display);
glutKeyboardFunc(Keyboard);

glShadeModel(GL_SMOOTH);
glEnable(GL_BLEND);
glEnable(GL_NORMALIZE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glCullFace(GL_FRONT);
InitTexture();

glutPostRedisplay();
glutMainLoop();

cout << "Freeing Memory!" << endl;
glDeleteTextures(1, &texid);

return 0;
}

V-man
05-03-2013, 12:25 PM
You are using glTexSubImage2D correctly. I don't know if /proc/meminfo reports driver memory usage but it is possible. How much is the memory leak every time you call it?
2048x2048x3 or 2048x2048x4?

Aumnayan
05-03-2013, 12:58 PM
It's ranging between 0 and 16,772 bytes, so it doesn't appear to be correlated to the size of the texture. <edit, I just saw it jump up a little over 1MB on a 'n' press, so I really don't know where the size of the mem leak is coming from>

It's looking like the memory leak is stemming from the ati drivers the device my app is running on is using. I'm examining some valgrind results at the moment to figure out more about it, while working on a work around. So far, deleting the texture then recreating it from scratch doesn't clear up the problem.

mhagain
05-04-2013, 07:46 AM
Using GL_RGB for a TexSubImage call, your driver must allocate a new buffer, expand (and possibly reswizzle) your data to 32-bit into that new buffer, then transfer that new buffer to your GPU. Making a wild guess, your driver is failing to properly free the new buffer when done; it may not be even leaking - it may just be keeping that buffer around in case it needs to reuse it later in order to save the cost of a new memory allocation sometime in the future.

In general, you should use GL_BGRA for the format param of TexSubImage calls; this is more likely to match what the driver and hardware are actually using internally, and get you a direct transfer without the intermediate software step. You may or may not also need to use GL_UNSIGNED_INT_8_8_8_8_REV for the type param. It's worth making this switch to see if your observed leak goes away, and as a bonus it will get you higher performance for the data transfer itself.

See further here: http://www.opengl.org/wiki/Common_Mistakes#Texture_upload_and_pixel_reads


The driver will likely not have to perform any CPU-based conversion and DMA this data directly to the video card.