Question

This is more out of curiosity than for any practical purpose: is there anything in the OpenGL specification that suggests that calling glTexImage2D many times (e.g., once per frame) is illegal? I mean illegal as in 'it could produce wrong results', not just inefficient (suppose I don't care about the performance impact of not using glTexSubImage2D instead).

The reason I'm asking is that I noticed some very odd artifacts when drawing overlapping, texture-mapped primitives that use a partly-transparent texture which is loaded once per every frame using glTexImage2D (see the attached picture): after a few seconds (i.e., a few hundred frames), small rectangular black patches appear on the screen (they're actually flipping between black and normal between consecutive frames).

Artifacts

I'm attaching below the simplest example code I could write that exhibits the problem.

#include <stdio.h>

#ifndef __APPLE__
# include <SDL/SDL.h>
# include <SDL/SDL_opengl.h>
#else
# include <SDL.h>
# include <SDL_opengl.h>
#endif

/* some constants and variables that several functions use */
const int width = 640;
const int height = 480;
#define texSize 64

GLuint vbo;
GLuint tex;

/* forward declaration, creates a random texture; uses glTexSubImage2D if 
   update is non-zero (otherwise glTexImage2D) */
void createTexture(GLuint label, int update);

int init() 
{
  /* SDL initialization */
  if (SDL_Init(SDL_INIT_VIDEO) < 0)
    return 0;

  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  if (!SDL_SetVideoMode(width, height, 0, SDL_OPENGL)) {
    fprintf(stderr, "Couldn't initialize OpenGL");
    return 0;
  }

  /* OpenGL initialization */
  glClearColor(0, 0, 0, 0);
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, width, height, 0, -1, 1);
  glMatrixMode(GL_MODELVIEW);

  /* creating the VBO and the textures */
  glGenBuffers(1, &vbo);
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glBufferData(GL_ARRAY_BUFFER, 1024, 0, GL_DYNAMIC_DRAW);

  glGenTextures(1, &tex);
  createTexture(tex, 0);

  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_TEXTURE_COORD_ARRAY);

  return 1;
}

/* draw a triangle at the specified point */
void drawTriangle(GLfloat x, GLfloat y)
{
  GLfloat coords1[12] = {0, 0, 0, 0, /**/200, 0, 1, 0, /**/200, 150, 1, 1};

  glLoadIdentity();
  glTranslatef(x, y, 0);

  glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(coords1), coords1);
  glVertexPointer(2, GL_FLOAT, 4*sizeof(GLfloat), (void*)0);
  glTexCoordPointer(2, GL_FLOAT, 4*sizeof(GLfloat),
    (char*)0 + 2*sizeof(GLfloat));

  glDrawArrays(GL_TRIANGLES, 0, 3);
}

void render()
{
  glClear(GL_COLOR_BUFFER_BIT);

  drawTriangle(250, 50);

  createTexture(tex, 0);
  drawTriangle(260, 120);

  SDL_GL_SwapBuffers();
}

void cleanup()
{
  glDeleteTextures(1, &tex);
  glDeleteBuffers(1, &vbo);

  SDL_Quit();
}

int main(int argc, char* argv[])
{
  SDL_Event event;

  if (!init()) return 1;

  while (1) {
    while (SDL_PollEvent(&event))
      if (event.type == SDL_QUIT)
        return 0;

    render();
  }

  cleanup();

  return 0;
}

void createTexture(GLuint label, int update)
{
  GLubyte data[texSize*texSize*4];
  GLubyte* p;
  int i, j;

  glBindTexture(GL_TEXTURE_2D, label);

  for (i = 0; i < texSize; ++i) {
    for (j = 0; j < texSize; ++j) {
      p = data + (i + j*texSize)*4;
      p[0] = ((i % 8) > 4?255:0);
      p[1] = ((j % 8) > 4?255:0);
      p[2] = ((i % 8) > 4?255:0);
      p[3] = 255 - i*3;
    }
  }

  if (!update)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texSize, texSize, 0, GL_RGBA,
      GL_UNSIGNED_BYTE, data);
  else
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texSize, texSize, GL_RGBA,
      GL_UNSIGNED_BYTE, data);

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

Notes:

  1. I'm using SDL, but I've seen the same happening in wxWidgets, so it's not an SDL-related problem.

  2. If I use glTexSubImage2D instead for every frame (use update = 1 in createTexture), the artifacts disappear.

  3. If I disable blending, there are no more artifacts.

  4. I've been testing this on a late 2010 MacBook Air, though I doubt that's particularly relevant.

Was it helpful?

Solution

This clearly an OpenGL implementation bug (just calling glTexImage2D in a loop should not cause this to happen).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top