Try using Toolkit.getDefaultToolkit().sync(); just after you call show() of BufferStrategy.
That makes a huge difference; on par with smoothness achieved with the self coded double buffered strategy - much less taxing on the cpu.
Maybe I’m missing something, but I seriously don’t know how you can call that ‘Unpredictable as Hell’ ???
Given that Thread.sleep() has a precision of 1ms, 9998757ns IS exactly the amount of time you intended to sleep, so your example just demonstrates that Thread.sleep() works as advertised and your code to report a failure is wrong.
Which doesn’t mean timing using the standard libraries isn’t without it’s problems, but my understanding is that nanoTime generally works fine except when you have some Dual core AMD system with outdated drivers.
The LWJGL timer is the most reliable one afaik. System.currentTimeMillis is too unreliable when you want to measure with 1ms accuracy (esp. on Windows).
That dramatically improves things. Interestingly, it causes Windows Vista to revert to what it calls a basic colour scheme: “The following program has performed an action that requires Windows to temporarily change the colour scheme to Windows Basic.” The popup help hints at the fact that there is either: (i) insufficient memory to continue displaying Windows Aero (the default scheme on Windows Vista) translucent Windows. Which is unlikely, or (ii) the computer’s hardware configuration or screen resolution was changed.
Looks like a call to sync() triggers this behaviour on Windows Vista. Bu-ut, the frame rate is now more or less steady at 58-60 FPS. Thanks jezek2.
–Mario
Unless it goes in speed mode and runs twice as fast as it should on some systems/VMs (http://lwjgl.org/forum/index.php/topic,2201.0.html)…an annoying problem that never got fixed AFAIK. I’m always using System.nanoTime() now and never had a problem with it.
Since when was 9998757ns ever equal to 10ms? Okay, it is close to 10ms but it is not exact.
This thread is about why Java is not a good language for game development and the sample code which attempts to synchronize a main loop to 60fps clearly doesn’t work properly on my machine (a reasonably well spec’d one) which is why I am investigating Java’s timing abilities. A jerky screen update is a major reason for abandoning Java as a games development language.
Here’s some timer test code written by David Brackeen that shows the problem. He’s already stated in a PM to me that he believes it to be a thread scheduling problem. When you execute it you should get no jumps forward.
import static java.lang.System.out;
public class TimerTest implements Runnable {
long testLength = 10L * 1000 * 1000 * 1000; // 60 seconds
long iterations = 0;
long jumpsForward = 0;
long jumpsBackward = 0;
boolean lastWasAJump = false;
long firstTime = System.nanoTime();
long lastTime = firstTime;
public void run() {
while (true) {
Thread.yield();
long time = System.nanoTime();
if (time < lastTime) {
out.println("Backwards by: " + ((lastTime - time) / 1000000.0f) + "ms");
lastWasAJump = true;
jumpsBackward++;
} else if (time >= lastTime + 1000 * 1000 * 10) { // 10ms
out.println("Large jump by: " + ((time - lastTime) / 1000000.0f) + "ms");
lastWasAJump = true;
jumpsForward++;
} else if (lastWasAJump) {
out.println("Back to normal");
lastWasAJump = false;
}
lastTime = time;
iterations++;
if (time >= firstTime + testLength) {
break;
}
}
out.println(iterations + " iterations.");
out.println(jumpsBackward + " jumps backwards");
out.println(jumpsForward + " jumps forwards");
out.println("Java " + System.getProperty("java.version"));
}
public synchronized static void main(String[] args) {
Thread t = new Thread(new TimerTest());
t.setPriority(Thread.MAX_PRIORITY);
t.start();
}
}
–Mario
What framerate do my games run at? (Assuming you’ve gotten some working GL drivers now?)
Cas
The installer for the newest graphics drivers from AMD fails to execute properly on my machine. Windows Vista notifies me with a “this program has stopped working” dialog. I think the installer wasn’t tested on Windows Vista. :-\ So, I can’t update the drivers to run LWJGL code until AMD get around to fixing their installer to work with Vista. :’(
–Mario
The newest drivers installed just fine for me (vista64). But I saw some people mention a problem installing them on the support forums. You might want to look there for a solution.
Damn. Graphics card doesn’t support OpenGL. I wanted to download and try the Puppy Games too.
–Mario
Sure you got the latest ones?
http://game.amd.com/us-en/drivers_catalyst.aspx?p=vista32/integrated-vista32
Cas
The xpress 1100 has very poor support for opengl. It’s a hardware issue.
If he feels like experimenting gldirect might help, but I am somewhat sceptical of it.
I barely use any OpenGL whatsoever - I literally blit quads and draw lines. If it can’t cope with that…
Cas
We’ve gotten way off topic here, but official drivers often have to be modded to work on laptops.
www.driverheaven.net should have information about how to modify the driver. Or http://www.omegadrivers.net/ might have drivers you can use.
But yeah, seems like a really poor card. Lots of people complaining about it.
Yup. The thread is about reasons why Java should not be used for games writing.
To bring the thread back on track I thought I’d post the updated test code I’m using. It is a physics based main loop and the physics update code is decoupled from the framerate with a fixed time step --quite groovy really. But it is still jerky, despite the addition of a call to synchronize with the display adapter. Recall from previous posts that the stuttering and lag is a major reason for saying no to using Java for writing games. It needs a bit of a clean-up because I’ve hacked about with it a bit but there is no reason I can think of why this code should produce a stuttering animation.
package gameloop;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
/**
*
* @author Mario
*/
public class Main implements KeyListener {
private static Color[] COLORS = new Color[] {
Color.red, Color.blue, Color.green, Color.white, Color.black,
Color.yellow, Color.gray, Color.cyan, Color.pink, Color.lightGray,
Color.magenta, Color.orange, Color.darkGray };
Frame mainFrame;
/*
* Saves the last time the frame counter looked at the clock
*/
long lastTime;
/*
* Stores the calculated frames per second
*/
int fps;
/*
* Stores a count of the number of frames that have been issued
* within 1 second
*/
int frameCounter;
/*
* Saves the elapsed time in nanoseconds
*/
long elapsedTime;
/*
* Stores the calculated time to delay at the bottom of the main loop
*/
double delayTime;
/*
* Total game time in nano seconds
*/
float gameTime;
/*
* Fixed time step in seconds
*/
float dt = 0.01f;
/*
* elapsed time accumulator in seconds
*/
float accumulator;
/*
* Desired frame rate
*/
double frameRate = (double)1000000000L/(double)60;
Rectangle2D rect;
public Main(GraphicsDevice device) {
try {
// Setup the frame
GraphicsConfiguration gc = device.getDefaultConfiguration();
mainFrame = new Frame(gc);
mainFrame.setUndecorated(true);
mainFrame.setIgnoreRepaint(true);
mainFrame.setVisible(true);
mainFrame.setSize(640, 480);
mainFrame.setLocationRelativeTo(null);
mainFrame.createBufferStrategy(3);
mainFrame.addKeyListener(this);
//device.setDisplayMode(new DisplayMode(640,480,8,DisplayMode.REFRESH_RATE_UNKNOWN));
//device.setFullScreenWindow(mainFrame);
// Cache the buffer strategy and create a rectangle to move
BufferStrategy bufferStrategy = mainFrame.getBufferStrategy();
rect = new Rectangle2D.Float(0,100,64,64);
// Main loop
while(true) {
long time = System.nanoTime();
calculateFramesPerSecond();
// Add the elapsed time in seconds to the accumulator
accumulator += (float)elapsedTime/(float)1000000000;
// For each dt in the accumulator, update the physics
while( accumulator >= dt ) {
// Update world
updateWorld(dt);
gameTime += dt;
accumulator -= dt;
}
// Draw
Graphics g = bufferStrategy.getDrawGraphics();
drawScreen(g);
g.dispose();
// Flip the buffer
if( ! bufferStrategy.contentsLost() )
bufferStrategy.show();
// Synchronise with the display hardware
Toolkit.getDefaultToolkit().sync();
// Delay for a period of time equal to 1/60th of a
// second minus the elapsed time
elapsedTime = System.nanoTime() - time;
delayTime = frameRate - (double)elapsedTime;
time = System.nanoTime();
while( System.nanoTime() - time <= delayTime)
Thread.yield();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
device.setFullScreenWindow(null);
}
}
private void updateWorld(float deltaTime) {
rect.setRect(rect.getX() + 240*deltaTime, 100, 64, 64);
if( rect.getX() > mainFrame.getWidth() )
rect.setRect(-rect.getWidth(), 100, 64, 64);
}
private void drawScreen(Graphics g) {
g.setColor(COLORS[0]);
g.fillRect(0, 0, mainFrame.getWidth(), mainFrame.getHeight());
g.setColor(COLORS[3]);
g.drawString("FPS: "+ fps, 0, 17);
g.drawString("Game time: " + gameTime, 0, 34);
g.setColor(COLORS[1]);
g.fillRect((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
}
private void calculateFramesPerSecond() {
long time = System.nanoTime();
if( time - lastTime >= 1000000000L ) {
fps = frameCounter;
lastTime = time;
frameCounter = 0;
}
frameCounter++;
}
public void keyPressed(KeyEvent e) {
if( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
System.exit(0);
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public static void main(String[] args) {
try {
GraphicsEnvironment env = GraphicsEnvironment.
getLocalGraphicsEnvironment();
GraphicsDevice device = env.getDefaultScreenDevice();
new Main(device);
} catch (Exception e) {
e.printStackTrace();
}
}
}
I realize that many of you use LWJGL because of the performance hit, but I actually can’t see the point of including native libraries with a Java program. When a Java program calls for native support I automatically reach for my C/C++ compiler. Once I’m in the C/C++ environment I’m reluctant to leave it to go back into Java as it just feels as though I’m creating a DLL for scripting purposes. I might as well stay in the C/C++ IDE and write the whole thing in C and then I don’t have to concern myself with producing JNI interfaces. So-o, I want to write pure Java games without native support.
–Mario
Native libraries for LWJGL are no issue, as they’re already precompiled for all platforms and I just need to include few files in my projects. The rest is done in pure Java and it’s huge saving to be able doing just one build for all platforms and code in sane programming language. And LWJGL is the only library where I need native access, everything other can be done in pure Java. Absolutely no need to touch any C/C++ at all.
LWJGL doesn’t work on all systems. To run LWJGL code you must either have a graphics card plus drivers that support OpenGL, or a software OpenGL emulation layer. A lot of people out there simply will not be able to run your game.
As soon as you include native libs like LWJGL you effectively reduce the portability of your code to people who have compatible hardware. Cas has already said that he doesn’t really use much OpenGL code, all he does is blit quads and draw lines. If that’s all then you might as well use Windows GDI and DirectDraw and your game will work on every Windows machine.
I think now that if you’re going to write games in Java, then for maximum portability you should stick to pure Java. But so far it looks as though pure Java isn’t up to the job of managing a simple game loop. :o
–Mario
Then what about Mac and Linux?
OpenGL is much more prolific and cross platform than DirectX.
Well that depends on your target audience. For example I’m aiming for cards that supports at least PS2.0a/b pixel shaders in an standalone (normal app) non-causal game.
For causal games, you can use pure Java2D and tune your usage of it according to Java2D’s software renderer (and have nice boost on 6u10 with D3D acceleration by default), or use own optimized software rendering by using either your own code or some existing pure Java libraries such as PulpCore, JPCT and others.
'Course you can, yeah. Bu-ut, the main game loop stutters and lags if you use pure Java. That is what this thread is currently about.
–Mario