Another performance thread...:S

I ve read a lot of java2d performance pages, browsed this forum a lot…

But, i think that i m missing something. I just cant seem to get much performance out of these classes i made…in fullscreen it works fine, but when windowed, it really gives cpu much hard work…You can all see it on that football game we created.

If any1 has some time, could u plz check out these classes to see if i missed something…i would be very grateful.
On second thought i ll post only code which needs reviewing.

GameWindow.java


private GraphicsDevice device;
    private BufferStrategy bs;
    private BufferedImage im;
    private Graphics2D g;
    private Frame window;
    private boolean fullscreen;
    
	
    public GameWindow(int width, int height, boolean fullscreen, int bitDepth, int refreshRate,String name){
        this.fullscreen=fullscreen;
        window=new Frame(name);
        window.setLocationByPlatform(true);
        window.setIgnoreRepaint(true);
        window.setResizable(false);
        window.setLayout(null);
        RepaintManager.currentManager(window).setDoubleBufferingEnabled(false);
        window.setFocusTraversalPolicy(new LayoutFocusTraversalPolicy());
        GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
        device=ge.getDefaultScreenDevice();
        GraphicsConfiguration gc=device.getDefaultConfiguration();
        DisplayMode oldDisplayMode = device.getDisplayMode();
        if(fullscreen){
        	window.setUndecorated(true);
            device.setFullScreenWindow(window);
            device.setDisplayMode(new DisplayMode(width,height,bitDepth,refreshRate));
        }else {
        	window.setSize(width,height);
        	window.addWindowListener(new WindowAdapter() {
        		public void windowClosing(WindowEvent e) {
        			System.exit(1);
        		}});
        	window.setVisible(true);
            
        }
        try{
            BufferCapabilities bc=gc.getBufferCapabilities();
            if(bc.isPageFlipping() && bc.getFlipContents()==BufferCapabilities.FlipContents.PRIOR){
            	window.createBufferStrategy(2,new BufferCapabilities(
                    				new ImageCapabilities(true),
                    				new ImageCapabilities(true),
                    				BufferCapabilities.FlipContents.PRIOR));
            	System.out.println("prior");
            }else if(bc.isPageFlipping() && bc.getFlipContents()==BufferCapabilities.FlipContents.COPIED){
            	window.createBufferStrategy(2,new BufferCapabilities(
        				new ImageCapabilities(true),
        				new ImageCapabilities(true),
        				BufferCapabilities.FlipContents.COPIED));
            	System.out.println("blit");
            }else if(bc.isPageFlipping()){
            	window.createBufferStrategy(2,new BufferCapabilities(
        				new ImageCapabilities(true),
        				new ImageCapabilities(true),
        				BufferCapabilities.FlipContents.UNDEFINED));
            	System.out.println("undefined");
            }else{
            	window.createBufferStrategy(2);
            	System.out.println("else omg");
            }
            bs=window.getBufferStrategy();
        }catch(RuntimeException e){
            System.out.println("Can not enter desired fsem mode!Entering undecorated window mode!");
            device.setDisplayMode(oldDisplayMode);
            device.setFullScreenWindow(null);
            Dimension d=Toolkit.getDefaultToolkit().getScreenSize();
            window.setSize(d);
            im=gc.createCompatibleImage(width,height,Transparency.TRANSLUCENT);
            g=im.createGraphics();
            fullscreen=false;
            window.setVisible(true);
        }
        catch(AWTException aw){
        	window.createBufferStrategy(2);
        }
        bs=window.getBufferStrategy();
    }

Then its 2 most important methods:


  public final Graphics2D getDrawGraphics(){
            return (Graphics2D)bs.getDrawGraphics();
    }
  
    public final void flushGraphics(){
            bs.show();
    } 

That concludes my gamewindow.Next i have 2 classes for loading and storing images.


public final  BufferedImage[] loadImageStrip(String path,int fw,int fh) throws IOException{
		BufferedImage im,strip[];
		Graphics2D g;
		int numr,numc;//number of rows and columns
		int trans,count=0;
		GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsConfiguration gc=ge.getDefaultScreenDevice().getDefaultConfiguration();
		try {
			im=ImageIO.read(getClass().getResource("\u002f"+path).openStream());
			numr=im.getWidth()/fw;
			numc=im.getHeight()/fh;
			strip=new BufferedImage[numr*numc];
			trans=im.getColorModel().getTransparency();
			for(int j=0;j<numc;j++)
			for(int i=0;i<numr;i++){
				strip[count]=gc.createCompatibleImage(fw,fh,trans);
				g=strip[count].createGraphics();
				g.drawImage(im,0,0,fw,fh,i*fw,j*fh,i*fw+fw,j*fh+fh,null);
				g.dispose();
				count++;
			}
			return strip;
		} catch (IOException e) {
			System.out.println("Error in making image strip");
			throw e;
		}
	}
	public final  BufferedImage loadImage(String path)throws IOException{
		GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsConfiguration gc=ge.getDefaultScreenDevice().getDefaultConfiguration();
		BufferedImage im,read;
		try {
			 read=ImageIO.read(getClass().getResource("\u002f"+path).openStream());
		}catch (IOException e) {
			System.out.println("Error in loading image");
			throw e;
		}
		im=gc.createCompatibleImage(read.getWidth(),read.getHeight(),read.getColorModel().getTransparency());
		im.getGraphics().drawImage(read,0,0,null);
		return im;
	}

