1

My game engine is drawing a tiled map on a Canvas inside a JFrame. I am using a Bufferstrategy to get smoother rendering and tried to boost the performance at several points inside the program and end up with ca. 120 frames per second to fill a 1920 * 1080 Window with a black color and draw some isometric 512 * 256 tiles on top. When moveing the map around there are small black lines in between the tiles and . I assume that they occur because some tiles have already been moved to the new position, but the image isn´t done yet, when put on the sreen. But I don´t really have a solution to that and I´m also not sure if I´m right.
I should probably metnion that I´m using two threads in my program, one thread calls update and render methods, the other one is currently just getting user input to move the camera position. Also in some lauches everything works smoothly and in others it flickers very bad.

Here is some code of the window creation and rendering:

private final void createWindow(){
    frame = new JFrame(title);
    frame.setMinimumSize(size);
    frame.setMaximumSize(size);
    frame.setPreferredSize(size);

    display = new Canvas();
    display.setMinimumSize(size);
    display.setMaximumSize(size);
    display.setPreferredSize(size);

    image = ImageLoader.boostPerformance(new BufferedImage((int)size.getWidth(), (int)size.getHeight(), BufferedImage.TYPE_INT_RGB));

    graphics = (Graphics2D) image.getGraphics();

    frame.add(display);
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
    frame.requestFocusInWindow();
}

public final void renderToScreen(){
    BufferStrategy bs = display.getBufferStrategy();
    if(bs==null){
        display.createBufferStrategy(bufferCount);
        return;
    }

    graphicsToScreen = (Graphics2D) bs.getDrawGraphics();

    graphicsToScreen.drawImage(image, 0, 0, display.getWidth(), display.getHeight(), null);
    graphicsToScreen.dispose();
    bs.show();
}

I use the Graphics2D graphics object in other classes to draw the tiles.

public void render(Graphics2D g) {
    g.setColor(Color.black);
    g.fillRect(0, 0, getWidth(), getHeight());

    tilemap.render(g);

    g.setColor(Color.red);
    g.drawString(String.valueOf(Main.actualFps), 30, 30);

}

Above is the render method in another class using the graphics object I mentioned. And here you can see the tile map render method:

private final void renderIsometric(Graphics2D g){

    for(int x = 0; x < 4; x++){
        for(int y = 3; y >= 0; y--){
            if(x > tilesX - 1|| y > tilesY - 1)break;
            if(x < 0 || y < 0)continue;
            if(map[x][y] == null)continue;

            g.drawImage(map[x][y].getImage(), (int)orthoToIso(x, y).x - cameraPosX, (int)orthoToIso(x, y).y - cameraPosY, null);
        }
    }
}

Finally here is the main loop to ensure that the fps counter is working and the value of 120 fps is correct:

while(running){
        long now = System.nanoTime();
        double ns = 1000000000.0 / preferredUps;
        delta += (now - lastTime) / ns;
        lastTime = now;
        while(delta >= 1){
            //call the update method 
            update();
            updates++;
            delta--;
        }
        //call the render method 
        render();
        frames++;

        //refresh the actualUps and actualFps once every second
        if(System.currentTimeMillis() - timer > 1000){
            timer+=1000;
            actualUps = updates;
            actualFps = frames;
            updates = 0;
            frames = 0;
        }
    }

How can I get rid of these tiny lags and flicker problems? And additionally I have been wondering if it would give better results to use OpenGL via LWJGL instead of Java2D?


Thanks for your help!

leomoe
  • 15
  • 3

2 Answers2

0

What you see there is most likely not flickering strickly speaking. Its tearing and weaving artifacts. Its a natural consequence of updating the screen without synchronizing to the vertical synchronization of screen refresh.

Windowing systems are in general not made to honor v-sync (they favor performance, as those effects are not grossly perceptible - or important - in a normal UI). As far as I know there is no way of synchronizing with a (J)Frame (you could probably hack something with JNI, asking the OS to tell you when the raster beam is at the top of the frame, but thats beside the point).

Also rendering at 120fps may actually make the problem worse, unless the display device also refreshes at 120hz - when you render more frames than the device actually displays you force the device to display graphics from more than one frame (e.g. frame 1 in the top half of screen, frame 2 in the bottom half). This will always result in perciptible artifacts with moving objects.

There is one way around it in pure java, and thats fullscreen mode (that will wait for v-sync, as a result your rendering rate is limited to and controlled by the refresh rate of the display device).

You can try openGL, but I have no idea if it will synchronize when rendering in windowed mode.

A first step to improve things would be to not render faster than the display devices displays (e.g. max. 60 fps for a 60hz screen).

Durandal
  • 19,919
  • 4
  • 36
  • 70
  • Thank you, the fullscreen mode works fine and it limits the fps to 60. However, since I have a 2560*1080 sreen, the fullsreen mode scales the canvas. Can anyone tell me how to get for example black bars at the sides off the sreen, so that the resolution stays the same? – leomoe Jul 30 '14 at 18:10
  • @leomoe The scaling comes from your implementation in renderToScreen() method: graphicsToScreen.drawImage(image, 0, 0, display.getWidth(), display.getHeight(), null);. If you use drawImage(image, x, y, null) instead, you should get non-scaled display (calculate x/y to center the image on screen or 0,0 to place it top left corner). – Durandal Jul 30 '14 at 18:26
  • oh thanks, should have probably been able to figure that out by myself... but anyways thanks alot! – leomoe Jul 30 '14 at 18:38
  • After testing the program many times I now realize that these artifacts still exist in 1 of 10 runs. I guess there is nothing more I can do about that? – leomoe Jul 31 '14 at 16:55
  • @loemoe Hard to say, may be dependent on the underlying platform, even on the graphics hardware and driver settings. But if its any comfort, I have seen *many* commercial games fail occasionally to get it right on both Windows and PS3, too. Presumably the problem plagues *all* platforms, but those two I had the most exposure to. – Durandal Jul 31 '14 at 17:38
0

In case your application is not time critical, you can resolve flickering by using a double-buffering strategy.

Essentially, the image you are compositing is copied to a secondary off-screen image buffer, which is not rendered this frame, but next frame. There are some tricks you can use to have Swing components double buffer automatically for you, see also:

https://gamedev.stackexchange.com/questions/30921/thread-safe-double-buffering

Double buffered image example in Jpanel

The downside of double buffering is that you introduce one frame of latency, which means your input is only going to result in visible changes one frame later than it would without double buffering. At 120hz this is probably not going to be a big issue, but just for completeness sake..

Community
  • 1
  • 1
StarShine
  • 1,940
  • 1
  • 27
  • 45