Using less Java heap with BufferedImage

Hey guys –

I’ve got a question about the best way to approach this. You see, I have a rather ginormious background image in my level (2000 x 1000) pixels that I load as a BufferedImage. Java can handle that, but if I include too many other big images (of course I need to have foreground stuff, too), then the Java heap overflows and that’s that. Furthermore, a lot of the foreground stuff rotates, so in order to do that correctly I draw it onto a new bigger BufferedImage, which will turn a rectangle into a square via the longer dimension. That means big BufferedImages too, even if most of the image might be devoid of data.

So I’m asking if there is some way I can at least keep the background image mostly out of memory and simply render it to the screen. If I can’t do that, how can I be more efficient here? Currently, I am using TYPE_INT_ARGB for all of these.

I’m optimizing rotation so that I’m not quite so wasteful, but this still isn’t working perfectly.

I also should note that, aside from rotation, I never need to modify these images at all.

Thanks.

PS - I know how to use LWJGL, so if that’s a recommendation I can do that. I was just trying to avoid external libraries on this game.

Well I’m stating the obvious here, but can’t you split the background into tiles? And draw the background as a tilemap?

Assuming the background is a whole you could also try to load as many of the other images into VRAM using volatileimages… BufferedImage’s get cached into vram when possible (but only after some draw calls, else they reside in system memory, which also means operations like rotations are done in software). Try to keep as much images in vram as possible. Also if you draw all these images on a backbuffer (bufferedstrategy or volatileimage); background (bufferedimage first), then foreground objects (volatileimages). That way you dont need that second framebuffer (bufferedimage), plus the rotations for the foreground objects can be HW accellerated.

And if that’s still isn’t enough, you could always try to load/stream chunks of the background dynamically and change the bufferedimage’s raster when needed.

Okay, thanks for the thoughts. Will a tiled approach actually use less heap? If it does, I can absolutely tile, that’s no issue. I didn’t really see the point, as you can see most of the image at one time anyway, even on very low resolutions. If I was only drawing the tile that was on the screen at the time, at the very best case scenario I am drawing half of it, and the worst case I am drawing the full thing anyway. So it didn’t seem to be worth the trouble for me.

Yeah, I read up on Chet Haase’s blog about the different image types, but he made it seem like VolatileImages weren’t the best option. Volatile was for ones that are modified frequently, and Buffered was for ones that are drawn frequently. Using VRAM is certainly a nice idea, but what about the issues VolatileImages are supposed to have? The background is the least temporary image in the level, so I need to rely upon it not disappearing, or I would have to load it over and over again, which is ridiculously processor intensive so not a good option either.

I am drawing everything using a JPanel buffer strategy – but I’m not quite sure what you mean here. I do draw things in the order you specified, but that’s because I want certain things on top of each other, not for any other reason. Are you basically saying that everything in the foreground that gets rotated etc. should be a VolatileImage?

Thanks a lot!

[quote=“Demonpants,post:3,topic:31341”]
Yes, indeed VolatileImage are used to be faster to update than a BufferedImage instance. But as a matter of fact, VolatileImage cannot support translucency to an existing background.
Hence, VolatileImage would be the back-layer for the rendering scene and we can draw upon the BufferedImage instance over it.
If you know how to create and control a VolatileImage instance, then you’re on to render a whole scene of graphics. :wink:

Depends, if the background has blocks with repeating patterns you could tile these, which obviously saves memory (as you store the pattern once). Tiles are actually a form of compression.

I’m sure you misinterpreted that one, it should be the other way around. VolatileImage’s reside in vram, if you wanted to actually change the image contents you’d have to stream to the new contents to vram over the agp/pci-e bus, which is relatively slow. Also, operations like scaling, rotating etc can be performed on the videocard for VolatileImage’s, making use of the operations exposed by D3D or OpenGL.

However… BufferedImage’s are supposed to get cached in vram too under some conditions (available vram, pixel source changes etc). But I’m unsure if that path is more efficient than directly declaring images you certainly want to be accelerated Volatile. If I recall correctly a VolatileImage gets degraded to a BufferedImage when it the graphics config doesn’t support it… but maybe someone from the Java2D team can tell you all about that.

It won’t disapear. You draw all to an intermediate buffer (this can be an BufferedStrategy or another VolatileImage). So you blit the background (the bufferedimage, so depending the conditions, this might go very fast when its cached in vram, else you get a system memory to vram blit) onto this screenbuffer first. Then the forground images (VolatileImages, so the blits should be virtually instant). Then finally you do a flip() for a bufferedstartegy or when you use a volatileimage, you draw that to the active graphics context.

Fore more info:
http://gpwiki.org/index.php/Java:Tutorials:VolatileImage

It used to be, but it has been fixed since a long time:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5002129

Is this with default memory settings? If yes and you are not targetting applet deployment, you could maybe just raise the heap limit by setting “-Xmx512m” or something.

No, drawing to a VolatileImage is accalerated (and also drawing a VI->VI or VI->Screen), so you don’t stream the new contents over the bus but (in the optimal case) only drawing commands, the GPU then modifies the image. Thats why chet recommends VI for drawing to it.
If you alter a BI and blit it again the contents are transferred over the bus which is slow.

A VI also can have higher overhead when drawing a VI to screen compared to a BI, at least with the OpenGL pipeline.

Well, for an accalerated BufferedImage this is also true.

The best in your case would probably to allow the JVM to allocate more heap .

lg Clemens

The easiest way to understand whats goin on is to look at how VolatileImages and BufferedImages are implemented.
A VI is a pbuffer (or this new, great pbuffer replacement ^^), a BI is a texture.

lg Clemens

I referred to his remark about “changing” images. If you’d want to change/replace the actual pixel data each frame, you’d have to upload a new VI each frame too. Doing that on an BufferedImage would be more efficient, as that one gives up acceleration when you get its raster and directly uploads it’s pixel array.

I always thought a VI was degraded to an BI (in sysmem) when accelerated VI’s are not possible?

Thats interesting, as a texture has less overhead, giving the advantage to accelerated BI’s in this case. I always thought VI’s and (accelerated) BI’s shared the same implementation…

This:

[quote=“Demonpants,post:1,topic:31341”]
combined with this:

[quote=“Demonpants,post:3,topic:31341”]
leads me to ask: Can you not simply scale the image to fit the resolution?

If the image is still to big and you can’t split it into repeating tiles, you have little option but to increase the heap space, AFAICS.

Hey, thanks for all the incredibly insightful replies, guys!

I have since converted my game to LWJGL, so I don’t need to worry about this quite so much anymore. However, the Level Editor is still in Java2D (so I can use Swing components), so I am still going to tweak things to allow for optimum efficiency.

I could definitely increase Heap size, but I was hoping to avoid that, seeing as some computers may not run correctly (I increased the heap on a different program before, and some computers with low RAM couldn’t run my program at all). But it’s possible I will increase the heap a small amount anyway.

Thanks again.