Stuttering in very simple Java2D fullscreen exclusive mode game on MS Windows

Hi!

I’m trying to write a simple game using Java2D and fullscreen exclusive mode api. I have followed the tutorials and examples, but I am not able to get

this little demo working smoothly on Vista. Mac OS X runs without problems! I have attached the source. A prebuilt JAR is available from http://gambitchess.org/moin.py/StutterDemo

Using the vsync hack as described in http://www.java-gaming.org/forums/index.php?topic=14696.0 does help a bit (framerate is locked to 60 Hz then), but

it stutters anyways. For running with vsync hack, please make sure, that pxsync.dll is in your working directory.

Thank you!

Specs: JDK 1.6, Intel Core 2 Duo, Windows Vista 32-bit, ATI Radeon X1600, DirectX Games work fine…

Source of Main.java:



/** send comments and ideas to sebastian.urban@gmx.de */

package stutterdemo;

import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferStrategy;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

// uncomment this and other lines below to enable vsync hack
//import de.pxlab.pxl.VideoSync;

/** This demo shows stuttering of simple graphics in fullscreen-mode using Java2D
 * on MS Windows.
 *
 * NOTE: This demo runs fine on Mac OS X!
 */
public class Main extends Frame implements Runnable {
    
    /** set to true to enable logging of frame delays */
    final boolean writeLog = false;
    
    /** path to logfile (make sure you have write permissions, especially on Vista!) */
    final String logPath = "C:\\users\\public\\StutterDemoLog.csv";
    
    /** set to true to change display mode at startup */
    final boolean changeDisplayMode = false;
    
    /** list of preferred display modes */
    private static DisplayMode[] BEST_DISPLAY_MODES = new DisplayMode[] {
        //new DisplayMode(1680, 1050, 32, 0),
        new DisplayMode(1024, 768, 32, 0),
        new DisplayMode(1024, 768, -1, 0), // for Linux
    };
    
    /** graphics device for fullscreen mode */
    GraphicsDevice device;
    
    /** used to stop the game thread on key press */
    boolean gameShouldRun = true;
    
    /** x coordinate of the box in the simulation */
    double boxX = 10;
    
