问题
Running a Java drawing benchmark on a new PC (Core i7-4820K 3.7 GHz, Asus P9X79 LE, GeForce GTX 650, Windows 8.1 and Ubuntu 14.04) appeared to run using vsync at around 60 FPS via Windows with Java RTE 1.7.0_65 . Then, on one later occasion, ran at an expected 400+ FPS, based on speeds on other PCs. Now it is back to 60 FPS. CPU utilisation is almost 0% with GPU at <10% on the lightest test. A new graphics driver made no difference. The same class file obtains 400 FPS via Ubuntu with linux java 1.7.0_55 and makes no difference compiled with JDK 6 or 7.
Below is the code without the functions that load, calculate and display images. This obtains nearly 600 FPS on an older slightly slower Win 7 PC but, again, 60 FPS on the new PC. A variation of the code to run on-line via an HTML applet runs at 400+ FPS. Does anyone have an explanation?
// Save as JavaDrawIt.java
// Compile command - javac JavaDrawIt.java
// Run command - java JavaDrawIt
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class JavaDrawIt extends JPanel implements ActionListener
{
Timer timer;
static int WIDTH = 1280 ;
static int HEIGHT = 720 ;
int gch = 1;
int gcm = 1;
int grn = 0;
String msg;
double fps;
double startTime;
double runTime = 0;
int frames = 0;
Random randNum = new Random();
private JavaDrawIt()
{
randNum.setSeed(999);
timer = new Timer(0, this);
startTime = (double)System.currentTimeMillis();
}
public void actionPerformed(ActionEvent e)
{
if (runTime <= 5.0)
{
repaint();
}
}
public void paintComponent(Graphics g)
{
int i;
float fh;
float fw;
super.paintComponent(g);
grn = grn + gch;
if (grn > 255)
{
gch = -gcm;
grn = 255;
}
if (grn < 1)
{
gch = gcm;
grn = 0;
}
g.setColor(new Color(0, grn, 255));
g.fillRect(0, 0, WIDTH, HEIGHT);
frames = frames + 1;
Graphics2D g2 = (Graphics2D)g;
Font font = new Font("MONOSPACE", Font.PLAIN, 24);
g2.setFont(font);
g.setColor (Color.WHITE);
runTime = ((double)System.currentTimeMillis() - startTime) / 1000;
fps = (double)frames / runTime;
msg = String.format(" %6.2f FPS, %6d Frames, %6.2f Seconds", fps, frames, runTime);
g2.drawString(msg, 10, 30);
}
public static void main(String[] args)
{
JFrame f = new JFrame(" JavaDrawIt");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JavaDrawIt m = new JavaDrawIt();
f.add(m);
f.setSize(WIDTH, HEIGHT);
f.setVisible(true);
m.timer.start();
}
}
I have tried a number of “Thread Safe” examples, attempting to obtain faster FPS speeds on the new PC, without any success. The last one was just refreshing a blank screen with no graphics. I noted that measured FPS was often up to 65 FPS, suggesting that it might not be a forced VSYNC. That lead to suspecting Swing Timer and I found that it could be calibrated as demonstrated in the following that also covers sleep, wait, currentTimeMillis AND nanoTime.
http://www.java2s.com/Code/Java/Swing-JFC/TimeResolution.htm
The following produces Swing Timer measurements:
// TimeResolution.java
// Copyright (c) 2007, Sun Microsystems, Inc
// All rights reserved.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class TimeResolution implements ActionListener
{
private static int INCREMENT = 5;
private static int MAX = 50;
// Variables used in measurement of Swing timer
int timerIteration = 0;
int iterations = 0;
Timer timer;
long startTime, endTime;
int sleepTime;
public void actionPerformed(ActionEvent ae)
{
if (++timerIteration > iterations)
{
timer.stop();
timerIteration = 0;
endTime = System.nanoTime();
long totalTime = (endTime - startTime) / 1000000;
float calculatedDelayTime = totalTime / (float)iterations;
System.out.printf(" %2d %5d %5d %5.2f\n",
sleepTime, iterations, totalTime, calculatedDelayTime);
}
}
public void measureTimer()
{
System.out.printf(" measured\n");
System.out.printf("timer delay iterations total time per-delay\n");
for (sleepTime = 0; sleepTime <= 5; ++sleepTime)
{
iterations = (sleepTime == 0) ? 1000 : (1000 / sleepTime);
timerIteration = 1;
timer = new Timer(sleepTime, this);
startTime = System.nanoTime();
timer.start();
while (timerIteration > 0)
{
try
{
Thread.sleep(1000);
}
catch (Exception e)
{
}
}
}
}
// Execute the various timer resolution tests.
public static void main(String args[])
{
TimeResolution timeResolution = new TimeResolution();
timeResolution.measureTimer();
}
}
Following shows results from the PC that produces fast FPS and the second from the new PC. With sleep time of zero the other PC produces 0.11 milliseconds and the new one 15.66 milliseconds, equivalent to nearly 64 FPS, with the former limit at 9091 FPS.
Does anyone know if it is possible to force a minimum Swing Timer resolution?
Other PC
timer delay iterations total time per-delay
0 1000 113 0.11
1 1000 15600 15.60
2 500 7800 15.60
3 333 5195 15.60
4 250 3900 15.60
5 200 3120 15.60
New PC
0 1000 15660 15.66
1 1000 15625 15.63
2 500 7812 15.62
3 333 5203 15.62
4 250 3906 15.62
5 200 3125 15.63
回答1:
I believe that this is a complete answer (understated earlier). I expected someone here to know, but that was not the case. So I had to do my own research.
In using Javax swing timer = new Timer(0, this), main activity execution should recommence with zero sleeping time, after actionPerformed is indicated. I found a simple program that demonstrated timing, with no graphics activity, and modified it to measure time taken for individual actions (frames) and cumulative Frames Per Second. Results are below.
After a few frames, the offending PC switched to a regular sleeping time of 15.3+ ms, probably, as reported here in another message, controlled by Windows timeslice granularity of 1000 ms / 64 or 15.625 ms, The other PC indicated high overheads to start with, but recorded 543 FPS over 50 frames.
New PC Win 8.1 Other PC Win 7
Frames FPS This frame FPS This frame
microsecs microsecs
1 500 2603 166 6170
2 44 42630 333 100
3 57 7051 500 107
4 58 15402 571 74
5 60 15550 555 231
6 60 15633 117 41586
7 60 15483 134 817
8 61 15396 106 22895
9 61 15388 107 8374
10 61 15370 117 867
11 62 15321 129 41
12 62 15418 141 217
13 62 15390 152 88
14 62 15353 162 73
To
45 63 15353 494 45
46 63 15360 505 48
47 63 14998 516 45
48 63 15306 521 113
49 63 15331 532 64
50 63 15365 543 44
The Java program is below.
// From http://www.java2s.com/Code/JavaAPI/javax.swing/Timerstop.htm
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.Timer;
class MainClass extends JFrame
{
Timer timer;
long startTime, startTimeF, runTime, fps, usecs;
int counter;
MainClass(String title)
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ActionListener a = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
runTime = (System.nanoTime() - startTime) / 1000000;
usecs = (System.nanoTime() - startTimeF) / 1000;
fps = counter * 1000 / runTime;
System.out.println(" Frames = " + counter + " " + fps + " FPS This frame " + usecs + " microseconds");
startTimeF = System.nanoTime();
if (++counter > 50)
{
timer.stop();
System.exit(0);
}
}
};
timer = new Timer(0, a);
startTime = System.nanoTime();
startTimeF = System.nanoTime();
counter = 1;
timer.start();
setSize(800, 600);
// pack();
setVisible(true);
}
public static void main(String[] args)
{
new MainClass("Timer Demo1");
}
}
来源:https://stackoverflow.com/questions/25093744/why-is-java-drawing-slow-on-a-new-pc-vsync