JDK-8068529 : Animations are sluggish in Java 8
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 8,9
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • OS: linux
  • CPU: x86_64
  • Submitted: 2015-01-03
  • Updated: 2018-09-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux filesrv 3.11.10-21-desktop #1 SMP PREEMPT Mon Jul 21 15:28:46 UTC 2014 (9a9565d) x86_64 x86_64 x86_64 GNU/Linux


EXTRA RELEVANT SYSTEM CONFIGURATION :
Tried this on multiple systems with different graphics cards. Same result.

A DESCRIPTION OF THE PROBLEM :
When using animations, they are sluggish on Java 8. They were fluent on Java 7. I found out that this has to do with the Blit type used. Java 8 uses a different Blit (from the xr package). The root cause is a change in X11GraphicsEnvironment.java:

                    boolean xRenderRequested = true;

This boolean was false in Java 7. According to the source history,  this is related to bug JDK-7077423: xrender is enabled by default.

Disabling xrender using -Dsun.java2d.xrender=false reverts Java 8 to the fluent animations Java 7 has, but I think xrender is now default because it is supposed to give a better performance, so that is not a solution.

Please check out this video I made: left you see a simple bouncing ball animation using -Dsun.java2d.xrender=false, right you see the same bouncing ball using -Dsun.java2d.xrender=true. With xrender on, the ui is not updated very often, so the animation is not fluent, unless actively moving around the mouse in that window.

https://www.youtube.com/watch?v=DEO6ZppjuzQ




REGRESSION.  Last worked in version 7u51

ADDITIONAL REGRESSION INFORMATION: 
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)

This was working in Java 7, because of the change I found in X11GraphicsConfiguration: the boolean is now default true:

                    boolean xRenderRequested = true;

In the C-code (without xrender) Blit uses Xsync to send each change to the display. I didn't see this happen in the xrender version, but I don't know whether Xsync can be added there, due to the different architecture of xrender, which is unknown to me.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Save the source code Animation.java and run it with or without -Dsun.java2d.xrender=false. So:

This is sluggish on Java 8:

java -cp . Animation

or

java -cp . -Dsun.java2d.xrender=true Animation

This runs fluently:

java -cp . -Dsun.java2d.xrender=false Animation



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Fluent animation with xrender as well.
ACTUAL -
Sluggish animation.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.Color;
import java.awt.Graphics;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Animation extends JPanel {
	private static int DELAY = 10;

	int position = 0;
	
	public void paint(Graphics g) {
		super.paint(g);
		g.setColor(Color.RED);
		
		int objectWidth = 50;
		int objectHeight = 50;
		int arcWidth = objectWidth;
		int arcHeight = objectHeight;
		
		int xspace = getWidth()-objectWidth;
		int x = calcPosition(xspace);
		int yspace = getHeight()-objectHeight;
		int y = calcPosition(yspace);
		
		g.fillRoundRect(x, y, objectWidth, objectHeight, arcWidth, arcHeight);
		g.setColor(Color.BLACK);
		g.drawString("Position " + position, 10, 10);
	}
	
	private int calcPosition(int space) {
		return space - Math.abs(space - (position % (space*2)));
	}
	
	
	public void go() {
		TimerTask task = new TimerTask() {
			public void run() {
				position++;
				repaint();
			}
		};
		Timer timer = new Timer();
		timer.schedule(task, 0, DELAY);
	}

	public static void main(String args[]) {
		Animation panel = new Animation();
		JFrame f = new JFrame();
		f.setSize(400, 300);
		f.setTitle("sun.java2d.xrender=" + System.getProperty("sun.java2d.xrender"));
		f.setContentPane(panel);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		panel.setDoubleBuffered(true);
		panel.go();
	}
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Starting Java with  -Dsun.java2d.xrender=false

But as I said, xrender probably is the future for Java 2D, so this problem should be solved.


Comments
on discussion
28-11-2016

The Swing repaint manager would only help if you were using Swing. This example may be .. but that is just an example. Also I suspect that if Swing's repaint manager were not already doing something like that it may be for good reason and regardless it may be a change that creates problems for some other application. A change like that is too risky at this stage as we wind down JDK 9 .. The observation that the application can call Toolkit.sync() may be the best - or at least safest - answer here.
15-11-2016

Victor, the more I think about the suggested solution (to do it like D3D/OGL) - the less I like it. Implicitly flushing the pipeline by detecting certain blit patterns and deducing what the calling code might have actually intended is a hack at best - and when using X over network will actually kill performance. There is already a proper method for achieving the intended behaviour, Toolkit.sync(). Even its javadoc description precisly describes its puprose: > public abstract void sync() > Synchronizes this toolkit's graphics state. Some window systems may do buffering of graphics events. > This method ensures that the display is up-to-date. It is useful for animation. Flushing should actually happen once, and the proper place in my opinion is Swing's RepaintManager class. May I file a bug report about this issue against Swing?
15-11-2016

Clemens, this bug will be deferred soon unless your fix is really in progress, on review phase
15-11-2016

Clemens, is this bug fix still in your plans to meet ZBB (Feb 16, 2017)? http://openjdk.java.net/projects/jdk9/
19-10-2016

Yes, this issue is easy to fix (although the solution suggested by sergey will reduce performance unfortunately). I'll send a patch the next few days.
02-09-2016

d3d �� ogl pipelines had the same problem, and now we flush render queue each time when the blit memory2window occur See IsoBlit in D3DBlitLoops/OGLBlitLoops: if (rtt && oglDst.isOnScreen()) { // we only have to flush immediately when copying from a // (non-texture) surface to the screen; otherwise Swing apps // might appear unresponsive until the auto-flush completes rq.flushNow(); }
19-01-2015

Maybe by accident (there are some syncs in the shm pixmap code), but I don't see any calls to XSync in the accelerated blit path (would be pretty bad for performance). I wonder wether this is something the J2D pipeline should handle (e.g. periodically flush) as according to javadoc a j2d pipeline it is allowed to buffer drawing commands, or whether AWT/Swing should notify us when it would to have stuff drawn on screen.
18-01-2015

Probably some in X11SurfaceData.c?
12-01-2015

Sure, OpenGL is not client/server based and doesn't have a command-buffer ;) What I wonder is whether the old X11 pipeline does some implicit flushing which is missing in the xrender pipeline. However, I didn't find the call to XSync as mentioned in the original description.
12-01-2015

Note that opengl works as expected.
12-01-2015

Sorry, I missed the comment in the bug description: > In the C-code (without xrender) Blit uses Xsync to send each change to the display. Where does the call to Xsync occur? I had a look at X11PMBlitLoops.java as well as X11PMBlitLoops.c (the nativeBlit method) but didn't find any explicity calls to XSync.
09-01-2015

The issue with this code is that it never calls Toolkit.sync(), therefore drawing-commands are queued up in Xlib's request buffer. When the buffer is full, all requests are sent to and processed by xserver in one go. For me the sample code also produces a choppy animation with the X11 pipeline, although not as bad as xrender does. Simply inserting the line "Toolkit.getDefaultToolkit().sync();" at the end of the paint method solves it for both pipelines. The reason why the X11 pipeline produces probably less choppy results might be text rendering. While xrender just queues up a few glyph-ids and their position, the X11 pipeline generates bitmask-images for text.
08-01-2015

Still reproducible on current JDK 9 builds.
06-01-2015