Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (744)
Games in Android Showcase (225)
games submitted by our members
Games in WIP (825)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  How to have BufferedImages take up less RAM  (Read 916 times)
0 Members and 1 Guest are viewing this topic.
Offline nik312123

Senior Newbie





« Posted 2017-12-05 21:13:52 »

Hello! As the title says, BufferedImages take up the majority of the RAM in my Java game. I use mostly pngs; some of which are transparent.

I initiate the BufferedImage objects simply like so:

1  
BufferedImage example = ImageIO.read(Runner.class.getResource("/exampleImage.png"));


Then, I use a getCompatibleImage() function to make it the best image for rendering on the system.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
static BufferedImage getCompatibleImage(BufferedImage current) {
    GraphicsConfiguration gfxConfig = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
    if(current.getColorModel().equals(gfxConfig.getColorModel()))
        return current;
    BufferedImage optimized = gfxConfig.createCompatibleImage(current.getWidth(), current.getHeight(), current.getTransparency());
    Graphics2D g2d = optimized.createGraphics();
    g2d.drawImage(current, 0, 0, null);
    optimized.setAccelerationPriority(1);
    return optimized;
}


I use about 1219 BufferedImage objects, which is quite a lot. I read all of them at the beginning with a loading screen splash image and keep all of them as static b/c I do not want to reload them on a restart.

Please let me know what I can do to use less RAM. Thanks!
Offline Abuse

JGO Ninja


Medals: 60


falling into the abyss of reality


« Reply #1 - Posted 2017-12-05 21:47:57 »

- Java is an OO language, thus the term for a unit of code is 'method' not 'function'.
- Not wishing to reload the images is not a good reason to make them static.
As your program develops, the scope of your resources is likely to change; encapsulating the resources within instanced objects makes refactoring your code to accommodate such changes easier.
The typical example of this would be where your design transitions from:
a) having a single level, where all resources are held in memory at once
b) to multiple levels, where intermediary loading screens are presented to the user.
c) to, perhaps, an infinite world where resources are streamed into memory in the background as the user navigates the world.

- Images returned from ImageIO have been eligible for hardware acceleration for many years; there's no need to copy them to a new image.

As to your question:

1) Increase heap size; seems obvious, but you haven't stated whether you've tried this.
2) Don't keep everything loaded at once. As suggested above, levels + load screens, or background streaming.

It's also worth keeping in mind that every Image in the heap also occupies VRAM for its cached copy used by the h/w accelerated pipeline.
Depending upon the h/w rendering pipeline being used by Java2D (which will vary by platform, and cmdline flags), you might be wasting large amounts of VRAM because Java2D is creating power-of-2 textures that poorly fit the dimensions of your Image.
While this inefficiency won't increase your heap usage, it might significantly increase vram usage - perhaps to the point where it negatively impacts performance.
The solution here is to adopt sprite sheets, along with the associated tooling to generate them, and code to draw them.
Offline nik312123

Senior Newbie





« Reply #2 - Posted 2017-12-06 07:25:19 »

Quote
As your program develops, the scope of your resources is likely to change; encapsulating the resources within instanced objects makes refactoring your code to accommodate such changes easier.
Okay. I will try to remove the static keyword and try to use encapsulation more for that mater.

Quote
Images returned from ImageIO have been eligible for hardware acceleration for many years; there's no need to copy them to a new image.
The part about writing to a new image is for making sure the graphics object is ideal for the graphics configuration on the system. So, it draws a compatible image onto the BufferedImage itself, replacing the other version. So, it's not simply for hardware acceleration.

Quote
Increase heap size; seems obvious, but you haven't stated whether you've tried this.
I have increased heap size using -Xmx if that's what you mean. The application runs smoothly, so my wish of reducing the RAM usage does not have to do with the application's performance.

Quote
Don't keep everything loaded at once. As suggested above, levels + load screens, or background streaming.
However, in doing so, the user may get impatient as there would be a lot of loading going on, and it takes quite a while. So, the user would have to keep waiting every time the application switches from the start screen to the game screen and vice versa.

