Memory leak while uploading textures

I’ve been wrestling with a memory leak problem and have it traced to the method I am using to upload & update textures. I wrote a small program in order to test this theory, and sure enough I can watch the system memory be eaten by the process until the system can no longer allocated resources to other processes. The full test program is included at the bottom, but my primary consern is the function which updates textures:


void textures::uploadData(unsigned short *_data)
{
  if (glIsTexture(tID)) {
    glBindTexture(GL_TEXTURE_2D, tID);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 
        0, 0, 
        TEXWIDTH, TEXHEIGHT, 
        GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 
        _data);
  } else {
    glBindTexture(GL_TEXTURE_2D, tID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, 
        GL_RGBA, TEXWIDTH, TEXHEIGHT, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, _data);
  }
}

Is there an error in the above code that would cause this?

Full code below:


// compile with -lglut -lGLU -lpthread
#define GL_GLEXT_PROTOTYPES
#include <GL/freeglut.h>
#include <GL/glext.h>
#include <memory.h>
#include <iostream>
#include <fstream>
using namespace std;
//#include <pthread.h>

#define STRIDE 4
#define HEIGHT 4
#define TEXWIDTH 512
#define TEXHEIGHT 512

typedef struct __point {
  float x;
  float y;
} POINT;

class textures {
public:
  textures();
  void display(void);
  void setBox(POINT &_lLeft, POINT &_uRight);
  void updateImage(void);
  ~textures();
protected:
  void uploadData(unsigned short *_data);
private:
  unsigned char pattern;
  int        dir;
  GLuint     tID;
  POINT      lLeft;
  POINT      uRight;
};

textures::textures(void) 
{
  unsigned short *pData = new unsigned short[TEXWIDTH * TEXHEIGHT];
  pattern = 0x00;
  dir = 1;
  memset((char*) pData, 0, sizeof(unsigned short) * TEXWIDTH * TEXHEIGHT);
  glGenTextures(1, &tID);
  uploadData(pData);
  delete []pData;
}

void textures::updateImage(void)
{
  if (!pattern) {
    pattern = 0x01;
    dir = 1;
  } if (pattern >= 0x0F) {
    pattern = 0x0E;
    dir = -1;
  } else {
    pattern = (pattern + dir);
  }
  
  if (pattern > 0xF) {
    pattern = 0xE;
    dir = -1;
  } 
  
  unsigned short *pData = new unsigned short[TEXWIDTH * TEXHEIGHT];
  unsigned short stamp;

  stamp = (pattern << 4) | pattern;
  stamp = stamp << 8;
  stamp |= (pattern << 4) | 0x0F;
  
  for (int i(0); i < TEXHEIGHT; i++) {
    for (int j(0); j < TEXWIDTH; j++) {
      pData[(i * TEXWIDTH) + j] = stamp; 
    }
  }

  uploadData(pData);
  delete []pData;  
}

textures::~textures(void)
{
  if (glIsTexture(tID)) {
    glDeleteTextures(1, &tID);
  }
}

void textures::setBox(POINT &_lLeft, POINT &_uRight)
{
  memcpy((char*) &lLeft, (char*) &_lLeft, sizeof(lLeft));
  memcpy((char*) &uRight, (char*) &_uRight, sizeof(uRight));
}

void textures::display(void)
{
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, tID);
  
  glBegin(GL_TRIANGLES);
  glTexCoord2f(0, 0); glVertex2f(lLeft.x, lLeft.y);
  glTexCoord2f(0, 1); glVertex2f(lLeft.x, uRight.y);
  glTexCoord2f(1, 0); glVertex2f(uRight.x, lLeft.y);

  glTexCoord2f(1, 1); glVertex2f(uRight.x, uRight.y);
  glTexCoord2f(0, 1); glVertex2f(lLeft.x, uRight.y);
  glTexCoord2f(1, 0); glVertex2f(uRight.x, lLeft.y);
  glEnd();
  
  glBindTexture(GL_TEXTURE_2D, 0);
  glDisable(GL_TEXTURE_2D);
}

void textures::uploadData(unsigned short *_data)
{
  if (glIsTexture(tID)) {
    glBindTexture(GL_TEXTURE_2D, tID);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 
        0, 0, 
        TEXWIDTH, TEXHEIGHT, 
        GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 
        _data);
  } else {
    glBindTexture(GL_TEXTURE_2D, tID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, 
        GL_RGBA, TEXWIDTH, TEXHEIGHT, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, _data);
  }
}

int mainWindowHD;
textures    *_toDisplay[HEIGHT][STRIDE];

int totalMem, usedMem, freeMem;

void UpdateMem (void)
{
  fstream file;

  totalMem = 0;
  usedMem = 0;
  freeMem = 0;
  
  file.open("/proc/meminfo", ios::in | ios::binary);
  if (file.is_open()) {
    char buffer[1024];
    file.read(buffer, 512);
    
    char *ptr;
    ptr = strstr(buffer, "MemTotal:");
    if (ptr) {
      totalMem = atoi(ptr + strlen("MemTotal:"));
    }
    
    ptr = strstr(buffer, "MemFree:");
    if (ptr) {
      freeMem = atoi(ptr + strlen("MemFree:"));
    }
    
    usedMem = totalMem - freeMem;
    
    file.close();
  }
}

void Display (void)
{
  static bool init(false);
  static int y(0);
  static int x(0);
  
  float maxw(400.0 / (1024.0 / 2.0));
  float maxh(300.0 / (768.0 / 2.0));
  UpdateMem();
  
  if (!init) {
    for (int i(0); i < HEIGHT; i++) {
      POINT ll, ur;
      
      ll.y = -maxh + (i * (maxh / 2.0));
      ur.y = -maxh + ((i + 1) * (maxh / 2.0));
      
      for (int j(0); j < STRIDE; j++) {
        ll.x = -maxw + (j * (maxw / 2.0));
        ur.x = -maxw + ((j + 1) * (maxw / 2.0));
        
        _toDisplay[i][j] = new textures;
        _toDisplay[i][j]->setBox(ll, ur);
      }
    }
    
    init = true;
  }
  
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glColor3f(1.0, 1.0, 1.0);
  
  for (int i(0); i < HEIGHT; i++) {
    for (int j(0); j < STRIDE; j++) {
      _toDisplay[i][j]->display();
    }
  }
  
  _toDisplay[y][x++]->updateImage();

  if (x >= STRIDE) {
    x = 0;
    y = (y + 1) % HEIGHT;
  }
  
  char MemUsage[80];
  snprintf(MemUsage, 80, "Total: %0.2f Used: %0.2f Free %0.2f",
      totalMem / 1024.0, usedMem / 1024.0, freeMem / 1024.0);
  
  glColor3f(1.0, 1.0, 1.0); 
  glRasterPos2f(0.0, -maxh - ((1.0 - maxh) / 2.0));
  glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, (const unsigned char*)MemUsage);
  
  glutSwapBuffers();
  glutPostRedisplay();
}

int main (int argc, char *argv[]) 
{
  glutInit(&argc, argv);
  glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
  
  glutInitWindowSize(1024, 768);
  mainWindowHD = glutCreateWindow("Testing screen");
  
  glutDisplayFunc(Display);
  
  glShadeModel(GL_SMOOTH);
  glEnable(GL_BLEND);
  glEnable(GL_NORMALIZE);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glCullFace(GL_FRONT);
  
  glutPostRedisplay();
  glutMainLoop();
  
  for (int i(0); i < HEIGHT; i++) {
    for (int j(0); j < STRIDE; j++) {
      delete _toDisplay[i][j];
    }
  }
}