These 2 methods are basically the same thing, but some1 might need that strip thingie, so i posted it :wink:

And finnaly the methods which alter loaded images:


public static BufferedImage makeTransparentImage(BufferedImage image,Color color) {
		GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsConfiguration gc=ge.getDefaultScreenDevice().getDefaultConfiguration();
		BufferedImage im=gc.createCompatibleImage(image.getWidth(),image.getHeight(),Transparency.BITMASK);
		for(int i=0;i<image.getHeight();i++)
			for(int j=0;j<image.getWidth();j++) {
				int c=image.getRGB(j,i);
				if(c==color.getRGB()) 
					im.setRGB(j,i,0);//make that pixel transparent
				else
					im.setRGB(j,i,c);
			}
	//	im.setAccelerationPriority(1);
		return im;
	}
	public static BufferedImage swapColor(BufferedImage target, Color[] sourceColor, Color[] destinColor){
		if(sourceColor.length<destinColor.length)
            return null;
		GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsConfiguration gc=ge.getDefaultScreenDevice().getDefaultConfiguration();
		BufferedImage image=gc.createCompatibleImage(target.getWidth(),target.getHeight(),Transparency.BITMASK);
		for(int i=0;i<target.getHeight();i++)
			for(int j=0;j<target.getWidth();j++) {
                int c = target.getRGB(j,i);
                image.setRGB(j,i,target.getRGB(j,i));
                for(int z=0;z<destinColor.length;z++) {
                    if(c==sourceColor[z].getRGB()) {
                        image.setRGB(j,i,destinColor[z].getRGB());
                        break;
                    }
                }
			}
	//	image.setAccelerationPriority(1);
		return image;
	}

I hope some1 will make use of this code, but i hope even more that some1 might tell me if there is a problem with it, or m i just pushing windowed mode 2 hard.
Window is 800X600, required fps about 50-60(depends on my mood :)). Test machines varied-amd,intel-from integrated graphics card to GeForce2MX toGeForce 5900xt. Seemed that GeForce 5900 gave the worst results…If u r interested in seeing results start php from my sig.

Tnx in advance.

Sorry, but thats way too much code to read for a quick response.

Run your game with -Dsun.java2d.trace=count, post it here and I’ll tell you whats going wrong.

lg Clemens

Welli know it is huge…i was hoping that some1 might wanna waste some time …:S

Trace is huge as well
28 calls to sun.java2d.loops.Blit::Blit(IntRgb, SrcNoEa, IntRgb)
65 calls to sun.java2d.loops.Blit$GeneralMaskBlit::Blit(ByteBinary4Bit, SrcOverNoEa, IntArgbBm)
21 calls to sun.awt.windows.Win32BlitLoops$DelegateBlitBgLoop::BlitBg(Any, SrcNoEa, “Integer RGB DirectDraw with 1 bit transp”)
224 calls to sun.java2d.loops.MaskBlit$General::MaskBlit(Any, SrcOverNoEa, IntArgb)
4698 calls to sun.awt.windows.Win32BlitLoops::Blit(“Integer RGB DirectDraw”, SrcNoEa, “Integer RGB DirectDraw”)
71 calls to sun.java2d.loops.Blit::Blit(ByteBinary4Bit, SrcNoEa, IntArgb)
7 calls to sun.java2d.loops.Blit::Blit(ByteBinary1Bit, SrcNoEa, IntArgb)
13 calls to sun.java2d.loops.Blit$GeneralMaskBlit::Blit(ByteBinary2Bit, SrcOverNoEa, IntArgbBm)
13 calls to sun.java2d.loops.MaskBlit$General::MaskBlit(ByteBinary2Bit, SrcOverNoEa, IntArgbBm)
12 calls to sun.java2d.loops.MaskBlit$General::MaskBlit(ByteBinary2Bit, SrcNoEa, IntRgb)
18 calls to sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, AnyAlpha, IntRgb)
24 calls to sun.java2d.loops.Blit::Blit(ByteIndexed, SrcNoEa, IntRgb)
80 calls to sun.java2d.loops.Blit::Blit(ByteIndexedBm, SrcOverNoEa, IntArgbBm)
6 calls to sun.java2d.loops.MaskBlit$General::MaskBlit(ByteBinary4Bit, SrcNoEa, IntRgb)
407 calls to sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa, IntArgb)
6 calls to sun.java2d.loops.Blit$GeneralMaskBlit::Blit(ByteBinary4Bit, SrcNoEa, IntRgb)
159779 calls to sun.awt.windows.Win32BlitLoops::Blit(“Integer RGB DirectDraw with 1 bit transp”, SrcOverNoEa, “Integer RGB DirectDraw”)
12 calls to sun.java2d.loops.Blit$GeneralMaskBlit::Blit(ByteBinary2Bit, SrcNoEa, IntRgb)
85 calls to sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, AnyAlpha, IntArgbBm)
6 calls to sun.java2d.loops.FillRect::FillRect(OpaqueColor, SrcNoEa, AnyInt)
11280 calls to sun.java2d.loops.DrawGlyphList::DrawGlyphList(OpaqueColor, SrcNoEa, AnyInt)
7 calls to sun.java2d.loops.MaskBlit$General::MaskBlit(ByteBinary1Bit, SrcOverNoEa, IntArgbBm)
4 calls to sun.java2d.loops.Blit::Blit(ThreeByteBgr, SrcNoEa, IntRgb)
224 calls to sun.java2d.loops.OpaqueCopyAnyToArgb::Blit(Any, SrcNoEa, IntArgb)
224 calls to sun.java2d.loops.Blit$GeneralMaskBlit::Blit(Any, SrcOverNoEa, IntArgb)
982 calls to sun.java2d.loops.Blit::Blit(IntArgbBm, SrcOverNoEa, IntRgb)
25 calls to sun.java2d.loops.Blit::Blit(ByteBinary2Bit, SrcNoEa, IntArgb)
21 calls to sun.java2d.loops.SetFillRectANY::FillRect(AnyColor, SrcNoEa, Any)
1284042 calls to DDFillRect
7 calls to sun.java2d.loops.Blit$GeneralMaskBlit::Blit(ByteBinary1Bit, SrcOverNoEa, IntArgbBm)
65 calls to sun.java2d.loops.MaskBlit$General::MaskBlit(ByteBinary4Bit, SrcOverNoEa, IntArgbBm)
631 calls to sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntArgb)
1463087 total calls to 32 different primitives