Quote
Depending upon the h/w rendering pipeline being used by Java2D (which will vary by platform, and cmdline flags), you might be wasting large amounts of VRAM because Java2D is creating power-of-2 textures that poorly fit the dimensions of your Image.
I see... I never knew that before. So, I have two questions based upon that:

    1) Is there a JVM argument that prevents this waste?
    2) Is loading one large image like a spritesheet more efficient than loading multiple images?

Thanks for taking the time to help!
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline princec

« JGO Spiffy Duke »


Medals: 982
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #3 - Posted 2017-12-06 08:57:02 »

The part about writing to a new image is for making sure the graphics object is ideal for the graphics configuration on the system. So, it draws a compatible image onto the BufferedImage itself, replacing the other version. So, it's not simply for hardware acceleration.
As I understand it BufferedImage already does this under the hood, so you are essentially wasting your time here I think (could probably prove this to yourself with some benchmarks).

Quote
   1) Is there a JVM argument that prevents this waste?
No. This is something you've got to get down and dirty with yourself, and it's pretty much a rite of passage in games development. Use the biggest sprite sheets you can get away with - 1024x1024 is supported by all video hardware these days, and actual dedicated GPUs all manage 4096x4096.

Quote
   2) Is loading one large image like a spritesheet more efficient than loading multiple images?
Vastly.

Cas Smiley

Offline nik312123

Senior Newbie





« Reply #4 - Posted 2017-12-07 22:21:13 »

Thanks! Also, I heard that VolatileImages are more efficient if you don't modify the image but draw them often, which fits my bill. So, would this be the best way to make an image into a volatile image, or am I doing this incorrectly?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
Image example = ImageIO.read(MainClass.class.getResource("/example.png"));
example = getCompatibleImage(example);

static VolatileImage getCompatibleImage(Image current) {
    GraphicsConfiguration gfxConfig = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
    VolatileImage optimized = gc.createCompatibleVolatileImage(current.getWidth(), current.getHeight(), Transparency.TRANSLUCENT);
    Graphics2D g2d = optimized.createGraphics();
    g2d.drawImage(current, 0, 0, null);
    optimized.setAccelerationPriority(1);
    return optimized;
}
Offline princec

« JGO Spiffy Duke »


Medals: 982
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #5 - Posted 2017-12-07 22:42:28 »

Seems right. The problem with VolatileImage is that they can randomly lose their contents so you need to keep the original around anyway to restore it.

Cas Smiley

Offline nik312123

Senior Newbie





« Reply #6 - Posted 2017-12-07 22:58:12 »

I see. Is there a way to detect whether the contents of a VolatileImage have been lost to check if it needs to be restored? If not, would it be better to use BufferedImages or generate the VolatileImage each time the image is to be drawn?
Offline princec

« JGO Spiffy Duke »


Medals: 982
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #7 - Posted 2017-12-07 23:37:27 »

All in the docs: https://docs.oracle.com/javase/7/docs/api/java/awt/image/VolatileImage.html

Maybe more trouble than it's worth though. Exactly how much blitting are you going to be doing?

Cas Smiley

Offline nik312123

Senior Newbie





« Reply #8 - Posted 2017-12-08 02:12:09 »

Hmmm... Based on what I looked up for "blitting," I do resize some images in Graphics.drawImage() or use AffineTransform. I also have several images where I change the AlphaComposite to change the transparency. If I am misinterpreting the term "blitting," please let me know.
Offline cygnus
« Reply #9 - Posted 2017-12-08 02:27:18 »

Maybe I'm confused, but I recall that Java automatically caches a VolatileImage copy of a BufferedImage somewhere if you draw that image a lot, meaning that it is in fact best to use BufferedImages for your regular textures.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Abuse

JGO Ninja


Medals: 60


falling into the abyss of reality


« Reply #10 - Posted 2017-12-08 06:57:00 »

Maybe I'm confused, but I recall that Java automatically caches a VolatileImage copy of a BufferedImage somewhere if you draw that image a lot, meaning that it is in fact best to use BufferedImages for your regular textures.

Essentially yes, VolatileImages are generally only of relevance for the target of a draw, not a source.
Typically of use if you're doing some fancy compositing, or buffering.

