what to expect ....

Hello, folks.
What would you expect from the following list?

  • a game running in 800*600
  • using java2D only.
  • working under 1.4.2 and 1.5
  • sprites loaded from 32 bits png files.
  • having -each frame- between 30 to 120 sprites alphacomposited, all drawn using affineTransforms. Most would be rotated and/or scaled differently every frames.
  • sprites original size ranging from 6464 to 256128 (player avatar) Player avatar would be rotated using AffineTransform, but not zoomed. Some sprites would be zoomed two times and drawn 512 * 256.
  • sprites can have from 10 to 50% surface transluscent, up to about 50% transparent and up to 50% opaque.
  • most sprites displayed would also be composited using their own global alpha.
  • some sprites would use vector shapes (Area) for clipping and avoid being drawn partially. shapes would be recreated every frame as they are non mutable and contain over 60 lines. Two would be used each frame.
  • drawn using double buffering, and a default bufferStrategy.
  • no object pooling. No care taken to avoid object creation. (sparkles from shoots would be objects allocated and unallocated once unused, so would be those of the ship)
  • no dirty zones, the game would be completely scrolling. each frame being a completely new one. Standard side scrolling game.
  • under 200Kb compressed jar including GageTimer and the windows DLL. (first shot without menu, no real death. Game starts directly and quits once you’re dead. Rude one at first. :slight_smile: )
  • sprite collisions would be done using AffineTransform-ed Areas, (10 to 16 connected lines), using the AWT methods to check for overlapping. Nevertheless, most of the collisions would occur using Rectangle, but collisions would be checked over almost all of the sprites.
  • the game would be time compensated, so whatever the framerate, it would react the same. (that implies that there are multiplications and divisions everywwhere for anything)

What framerate would you expect on a 1Ghz machine using a geforce 2 under windows and a plain 1.4.2 ( no transaccel, no ddrawScale ). How do you expect linux to behave?
Do you expect GC pauses? how long?
Do you expect problems playing the game under the 1.5b2 GL pipeline?

I’d suspect you would get less <60fps. Lots of GC pauses on older VMs. At the moment 1.5b GL pipeline isn’t showing me any benefits in performance. Linux generally renders reasonably but doesn’t perform quite as well (although since you’re back to software rendering you might not notice much difference).

Transluceny + AffineTransforms = Software Rendering

Kev

What do you think would performance be under those conditions?

Also, what is the minimum framerate you’d expect for a game to be playable? (think rtype or equivalent)

I’d expect <5fps, under 1.4.2 without any flags set.

As kev said, if you are alpha compositing and affinetransforming, you realy dont want your back buffer to be in vram.

If you changed the BufferStrategy to a BufferedImage backbuffer, you would have faster alpha compositing.

However, the fillrate for drawing an 800x600 software backbuffer to the screen would also hit your framerate alot.
I’d say 10-15fps using a software backbuffer @ 800x600.

I don’t think your collision detection using Area calcs would be your major bottleneck unless you were using the 1.5 OGL pipeline.
If you were using the 1.5 OGL pipeline and it was functioning correctly it should give you 60+fps.

I would expect no less than 60fps on a modern machine, but I suspect that your combination of factors will lead you to get 15fps. 30fps is about the minimum for a professional look.

Cas :slight_smile:

the stuff you described is almost exactly my game you’ve been helping me get ideas to speed up :wink: And it runs at 60fps on my machine and yours no problem. Of course I actually TRY to make it fast…

Thanks for your answers.

For you, what would be the specs of a modern machine, Cas?
For you that did statistics about gamer’s machine, is the machine i gave in my specs a good representative of the market?

Malohkan:
It’s true that there are things that look like.
Nevertheless, you don’t have as much transformed sprites, you have very sparse translucency, i saw no global transparency applied to sprites, and you don’t seem to use polygon clipping. Those are important and are part of the specs.
i can’t see the FPS anymore, is that normal?

Only 60 Sprites (33% opaque, 33% transparent, 33% translucent)
No Collision Detection,
No Scaling,
etc etc

On an Athlon1.33 with GF2gts, I get
9-12fps in both 1.4.2 & 1.5.

In 1.5 with the OpenGL pipeline enabled, I get more than 180fps.


import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import java.awt.geom.*;
public class TinyTest extends Frame      implements Runnable, KeyListener
{
      volatile boolean running = true;
      Sprite [] sprites;
      
      Random rnd = new Random();
      
