Graphics2D.drawImage() is my bottleneck

do you mean :-

drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) ?

I havn’t ever needed to use that.
However, the symptoms you state do fit the possibility that your 2 rectangles are different dimensions from 1 another.

This would result in your image drawing being scaled.
As scaling isn’t currently HW accelerated, this would force the JVM to use the software rendering loops.

Using BufferedImages, it would simply be slow, because of no HW acceleration.
Using VolatileImages it would be very very very slow, as each image would have to be read back from vram, scaled, and then blitted to the destination Image (which is probably back in vram)

So yes, using that drawImage is probably the cause of your problems.

p.s.

There is a flag to enable HW accelerated scaling.
something.something.ddscale=true I believe.

yep that’s the one. Thanks for the explanation, it makes sense why VolatileImages would be slow in that case, thanks!

sun.java2d.ddscale = true has been set! Thanks for that tip :slight_smile:

I was hoping it would help speed up my AffineTransform translations, but unfortunately it hasn’t. I’m pretty much doing everything horribly bad I could do in one fell swoop on this one particular effect. I’m rotating a highly translucent image and scaling it differently every frame for my Trekky jump effect :slight_smile:

The ddscale doesn’t seem to help that at all. I still go from a steady 60fps (on the standalone version the fps is VERY constant) to 5 fps when the jump flash occurs.

Oh, one thing I did do good. One source of translucency problems was in my stars being drawn on the background. However, since they never move, I was able to simulate the background being in the correct place and drawing it onto an OPAQUE image the size of the star, and then drawing the star on top of that. Now my planets/stars use OPAQUE BufferedImages instead of Translucent ones. That’s helped a lot!

Well I decided to try using VolatileImages without scaling them to see if that would make a difference, and as it turns out, VolatileImages show themselves to get slower every time they’re drawn. Something HAS to be wrong here.

Here are benchmarks of calling my draw method 1000 times with OPAQUE BufferedImages:

6x6      8x8      4x4      2x2      1x1
6547      7281      5984      5234      4844
6719      7781      6391      5234      4766
6875      7594      6531      5406      4875
6813      7594      6234      5344      4844
7000      8000      6218      5453      4813
7313      7594      6203      5203      4875
              7688      6344      5516      4906

As you can see, my efforts to help them cache in vram by chopping them into smaller images actually made things slower. To clarify, Number x Number is the array size of my image. So 1x1 means 1 512x512 image. 8x8 means a whole lot of 64x64 images.

Here’s how things turned out with VolatileImages:

1x1      4x4      8x8
 4859       5469      8500
56500      74297
73937

In this case every time I called the method things got amazingly slower, from the first call being normal (under 1 second), to the next call being about a minute long. I’m not modifying the images between calls in any way. The only access done to the images are a getWidth(), getHeight() and then drawImage(image, x, y, null); As you can imagine with the first result being 8500ms, I wasn’t about to see how that time multiplied on the next call.

There’s no scaling, no alpha, just straight drawing. Why would VolatileImage not only be slower than BufferedImage, but actually get worse as time progresses? More importantly, how do I fix this?

what are you drawing onto? a Graphics context obtained from the current BufferStrategy?

I’m drawing onto a Graphics2D object obtained from a VolatileImage obtained from the GraphicsEnvironment…createCompatibleVolatileImage method. Ultimately that image gets drawn onto the Graphics from the Applet.getGraphics() call. Both of these facts are true for all of the benchmark results I posted.

hmm, without seeing the code - Im not sure I can see where the problem is occuring.

However, 1 thing you should try.

Add a canvas to the Applet, and on the Canvas, use createBufferStrategy(2).

Then try blitting onto the graphics context obtained from getBufferStrategy().getDrawGraphics().

That may give different results.
If it does, it means something is going pear-shaped when trying to blit onto VolatileImages.

Also, make sure you are performing no transforms of any kind, and that the source image does not have any kind of an alpha channel. (though BITMASK should be ok)

