speeding up texture creation

I have a C++ api which draws onto a bitmap.
the bitmap is converted to an array with pixel data in my DLL and transferred to Java using JNA.
all is fine thus far, and it is quite fast (<20ms).

now I load the image data into a jogl texture, this is really slow.
the first time i create the jogl texture like this;

img = new BufferedImage(wd, ht, BufferedImage.TYPE_INT_RGB);
texdata = TextureIO.newTextureData(img, false);
texdata.setBuffer(buffer_from_cpp);
tex = TextureIO.newTexture(texdata);

this takes ~400ms for an image sized 1400x1000.

after creation i update the texture periodically using;

texdata.setBuffer(buffer_from_cpp);
tex.updateImage(texdata);

this takes ~200ms for an image sized 1400x800.

i can understand the initial creation is slow, but i dislike the slow updating of the jogl texture while i have the data ready.
is there anything i’m doing wrong? are there ways to speedup this code?

alternatively, can i access/manipulate the jogl texture from the C++ DLL in order
to achieve more speed?

my system;
Pentium mobile 1600MHz
768MB Ram
Mobility radeon 9000

jogl debug info;
GL_VENDOR: ATI Technologies Inc.
GL_RENDERER: MOBILITY RADEON 9000 DDR x86/SSE2
GL_VERSION: 1.3.3967 WinXP Release

My advice is to go with direct NIO buffers.

  1. On the Java side, create the buffer of appropriate capacity:

final int width = 512;
final int height = 512;
IntBuffer bits = ByteBuffer.allocateDirect( 4 * width * height ).order( ByteOrder.nativeOrder() ).asIntBuffer();

  1. On the C++ side, using JNI get the native address of the direct buffer:

jobject jbits = ...; // this really is a JNI method argument
jint width = ...; // ...
jint height = ...; // ...
jlong count = env->GetDirectBufferCapacity( jbits ); // might not need this other than for asserts
int * const bits = static_cast< int* >( env->GetDirectBufferAddress( jbits ) );
if ( bits == 0 )
{
   // TODO error handling
}
// fill the bits of the image...

  1. On the Java side, create the texture manually, using OpenGL API through JOGL. Update the texture bits using glTexSubImage2D() - this allows you to pass the direct buffer!
  2. Make sure your texture format is ABGR this is what most gfx cards support natively and is a lot faster than standard RGBA.

I hope this helps! Texture uploads on an NVIDIA G80 are well in excess of 2GB/sec. If you want highest throughput then look into Pixel Buffer Objects (PBOs).

[EDIT] I just noticed your image width and height are not powers of 2 - you need to bear that in mind when using straight OpenGL API instead of TextureIO. Maybe someone who knows TextureIO better than I could comment on whether this could be combined with my above suggestions…

Taken from a web article, I noticed the Java Pipelines doesn’t support fast loading of images bigger than 150x150 averaging. That means Texture or Sprites should not overpass this limit, otherwise timing is broken. But it is ok to draw in bigger dimensions if this doesn’t use extra file loading or image transforms. ::slight_smile:

It should be possible to use TextureIO even with raw NIO Buffers. You will need to allocate the TextureData yourself and make 100% sure that all of the arguments are self-consistent, but if you do that then it should provide some convenience while not imposing significant overhead over just calling glTexSubImage2D yourself. Note though that TextureIO doesn’t yet support PBOs.

the performance problem seems to be “tex.updateImage(texdata)”, not the updating of the TextureData object using texdata.setBuffer().
i will try to create the texture using opengl, without TextureIO, like mabraham advised.

will post the results as soon as i figure out the code.

update:
i cannot get the code to work.
my new texture creation/updating:

    
  private int texture_id=-1, wd=0, ht=0;
  private Buffer buffer = null;

  //initial create
  public void init(GL gl) {
    createBuffer(); //creates the buffer and sets the widht and height. these are valid when i use the TextureIO class for creating/displaying textures

    texture_id = genTexture(gl);
    gl.glBindTexture(GL.GL_TEXTURE_2D, texture_id);    
    updateTexture(gl);
  }

  private int genTexture(GL gl) {
      final int[] tmp = new int[1];
      gl.glGenTextures(1, tmp, 0);
      return tmp[0];
  }  

  private void updateTexture(GL gl) {
    gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGB, wd, ht, 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, buffer);
    System.out.println("wd="+wd + " ht="+ht + " texture_id="+texture_id); //width and height are fine,  texture_id==1
  }  


  //drawing the texture:
  public void draw(GL gl) {
    gl.glEnable( GL.GL_TEXTURE_2D );    
    gl.glBindTexture(GL.GL_TEXTURE_2D, texture_id);

    gl.glBegin(gl.GL_QUADS);
        gl.glTexCoord2f(0.0f, 0.0f);
        gl.glVertex3f(0,  ht, 0f);
        gl.glTexCoord2f(1.0f, 0.0f);
        gl.glVertex3f(wd,  ht, 0f);
        gl.glTexCoord2f(1.0f, 1.0f);
        gl.glVertex3f(wd, 0, 0f);
        gl.glTexCoord2f(0.0f, 1.0f);
        gl.glVertex3f(0, 0, 0f);
    gl.glEnd();

    gl.glDisable( GL.GL_TEXTURE_2D );    
  }

the result is a black screen with a white rectangle, sized and located like my texture should be.
after i get this to work i will enforce 1024*1024 image sizes and try to switch to the ABGR format.

First of all, you must set the texture filtering modes - this would explain why you see nothing. The following sets nearest neighbour filtering for both magnification and minification:


