Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8208113

Graphical performance issue in Linux with JDK 8 and above

    XMLWordPrintable

    Details

    • Subcomponent:
    • Introduced In Version:
      8
    • CPU:
      x86_64
    • OS:
      linux

      Description

      ADDITIONAL SYSTEM INFORMATION :
      The "OK" sanction is a nominal animation, the NOK is the abnormal behavior
      OK : CentOs6.8 gdm 2.30.4 Xorg 1.17.4 jre 1.7
      NOK : CentOs6.8 gdm 2.30.4 Xorg 1.17.4 jre 1.8-172
      OK : CentOs7.2 gdm 3.14.2 Xorg 1.17.2 jre 1.7-80
      OK : CentOs7.2 gdm 3.14.2 Xorg 1.17.2 jre 1.7-80
      NOK : CentOs7.2 gdm 3.26.2 Xorg 1.19.5 jre 1.8.0_171-b10
      NOK : CentOs7.2 gdm 3.26.2 Xorg 1.19.5 jre 1.8.0_171-b10
      NOK : CentOs7.2 gdm 3.14.2 Xorg 1.17.2 jre 1.8.0_65-b17
      NOK : CentOs7.4 gdm 3.22.3 Xorg ?.??.? jre 1.8-131
      NOK : CentOs7.4 gdm 3.22.3 Xorg ?.??.? jre 1.8-172
      OK : CentOs7.5 gdm 3.26.2 Xorg 1.19.5 jre 1.7-67
      NOK : CentOs7.5 gdm 3.26.2 Xorg 1.19.5 jre 1.8-161
      NOK : CentOs7.5 gdm 3.26.2 Xorg 1.19.5 jre 1.8-161
      NOK : CentOs7.5 kde 4.11.19 Xorg 1.19.5 jre 1.8-161

      A DESCRIPTION OF THE PROBLEM :
      we detected a performance regression for all UI processing on CentOS for Java 1.8 and later releases (test performed on 1.10 also) which was not present on previous releases.
      We established a basic test application which only perform a repaint animation in a JPanel. On 1.8 and later, the animation is slow (from 15Hz normally to 1hz on 1.8). We performed several test on several versions.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      A simple translation animation as performed in the following code sample :
      /** Custom painting codes on this JPanel */
      @Override
      public void paintComponent(Graphics g) {
      super.paintComponent(g); // paint background
      setBackground(Color.WHITE);
      Graphics2D g2d = (Graphics2D) g;

      transform.setToIdentity();
      // The origin is initially set at the top-left corner of the image.
      // Move the center of the image to (x, y).
      transform.translate(x - imgWidth / 2, y - imgHeight / 2);
      // Rotate about the center of the image
      transform.rotate(Math.toRadians(direction),
      imgWidth / 2, imgHeight / 2);
      // Apply the transform to the image and draw
      g2d.drawImage(imgFrames[currentFrame], transform, null);
      }

      triggered by a independent Thread
      Thread animationThread = new Thread() {
      @Override
      public void run() {
      long time = System.currentTimeMillis();
      while (true) {
      update(); // update the position and image
      repaint(); // Refresh the display
      try {
      Thread.sleep(1000 / frameRate); // delay and yield to other threads
      } catch (InterruptedException ex) {
      }
      time = System.currentTimeMillis();
      }
      }
      };

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      a fluid animation close to the selected rate (behavior in 1.7)
      ACTUAL -
      The animation is slow (slightly over 1Hz) and seems to be faster when the mouse is moving over the JPanel

      ---------- BEGIN SOURCE ----------
      //Requires additionnal pictures for this test case

      package test;

      import java.awt.BorderLayout;
      import java.awt.Color;
      import java.awt.Dimension;
      import java.awt.Graphics;
      import java.awt.Graphics2D;
      import java.awt.Image;
      import java.awt.geom.AffineTransform;
      import java.io.IOException;
      import java.net.URL;

      import javax.imageio.ImageIO;
      import javax.swing.JFrame;
      import javax.swing.JLabel;
      import javax.swing.JPanel;
      import javax.swing.JSlider;
      import javax.swing.SwingUtilities;
      import javax.swing.event.ChangeEvent;
      import javax.swing.event.ChangeListener;

      /** Animating image frames. Each frame has its own file */
      @SuppressWarnings("serial")
      public class AnimatedFramesDemo extends JPanel {
      // Named-constants
      static final int CANVAS_WIDTH = 640;
      static final int CANVAS_HEIGHT = 480;
      public static final String TITLE = "Animated Frame Demo";

      private String[] imgFilenames = {
      "images/pacman_1.png", "images/pacman_2.png", "images/pacman_3.png" };
      private Image[] imgFrames; // array of Images to be animated
      private int currentFrame = 0; // current frame number
      private static int frameRate = 20; // frame rate in frames per second
      private static JLabel rateLbl = new JLabel(Integer.toString(frameRate) + " fps");
      private int imgWidth, imgHeight; // width and height of the image
      private double x = 100.0, y = 80.0; // (x,y) of the center of image
      private double speed = 8; // displacement in pixels per move
      private double direction = 0; // in degrees
      private double rotationSpeed = 1; // in degrees per move

      // Used to carry out the affine transform on images
      private AffineTransform transform = new AffineTransform();

      /** Constructor to set up the GUI components */
      public AnimatedFramesDemo() {
      // Setup animation
      loadImages(imgFilenames);
      Thread animationThread = new Thread() {
      @Override
      public void run() {
      long time = System.currentTimeMillis();
      while (true) {
      update(); // update the position and image
      repaint(); // Refresh the display
      try {
      Thread.sleep(1000 / frameRate); // delay and yield to other threads
      } catch (InterruptedException ex) {
      }
      time = System.currentTimeMillis();
      }
      }
      };
      animationThread.start(); // start the thread to run animation

      // Setup GUI
      setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
      }

      /** Helper method to load all image frames, with the same height and width */
      private void loadImages(String[] imgFileNames) {
      int numFrames = imgFileNames.length;
      imgFrames = new Image[numFrames]; // allocate the array
      URL imgUrl = null;
      for (int i = 0; i < numFrames; ++i) {
      imgUrl = getClass().getClassLoader().getResource(imgFileNames[i]);
      if (imgUrl == null) {
      System.err.println("Couldn't find file: " + imgFileNames[i]);
      } else {
      try {
      imgFrames[i] = ImageIO.read(imgUrl); // load image via URL
      } catch (IOException ex) {
      ex.printStackTrace();
      }
      }
      }
      imgWidth = imgFrames[0].getWidth(null);
      imgHeight = imgFrames[0].getHeight(null);
      }

      /** Update the position based on speed and direction of the sprite */
      public void update() {
      x += speed * Math.cos(Math.toRadians(direction)); // x-position
      if (x >= CANVAS_WIDTH) {
      x -= CANVAS_WIDTH;
      } else if (x < 0) {
      x += CANVAS_WIDTH;
      }
      y += speed * Math.sin(Math.toRadians(direction)); // y-position
      if (y >= CANVAS_HEIGHT) {
      y -= CANVAS_HEIGHT;
      } else if (y < 0) {
      y += CANVAS_HEIGHT;
      }
      direction += rotationSpeed; // update direction based on rotational speed
      if (direction >= 360) {
      direction -= 360;
      } else if (direction < 0) {
      direction += 360;
      }
      ++currentFrame; // display next frame
      if (currentFrame >= imgFrames.length) {
      currentFrame = 0;
      }
      }

      /** Custom painting codes on this JPanel */
      @Override
      public void paintComponent(Graphics g) {
      super.paintComponent(g); // paint background
      setBackground(Color.WHITE);
      Graphics2D g2d = (Graphics2D) g;

      transform.setToIdentity();
      // The origin is initially set at the top-left corner of the image.
      // Move the center of the image to (x, y).
      transform.translate(x - imgWidth / 2, y - imgHeight / 2);
      // Rotate about the center of the image
      transform.rotate(Math.toRadians(direction),
      imgWidth / 2, imgHeight / 2);
      // Apply the transform to the image and draw
      g2d.drawImage(imgFrames[currentFrame], transform, null);
      }

      /** The Entry main method */
      public static void main(String[] args) {
      // Run the GUI codes on the Event-Dispatching thread for thread safety
      SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
      JFrame frame = new JFrame(TITLE);
      frame.setLayout(new BorderLayout());
      frame.add(new AnimatedFramesDemo(), BorderLayout.CENTER);
      JPanel rate = new JPanel();
      rate.setLayout(new BorderLayout());

      final JSlider progress = new JSlider(5, 50, frameRate);
      progress.addChangeListener(new ChangeListener() {

      @Override
      public void stateChanged(ChangeEvent e) {
      frameRate = progress.getValue();
      rateLbl.setText(Integer.toString(frameRate) + " fps");

      }
      });
      progress.setValue(frameRate);

      rate.add(progress, BorderLayout.CENTER);
      rate.add(rateLbl, BorderLayout.EAST);

      frame.add(rate, BorderLayout.SOUTH);
      frame.pack();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setLocationRelativeTo(null); // center the application window
      frame.setVisible(true);
      }
      });

      }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      it seems that the usage of calling the method "Toolkit.getDefaultToolkit().sync();" after the drawing method (end of paintComponent method), the animation is fluid again even in Java 1.8


        Attachments

          Activity

            People

            Assignee:
            psadhukhan Prasanta Sadhukhan
            Reporter:
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Dates

              Created:
              Updated: