1

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.

Legendre17
  • 621
  • 5
  • 13
  • Just out of interest ... if you move the createTexture to BEFORE the first drawTriangles ... does it work? (I suspect a bug in the driver's texture sync paths) – Goz Dec 30 '12 at 16:59
  • @Goz The issue disappears if I move `createTexture` before both `drawTriangle`s. I guess I hadn't really considered a bug in the driver... I'll try the code on a couple of other systems when I get a chance. – Legendre17 Dec 30 '12 at 20:25
  • Basically it looks like the system is updating the texture while it is still in use by the previous draw call. I'd guess that turning off alpha blend affects the render paths to the point that the texture change is not happening WHILE the previous draw is in progress. Having written drivers in the past its a pretty elementary (and REALLY easy to make) mistake. Your best bet is to work around it and accept that its out of your control. Still worth posting the bug to apple in the hope that they get the driver fixed! – Goz Dec 31 '12 at 00:46
  • I tested this on a Linux machine, and the artifacts do not appear, supporting the idea that this might be a bug in the driver. Interestingly enough, the artifacts also don't appear when I run the program under Linux in a virtual machine on my macbook air... – Legendre17 Jan 05 '13 at 21:53

1 Answers1

1

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

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • That's a good point, this could just be a bug. It would be hard to notice because most applications call `glTexImage2D` much less often than once a frame... I'll try this on a few more systems to see how reproducible it is. – Legendre17 Dec 30 '12 at 23:26
  • This is probably the right answer. To get some support for it, I tried the program on a different machine (running Ubuntu Linux), and couldn't see the artifacts. Moreover, the artifacts disappear even when running the program in a virtual box on the Macbook Air (the virtual machine running Ubuntu Linux, as well). – Legendre17 Jan 05 '13 at 21:54