If all you're doing is drawing images, stay well clear of them.
Offline nik312123

Senior Newbie





« Reply #11 - Posted 2017-12-08 07:49:44 »

So, the main thing I seem to be getting from this thread is that sprite sheets are a good way of reducing memory usage. The way my game is set up, the intermediate loading screen would not really work too well. Although, I'll look into it anyway.

I'm going to use a profiler to see what things are taking the most resources. (I already know that BufferedImages take up around 99% of the RAM usage, so I'll try and look more specifically at the methods.) I'll update with any questions I have involving that later.

Other than that, I can't seem to find many other ways to reduce the amount of RAM being taken up by the large quantity of images I have. If there are any other general tips anyone could provide when dealing with many resources, I would appreciate it.
Offline princec

« JGO Spiffy Duke »


Medals: 982
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #12 - Posted 2017-12-08 09:00:23 »

There is no way around graphics taking up the amount of system RAM that they do. The best you can do is pack them all tightly into a single big BufferedImage and blit out chunks when you want.
"Blitting" btw is an old term, "blit" being short for "block image transfer". In other words, copying a rectangle of pixels from one place to another. It's almost always hardware accelerated these days, even with BufferedImage, and BufferedImage is a perfectly sensible way to do everything.

Of course if you wanted to do something a bit more modern you'd use JavaFX. And if you wanted to get a bit more fancy, LWJGL.

Cas Smiley

Offline nsigma
« Reply #13 - Posted 2017-12-08 11:47:49 »

There is no way around graphics taking up the amount of system RAM that they do. The best you can do is pack them all tightly into a single big BufferedImage and blit out chunks when you want.

My inclination is that a single BufferedImage might cause issues too?  I'd meet in the middle, create a series of BufferedImage with multiple images on (sprite sheets), trying to group images that are used together on the same image.  Maybe 1024x1024 (4MB) or 2048x2048 (16MB)?  You want to give the images used in the same frame the best chance of being managed and cached in hardware together.

I heard that VolatileImages are more efficient if you don't modify the image but draw them often

As @Abuse says, this is the opposite of the case.  You want a VolatileImage if you are modifying the image a lot.  If you don't use a Graphics2D obtained from calling image.getGraphics() much or at all, you don't need VolatileImage.  Where people often fall down is they assume that managed images mean the Graphics2D from a BufferedImage is hardware accelerated - it isn't!

Praxis LIVE - hybrid visual IDE for (live) creative coding
Offline nik312123

Senior Newbie





« Reply #14 - Posted 2017-12-09 23:30:25 »

I have some questions.

Quote
Java2D is creating power-of-2 textures that poorly fit the dimensions of your Image.

So, would it be better to have a 1024x1024 image with extra white or transparent pixels (whichever is more efficient) or have spritesheets of various sizes such as 960 x 905?

Also, would it be best to get the subImages when drawing each time (example two) or store them into BufferedImage objects and have them draw those without having to use the getSubImage() method any more times (example one)?

For example:
1  
2  
3  
4  
5  
6  
7  
8  
BufferedImage spriteSheet = ImageIO.read(fileLocation);

//Example one
BufferedImage exampleOne = spriteSheet.getSubimage(50, 50, 40, 40);
g.drawImage(exampleOne, 0, 0, null);

//Example two
g.drawImage(spriteSheet.getSubimage(50, 50, 40, 40), 0, 0, null);
Offline nsigma
« Reply #15 - Posted 2017-12-10 14:00:23 »

Also, would it be best to get the subImages when drawing each time (example two) or store them into BufferedImage objects and have them draw those without having to use the getSubImage() method any more times (example one)?

Neither! Don't create sub-images, use the variant of drawImage() that takes 8 arguments.

Praxis LIVE - hybrid visual IDE for (live) creative coding
Offline nik312123

Senior Newbie





« Reply #16 - Posted 2017-12-10 16:58:57 »

I see! That makes sense. Could you or someone else respond to the part in my previous post about the image dimensions and filler?
Offline Paradox123

Senior Devvie


Medals: 5
Projects: 5



« Reply #17 - Posted 2017-12-10 17:26:07 »

