Size matters? A managed image problem...

Hi all,

I’m aware that all managed images will be accelerated with no size constraints according to this thread by trembovetski:

http://www.java-gaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=2D;action=display;num=1065777178;start=1#1.

Assuming of course that there is enough VRAM available.

Then I’m puzzled as to why this BufferedImage with BITMASK transparency created using this piece of code doesn’t seem to get accelerated:

[quote]squareBuffer = Globals.GC.createCompatibleImage(playerBounds.width, playerBounds.height, Transparency.BITMASK);
[/quote]
The width of that image created is 364 px, and the height is 700 px.

How I tell that it ISN’T accelerated is by using JProfiler to trace method invocations made by sun.java2d.SurfaceData.getSurfaceDataFromImage, and it calls sun.awt.image.BufImgSurfaceData.createData. If it was accelerated with BITMASK transparency, it should be calling sun.awt.image.AcceleratedOffScreenImage.getSourceSurfaceData instead.

Information on the environment I’m runnng in:

This WORKS however:

[quote]squareBuffer = Globals.GC.createCompatibleImage(playerBounds.width / 10, playerBounds.height / 10, Transparency.BITMASK);
[/quote]
I’ve got translucent acceleration enabled and this WORKS:

[quote]squareBuffer = Globals.GC.createCompatibleImage(playerBounds.width, playerBounds.height, Transparency.TRANSLUCENT);
[/quote]
And this too:

[quote]squareBuffer = Globals.GC.createCompatibleImage(playerBounds.width, playerBounds.height, Transparency.OPAQUE);
[/quote]

So, does size actually matter with regards to BITMASK transparency managed images? Or did I miss something?

I’d appreciate it if someone could clarify. Thanks.

No idea,
but try setting sun.java2d.accthreshold=0, rather than 1.
or try sun.java2d.forceddvram=true

[quote]No idea,
but try setting sun.java2d.accthreshold=0, rather than 1.
or try sun.java2d.forceddvram=true
[/quote]
Whoa, that response was quick! :slight_smile:
I’ve tried enabling and disabling them both already though, doesn’t help…

Fast, but absolutely useless to you :wink:

Try iterating through a variety of widths/heights for your bitmask image.
Find out at exactly what resolution the image stops being managed. It maybe some ‘clever’ code deeming your image too large to be managed. (though I don’t see how exactly that could be considered clever ^_^)

also, I was under the impression images larger than the display resolution may not be accelerated (driver issues and the like).
Have you changed the DisplayMode to something with a height < 700?

Heh :). Any response is good I say.

I’m running the app in windowed mode and I’m not using BufferStrategy for my back buffer, so it shouldn’t be a DisplayMode quirk. The “squareBuffer” BufferedImage is just a 368 * 700 size image I’m using to render square tiles on occasionally, but it is imperative that it is accelerated since it gets copied from every animation cycle . If i reduce the size to say a 10th, it works as intended.

Btw, my desktop is set at 1280 x 1024 at 32 bpp if that makes a difference.

Appreciate your responses :).

You are aware that each time you draw a tile to the BufferedImage, it will lose its vram cached version?

For that situation(an intermediary buffer), VolatileImage is a better solution.
Ofcourse, you need a BITMASK volatile image, which is impossible pre-1.5, so ignore what I just said :wink:

Either way, I can’t see why an OPAQUE image would be accelerated, and a BITMASK not :S very confusing.

You are using windowed mode, but not using BufferStrategy.
What are you using for your back buffer? a VolatileImage?
What size is the window?

just a 368 * 700 size image

Next power of 2 combo would be 512x1024.

Try breaking it up that it fits in 512x512 or 256x256 tiles. It should work better then. 2 or 4 blits instead of one, but that doesnt really matter.

It could be that your graphics card doesnt support textures that big or (wich I don’t know exactly) that the maximum is either 512x512 or 256x256 (might have been a compatibility decision).

Can anyone else running JVM 1.4.2_04 on Windows XP confirm having the same problem with BITMASK Managed Images larger than 364 * 700 size?

[quote]You are aware that each time you draw a tile to the BufferedImage, it will lose its vram cached version?

For that situation(an intermediary buffer), VolatileImage is a better solution.
Ofcourse, you need a BITMASK volatile image, which is impossible pre-1.5, so ignore what I just said :wink:
[/quote]
I’m aware of that, but I can’t have it any other way since VolatileImages are only OPAQUE. Next best option would be to use TRANSLUCENT BufferedImages, but that would be wasteful since I don’t need that many levels of transparency.

