Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (539)
Games in Android Showcase (133)
games submitted by our members
Games in WIP (603)
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  
  When using VolatileImage, keep reference to BufferedImage?  (Read 3832 times)
0 Members and 1 Guest are viewing this topic.
Offline CommanderKeith
« Posted 2009-04-27 15:50:02 »

Hi,

When using java2D's accelerated VolatileImages to show images loaded from the disk, do you keep the reference to the original BufferedImage loaded from the disk? Or once you draw it to the VolatileImage, do you let it be garbage collected. So if you're VolatileImage ever needs repainting, you read the BufferedImage from the disk again and repaint it on the VolatileImage.

I was just wondering because I'm making a java2D game with a lot of images and when holding onto all of the BufferedImages the game's memory usage goes up quite a bit. On the other hand, if i throw away the references to the BufferedImages then I'll have to read them back again from the disk if the VolatileImage needs repainting which might cause a delay...

I was just wondering what everyone else does.

It seems pretty rare that VolatileImages need re-painting because of lost contents, these are the things that make them 'Volatile' and cause them to need re-painting (from Chet Haase's old blog http://weblogs.java.net/blog/chet/archive/2003/09/volatileimage_n.html):
    * Another app going into fullscreen mode
    * The display mode changing on your screen (whether caused by your application or the user)
    * TaskManager being run
    * A screensaver kicking in
    * The system going into or out of StandBy or Hibernate mode

Thanks,
keith

Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 845
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #1 - Posted 2009-04-27 16:15:14 »

If those are the only conditions that the native surface gets lost, definitely store the images on disk. The user did something so 'drastic' that he certainly will accept some delay in continuing with your app.

You might however print to System.out when you actually need to repaint your volatile image (maybe on minimizing / restore?).

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline trembovetski

Senior Devvie




If only I knew what I'm talking about!


« Reply #2 - Posted 2009-04-27 16:18:19 »

Cases where the surface is lost are "relatively" rare (meaning, not several times per second) - once every once in a while. Unless you have multi-megabyte images, or your users are over a slow connection and your images are loaded from network, I think it's safe to drop your bufferedimages and load them if the surface is lost.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline CommanderKeith
« Reply #3 - Posted 2009-04-27 16:32:12 »

Cool, thanks a lot Riven and Dmitri.

I'll do that then. I was actually getting OutOfMemoryErrors when using java2D's software pipeline (as opposed to the d3d pipeline) and I think it was because the BufferedImages and VolatileImages were being stored in RAM (rather than the VolatileImages being stored in VRAM). Letting the BufferedImages go should halve the memory requirements. I just wanted to make sure it's the right thing to do.

Thanks heaps

Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 845
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #4 - Posted 2009-04-27 16:49:38 »

Thanks heaps

 persecutioncomplex Grin



Anyway, if you're going to serialize them, don't use ImageIO. Just convert your int[] to a byte[] and dump it to disk using a RandomAccessFile (be sure to raf.setLength(long); raf.sync(); first, so that the filesystem doesn't have to grow your file every few writes).

You'll have to feed your data back into your BufferedImage, which will *never* be accelerated when you access its pixels, so create 2 BufferedImages: once to feed it your pixels, once to draw the first on the second. Then flush() the first.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline CommanderKeith
« Reply #5 - Posted 2009-04-27 17:03:53 »

Ok i did it and memory use in software mode halved, so that's great. And in d3d mode the game hardly uses any memory, I guess it's all in VRAM.

persecutioncomplex Grin

Anyway, if you're going to serialize them, don't use ImageIO. Just convert your int[] to a byte[] and dump it to disk using a RandomAccessFile (be sure to raf.setLength(long); raf.sync(); first, so that the filesystem doesn't have to grow your file every few writes).

You'll have to feed your data back into your BufferedImage, which will *never* be accelerated when you access its pixels, so create 2 BufferedImages: once to feed it your pixels, once to draw the first on the second. Then flush() the first.

Gee, sounds like you've learned that from experience!  Smiley Thanks, but I'm not having to serialize them, the images are packaged in the jar file.

By the way, I pretty much stay clear of BufferedImages for rendering and only use VolatileImages because BufferedImage acceleration is really flaky - seems like there's 3 levels of acceleration in java2d:
* not accelerated - like a BufferedImage that has it's raster modified like what you said.
* BufferedImage accelerated - some kind of half-way acceleration that seems to either not work, break very easilly or is just slower than VolatileImage acceleration.
* VolatileImage acceleration - very fast and not breakable so long as you throw in a lot of code to check that it hasn't lost its contents.


Offline Abuse

JGO Knight


Medals: 15


falling into the abyss of reality


« Reply #6 - Posted 2009-04-27 17:12:24 »

Ok i did it and memory use in software mode halved, so that's great. And in d3d mode the game hardly uses any memory, I guess it's all in VRAM.

Gee, sounds like you've learned that from experience!  Smiley Thanks, but I'm not having to serialize them, the images are packaged in the jar file.

By the way, I pretty much stay clear of BufferedImages for rendering and only use VolatileImages because BufferedImage acceleration is really flaky - seems like there's 3 levels of acceleration in java2d:
* not accelerated - like a BufferedImage that has it's raster modified like what you said.
* BufferedImage accelerated - some kind of half-way acceleration that seems to either not work, break very easilly or is just slower than VolatileImage acceleration.
* VolatileImage acceleration - very fast and not breakable so long as you throw in a lot of code to check that it hasn't lost its contents.



Surely BufferedImages should simply be a wrapped VolatileImage, that handles all of the contents lost/restored exceptional circumstances for you.

If you implement that all yourself, you'll end up using twice as much VRAM than is necessary - The VolatileImage; the BufferedImage that you use to restore the VolatileImage, and a 2nd VolatileImage that is allocated internally for accelerating the drawing of the BufferedImage onto vram resident surfaces.

:edit:

Oh; nvm just read the first 4 posts of the Thread and realized i've repeated what you'd already discovered  Grin

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline CommanderKeith
« Reply #7 - Posted 2009-04-27 17:21:58 »

You're right, except I think the VolatileImage keeps the data in VRAM or RAM, not both AFAIK.

Quote
Surely BufferedImages should simply be a wrapped VolatileImage, that handles all of the contents lost/restored exceptional circumstances for you.
I read in Sun's VolatileImage guide that the VI's can't handle all repainting etc automatically because there's no way to know when the VolatileImage will lose its contents on Windows. So we have to manually paint it ourselves which is unavoidable I suppose.

Offline CommanderKeith
« Reply #8 - Posted 2009-04-27 17:31:00 »

If it's helpful to anyone, here's some code I use to take care of all of the VolatileImage rendering. My game sprite objects just hold a reference to the AcceleratedImage and draw the VolatileImage returned by acceleratedImage.getAndCheckVolatileImage(). All VolatileImages are held in the static ImageBank so there's only one copy rather than having lots floating around wasting memory.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import java.net.*;
import javax.imageio.*;

/**
 *
 * @author Keith
 */

public class ImageBank{
   static HashMap<String, VolatileImage> volatileImageMap = new HashMap<String, VolatileImage>();
   static String imageDirName = "images/";

   public ImageBank(){
   }
   
   static public void loadAndPutImages(String[] relativeURLNames){
      for (String relativeURLName : relativeURLNames){
         loadAndPutImageFromRelativeURLString(relativeURLName);
      }
   }

   static public void loadAndPutImageFromRelativeURLString(String relativeURLName){
      VolatileImage volatileImage = null;
      volatileImageMap.put(relativeURLName, volatileImage);
   }
   static public void putVolatileImage(String relativeURLName, VolatileImage volatileImage){
      volatileImageMap.put(relativeURLName, volatileImage);
   }
   static public VolatileImage getVolatileImage(String relativeURLName){
      return volatileImageMap.get(relativeURLName);
   }
   public static HashMap<String, VolatileImage> getVolatileImageMap() {
      return volatileImageMap;
   }
   static public BufferedImage readBufferedImage(String relativeURLName){
      URL imageURL = ImageBank.class.getResource(imageDirName + relativeURLName);
      BufferedImage bufferedImage;
      try{
         bufferedImage = ImageIO.read(imageURL);
      }catch(IOException e){
         e.printStackTrace();
         return null;
      }
      System.out.println("ImageBank.loadAndPutImageFromRelativeURLString(): relativeURLName == "+relativeURLName+", w == "+bufferedImage.getWidth()+", h == "+bufferedImage.getHeight());
      return bufferedImage;
   }

   public static VolatileImage createVolatileImage(int width, int height, int transparency) {  
      GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
      GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
      VolatileImage image = null;

      image = gc.createCompatibleVolatileImage(width, height, transparency);

      int valid = image.validate(gc);

      if (valid == VolatileImage.IMAGE_INCOMPATIBLE) {
         image = createVolatileImage(width, height, transparency);
      }
      //System.out.println(ImageBank.class.getSimpleName() + ": created new VolatileImage");
      return image;
   }
   public static VolatileImage createTransparentVolatileImage(int width, int height) {
      VolatileImage image =  ImageBank.createVolatileImage(width, height, Transparency.TRANSLUCENT);
      Graphics2D g = (Graphics2D)image.getGraphics();
      g.setColor(new Color(0,0,0,0));
      g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT));
      g.fillRect(0, 0, image.getWidth(), image.getHeight());
      return image;
   }

}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import sydneyengine.shooter.*;
import java.net.*;
/**
 *
 * @author woodwardk
 */

public class AcceleratedImage extends SSAdapter{
   String imageURLString;
   int width;
   int height;

   public AcceleratedImage(){
      width = -1;
      height = -1;
   }
   public AcceleratedImage(String imageURLString){
      this.imageURLString = imageURLString;
      // record the width and height
      VolatileImage volatileImage = getAndCheckVolatileImage();
      width = volatileImage.getWidth();
      height = volatileImage.getHeight();
   }

   protected void drawOntoVolatileImage(VolatileImage vImage, BufferedImage bufferedImage){
      Graphics2D g = vImage.createGraphics();
      g.drawImage(bufferedImage, 0, 0, null);
      g.dispose();
   }
   public VolatileImage getAndCheckVolatileImage(){
      //System.out.println(this.getClass().getSimpleName() + ": drawOntoImage(), "+this.getIndexX()+", "+this.getIndexY()+", time == "+v.getWorld().getTimeNowSeconds());
      VolatileImage vImage = ImageBank.getVolatileImage(imageURLString);
      GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
      GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
      if (vImage == null || vImage.validate(gc) != VolatileImage.IMAGE_OK) {
         BufferedImage bufferedImage = ImageBank.readBufferedImage(imageURLString);
         vImage = ImageBank.createTransparentVolatileImage(bufferedImage.getWidth(), bufferedImage.getHeight());
         ImageBank.putVolatileImage(this.imageURLString, vImage);
         drawOntoVolatileImage(vImage, bufferedImage);
      }
      do {
         int valid = vImage.validate(gc);
         if (valid == VolatileImage.IMAGE_INCOMPATIBLE) {
            BufferedImage bufferedImage = ImageBank.readBufferedImage(imageURLString);
            vImage = ImageBank.createTransparentVolatileImage(bufferedImage.getWidth(), bufferedImage.getHeight());
            ImageBank.putVolatileImage(this.imageURLString, vImage);
            drawOntoVolatileImage(vImage, bufferedImage);
         }
      } while (vImage.contentsLost());
      return vImage;
   }

   public String getImageURLString() {
      return imageURLString;
   }

   public int getHeight() {
      return height;
   }

   public int getWidth() {
      return width;
   }

}

Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 845
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #9 - Posted 2009-04-27 19:25:21 »

Surely BufferedImages should simply be a wrapped VolatileImage, that handles all of the contents lost/restored exceptional circumstances for you.

Actually, that's not true.

BufferedImage -> texture
VolatileImage -> pBuffer (or maybe FBO these days?)

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline trembovetski

Senior Devvie




If only I knew what I'm talking about!


« Reply #10 - Posted 2009-04-27 20:03:08 »

Quote
* BufferedImage accelerated - some kind of half-way acceleration that seems to either not work, break very easilly or is just slower than VolatileImage acceleration.

Could you elaborate on this? If system memory is not an issue BIs should be just as fast as VIs, but w/o the hassle of managing it yourself.
Offline CommanderKeith
« Reply #11 - Posted 2009-04-29 04:06:06 »

When I used to try using just BufferedImages, if I painted too many translucent pixels to the BufferedImage it would make it really slow all of a sudden, it's as if the BufferedImage would lose its acceleration. That was before java6 update 10, so I'll try it again and let you know.

Offline trembovetski

Senior Devvie




If only I knew what I'm talking about!


« Reply #12 - Posted 2009-04-29 05:45:03 »

A couple of points: until 6u10 translucent BI weren't accelerated by default (unless you enabled opengl pipeline, or the old d3d pipeline).
Second point is that if you render to the buffered image and then copy from it, it won't be accelerated - it requires at least one copy from such image in order for it to be cached in vram. So buffered images are better used for sprites.

As someone mentioned: VI == pbuffer/fbobject (ogl) or RTT (d3d), BI == texture (d3d,ogl)

BTW, translucent VIs weren't accelerated until 6u10 either.
Offline CommanderKeith
« Reply #13 - Posted 2009-04-29 09:05:50 »

That makes sense, thanks. I just replaced the VI's with BI's in my game and got the same performance on a 6u10 VM so that's great, you're right.

I think you could accelerate VI's with a pre 6u10 JVM using VM options like -Dsun.java2d.d3d=true and translaccel=true but they're non-standard (http://java.sun.com/j2se/1.5.0/docs/guide/2d/flags.html).

Quote
As someone mentioned: VI == pbuffer/fbobject (ogl) or RTT (d3d), BI == texture (d3d,ogl)

Is there difference between a texture and a pbuffer (ogl) or an RTT (d3d)?  I guess they're the same?

thanks,
keith

Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 845
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #14 - Posted 2009-04-29 09:14:38 »

PBuffer/FBO has higher overhead.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline CommanderKeith
« Reply #15 - Posted 2009-04-29 14:36:32 »

Thanks, i googled them too and they seem pretty much the same except one is more recent than the other.

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

rwatson462 (37 views)
2014-12-15 09:26:44

Mr.CodeIt (31 views)
2014-12-14 19:50:38

BurntPizza (62 views)
2014-12-09 22:41:13

BurntPizza (99 views)
2014-12-08 04:46:31

JscottyBieshaar (60 views)
2014-12-05 12:39:02

SHC (74 views)
2014-12-03 16:27:13

CopyableCougar4 (77 views)
2014-11-29 21:32:03

toopeicgaming1999 (138 views)
2014-11-26 15:22:04

toopeicgaming1999 (127 views)
2014-11-26 15:20:36

toopeicgaming1999 (38 views)
2014-11-26 15:20:08
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
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!