      public TinyTest(GraphicsDevice gd)
      {
            setUndecorated(true);
            addKeyListener(this);
            
            gd.setFullScreenWindow(this);
            DisplayMode      dm = new DisplayMode(800,600,32,60);
            gd.setDisplayMode(dm);
            createBufferStrategy(2);
            
            GraphicsConfiguration gc = this.getGraphicsConfiguration();
            
            BufferedImage [] images      = new BufferedImage[60];
            
            images[0] =      gc.createCompatibleImage(256,128,Transparency.TRANSLUCENT);
            for(int      i =1;i<images.length;i++)
            {
                  images[i] =      gc.createCompatibleImage(64,64,Transparency.TRANSLUCENT);
            }
            
            for(int      i =      0;i< images.length;i++)
            {
                  int      width =      images[i].getWidth();
                  int      height = images[i].getHeight();

                  Graphics2D g2d = images[i].createGraphics();
      
                  int      color =      rnd.nextInt();
                  g2d.setPaint(new GradientPaint(width/3,0,new Color(color),(width*2)/3,0,new      Color(color&0x00FFFFFF,true)));
                  g2d.fillRect(width/3,0,(width*2)/3,height);
                  
                  g2d.setColor(new Color(rnd.nextInt()));
                  g2d.fillRect(0,0,width/3,height);
                  
                  g2d.setColor(Color.black);
                  g2d.drawRect(0,0,width-1,height-1);
                  
                  g2d.dispose();
            }
            
            sprites      = new Sprite [images.length];
            
            for(int      i =      0;i      < sprites.length;i++)
            {
                  sprites[i] = new Sprite(images[i],
                                                      rnd.nextInt(800),//x
                                                      rnd.nextInt(600),//y
                                                      rnd.nextInt(2)*2-1,//dx
                                                      rnd.nextInt(2)*2-1,//dy
                                                      rnd.nextDouble()*Math.PI*2,
                                                      (rnd.nextInt(2)*2-1)*Math.PI/72);
            }
            new      Thread(this).start();
      }
      
      public void      keyPressed(KeyEvent      ke)
      {
            if(ke.getKeyCode()==KeyEvent.VK_ESCAPE)
            {
                  running      = false;
            }
      }
      
      public void      keyReleased(KeyEvent ke){}
      
      public void      keyTyped(KeyEvent ke){}
      
      public void      run()
      {
            float globalAlpha =      0.0f;
            float alphaStep      = 0.05f;
            
            long prevTime =      System.currentTimeMillis();
            int      counter      = 0;
            
            String fps = "N/A";
            
            while(running)
            {
                  float newAlpha = globalAlpha+alphaStep;
                  if(newAlpha>1.0f ||      newAlpha < 0.0f) alphaStep=-alphaStep;
                  
                  globalAlpha+=alphaStep;
                  
                  Graphics2D g2d = (Graphics2D)(getBufferStrategy().getDrawGraphics());
                  g2d.setColor(Color.white);
                  g2d.fillRect(0,0,800,600);
                  //g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,globalAlpha));
                  g2d.setColor(Color.yellow);
                  for(int      i =      0;i      < sprites.length;i++)
                  {
                        sprites[i].update();
                        sprites[i].render(g2d);
                  }
                  counter++;
                  long timeDifference      = System.currentTimeMillis()-prevTime;
                  if(timeDifference>=1000)
                  {
                        fps      = Float.toString((counter*1000f)/timeDifference);
                        prevTime+=timeDifference;
                        counter=0;
                  }
                  g2d.setComposite(AlphaComposite.SrcOver);
                  g2d.setColor(Color.black);
                  g2d.drawString(fps,50,50);
                  g2d.dispose();
                  getBufferStrategy().show();
            }
            dispose();
      }
      
      
      public static boolean OPENGL = true;
      
      public static void main(String[] args)
      {
            if(OPENGL) System.setProperty("sun.java2d.opengl","True");
            new      TinyTest(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice());
      }
      
      static class Sprite
      {
            BufferedImage image;
            int      x,y,dx,dy; //position &      velocity
            int      width, height; //dimensions
            double angle, dangle; //angle &      angular      velocity
            
            public Sprite(BufferedImage      image, int x, int y, int dx, int dy, double      angle, double dangle)
            {
                  this.image = image;
                  this.x = x;
                  this.y = y;
                  this.width = image.getWidth();
                  this.height      = image.getHeight();
                  this.dx      = dx;
                  this.dy      = dy;
                  this.angle = angle;
                  this.dangle      = dangle;
            }
            
            public void      update()
            {
                  x+=dx;
                  y+=dy;
                  angle=(angle+dangle)%(Math.PI*2);
                  
                  if(x<0 || x>800)
                  {
                        dx=-dx;
                  }
                  if(y<0 || y      > 600)
                  {
                        dy=-dy;
                  }
            }
            