[quote]Either way, I can’t see why an OPAQUE image would be accelerated, and a BITMASK not :S very confusing.
[/quote]
Exactly. Could be a bug.

Yes, a VolatileImage for my back buffer. The window is sized at 1024 x 768.

[quote]It could be that your graphics card doesnt support textures that big or (wich I don’t know exactly) that the maximum is either 512x512 or 256x256 (might have been a compatibility decision).
[/quote]
Hmm. IMO, unlikely since even larger TRANSLUCENT and OPAQUE images are accelerated.

Ok this is entirely guess work - but… because you have transaccel enabled, TRANSLUCENT and OPAQUE images maybe created as D3D surfaces, but BITMASK may still be created as DirectDraw surfaces.
Try disabling transaccel, and see if the OPAQUE 364x700 image is still accelerated.

Can you put together a simple test case that we can all run to establish if its a Java problem, or driver problem.

Tried that too. Doesn’t work.

Done! :wink:
The code is listed below. Just cut and paste:

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.*;
import java.util.Random;

public class ImageTest extends JFrame implements Runnable
{
      Image bb;
      BufferedImage bitmaskImage;
      long elapsedTime;
      int delayToUpdate = 100;
      
      final int numOfDraws = 400; // Modify this to change the number of times the BITMASK image is drawn onto the volatile buffer
                                             // A larger number stresses the rendering pipeling
                                             
      /* NOTE: if the width and height is set to 256, it should be accelerated. 
       *             If either one is increased by 1, then the image should not be accelerated
       *             Check the FPS to confirm
       */                                 
      final int bitmaskWidth = 256; // Modify this to change the width of the BITMASK image
      final int bitmaskHeight = 256; // Modify this to change the height of the BITMASK image
      
      public static void main(String[] args)
      {
            ImageTest bitmaskImageTest = new ImageTest();
            Thread start = new Thread(bitmaskImageTest);
            start.start();
      }
      
      public ImageTest()
      {
            setIgnoreRepaint(true);
        getContentPane().setLayout(null);
        setBounds(new Rectangle(0, 0, 1024, 768));        
            setVisible(true);
            
            bitmaskImage = getGraphicsConfiguration().createCompatibleImage(bitmaskWidth, bitmaskHeight, Transparency.BITMASK);           
            
            Graphics2D g = bitmaskImage.createGraphics();
            g.setColor(Color.GREEN);
            g.fillRect(0, 0, bitmaskImage.getWidth(null), bitmaskImage.getHeight(null));
            g.dispose();
            
            bb = getGraphicsConfiguration().createCompatibleVolatileImage(1024, 768);
            
            elapsedTime = 0;
            
            // Add window listener.
        this.addWindowListener
        (
            new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
            }
        );
        }
        
       public void run()
       {
             int counter = 0;
             Random rand = new Random();
             while(true)
             {
                   long startTime = System.currentTimeMillis();
                   
                   if(counter == delayToUpdate)
                   {
                         Graphics2D bitmaskG = bitmaskImage.createGraphics();
                         bitmaskG.setColor(Color.GREEN);
                         bitmaskG.fillRect(0, 0, bitmaskImage.getWidth(null), bitmaskImage.getHeight(null));
                         bitmaskG.setColor(Color.BLUE);
                         for(int i=0; i < 10; i++)
                               bitmaskG.fillOval(rand.nextInt(1024), rand.nextInt(768), 50, 50);
                         bitmaskG.dispose();
                         counter = 0;
                   }      
                         
                   Graphics g = bb.getGraphics();
                  for(int i=0; i < numOfDraws; i++)
                        g.drawImage(bitmaskImage, (bb.getWidth(null) - bitmaskImage.getWidth(null))/2, (bb.getHeight(null) - bitmaskImage.getHeight(null))/2, null);
                  
                  g.setColor(Color.RED);
                  String FPS = new String("FPS: " + ((float)1000)/elapsedTime);
                  g.drawString(FPS, (1024 >> 1) - 50, 
                                            768 >> 1);
                  g.dispose();
                  
                  g = getGraphics();
                  g.drawImage(bb, 0, 0, null);
                  g.dispose();
                     
                     counter++;               
                  elapsedTime = System.currentTimeMillis() - startTime;
            }      
      }
}

Do try it and see if you can get hardware accelerated BITMASK images greater than 256 x 256.

Thanks.

Oh, and btw, on my P4 3.0ghz and ATI RADEON 9800 running the latest catalyst drivers, I get:

7-8 fps with 257 x 256 sized image and 33+ fps using a 256 x 256 sized image with the default parameters.

It clearly shows that a 257 x 256 image isn’t accelerated.