    /** Creates a new instance of Main */
    public Main(GraphicsConfiguration gc, GraphicsDevice device) {
        super(gc);
        
        this.device = device;
        
        addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                gameShouldRun = false;
            }
        });
        
        setResizable(false);
        setUndecorated(true);
        setIgnoreRepaint(true);
        device.setFullScreenWindow(this);
        setVisible(true);
        
        if (!device.isFullScreenSupported()) {
            JOptionPane.showMessageDialog(null, "Fullscreen mode not supported!");
            System.exit(1);
        }
        if (changeDisplayMode) {
            if (!device.isDisplayChangeSupported()) {
                JOptionPane.showMessageDialog(null, "Display mode change not supported!");
                System.exit(2);
            }
            chooseBestDisplayMode(device);
        }
        
        createBufferStrategy(2);
        new Thread(this).start();
    }
    
    
    /** gets best display mode */
    private static DisplayMode getBestDisplayMode(GraphicsDevice device) {
        for (int x = 0; x < BEST_DISPLAY_MODES.length; x++) {
            DisplayMode[] modes = device.getDisplayModes();
            for (int i = 0; i < modes.length; i++) {
                if (modes[i].getWidth() == BEST_DISPLAY_MODES[x].getWidth()
                        && modes[i].getHeight() == BEST_DISPLAY_MODES[x].getHeight()
                        && modes[i].getBitDepth() == BEST_DISPLAY_MODES[x].getBitDepth()
                        ) {
                    return modes[i];
                }
            }
        }
        return null;
    }
    
    /** sets the display mode to the preferred mode */
    protected static void chooseBestDisplayMode(GraphicsDevice device) {
        DisplayMode best = getBestDisplayMode(device);
        if (best != null) {
            device.setDisplayMode(best);
        }
    }
    
    
    /** game loop thread */
    public void run() {
        BufferStrategy bufferStrategy = getBufferStrategy();
        
        BufferedWriter logWriter = null;
        
        long startTime = System.nanoTime();
        long lastTickStartTime = System.nanoTime();
        long frameCount = 0;
        long lastFrameCountStartTime = lastTickStartTime;
        long frameRateCalcInterval = 40000 * 1000 * 1000;
        double frameRate = 0;
        long totalFrames = 0;
        
        if (writeLog) {
            try {
                logWriter = new BufferedWriter(new FileWriter(logPath));
                logWriter.write("Frame; lastTickDuration (mSec); Time since start (mSec); boxX\n");
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        
        // GameLoop
        while (gameShouldRun) {
            long lastTickDuration = System.nanoTime() - lastTickStartTime;
            lastTickStartTime = System.nanoTime();
            
            if (logWriter != null) {
                try {
                    logWriter.write(String.format("%d; %d; %f; %f\n", totalFrames, lastTickDuration / (1000 * 1000),
                            (float) (lastTickStartTime - startTime) / (1000f * 1000f),
                            boxX));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
                totalFrames++;
            }
            
            // calc framerate
            frameCount++;
            if (lastFrameCountStartTime + frameRateCalcInterval < lastTickStartTime) {
                frameRate = (double) frameCount / (double) (lastTickStartTime - lastFrameCountStartTime) * 1000 * 1000 * 1000;
                frameCount = 0;
                lastFrameCountStartTime = lastTickStartTime;
            }
            
            // run simulation step
            simulation(lastTickDuration);
            
            // redraw
            Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
            if (!bufferStrategy.contentsLost()) {
                drawScene(g);
                g.setColor(Color.WHITE);
                g.drawString(String.format("%.0f", frameRate), getWidth() - 50, getHeight());
                g.dispose();
                
                // UNCOMMENT the following to enable vsync hack
                // does help, but with it, it stutters every 3-4 seconds
                //if (VideoSync.isEmulated()) // make sure it's not emulated
                //    System.exit(3);
                //VideoSync.waitForBeginOfVerticalBlank();
                
                bufferStrategy.show();
            }
            
            // run event thread, otherwise key events get stuck
            Thread.yield();
        }
        
        // clean up
        try {
            if (logWriter != null)
                logWriter.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                device.setFullScreenWindow(null);
                dispose();
            }
        });
    }
    
    /** game animation */
    protected void simulation(double t) {
        boxX += t / (1000 * 1000) * 11/15;
        
        if (boxX > getWidth())
            boxX = 0;
    }
    
    /** scene drawer */
    protected void drawScene(Graphics2D g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, getWidth(), getHeight());
        
        g.setColor(Color.WHITE);
        g.fillRect((int)boxX, getHeight()/2, 20, 20);
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        
        SwingUtilities.invokeLater(new Thread() {
            public void run() {
                GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
                GraphicsDevice device = env.getDefaultScreenDevice();
                GraphicsConfiguration gc = device.getDefaultConfiguration();
                
                new Main(gc, device);
            }
        });
    }
}


This could be because on Vista the default accelerated Java2D pipeline is
not available (because it’s incompatible with Vista’s DWM), so all rendering
goes through software.

Another thing: do not make your frame non-resizeable, it needs to be
resized to the display mode’s dimensions when it goes
full-screen…

Thanks,
Dmitri

Thanks,

when will the accelerated pipeline be available for Vista?

's scary as it looks, you really should try opengl :frowning: Seriously, just spend the time and stress out over getting it installed: You’ll have alot of trouble getting it to work, but when you do, you will be happy.

Just do it.

runs smoothly on my vista @ 160fps

vista 32bit // 3Ghz Core Duo // 1GB ram // ATI VisionTek Radion X1550 // 256MB vram

The current plan is the “Consumer JRE” release (search for more info),
due to be out sometime next year.

Alternatively you can use the OpenGL pipeline (if you install the drivers).

Dmitri