            public void      render(Graphics2D g2d)
            {
                  AffineTransform      af = new AffineTransform();
                  af.setToTranslation(x,y);
                  af.rotate(angle);
                  af.translate(width/-2,height/-2);
                  g2d.drawImage(image, af, null);
                  g2d.drawLine(x,y,x+dx,y+dy);
            }
      }
}

abuse:
Your test runs at 11fps on my machine, and i have to admit i’m a bit puzzled. Please read your PM.
My first thought is that this microbenchmark thing is not right.

I actually have a 2.8Ghz p4 and a radeon 9700. Is that a uber machine, or can it be elected to the rank of ‘modern machine’?

Not suprising realy,
The CPU & graphics card have very little to do with the app. speed when performing software operations (AlphaComposite & AffineTransform) on a surface in vram.

The speed limiting factor is bus speed - and, your results being so close to mine makes me think your machine is running its AGP port at 4x, rather than 8x or above.

With opengl enabled, I would expect your machine to get 400-500fps, as the radeon 9700 is far superior to my GF2gts, and twice the CPU speed can’t hurt either =)

abuse:
i can tell you it’s surprising. please read your PM (private board messaging…) you have a message…

I adjusted my little TinyTest, so it is closer to what yours is doing - and got a comparable framerate. (23-24fps)

1 interesting thing I noticed.

Windowed mode appears to be faster when performing software operations on vram surfaces. (between 10-20% faster)

So it would seem that either :-

  1. Read-back from vram is quicker when operating in windowed mode.
    I can’t however see why this would be so.

or

  1. The software rendering loops for performing the AffineTransform/AlphaComposite are faster when in windowed mode.
    This could possibly be explained if the pixel format in windowed mode is compatible with the software loops,
    and the pixelformat for fullscreen is not (and so requires the color components be reordered)

Either way it does seem abit peculiar :-/

comparable… not that sure.
there are about 50 sprites (outside player, sparks, and the rest) blitted with a big clipping zone each frame. (sea and sky which are not in the actor count) and i saw no global alpha applied to your sprites.
If that is no cost, it’s nice, but i doubt it is.
Anyway, it’s not that bad.

[quote]comparable… not that sure.
there are about 50 sprites blitted with a big clipping zone each frame. (sea and sky) and i saw no global alpha applied to your sprites.
If that is no cost, it’s nice, but i doubt it is.
Anyway, it’s not that bad.
[/quote]
I presume the sea and sky pre-rendered images(rather than a runtime GradientPaint),
and they are not transformed or drawn with alpha?
If those 2 assumptions are true, then its equivalent to simply clearing the back buffer each frame - which my example already does.

As for global Alpha, yeah I commented that out, dunno why :slight_smile:
Putting it back in costs almost nothing, around 1fps drop.

p.s.

I tried using a BufferedImage for the backBuffer.

and, while this sped up the AlphaCompositing (avoided the vram readback), the cost of blitting the 800x600 BufferedImage to the screen is so high, it results in no gain at all (infact, a slight drop).

If you lower the res. to 640x480, or increase the number of Sprites rendered per frame, the benefit of a software backbuffer is more apparent.

it’s true that they are not runtime rendered, nor transformed or alpha blit. nevertheless, when i don’t render them, i get 6 more FPS.
did not try changing global alpha, btw.

should post the gamechmark officially by wednesday.

I’m targeting 700MHz and GeForce 1 - level cards as my min spec now.

Cas :slight_smile:

Abuse - I ran the test you wrote and got the same as others, 6 - 8 fps on a 1.3 GHz with Java 1.4.2. I uninstalled 1.5.x, so I can’t check the openGL pipeline.

At any rate, I noticed you are using a BufferStrategy. Is this the better/best way of double buffering on the JFrame? I’ve got an app where I declared a BufferedImage and then clear it each frame. Then I draw to it, then blit it to the graphics context using drawImage(xxx). It seems like maybe I’m doing work I don’t need to.

Does it amount to the same thing roughly?

It depends what you are blitting to it, but in most cases using BufferStrategy will be alot faster.

BufferStrategy is implemented as a VolatileImage, this means the backBuffer is in vram, not system ram - hence any blits to it will be performed by the GPU not CPU - making the blits almost free.(the only CPU cost remaining, is the cost to tell the GPU to perform the blit)

I have uploaded the gamechmark®©(*) that corresponds to that specifications. It is webstartable and small. Please try it and report statistics for your machine !!

http://frederic.barachant.com
In programming section.

thanks all for your advice !!

8fps on my GF2, Fullscreen, no throttle, Thread.sleep()

Quite a fun game too :slight_smile: Incidently, didn’t revert to the original display mode until I closed the little configuration window which was a bit odd.

GoSub - ace name btw.

Kev