Volatile/Buffered Images

I'm drawing my screen background on a BufferedImage, and then copying it over to a VolatileImage, which is then drawn to the screen.

My problem is... when I first render the BufferedImage, it whips right through... No problem whatsoever. But when I write over the data, it's painfully slow. Any ideas why this might be?

[334 byte] By [cbisbee] at [2007-9-27 16:45:41]
# 1

Hard to tell what the problem is without code. But here is a hint. Try using GraphicsConfiguration.createCompatibleImage() or Component.createImage(). I've heard that the resulting "BufferedImage" actually has a hardware accelerated cache associated with it, and thus provides most of the benefits of a volatile image without the headaches, thus you should be able to eliminate the copy step that is causing your problems.

Go to www.javagaming.org and do some searches on the forums for "automatic images" to see some discussions about this.

rgeimer at 2007-7-6 1:03:45 > top of Java-index,Other Topics,Java Game Development...
# 2

Thanks for the tip... But it didn't fix anything. I've also posted a similar message on those boards, but I'll put my code up here too.

From my PiratesMap class:

public void paintComponent(Graphics g){

Graphics2D g2=(Graphics2D)g;

//find the x-y coordinates of the ship, make them positive, and find the upper left corner of the

// display screen

xco=32767+myShip.getSp().getXco()-d.width/2;

yco=32767+myShip.getSp().getYco()-d.height/2;

//determine the number of pixels wide the top and left boxes need to be

int xoffset=-Math.abs(xco%squarewidth);

int yoffset=-Math.abs(yco%squareheight);

//find which 32x32 megapixel is in the top left corner

int xsquare=(xco%3072)/squarewidth+16;

int ysquare=(yco%3072)/squareheight+16;

paintToScreen(g2, xsquare, ysquare, NumVisCols, NumVisRows, xoffset, yoffset);

g2.dispose();

}

void paintToScreen(Graphics2D g2, int x, int y, int w, int h, int xoff, int yoff){

//paints the background map to the screen

g2.drawImage(((BufferedImage)mapimage).getSubimage(x , y, w, h),

new AffineTransform(32,0,0,32,xoff,yoff),null);

}

void copyMaps(MapLoader ml){

mapimage=gc.createCompatibleImage(160, 160);

Graphics2D g2=(Graphics2D)mapimage.getGraphics();

g2.drawImage(ml.getMapimage(),0,0,null);

g2.dispose();

System.out.println("Should've copied");

}

And from my MapLoader class:

private void copyMap(){

//draw the map data to an image

Graphics2D g=(Graphics2D)holderimage.getGraphics();

for(int x=0;x<112;x++){

for(int y=0;y<112;y++){

g.setPaint(c[map[0].xy[x][y]]);

System.out.println(x+" "+y);

g.draw(new Rectangle2D.Double(x,y,1,1));

}

}

for(int x=0;x<48;x++){

for(int y=0;y<112;y++){

g.setPaint(c[map[2].xy[x+16][y]]);

g.draw(new Rectangle2D.Double(x+112,y,1,1));

}

}

for(int x=0;x<112;x++){

for(int y=0;y<48;y++){

g.setPaint(c[map[1].xy[x][y+16]]);

g.draw(new Rectangle2D.Double(x,y+112,1,1));

}

}

for(int x=0;x<48;x++){

for(int y=0;y<48;y++){

g.setPaint(c[map[3].xy[x+16][y+16]]);

g.draw(new Rectangle2D.Double(x+112,y+112,1,1));

}

}

System.out.println("ending draw");

g.dispose();

}

public Image getMapimage(){

//return the map

ready=false;

loading=false;

return holderimage;

}

You may note the printlns in my copyMap method... These are for debugging, and they're telling me what my problem is... The first map I draw is no problem, it whips right through. But on the second drawing of the map (when mapimage is written over by copyMap), it's PAINFULLY slow.

So I have two questions:

A) My theory is that somehow the second writing of the map is somehow going into vram, or some other similarly slow writing technique. How do I fix this, or if I'm wrong, what is the problem?

B) Am I totally on the wrong track here? Is there a better way to do this?

cbisbee at 2007-7-6 1:03:45 > top of Java-index,Other Topics,Java Game Development...
# 3

Hmmm. Im wondering if you are running into the situation outlined in the following javagaming.org thread:

http://www.javagaming.org/discus/messages/27/1608.html?1030358070

To summarize, mapimage is being created in the "eden" space, but since it is not nulled before the second image is assigned to mapimage, the old image gets moved to the survivor generation (an expensive operation) since both images cannot fit into the eden space. This is just a theory of course, and I could be wrong. The way to test would be to either increase the size of the eden space using the -Xmn command line option, or adding something like the following to your code:

mapimage=null; // allow for the old image to be garbage collected

mapimage=gc.createCompatibleImage(160, 160);

You might also want to run with the -verbose:gc command to see if garbage collection is slowing you down.

Also, if this theory is correct, then your image creation should speed up again over time, since hotspot is supposed to dynamically resize the eden space.

rgeimer at 2007-7-6 1:03:45 > top of Java-index,Other Topics,Java Game Development...
# 4

First off, let me thank you for the hint on "automatic" images. That's been a big help.

But, I found my problem, and it has nothing to do with what I thought it was. Turns out, I'm having multithreading issues. The first time the map was loaded I had the main thread and the drawing thread. I then had three MapLoader threads taking care of loading the horizontally, vertically, and diagonally adjacent maps. Turns out, running even one MapLoader in a separate thread is trashing the performance. Probably has something to do with my animation thread eating most of the processor time... Guess I'd better read up on multithreading instead.

cbisbee at 2007-7-6 1:03:45 > top of Java-index,Other Topics,Java Game Development...