This is what I would do.
-> load tilesheet into a BufferedImage and convert it to int[]
-> create a BufferedImage that will handle the drawings with, again, an int[]
-> with this way, you will have to create your own drawing methods, since all image data is handled through int[]
Offline nsigma
« Reply #18 - Posted 2017-12-10 17:32:45 »

I'm sceptical that Java2D is using power-of-two surfaces, but either way I'd use a uniform size as it'll make things easier to deal with - 1024x1024 sounds like a good starting point.  Definitely fill unused bits with transparent pixels if the image has transparency - there's no difference in efficiency and no chance of getting any white pixels showing by accident.

This is what I would do.
-> load tilesheet into a BufferedImage and convert into to int[]
-> create a BufferedImage that will handle the drawings with, again, an int[]
-> with this way, you will have to create your own drawing methods, since all image data is handled through int[]

What for?!  For the purpose of ensuring there's no way at all any capabilities of the GPU are used?  Undecided

Praxis LIVE - hybrid visual IDE for (live) creative coding
Offline Paradox123

Senior Devvie


Medals: 5
Projects: 5



« Reply #19 - Posted 2017-12-10 17:56:58 »

Okay, you are right about that. But I don't know the dimensions of his game. It would reduce the BufferedImages drastically and he would only need to get dta from the tilesheet int[] and copy it to the main int[] (the one that is being drawn)
Offline nsigma
« Reply #20 - Posted 2017-12-10 18:02:55 »

Okay, you are right about that. But I don't know the dimensions of his game. It would reduce the BufferedImages drastically and he would only need to get dta from the tilesheet int[] and copy it to the main int[] (the one that is being drawn)

A BufferedImage is basically a thin wrapper over an array, usually an int[].  Doesn't really gain you anything, except the need to write a software renderer (which can be fun mind you, and faster than the software Java2D loops in some cases)

Praxis LIVE - hybrid visual IDE for (live) creative coding
Offline Paradox123

Senior Devvie


Medals: 5
Projects: 5



« Reply #21 - Posted 2017-12-10 18:17:56 »

It wouldn't probably affect the ram, but it would reduce the use of BI. Usually I use this method for small games in pure java which is very efficient. Now, he has 1000+ image objects, I don't see how he can reduce the ram being used. Unless he creates a way to draw compressed images, which would be interesting
Offline nik312123

Senior Newbie





« Reply #22 - Posted 2017-12-10 18:38:45 »

It seems that it has more to do with OpenGL textures based on the fourth bullet point of general comments here: https://community.oracle.com/docs/DOC-983015

I am using the JVM argument -Dsun.java2d.opengl=true. Does this mean that this affects me?
Offline nsigma
« Reply #23 - Posted 2017-12-10 18:43:34 »

You're brave!  Grin  That's a very old document.  There's definitely code in the JDK source for GL to support non-power-of-two textures, it's even mentioned in that document.  I assume it's working?!  Undecided

Praxis LIVE - hybrid visual IDE for (live) creative coding
Offline nik312123

Senior Newbie





« Reply #24 - Posted 2017-12-10 22:32:57 »

Never mind about the post I just deleted. The content was irrelevant.
Pages: [1]
  ignore  |  Print  
 
 

 
Ecumene (146 views)
2017-09-30 02:57:34

theagentd (213 views)
2017-09-26 18:23:31

cybrmynd (295 views)
2017-08-02 12:28:51

cybrmynd (284 views)
2017-08-02 12:19:43

cybrmynd (294 views)
2017-08-02 12:18:09

Sralse (287 views)
2017-07-25 17:13:48

Archive (966 views)
2017-04-27 17:45:51

buddyBro (1092 views)
2017-04-05 03:38:00

CopyableCougar4 (1663 views)
2017-03-24 15:39:42

theagentd (1425 views)
2017-03-24 15:32:08
Java Gaming Resources
by philfrei
2017-12-05 19:38:37

Java Gaming Resources
by philfrei
2017-12-05 19:37:39

Java Gaming Resources
by philfrei
2017-12-05 19:36:10

Java Gaming Resources
by philfrei
2017-12-05 19:33:10

List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!