1

I'm trying to make my windowed application VSync with the monitor refresh. This code works properly with "fullscreen = true" (reports 75FPS, the monitors refresh rate) but in windowed mode it reports only 32FPS. Anybody know what I'm doing wrong here?

import java.awt.BufferCapabilities;
import java.awt.GraphicsEnvironment;
import java.awt.ImageCapabilities;
import java.awt.image.BufferStrategy;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import sun.java2d.pipe.hw.ExtendedBufferCapabilities;

public class VSyncTest {

    public static void main(String args[]) {
        final boolean fullscreen = true;
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                JFrame v = new JFrame();
                if (fullscreen) {
                    GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(v);
                    v.createBufferStrategy(2);
                } else {
                    v.setVisible(true);
                    ExtendedBufferCapabilities b = new ExtendedBufferCapabilities(new BufferCapabilities(new ImageCapabilities(true), new ImageCapabilities(true), BufferCapabilities.FlipContents.PRIOR), ExtendedBufferCapabilities.VSyncType.VSYNC_ON);
                    try {
                        v.createBufferStrategy(2, b);
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new RuntimeException(e);
                    }
                }
                new Thread() {
                    @Override
                    public void run() {
                        BufferStrategy bs = v.getBufferStrategy();
                        long start = System.nanoTime(), frames = 0;
                        do {
                            bs.getDrawGraphics();
                            bs.show();
                            frames++;
                            long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                            long framesPerS = (long) (frames / (elapsed / 1000D));
                            if (frames % 60 == 0) {
                                System.out.println("Frames/s: " + framesPerS + ", Elapsed: " + elapsed / 1000);
                            }

                        } while (true);
                    }
                }.start();
            }
        });
    }
}
Colby
  • 452
  • 4
  • 19

2 Answers2

2

I will try an answer although the problem doesn't seem to be a Java problem per se, it seems to be a graphics one. So first of all this answer is based on the configuration that I'm testing, Windows 10, DELL laptop with Intel graphics card, 2560x1440 monitor.

Secondly a comment for your code, the following setup failed miserably on my configuration:

ExtendedBufferCapabilities b = new ExtendedBufferCapabilities(
    new BufferCapabilities(new ImageCapabilities(true), new ImageCapabilities(true), BufferCapabilities.FlipContents.PRIOR),
ExtendedBufferCapabilities.VSyncType.VSYNC_ON);

It caused flicker and the frame counter went crazy. If you look into the code that createBufferStrategy(2) uses -the overload without the capabilities- it uses FlipContents.UNDEFINED not FlipContents.PRIOR. Regardless I would suggest you use

GraphicsEnvironment.getLocalGraphicsEnvironment()
          .getDefaultScreenDevice()
          .getDefaultConfiguration()
          .getBufferCapabilities()

in order to get the appropriate device capabilities. That stopped flickering and fixed the refresh rate meter to 60 fps which is the same as the one I get running full screen.

One other thing is that I tried to set an actual size to the frame and remove the decoration. Calling setUndecorated(true) doesn't seem to make any difference, although it's suggested to call it before you enter full screen. Finally, the counter will go off again if you resize the window so see if you can survive with frame.setResizable( false ); and if you move your frame to another monitor, then the vsync is lost -you can actually see the frame counter going crazy again. Unfortunately I'm not a graphics expert to explain these, though if I recall correctly, a buffer is allocated on the device and needs to be invalidated when resizing takes place -that's why full screen makes sense after all. I don't remember where I've read this though.

Stelios Adamantidis
  • 1,866
  • 21
  • 36
0

I created a test setup based on the code provided, and it exhibited exactly the same behavior as described.

I applied the suggestions given by Stelios Adamantidis, which are useful suggestions, but they made no difference.

Finally, the following embarrassingly trivial change made all the difference:

jFrame.setSize( new Dimension( 800, 600 ) );

With this change, I am enjoying 60 fps both when windowed and when full-screen.

In other words, when running windowed, (non-full-screen,) you have to give your JFrame a certain size so that it is not completely collapsed, otherwise Swing gets confused about the framerate. (Go figure.)

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142