为什么Java在新电脑上绘图很慢?——VSYNC



在一台新电脑(酷睿i7-4820K 3.7 GHz,华硕P9X79 LE, GeForce GTX 650, Windows 8.1和Ubuntu 14.04)上运行Java绘图基准测试,在Java RTE 1.7.0_65的Windows上,使用vsync以60帧/秒左右的速度运行。然后,在后来的一个场合,基于其他pc的速度,我以400+ FPS的速度运行。现在又回到了60帧/秒。在最轻的测试中,CPU利用率几乎为0%,GPU为10%。一个新的图形驱动程序没有什么不同。同样的类文件在linux java 1.7.0_55下,在Ubuntu上可以得到400 FPS,在JDK 6和JDK 7下编译没有差别。

下面是没有加载、计算和显示图像函数的代码。在稍慢一点的Win 7电脑上,这款游戏的帧率接近600,但在新电脑上,帧率同样是60。通过HTML applet在线运行的代码变体以400+ FPS的速度运行。有人能解释一下吗?
// 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();  
   }  
}

我已经尝试了一些"线程安全"的例子,试图在新PC上获得更快的FPS速度,没有任何成功。最后一个只是刷新一个没有图形的空白屏幕。我注意到测量的FPS通常高达65 FPS,这表明它可能不是强制的垂直同步。这导致怀疑Swing Timer,我发现它可以校准,如下所示,还包括睡眠,等待,currentTimeMillis和nanoTime。

http://www.java2s.com/Code/Java/Swing-JFC/TimeResolution.htm

下面产生Swing Timer测量:

//  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.2fn", 
                    sleepTime, iterations, totalTime, calculatedDelayTime);
        }
    }
    public void measureTimer() 
    {
        System.out.printf("                                  measuredn");
        System.out.printf("timer delay   iterations   total time   per-delayn");
        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();
    }
}

以下显示了PC产生快速FPS的结果和第二种来自新PC的结果。在休眠时间为零的情况下,另一台PC产生0.11毫秒,新一台产生15.66毫秒,相当于近64 FPS,前者的限制为9091 FPS。

有人知道是否有可能强制最小摆动计时器分辨率吗?

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

我相信这是一个完整的答案(前面低估了)。我希望这里有人知道,但事实并非如此。所以我不得不自己做研究。

在使用Javax swing timer = new timer (0, this)时,主活动的执行应该在指定actionPerformed之后以零睡眠时间重新开始。我找到了一个简单的程序来演示计时,没有图形活动,并将其修改为测量单个动作(帧)和累计帧每秒所花费的时间。结果如下:

几帧后,有问题的PC切换到15.3+ ms的常规睡眠时间,可能,正如这里的另一条消息所报道的那样,由Windows时间片粒度控制为1000 ms/64或15.625 ms,另一台PC表示开始时开销很高,但在50帧中记录了543 FPS。

           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

Java程序如下。

// 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");
  }
}

相关内容

  • 没有找到相关文章

最新更新