3

I'm altering someone else's code. They used PNG's which are loaded via BufferedImage. I need to load a TGA instead, which is just simply a 18 byte header and BGR codes. I have the textures loaded and running, but I get a gray box instead of the texture. I don't even know how to DEBUG this.

Textures are loaded in a ByteBuffer:

final static int datasize = (WIDTH*HEIGHT*3) *2; // Double buffer size for OpenGL // not +18 no header
static ByteBuffer buffer = ByteBuffer.allocateDirect(datasize);

FileInputStream fin = new FileInputStream("/Volumes/RAMDisk/shot00021.tga");
FileChannel inc = fin.getChannel();

inc.position(18); // skip header

buffer.clear(); // prepare for read
int ret = inc.read(buffer);
fin.close();

I've followed this: [how-to-manage-memory-with-texture-in-opengl][1] ... because I am updating the texture once per frame, like video.

Called once:

GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);

GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, width, height, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
assert(GL11.GL_NO_ERROR == GL11.glGetError());

Called repeatedly:

GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, width, height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, byteBuffer);
assert(GL11.GL_NO_ERROR == GL11.glGetError());

return textureID;

The render code hasn't changed and is based on:

GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, this.vertexCount);
PLG
  • 448
  • 5
  • 21

4 Answers4

2

Make sure you set the texture sampling mode. Especially min filter: glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR). The default setting is mip mapped (GL_NEAREST_MIPMAP_LINEAR) so unless you upload mip maps you will get a white read result.

So either set the texture to no mip or generate them. One way to do that is to call glGenerateMipmap after the tex img call.

(see https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexParameter.xml).

It's a very common gl pitfall and something people just tend to know after getting bitten by it a few times.

There is no easy way to debug stuff like this. There are good gl debugging tools in for example xcode but they will not tell you about this case.

starmole
  • 4,974
  • 1
  • 28
  • 48
  • Thank you @starmole, I added the calls you specified. I tried this once before, but didn't help then so I removed them again. I updated the code on my original question up top. Now, I have a black box instead. I'm using AppCode with Java, so not Xcode. – PLG May 22 '15 at 15:24
2

Debugging GPU code is always a hassle. I would bet my money on a big industry progress in this area as more companies discover the power of GPU. Until then; I'll share my two best GPU debugging friends:

1) Define a function to print OGL errors:

int printOglError(const char *file, int line)
{
    /* Returns 1 if an OpenGL error occurred, 0 otherwise. */
    GLenum glErr;
    int    retCode = 0;

    glErr = glGetError();
    while (glErr != GL_NO_ERROR) {
        printf("glError in file %s @ line %d: %s\n", file, line, gluErrorString(glErr));
        retCode = 1;
        glErr = glGetError();
    }
    return retCode;
}

#define printOpenGLError() printOglError(__FILE__, __LINE__)

And call it after your render draw calls (possible earlier errors will also show up):

GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, this.vertexCount);
printOpenGLError();

This alerts if you make some invalid operations (which might just be your case) but you usually have to find where the error occurs by trial and error.

2) Check out gDEBugger, free software with tons of GPU memory information.

[Edit]: I would also recommend using the opensource lib DevIL - its quite competent in loading various image formats.

Antiiee
  • 33
  • 8
  • Antiiee, thank you for your answer, but I don't have any errors. I just have a black texture. I will try your statements tho. =) – PLG May 29 '15 at 16:43
1

Some ideas which might cause the issue:

  • Your texture is disposed somewhere. I don't know the whole code but I guess somewhere there is a glDeleteTextures and this could cause some issues if called at the wrong time.
  • Are the texture width and height powers of two? If not this might be an issue depending on your hardware. Old hardware sometimes won't support non-power of two images.
  • The texture parameters changed between the draw calls at some other point ( Make a debug check of the parameters with glGetTexParameter ).
  • There could be a loading issue when loading the next image ( edit: or even the first image ). Check if the first image is displayed without loading the next images. If so it must be one of the cases above.
Eugene Primako
  • 2,767
  • 9
  • 26
  • 35
Felix K.
  • 6,201
  • 2
  • 38
  • 71
  • Felix, thanks for your answer. I don't glDeleteTextures at all actually. I overwrite the texture memory and call glTexSubImage2D to update the texture with the next image (of the video). Yes, the texture is power of two: 1024^2. I will check the last two points tomorrow. – PLG May 29 '15 at 19:39
  • 1
    @PLG Oh well i thought maybe it's happening at some point of the other ones code. However i see you are using OpenGL 1.1, that means no shaders are used. So i guess you are using no vertex buffers at all? Are you using deprecated functionality like ´glTexCoord´ and `glVertex`? I guess you did not changed anything there? ( A wrong setup could also cause errors ). Another error source: Did you bind the texture prior due rendering? If you changed the existing code maybe the texture didn't got bind and just causes a black texture. – Felix K. May 29 '15 at 22:19
1

Thanks to Felix, by not calling glTexSubImage2D (leaving the memory valid, but uninitialized) I noticed a remnant pattern left by the default memory. This indicated that the texture is being displayed, but the load is most likely the problem.

**UPDATE:

The, problem with the code above is essentially the buffer. The buffer is 1024*1024, but it is only partially filled in by the read, leaving the limit marker of the ByteBuffer at 2359296(1024*768*3) instead of 3145728(1024*1024*3). This gives the error:

Number of remaining buffer elements is must be ... at least ...

I thought that OpenGL needed space to return data, so I doubled the size of the buffer. The buffer size is doubled to compensate for the error.

final static int datasize = (WIDTH*HEIGHT*3) *2; // Double buffer size for OpenGL // not +18 no header

This is wrong, what is needed is the flip() function (Big THANKS to Reto Koradi for the small hint to the buffer rewind) to put the ByteBuffer in read mode. Since the buffer is only semi-full, the OpenGL buffer check gives an error. The correct thing to do is not double the buffer size; use buffer.position(buffer.capacity()) to fill the buffer before doing a flip().

final static int datasize = (WIDTH*HEIGHT*3); // not +18 no header

buffer.clear(); // prepare for read
int ret = inc.read(buffer);
fin.close();
buffer.position(buffer.capacity()); // make sure buffer is completely FILLED!
buffer.flip(); // flip buffer to read mode

To figure this out, it is helpful to hardcode the memory of the buffer to make sure the OpenGL calls are working, isolating the load problem. Then when the OpenGL calls are correct, concentrate on the loading of the buffer. As suggested by Felix K, it is good to make sure one texture has been drawn correctly before calling glTexSubImage2D repeatedly.

PLG
  • 448
  • 5
  • 21
  • Would be interesting to know different types of tests possible by manually writing to to buffer. Just R,G and B individually throughout the whole buffer is evident, but are there others that can test other situations? – PLG May 31 '15 at 05:34