Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (517)
Games in Android Showcase (123)
games submitted by our members
Games in WIP (578)
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  
  [SOLVED] Java2D drawing speed (need help with performance).  (Read 3730 times)
0 Members and 1 Guest are viewing this topic.
Offline Donald_W

Senior Newbie





« Posted 2012-04-02 10:07:13 »

Since it is my first post I would like to say hello everybody Smiley

The goal of my program is to test how many FPS I can get while consnstantly drawing on the screen.
I would really appreciate if you can point any bottlenecks or suggest improvements.

The problem is that on one machine I can get around 2700 FPS, on the other with similiar hardware setup (but different OS) I get like 60-70.

If you want to compile those codes just remember to set image paths.

First my interface and implementation of FPS counting.
1  
2  
3  
4  
public interface FPS {
    int getFPSFrames();
    void setFPSFrames(int value);
}

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
public class FPSTask extends TimerTask {
    private FPS fps;

    public FPSTask(FPS fps) {
        this.fps = fps;
    }

    public void run() {
        System.out.println("Last: " + System.currentTimeMillis() + " frames: " + fps.getFPSFrames());
        fps.setFPSFrames(0);
    }
}

Bitmap with Screen based on the Catacomb Snatch sources.
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  
77  
public class Bitmap {
    protected int w, h;
    protected int pixels[];

    public Bitmap(int w, int h) {
        this(w, h, true);
    }

    protected Bitmap(int w, int h, boolean createPixels) {
        this.w = w;
        this.h = h;
        if (createPixels) {
            pixels = new int[w * h];
        }
    }

    public void blit(Bitmap bitmap, int x, int y) {
        Rectangle blitArea = new Rectangle(x, y, x + bitmap.w, y + bitmap.h);
        adjustBlitArea(blitArea);
        int blitWidth = blitArea.x2 - blitArea.x1;
        for (int yy = blitArea.y1; yy < blitArea.y2; yy++) {
            int targetPixel = yy * w + blitArea.x1;
            int sourcePixel = (yy - y) * bitmap.w + (blitArea.x1 - x);
            targetPixel -= sourcePixel;
            for (int xx = sourcePixel; xx < sourcePixel + blitWidth; xx++) {
                int col = bitmap.pixels[xx];
                int alpha = (col >> 24) & 0xff;
                if (alpha == 0xff) {
                    pixels[targetPixel + xx] = col;
                } else {
                    int bgCol = pixels[targetPixel + xx];
                    pixels[targetPixel + xx] = blendColors(bgCol, col);
                }
            }
        }
    }

    private Rectangle adjustBlitArea(Rectangle blitArea) {
        if (blitArea.x1 < 0) blitArea.x1 = 0;
        if (blitArea.y1 < 0) blitArea.y1 = 0;
        if (blitArea.x2 > w) blitArea.x2 = w;
        if (blitArea.y2 > h) blitArea.y2 = h;
        return blitArea;
    }

    public int getWidth() {
        return w;
    }

    public int getHeight() {
        return h;
    }

    public static Bitmap load(String filename) {
        try {
            BufferedImage bi = ImageIO.read(new File(filename));
            int biWidth = bi.getWidth();
            int biHeight = bi.getHeight();
            Bitmap bitmap = new Bitmap(biWidth, biHeight);
            bi.getRGB(0, 0, biWidth, biHeight, bitmap.pixels, 0, biWidth);
            return bitmap;
        } catch (IOException e) {
            return null;
        }
    }

    public static int blendColors(int backgroundColor, int colorToBlend) {
        RGB bgRGB = new RGB(backgroundColor);
        RGB pixelRGB = new RGB(colorToBlend);
        int bgAlpha = 256 - pixelRGB.alpha;
        int r = ((pixelRGB.r * pixelRGB.alpha + bgRGB.r * bgAlpha) >> 8) & 0xff0000;
        int g = ((pixelRGB.g * pixelRGB.alpha + bgRGB.g * bgAlpha) >> 8) & 0xff00;
        int b = ((pixelRGB.b * pixelRGB.alpha + bgRGB.b * bgAlpha) >> 8) & 0xff;
        return 0xff000000 | r | g | b;
    }

}

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
public class Screen extends Bitmap {
    protected final BufferedImage image;

    public Screen(int w, int h) {
        super(w, h, false);
        image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
    }

    public Image getImage() {
        return image;
    }

}


Helper classes used in Bitmap.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
public class Rectangle extends java.awt.Rectangle {
    public int x1, y1, x2, y2;