I ve noticed that when enabling opengl pipeline i get 60%cpu usage,but without(that is the trace) it is 100%. Dont flame me for this,coz we put java autoinstaler which can only dwnld 1.4, not 1.5 so no opengl pipeline.

hehe, looks like i’ve found performance bottleneck-it is darn drawString method which appears to be slow as a slug…correct me if i m wrong.
We will have to pre render all strings into small images, then draw thise instead of actuall strings…

Hi again,

well in your case it doesn’t look that easy :wink:
Just because of interest are the 60% with Mustang’s (6.0 beta) OpenGL pipeline or with Tiger’s(5.0) one?

1.) Well there are some software-only operations like:

Which could be problematic during gameplay, but I guess most of these happen after startup and should not affect game performance.
Where do you draw the Strings to?

2.) I saw you’re drawing BufferedImage, these are not accalerated under java-1.4. You may better stick with images created with Toolkit.createImage(String imageName) if you don’t need to change them or with Volatile if you would like to draw onto them too.
Please keep in mind that drawing Volatile Images is not only more complex code-wise but also slower under the OpenGL pipeline than drawing accalerated BufferedImages (that is because BufferedImages can be cached in a plain texture whereas a VolatileImage is a pbuffer which causes more overhead. This will change with FBOs).

3.) You blit way too much

I don’t know for how long you’ve had your demo running but is it really needed to draw almost 165.000 images, this is really a lot although they are accalerated as far as I can see (won’t be under 1.4).
Is there no way saving time by using copyArea onto the Buffer or cache often used results in a VolatileImage?

I know this is not the receipe you’ve hoped for :-/
If nothing helps try Netbean’s free profiler, it should help you to easily find the bottlenecks.

Hope this helps, lg Clemens

Well…pointers are nice but…

I can’t find a way to reduce blits significantly-we have 16x9 football field(each tile 4 different pics), 10players(14x8 pics) and background picture and it is all blitted 50 times/sec in that test. If u have any suggestions regarding optimization…:slight_smile:

Anyway i assume that our setting is probably a typical game setting(tiles, players etc.) and windowed performance is quite poor-note that our target is 1.4 platform,cos it can be autodownloaded. Do u have other experience regarding windowed performance?

Also about strings-we profiled app using yourkit profiler and found out that drawString takes a lot of time-also drawPolygon. It seems that it is way more optimal to prerender string into image and then blit it onto surface-though it is a lot more memory expensive…

So it comes to this- is this maxed performance we can expect from 1.4 windowed mode,or did we just mess up something?

Tnx

Just because of interrest, has the output above been generated by java-5+ or 1.4.2? Could it be that you run 1.4 windowed and 1.5+ in fullscreen?

Since as I already mentioned SUN’s java-1.4 implementation and below does not accalerate BufferedImage Blits, which could explain the slow performance you’re experiencing.

You could also try to use a Bitmap-Font implementation, there are tons out there for JME which should be very trivial to port to JSE.

lg Clemens