gl.glTexParameteri( GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST );
gl.glTexParameteri( GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST );

Inside updateTexture(), use glTexSubImage2D() to update the texture image, (use glTexImage2D() only when you initially create the texture). This may further improve performance.

Also, before calling glTexSubImage2D() you should set the unpack buffer alignment to 1 (the default is 4, but your samples are 3 bytes R-G-B). Failure to do so will likely cause the JVM to crash, unless the width argument is always a multiple of 4.


gl.glPixelStorei( GL.GL_UNPACK_ALIGNMENT, 1 );

I hope this helps!

[EDIT] With your graphics card, I think you get somewhere near 500MB/sec texture upload speed (pretty much regardless of whether you use RGBA or BGRA on the Radeon 9000, and btw it does not support PBOs). With that in mind, uploading a 1024x1024 32-bit image (4MB) shouldn’t take more than 10ms!
[EDIT2] Turns out I was being a bit optimistic! I just ran a quick test on my good old Dell D600 (pretty much the same spec as what you use), and I measure a bit less than 20ms for uploading a 1024x1024 BGRA texture. So that’s no more than 200MB/s - a bit disappointing really given that this is actually AGP4x.

i’m almost there, i can see the texture now but it is distorted.
there is a pattern to be seen, somehow a part of the texture is repeated horizontally, but i cannot figure out why.
the wd and ht parameters both are 1024 after buffer creation.

a screenshot i made;
[img=http://img407.imageshack.us/img407/3885/textureqx0.th.jpg]

a complete post of my current code;


  private int texture_id=-1, wd=0, ht=0;
  private Buffer buffer = null;

  public void init(GL gl) {
    createBuffer(); //creates the buffer and sets the widht and height. these are valid when i use the TextureIO class for creating/displaying textures

    texture_id = genTexture(gl);
    updateTexture(gl);
    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);    
  }

  private int genTexture(GL gl) {
      final int[] tmp = new int[1];
      gl.glGenTextures(1, tmp, 0);
      return tmp[0];
  } 

  private void updateTexture(GL gl) {
    gl.glBindTexture(GL.GL_TEXTURE_2D, texture_id);
    //create. orig c++ bitmap type = DIB_RGB_COLORS
    gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGB, wd, ht, 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, buffer);
    //on update, should use this;
    //gl.glPixelStorei( GL.GL_UNPACK_ALIGNMENT, 1 );
    //glTexSubImage2D
  }

  public void draw(GL gl) {
    gl.glEnable( GL.GL_TEXTURE_2D );    
    gl.glBindTexture(GL.GL_TEXTURE_2D, texture_id);

    gl.glBegin(gl.GL_QUADS);
        gl.glTexCoord2f(0.0f, 0.0f);
        gl.glVertex3f(0,  ht, 0f);
        gl.glTexCoord2f(1.0f, 0.0f);
        gl.glVertex3f(wd,  ht, 0f);
        gl.glTexCoord2f(1.0f, 1.0f);
        gl.glVertex3f(wd, 0, 0f);
        gl.glTexCoord2f(0.0f, 1.0f);
        gl.glVertex3f(0, 0, 0f);
    gl.glEnd();

    gl.glDisable( GL.GL_TEXTURE_2D );    
  }


I’ll hazard a guess that you use the Win32 CreateDIBitmap() API. Are you quite sure that you don’t create a 32-bit format bitmap? I’m asking because the BITMAPINFO structure’s bmiColors member is of type RGBQUAD:


typedef struct tagRGBQUAD {
  BYTE    rgbBlue; 
  BYTE    rgbGreen; 
  BYTE    rgbRed; 
  BYTE    rgbReserved; 
} RGBQUAD;

If I’m right then your texture format should (incidentally) be GL_BGRA. Beware that this format requires a certain OpenGL version (1.4??) - there’s also an EXT flavour for older versions…

My C++ bitmap code;


    BITMAPINFO bmi2;
    memset(&bmi2, 0, sizeof(BITMAPINFO));
    bmi2.bmiHeader.biSize		 = sizeof(BITMAPINFOHEADER);
    bmi2.bmiHeader.biWidth		 = wd;
    bmi2.bmiHeader.biHeight		 = ht;
    bmi2.bmiHeader.biPlanes		 = 1;
    bmi2.bmiHeader.biBitCount	 = 32; //24?
    bmi2.bmiHeader.biCompression = BI_RGB;
 
    hBitmap = CreateDIBSection( memDC, &bmi2, DIB_RGB_COLORS, NULL, NULL, 0 );
    drawonbitmap();
    result = ::GetDIBits(memDC, hBitmap, 0, ht, colorBits, &bmi2, DIB_RGB_COLORS);
    //now send colorBits to java

This is a 32 bit bitmap indeed, thanks for pointing me in the right direction.
So i have set GL_RGBA and now the image is readable;

gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, wd, ht, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, buffer);

Unfortunately, it is not 100% yet.
The image is upside down and although the image is readable, the colors are different from the original.

Well you see, I pointed you in the right direction:

The layout of this isn’t compatible with GL_RGBA but GL_BGRA (hence uploading textures is faster, no swizzling involved with the latter).

And as for the image coming up upside down, just play with the coordinates of the primitive that you draw (swap y texture coords) and you’re done.

Good luck, you’re nearly there! :slight_smile:

i’ve got it, thanks a lot for your help ;D
using both texture formats i have it working now. performance is good, about ~20ms.

gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, wd, ht, 0, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, buffer);