    public Rectangle(int x1, int y1, int x2, int y2) {
        super(x1, y1, x2 - x1, y2 - y1);
        this.x1 = x1;
        this.y1 = y1;
        this.x2 = x2;
        this.y2 = y2;
    }

}

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public class RGB {
    public int r, g, b;
    public int alpha;

    public RGB(int color) {
        r = color & 0xff0000;
        g = color & 0xff00;
        b = color & 0xff;
        alpha = color & 0xff000000;
    }
}


And finally whole program (based on ra4king's game skeleton):
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  
public class TestJFrame extends JFrame implements FPS, Runnable {
    public static final int MAX_OBJECTS = 500;

    private int frames;
    private Screen screen;

    private Bitmap background;
    private Bitmap object;

    private Canvas canvas;

    public TestJFrame() {
        setSize(640, 480);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setVisible(true);
        setIgnoreRepaint(true);
        screen = new Screen(getWidth(), getHeight());
        canvas = new Canvas();
        canvas.setSize(getWidth(), getHeight());
        add(canvas);
        pack();
        background = Bitmap.load("your_path_to_any_background.png"); //TODO set path, my background was 640x480.
        object = Bitmap.load("your_path_to_any_object"); //TODO set path, my object was 32x32.
    }

    public static void main(String[] args) {
        TestJFrame testJFrame = new TestJFrame();
        Thread t = new Thread(testJFrame);
        t.start();
    }

    public void run() {
        Timer timer = new Timer();
        FPSTask fpsTask = new FPSTask(this);
        timer.schedule(fpsTask, 0, 1000);
        canvas.createBufferStrategy(2);
        canvas.setIgnoreRepaint(true);
        BufferStrategy bs = canvas.getBufferStrategy();
        while (true) {
            do {
                do {
                    Graphics2D g = (Graphics2D) bs.getDrawGraphics();
                    render(g);
                    g.dispose();
                } while (bs.contentsRestored());
                bs.show();
            } while (bs.contentsLost());
            frames++;
        }
    }

    private void render(Graphics g) {
        g.setColor(Color.black);
        draw(MAX_OBJECTS);
        g.drawImage(screen.getImage(), 0, 0, getWidth(), getHeight(), null);
    }

    private void draw(int objects) {
        screen.blit(background, 0, 0);
        for (int i = 0; i < MAX_OBJECTS; i++) {
            screen.blit(object, (int) (Math.random() * 640), (int) (Math.random() * 480));
        }
    }

    public int getFPSFrames() {
        return frames;
    }

    public void setFPSFrames(int value) {
        frames = value;
    }
}
Offline gimbal

JGO Knight


Medals: 25



« Reply #1 - Posted 2012-04-02 11:03:05 »

2700 = Windows, 60-70 = Linux perhaps? You may want to try explicitly turning on the OpenGL pipeline to see if that changes anything.

But you have also run into Java2D's major downfall and one of the primary reasons why people tend to avoid it in favor of LWJGL or JOGL - the API is highly unpredictable and it is across not only platforms, but also hardware and even minor versions of Java :s You can't even really call it an API as an API is supposed to hide implementation details; with Java2D you *must* know what is going on under the hood in order to get any kind of performance out of it.

That being said, you can turn on trace logging to see what drawing calls Java2D is doing under the hood; with that logging you can see if it is using the hardware pipeline or the software pipeline.

-Dsun.java2d.trace=log
Offline nsigma
« Reply #2 - Posted 2012-04-02 11:23:43 »

2700 = Windows, 60-70 = Linux perhaps? You may want to try explicitly turning on the OpenGL pipeline to see if that changes anything.

But you have also run into Java2D's major downfall and one of the primary reasons why people tend to avoid it in favor of LWJGL or JOGL - the API is highly unpredictable and it is across not only platforms, but also hardware and even minor versions of Java :s You can't even really call it an API as an API is supposed to hide implementation details; with Java2D you *must* know what is going on under the hood in order to get any kind of performance out of it.

Vast majority of that code looks like it's a non-Java2d software renderer though.  Only bit of Java2d looks like the buffer strategy.  Pure Java software renderers tend to be quite similar performance across VM's.  More likely with those figures is that one of the canvas buffer strategies is vsynced.

Praxis LIVE - open-source intermedia toolkit and live interactive visual editor
Digital Prisoners - interactive spaces and projections
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline cylab

JGO Ninja


Medals: 52



« Reply #3 - Posted 2012-04-02 11:27:42 »

Quote
60-70

Forced VSync? How's the CPU usage?

Mathias - I Know What [you] Did Last Summer!
Offline Donald_W

Senior Newbie





« Reply #4 - Posted 2012-04-02 11:42:49 »

@gimbal: yes, 2700 on Windows, 60-70 on Linux. Here's my log:
1  
2  
3  
4  
5  
6  
7  
8  
X11FillRect
X11FillRect
sun.java2d.loops.FillRect::FillRect(AnyColor, SrcNoEa, AnyInt)
sun.java2d.loops.FillRect::FillRect(AnyColor, SrcNoEa, AnyInt)
sun.java2d.loops.FillRect::FillRect(AnyColor, SrcNoEa, AnyInt)
sun.java2d.loops.FillRect::FillRect(AnyColor, SrcNoEa, AnyInt)
sun.java2d.loops.Blit::Blit(IntRgb, SrcNoEa, IntRgb)
X11FillRect

Followed by lots of (I guess this is invoked in the loop):
1  
sun.java2d.loops.TransformHelper::TransformHelper(IntArgb, SrcNoEa, IntArgbPre)


Also I have tried -Dsun.java2d.opengl=true, but got performance loss.

@nsigma:
I swapped the code to use BufferedImage but only lost performance. In my code bitmaps are arrays of int, so drawing is in fact done on arrays and I blit my screen (Screen class) every render on the physical screen. Can that be a bottle neck?

@cylab:
CPU usage: core1: 60-80%, core2: 40-50%, cores3-8 are under 5%
What should I do with VSync? Is there a way to turn it off (would I get screen flickering then)?
Offline gimbal

JGO Knight


Medals: 25



« Reply #5 - Posted 2012-04-02 12:36:16 »

Also I have tried -Dsun.java2d.opengl=true, but got performance loss.

Yeah when you only do manual drawing stuff in stead of blitting primitives the performance is likely better when using the software pipeline. (forced) vsync seems like a candidate to explain the results; it may be that it is enforced because OS drawing functions are used (X11FillRect). Not to familiar with Linux / X11, I can only make haphazard guesses there.

> I swapped the code to use BufferedImage but only lost performance.
That will probably depend on if the BufferedImage is a "compatible" image or not. Transparency can also be a bottleneck.
Offline nsigma
« Reply #6 - Posted 2012-04-02 12:52:37 »

I swapped the code to use BufferedImage but only lost performance. In my code bitmaps are arrays of int, so drawing is in fact done on arrays and I blit my screen (Screen class) every render on the physical screen. Can that be a bottle neck?

Think you missed the point of mine and cylab's comments - if it's caused by VSync you don't have a performance bottleneck across the two OS's.  VSync is good, as it will stop tearing and it's pointless running faster than the screen can update anyway.  If you just want to check the performance of the code across the two OS's, try taking out the blit to screen and just running the rest of your code - they'll almost certainly come out about equal performance.

Unless you can take advantage of Java2D hardware acceleration, working directly with arrays of int is often faster than using the Java2D software renderer.  It's the same mechanism I use in the optimized software renderer for Praxis, and I use LWJGL and some code forked from libGDX for the OpenGL renderer (all of which you can read as Java2D is often a PITA!  Smiley )

Praxis LIVE - open-source intermedia toolkit and live interactive visual editor
Digital Prisoners - interactive spaces and projections
Offline Donald_W

Senior Newbie





« Reply #7 - Posted 2012-04-02 13:38:57 »

Huh, without rendering on the screen (only blitting on bitmap) I get like 250-260 FPS. I will test my code at Windows machine when I get back home.
Also I have uploaded sources to GitHub: https://github.com/tomaszwojcik/java2d-graphics-engine
Offline 65K
« Reply #8 - Posted 2012-04-02 13:48:28 »

Try this on Linux (with Java 7):
-Dsun.java2d.xrender=true

Offline Donald_W

Senior Newbie





« Reply #9 - Posted 2012-04-02 13:55:43 »

@65K: Now I have 230 FPS (With rendering on screen). So it is a great improvement!

Thanks a lot guys!
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.

DarkCart (6 views)
2014-10-31 21:44:48

DarkCart (7 views)
2014-10-31 21:43:57

TehJavaDev (38 views)
2014-10-27 03:28:38

TehJavaDev (29 views)
2014-10-27 03:27:51

DarkCart (43 views)
2014-10-26 19:37:11

Luminem (24 views)
2014-10-26 10:17:50

Luminem (29 views)
2014-10-26 10:14:04

theagentd (35 views)
2014-10-25 15:46:29

Longarmx (63 views)
2014-10-17 03:59:02

Norakomi (61 views)
2014-10-16 15:22:06
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

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