If the drivers require square power of 2 textures, 257x256 would round to 512x512.
That is 4 times as much work as 256x256, which would explain the drop from 33fps to 7-8fps.

What fps do you get with 512x512? is it the same 7-8fps?

btw, Is there a reason you arn’t using BufferStrategy?

oh, I took the opportunity to tweak your code a litte, hope you don’t mind (your fps calcing was abit poo ;))


import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.util.Random;
      
public class ImageTest extends JFrame implements Runnable
{
      Image      bb;
      BufferedImage bitmaskImage;
      
      static final boolean      BUFFER_STRATEGY =      true;
      
      static final int BITMASK_BLITS_PER_FRAME = 400;
      
      static final int WINDOW_WIDTH      = 800, WINDOW_HEIGHT      = 600;
      static final int BITMASK_WIDTH =      256, BITMASK_HEIGHT = 256;
      
      public static void main(String[]      args)
      {
            System.setProperty("sun.java2d.accthreshold", "0");
            Thread t      = new      Thread(new ImageTest());
            t.setPriority(Thread.MIN_PRIORITY);                  
            t.start();
      }
            
      public ImageTest()
      {
            setIgnoreRepaint(true);
            getContentPane().setLayout(null);
            setBounds(new Rectangle(0,      0,      WINDOW_WIDTH, WINDOW_HEIGHT));            
            setVisible(true);
      
            bitmaskImage =      getGraphicsConfiguration().createCompatibleImage(BITMASK_WIDTH, BITMASK_HEIGHT, Transparency.OPAQUE);                  
      
            Graphics2D g =      bitmaskImage.createGraphics();
            g.setColor(Color.GREEN);
      g.fillRect(0, 0, BITMASK_WIDTH, BITMASK_HEIGHT);
            g.dispose();
      
            if(BUFFER_STRATEGY)
            {
                  createBufferStrategy(2);
            }
            else
            {
                  bb      = getGraphicsConfiguration().createCompatibleVolatileImage(WINDOW_WIDTH, WINDOW_HEIGHT);
            }
      
            setDefaultCloseOperation(EXIT_ON_CLOSE);
      }
      
      public void      run()
      {
            Random rand      = new      Random();
            long lastTime = System.currentTimeMillis();
            int frameCount      = 0;
            String fps = "n/a";
            while(true)
            {
                  frameCount++;
                  
                  long time =      System.currentTimeMillis();
                  if(time-lastTime > 1000) //once a second
                  {
                        //update      the fps counter
                        fps =      Integer.toString(frameCount);
                        frameCount=0;
                        lastTime+=1000;
                        
                        
                        Graphics2D bitmaskG = bitmaskImage.createGraphics(); //and alter the      bitmask image
                        bitmaskG.setColor(Color.GREEN);
            bitmaskG.fillRect(0, 0, BITMASK_WIDTH, BITMASK_HEIGHT);
                        bitmaskG.setColor(Color.BLUE);
                        for(int i=0; i      < 10;      i++)
                        {
                              bitmaskG.fillOval(rand.nextInt(BITMASK_WIDTH-25), rand.nextInt(BITMASK_HEIGHT-25), 50,      50);
                        }
                        bitmaskG.dispose();
                  }      
                  
                  Graphics      bg      = BUFFER_STRATEGY?getBufferStrategy().getDrawGraphics():bb.getGraphics(); //buffers      Graphics
                  
                  for(int i=0; i      < BITMASK_BLITS_PER_FRAME;      i++)
                  {
                        bg.drawImage(bitmaskImage,      (WINDOW_WIDTH - BITMASK_WIDTH)/2, (WINDOW_HEIGHT- BITMASK_HEIGHT)/2,      null);
                  }
            
                  bg.setColor(Color.RED);
                  bg.drawString(fps, WINDOW_WIDTH/2 -      50, WINDOW_HEIGHT/2);
                  bg.dispose();
                  
                  if(BUFFER_STRATEGY)
                  {
                        getBufferStrategy().show();
                  }
                  else
                  {
                        Graphics      g = getGraphics();
                        g.drawImage(bb, 0, 0, null);
                        g.dispose();
                  }
                  Thread.yield();
            }      
      }      
}

Btw, with this code I get 15-16fps using a 256x256 image.
With a 257x257 image I get the same 15-16fps.
With a 512x512 image I get 4-5fps.

(Athlon1.33gig 512ddr GF2gts 32mb)

[quote]If the drivers require square power of 2 textures, 257x256 would round to 512x512.
That is 4 times as much work as 256x256, which would explain the drop from 33fps to 7-8fps.

What fps do you get with 512x512? is it the same 7-8fps?