p.s. what acceleration flags do you have set?
In 1.4.2 some of them have undesirable side effects (that maybe the cause of Volatile->Volatile blits losing acceleration)

p.s. what is your gfx card? does it fully support DirectX?
That could be another problem (though rather unlikely)

What sort of performance do you get with Balls.jar?

I tried your suggestion with the canvas, but it didn’t change the benchmarks for BufferedImage or VolatileImage at all.

I have the flags: “sun.java2d.translaccel” and “sun.java2d.ddscale” and I tried turning them both on and off, but neither case made any difference either.

The surface I’m drawing onto was a VolatileImage, so no alpha there. Then when on the canvas it was going onto the BufferStrategy. My source image drawing onto it has no alpha, it’s coming from an OPAQUE BufferedImage, and I’m using the drawImage(image, x, y, null); method. I draw onto it once, and never again.

My graphics card is an Intel 82852/82855 GM/GME Graphics Controller on a Toshiba laptop. It’s got 64 megs of graphics memory. It has full OpenGL support but I’m not sure about DirectX. All the DX diagnostic tests run successfully though.

Balls could keep at 50fps with about 2100 balls with VolatileImages, but only about 1500 balls with Managed Images on 800x600 32bit. With ImageIO about 700, Unaccelerated about 800, and Masked Managed about 1800.

ok, well most of the code for Balls is included in the jar (though I missed out a few of the utility classes unfortunetly)

You need to have a look what your program is doing differently to mine.

Unless your GameFrame class is doing something different, I can find no differences. I took everything from Balls I could make use of, anything that ever did anything to the Graphics2D Object I did the same to. I even set the AlphaComposite.SrcOver in that you had where you had it.

I try printing out the capabilities after I create my images and I get that isAccelerated = true and isTrueVolatile = true on every frame of animation. What else can I try? VolatileImage is still very slow and BufferedImage is fast but just not fast enough. I even tried setting extra RenderingHints for speed but found no change.

Here’s from creation to usage of my VolatileImages:

back = new VolatileImage[1][1];
for (int i = 0; i < back.length; i++) {
      for (int j = 0; j < back.length; j++) {
            back[i][j] = GLUIUtil.createVI(Constants.SYSTEM_TILE_WIDTH/back.length,
                        Constants.SYSTEM_TILE_HEIGHT/back.length);
      }
}
public VolatileImage GLUIUtil.createVI(int width, int height) {
      return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().createCompatibleVolatileImage(width, height);
}
//In drawing, which only ever happens once:
Graphics2D g2d = back[i][j].createGraphics();
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(tile, i*back[0][0].getWidth()/back.length, j*back[0][0].getHeight()/back[0].length, null);

g2d.dispose();

ImageCapabilities ic = back[i][j].getCapabilities();
System.out.println("isAccelerated = " + ic.isAccelerated() + "\n" +
      "isTrueVolatile

//Then finally when I use them:
int i, j, frameX = 0, frameY = 0;
int w = Constants.GAME_WIDTH;
int h = Constants.GAME_HEIGHT;
int tW = back[0][0].getWidth();
int tH = back[0][0].getHeight();

g2d.setComposite(AlphaComposite.SrcOver);

int startX, startY, width, height, sourceX, sourceY;

for (i = bgStartX-Constants.SYSTEM_TILE_WIDTH; i < w; i += tW) {
      for (j = bgStartY-Constants.SYSTEM_TILE_HEIGHT; j < h; j += tH) {
            
            startX = Math.max(0, i);
            if ((width = Math.min(tW - (startX - i), w - startX)) > 0) {
                  
                  startY = Math.max(0, j);
                  if ((height = Math.min(tH - (startY - j), h - startY)) > 0) {
                        
                        g2d.drawImage(back[frameX][frameY], i, j, null);
                  }
            }
            
            frameY = (frameY + 1)%back.length;
      }
      frameY = 0;
      
      frameX = (frameX + 1)%back.length;
}

And that’s all that ever touches them.