BufferedImage backed by a ByteBuffer

hi

I am reviewing my code for a BufferedImage backed by a DirectByteBuffer. I need it to use AWT drawing code to directly draw onto OpenGL texture data.

The problem is, that it is about 4 times slower than using a standard BufferedImage.

Could possibly someone have a look at my code and tell me, if there is anything, that I should do differently?

This is the class in question:
http://jagatoo.svn.sourceforge.net/viewvc/jagatoo/trunk/src/org/jagatoo/image/DirectBufferedImage.java?view=markup

And this is my testcase:
http://jagatoo.svn.sourceforge.net/viewvc/jagatoo/trunk/test/src/org/jagatoo/test/util/image/DirectBufferedImageTest.java?view=markup

Thank you very much in advance.

Marvin

That’s to be expected since the BufferedImage won’t be accelerated when you directly manipulate the data behind it. You could copy it to another image and then draw that one onto whatever, since it will be accelerated, but I don’t think that will help you since it will be still be doing the non-accelerated copy onto the intermediate image.

I hope that helped.
Keith

Well, this is my current solution and I am not happy with it. But if it doesn’t work another way, it has to be that way. What a shame. Sun should have implemented accelerated DirectByteBuffer BufferedImages.

Thanks for your replay, Keith.

Marvin


  200         // create a standard sRGB color space
  201         ColorSpace cs = ColorSpace.getInstance( ColorSpace.CS_sRGB );

IIRC, sRGB does some color transformations… (?)

Does using ColorSpace.CS_LINEAR_RGB affect performance?

The problem is that Java2D doesn’t have built-in support for these types of images and has to work with them on pixel by pixel basis (getRGB/setRGB), which is very slow (even slower if you have to do color space conversion). We have an RFE for adding support for images backed by nio buffers, but haven’t had the time to implement it (it’s not trivial to make sure they work with existing infrastructure).

Dmitri

If only we could pin down a byte[] in memory! :-\

Then it would be trivial to make a byte[] and ByteBuffer point to the same memory location. I implemented it, it works, until the GC comes along…

It would make all your problems disappear Dmitri, why don’t you poke some folks in the JVM team :wink:

And if you do that, you could tell them to implement unsigned byte, too :).

Marvin

Can you explain what you mean? If GC is involved then ISTM that you should be able to work around it with a HashMap.

The moment you read back the data at the native pointer, the byte[] may not be there anymore (the GC can kick in at ‘any’ time), and the space can be filled with new objects. It can even result in a ACCESS_VIOLATION (crash!) if the heap has shrunk (yes! the heap shrinks these days!).

So passing your native pointer (or DirectByteBuffer for that matter), that is referencing anything on the heap, to OpenGL is never safe.


public class NativeSharedFloat
{
   public static void main(String[] args)
   {
      int len = 1024;
      long headerSize = NativeHacks.OBJECT_ARRAY_BASE_OFFSET;
      long pntr = NativeAllocator.malloc(headerSize + len * 4);

      float[] arr = NativeHacks.createFloatArrayAt(pntr, len);
      FloatBuffer buf = NativeHacks.createFloatBufferAt(pntr + headerSize, len);

      System.out.println("arr.length=" + arr.length);
      System.out.println("buf.capacity=" + buf.capacity());

      System.out.println();
      System.out.println("arr[0] = " + arr[0]);
      System.out.println("buf(0) = " + buf.get(0));

      arr[0] = 12.34f;

      System.out.println();
      System.out.println("arr[0] = " + arr[0]);
      System.out.println("buf(0) = " + buf.get(0));

      createGarbageAndCleanup();

      arr[0] = 23.45f;

      System.out.println();
      System.out.println("arr[0] = " + arr[0]);
      System.out.println("buf(0) = " + buf.get(0));
   }

   private static void createGarbageAndCleanup()
   {
      String[] arr = new String[1024 * 1024];
      for (int i = 0; i < 1024; i++)
      {
         for (int k = 0; k < 1024; k++)
         {
            arr[i * 1024 + k] = String.valueOf(i) + String.valueOf(k);
         }
      }

      for (int i = 0; i < 1024; i++)
      {
         for (int k = 0; k < 1024; k++)
         {
            arr[k * 1024 + i] = String.valueOf(i) + String.valueOf(k);
         }
      }

      // ensure 'arr' cannot be optimized away
      int h = Arrays.hashCode(arr) % 2;

      long usedBefore = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
      for (int i = 0; i < (8 + h); i++)
      {
         System.gc();
      }
      long usedAfter = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
      System.out.println("collected garbage: " + (usedBefore - usedAfter) / 1024 + "KB");
   }
}

Output:


arr.length=1024
buf.capacity=1024

arr[0] = 0.0
buf(0) = 0.0

arr[0] = 12.34
buf(0) = 12.34
collected garbage: 62841KB

arr[0] = 23.45
buf(0) = 12.34

No, unfortunately it doesn’t.

For now I just live with the performance drawback and do as much of the drawing directly on the ByteBuffer, so that the slow Graphics2D drawing doesn’t do too much.

But now I have a new problem. When I use clipping in combination with a DirectBufferedImage, all drawn strings get corrupted or even completely clipped away glyph by glyph. I have tested it on my own Linux box with Java 1.5 and 1.6 and a friend tested it on his Linux box and on Windows with 1.5 and 1.6. It is all the same. Another friend tested it on his mac with Java 1.6 and it worked.

It really looks like a bug in the JDK. But maybe and hopefully I made something bad. I have stripped down my code to a minimum and assembled a small testcase (really not much code). I would be very cool, if someone could have a look at the code to tell me, if I am doing something wrong or if it is a bug in the JDK.

The attached screenshot shows the corrupted text as seen on Linux and Windows.

Marvin

Vista 64bit
JRE 1.6.0_04

The result is exactly like the screenshot you posted.

Did you have a look at my code? Could you see anything wrong in it?

Marvin

Mind if I ask what this is for? If rendering text with Java2D, you might check out the new Hiero in Slick.

I need it to render HUD Widgets in Xith3D. I found a workaround to proceed my work. But I don’t like it.

Would you mind having a look at the JAGaToo project, Nate? I know, the project’s description on the project page is very short. But you might want to check it out from SVN and have a look at the code. It provides many tools and abstract libs, that you will want to use in your project. (You won’t want to reinvent the wheel, do you? ;)) For example the InputSystem is extremely advanced as well as the TextureLoader. It also has a complete CommandLine parser and many, many more tools.

Marvin