btw, Is there a reason you arn’t using BufferStrategy?

oh, I took the opportunity to tweak your code a litte, hope you don’t mind (your fps calcing was abit poo ;))

Btw, with this code I get 15-16fps using a 256x256 image.
With a 257x257 image I get the same 15-16fps.
With a 512x512 image I get 4-5fps.

(Athlon1.33gig 512ddr GF2gts 32mb)
[/quote]
Yes, it’s scaling down nicely in your case. Were you using BITMASK transparency?

Here’s what I got:

You can tell that there’s a noticable difference in the frame rates for the OPAQUE images as compared to the BITMASK ones, which proves that the OPAQUE images are accelerated in all cases and in only 1 case for the BITMASK images.

And even then there’s a drastic decrease in 8-9 fps for a 1px increase in the width from 256 to 257 in the OPAQUE image.

What do you think is the problem? Driver issue?

Thanks Abuse, you’ve been really helpful btw :slight_smile:

And here are the results for TRANSLUCENT images with acceleration enabled:

Transparecy.TRANSLUCENT

BUFFER_STRATEGY = true;
BITMASK_BLITS_PER_FRAME = 400;
WINDOW_WIDTH = 800 WINDOW_HEIGHT = 600

[quote]BITMASK_WIDTH = 256 BITMASK_HEIGHT = 256
FPS: 42 - 43

BITMASK_WIDTH = 257 BITMASK_HEIGHT = 256
FPS: 37 - 38

BITMASK_WIDTH = 256 BITMASK_HEIGHT = 257
FPS: 39 - 40

BITMASK_WIDTH = 257 BITMASK_HEIGHT = 257
FPS: 33 - 34 [b]Drastic drop compared to 256 x 256[b]

BITMASK_WIDTH = 512 BITMASK_HEIGHT = 512
FPS: 12 - 13
[/quote]
Again, it shows that OPAQUE and TRANSLUCENT images are accelerated while BITMASK images aren’t.

Also, TRANSLUCENT images outperform OPAQUE images with acceleration enable. Probably because of D3D vs DirectDraw…

This one is easy: we have this limit (which we often forget about ourselves) for bitmask images on windows, the size (w * h) must be less than 65536 in order for image to get accelerated. This is because of the way we accelerate bitmask images with DirectX pipeline.

This won’t be a problem with opengl pipelines on either windows or unix.

[edit]

[quote] trembovetski: All of the sprites will be attempted to get accelerated. Size doesn’t matter.
[/quote]
Mwahaha! And you believed me! Size always matters!

Nyah! Guessed right :slight_smile:

256x256 would have worked :stuck_out_tongue:

[quote]This one is easy: we have this limit (which we often forget about ourselves) for bitmask images on windows, the size (w * h) must be less than 65536 in order for image to get accelerated. This is because of the way we accelerate bitmask images with DirectX pipeline.
[/quote]
Ohhhh. ::slight_smile:

That explains my situation. But how come Abuse got accelerated BITMASK images with the test program we ran?

And is this opengl pipeline available on 1.4.2 on windows? How would I use/enable it if it is?

Failing that, my next best option is to chop up the image into smaller chunks like Onyx said. But that is exactly what I want to avoid since I want to incur as little overhead as possible with fewer drawImage() calls.

Bah. :-/

I want to avoid since I want to incur as little overhead as
possible with fewer drawImage() calls.

Won’t make a difference. More accelerated calls will be faster than one non-accelerated call. Also 600 blits aren’t a big deal @60fps (even on rather old hardware).

So don’t mind that and just try it :slight_smile:

If you can use dirty rectangles, you could even split it into even smaler tiles like 32x32 and only redraw those who needs to be redrawn. That will give you a nice performance boost compared to draw the whole thing each time.

Generally true. That’s why I mentioned earlier that this “squareBuffer” image I’m using has to be accelerated even though it gets written TO occasionally.

I’m not so sure about this. In fact, the test program I ran shows that the overhead involved in calling drawImage() is very significant. For example, all other things being equal, I compared blitting on screen a 16 x 16 sized accelerated BITMASK image 256 times to blitting a 256 x 256 sized accelerated BITMASK image 1 time.

FPS, 16 x 16, 256 times: ~280
FPS, 256 x 256, 1 time: ~1778+, 6x faster

Drastic frame rate difference. But the large overhead could be local to my hardware :-/. If you want to try it yourself, the code for the test program is listed on the previous page.

In the worst case however, when all rectangles are dirty, performance will suffer. Even when not all rectangles are dirty, the added complexities/overhead with using such a scheme and the additional drawImage calls might make it a less viable option.