Why is Java drawing slow on a new PC? - VSYNC?

纵饮孤独 提交于 2019-12-13 05:18:55

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!